~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

Viewing changes to grub-core/normal/completion.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* completion.c - complete a command, a disk, a partition or a file */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009  Free Software Foundation, Inc.
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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/>.
 
18
 */
 
19
 
 
20
#include <grub/normal.h>
 
21
#include <grub/misc.h>
 
22
#include <grub/err.h>
 
23
#include <grub/mm.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
 
 
30
/* The current word.  */
 
31
static char *current_word;
 
32
 
 
33
/* The matched string.  */
 
34
static char *match;
 
35
 
 
36
/* The count of candidates.  */
 
37
static int num_found;
 
38
 
 
39
/* The string to be appended.  */
 
40
static const char *suffix;
 
41
 
 
42
/* The callback function to print items.  */
 
43
static void (*print_func) (const char *, grub_completion_type_t, int);
 
44
 
 
45
/* The state the command line is in.  */
 
46
static grub_parser_state_t cmdline_state;
 
47
 
 
48
 
 
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.  */
 
52
static int
 
53
add_completion (const char *completion, const char *extra,
 
54
                grub_completion_type_t type)
 
55
{
 
56
  if (grub_strncmp (current_word, completion, grub_strlen (current_word)) == 0)
 
57
    {
 
58
      num_found++;
 
59
 
 
60
      switch (num_found)
 
61
        {
 
62
        case 1:
 
63
          match = grub_strdup (completion);
 
64
          if (! match)
 
65
            return 1;
 
66
          suffix = extra;
 
67
          break;
 
68
 
 
69
        case 2:
 
70
          if (print_func)
 
71
            print_func (match, type, 0);
 
72
 
 
73
          /* Fall through.  */
 
74
 
 
75
        default:
 
76
          {
 
77
            char *s = match;
 
78
            const char *t = completion;
 
79
 
 
80
            if (print_func)
 
81
              print_func (completion, type, num_found - 1);
 
82
 
 
83
            /* Detect the matched portion.  */
 
84
            while (*s && *t && *s == *t)
 
85
              {
 
86
                s++;
 
87
                t++;
 
88
              }
 
89
 
 
90
            *s = '\0';
 
91
          }
 
92
          break;
 
93
        }
 
94
    }
 
95
 
 
96
  return 0;
 
97
}
 
98
 
 
99
static int
 
100
iterate_partition (grub_disk_t disk, const grub_partition_t p)
 
101
{
 
102
  const char *disk_name = disk->name;
 
103
  char *name;
 
104
  int ret;
 
105
  char *part_name;
 
106
 
 
107
  part_name = grub_partition_get_name (p);
 
108
  if (! part_name)
 
109
    return 1;
 
110
 
 
111
  name = grub_xasprintf ("%s,%s", disk_name, part_name);
 
112
  grub_free (part_name);
 
113
 
 
114
  if (! name)
 
115
    return 1;
 
116
 
 
117
  ret = add_completion (name, ")", GRUB_COMPLETION_TYPE_PARTITION);
 
118
  grub_free (name);
 
119
  return ret;
 
120
}
 
121
 
 
122
static int
 
123
iterate_dir (const char *filename, const struct grub_dirhook_info *info)
 
124
{
 
125
  if (! info->dir)
 
126
    {
 
127
      const char *prefix;
 
128
      if (cmdline_state == GRUB_PARSER_STATE_DQUOTE)
 
129
        prefix = "\" ";
 
130
      else if (cmdline_state == GRUB_PARSER_STATE_QUOTE)
 
131
        prefix = "\' ";
 
132
      else
 
133
        prefix = " ";
 
134
 
 
135
      if (add_completion (filename, prefix, GRUB_COMPLETION_TYPE_FILE))
 
136
        return 1;
 
137
    }
 
138
  else if (grub_strcmp (filename, ".") && grub_strcmp (filename, ".."))
 
139
    {
 
140
      char *fname;
 
141
 
 
142
      fname = grub_xasprintf ("%s/", filename);
 
143
      if (add_completion (fname, "", GRUB_COMPLETION_TYPE_FILE))
 
144
        {
 
145
          grub_free (fname);
 
146
          return 1;
 
147
        }
 
148
      grub_free (fname);
 
149
    }
 
150
 
 
151
  return 0;
 
152
}
 
