~darkmuggle-deactivatedaccount/ubuntu/quantal/grub2/fix-872244

« 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, Evan Broder, Mario Limonciello
  • Date: 2010-11-24 13:59:55 UTC
  • mfrom: (1.17.6 upstream) (17.6.15 experimental)
  • Revision ID: james.westby@ubuntu.com-20101124135955-r6ii5sepayr7jt53
Tags: 1.99~20101124-1ubuntu1
[ Colin Watson ]
* Resynchronise with Debian experimental.  Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu.  Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed.  If it is, show the
    menu, otherwise boot immediately.  If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt.  Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - Suppress progress messages as the kernel and initrd load for
    non-recovery kernel menu entries.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Don't generate /boot/grub/device.map during grub-install or
    grub-mkconfig by default.
  - Adjust upgrade version checks for Ubuntu.
  - Don't display "GRUB loading" unless Shift is held down.
  - Adjust versions of grub-doc and grub-legacy-doc conflicts to tolerate
    our backport of the grub-doc split.
  - Fix LVM/RAID probing in the absence of /boot/grub/device.map.
  - Look for .mo files in /usr/share/locale-langpack as well, in
    preference.
  - Make sure GRUB_TIMEOUT isn't quoted unnecessarily.
  - Probe all devices in 'grub-probe --target=drive' if
    /boot/grub/device.map is missing.
  - Build-depend on qemu-kvm rather than qemu-system for grub-pc tests.
  - Use qemu rather than qemu-system-i386.
  - Program vesafb on BIOS systems rather than efifb.
  - Add a grub-rescue-efi-amd64 package containing a rescue CD-ROM image
    for EFI-AMD64.
  - On Wubi, don't ask for an install device, but just update wubildr
    using the diverted grub-install.
  - When embedding the core image in a post-MBR gap, check for and avoid
    sectors matching any of a list of known signatures.
  - Disable video_bochs and video_cirrus on PC BIOS systems, as probing
    PCI space seems to break on some systems.
* Downgrade "ACPI shutdown failed" error to a debug message, since it can
  cause spurious test failures.

[ Evan Broder ]
* Enable lua from grub-extras.
* Incorporate the bitop library into lua.
* Add enum_pci function to grub module in lua.
* Switch back to gfxpayload=keep by default, unless the video hardware
  is known to not support it.

[ Mario Limonciello ]
* Built part_msdos and vfat into bootx64.efi (LP: #677758)

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
}