root/getopt_long.c

Revision 81ac86a15e3940ff0b787698853fb6a6157f800b, 13.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 7 years ago)

initial import with Ecelerity bits removed and some autoconf glue added in. Could certainly use some work on the build/install. Needs shared lib support for multiple platforms

  • Property mode set to 100644
Line 
1 /*-
2  * Copyright (c) 2000 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Dieter Baron and Thomas Klausner.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *        This product includes software developed by the NetBSD
19  *        Foundation, Inc. and its contributors.
20  * 4. Neither the name of The NetBSD Foundation nor the names of its
21  *    contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  */
36
37 #include "jlog_config.h"
38 #include "util.h"
39 #include "getopt_long.h"
40
41 /* We need out logging to take const char *'s */
42
43 #if HAVE_CONFIG_H && !HAVE_GETOPT_LONG && !HAVE_DECL_OPTIND
44 #define REPLACE_GETOPT
45 #endif
46
47 #ifdef REPLACE_GETOPT
48 #ifdef __weak_alias
49 __weak_alias(getopt,_getopt)
50 #endif
51 int     opterr = 1;             /* if error message should be printed */
52 int     optind = 1;             /* index into parent argv vector */
53 int     optopt = '?';           /* character checked for validity */
54 int     optreset;               /* reset getopt */
55 char    *optarg;                /* argument associated with option */
56 #elif HAVE_CONFIG_H && !HAVE_DECL_OPTRESET
57 static int optreset;
58 #endif
59
60 #ifdef __weak_alias
61 __weak_alias(getopt_long,_getopt_long)
62 #endif
63
64 #if !HAVE_GETOPT_LONG
65 #define IGNORE_FIRST    (*options == '-' || *options == '+')
66 #define PRINT_ERROR     ((opterr) && ((*options != ':') \
67                                       || (IGNORE_FIRST && options[1] != ':')))
68 #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
69 #define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
70 /* XXX: GNU ignores PC if *options == '-' */
71 #define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
72
73 /* return values */
74 #define BADCH   (int)'?'
75 #define BADARG          ((IGNORE_FIRST && options[1] == ':') \
76                          || (*options == ':') ? (int)':' : (int)'?')
77 #define INORDER (int)1
78
79 #define EMSG    ""
80
81 static int getopt_internal __P((int, char * const *, const char *));
82 static int gcd __P((int, int));
83 static void permute_args __P((int, int, int, char * const *));
84
85 static char *place = EMSG; /* option letter processing */
86
87 /* XXX: set optreset to 1 rather than these two */
88 static int nonopt_start = -1; /* first non option argument (for permute) */
89 static int nonopt_end = -1;   /* first option after non options (for permute) */
90
91 /* Error messages */
92 static const char recargchar[] = "option requires an argument -- %c";
93 static const char recargstring[] = "option requires an argument -- %s";
94 static const char ambig[] = "ambiguous option -- %.*s";
95 static const char noarg[] = "option doesn't take an argument -- %.*s";
96 static const char illoptchar[] = "unknown option -- %c";
97 static const char illoptstring[] = "unknown option -- %s";
98
99 /*
100  * Compute the greatest common divisor of a and b.
101  */
102 static int
103 gcd(a, b)
104      int a;
105      int b;
106 {
107   int c;
108  
109   c = a % b;
110   while (c != 0) {
111     a = b;
112     b = c;
113     c = a % b;
114   }
115  
116   return b;
117 }
118
119 /*
120  * Exchange the block from nonopt_start to nonopt_end with the block
121  * from nonopt_end to opt_end (keeping the same order of arguments
122  * in each block).
123  */
124 static void
125 permute_args(panonopt_start, panonopt_end, opt_end, nargv)
126      int panonopt_start;
127      int panonopt_end;
128      int opt_end;
129      char * const *nargv;
130 {
131   int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
132   char *swap;
133
134   /*
135    * compute lengths of blocks and number and size of cycles
136    */
137   nnonopts = panonopt_end - panonopt_start;
138   nopts = opt_end - panonopt_end;
139   ncycle = gcd(nnonopts, nopts);
140   cyclelen = (opt_end - panonopt_start) / ncycle;
141
142   for (i = 0; i < ncycle; i++) {
143     cstart = panonopt_end+i;
144     pos = cstart;
145     for (j = 0; j < cyclelen; j++) {
146       if (pos >= panonopt_end)
147         pos -= nnonopts;
148       else
149         pos += nopts;
150       swap = nargv[pos];
151       /* LINTED const cast */
152       ((char **) nargv)[pos] = nargv[cstart];
153       /* LINTED const cast */
154       ((char **)nargv)[cstart] = swap;
155     }
156   }
157 }
158
159 /*
160  * getopt_internal --
161  *      Parse argc/argv argument vector.  Called by user level routines.
162  *  Returns -2 if -- is found (can be long option or end of options marker).
163  */
164 static int
165 getopt_internal(nargc, nargv, options)
166      int nargc;
167      char * const *nargv;
168      const char *options;
169 {
170   char *oli;                            /* option letter list index */
171   int optchar;
172
173   optarg = NULL;
174
175   /*
176    * XXX Some programs (like rsyncd) expect to be able to
177    * XXX re-initialize optind to 0 and have getopt_long(3)
178    * XXX properly function again.  Work around this braindamage.
179    */
180   if (optind == 0)
181     optind = 1;
182
183   if (optreset)
184     nonopt_start = nonopt_end = -1;
185  start:
186   if (optreset || !*place) {            /* update scanning pointer */
187     optreset = 0;
188     if (optind >= nargc) {          /* end of argument vector */
189       place = EMSG;
190       if (nonopt_end != -1) {
191         /* do permutation, if we have to */
192         permute_args(nonopt_start, nonopt_end,
193                      optind, nargv);
194         optind -= nonopt_end - nonopt_start;
195       }
196       else if (nonopt_start != -1) {
197         /*
198          * If we skipped non-options, set optind
199          * to the first of them.
200          */
201         optind = nonopt_start;
202       }
203       nonopt_start = nonopt_end = -1;
204       return -1;
205     }
206     if ((*(place = nargv[optind]) != '-')
207         || (place[1] == '\0')) {    /* found non-option */
208       place = EMSG;
209       if (IN_ORDER) {
210         /*
211          * GNU extension:
212          * return non-option as argument to option 1
213          */
214         optarg = nargv[optind++];
215         return INORDER;
216       }
217       if (!PERMUTE) {
218         /*
219          * if no permutation wanted, stop parsing
220          * at first non-option
221          */
222         return -1;
223       }
224       /* do permutation */
225       if (nonopt_start == -1)
226         nonopt_start = optind;
227       else if (nonopt_end != -1) {
228         permute_args(nonopt_start, nonopt_end,
229                      optind, nargv);
230         nonopt_start = optind -
231           (nonopt_end - nonopt_start);
232         nonopt_end = -1;
233       }
234       optind++;
235       /* process next argument */
236       goto start;
237     }
238     if (nonopt_start != -1 && nonopt_end == -1)
239       nonopt_end = optind;
240     if (place[1] && *++place == '-') {  /* found "--" */
241       place++;
242       return -2;
243     }
244   }
245   if ((optchar = (int)*place++) == (int)':' ||
246       (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
247     /* option letter unknown or ':' */
248     if (!*place)
249       ++optind;
250     if (PRINT_ERROR)
251       fprintf(stderr, (char *)illoptchar, optchar);
252     optopt = optchar;
253     return BADCH;
254   }
255   if (optchar == 'W' && oli[1] == ';') {                /* -W long-option */
256     /* XXX: what if no long options provided (called by getopt)? */
257     if (*place)
258       return -2;
259
260     if (++optind >= nargc) {    /* no arg */
261       place = EMSG;
262       if (PRINT_ERROR)
263         fprintf(stderr, recargchar, optchar);
264       optopt = optchar;
265       return BADARG;
266     } else                              /* white space */
267       place = nargv[optind];
268     /*
269      * Handle -W arg the same as --arg (which causes getopt to
270      * stop parsing).
271      */
272     return -2;
273   }
274   if (*++oli != ':') {                  /* doesn't take argument */
275     if (!*place)
276       ++optind;
277   } else {                              /* takes (optional) argument */
278     optarg = NULL;
279     if (*place)                 /* no white space */
280       optarg = place;
281     /* XXX: disable test for :: if PC? (GNU doesn't) */
282     else if (oli[1] != ':') {   /* arg not optional */
283       if (++optind >= nargc) {  /* no arg */
284         place = EMSG;
285         if (PRINT_ERROR)
286           fprintf(stderr, recargchar, optchar);
287         optopt = optchar;
288         return BADARG;
289       } else
290         optarg = nargv[optind];
291     }
292     place = EMSG;
293     ++optind;
294   }
295   /* dump back option letter */
296   return optchar;
297 }
298
299 #ifdef REPLACE_GETOPT
300 /*
301  * getopt --
302  *      Parse argc/argv argument vector.
303  *
304  * [eventually this will replace the real getopt]
305  */
306 int
307 getopt(nargc, nargv, options)
308      int nargc;
309      char * const *nargv;
310      const char *options;
311 {
312   int retval;
313
314   if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
315     ++optind;
316     /*
317      * We found an option (--), so if we skipped non-options,
318      * we have to permute.
319      */
320     if (nonopt_end != -1) {
321       permute_args(nonopt_start, nonopt_end, optind,
322                    nargv);
323       optind -= nonopt_end - nonopt_start;
324     }
325     nonopt_start = nonopt_end = -1;
326     retval = -1;
327   }
328   return retval;
329 }
330 #endif
331
332 /*
333  * getopt_long --
334  *      Parse argc/argv argument vector.
335  */
336 int
337 getopt_long(nargc, nargv, options, long_options, idx)
338      int nargc;
339      char * const *nargv;
340      const char *options;
341      const struct option *long_options;
342      int *idx;
343 {
344   int retval;
345
346   /* idx may be NULL */
347
348   if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
349     char *current_argv, *has_equal;
350     size_t current_argv_len;
351     int i, match;
352
353     current_argv = place;
354     match = -1;
355
356     optind++;
357     place = EMSG;
358
359     if (*current_argv == '\0') {                /* found "--" */
360       /*
361        * We found an option (--), so if we skipped
362        * non-options, we have to permute.
363        */
364       if (nonopt_end != -1) {
365         permute_args(nonopt_start, nonopt_end,
366                      optind, nargv);
367         optind -= nonopt_end - nonopt_start;
368       }
369       nonopt_start = nonopt_end = -1;
370       return -1;
371     }
372     if ((has_equal = strchr(current_argv, '=')) != NULL) {
373       /* argument found (--option=arg) */
374       current_argv_len = has_equal - current_argv;
375       has_equal++;
376     } else
377       current_argv_len = strlen(current_argv);
378            
379     for (i = 0; long_options[i].name; i++) {
380       /* find matching long option */
381       if (strncmp(current_argv, long_options[i].name,
382                   current_argv_len))
383         continue;
384
385       if (strlen(long_options[i].name) ==
386           (unsigned)current_argv_len) {
387         /* exact match */
388         match = i;
389         break;
390       }
391       if (match == -1)          /* partial match */
392         match = i;
393       else {
394         /* ambiguous abbreviation */
395         if (PRINT_ERROR)
396           fprintf(stderr, ambig, (int)current_argv_len,
397                       current_argv);
398         optopt = 0;
399         return BADCH;
400       }
401     }
402     if (match != -1) {                  /* option found */
403       if (long_options[match].has_arg == no_argument
404           && has_equal) {
405         if (PRINT_ERROR)
406           fprintf(stderr, noarg, (int)current_argv_len,
407                   current_argv);
408         /*
409          * XXX: GNU sets optopt to val regardless of
410          * flag
411          */
412         if (long_options[match].flag == NULL)
413           optopt = long_options[match].val;
414         else
415           optopt = 0;
416         return BADARG;
417       }
418       if (long_options[match].has_arg == required_argument ||
419           long_options[match].has_arg == optional_argument) {
420         if (has_equal)
421           optarg = has_equal;
422         else if (long_options[match].has_arg ==
423                  required_argument) {
424           /*
425            * optional argument doesn't use
426            * next nargv
427            */
428           optarg = nargv[optind++];
429         }
430       }
431       if ((long_options[match].has_arg == required_argument)
432           && (optarg == NULL)) {
433         /*
434          * Missing argument; leading ':'
435          * indicates no erroc should be generated
436          */
437         if (PRINT_ERROR)
438           fprintf(stderr, recargstring, current_argv);
439         /*
440          * XXX: GNU sets optopt to val regardless
441          * of flag
442          */
443         if (long_options[match].flag == NULL)
444           optopt = long_options[match].val;
445         else
446           optopt = 0;
447         --optind;
448         return BADARG;
449       }
450     } else {                    /* unknown option */
451       if (PRINT_ERROR)
452         fprintf(stderr, illoptstring, current_argv);
453       optopt = 0;
454       return BADCH;
455     }
456     if (long_options[match].flag) {
457       *long_options[match].flag = long_options[match].val;
458       retval = 0;
459     } else
460       retval = long_options[match].val;
461     if (idx)
462       *idx = match;
463   }
464   return retval;
465 }
466 #endif /* !GETOPT_LONG */
Note: See TracBrowser for help on using the browser.