4
|
1 /*
|
|
2 lw_cmdline.c
|
|
3
|
|
4 Copyright © 2010 William Astle
|
|
5
|
|
6 This file is part of LWTOOLS.
|
|
7
|
|
8 LWTOOLS is free software: you can redistribute it and/or modify it under the
|
|
9 terms of the GNU General Public License as published by the Free Software
|
|
10 Foundation, either version 3 of the License, or (at your option) any later
|
|
11 version.
|
|
12
|
|
13 This program is distributed in the hope that it will be useful, but WITHOUT
|
|
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
16 more details.
|
|
17
|
|
18 You should have received a copy of the GNU General Public License along with
|
|
19 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
20 */
|
|
21
|
|
22 #include <stdio.h>
|
|
23 #include <stdlib.h>
|
|
24 #include <string.h>
|
|
25
|
|
26 #include "lw_alloc.h"
|
|
27
|
|
28 #define ___lw_cmdline_c_seen___
|
|
29 #include "lw_cmdline.h"
|
|
30
|
|
31 static struct lw_cmdline_options builtin[3] =
|
|
32 {
|
|
33 { "help", '?', 0, 0, "give this help list" },
|
|
34 { "usage", 0, 0, 0, "give a short usage message" },
|
|
35 { "version", 'V', 0, 0, "print program version" }
|
|
36 };
|
|
37
|
|
38 static void lw_cmdline_usage(struct lw_cmdline_parser *parser, char *name)
|
|
39 {
|
|
40 struct lw_cmdline_options **slist, **llist;
|
|
41 int nopt;
|
|
42 int i;
|
|
43 int t;
|
|
44
|
|
45 for (nopt = 0; parser -> options[nopt].name; nopt++)
|
|
46 /* do nothing */ ;
|
|
47
|
|
48 slist = lw_alloc(sizeof(struct lw_cmdline_options *) * (nopt + 3));
|
|
49 llist = lw_alloc(sizeof(struct lw_cmdline_options *) * (nopt + 3));
|
|
50
|
|
51 for (i = 0; i < nopt; i++)
|
|
52 {
|
|
53 slist[i] = &(parser -> options[i]);
|
|
54 llist[i] = &(parser -> options[i]);
|
|
55 }
|
|
56
|
|
57 /* now sort the two lists */
|
|
58
|
|
59 /* now append the automatic options */
|
|
60 slist[nopt] = &(builtin[0]);
|
|
61 slist[nopt + 1] = &(builtin[1]);
|
|
62 slist[nopt + 2] = &(builtin[2]);
|
|
63
|
|
64 llist[nopt] = &(builtin[0]);
|
|
65 llist[nopt + 1] = &(builtin[1]);
|
|
66 llist[nopt + 2] = &(builtin[2]);
|
|
67
|
|
68 /* now show the usage message */
|
|
69 printf("Usage: %s", name);
|
|
70
|
|
71 /* print short options that take no args */
|
|
72 t = 0;
|
|
73 for (i = 0; i < nopt + 3; i++)
|
|
74 {
|
|
75 if (slist[i]->key > 0x20 && slist[i]->key < 0x7f)
|
|
76 {
|
|
77 if (slist[i]->arg == NULL)
|
|
78 {
|
|
79 if (!t)
|
|
80 {
|
|
81 printf(" [-");
|
|
82 t = 1;
|
|
83 }
|
|
84 printf("%c", slist[i]->key);
|
|
85 }
|
|
86 }
|
|
87 }
|
|
88 if (t)
|
|
89 printf("]");
|
|
90
|
|
91 /* print short options that take args */
|
|
92 for (i = 0; i < nopt + 3; i++)
|
|
93 {
|
|
94 if (slist[i]->key > 0x20 && slist[i]->key < 0x7f && slist[i] -> arg)
|
|
95 {
|
|
96 if (slist[i]->flags & lw_cmdline_opt_optional)
|
|
97 {
|
|
98 printf(" [-%c[%s]]", slist[i]->key, slist[i]->arg);
|
|
99 }
|
|
100 else
|
|
101 {
|
|
102 printf(" [-%c %s]", slist[i]->key, slist[i]->arg);
|
|
103 }
|
|
104 }
|
|
105 }
|
|
106
|
|
107 /* print long options */
|
|
108 for (i = 0; i < nopt + 3; i++)
|
|
109 {
|
|
110 if (llist[i]->arg)
|
|
111 {
|
|
112 printf(" [--%s=%s%s%s]",
|
|
113 llist[i] -> name,
|
|
114 (llist[i] -> flags & lw_cmdline_opt_optional) ? "[" : "",
|
|
115 llist[i] -> arg,
|
|
116 (llist[i] -> flags & lw_cmdline_opt_optional) ? "]" : "");
|
|
117 }
|
|
118 else
|
|
119 {
|
|
120 printf(" [--%s]", llist[i] -> name);
|
|
121 }
|
|
122 }
|
|
123
|
|
124 /* print "non option" text */
|
|
125 if (parser -> args_doc)
|
|
126 {
|
|
127 printf(" %s", parser -> args_doc);
|
|
128 }
|
|
129 printf("\n");
|
|
130
|
|
131 /* clean up scratch lists */
|
|
132 lw_free(slist);
|
|
133 lw_free(llist);
|
|
134 }
|
|
135
|
5
|
136 static void lw_cmdline_help(struct lw_cmdline_parser *parser, char *name)
|
4
|
137 {
|
5
|
138 struct lw_cmdline_options **llist;
|
|
139 int nopt;
|
|
140 int i;
|
|
141 int t;
|
|
142 char *tstr;
|
|
143
|
|
144 tstr = parser -> doc;
|
|
145 for (nopt = 0; parser -> options[nopt].name; nopt++)
|
|
146 /* do nothing */ ;
|
|
147
|
|
148 llist = lw_alloc(sizeof(struct lw_cmdline_options *) * (nopt + 3));
|
|
149
|
|
150 for (i = 0; i < nopt; i++)
|
|
151 {
|
|
152 llist[i] = &(parser -> options[i]);
|
|
153 }
|
|
154
|
|
155 /* now sort the list */
|
|
156
|
|
157 /* now append the automatic options */
|
|
158 llist[nopt] = &(builtin[0]);
|
|
159 llist[nopt + 1] = &(builtin[1]);
|
|
160 llist[nopt + 2] = &(builtin[2]);
|
|
161
|
|
162 /* print brief usage */
|
|
163 printf("Usage: %s [OPTION...] %s\n", name, parser -> args_doc ? parser -> args_doc : "");
|
|
164 if (tstr)
|
|
165 {
|
|
166 while (*tstr && *tstr != '\v')
|
|
167 fputc(*tstr++, stdout);
|
|
168 if (*tstr)
|
|
169 tstr++;
|
|
170 }
|
|
171 fputc('\n', stdout);
|
|
172 fputc('\n', stdout);
|
|
173
|
|
174 /* display options - do it the naïve way for now */
|
|
175 for (i = 0; i < (nopt + 3); i++)
|
|
176 {
|
|
177 if (llist[i] -> key > 0x20 && llist[i] -> key < 0x7F)
|
|
178 {
|
|
179 printf(" -%c, ", llist[i] -> key);
|
|
180 }
|
|
181 else
|
|
182 {
|
|
183 printf(" ");
|
|
184 }
|
|
185
|
|
186 printf("--%s", llist[i] -> name);
|
|
187 if (llist[i] -> arg)
|
|
188 {
|
|
189 if (llist[i] -> flags & lw_cmdline_opt_optional)
|
|
190 {
|
|
191 printf("[=%s]", llist[i] -> arg);
|
|
192 }
|
|
193 else
|
|
194 {
|
|
195 printf("=%s", llist[i] -> arg);
|
|
196 }
|
|
197 }
|
|
198 if (llist[i] -> doc)
|
|
199 {
|
|
200 printf("\t\t%s", llist[i] -> doc);
|
|
201 }
|
|
202 fputc('\n', stdout);
|
|
203 }
|
|
204
|
|
205 printf("\nMandatory or optional arguments to long options are also mandatory or optional\nfor any corresponding short options.\n");
|
|
206
|
|
207 if (*tstr)
|
|
208 {
|
|
209 printf("\n%s\n", tstr);
|
|
210 }
|
|
211
|
|
212 /* clean up scratch lists */
|
|
213 lw_free(llist);
|
4
|
214 }
|
|
215
|
|
216 int lw_cmdline_parse(struct lw_cmdline_parser *parser, int argc, char **argv, unsigned flags, int *arg_index, void *input)
|
|
217 {
|
|
218 int i, j, r;
|
|
219 int firstarg;
|
|
220 int nextarg;
|
|
221 char *tstr;
|
|
222 int cch;
|
|
223
|
|
224 /* first, permute the argv array so that all option arguments are at the start */
|
|
225 for (i = 1, firstarg = 1; i < argc; i++)
|
|
226 {
|
|
227 if (argv[i][0] == '-' && argv[i][1])
|
|
228 {
|
|
229 /* have an option arg */
|
|
230 if (firstarg == i)
|
|
231 {
|
|
232 firstarg++;
|
|
233 continue;
|
|
234 }
|
|
235 tstr = argv[i];
|
|
236 for (j = i; j > firstarg; j--)
|
|
237 {
|
|
238 argv[j] = argv[j - 1];
|
|
239 }
|
|
240 argv[firstarg] = tstr;
|
|
241 firstarg++;
|
|
242 if (argv[firstarg - 1][1] == '-' && argv[firstarg - 1][2] == 0)
|
|
243 break;
|
|
244 }
|
|
245 }
|
|
246
|
|
247 /* now start parsing options */
|
|
248 nextarg = firstarg;
|
|
249 i = 1;
|
|
250 cch = 0;
|
|
251 while (i < firstarg)
|
|
252 {
|
|
253 if (cch > 0 && argv[i][cch] == 0)
|
|
254 {
|
|
255 i++;
|
|
256 cch = 0;
|
|
257 continue;
|
|
258 }
|
|
259
|
|
260 if (cch > 0)
|
|
261 goto shortopt;
|
|
262
|
|
263 /* skip the "--" option */
|
|
264 if (argv[i][1] == '-' && argv[i][2] == 0)
|
|
265 break;
|
|
266
|
|
267 if (argv[i][1] == '-')
|
|
268 {
|
|
269 goto longopt;
|
|
270 }
|
|
271
|
|
272 cch = 1;
|
|
273 shortopt:
|
|
274 /* handle a short option here */
|
|
275
|
|
276 /* automatic options */
|
|
277 if (argv[i][cch] == '?')
|
|
278 goto do_help;
|
|
279 if (argv[i][cch] == 'V')
|
|
280 goto do_version;
|
|
281 /* look up key */
|
|
282 for (j = 0; parser -> options[j].name; j++)
|
|
283 if (parser -> options[j].key == argv[i][cch])
|
|
284 break;
|
|
285 cch++;
|
|
286 tstr = argv[i] + cch;
|
|
287 if (!*tstr)
|
|
288 {
|
|
289 if (nextarg < argc)
|
|
290 tstr = argv[nextarg];
|
|
291 else
|
|
292 tstr = NULL;
|
|
293 }
|
|
294 goto common;
|
|
295
|
|
296 longopt:
|
|
297 if (strcmp(argv[i], "--help") == 0)
|
|
298 goto do_help;
|
|
299 if (strcmp(argv[i], "--usage") == 0)
|
|
300 goto do_usage;
|
|
301 if (strcmp(argv[i], "--version") == 0)
|
|
302 goto do_version;
|
|
303 /* look up name */
|
|
304
|
|
305 for (j = 2; argv[i][j] && argv[i][j] != '='; j++)
|
|
306 /* do nothing */ ;
|
|
307 tstr = lw_alloc(j - 1);
|
|
308 strncpy(tstr, argv[i] + 2, j - 2);
|
|
309 tstr[j - 1] = 0;
|
|
310 if (argv[i][j] == '=')
|
|
311 j++;
|
|
312 cch = j;
|
|
313
|
|
314 for (j = 0; parser -> options[j].name; j++)
|
|
315 {
|
|
316 if (strcmp(parser -> options[j].name, tstr) == 0)
|
|
317 break;
|
|
318 }
|
|
319 lw_free(tstr);
|
|
320 tstr = argv[i] + cch;
|
|
321 cch = 0;
|
|
322
|
|
323 common:
|
|
324 /* j will be the offset into the option table when we get here */
|
|
325 /* cch will be zero and tstr will point to the arg if it's a long option */
|
|
326 /* cch will be > 0 and tstr points to the theoretical option, either within */
|
|
327 /* this string or "nextarg" */
|
|
328 if (parser -> options[j].name == NULL)
|
|
329 {
|
|
330 fprintf(stderr, "Unknown option. See %s --usage.\n", argv[0]);
|
|
331 exit(1);
|
|
332 }
|
|
333 if (parser -> options[j].arg)
|
|
334 {
|
|
335 if (tstr && cch && argv[i][cch] == 0)
|
|
336 nextarg++;
|
|
337
|
|
338 if (!*tstr)
|
|
339 tstr = NULL;
|
|
340
|
|
341 if (!tstr && (parser -> options[j].flags & lw_cmdline_opt_optional) == 0)
|
|
342 {
|
|
343 fprintf(stderr, "Option %s requires argument.\n", parser -> options[j].name);
|
|
344 }
|
|
345 }
|
|
346 r = (*(parser -> parser))(parser -> options[j].key, tstr, input);
|
|
347 if (r != 0)
|
|
348 return r;
|
|
349 }
|
|
350 /* handle non-option args */
|
|
351 if (arg_index)
|
|
352 *arg_index = nextarg;
|
|
353 for (i = nextarg; i < argc; i++)
|
|
354 {
|
|
355 r = (*(parser -> parser))(lw_cmdline_key_arg, argv[i], input);
|
|
356 if (r != 0)
|
|
357 return r;
|
|
358 }
|
|
359 r = (*(parser -> parser))(lw_cmdline_key_end, NULL, input);
|
|
360 return r;
|
|
361
|
|
362 do_help:
|
5
|
363 lw_cmdline_help(parser, argv[0]);
|
4
|
364 exit(0);
|
|
365
|
|
366 do_version:
|
|
367 printf("%s\n", parser -> program_version);
|
|
368 exit(0);
|
|
369
|
|
370 do_usage:
|
|
371 lw_cmdline_usage(parser, argv[0]);
|
|
372 exit(0);
|
|
373 }
|