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 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>
29 /* The current word. */
30 static char *current_word
;
32 /* The matched string. */
35 /* The count of candidates. */
38 /* The string to be appended. */
39 static const char *suffix
;
41 /* The callback function to print items. */
42 static void (*print_func
) (const char *, grub_completion_type_t
, int);
44 /* The state the command line is in. */
45 static grub_parser_state_t cmdline_state
;
48 /* Add a string to the list of possible completions. COMPLETION is the
49 string that should be added. EXTRA will be appended if COMPLETION
50 matches uniquely. The type TYPE specifies what kind of data is added. */
52 add_completion (const char *completion
, const char *extra
,
53 grub_completion_type_t type
)
55 if (grub_strncmp (current_word
, completion
, grub_strlen (current_word
)) == 0)
62 match
= grub_strdup (completion
);
70 print_func (match
, type
, 0);
77 const char *t
= completion
;
80 print_func (completion
, type
, num_found
- 1);
82 /* Detect the matched portion. */
83 while (*s
&& *t
&& *s
== *t
)
99 iterate_partition (grub_disk_t disk
, const grub_partition_t p
)
101 const char *disk_name
= disk
->name
;
102 char *partition_name
= grub_partition_get_name (p
);
106 if (! partition_name
)
109 name
= grub_malloc (grub_strlen (disk_name
) + 1
110 + grub_strlen (partition_name
) + 1);
113 grub_free (partition_name
);
117 grub_sprintf (name
, "%s,%s", disk_name
, partition_name
);
118 grub_free (partition_name
);
120 ret
= add_completion (name
, ")", GRUB_COMPLETION_TYPE_PARTITION
);
126 iterate_dir (const char *filename
, int dir
)
131 if (cmdline_state
== GRUB_PARSER_STATE_DQUOTE
)
133 else if (cmdline_state
== GRUB_PARSER_STATE_QUOTE
)
138 if (add_completion (filename
, prefix
, GRUB_COMPLETION_TYPE_FILE
))
141 else if (grub_strcmp (filename
, ".") && grub_strcmp (filename
, ".."))
143 char fname
[grub_strlen (filename
) + 2];
145 grub_sprintf (fname
, "%s/", filename
);
146 if (add_completion (fname
, "", GRUB_COMPLETION_TYPE_FILE
))
154 iterate_dev (const char *devname
)
158 /* Complete the partition part. */
159 dev
= grub_device_open (devname
);
163 if (dev
->disk
&& dev
->disk
->has_partitions
)
165 if (add_completion (devname
, ",", GRUB_COMPLETION_TYPE_DEVICE
))
170 if (add_completion (devname
, ")", GRUB_COMPLETION_TYPE_DEVICE
))
175 grub_errno
= GRUB_ERR_NONE
;
180 iterate_command (grub_command_t cmd
)
182 if (grub_command_find (cmd
->name
))
184 if (cmd
->flags
& GRUB_COMMAND_FLAG_CMDLINE
)
186 if (add_completion (cmd
->name
, " ", GRUB_COMPLETION_TYPE_COMMAND
))
194 /* Complete a device. */
196 complete_device (void)
198 /* Check if this is a device or a partition. */
199 char *p
= grub_strchr (++current_word
, ',');
204 /* Complete the disk part. */
205 if (grub_disk_dev_iterate (iterate_dev
))
210 /* Complete the partition part. */
212 dev
= grub_device_open (current_word
);
214 grub_errno
= GRUB_ERR_NONE
;
218 if (dev
->disk
&& dev
->disk
->has_partitions
)
220 if (grub_partition_iterate (dev
->disk
, iterate_partition
))
222 grub_device_close (dev
);
227 grub_device_close (dev
);
236 /* Complete a file. */
247 device
= grub_file_get_device_name (current_word
);
248 if (grub_errno
!= GRUB_ERR_NONE
)
251 dev
= grub_device_open (device
);
258 fs
= grub_fs_probe (dev
);
265 dir
= grub_strchr (current_word
, '/');
266 last_dir
= grub_strrchr (current_word
, '/');
271 current_word
= last_dir
+ 1;
273 dir
= grub_strdup (dir
);
280 /* Cut away the filename part. */
281 dirfile
= grub_strrchr (dir
, '/');
284 /* Iterate the directory. */
285 (fs
->dir
) (dev
, dir
, iterate_dir
);
297 current_word
+= grub_strlen (current_word
);
298 match
= grub_strdup ("/");
311 grub_device_close (dev
);
316 /* Complete an argument. */
318 complete_arguments (char *command
)
321 const struct grub_arg_option
*option
;
322 char shortarg
[] = "- ";
324 cmd
= grub_command_find (command
);
326 if (!cmd
|| !cmd
->options
)
329 if (add_completion ("-u", " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
332 /* Add the short arguments. */
333 for (option
= cmd
->options
; option
->doc
; option
++)
335 if (! option
->shortarg
)
338 shortarg
[1] = option
->shortarg
;
339 if (add_completion (shortarg
, " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
344 /* First add the built-in arguments. */
345 if (add_completion ("--help", " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
347 if (add_completion ("--usage", " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
350 /* Add the long arguments. */
351 for (option
= cmd
->options
; option
->doc
; option
++)
354 if (!option
->longarg
)
357 longarg
= grub_malloc (grub_strlen (option
->longarg
));
358 grub_sprintf (longarg
, "--%s", option
->longarg
);
360 if (add_completion (longarg
, " ", GRUB_COMPLETION_TYPE_ARGUMENT
))
372 static grub_parser_state_t
373 get_state (const char *cmdline
)
375 grub_parser_state_t state
= GRUB_PARSER_STATE_TEXT
;
379 state
= grub_parser_cmdline_state (state
, *(cmdline
++), &use
);
384 /* Try to complete the string in BUF. Return the characters that
385 should be added to the string. This command outputs the possible
386 completions by calling HOOK, in that case set RESTORE to 1 so the
387 caller can restore the prompt. */
389 grub_normal_do_completion (char *buf
, int *restore
,
390 void (*hook
) (const char *, grub_completion_type_t
, int))
395 /* Initialize variables. */
403 if (grub_parser_split_cmdline (buf
, 0, &argc
, &argv
))
406 current_word
= argv
[argc
];
408 /* Determine the state the command line is in, depending on the
409 state, it can be determined how to complete. */
410 cmdline_state
= get_state (buf
);
414 /* Complete a command. */
415 if (grub_iterate_commands (iterate_command
))
418 else if (*current_word
== '-')
420 if (complete_arguments (buf
))
423 else if (*current_word
== '(' && ! grub_strchr (current_word
, ')'))
425 /* Complete a device. */
426 if (complete_device ())
431 /* Complete a file. */
432 if (complete_file ())
436 /* If more than one match is found those matches will be printed and
437 the prompt should be restored. */
443 /* Return the part that matches. */
453 current_len
= grub_strlen (current_word
);
454 match_len
= grub_strlen (match
);
456 /* Count the number of spaces that have to be escaped. XXX:
457 More than just spaces have to be escaped. */
458 for (escstr
= match
+ current_len
; *escstr
; escstr
++)
462 ret
= grub_malloc (match_len
- current_len
+ grub_strlen (suffix
) + spaces
+ 1);
464 for (escstr
= match
+ current_len
; *escstr
; escstr
++)
466 if (*escstr
== ' ' && cmdline_state
!= GRUB_PARSER_STATE_QUOTE
467 && cmdline_state
!= GRUB_PARSER_STATE_QUOTE
)
469 *(newstr
++) = *escstr
;
474 grub_strcat (ret
, suffix
);
490 grub_errno
= GRUB_ERR_NONE
;