initial load of upstream version 1.06.32
[xmlrpc-c] / lib / util / cmdline_parser.c
1 #include "xmlrpc_config.h"  /* prereq for mallocvar.h -- defines __inline__ */
2
3 #include <string.h>
4 #include <stdio.h>
5 #include <errno.h>
6 #include <limits.h>
7
8 #include "bool.h"
9 #include "mallocvar.h"
10 #include "casprintf.h"
11 #include "getoptx.h"
12
13 #include "cmdline_parser.h"
14
15 #define MAXOPTS 100
16
17 struct optionDesc {
18     const char *    name;
19     enum optiontype type;
20     bool            present;
21     union {
22         unsigned int u;
23         int          i;
24         const char * s;
25     } value;
26 };
27
28
29
30 struct cmdlineParserCtl {
31     struct optionDesc * optionDescArray;
32     unsigned int        numOptions;
33     const char **       argumentArray;
34     unsigned int        numArguments;
35 };
36
37
38
39 static struct optionx *
40 createLongOptsArray(struct optionDesc * const optionDescArray,
41                     unsigned int        const numOptions) {
42
43     struct optionx * longopts; 
44
45     MALLOCARRAY(longopts, numOptions+1);
46     if (longopts != NULL) {
47         unsigned int i;
48
49         for (i = 0; i < numOptions; ++i) {
50             longopts[i].name = optionDescArray[i].name;
51             /* If the option takes a value, we say it is optional even
52                though it never is.  That's because if we say it is
53                mandatory, getopt_long_only() pretends it doesn't even
54                recognize the option if the user doesn't give a value.
55                We prefer to generate a meaningful error message when
56                the user omits a required option value.
57             */
58             longopts[i].has_arg = 
59                 optionDescArray[i].type == OPTTYPE_FLAG ? 
60                 no_argument : optional_argument;
61             longopts[i].flag = NULL;
62             longopts[i].val = i;
63         }
64         longopts[numOptions].name = 0;
65         longopts[numOptions].has_arg = 0;
66         longopts[numOptions].flag = 0;
67         longopts[numOptions].val = 0;
68     }
69     return longopts;
70 }
71
72
73
74 static void
75 parseOptionValue(const char *        const optarg, 
76                  struct optionDesc * const optionP,
77                  const char **       const errorP) {
78     
79     switch (optionP->type) {
80     case OPTTYPE_UINT: 
81     case OPTTYPE_INT: {
82         if (optarg == NULL)
83             casprintf(errorP, "Option requires a value");
84         else if (strlen(optarg) == 0)
85             casprintf(errorP, "Numeric option value is null string");
86         else {
87             char * tailptr;
88             long const longvalue = strtol(optarg, &tailptr, 10);
89             if (*tailptr != '\0')
90                 casprintf(errorP, "Non-numeric value "
91                          "for numeric option value: '%s'", optarg);
92             else if (errno == ERANGE || longvalue > INT_MAX)
93                 casprintf(errorP, "Numeric value out of range: %s", optarg);
94             else { 
95                 if (optionP->type == OPTTYPE_UINT) {
96                     if (longvalue < 0)
97                         casprintf(errorP, "Unsigned numeric value is "
98                                   "negative: %ld", longvalue);
99                     else {
100                         *errorP = NULL;
101                         optionP->value.u = (unsigned int) longvalue;
102                     }
103                 } else {
104                     *errorP = NULL;
105                     optionP->value.u = (int) longvalue;
106                 }
107             }
108         }
109     }
110     break;
111     case OPTTYPE_STRING:
112         if (optarg == NULL)
113             casprintf(errorP, "Option requires a value");
114         else {
115             *errorP = NULL;
116             optionP->value.s = strdup(optarg);
117         }
118         break;
119     case OPTTYPE_FLAG:
120         *errorP = NULL;
121         break;
122     }
123 }
124
125
126
127 static void
128 processOption(struct optionDesc * const optionP,
129               const char *        const optarg,
130               const char **       const errorP) {
131
132     const char * error;
133     
134     parseOptionValue(optarg, optionP, &error);
135     if (error)
136         casprintf(errorP, "Error in '%s' option: %s", optionP->name, error);
137     else
138         optionP->present = true;
139 }
140
141
142
143 static void
144 extractArguments(struct cmdlineParserCtl * const cpP,
145                  unsigned int              const argc,
146                  const char **             const argv) {
147     
148     cpP->numArguments = argc - getopt_argstart();
149     MALLOCARRAY(cpP->argumentArray, cpP->numArguments);
150
151     if (cpP->argumentArray == NULL) {
152         fprintf(stderr, "Unable to allocate memory for argument array\n");
153         abort();
154     } else {
155         unsigned int i;
156
157         for (i = 0; i < cpP->numArguments; ++i) {
158             cpP->argumentArray[i] = strdup(argv[getopt_argstart() + i]);
159             if (cpP->argumentArray[i] == NULL) {
160                 fprintf(stderr, "Unable to allocate memory for Argument %u\n",
161                         i);
162                 abort();
163             }
164         }
165     }
166 }
167
168
169
170 void
171 cmd_processOptions(cmdlineParser   const cpP,
172                    int             const argc,
173                    const char **   const argv, 
174                    const char **   const errorP) {
175
176     struct optionx * longopts;
177
178     longopts = createLongOptsArray(cpP->optionDescArray, cpP->numOptions);
179
180     if (longopts == NULL) 
181         casprintf(errorP, "Unable to get memory for longopts array");
182     else {
183         int endOfOptions;
184         unsigned int i;
185
186         *errorP = NULL;
187
188         /* Set up initial assumption:  No options present */
189
190         for (i = 0; i < cpP->numOptions; ++i)
191             cpP->optionDescArray[i].present = false;
192
193         endOfOptions = false;  /* initial value */
194             
195         while (!endOfOptions && !*errorP) {
196             int const opterr0 = 0;
197                 /* Don't let getopt_long_only() print an error message */
198             unsigned int longoptsIndex;
199             const char * unrecognizedOption;
200             const char * optarg;
201             
202             getopt_long_onlyx(argc, (char**) argv, "", longopts, 
203                               &longoptsIndex, opterr0,
204                               &endOfOptions, &optarg, &unrecognizedOption);
205                               
206             if (unrecognizedOption)
207                 casprintf(errorP, "Unrecognized option: '%s'", 
208                           unrecognizedOption);
209             else {
210                 if (!endOfOptions)
211                     processOption(&cpP->optionDescArray[longoptsIndex], optarg,
212                                   errorP);
213             }
214         }
215         if (!*errorP)
216             extractArguments(cpP, argc, argv);
217
218         free(longopts);
219     }
220 }
221
222
223
224 cmdlineParser
225 cmd_createOptionParser(void) {
226
227     struct cmdlineParserCtl * cpP;
228
229     MALLOCVAR(cpP);
230
231     if (cpP != NULL) {
232         struct optionDesc * optionDescArray;
233
234         cpP->numOptions = 0;
235         MALLOCARRAY(optionDescArray, MAXOPTS);
236         if (optionDescArray == NULL) {
237             free(cpP);
238             cpP = NULL;
239         } else 
240             cpP->optionDescArray = optionDescArray;
241     }
242     return cpP;
243 }
244
245
246
247 void
248 cmd_destroyOptionParser(cmdlineParser const cpP) {
249     
250     unsigned int i;
251
252     for (i = 0; i < cpP->numOptions; ++i) {
253         struct optionDesc const option = cpP->optionDescArray[i];
254         if (option.type == OPTTYPE_STRING && option.present)
255             strfree(option.value.s);
256         strfree(option.name);
257     }
258
259     for (i = 0; i < cpP->numArguments; ++i)
260         strfree(cpP->argumentArray[i]);
261
262     free(cpP->optionDescArray);
263     free(cpP);
264 }
265
266
267
268 void
269 cmd_defineOption(cmdlineParser   const cpP,
270                  const char *    const name, 
271                  enum optiontype const type) {
272     
273     if (cpP->numOptions < MAXOPTS) {
274         cpP->optionDescArray[cpP->numOptions].name = strdup(name);
275         cpP->optionDescArray[cpP->numOptions].type = type;
276         
277         ++cpP->numOptions;
278     }
279 }
280
281
282
283 static struct optionDesc *
284 findOptionDesc(struct cmdlineParserCtl * const cpP,
285                const char *              const name) {
286
287     struct optionDesc * retval;
288     unsigned int i;
289
290     retval = NULL;
291
292     for (i = 0; i < cpP->numOptions && !retval; ++i)
293         if (strcmp(cpP->optionDescArray[i].name, name) == 0)
294             retval = &cpP->optionDescArray[i];
295
296     return retval;
297 }
298
299
300
301 int
302 cmd_optionIsPresent(cmdlineParser const cpP,
303                     const char *  const name) {
304
305     struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
306
307     bool present;
308
309     if (!optionDescP) {
310         fprintf(stderr, "cmdlineParser called incorrectly.  "
311                 "optionIsPresent() called for undefined option '%s'\n",
312                 name);
313         abort();
314     } else 
315         present = optionDescP->present;
316
317     return present;
318 }
319
320
321
322 unsigned int
323 cmd_getOptionValueUint(cmdlineParser const cpP,
324                        const char *  const name) {
325
326     struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
327
328     unsigned int retval;
329
330     if (!optionDescP) {
331         fprintf(stderr, "cmdlineParser called incorrectly.  "
332                 "cmd_getOptionValueUint() called for undefined option '%s'\n",
333                 name);
334         abort();
335     } else {
336         if (optionDescP->type != OPTTYPE_UINT) {
337             fprintf(stderr, "cmdlineParser called incorrectly.  "
338                     "cmd_getOptionValueUint() called for non-unsigned integer "
339                     "option '%s'\n", optionDescP->name);
340             abort();
341         } else {
342             if (optionDescP->present) 
343                 retval = optionDescP->value.u;
344             else
345                 retval = 0;
346         }
347     }
348     return retval;
349 }
350
351
352
353 int
354 cmd_getOptionValueInt(cmdlineParser const cpP,
355                       const char *  const name) {
356
357     struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
358
359     int retval;
360
361     if (!optionDescP) {
362         fprintf(stderr, "cmdlineParser called incorrectly.  "
363                 "cmd_getOptionValueInt() called for undefined option '%s'\n",
364                 name);
365         abort();
366     } else {
367         if (optionDescP->type != OPTTYPE_INT) {
368             fprintf(stderr, "cmdlineParser called incorrectly.  "
369                     "cmd_getOptionValueInt() called for non-integer "
370                     "option '%s'\n", optionDescP->name);
371             abort();
372         } else {
373             if (optionDescP->present) 
374                 retval = optionDescP->value.i;
375             else
376                 retval = 0;
377         }
378     }
379
380     return retval;
381 }
382
383
384
385 const char *
386 cmd_getOptionValueString(cmdlineParser const cpP,
387                          const char *  const name) {
388
389     struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
390
391     const char * retval;
392
393     if (!optionDescP) {
394         fprintf(stderr, "cmdlineParser called incorrectly.  "
395                 "cmd_getOptionValueString() called for " 
396                 "undefined option '%s'\n",
397                 name);
398         abort();
399     } else {
400         if (optionDescP->type != OPTTYPE_STRING) {
401             fprintf(stderr, "cmdlineParser called incorrectly.  "
402                     "getOptionValueString() called for non-string "
403                     "option '%s'\n", optionDescP->name);
404             abort();
405         } else {
406             if (optionDescP->present) {
407                 retval = strdup(optionDescP->value.s);
408                 if (retval == NULL) {
409                     fprintf(stderr, 
410                             "out of memory in cmd_getOptionValueString()\n");
411                     abort();
412                 }
413             } else
414                 retval = NULL;
415         }
416     }
417     return retval;
418 }
419
420
421
422 unsigned int 
423 cmd_argumentCount(cmdlineParser const cpP) {
424
425     return cpP->numArguments;
426
427 }
428                   
429
430
431 const char * 
432 cmd_getArgument(cmdlineParser const cpP, 
433                 unsigned int  const argNumber) { 
434
435     const char * retval;
436  
437     if (argNumber >= cpP->numArguments)
438         retval = NULL;
439     else {
440         retval = strdup(cpP->argumentArray[argNumber]);
441
442         if (retval == NULL) {
443             fprintf(stderr, 
444                     "out of memory in cmd_getArgument()\n");
445             abort();
446         }
447     }
448     return retval;
449 }