comparison lwlib/lw_cmdline.c @ 4:a4812d57ed13

Started implementing command line parsing in lwlib
author lost@l-w.ca
date Fri, 21 Jan 2011 22:54:48 -0700
parents
children 0e01d1343c02
comparison
equal deleted inserted replaced
3:d4eb3c328a47 4:a4812d57ed13
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 }