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 Free Software Foundation, Inc.
6
* This program 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 2 of the License, or
9
* (at your option) any later version.
11
* This program 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 this program; if not, write to the Free Software
18
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
#include <grub/normal.h>
22
#include <grub/misc.h>
25
#include <grub/partition.h>
26
#include <grub/disk.h>
27
#include <grub/file.h>
28
#include <grub/parser.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, int dir)
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))
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 (grub_command_find (cmd->name))
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)
322
const struct grub_arg_option *option;
323
char shortarg[] = "- ";
325
cmd = grub_command_find (command);
327
if (!cmd || !cmd->options)
330
if (add_completion ("-u", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
333
/* Add the short arguments. */
334
for (option = cmd->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 = cmd->options; option->doc; option++)
355
if (!option->longarg)
358
longarg = grub_malloc (grub_strlen (option->longarg));
359
grub_sprintf (longarg, "--%s", option->longarg);
361
if (add_completion (longarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
373
static grub_parser_state_t
374
get_state (const char *cmdline)
376
grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
380
state = grub_parser_cmdline_state (state, *(cmdline++), &use);
385
/* Try to complete the string in BUF. Return the characters that
386
should be added to the string. This command outputs the possible
387
completions by calling HOOK, in that case set RESTORE to 1 so the
388
caller can restore the prompt. */
390
grub_normal_do_completion (char *buf, int *restore,
391
void (*hook) (const char *, grub_completion_type_t, int))
396
/* Initialize variables. */
404
if (grub_parser_split_cmdline (buf, 0, &argc, &argv))
407
current_word = argv[argc];
409
/* Determine the state the command line is in, depending on the
410
state, it can be determined how to complete. */
411
cmdline_state = get_state (buf);
415
/* Complete a command. */
416
if (grub_iterate_commands (iterate_command))
419
else if (*current_word == '-')
421
if (complete_arguments (buf))
424
else if (*current_word == '(' && ! grub_strchr (current_word, ')'))
426
/* Complete a device. */
427
if (complete_device ())
432
/* Complete a file. */
433
if (complete_file ())
437
/* If more than one match is found those matches will be printed and
438
the prompt should be restored. */
444
/* Return the part that matches. */
454
current_len = grub_strlen (current_word);
455
match_len = grub_strlen (match);
457
/* Count the number of spaces that have to be escaped. XXX:
458
More than just spaces have to be escaped. */
459
for (escstr = match + current_len; *escstr; escstr++)
463
ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + spaces + 1);
465
for (escstr = match + current_len; *escstr; escstr++)
467
if (*escstr == ' ' && cmdline_state != GRUB_PARSER_STATE_QUOTE
468
&& cmdline_state != GRUB_PARSER_STATE_QUOTE)
470
*(newstr++) = *escstr;
475
grub_strcat (ret, suffix);
490
grub_errno = GRUB_ERR_NONE;