153
 
 
154
static int
 
155
iterate_dev (const char *devname)
 
156
{
 
157
  grub_device_t dev;
 
158
 
 
159
  /* Complete the partition part.  */
 
160
  dev = grub_device_open (devname);
 
161
 
 
162
  if (dev)
 
163
    {
 
164
      char tmp[grub_strlen (devname) + sizeof (",")];
 
165
 
 
166
      grub_memcpy (tmp, devname, grub_strlen (devname));
 
167
 
 
168
      if (grub_strcmp (devname, current_word) == 0)
 
169
        {
 
170
          if (add_completion (devname, ")", GRUB_COMPLETION_TYPE_PARTITION))
 
171
            return 1;
 
172
 
 
173
          if (dev->disk)
 
174
            if (grub_partition_iterate (dev->disk, iterate_partition))
 
175
              return 1;
 
176
        }
 
177
      else
 
178
        {
 
179
          grub_memcpy (tmp + grub_strlen (devname), "", sizeof (""));
 
180
          if (add_completion (tmp, "", GRUB_COMPLETION_TYPE_DEVICE))
 
181
            return 1;
 
182
        }
 
183
    }
 
184
 
 
185
  grub_errno = GRUB_ERR_NONE;
 
186
  return 0;
 
187
}
 
188
 
 
189
/* Complete a device.  */
 
190
static int
 
191
complete_device (void)
 
192
{
 
193
  /* Check if this is a device or a partition.  */
 
194
  char *p = grub_strchr (++current_word, ',');
 
195
  grub_device_t dev;
 
196
 
 
197
  if (! p)
 
198
    {
 
199
      /* Complete the disk part.  */
 
200
      if (grub_disk_dev_iterate (iterate_dev))
 
201
        return 1;
 
202
    }
 
203
  else
 
204
    {
 
205
      /* Complete the partition part.  */
 
206
      *p = '\0';
 
207
      dev = grub_device_open (current_word);
 
208
      *p = ',';
 
209
      grub_errno = GRUB_ERR_NONE;
 
210
 
 
211
      if (dev)
 
212
        {
 
213
          if (dev->disk)
 
214
            {
 
215
              if (grub_partition_iterate (dev->disk, iterate_partition))
 
216
                {
 
217
                  grub_device_close (dev);
 
218
                  return 1;
 
219
                }
 
220
            }
 
221
 
 
222
          grub_device_close (dev);
 
223
        }
 
224
      else
 
225
        return 1;
 
226
    }
 
227
 
 
228
  return 0;
 
229
}
 
230
 
 
231
/* Complete a file.  */
 
232
static int
 
233
complete_file (void)
 
