]>
Commit | Line | Data |
---|---|---|
fd6b7a7f KZ |
1 | /* $Id: shhopt.c,v 2.2 1997/07/06 23:11:55 aebr Exp $ */ |
2 | /************************************************************************** | |
3 | * | |
4 | * FILE shhopt.c | |
5 | * | |
6 | * DESCRIPTION Functions for parsing command line arguments. Values | |
7 | * of miscellaneous types may be stored in variables, | |
8 | * or passed to functions as specified. | |
9 | * | |
10 | * REQUIREMENTS Some systems lack the ANSI C -function strtoul. If your | |
11 | * system is one of those, you'll ned to write one yourself, | |
12 | * or get the GNU liberty-library (from prep.ai.mit.edu). | |
13 | * | |
14 | * WRITTEN BY Sverre H. Huseby <sverrehu@ifi.uio.no> | |
15 | * | |
16 | **************************************************************************/ | |
17 | ||
18 | #include <stdio.h> | |
19 | #include <stdlib.h> | |
20 | #include <stdarg.h> | |
21 | #include <string.h> | |
22 | #include <ctype.h> | |
23 | #include <limits.h> | |
24 | #include <errno.h> | |
25 | ||
26 | #include "shhopt.h" | |
27 | ||
28 | /************************************************************************** | |
29 | * * | |
30 | * P R I V A T E D A T A * | |
31 | * * | |
32 | **************************************************************************/ | |
33 | ||
34 | static void optFatalFunc(const char *, ...); | |
35 | static void (*optFatal)(const char *format, ...) = optFatalFunc; | |
36 | ||
37 | ||
38 | ||
39 | /************************************************************************** | |
40 | * * | |
41 | * P R I V A T E F U N C T I O N S * | |
42 | * * | |
43 | **************************************************************************/ | |
44 | ||
45 | /*------------------------------------------------------------------------- | |
46 | * | |
47 | * NAME optFatalFunc | |
48 | * | |
49 | * FUNCTION Show given message and abort the program. | |
50 | * | |
51 | * INPUT format, ... | |
52 | * Arguments used as with printf(). | |
53 | * | |
54 | * RETURNS Never returns. The program is aborted. | |
55 | * | |
56 | */ | |
57 | void optFatalFunc(const char *format, ...) | |
58 | { | |
59 | va_list ap; | |
60 | ||
61 | fflush(stdout); | |
62 | va_start(ap, format); | |
63 | vfprintf(stderr, format, ap); | |
64 | va_end(ap); | |
65 | exit(99); | |
66 | } | |
67 | ||
68 | ||
69 | ||
70 | /*------------------------------------------------------------------------- | |
71 | * | |
72 | * NAME optStructCount | |
73 | * | |
74 | * FUNCTION Get number of options in a optStruct. | |
75 | * | |
76 | * INPUT opt array of possible options. | |
77 | * | |
78 | * RETURNS Number of options in the given array. | |
79 | * | |
80 | * DESCRIPTION Count elements in an optStruct-array. The strcture must | |
81 | * be ended using an element of type OPT_END. | |
82 | * | |
83 | */ | |
84 | static int optStructCount(const optStruct opt[]) | |
85 | { | |
86 | int ret = 0; | |
87 | ||
88 | while (opt[ret].type != OPT_END) | |
89 | ++ret; | |
90 | return ret; | |
91 | } | |
92 | ||
93 | ||
94 | ||
95 | /*------------------------------------------------------------------------- | |
96 | * | |
97 | * NAME optMatch | |
98 | * | |
99 | * FUNCTION Find a matching option. | |
100 | * | |
101 | * INPUT opt array of possible options. | |
102 | * s string to match, without `-' or `--'. | |
103 | * lng match long option, otherwise short. | |
104 | * | |
105 | * RETURNS Index to the option if found, -1 if not found. | |
106 | * | |
107 | * DESCRIPTION Short options are matched from the first character in | |
108 | * the given string. | |
109 | * | |
110 | */ | |
111 | static int optMatch(const optStruct opt[], const char *s, int lng) | |
112 | { | |
113 | int nopt, q, matchlen = 0; | |
114 | char *p; | |
115 | ||
116 | nopt = optStructCount(opt); | |
117 | if (lng) { | |
118 | if ((p = strchr(s, '=')) != NULL) | |
119 | matchlen = p - s; | |
120 | else | |
121 | matchlen = strlen(s); | |
122 | } | |
123 | for (q = 0; q < nopt; q++) { | |
124 | if (lng) { | |
125 | if (!opt[q].longName) | |
126 | continue; | |
127 | if (strncmp(s, opt[q].longName, matchlen) == 0) | |
128 | return q; | |
129 | } else { | |
130 | if (!opt[q].shortName) | |
131 | continue; | |
132 | if (*s == opt[q].shortName) | |
133 | return q; | |
134 | } | |
135 | } | |
136 | return -1; | |
137 | } | |
138 | ||
139 | ||
140 | ||
141 | /*------------------------------------------------------------------------- | |
142 | * | |
143 | * NAME optString | |
144 | * | |
145 | * FUNCTION Return a (static) string with the option name. | |
146 | * | |
147 | * INPUT opt the option to stringify. | |
148 | * lng is it a long option? | |
149 | * | |
150 | * RETURNS Pointer to static string. | |
151 | * | |
152 | */ | |
153 | static char *optString(const optStruct *opt, int lng) | |
154 | { | |
155 | static char ret[31]; | |
156 | ||
157 | if (lng) { | |
158 | strcpy(ret, "--"); | |
159 | strncpy(ret + 2, opt->longName, 28); | |
160 | } else { | |
161 | ret[0] = '-'; | |
162 | ret[1] = opt->shortName; | |
163 | ret[2] = '\0'; | |
164 | } | |
165 | return ret; | |
166 | } | |
167 | ||
168 | ||
169 | ||
170 | /*------------------------------------------------------------------------- | |
171 | * | |
172 | * NAME optNeedsArgument | |
173 | * | |
174 | * FUNCTION Check if an option requires an argument. | |
175 | * | |
176 | * INPUT opt the option to check. | |
177 | * | |
178 | * RETURNS Boolean value. | |
179 | * | |
180 | */ | |
181 | static int optNeedsArgument(const optStruct *opt) | |
182 | { | |
183 | return opt->type == OPT_STRING | |
184 | || opt->type == OPT_INT | |
185 | || opt->type == OPT_UINT | |
186 | || opt->type == OPT_LONG | |
187 | || opt->type == OPT_ULONG; | |
188 | } | |
189 | ||
190 | ||
191 | ||
192 | /*------------------------------------------------------------------------- | |
193 | * | |
194 | * NAME argvRemove | |
195 | * | |
196 | * FUNCTION Remove an entry from an argv-array. | |
197 | * | |
198 | * INPUT argc pointer to number of options. | |
199 | * argv array of option-/argument-strings. | |
200 | * i index of option to remove. | |
201 | * | |
202 | * OUTPUT argc new argument count. | |
203 | * argv array with given argument removed. | |
204 | * | |
205 | */ | |
206 | static void argvRemove(int *argc, char *argv[], int i) | |
207 | { | |
208 | if (i >= *argc) | |
209 | return; | |
210 | while (i++ < *argc) | |
211 | argv[i - 1] = argv[i]; | |
212 | --*argc; | |
213 | } | |
214 | ||
215 | ||
216 | ||
217 | /*------------------------------------------------------------------------- | |
218 | * | |
219 | * NAME optExecute | |
220 | * | |
221 | * FUNCTION Perform the action of an option. | |
222 | * | |
223 | * INPUT opt array of possible options. | |
224 | * arg argument to option, if it applies. | |
225 | * lng was the option given as a long option? | |
226 | * | |
227 | * RETURNS Nothing. Aborts in case of error. | |
228 | * | |
229 | */ | |
230 | void optExecute(const optStruct *opt, char *arg, int lng) | |
231 | { | |
232 | switch (opt->type) { | |
233 | case OPT_FLAG: | |
234 | if (opt->flags & OPT_CALLFUNC) | |
235 | ((void (*)(void)) opt->arg)(); | |
236 | else | |
237 | *((int *) opt->arg) = 1; | |
238 | break; | |
239 | ||
240 | case OPT_STRING: | |
241 | if (opt->flags & OPT_CALLFUNC) | |
242 | ((void (*)(char *)) opt->arg)(arg); | |
243 | else | |
244 | *((char **) opt->arg) = arg; | |
245 | break; | |
246 | ||
247 | case OPT_INT: | |
248 | case OPT_LONG: { | |
249 | long tmp; | |
250 | char *e; | |
251 | ||
252 | tmp = strtol(arg, &e, 10); | |
253 | if (*e) | |
254 | optFatal("invalid number `%s'\n", arg); | |
255 | if (errno == ERANGE | |
256 | || (opt->type == OPT_INT && (tmp > INT_MAX || tmp < INT_MIN))) | |
257 | optFatal("number `%s' to `%s' out of range\n", | |
258 | arg, optString(opt, lng)); | |
259 | if (opt->type == OPT_INT) { | |
260 | if (opt->flags & OPT_CALLFUNC) | |
261 | ((void (*)(int)) opt->arg)((int) tmp); | |
262 | else | |
263 | *((int *) opt->arg) = (int) tmp; | |
264 | } else /* OPT_LONG */ { | |
265 | if (opt->flags & OPT_CALLFUNC) | |
266 | ((void (*)(long)) opt->arg)(tmp); | |
267 | else | |
268 | *((long *) opt->arg) = tmp; | |
269 | } | |
270 | break; | |
271 | } | |
272 | ||
273 | case OPT_UINT: | |
274 | case OPT_ULONG: { | |
275 | unsigned long tmp; | |
276 | char *e; | |
277 | ||
278 | tmp = strtoul(arg, &e, 10); | |
279 | if (*e) | |
280 | optFatal("invalid number `%s'\n", arg); | |
281 | if (errno == ERANGE | |
282 | || (opt->type == OPT_UINT && tmp > UINT_MAX)) | |
283 | optFatal("number `%s' to `%s' out of range\n", | |
284 | arg, optString(opt, lng)); | |
285 | if (opt->type == OPT_UINT) { | |
286 | if (opt->flags & OPT_CALLFUNC) | |
287 | ((void (*)(unsigned)) opt->arg)((unsigned) tmp); | |
288 | else | |
289 | *((unsigned *) opt->arg) = (unsigned) tmp; | |
290 | } else /* OPT_ULONG */ { | |
291 | if (opt->flags & OPT_CALLFUNC) | |
292 | ((void (*)(unsigned long)) opt->arg)(tmp); | |
293 | else | |
294 | *((unsigned long *) opt->arg) = tmp; | |
295 | } | |
296 | break; | |
297 | } | |
298 | ||
299 | default: | |
300 | break; | |
301 | } | |
302 | } | |
303 | ||
304 | ||
305 | ||
306 | /************************************************************************** | |
307 | * * | |
308 | * P U B L I C F U N C T I O N S * | |
309 | * * | |
310 | **************************************************************************/ | |
311 | ||
312 | /*------------------------------------------------------------------------- | |
313 | * | |
314 | * NAME optSetFatalFunc | |
315 | * | |
316 | * FUNCTION Set function used to display error message and exit. | |
317 | * | |
318 | * SYNOPSIS #include "shhmsg.h" | |
319 | * void optSetFatalFunc(void (*f)(const char *, ...)); | |
320 | * | |
321 | * INPUT f function accepting printf()'like parameters, | |
322 | * that _must_ abort the program. | |
323 | * | |
324 | */ | |
325 | void optSetFatalFunc(void (*f)(const char *, ...)) | |
326 | { | |
327 | optFatal = f; | |
328 | } | |
329 | ||
330 | ||
331 | ||
332 | /*------------------------------------------------------------------------- | |
333 | * | |
334 | * NAME optParseOptions | |
335 | * | |
336 | * FUNCTION Parse commandline options. | |
337 | * | |
338 | * SYNOPSIS #include "shhopt.h" | |
339 | * void optParseOptions(int *argc, char *argv[], | |
340 | * const optStruct opt[], int allowNegNum); | |
341 | * | |
342 | * INPUT argc Pointer to number of options. | |
343 | * argv Array of option-/argument-strings. | |
344 | * opt Array of possible options. | |
345 | * allowNegNum | |
346 | * a negative number is not to be taken as | |
347 | * an option. | |
348 | * | |
349 | * OUTPUT argc new argument count. | |
350 | * argv array with arguments removed. | |
351 | * | |
352 | * RETURNS Nothing. Aborts in case of error. | |
353 | * | |
354 | * DESCRIPTION This function checks each option in the argv-array | |
355 | * against strings in the opt-array, and `executes' any | |
356 | * matching action. Any arguments to the options are | |
357 | * extracted and stored in the variables or passed to | |
358 | * functions pointed to by entries in opt. | |
359 | * | |
360 | * Options and arguments used are removed from the argv- | |
361 | * array, and argc is decreased accordingly. | |
362 | * | |
363 | * Any error leads to program abortion. | |
364 | * | |
365 | */ | |
366 | void optParseOptions(int *argc, char *argv[], | |
367 | const optStruct opt[], int allowNegNum) | |
368 | { | |
369 | int ai, /* argv index. */ | |
370 | optarg, /* argv index of option argument, or -1 if none. */ | |
371 | mi, /* Match index in opt. */ | |
372 | done; | |
373 | char *arg, /* Pointer to argument to an option. */ | |
374 | *o, /* pointer to an option character */ | |
375 | *p; | |
376 | ||
377 | /* | |
378 | * Loop through all arguments. | |
379 | */ | |
380 | for (ai = 0; ai < *argc; ) { | |
381 | /* | |
382 | * "--" indicates that the rest of the argv-array does not | |
383 | * contain options. | |
384 | */ | |
385 | if (strcmp(argv[ai], "--") == 0) { | |
386 | argvRemove(argc, argv, ai); | |
387 | break; | |
388 | } | |
389 | ||
390 | if (allowNegNum && argv[ai][0] == '-' && isdigit(argv[ai][1])) { | |
391 | ++ai; | |
392 | continue; | |
393 | } else if (strncmp(argv[ai], "--", 2) == 0) { | |
394 | /* long option */ | |
395 | /* find matching option */ | |
396 | if ((mi = optMatch(opt, argv[ai] + 2, 1)) < 0) | |
397 | optFatal("unrecognized option `%s'\n", argv[ai]); | |
398 | ||
399 | /* possibly locate the argument to this option. */ | |
400 | arg = NULL; | |
401 | if ((p = strchr(argv[ai], '=')) != NULL) | |
402 | arg = p + 1; | |
403 | ||
404 | /* does this option take an argument? */ | |
405 | optarg = -1; | |
406 | if (optNeedsArgument(&opt[mi])) { | |
407 | /* option needs an argument. find it. */ | |
408 | if (!arg) { | |
409 | if ((optarg = ai + 1) == *argc) | |
410 | optFatal("option `%s' requires an argument\n", | |
411 | optString(&opt[mi], 1)); | |
412 | arg = argv[optarg]; | |
413 | } | |
414 | } else { | |
415 | if (arg) | |
416 | optFatal("option `%s' doesn't allow an argument\n", | |
417 | optString(&opt[mi], 1)); | |
418 | } | |
419 | /* perform the action of this option. */ | |
420 | optExecute(&opt[mi], arg, 1); | |
421 | /* remove option and any argument from the argv-array. */ | |
422 | if (optarg >= 0) | |
423 | argvRemove(argc, argv, ai); | |
424 | argvRemove(argc, argv, ai); | |
425 | } else if (*argv[ai] == '-') { | |
426 | /* A dash by itself is not considered an option. */ | |
427 | if (argv[ai][1] == '\0') { | |
428 | ++ai; | |
429 | continue; | |
430 | } | |
431 | /* Short option(s) following */ | |
432 | o = argv[ai] + 1; | |
433 | done = 0; | |
434 | optarg = -1; | |
435 | while (*o && !done) { | |
436 | /* find matching option */ | |
437 | if ((mi = optMatch(opt, o, 0)) < 0) | |
438 | optFatal("unrecognized option `-%c'\n", *o); | |
439 | ||
440 | /* does this option take an argument? */ | |
441 | optarg = -1; | |
442 | arg = NULL; | |
443 | if (optNeedsArgument(&opt[mi])) { | |
444 | /* option needs an argument. find it. */ | |
445 | arg = o + 1; | |
446 | if (!*arg) { | |
447 | if ((optarg = ai + 1) == *argc) | |
448 | optFatal("option `%s' requires an argument\n", | |
449 | optString(&opt[mi], 0)); | |
450 | arg = argv[optarg]; | |
451 | } | |
452 | done = 1; | |
453 | } | |
454 | /* perform the action of this option. */ | |
455 | optExecute(&opt[mi], arg, 0); | |
456 | ++o; | |
457 | } | |
458 | /* remove option and any argument from the argv-array. */ | |
459 | if (optarg >= 0) | |
460 | argvRemove(argc, argv, ai); | |
461 | argvRemove(argc, argv, ai); | |
462 | } else { | |
463 | /* a non-option argument */ | |
464 | ++ai; | |
465 | } | |
466 | } | |
467 | } |