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
|
|
136 static void lw_cmdline_help(struct lw_cmdline_parser *parser)
|
|
137 {
|
|
138 printf("TODO: help\n");
|
|
139 }
|
|
140
|
|
141 int lw_cmdline_parse(struct lw_cmdline_parser *parser, int argc, char **argv, unsigned flags, int *arg_index, void *input)
|
|
142 {
|
|
143 int i, j, r;
|
|
144 int firstarg;
|
|
145 int nextarg;
|
|
146 char *tstr;
|
|
147 int cch;
|
|
148
|
|
149 /* first, permute the argv array so that all option arguments are at the start */
|
|
150 for (i = 1, firstarg = 1; i < argc; i++)
|
|
151 {
|
|
152 if (argv[i][0] == '-' && argv[i][1])
|
|
153 {
|
|
154 /* have an option arg */
|
|
155 if (firstarg == i)
|
|
156 {
|
|
157 firstarg++;
|
|
158 continue;
|
|
159 }
|
|
160 tstr = argv[i];
|
|
161 for (j = i; j > firstarg; j--)
|
|
162 {
|
|
163 argv[j] = argv[j - 1];
|
|
164 }
|
|
165 argv[firstarg] = tstr;
|
|
166 firstarg++;
|
|
167 if (argv[firstarg - 1][1] == '-' && argv[firstarg - 1][2] == 0)
|
|
168 break;
|
|
169 }
|
|
170 }
|
|
171
|
|
172 /* now start parsing options */
|
|
173 nextarg = firstarg;
|
|
174 i = 1;
|
|
175 cch = 0;
|
|
176 while (i < firstarg)
|
|
177 {
|
|
178 if (cch > 0 && argv[i][cch] == 0)
|
|
179 {
|
|
180 i++;
|
|
181 cch = 0;
|
|
182 continue;
|
|
183 }
|
|
184
|
|
185 if (cch > 0)
|
|
186 goto shortopt;
|
|
187
|
|
188 /* skip the "--" option */
|
|
189 if (argv[i][1] == '-' && argv[i][2] == 0)
|
|
190 break;
|
|
191
|
|
192 if (argv[i][1] == '-')
|
|
193 {
|
|
194 goto longopt;
|
|
195 }
|
|
196
|
|
197 cch = 1;
|
|
198 shortopt:
|
|
199 /* handle a short option here */
|
|
200
|
|
201 /* automatic options */
|
|
202 if (argv[i][cch] == '?')
|
|
203 goto do_help;
|
|
204 if (argv[i][cch] == 'V')
|
|
205 goto do_version;
|
|
206 /* look up key */
|
|
207 for (j = 0; parser -> options[j].name; j++)
|
|
208 if (parser -> options[j].key == argv[i][cch])
|
|
209 break;
|
|
210 cch++;
|
|
211 tstr = argv[i] + cch;
|
|
212 if (!*tstr)
|
|
213 {
|
|
214 if (nextarg < argc)
|
|
215 tstr = argv[nextarg];
|
|
216 else
|
|
217 tstr = NULL;
|
|
218 }
|
|
219 goto common;
|
|
220
|
|
221 longopt:
|
|
222 if (strcmp(argv[i], "--help") == 0)
|
|
223 goto do_help;
|
|
224 if (strcmp(argv[i], "--usage") == 0)
|
|
225 goto do_usage;
|
|
226 if (strcmp(argv[i], "--version") == 0)
|
|
227 goto do_version;
|
|
228 /* look up name */
|
|
229
|
|
230 for (j = 2; argv[i][j] && argv[i][j] != '='; j++)
|
|
231 /* do nothing */ ;
|
|
232 tstr = lw_alloc(j - 1);
|
|
233 strncpy(tstr, argv[i] + 2, j - 2);
|
|
234 tstr[j - 1] = 0;
|
|
235 if (argv[i][j] == '=')
|
|
236 j++;
|
|
237 cch = j;
|
|
238
|
|
239 for (j = 0; parser -> options[j].name; j++)
|
|
240 {
|
|
241 if (strcmp(parser -> options[j].name, tstr) == 0)
|
|
242 break;
|
|
243 }
|
|
244 lw_free(tstr);
|
|
245 tstr = argv[i] + cch;
|
|
246 cch = 0;
|
|
247
|
|
248 common:
|
|
249 /* j will be the offset into the option table when we get here */
|
|
250 /* cch will be zero and tstr will point to the arg if it's a long option */
|
|
251 /* cch will be > 0 and tstr points to the theoretical option, either within */
|
|
252 /* this string or "nextarg" */
|
|
253 if (parser -> options[j].name == NULL)
|
|
254 {
|
|
255 fprintf(stderr, "Unknown option. See %s --usage.\n", argv[0]);
|
|
256 exit(1);
|
|
257 }
|
|
258 if (parser -> options[j].arg)
|
|
259 {
|
|
260 if (tstr && cch && argv[i][cch] == 0)
|
|
261 nextarg++;
|
|
262
|
|
263 if (!*tstr)
|
|
264 tstr = NULL;
|
|
265
|
|
266 if (!tstr && (parser -> options[j].flags & lw_cmdline_opt_optional) == 0)
|
|
267 {
|
|
268 fprintf(stderr, "Option %s requires argument.\n", parser -> options[j].name);
|
|
269 }
|
|
270 }
|
|
271 r = (*(parser -> parser))(parser -> options[j].key, tstr, input);
|
|
272 if (r != 0)
|
|
273 return r;
|
|
274 }
|
|
275 /* handle non-option args */
|
|
276 if (arg_index)
|
|
277 *arg_index = nextarg;
|
|
278 for (i = nextarg; i < argc; i++)
|
|
279 {
|
|
280 r = (*(parser -> parser))(lw_cmdline_key_arg, argv[i], input);
|
|
281 if (r != 0)
|
|
282 return r;
|
|
283 }
|
|
284 r = (*(parser -> parser))(lw_cmdline_key_end, NULL, input);
|
|
285 return r;
|
|
286
|
|
287 do_help:
|
|
288 lw_cmdline_help(parser);
|
|
289 exit(0);
|
|
290
|
|
291 do_version:
|
|
292 printf("%s\n", parser -> program_version);
|
|
293 exit(0);
|
|
294
|
|
295 do_usage:
|
|
296 lw_cmdline_usage(parser, argv[0]);
|
|
297 exit(0);
|
|
298 }
|