~ubuntu-branches/ubuntu/trusty/grub2/trusty-updates

« back to all changes in this revision

Viewing changes to grub-core/lib/arg.c

Tags: upstream-1.99~20101122
ImportĀ upstreamĀ versionĀ 1.99~20101122

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* arg.c - argument parser */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2003,2004,2005,2007,2008  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/misc.h>
 
21
#include <grub/mm.h>
 
22
#include <grub/err.h>
 
23
#include <grub/term.h>
 
24
#include <grub/extcmd.h>
 
25
#include <grub/i18n.h>
 
26
 
 
27
/* Built-in parser for default options.  */
 
28
#define SHORT_ARG_HELP  -100
 
29
#define SHORT_ARG_USAGE -101
 
30
 
 
31
static const struct grub_arg_option help_options[] =
 
32
  {
 
33
    {"help", SHORT_ARG_HELP, 0,
 
34
     N_("Display this help and exit."), 0, ARG_TYPE_NONE},
 
35
    {"usage", SHORT_ARG_USAGE, 0,
 
36
     N_("Display the usage of this command and exit."), 0, ARG_TYPE_NONE},
 
37
    {0, 0, 0, 0, 0, 0}
 
38
  };
 
39
 
 
40
static struct grub_arg_option *
 
41
find_short (const struct grub_arg_option *options, char c)
 
42
{
 
43
  struct grub_arg_option *found = 0;
 
44
  auto struct grub_arg_option *fnd_short (const struct grub_arg_option *opt);
 
45
 
 
46
  struct grub_arg_option *fnd_short (const struct grub_arg_option *opt)
 
47
    {
 
48
      while (opt->doc)
 
49
        {
 
50
          if (opt->shortarg == c)
 
51
            return (struct grub_arg_option *) opt;
 
52
          opt++;
 
53
        }
 
54
      return 0;
 
55
    }
 
56
 
 
57
  if (options)
 
58
    found = fnd_short (options);
 
59
 
 
60
  if (! found)
 
61
    {
 
62
      switch (c)
 
63
        {
 
64
        case 'h':
 
65
          found = (struct grub_arg_option *) help_options;
 
66
          break;
 
67
 
 
68
        case 'u':
 
69
          found = (struct grub_arg_option *) (help_options + 1);
 
70
          break;
 
71
 
 
72
        default:
 
73
          break;
 
74
        }
 
75
    }
 
76
 
 
77
  return found;
 
78
}
 
79
 
 
80
static struct grub_arg_option *
 
81
find_long (const struct grub_arg_option *options, const char *s, int len)
 
82
{
 
83
  struct grub_arg_option *found = 0;
 
84
  auto struct grub_arg_option *fnd_long (const struct grub_arg_option *opt);
 
85
 
 
86
  struct grub_arg_option *fnd_long (const struct grub_arg_option *opt)
 
87
    {
 
88
      while (opt->doc)
 
89
        {
 
90
          if (opt->longarg && ! grub_strncmp (opt->longarg, s, len) &&
 
91
              opt->longarg[len] == '\0')
 
92
            return (struct grub_arg_option *) opt;
 
93
          opt++;
 
94
        }
 
95
      return 0;
 
96
    }
 
97
 
 
98
  if (options)
 
99
    found = fnd_long (options);
 
100
 
 
101
  if (! found)
 
102
    found = fnd_long (help_options);
 
103
 
 
104
  return found;
 
105
}
 
106
 
 
107
static void
 
108
show_usage (grub_extcmd_t cmd)
 
109
{
 
110
  grub_printf ("%s %s %s\n", _("Usage:"), cmd->cmd->name, _(cmd->cmd->summary));
 
111
}
 
112
 
 
113
void
 
114
grub_arg_show_help (grub_extcmd_t cmd)
 
