]>
Commit | Line | Data |
---|---|---|
db1771cf | 1 | /* arg.c - argument parser */ |
2 | /* | |
4b13b216 | 3 | * GRUB -- GRand Unified Bootloader |
db1771cf | 4 | * Copyright (C) 2003, 2004 Free Software Foundation, Inc. |
5 | * | |
4b13b216 | 6 | * GRUB is free software; you can redistribute it and/or modify |
db1771cf | 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 | |
4b13b216 | 17 | * along with GRUB; if not, write to the Free Software |
db1771cf | 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 | */ | |
20 | ||
4b13b216 | 21 | #include "grub/arg.h" |
22 | #include "grub/misc.h" | |
23 | #include "grub/mm.h" | |
24 | #include "grub/err.h" | |
25 | #include "grub/normal.h" | |
db1771cf | 26 | |
27 | /* Build in parser for default options. */ | |
4b13b216 | 28 | static const struct grub_arg_option help_options[] = |
db1771cf | 29 | { |
30 | {"help", 'h', 0, "Display help", 0, ARG_TYPE_NONE}, | |
31 | {"usage", 'u', 0, "Show how to use this command", 0, ARG_TYPE_NONE}, | |
32 | {0, 0, 0, 0, 0, 0} | |
33 | }; | |
34 | ||
4b13b216 | 35 | static struct grub_arg_option * |
36 | find_short (const struct grub_arg_option *options, char c) | |
db1771cf | 37 | { |
4b13b216 | 38 | struct grub_arg_option *found = 0; |
39 | auto struct grub_arg_option *fnd_short (const struct grub_arg_option *opt); | |
db1771cf | 40 | |
4b13b216 | 41 | struct grub_arg_option *fnd_short (const struct grub_arg_option *opt) |
db1771cf | 42 | { |
43 | while (opt->doc) | |
44 | { | |
45 | if (opt->shortarg == c) | |
4b13b216 | 46 | return (struct grub_arg_option *) opt; |
db1771cf | 47 | opt++; |
48 | } | |
49 | return 0; | |
50 | } | |
51 | ||
52 | if (options) | |
53 | found = fnd_short (options); | |
54 | if (! found) | |
55 | found = fnd_short (help_options); | |
56 | ||
57 | return found; | |
58 | } | |
59 | ||
60 | static char * | |
61 | find_long_option (char *s) | |
62 | { | |
4b13b216 | 63 | char *argpos = grub_strchr (s, '='); |
db1771cf | 64 | |
65 | if (argpos) | |
66 | { | |
67 | *argpos = '\0'; | |
68 | return ++argpos; | |
69 | } | |
70 | return 0; | |
71 | } | |
72 | ||
4b13b216 | 73 | static struct grub_arg_option * |
74 | find_long (const struct grub_arg_option *options, char *s) | |
db1771cf | 75 | { |
4b13b216 | 76 | struct grub_arg_option *found = 0; |
77 | auto struct grub_arg_option *fnd_long (const struct grub_arg_option *opt); | |
db1771cf | 78 | |
4b13b216 | 79 | struct grub_arg_option *fnd_long (const struct grub_arg_option *opt) |
db1771cf | 80 | { |
81 | while (opt->doc) | |
82 | { | |
4b13b216 | 83 | if (opt->longarg && !grub_strcmp (opt->longarg, s)) |
84 | return (struct grub_arg_option *) opt; | |
db1771cf | 85 | opt++; |
86 | } | |
87 | return 0; | |
88 | } | |
89 | ||
90 | if (options) | |
91 | found = fnd_long (options); | |
92 | if (!found) | |
93 | found = fnd_long (help_options); | |
94 | ||
95 | return found; | |
96 | } | |
97 | ||
98 | static void | |
4b13b216 | 99 | show_usage (grub_command_t cmd) |
db1771cf | 100 | { |
4b13b216 | 101 | grub_printf ("Usage: %s\n", cmd->summary); |
db1771cf | 102 | } |
103 | ||
104 | static void | |
4b13b216 | 105 | show_help (grub_command_t cmd) |
db1771cf | 106 | { |
4b13b216 | 107 | static void showargs (const struct grub_arg_option *opt) |
db1771cf | 108 | { |
109 | for (; opt->doc; opt++) | |
110 | { | |
4b13b216 | 111 | if (opt->shortarg && grub_isgraph (opt->shortarg)) |
112 | grub_printf ("-%c%c ", opt->shortarg, opt->longarg ? ',':' '); | |
db1771cf | 113 | else |
4b13b216 | 114 | grub_printf (" "); |
db1771cf | 115 | if (opt->longarg) |
116 | { | |
4b13b216 | 117 | grub_printf ("--%s", opt->longarg); |
db1771cf | 118 | if (opt->arg) |
4b13b216 | 119 | grub_printf ("=%s", opt->arg); |
db1771cf | 120 | } |
121 | else | |
4b13b216 | 122 | grub_printf ("\t"); |
db1771cf | 123 | |
4b13b216 | 124 | grub_printf ("\t\t%s\n", opt->doc); |
db1771cf | 125 | } |
126 | } | |
127 | ||
128 | show_usage (cmd); | |
4b13b216 | 129 | grub_printf ("%s\n\n", cmd->description); |
db1771cf | 130 | if (cmd->options) |
131 | showargs (cmd->options); | |
132 | showargs (help_options); | |
4b13b216 | 133 | grub_printf ("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT); |
db1771cf | 134 | } |
135 | ||
136 | ||
137 | static int | |
4b13b216 | 138 | parse_option (grub_command_t cmd, int key, char *arg, struct grub_arg_list *usr) |
db1771cf | 139 | { |
140 | switch (key) | |
141 | { | |
142 | case 'h': | |
143 | show_help (cmd); | |
144 | return -1; | |
145 | ||
146 | case 'u': | |
147 | show_usage (cmd); | |
148 | return -1; | |
149 | ||
150 | default: | |
151 | { | |
152 | int found = -1; | |
153 | int i = 0; | |
4b13b216 | 154 | const struct grub_arg_option *opt = cmd->options; |
db1771cf | 155 | |
156 | while (opt->doc) | |
157 | { | |
158 | if (opt->shortarg && key == opt->shortarg) | |
159 | { | |
160 | found = i; | |
161 | break; | |
162 | } | |
163 | opt++; | |
164 | i++; | |
165 | } | |
166 | ||
167 | if (found == -1) | |
168 | return -1; | |
169 | ||
170 | usr[found].set = 1; | |
171 | usr[found].arg = arg; | |
172 | } | |
173 | } | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
178 | int | |
4b13b216 | 179 | grub_arg_parse (grub_command_t cmd, int argc, char **argv, |
180 | struct grub_arg_list *usr, char ***args, int *argnum) | |
db1771cf | 181 | { |
182 | int curarg; | |
183 | char *longarg = 0; | |
184 | int complete = 0; | |
185 | char **argl = 0; | |
186 | int num = 0; | |
4b13b216 | 187 | auto grub_err_t add_arg (char *s); |
db1771cf | 188 | |
4b13b216 | 189 | grub_err_t add_arg (char *s) |
db1771cf | 190 | { |
4b13b216 | 191 | argl = grub_realloc (argl, (++num) * sizeof (char *)); |
db1771cf | 192 | if (! args) |
4b13b216 | 193 | return grub_errno; |
db1771cf | 194 | argl[num - 1] = s; |
195 | return 0; | |
196 | } | |
197 | ||
198 | ||
199 | for (curarg = 0; curarg < argc; curarg++) | |
200 | { | |
201 | char *arg = argv[curarg]; | |
4b13b216 | 202 | struct grub_arg_option *opt; |
db1771cf | 203 | char *option = 0; |
204 | ||
205 | /* No option is used. */ | |
4b13b216 | 206 | if (arg[0] != '-' || grub_strlen (arg) == 1) |
db1771cf | 207 | { |
208 | if (add_arg (arg) != 0) | |
209 | goto fail; | |
210 | ||
211 | continue; | |
212 | } | |
213 | ||
214 | /* One or more short options. */ | |
215 | if (arg[1] != '-') | |
216 | { | |
217 | char *curshort = arg + 1; | |
218 | ||
219 | while (1) | |
220 | { | |
221 | opt = find_short (cmd->options, *curshort); | |
222 | if (!opt) | |
223 | { | |
4b13b216 | 224 | grub_error (GRUB_ERR_BAD_ARGUMENT, |
db1771cf | 225 | "Unknown argument `-%c'\n", *curshort); |
226 | goto fail; | |
227 | } | |
228 | ||
229 | curshort++; | |
230 | ||
231 | /* Parse all arguments here except the last one because | |
232 | it can have an argument value. */ | |
233 | if (*curshort) | |
234 | { | |
4b13b216 | 235 | if (parse_option (cmd, opt->shortarg, 0, usr) || grub_errno) |
db1771cf | 236 | goto fail; |
237 | } | |
238 | else | |
239 | { | |
240 | if (opt->type != ARG_TYPE_NONE) | |
241 | { | |
242 | if (curarg + 1 < argc) | |
243 | { | |
244 | char *nextarg = argv[curarg + 1]; | |
4b13b216 | 245 | if (!(opt->flags & GRUB_ARG_OPTION_OPTIONAL) |
246 | || (grub_strlen (nextarg) < 2 || nextarg[0] != '-')) | |
db1771cf | 247 | option = argv[++curarg]; |
248 | } | |
249 | } | |
250 | break; | |
251 | } | |
252 | } | |
253 | ||
254 | } | |
255 | else /* The argument starts with "--". */ | |
256 | { | |
257 | /* If the argument "--" is used just pass the other | |
258 | arguments. */ | |
4b13b216 | 259 | if (grub_strlen (arg) == 2) |
db1771cf | 260 | { |
261 | for (curarg++; curarg < argc; curarg++) | |
262 | if (add_arg (arg) != 0) | |
263 | goto fail; | |
264 | break; | |
265 | } | |
266 | ||
4b13b216 | 267 | longarg = (char *) grub_strdup (arg); |
db1771cf | 268 | if (! longarg) |
269 | goto fail; | |
270 | ||
271 | option = find_long_option (longarg); | |
272 | arg = longarg; | |
273 | ||
274 | opt = find_long (cmd->options, arg + 2); | |
275 | if (!opt) | |
276 | { | |
4b13b216 | 277 | grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown argument `%s'\n", arg); |
db1771cf | 278 | goto fail; |
279 | } | |
280 | } | |
281 | ||
282 | if (! (opt->type == ARG_TYPE_NONE | |
4b13b216 | 283 | || (!option && (opt->flags & GRUB_ARG_OPTION_OPTIONAL)))) |
db1771cf | 284 | { |
285 | if (!option) | |
286 | { | |
4b13b216 | 287 | grub_error (GRUB_ERR_BAD_ARGUMENT, |
db1771cf | 288 | "Missing mandatory option for `%s'\n", opt->longarg); |
289 | goto fail; | |
290 | } | |
291 | ||
292 | switch (opt->type) | |
293 | { | |
294 | case ARG_TYPE_NONE: | |
295 | /* This will never happen. */ | |
296 | break; | |
297 | ||
298 | case ARG_TYPE_STRING: | |
299 | /* No need to do anything. */ | |
300 | break; | |
301 | ||
302 | case ARG_TYPE_INT: | |
303 | { | |
304 | char *tail; | |
305 | ||
4b13b216 | 306 | grub_strtoul (option, &tail, 0); |
307 | if (tail == 0 || tail == option || *tail != '\0' || grub_errno) | |
db1771cf | 308 | { |
4b13b216 | 309 | grub_error (GRUB_ERR_BAD_ARGUMENT, |
db1771cf | 310 | "The argument `%s' requires an integer.", |
311 | arg); | |
312 | ||
313 | goto fail; | |
314 | } | |
315 | break; | |
316 | } | |
317 | ||
318 | case ARG_TYPE_DEVICE: | |
319 | case ARG_TYPE_DIR: | |
320 | case ARG_TYPE_FILE: | |
321 | case ARG_TYPE_PATHNAME: | |
322 | /* XXX: Not implemented. */ | |
323 | break; | |
324 | } | |
4b13b216 | 325 | if (parse_option (cmd, opt->shortarg, option, usr) || grub_errno) |
db1771cf | 326 | goto fail; |
327 | } | |
328 | else | |
329 | { | |
330 | if (option) | |
331 | { | |
4b13b216 | 332 | grub_error (GRUB_ERR_BAD_ARGUMENT, |
db1771cf | 333 | "A value was assigned to the argument `%s' while it " |
334 | "doesn't require an argument\n", arg); | |
335 | goto fail; | |
336 | } | |
337 | ||
4b13b216 | 338 | if (parse_option (cmd, opt->shortarg, 0, usr) || grub_errno) |
db1771cf | 339 | goto fail; |
340 | } | |
4b13b216 | 341 | grub_free (longarg); |
db1771cf | 342 | longarg = 0; |
343 | } | |
344 | ||
345 | complete = 1; | |
346 | ||
347 | *args = argl; | |
348 | *argnum = num; | |
349 | ||
350 | fail: | |
4b13b216 | 351 | grub_free (longarg); |
db1771cf | 352 | |
353 | return complete; | |
354 | } |