initial load of upstream version 1.06.32
[xmlrpc-c] / lib / util / getoptx.c
1 /* This version of `getopt' appears to the caller like standard Unix getopt()
2    but it behaves differently for the user, since it allows the user
3    to intersperse the options with the other arguments.
4
5    As getopt() works, it permutes the elements of `argv' so that,
6    when it is done, all the options precede everything else.  Thus
7    all application programs are extended to handle flexible argument order.
8
9    Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
10    Then the behavior is completely standard.
11
12    GNU application programs can use a third alternative mode in which
13    they can distinguish the relative order of options and other arguments.  
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19
20 #include "getoptx.h"
21
22 /* Note that on some systems, the header files above declare variables
23    for use with their native getopt facilities, and those variables have
24    the same names as we'd like to use.  So we use things like optargx
25    instead of optarg to avoid the collision.
26 */
27
28 /* For communication from `getopt' to the caller.
29    When `getopt' finds an option that takes an argument,
30    the argument value is returned here.
31 */
32 static char *optargx = 0;
33
34 /* Index in ARGV of the next element to be scanned.
35    This is used for communication to and from the caller
36    and for communication between successive calls to getoptx().
37
38    On entry to getoptx(), zero means this is the first call; initialize.
39
40    When getoptx() returns EOF, this is the index of the first of the
41    non-option elements that the caller should itself scan.
42
43    Otherwise, `optindx' communicates from one call to the next
44    how much of ARGV has been scanned so far.  
45 */
46
47 static int optindx = 0;
48
49 /* The next char to be scanned in the option-element
50    in which the last option character we returned was found.
51    This allows us to pick up the scan where we left off.
52
53    If this is zero, or a null string, it means resume the scan
54    by advancing to the next ARGV-element.  */
55
56 static char *nextchar;
57
58 /* Callers store zero here to inhibit the error message
59    for unrecognized options.  
60 */
61
62 static int opterrx;
63
64 /* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found.
65    Only valid when a long-named option was found. */
66
67 static int option_index;
68
69 struct optionx * _getopt_long_options;
70
71 /* Handle permutation of arguments.  */
72
73 /* Describe the part of ARGV that contains non-options that have
74    been skipped.  `first_nonopt' is the index in ARGV of the first of them;
75    `last_nonopt' is the index after the last of them.  */
76
77 static int first_nonopt;
78 static int last_nonopt;
79
80 /* Exchange two adjacent subsequences of ARGV.
81    One subsequence is elements [first_nonopt,last_nonopt)
82     which contains all the non-options that have been skipped so far.
83    The other is elements [last_nonopt,optindx), which contains all
84     the options processed since those non-options were skipped.
85
86    `first_nonopt' and `last_nonopt' are relocated so that they describe
87     the new indices of the non-options in ARGV after they are moved.  */
88
89 static void
90 exchange(char ** const argv) {
91     unsigned int const nonopts_size = 
92         (last_nonopt - first_nonopt) * sizeof (char *);
93     char **temp = (char **) malloc (nonopts_size);
94
95     if (temp == NULL)
96         abort();
97
98     /* Interchange the two blocks of data in argv.  */
99
100     bcopy (&argv[first_nonopt], temp, nonopts_size);
101     bcopy (&argv[last_nonopt], &argv[first_nonopt],
102            (optindx - last_nonopt) * sizeof (char *));
103     bcopy (temp, &argv[first_nonopt + optindx - last_nonopt],
104            nonopts_size);
105
106     /* Update records for the slots the non-options now occupy.  */
107
108     first_nonopt += (optindx - last_nonopt);
109     last_nonopt = optindx;
110
111     free(temp);
112 }
113 \f
114 /* Scan elements of ARGV (whose length is ARGC) for option characters
115    given in OPTSTRING.
116
117    If an element of ARGV starts with '-', and is not exactly "-" or "--",
118    then it is an option element.  The characters of this element
119    (aside from the initial '-') are option characters.  If getoptx()
120    is called repeatedly, it returns successively each of the option characters
121    from each of the option elements.
122
123    If getoptx() finds another option character, it returns that character,
124    updating `optindx' and `nextchar' so that the next call to getoptx() can
125    resume the scan with the following option character or ARGV-element.
126
127    If there are no more option characters, getoptx() returns `EOF'.
128    Then `optindx' is the index in ARGV of the first ARGV-element
129    that is not an option.  (The ARGV-elements have been permuted
130    so that those that are not options now come last.)
131
132    OPTSTRING is a string containing the legitimate option characters.
133    If an option character is seen that is not listed in OPTSTRING,
134    return '?' after printing an error message.  If you set `opterrx' to
135    zero, the error message is suppressed but we still return '?'.
136
137    If a char in OPTSTRING is followed by a colon, that means it wants an arg,
138    so the following text in the same ARGV-element, or the text of the following
139    ARGV-element, is returned in `optargx'.  Two colons mean an option that
140    wants an optional arg; if there is text in the current ARGV-element,
141    it is returned in `optargx', otherwise `optargx' is set to zero.
142
143    If OPTSTRING starts with `-', it requests a different method of handling the
144    non-option ARGV-elements.  See the comments about RETURN_IN_ORDER, above.
145
146    Long-named options begin with `+' instead of `-'.
147    Their names may be abbreviated as long as the abbreviation is unique
148    or is an exact match for some defined option.  If they have an
149    argument, it follows the option name in the same ARGV-element, separated
150    from the option name by a `=', or else the in next ARGV-element.
151    getoptx() returns 0 when it finds a long-named option.  */
152
153 static int
154 getoptx(int          const argc, 
155         char **      const argv, 
156         const char * const optstring) {
157
158     optargx = 0;
159
160     /* Initialize the internal data when the first call is made.
161        Start processing options with ARGV-element 1 (since ARGV-element 0
162        is the program name); the sequence of previously skipped
163        non-option ARGV-elements is empty.  */
164
165     if (optindx == 0)
166     {
167         first_nonopt = last_nonopt = optindx = 1;
168
169         nextchar = 0;
170
171     }
172
173     if (nextchar == 0 || *nextchar == 0)
174     {
175         /* If we have just processed some options following some non-options,
176                exchange them so that the options come first.  */
177
178         if (first_nonopt != last_nonopt && last_nonopt != optindx)
179             exchange (argv);
180         else if (last_nonopt != optindx)
181             first_nonopt = optindx;
182
183             /* Now skip any additional non-options
184                and extend the range of non-options previously skipped.  */
185
186         while (optindx < argc
187                && (argv[optindx][0] != '-'|| argv[optindx][1] == 0)
188                && (argv[optindx][0] != '+'|| argv[optindx][1] == 0))
189             optindx++;
190         last_nonopt = optindx;
191
192         /* Special ARGV-element `--' means premature end of options.
193            Skip it like a null option,
194            then exchange with previous non-options as if it were an option,
195            then skip everything else like a non-option.  */
196
197         if (optindx != argc && !strcmp (argv[optindx], "--"))
198         {
199             optindx++;
200
201             if (first_nonopt != last_nonopt && last_nonopt != optindx)
202                 exchange (argv);
203             else if (first_nonopt == last_nonopt)
204                 first_nonopt = optindx;
205             last_nonopt = argc;
206
207             optindx = argc;
208         }
209
210         /* If we have done all the ARGV-elements, stop the scan
211            and back over any non-options that we skipped and permuted.  */
212
213         if (optindx == argc)
214         {
215             /* Set the next-arg-index to point at the non-options
216                that we previously skipped, so the caller will digest them.  */
217             if (first_nonopt != last_nonopt)
218                 optindx = first_nonopt;
219             return EOF;
220         }
221      
222         /* If we have come to a non-option and did not permute it,
223            either stop the scan or describe it to the caller and pass
224            it by.  
225         */
226
227         if ((argv[optindx][0] != '-' || argv[optindx][1] == 0)
228             && (argv[optindx][0] != '+' || argv[optindx][1] == 0))
229         {
230             optargx = argv[optindx++];
231             return 1;
232         }
233
234         /* We have found another option-ARGV-element.
235            Start decoding its characters.  */
236
237         nextchar = argv[optindx] + 1;
238     }
239
240     if ((argv[optindx][0] == '+' || (argv[optindx][0] == '-'))
241         )
242     {
243         struct optionx *p;
244         char *s = nextchar;
245         int exact = 0;
246         int ambig = 0;
247         struct optionx * pfound;
248         int indfound;
249
250         while (*s && *s != '=') s++;
251
252         indfound = 0;  /* quite compiler warning */
253
254         /* Test all options for either exact match or abbreviated matches.  */
255         for (p = _getopt_long_options, option_index = 0, pfound = NULL;
256              p->name; 
257              p++, option_index++)
258             if (!strncmp (p->name, nextchar, s - nextchar))
259             {
260                 if ((unsigned int)(s - nextchar) == strlen (p->name))
261                 {
262                     /* Exact match found.  */
263                     pfound = p;
264                     indfound = option_index;
265                     exact = 1;
266                     break;
267                 }
268                 else if (!pfound)
269                 {
270                     /* First nonexact match found.  */
271                     pfound = p;
272                     indfound = option_index;
273                 }
274                 else
275                     /* Second nonexact match found.  */
276                     ambig = 1;
277             }
278
279         if (ambig && !exact)
280         {
281             fprintf (stderr, "%s: option `%s' is ambiguous\n",
282                      argv[0], argv[optindx]);
283             nextchar += strlen (nextchar);               
284             return '?';
285         }
286
287         if (pfound)
288         {
289             option_index = indfound;
290             optindx++;
291             if (*s)
292             {
293                 if (pfound->has_arg > 0)
294                     optargx = s + 1;
295                 else
296                 {
297                     fprintf (stderr,
298                              "%s: option `%c%s' doesn't allow an argument\n",
299                              argv[0], argv[optindx - 1][0], pfound->name);
300                     nextchar += strlen (nextchar);               
301                     return '?';
302                 }
303             }
304             else if (pfound->has_arg)
305             {
306                 if (optindx < argc)
307                     optargx = argv[optindx++];
308                 else if (pfound->has_arg != 2)
309                 {
310                     fprintf (stderr, "%s: option `%s' requires an argument\n",
311                              argv[0], argv[optindx - 1]);
312                     nextchar += strlen (nextchar);           
313                     return '?';
314                 }
315             }
316             nextchar += strlen (nextchar);
317             if (pfound->flag)
318                 *(pfound->flag) = pfound->val;
319             return 0;
320         }
321         if (argv[optindx][0] == '+' || index (optstring, *nextchar) == 0)
322         {
323             if (opterrx != 0)
324                 fprintf (stderr, "%s: unrecognized option `%c%s'\n",
325                          argv[0], argv[optindx][0], nextchar);
326             nextchar += strlen (nextchar);           
327             return '?';
328         }
329     }
330  
331     /* Look at and handle the next option-character.  */
332
333     {
334         char c = *nextchar++;
335         char *temp = index (optstring, c);
336
337         /* Increment `optindx' when we start to process its last character.  */
338         if (*nextchar == 0)
339             optindx++;
340
341         if (temp == 0 || c == ':')
342         {
343             if (opterrx != 0)
344             {
345                 if (c < 040 || c >= 0177)
346                     fprintf (stderr, "%s: unrecognized option, "
347                              "character code 0%o\n",
348                              argv[0], c);
349                 else
350                     fprintf (stderr, "%s: unrecognized option `-%c'\n",
351                              argv[0], c);
352             }
353             return '?';
354         }
355         if (temp[1] == ':')
356         {
357             if (temp[2] == ':')
358             {
359                 /* This is an option that accepts an argument optionally.  */
360                 if (*nextchar != 0)
361                 {
362                     optargx = nextchar;
363                     optindx++;
364                 }
365                 else
366                     optargx = 0;
367                 nextchar = 0;
368             }
369             else
370             {
371                 /* This is an option that requires an argument.  */
372                 if (*nextchar != 0)
373                 {
374                     optargx = nextchar;
375                     /* If we end this ARGV-element by taking the rest
376                        as an arg, we must advance to the next element
377                        now.  
378                     */
379                     optindx++;
380                 }
381                 else if (optindx == argc)
382                 {
383                     if (opterrx != 0)
384                         fprintf (stderr,
385                                  "%s: option `-%c' requires an argument\n",
386                                  argv[0], c);
387                     c = '?';
388                 }
389                 else
390                     /* We already incremented `optindx' once;
391                        increment it again when taking next ARGV-elt as
392                        argument.
393                     */
394                     optargx = argv[optindx++];
395                 nextchar = 0;
396             }
397         }
398         return c;
399     }
400 }
401
402
403
404 void
405 getopt_long_onlyx(int              const argc, 
406                   char **          const argv, 
407                   const char *     const options, 
408                   struct optionx * const long_options, 
409                   unsigned int *   const opt_index, 
410                   int              const opterrArg,
411                   int *            const end_of_options,
412                   const char **    const optarg_arg,
413                   const char **    const unrecognized_option) {
414
415     int rc;
416
417     opterrx = opterrArg;
418     _getopt_long_options = long_options;
419     rc = getoptx(argc, argv, options);
420     if (rc == 0)
421         *opt_index = option_index;
422
423     if (rc == '?')
424         *unrecognized_option = argv[optindx];
425     else
426         *unrecognized_option = NULL;
427
428     if (rc < 0)
429         *end_of_options = 1;
430     else
431         *end_of_options = 0;
432
433     *optarg_arg = optargx;
434 }
435      
436
437 unsigned int
438 getopt_argstart(void) {
439 /*----------------------------------------------------------------------------
440    This is a replacement for what traditional getopt does with global
441    variables.
442
443    You call this after getopt_long_onlyx() has returned "end of
444    options"
445 -----------------------------------------------------------------------------*/
446     return optindx;
447 }
448
449
450 /* Getopt for GNU.
451    Copyright (C) 1987, 1989 Free Software Foundation, Inc.
452
453    This program is free software; you can redistribute it and/or modify
454    it under the terms of the GNU General Public License as published by
455    the Free Software Foundation; either version 1, or (at your option)
456    any later version.
457
458    This program is distributed in the hope that it will be useful,
459    but WITHOUT ANY WARRANTY; without even the implied warranty of
460    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
461    GNU General Public License for more details.
462
463    You should have received a copy of the GNU General Public License
464    along with this program; if not, write to the Free Software
465    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
466 */