115
{
 
116
  auto void showargs (const struct grub_arg_option *opt);
 
117
  int h_is_used = 0;
 
118
  int u_is_used = 0;
 
119
 
 
120
  auto void showargs (const struct grub_arg_option *opt)
 
121
    {
 
122
      for (; opt->doc; opt++)
 
123
        {
 
124
          int spacing = 20;
 
125
 
 
126
          if (opt->shortarg && grub_isgraph (opt->shortarg))
 
127
            grub_printf ("-%c%c ", opt->shortarg, opt->longarg ? ',':' ');
 
128
          else if (opt->shortarg == SHORT_ARG_HELP && ! h_is_used)
 
129
            grub_printf ("-h, ");
 
130
          else if (opt->shortarg == SHORT_ARG_USAGE && ! u_is_used)
 
131
            grub_printf ("-u, ");
 
132
          else
 
133
            grub_printf ("    ");
 
134
 
 
135
          if (opt->longarg)
 
136
            {
 
137
              grub_printf ("--%s", opt->longarg);
 
138
              spacing -= grub_strlen (opt->longarg) + 2;
 
139
 
 
140
              if (opt->arg)
 
141
                {
 
142
                  grub_printf ("=%s", opt->arg);
 
143
                  spacing -= grub_strlen (opt->arg) + 1;
 
144
                }
 
145
            }
 
146
 
 
147
          if (spacing < 0)
 
148
            spacing = 3;
 
149
 
 
150
          while (spacing--)
 
151
            grub_xputs (" ");
 
152
 
 
153
          grub_printf ("%s\n", _(opt->doc));
 
154
 
 
155
          switch (opt->shortarg)
 
156
            {
 
157
            case 'h':
 
158
              h_is_used = 1;
 
159
              break;
 
160
 
 
161
            case 'u':
 
162
              u_is_used = 1;
 
163
              break;
 
164
 
 
165
            default:
 
166
              break;
 
167
            }
 
168
        }
 
169
    }
 
170
 
 
171
  show_usage (cmd);
 
172
  grub_printf ("%s\n\n", _(cmd->cmd->description));
 
173
  if (cmd->options)
 
174
    showargs (cmd->options);
 
175
  showargs (help_options);
 
176
#if 0
 
177
  grub_printf ("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
 
178
#endif
 
179
}
 
180
 
 
181
 
 
182
static int
 
183
parse_option (grub_extcmd_t cmd, int key, char *arg, struct grub_arg_list *usr)
 
184
{
 
185
  switch (key)
 
186
    {
 
187
    case SHORT_ARG_HELP:
 
188
      grub_arg_show_help (cmd);
 
189
      return -1;
 
190
 
 
191
    case SHORT_ARG_USAGE:
 
192
      show_usage (cmd);
 
193
      return -1;
 
194
 
 
195
    default:
 
196
      {
 
197
        int found = -1;
 
198
        int i = 0;
 
199
        const struct grub_arg_option *opt = cmd->options;
 
200
 
 
201
        while (opt->doc)
 
202
          {
 
203
            if (opt->shortarg && key == opt->shortarg)
 
204
              {
 
205
                found = i;
 
206
                break;
 
207
              }
 
208
            opt++;
 
209
            i++;
 
210
          }
 
211
 
 
212
        if (found == -1)
 
213
          return -1;
 
214
 
 
215
        if (opt->flags & GRUB_ARG_OPTION_REPEATABLE)
 
216
          {
 
217
            usr[found].args[usr[found].set++] = arg;
 
218
            usr[found].args[usr[found].set] = NULL;
 
219
          }
 
220
        else
 
221
          {
 
222
            usr[found].set = 1;
 
223
            usr[found].arg = arg;
 
224
          }
 
225
      }
 
226
    }
 
227
 
 
228
  return 0;
 
229
}
 
230
 
 
231
int
 
232
grub_arg_parse (grub_extcmd_t cmd, int argc, char **argv,
 
233
                struct grub_arg_list *usr, char ***args, int *argnum)
 
234
{
 
235
  int curarg;
 
236
  int arglen;
 
237
  char **argl = 0;
 
238
  int num = 0;
 
239
  auto grub_err_t add_arg (char *s);
 
240
 
 
241
  grub_err_t add_arg (char *s)
 
242
    {
 
243
      char **p = argl;
 
244
      argl = grub_realloc (argl, (++num + 1) * sizeof (char *));
 
245
      if (! argl)
 
246
        {
 
247
          grub_free (p);
 
248
          return grub_errno;
 
249
        }
 
250
      argl[num - 1] = s;
 
251
      argl[num] = NULL;
 
252
      return 0;
 
253
    }
 
254
 
 
255
 
 
256
  for (curarg = 0; curarg < argc; curarg++)
 
257
    {
 
258
      char *arg = argv[curarg];
 
259
      struct grub_arg_option *opt;
 
260
      char *option = 0;
 
261
 
 
262
      /* No option is used.  */
 
263
      if ((num && (cmd->cmd->flags & GRUB_COMMAND_OPTIONS_AT_START))
 
264
          || arg[0] != '-' || grub_strlen (arg) == 1)
 
265
        {
 
266
          if (add_arg (arg) != 0)
 
267
            goto fail;
 
268
 
 
269
          continue;
 
270
        }
 
271
 
 
272
      /* One or more short options.  */
 
273
      if (arg[1] != '-')
 
274
        {
 
275
          char *curshort;
 
276
 
 
277
          if (cmd->cmd->flags & GRUB_COMMAND_ACCEPT_DASH)
 
278
            {
 
279
              for (curshort = arg + 1; *curshort; curshort++)
 
280
                if (!find_short (cmd->options, *curshort))
 
281
                  break;
 
282
            
 
283
              if (*curshort)
 
284
                {
 
285
                  if (add_arg (arg) != 0)
 
286
                    goto fail;
 
287
                  continue;
 
288
                }
 
289
            }
 
290
 
 
291
          curshort = arg + 1;
 
292
 
 
293
          while (1)
 
294
            {
 
295
              opt = find_short (cmd->options, *curshort);
 
296
 
 
297
              if (! opt)
 
298
                {
 
299
                  grub_error (GRUB_ERR_BAD_ARGUMENT,
 
300
                              "unknown argument `-%c'", *curshort);
 
301
                  goto fail;
 
302
                }
 
303
 
 
304
              curshort++;
 
305
 
 
306
              /* Parse all arguments here except the last one because
 
307
                 it can have an argument value.  */
 
308
              if (*curshort)
 
309
                {
 
310
                  if (parse_option (cmd, opt->shortarg, 0, usr) || grub_errno)
 
311
                    goto fail;
 
312
                }
 
313
              else
 
314
                {
 
315
                  if (opt->type != ARG_TYPE_NONE)
 
316
                    {
 
317
                      if (curarg + 1 < argc)
 
318
                        {
 
319
                          char *nextarg = argv[curarg + 1];
 
320
                          if (!(opt->flags & GRUB_ARG_OPTION_OPTIONAL)
 
321
                              || (grub_strlen (nextarg) < 2 || nextarg[0] != '-'))
 
322
                            option = argv[++curarg];
 
323
                        }
 
324
                    }
 
325
                  break;
 
326
                }
 
327
            }
 
328
 
 
329
        }
 
330
      else /* The argument starts with "--".  */
 
331
        {
 
332
          /* If the argument "--" is used just pass the other
 
333
             arguments.  */
 
334
          if (grub_strlen (arg) == 2)
 
335
            {
 
336
              for (curarg++; curarg < argc; curarg++)
 
337
                if (add_arg (argv[curarg]) != 0)
 
338
                  goto fail;
 
339
              break;
 
340
            }
 
341
 
 
342
          option = grub_strchr (arg, '=');
 
343
          if (option)
 
344
            {
 
345
              arglen = option - arg - 2;
 
346
              option++;
 
347
            }
 
348
          else
 
349
            arglen = grub_strlen (arg) - 2;
 
350
 
 
351
          opt = find_long (cmd->options, arg + 2, arglen);
 
352
 
 
353
          if (!option && argv[curarg + 1] && argv[curarg + 1][0] != '-'
 
354
              && opt->type != ARG_TYPE_NONE)
 
355
            option = argv[++curarg];
 
356
 
 
357
          if (!opt && (cmd->cmd->flags & GRUB_COMMAND_ACCEPT_DASH))
 
358
            {
 
359
              if (add_arg (arg) != 0)
 
360
                goto fail;
 
361
              continue;
 
362
            }
 
363
 
 
364
          if (! opt)
 
365
            {
 
366
              grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown argument `%s'", arg);
 
367
              goto fail;
 
368
            }
 
369
        }
 
370
 
 
371
      if (! (opt->type == ARG_TYPE_NONE
 
372
             || (! option && (opt->flags & GRUB_ARG_OPTION_OPTIONAL))))
 
373
        {
 
374
          if (! option)
 
375
            {
 
376
              grub_error (GRUB_ERR_BAD_ARGUMENT,
 
377
                          "missing mandatory option for `%s'", opt->longarg);
 
378
              goto fail;
 
379
            }
 
380
 
 
381
          switch (opt->type)
 
382
            {
 
383
            case ARG_TYPE_NONE:
 
384
              /* This will never happen.  */
 
385
              break;
 
386
 
 
387
            case ARG_TYPE_STRING:
 
388
                  /* No need to do anything.  */
 
389
              break;
 
390
 
 
391
            case ARG_TYPE_INT:
 
392
              {
 
393
                char *tail;
 
394
 
 
395
                grub_strtoull (option, &tail, 0);
 
396
                if (tail == 0 || tail == option || *tail != '\0' || grub_errno)
 
397
                  {
 
398
                    grub_error (GRUB_ERR_BAD_ARGUMENT,
 
399
                                "the argument `%s' requires an integer",
 
400
                                arg);
 
401
 
 
402
                    goto fail;
 
403
                  }
 
404
                break;
 
405
              }
 
406
 
 
407
            case ARG_TYPE_DEVICE:
 
408
            case ARG_TYPE_DIR:
 
409
            case ARG_TYPE_FILE:
 
410
            case ARG_TYPE_PATHNAME:
 
411
              /* XXX: Not implemented.  */
 
412
              break;
 
413
            }
 
414
          if (parse_option (cmd, opt->shortarg, option, usr) || grub_errno)
 
415
            goto fail;
 
416
        }
 
417
      else
 
418
        {
 
419
          if (option)
 
420
            {
 
421
              grub_error (GRUB_ERR_BAD_ARGUMENT,
 
422
                          "a value was assigned to the argument `%s' while it "
 
423
                          "doesn't require an argument", arg);
 
424
              goto fail;
 
425
            }
 
426
 
 
427
          if (parse_option (cmd, opt->shortarg, 0, usr) || grub_errno)
 
428
            goto fail;
 
429
        }
 
430
    }
 
431
 
 
432
  *args = argl;
 
433
  *argnum = num;
 
434
  return 1;
 
435
 
 
436
 fail:
 
437
  return 0;
 
438
}
 
439
 
 
440
struct grub_arg_list*
 
441
grub_arg_list_alloc(grub_extcmd_t extcmd, int argc,
 
442
                    char **argv __attribute__((unused)))
 
443
{
 
444
  int i;
 
445
  char **args;
 
446
  unsigned argcnt;
 
447
  struct grub_arg_list *list;
 
448
  const struct grub_arg_option *options;
 
449
 
 
450
  options = extcmd->options;
 
451
  if (! options)
 
452
    return 0;
 
453
 
 
454
  argcnt = 0;
 
455
  for (i = 0; options[i].doc; i++)
 
456
    {
 
457
      if (options[i].flags & GRUB_ARG_OPTION_REPEATABLE)
 
458
        argcnt += (argc + 1) / 2 + 1; /* max possible for any option */
 
459
    }
 
460
 
 
461
  list = grub_zalloc (sizeof (*list) * i + sizeof (char*) * argcnt);
 
462
  if (! list)
 
463
    return 0;
 
464
 
 
465
  args = (char**) (list + i);
 
466
  for (i = 0; options[i].doc; i++)
 
467
    {
 
468
      list[i].set = 0;
 
469
      list[i].arg = 0;
 
470
 
 
471
      if (options[i].flags & GRUB_ARG_OPTION_REPEATABLE)
 
472
        {
 
473
          list[i].args = args;
 
474
          args += argc / 2 + 1;
 
475
        }
 
476
    }
 
477
  return list;
 
478
}