234
{
 
235
  char *device;
 
236
  char *dir;
 
237
  char *last_dir;
 
238
  grub_fs_t fs;
 
239
  grub_device_t dev;
 
240
  int ret = 0;
 
241
 
 
242
  device = grub_file_get_device_name (current_word);
 
243
  if (grub_errno != GRUB_ERR_NONE)
 
244
    return 1;
 
245
 
 
246
  dev = grub_device_open (device);
 
247
  if (! dev)
 
248
    {
 
249
      ret = 1;
 
250
      goto fail;
 
251
    }
 
252
 
 
253
  fs = grub_fs_probe (dev);
 
254
  if (! fs)
 
255
    {
 
256
      ret = 1;
 
257
      goto fail;
 
258
    }
 
259
 
 
260
  dir = grub_strchr (current_word + (device ? 2 + grub_strlen (device) : 0),
 
261
                     '/');
 
262
  last_dir = grub_strrchr (current_word, '/');
 
263
  if (dir)
 
264
    {
 
265
      char *dirfile;
 
266
 
 
267
      current_word = last_dir + 1;
 
268
 
 
269
      dir = grub_strdup (dir);
 
270
      if (! dir)
 
271
        {
 
272
          ret = 1;
 
273
          goto fail;
 
274
        }
 
275
 
 
276
      /* Cut away the filename part.  */
 
277
      dirfile = grub_strrchr (dir, '/');
 
278
      dirfile[1] = '\0';
 
279
 
 
280
      /* Iterate the directory.  */
 
281
      (fs->dir) (dev, dir, iterate_dir);
 
282
 
 
283
      grub_free (dir);
 
284
 
 
285
      if (grub_errno)
 
286
        {
 
287
          ret = 1;
 
288
          goto fail;
 
289
        }
 
290
    }
 
291
  else
 
292
    {
 
293
      current_word += grub_strlen (current_word);
 
294
      match = grub_strdup ("/");
 
295
      if (! match)
 
296
        {
 
297
          ret = 1;
 
298
          goto fail;
 
299
        }
 
300
 
 
301
      suffix = "";
 
302
      num_found = 1;
 
303
    }
 
304
 
 
305
 fail:
 
306
  if (dev)
 
307
    grub_device_close (dev);
 
308
  grub_free (device);
 
309
  return ret;
 
310
}
 
311
 
 
312
/* Complete an argument.  */
 
313
static int
 
314
complete_arguments (char *command)
 
