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