1 /* completion.c - complete a command, a disk, a partition or a file */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/normal.h>
21 #include <grub/misc.h>
24 #include <grub/partition.h>
25 #include <grub/disk.h>
26 #include <grub/file.h>
27 #include <grub/parser.h>
28 #include <grub/extcmd.h>
29 #include <grub/charset.h>
31 /* The current word. */
32 static const char *current_word
;
34 /* The matched string. */
37 /* The count of candidates. */
40 /* The string to be appended. */
41 static const char *suffix
;
43 /* The callback function to print items. */
44 static void (*print_func
) (const char *, grub_completion_type_t
, int);
46 /* The state the command line is in. */
47 static grub_parser_state_t cmdline_state
;
50 /* Add a string to the list of possible completions. COMPLETION is the
51 string that should be added. EXTRA will be appended if COMPLETION
52 matches uniquely. The type TYPE specifies what kind of data is added. */
54 add_completion (const char *completion
, const char *extra
,
55 grub_completion_type_t type
)
57 if (grub_strncmp (current_word
, completion
, grub_strlen (current_word
)) == 0)
64 match
= grub_strdup (completion
);
72 print_func (match
, type
, 0);
79 const char *t
= completion
;
82 print_func (completion
, type
, num_found
- 1);
84 /* Detect the matched portion. */
85 while (*s
&& *t
&& *s
== *t
)
90 s
= match
+ grub_getend (match
, s
);
102 iterate_partition (grub_disk_t disk
, const grub_partition_t p
,
103 void *data
__attribute__ ((unused
)))
105 const char *disk_name
= disk
->name
;
110 part_name
= grub_partition_get_name (p
);
114 name
= grub_xasprintf ("%s,%s", disk_name
, part_name
);
115 grub_free (part_name
);
120 ret
= add_completion (name
, ")", GRUB_COMPLETION_TYPE_PARTITION
);
126 iterate_dir (const char *filename
, const struct grub_dirhook_info
*info
,
127 void *data
__attribute__ ((unused
)))
132 if (cmdline_state
== GRUB_PARSER_STATE_DQUOTE
)
134 else if (cmdline_state
== GRUB_PARSER_STATE_QUOTE
)
139 if (add_completion (filename
, prefix
, GRUB_COMPLETION_TYPE_FILE
))
142 else if (grub_strcmp (filename
, ".") && grub_strcmp (filename
, ".."))
146 fname
= grub_xasprintf ("%s/", filename
);
147 if (add_completion (fname
, "", GRUB_COMPLETION_TYPE_FILE
))
159 iterate_dev (const char *devname
, void *data
__attribute__ ((unused
)))
163 /* Complete the partition part. */
164 dev
= grub_device_open (devname
);
168 grub_errno
= GRUB_ERR_NONE
;
172 if (grub_strcmp (devname
, current_word
) == 0)
174 if (add_completion (devname
, ")", GRUB_COMPLETION_TYPE_PARTITION
))
176 grub_device_close (dev
);
181 if (grub_partition_iterate (dev
->disk
, iterate_partition
, NULL
))
183 grub_device_close (dev
);
187 else if (add_completion (devname
, "", GRUB_COMPLETION_TYPE_DEVICE
))
189 grub_device_close (dev
);
193 grub_device_close (dev
);
194 grub_errno
= GRUB_ERR_NONE
;
198 /* Complete a device. */
200 complete_device (void)
202 /* Check if this is a device or a partition. */
203 char *p
= grub_strchr (++current_word
, ',');
208 /* Complete the disk part. */
209 if (grub_disk_dev_iterate (iterate_dev
, NULL
))
214 /* Complete the partition part. */
216 dev
= grub_device_open (current_word
);
218 grub_errno
= GRUB_ERR_NONE
;
224 if (grub_partition_iterate (dev
->disk
, iterate_partition
, NULL
))
226 grub_device_close (dev
);
231 grub_device_close (dev
);
240 /* Complete a file. */
251 device
= grub_file_get_device_name (current_word
);
252 if (grub_errno
!= GRUB_ERR_NONE
)
255 dev
= grub_device_open (device
);
262 fs
= grub_fs_probe (dev
);
269 dir
= grub_strchr (current_word
+ (device
? 2 + grub_strlen (device
) : 0),
271 last_dir
= grub_strrchr (current_word
, '/');
276 current_word
= last_dir
+ 1;
278 dir
= grub_strdup (dir
);
285 /* Cut away the filename part. */
286 dirfile
= grub_strrchr (dir
, '/');
289 /* Iterate the directory. */
290 (fs
->dir
) (dev
, dir
, iterate_dir
, NULL
);
302 current_word
+= grub_strlen (current_word
);
303 match
= grub_strdup ("/");
316 grub_device_close (dev
);
321 /* Complete an argument. */
323 complete_arguments (char *command
)
327 const struct grub_arg_option
*option
;
328 char shortarg
[] = "- ";
330 cmd
= grub_command_find (command
);
332 if (!cmd
|| !(cmd
->flags
& GRUB_COMMAND_FLAG_EXTCMD
))
339 if (add_completion ("-u", " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
342 /* Add the short arguments. */
343 for (option
= ext
->options
; option
->doc
; option
++)
345 if (! option
->shortarg
)
348 shortarg
[1] = option
->shortarg
;
349 if (add_completion (shortarg
, " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
354 /* First add the built-in arguments. */
355 if (add_completion ("--help", " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
357 if (add_completion ("--usage", " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
360 /* Add the long arguments. */
361 for (option
= ext
->options
; option
->doc
; option
++)
364 if (!option
->longarg
)
367 longarg
= grub_xasprintf ("--%s", option
->longarg
);
371 if (add_completion (longarg
, " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
383 static grub_parser_state_t
384 get_state (const char *cmdline
)
386 grub_parser_state_t state
= GRUB_PARSER_STATE_TEXT
;
390 state
= grub_parser_cmdline_state (state
, *(cmdline
++), &use
);
395 /* Try to complete the string in BUF. Return the characters that
396 should be added to the string. This command outputs the possible
397 completions by calling HOOK, in that case set RESTORE to 1 so the
398 caller can restore the prompt. */
400 grub_normal_do_completion (char *buf
, int *restore
,
401 void (*hook
) (const char *, grub_completion_type_t
, int))
406 /* Initialize variables. */
414 if (grub_parser_split_cmdline (buf
, 0, 0, &argc
, &argv
))
420 current_word
= argv
[argc
- 1];
422 if (argc
> 1 && ! grub_strcmp (argv
[0], "set"))
424 char *equals
= grub_strchr (current_word
, '=');
426 /* Complete the value of the variable. */
427 current_word
= equals
+ 1;
430 /* Determine the state the command line is in, depending on the
431 state, it can be determined how to complete. */
432 cmdline_state
= get_state (buf
);
434 if (argc
== 1 || argc
== 0)
436 /* Complete a command. */
440 if (cmd
->prio
& GRUB_COMMAND_FLAG_ACTIVE
)
442 if (add_completion (cmd
->name
, " ", GRUB_COMPLETION_TYPE_COMMAND
))
447 else if (*current_word
== '-')
449 if (complete_arguments (buf
))
452 else if (*current_word
== '(' && ! grub_strchr (current_word
, ')'))
454 /* Complete a device. */
455 if (complete_device ())
460 /* Complete a file. */
461 if (complete_file ())
465 /* If more than one match is found those matches will be printed and
466 the prompt should be restored. */
472 /* Return the part that matches. */
482 current_len
= grub_strlen (current_word
);
483 match_len
= grub_strlen (match
);
485 /* Count the number of spaces that have to be escaped. XXX:
486 More than just spaces have to be escaped. */
487 for (escstr
= match
+ current_len
; *escstr
; escstr
++)
491 ret
= grub_malloc (match_len
- current_len
+ grub_strlen (suffix
) + spaces
+ 1);
493 for (escstr
= match
+ current_len
; *escstr
; escstr
++)
495 if (*escstr
== ' ' && cmdline_state
!= GRUB_PARSER_STATE_QUOTE
496 && cmdline_state
!= GRUB_PARSER_STATE_QUOTE
)
498 *(newstr
++) = *escstr
;
503 grub_strcpy (newstr
, suffix
);
524 grub_errno
= GRUB_ERR_NONE
;