315
{
 
316
  grub_command_t cmd;
 
317
  grub_extcmd_t ext;
 
318
  const struct grub_arg_option *option;
 
319
  char shortarg[] = "- ";
 
320
 
 
321
  cmd = grub_command_find (command);
 
322
 
 
323
  if (!cmd || !(cmd->flags & GRUB_COMMAND_FLAG_EXTCMD))
 
324
    return 0;
 
325
 
 
326
  ext = cmd->data;
 
327
  if (!ext->options)
 
328
    return 0;
 
329
 
 
330
  if (add_completion ("-u", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
 
331
    return 1;
 
332
 
 
333
  /* Add the short arguments.  */
 
334
  for (option = ext->options; option->doc; option++)
 
335
    {
 
336
      if (! option->shortarg)
 
337
        continue;
 
338
 
 
339
      shortarg[1] = option->shortarg;
 
340
      if (add_completion (shortarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
 
341
        return 1;
 
342
 
 
343
    }
 
344
 
 
345
  /* First add the built-in arguments.  */
 
346
  if (add_completion ("--help", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
 
347
    return 1;
 
348
  if (add_completion ("--usage", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
 
349
    return 1;
 
350
 
 
351
  /* Add the long arguments.  */
 
352
  for (option = ext->options; option->doc; option++)
 
353
    {
 
354
      char *longarg;
 
355
      if (!option->longarg)
 
356
        continue;
 
357
 
 
358
      longarg = grub_xasprintf ("--%s", option->longarg);
 
359
      if (!longarg)
 
360
        return 1;
 
361
 
 
362
      if (add_completion (longarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
 
363
        {
 
364
          grub_free (longarg);
 
365
          return 1;
 
366
        }
 
367
      grub_free (longarg);
 
368
    }
 
369
 
 
370
  return 0;
 
371
}
 
372
 
 
373
 
 
374
static grub_parser_state_t
 
375
get_state (const char *cmdline)
 
376
{
 
377
  grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
 
378
  char use;
 
379
 
 
380
  while (*cmdline)
 
381
    state = grub_parser_cmdline_state (state, *(cmdline++), &use);
 
382
  return state;
 
383
}
 
384
 
 
385
 
 
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.  */
 
390
char *
 
391
grub_normal_do_completion (char *buf, int *restore,
 
392
                           void (*hook) (const char *, grub_completion_type_t, int))
 
393
{
 
394
  int argc;
 
395
  char **argv;
 
396
 
 
397
  /* Initialize variables.  */
 
398
  match = 0;
 
399
  num_found = 0;
 
400
  suffix = "";
 
401
  print_func = hook;
 
402
 
 
403
  *restore = 1;
 
404
 
 
405
  if (grub_parser_split_cmdline (buf, 0, &argc, &argv))
 
406
    return 0;
 
407
 
 
408
  if (argc == 0)
 
409
    current_word = "";
 
410
  else
 
411
    current_word = argv[argc - 1];
 
412
 
 
413
  if (argc > 1 && ! grub_strcmp (argv[0], "set"))
 
414
    {
 
415
      char *equals = grub_strchr (current_word, '=');
 
416
      if (equals)
 
417
        /* Complete the value of the variable.  */
 
418
        current_word = equals + 1;
 
419
    }
 
420
 
 
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);
 
424
 
 
425
  if (argc == 1 || argc == 0)
 
426
    {
 
427
      /* Complete a command.  */
 
428
      grub_command_t cmd;
 
429
      FOR_COMMANDS(cmd)
 
430
      {
 
431
        if (cmd->prio & GRUB_PRIO_LIST_FLAG_ACTIVE)
 
432
          {
 
433
            if (add_completion (cmd->name, " ", GRUB_COMPLETION_TYPE_COMMAND))
 
434
              goto fail;
 
435
          }
 
436
      }
 
437
    }
 
438
  else if (*current_word == '-')
 
439
    {
 
440
      if (complete_arguments (buf))
 
441
        goto fail;
 
442
    }
 
443
  else if (*current_word == '(' && ! grub_strchr (current_word, ')'))
 
444
    {
 
445
      /* Complete a device.  */
 
446
      if (complete_device ())
 
447
            goto fail;
 
448
    }
 
449
  else
 
450
    {
 
451
      /* Complete a file.  */
 
452
      if (complete_file ())
 
453
        goto fail;
 
454
    }
 
455
 
 
456
  /* If more than one match is found those matches will be printed and
 
457
     the prompt should be restored.  */
 
458
  if (num_found > 1)
 
459
    *restore = 1;
 
460
  else
 
461
    *restore = 0;
 
462
 
 
463
  /* Return the part that matches.  */
 
464
  if (match)
 
465
    {
 
466
      char *ret;
 
467
      char *escstr;
 
468
      char *newstr;
 
469
      int current_len;
 
470
      int match_len;
 
471
      int spaces = 0;
 
472
 
 
473
      current_len = grub_strlen (current_word);
 
474
      match_len = grub_strlen (match);
 
475
 
 
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++)
 
479
        if (*escstr == ' ')
 
480
          spaces++;
 
481
 
 
482
      ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + spaces + 1);
 
483
      newstr = ret;
 
484
      for (escstr = match + current_len; *escstr; escstr++)
 
485
        {
 
486
          if (*escstr == ' ' && cmdline_state != GRUB_PARSER_STATE_QUOTE
 
487
              && cmdline_state != GRUB_PARSER_STATE_QUOTE)
 
488
            *(newstr++) = '\\';
 
489
          *(newstr++) = *escstr;
 
490
        }
 
491
      *newstr = '\0';
 
492
 
 
493
      if (num_found == 1)
 
494
        grub_strcat (ret, suffix);
 
495
 
 
496
      if (*ret == '\0')
 
497
        {
 
498
          grub_free (ret);
 
499
          goto fail;
 
500
        }
 
501
 
 
502
      if (argc != 0)
 
503
        grub_free (argv[0]);
 
504
      grub_free (match);
 
505
      return ret;
 
506
    }
 
507
 
 
508
 fail:
 
509
  if (argc != 0)
 
510
    {
 
511
      grub_free (argv[0]);
 
512
      grub_free (argv);
 
513
    }
 
514
  grub_free (match);
 
515
  grub_errno = GRUB_ERR_NONE;
 
516
 
 
517
  return 0;
 
518
}