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

« back to all changes in this revision

Viewing changes to normal/completion.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-01-05 15:20:40 UTC
  • mto: (17.3.1 squeeze) (1.9.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20060105152040-b72i5pq1a82z22yi
Tags: upstream-1.92
ImportĀ upstreamĀ versionĀ 1.92

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  Free Software Foundation, Inc.
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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.
 
19
 */
 
20
 
 
21
#include <grub/normal.h>
 
22
#include <grub/misc.h>
 
23
#include <grub/err.h>
 
24
#include <grub/mm.h>
 
25
#include <grub/partition.h>
 
26
#include <grub/disk.h>
 
27
#include <grub/file.h>
 
28
#include <grub/parser.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 *partition_name = grub_partition_get_name (p);
 
104
  char *name;
 
105
  int ret;
 
106
  
 
107
  if (! partition_name)
 
108
    return 1;
 
109
 
 
110
  name = grub_malloc (grub_strlen (disk_name) + 1
 
111
                      + grub_strlen (partition_name) + 1);
 
112
  if (! name)
 
113
    {
 
114
      grub_free (partition_name);
 
115
      return 1;
 
116
    }
 
117
 
 
118
  grub_sprintf (name, "%s,%s", disk_name, partition_name);
 
119
  grub_free (partition_name);
 
120
  
 
121
  ret = add_completion (name, ")", GRUB_COMPLETION_TYPE_PARTITION);
 
122
  grub_free (name);
 
123
  return ret;
 
124
}
 
125
 
 
126
static int
 
127
iterate_dir (const char *filename, int dir)
 
128
{
 
129
  if (! dir)
 
130
    {
 
131
      const char *prefix;
 
132
      if (cmdline_state == GRUB_PARSER_STATE_DQUOTE)
 
133
        prefix = "\" ";
 
134
      else if (cmdline_state == GRUB_PARSER_STATE_QUOTE)
 
135
        prefix = "\' ";
 
136
      else
 
137
        prefix = " ";
 
138
 
 
139
      if (add_completion (filename, prefix, GRUB_COMPLETION_TYPE_FILE))
 
140
        return 1;
 
141
    }
 
142
  else
 
143
    {
 
144
      char fname[grub_strlen (filename) + 2];
 
145
 
 
146
      grub_sprintf (fname, "%s/", filename);
 
147
      if (add_completion (fname, "", GRUB_COMPLETION_TYPE_FILE))
 
148
        return 1;
 
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
      if (dev->disk && dev->disk->has_partitions)
 
165
        {
 
166
          if (add_completion (devname, ",", GRUB_COMPLETION_TYPE_DEVICE))
 
167
            return 1;
 
168
        }
 
169
      else
 
170
        {
 
171
          if (add_completion (devname, ")", GRUB_COMPLETION_TYPE_DEVICE))
 
172
            return 1;
 
173
        }
 
174
    }
 
175
  
 
176
  grub_errno = GRUB_ERR_NONE;
 
177
  return 0;
 
178
}
 
179
 
 
180
static int
 
181
iterate_command (grub_command_t cmd)
 
182
{
 
183
  if (grub_command_find (cmd->name))
 
184
    {
 
185
      if (cmd->flags & GRUB_COMMAND_FLAG_CMDLINE)
 
186
        {
 
187
          if (add_completion (cmd->name, " ", GRUB_COMPLETION_TYPE_COMMAND))
 
188
            return 1;
 
189
        }
 
190
    }
 
191
  
 
192
  return 0;
 
193
}
 
194
 
 
195
/* Complete a device.  */
 
196
static int
 
197
complete_device (void)
 
