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 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>
30 /* The current word. */
31 static char *current_word
;
33 /* The matched string. */
36 /* The count of candidates. */
39 /* The string to be appended. */
40 static const char *suffix
;
42 /* The callback function to print items. */
43 static void (*print_func
) (const char *, grub_completion_type_t
, int);
45 /* The state the command line is in. */
46 static grub_parser_state_t cmdline_state
;
49 /* Add a string to the list of possible completions. COMPLETION is the
50 string that should be added. EXTRA will be appended if COMPLETION
51 matches uniquely. The type TYPE specifies what kind of data is added. */
53 add_completion (const char *completion
, const char *extra
,
54 grub_completion_type_t type
)
56 if (grub_strncmp (current_word
, completion
, grub_strlen (current_word
)) == 0)
63 match
= grub_strdup (completion
);
71 print_func (match
, type
, 0);
78 const char *t
= completion
;
81 print_func (completion
, type
, num_found
- 1);
83 /* Detect the matched portion. */
84 while (*s
&& *t
&& *s
== *t
)
100 iterate_partition (grub_disk_t disk
, const grub_partition_t p
)
102 const char *disk_name
= disk
->name
;
103 char *partition_name
= grub_partition_get_name (p
);
107 if (! partition_name
)
110 name
= grub_malloc (grub_strlen (disk_name
) + 1
111 + grub_strlen (partition_name
) + 1);
114 grub_free (partition_name
);
118 grub_sprintf (name
, "%s,%s", disk_name
, partition_name
);
119 grub_free (partition_name
);
121 ret
= add_completion (name
, ")", GRUB_COMPLETION_TYPE_PARTITION
);
127 iterate_dir (const char *filename
, const struct grub_dirhook_info
*info
)
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
, ".."))
144 char fname
[grub_strlen (filename
) + 2];
146 grub_sprintf (fname
, "%s/", filename
);
147 if (add_completion (fname
, "", GRUB_COMPLETION_TYPE_FILE
))
155 iterate_dev (const char *devname
)
159 /* Complete the partition part. */
160 dev
= grub_device_open (devname
);
164 if (dev
->disk
&& dev
->disk
->has_partitions
)
166 if (add_completion (devname
, ",", GRUB_COMPLETION_TYPE_DEVICE
))
171 if (add_completion (devname
, ")", GRUB_COMPLETION_TYPE_DEVICE
))
176 grub_errno
= GRUB_ERR_NONE
;
181 iterate_command (grub_command_t cmd
)
183 if (cmd
->prio
& GRUB_PRIO_LIST_FLAG_ACTIVE
)
185 if (cmd
->flags
& GRUB_COMMAND_FLAG_CMDLINE
)
187 if (add_completion (cmd
->name
, " ", GRUB_COMPLETION_TYPE_COMMAND
))
195 /* Complete a device. */
197 complete_device (void)
199 /* Check if this is a device or a partition. */
200 char *p
= grub_strchr (++current_word
, ',');
205 /* Complete the disk part. */
206 if (grub_disk_dev_iterate (iterate_dev
))
211 /* Complete the partition part. */
213 dev
= grub_device_open (current_word
);
215 grub_errno
= GRUB_ERR_NONE
;
219 if (dev
->disk
&& dev
->disk
->has_partitions
)
221 if (grub_partition_iterate (dev
->disk
, iterate_partition
))
223 grub_device_close (dev
);
228 grub_device_close (dev
);
237 /* Complete a file. */
248 device
= grub_file_get_device_name (current_word
);
249 if (grub_errno
!= GRUB_ERR_NONE
)
252 dev
= grub_device_open (device
);
259 fs
= grub_fs_probe (dev
);
266 dir
= grub_strchr (current_word
, '/');
267 last_dir
= grub_strrchr (current_word
, '/');
272 current_word
= last_dir
+ 1;
274 dir
= grub_strdup (dir
);
281 /* Cut away the filename part. */
282 dirfile
= grub_strrchr (dir
, '/');
285 /* Iterate the directory. */
286 (fs
->dir
) (dev
, dir
, iterate_dir
);
298 current_word
+= grub_strlen (current_word
);
299 match
= grub_strdup ("/");
312 grub_device_close (dev
);
317 /* Complete an argument. */
319 complete_arguments (char *command
)
323 const struct grub_arg_option
*option
;
324 char shortarg
[] = "- ";
326 cmd
= grub_command_find (command
);
328 if (!cmd
|| !(cmd
->flags
& GRUB_COMMAND_FLAG_EXTCMD
))
335 if (add_completion ("-u", " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
338 /* Add the short arguments. */
339 for (option
= ext
->options
; option
->doc
; option
++)
341 if (! option
->shortarg
)
344 shortarg
[1] = option
->shortarg
;
345 if (add_completion (shortarg
, " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
350 /* First add the built-in arguments. */
351 if (add_completion ("--help", " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
353 if (add_completion ("--usage", " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
356 /* Add the long arguments. */
357 for (option
= ext
->options
; option
->doc
; option
++)
360 if (!option
->longarg
)
363 longarg
= grub_malloc (grub_strlen (option
->longarg
));
364 grub_sprintf (longarg
, "--%s", option
->longarg
);
366 if (add_completion (longarg
, " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
378 static grub_parser_state_t
379 get_state (const char *cmdline
)
381 grub_parser_state_t state
= GRUB_PARSER_STATE_TEXT
;
385 state
= grub_parser_cmdline_state (state
, *(cmdline
++), &use
);
390 /* Try to complete the string in BUF. Return the characters that
391 should be added to the string. This command outputs the possible
392 completions by calling HOOK, in that case set RESTORE to 1 so the
393 caller can restore the prompt. */
395 grub_normal_do_completion (char *buf
, int *restore
,
396 void (*hook
) (const char *, grub_completion_type_t
, int))
401 /* Initialize variables. */
409 if (grub_parser_split_cmdline (buf
, 0, &argc
, &argv
))
412 current_word
= argv
[argc
];
414 /* Determine the state the command line is in, depending on the
415 state, it can be determined how to complete. */
416 cmdline_state
= get_state (buf
);
420 /* Complete a command. */
421 if (grub_command_iterate (iterate_command
))
424 else if (*current_word
== '-')
426 if (complete_arguments (buf
))
429 else if (*current_word
== '(' && ! grub_strchr (current_word
, ')'))
431 /* Complete a device. */
432 if (complete_device ())
437 /* Complete a file. */
438 if (complete_file ())
442 /* If more than one match is found those matches will be printed and
443 the prompt should be restored. */
449 /* Return the part that matches. */
459 current_len
= grub_strlen (current_word
);
460 match_len
= grub_strlen (match
);
462 /* Count the number of spaces that have to be escaped. XXX:
463 More than just spaces have to be escaped. */
464 for (escstr
= match
+ current_len
; *escstr
; escstr
++)
468 ret
= grub_malloc (match_len
- current_len
+ grub_strlen (suffix
) + spaces
+ 1);
470 for (escstr
= match
+ current_len
; *escstr
; escstr
++)
472 if (*escstr
== ' ' && cmdline_state
!= GRUB_PARSER_STATE_QUOTE
473 && cmdline_state
!= GRUB_PARSER_STATE_QUOTE
)
475 *(newstr
++) = *escstr
;
480 grub_strcat (ret
, suffix
);
496 grub_errno
= GRUB_ERR_NONE
;