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