198
{
 
199
  /* Check if this is a device or a partition.  */
 
200
  char *p = grub_strchr (++current_word, ',');
 
201
  grub_device_t dev;
 
202
  
 
203
  if (! p)
 
204
    {
 
205
      /* Complete the disk part.  */
 
206
      if (grub_disk_dev_iterate (iterate_dev))
 
207
        return 1;
 
208
    }
 
209
  else
 
210
    {
 
211
      /* Complete the partition part.  */
 
212
      *p = '\0';
 
213
      dev = grub_device_open (current_word);
 
214
      *p = ',';
 
215
      grub_errno = GRUB_ERR_NONE;
 
216
      
 
217
      if (dev)
 
218
        {
 
219
          if (dev->disk && dev->disk->has_partitions)
 
220
            {
 
221
              if (grub_partition_iterate (dev->disk, iterate_partition))
 
222
                {
 
223
                  grub_device_close (dev);
 
224
                  return 1;
 
225
                }
 
226
            }
 
227
          
 
228
          grub_device_close (dev);
 
229
        }
 
230
      else
 
231
        return 1;
 
232
    }
 
233
 
 
234
  return 0;
 
235
}
 
236
 
 
237
/* Complete a file.  */
 
238
static int
 
239
complete_file (void)
 
240
{
 
241
  char *device;
 
242
  char *dir;
 
243
  char *last_dir;
 
244
  grub_fs_t fs;
 
245
  grub_device_t dev;
 
246
  int ret = 0;
 
247
  
 
248
  device = grub_file_get_device_name (current_word);
 
249
  if (grub_errno != GRUB_ERR_NONE)
 
250
    return 1;
 
251
  
 
252
  dev = grub_device_open (device);
 
253
  if (! dev)
 
254
    {
 
255
      ret = 1;
 
256
      goto fail;
 
257
    }
 
258
  
 
259
  fs = grub_fs_probe (dev);
 
260
  if (! fs)
 
261
    {
 
262
      ret = 1;
 
263
      goto fail;
 
264
    }
 
265
 
 
266
  dir = grub_strchr (current_word, '/');
 
267
  last_dir = grub_strrchr (current_word, '/');
 
268
  if (dir)
 
269
    {
 
270
      char *dirfile;
 
271
      
 
272
      current_word = last_dir + 1;
 
273
      
 
274
      dir = grub_strdup (dir);
 
275
      if (! dir)
 
276
        {
 
277
          ret = 1;
 
278
          goto fail;
 
279
        }
 
280
      
 
281
      /* Cut away the filename part.  */
 
282
      dirfile = grub_strrchr (dir, '/');
 
283
      dirfile[1] = '\0';
 
284
      
 
285
      /* Iterate the directory.  */
 
286
      (fs->dir) (dev, dir, iterate_dir);
 
287
      
 
288
      grub_free (dir);
 
289
      
 
290
      if (grub_errno)
 
291
        {
 
292
          ret = 1;
 
293
          goto fail;
 
294
        }
 
295
    }
 
296
  else
 
297
    {
 
298
      current_word += grub_strlen (current_word);
 
299
      match = grub_strdup ("/");
 
300
      if (! match)
 
301
        {
 
302
          ret = 1;
 
303
          goto fail;
 
304
        }
 
305
      
 
306
      suffix = "";
 
307
      num_found = 1;
 
308
    }
 
309
 
 
310
 fail:
 
311
  if (dev)
 
312
    grub_device_close (dev);
 
313
  grub_free (device);
 
314
  return ret;
 
315
}
 
316
 
 
317
/* Complete an argument.  */
 
318
static int
 
319
complete_arguments (char *command)
 
