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>
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;
107
part_name = grub_partition_get_name (p);
111
name = grub_xasprintf ("%s,%s", disk_name, part_name);
112
grub_free (part_name);
117
ret = add_completion (name, ")", GRUB_COMPLETION_TYPE_PARTITION);
123
iterate_dir (const char *filename, const struct grub_dirhook_info *info)
128
if (cmdline_state == GRUB_PARSER_STATE_DQUOTE)
130
else if (cmdline_state == GRUB_PARSER_STATE_QUOTE)
135
if (add_completion (filename, prefix, GRUB_COMPLETION_TYPE_FILE))
138
else if (grub_strcmp (filename, ".") && grub_strcmp (filename, ".."))
142
fname = grub_xasprintf ("%s/", filename);
143
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
char tmp[grub_strlen (devname) + sizeof (",")];
166
grub_memcpy (tmp, devname, grub_strlen (devname));
168
if (grub_strcmp (devname, current_word) == 0)
170
if (add_completion (devname, ")", GRUB_COMPLETION_TYPE_PARTITION))
174
if (grub_partition_iterate (dev->disk, iterate_partition))
179
grub_memcpy (tmp + grub_strlen (devname), "", sizeof (""));
180
if (add_completion (tmp, "", GRUB_COMPLETION_TYPE_DEVICE))
185
grub_errno = GRUB_ERR_NONE;
189
/* Complete a device. */
191
complete_device (void)
193
/* Check if this is a device or a partition. */
194
char *p = grub_strchr (++current_word, ',');
199
/* Complete the disk part. */
200
if (grub_disk_dev_iterate (iterate_dev))
205
/* Complete the partition part. */
207
dev = grub_device_open (current_word);
209
grub_errno = GRUB_ERR_NONE;
215
if (grub_partition_iterate (dev->disk, iterate_partition))
217
grub_device_close (dev);
222
grub_device_close (dev);
231
/* Complete a file. */
242
device = grub_file_get_device_name (current_word);
243
if (grub_errno != GRUB_ERR_NONE)
246
dev = grub_device_open (device);
253
fs = grub_fs_probe (dev);
260
dir = grub_strchr (current_word + (device ? 2 + grub_strlen (device) : 0),
262
last_dir = grub_strrchr (current_word, '/');
267
current_word = last_dir + 1;
269
dir = grub_strdup (dir);
276
/* Cut away the filename part. */
277
dirfile = grub_strrchr (dir, '/');
280
/* Iterate the directory. */
281
(fs->dir) (dev, dir, iterate_dir);
293
current_word += grub_strlen (current_word);
294
match = grub_strdup ("/");
307
grub_device_close (dev);
312
/* Complete an argument. */
314
complete_arguments (char *command)
318
const struct grub_arg_option *option;
319
char shortarg[] = "- ";
321
cmd = grub_command_find (command);
323
if (!cmd || !(cmd->flags & GRUB_COMMAND_FLAG_EXTCMD))
330
if (add_completion ("-u", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
333
/* Add the short arguments. */
334
for (option = ext->options; option->doc; option++)
336
if (! option->shortarg)
339
shortarg[1] = option->shortarg;
340
if (add_completion (shortarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
345
/* First add the built-in arguments. */
346
if (add_completion ("--help", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
348
if (add_completion ("--usage", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
351
/* Add the long arguments. */
352
for (option = ext->options; option->doc; option++)
355
if (!option->longarg)
358
longarg = grub_xasprintf ("--%s", option->longarg);
362
if (add_completion (longarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
374
static grub_parser_state_t
375
get_state (const char *cmdline)
377
grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
381
state = grub_parser_cmdline_state (state, *(cmdline++), &use);
386
/* Try to complete the string in BUF. Return the characters that
387
should be added to the string. This command outputs the possible
388
completions by calling HOOK, in that case set RESTORE to 1 so the
389
caller can restore the prompt. */
391
grub_normal_do_completion (char *buf, int *restore,
392
void (*hook) (const char *, grub_completion_type_t, int))
397
/* Initialize variables. */
405
if (grub_parser_split_cmdline (buf, 0, &argc, &argv))
411
current_word = argv[argc - 1];
413
if (argc > 1 && ! grub_strcmp (argv[0], "set"))
415
char *equals = grub_strchr (current_word, '=');
417
/* Complete the value of the variable. */
418
current_word = equals + 1;
421
/* Determine the state the command line is in, depending on the
422
state, it can be determined how to complete. */
423
cmdline_state = get_state (buf);
425
if (argc == 1 || argc == 0)
427
/* Complete a command. */
431
if (cmd->prio & GRUB_PRIO_LIST_FLAG_ACTIVE)
433
if (add_completion (cmd->name, " ", GRUB_COMPLETION_TYPE_COMMAND))
438
else if (*current_word == '-')
440
if (complete_arguments (buf))
443
else if (*current_word == '(' && ! grub_strchr (current_word, ')'))
445
/* Complete a device. */
446
if (complete_device ())
451
/* Complete a file. */
452
if (complete_file ())
456
/* If more than one match is found those matches will be printed and
457
the prompt should be restored. */
463
/* Return the part that matches. */
473
current_len = grub_strlen (current_word);
474
match_len = grub_strlen (match);
476
/* Count the number of spaces that have to be escaped. XXX:
477
More than just spaces have to be escaped. */
478
for (escstr = match + current_len; *escstr; escstr++)
482
ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + spaces + 1);
484
for (escstr = match + current_len; *escstr; escstr++)
486
if (*escstr == ' ' && cmdline_state != GRUB_PARSER_STATE_QUOTE
487
&& cmdline_state != GRUB_PARSER_STATE_QUOTE)
489
*(newstr++) = *escstr;
494
grub_strcat (ret, suffix);
515
grub_errno = GRUB_ERR_NONE;