320
{
 
321
  grub_command_t cmd;
 
322
  const struct grub_arg_option *option;
 
323
  char shortarg[] = "- ";
 
324
 
 
325
  cmd = grub_command_find (command); 
 
326
 
 
327
  if (!cmd || !cmd->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 = cmd->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 = cmd->options; option->doc; option++)
 
353
    {
 
354
      char *longarg;
 
355
      if (!option->longarg)
 
356
        continue;
 
357
 
 
358
      longarg = grub_malloc (grub_strlen (option->longarg));
 
359
      grub_sprintf (longarg, "--%s", option->longarg);
 
360
 
 
361
      if (add_completion (longarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
 
362
        {
 
363
          grub_free (longarg);
 
364
          return 1;
 
365
        }
 
366
      grub_free (longarg);
 
367
    }
 
368
 
 
369
  return 0;
 
370
}
 
371
 
 
372
 
 
373
static grub_parser_state_t
 
374
get_state (const char *cmdline)
 
375
{
 
376
  grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
 
377
  char use;
 
378
 
 
379
  while (*cmdline)
 
380
    state = grub_parser_cmdline_state (state, *(cmdline++), &use);
 
381
  return state;
 
382
}
 
383
 
 
384
 
 
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.  */
 
389
char *
 
390
grub_normal_do_completion (char *buf, int *restore,
 
391
                           void (*hook) (const char *, grub_completion_type_t, int))
 
392
{
 
393
  int argc;
 
394
  char **argv;
 
395
 
 
396
  /* Initialize variables.  */
 
397
  match = 0;
 
398
  num_found = 0;
 
399
  suffix = "";
 
400
  print_func = hook;
 
401
 
 
402
  *restore = 1;
 
403
 
 
404
  if (grub_parser_split_cmdline (buf, 0, &argc, &argv))
 
405
    return 0;
 
406
 
 
407
  current_word = argv[argc];
 
408
 
 
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);
 
412
 
 
413
  if (argc == 0)
 
414
    {
 
415
      /* Complete a command.  */
 
416
      if (grub_iterate_commands (iterate_command))
 
417
        goto fail;
 
418
    }
 
419
  else if (*current_word == '-')
 
420
    {
 
421
      if (complete_arguments (buf))
 
422
        goto fail;
 
423
    }
 
424
  else if (*current_word == '(' && ! grub_strchr (current_word, ')'))
 
425
    {
 
426
      /* Complete a device.  */
 
427
      if (complete_device ())
 
428
            goto fail;
 
429
    }
 
430
  else
 
431
    {
 
432
      /* Complete a file.  */
 
433
      if (complete_file ())
 
434
        goto fail;
 
435
    }
 
436
 
 
437
  /* If more than one match is found those matches will be printed and
 
438
     the prompt should be restored.  */
 
439
  if (num_found > 1)
 
440
    *restore = 1;
 
441
  else
 
442
    *restore = 0;
 
443
 
 
444
  /* Return the part that matches.  */
 
445
  if (match)
 
446
    {
 
447
      char *ret;
 
448
      char *escstr;
 
449
      char *newstr;
 
450
      int current_len;
 
451
      int match_len;
 
452
      int spaces = 0;
 
453
 
 
454
      current_len = grub_strlen (current_word);
 
455
      match_len = grub_strlen (match);
 
456
 
 
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++)
 
460
        if (*escstr == ' ')
 
461
          spaces++;
 
462
 
 
463
      ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + spaces + 1);
 
464
      newstr = ret;
 
465
      for (escstr = match + current_len; *escstr; escstr++)
 
466
        {
 
467
          if (*escstr == ' ' && cmdline_state != GRUB_PARSER_STATE_QUOTE
 
468
              && cmdline_state != GRUB_PARSER_STATE_QUOTE)
 
469
            *(newstr++) = '\\';
 
470
          *(newstr++) = *escstr;
 
471
        }
 
472
      *newstr = '\0';
 
473
 
 
474
      if (num_found == 1)
 
475
        grub_strcat (ret, suffix);
 
476
      
 
477
      grub_free (match);
 
478
 
 
479
      if (*ret == '\0')
 
480
        {
 
481
          grub_free (ret);
 
482
          return 0;
 
483
        }
 
484
      
 
485
      return ret;
 
486
    }
 
487
 
 
488
 fail:
 
489
  grub_free (match);
 
490
  grub_errno = GRUB_ERR_NONE;
 
491
 
 
492
  return 0;
 
493
}