1 /*
2 Copyright (C) 1997 Gregory Pietsch
3 
4 [These files] are hereby placed in the public domain without restrictions. Just
5 give the author credit, don't claim you wrote it or prevent anyone else from
6 using it.
7 */
8 
9 /****************************************************************************
10 
11 getopt.c - Read command line options
12 
13 AUTHOR: Gregory Pietsch
14 CREATED Fri Jan 10 21:13:05 1997
15 
16 DESCRIPTION:
17 
18 The getopt() function parses the command line arguments.  Its arguments argc
19 and argv are the argument count and array as passed to the main() function
20 on program invocation.  The argument optstring is a list of available option
21 characters.  If such a character is followed by a colon (`:'), the option
22 takes an argument, which is placed in optarg.  If such a character is
23 followed by two colons, the option takes an optional argument, which is
24 placed in optarg.  If the option does not take an argument, optarg is NULL.
25 
26 The external variable optind is the index of the next array element of argv
27 to be processed; it communicates from one call to the next which element to
28 process.
29 
30 The getopt_long() function works like getopt() except that it also accepts
31 long options started by two dashes `--'.  If these take values, it is either
32 in the form
33 
34 --arg=value
35 
36  or
37 
38 --arg value
39 
40 It takes the additional arguments longopts which is a pointer to the first
41 element of an array of type GETOPT_LONG_OPTION_T.  The last element of the
42 array has to be filled with NULL for the name field.
43 
44 The longind pointer points to the index of the current long option relative
45 to longopts if it is non-NULL.
46 
47 The getopt() function returns the option character if the option was found
48 successfully, `:' if there was a missing parameter for one of the options,
49 `?' for an unknown option character, and EOF for the end of the option list.
50 
51 The getopt_long() function's return value is described in the header file.
52 
53 The function getopt_long_only() is identical to getopt_long(), except that a
54 plus sign `+' can introduce long options as well as `--'.
55 
56 The following describes how to deal with options that follow non-option
57 argv-elements.
58 
59 If the caller did not specify anything, the default is REQUIRE_ORDER if the
60 environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
61 
62 REQUIRE_ORDER means don't recognize them as options; stop option processing
63 when the first non-option is seen.  This is what Unix does.  This mode of
64 operation is selected by either setting the environment variable
65 POSIXLY_CORRECT, or using `+' as the first character of the optstring
66 parameter.
67 
68 PERMUTE is the default.  We permute the contents of ARGV as we scan, so that
69 eventually all the non-options are at the end.  This allows options to be
70 given in any order, even with programs that were not written to expect this.
71 
72 RETURN_IN_ORDER is an option available to programs that were written to
73 expect options and other argv-elements in any order and that care about the
74 ordering of the two.  We describe each non-option argv-element as if it were
75 the argument of an option with character code 1.  Using `-' as the first
76 character of the optstring parameter selects this mode of operation.
77 
78 The special argument `--' forces an end of option-scanning regardless of the
79 value of ordering.  In the case of RETURN_IN_ORDER, only `--' can cause
80 getopt() and friends to return EOF with optind != argc.
81 
82 COPYRIGHT NOTICE AND DISCLAIMER:
83 
84 Copyright (C) 1997 Gregory Pietsch
85 
86 This file and the accompanying getopt.h header file are hereby placed in the
87 public domain without restrictions.  Just give the author credit, don't
88 claim you wrote it or prevent anyone else from using it.
89 
90 Gregory Pietsch's current e-mail address:
91 gpietsch@comcast.net
92 ****************************************************************************/
93 
94 /* include files */
95 #include <stdio.h>
96 #include <stdlib.h>
97 #include <string.h>
98 #ifndef GETOPT_H
99 #include "getopt.h"
100 #endif
101 
102 namespace crashpad {
103 
104 /* macros */
105 
106 /* types */
107 typedef enum GETOPT_ORDERING_T
108 {
109   PERMUTE,
110   RETURN_IN_ORDER,
111   REQUIRE_ORDER
112 } GETOPT_ORDERING_T;
113 
114 /* globally-defined variables */
115 char *optarg = NULL;
116 int optind = 0;
117 int opterr = 1;
118 int optopt = '?';
119 
120 /* functions */
121 
122 /* reverse_argv_elements:  reverses num elements starting at argv */
123 static void
reverse_argv_elements(char ** argv,int num)124 reverse_argv_elements (char **argv, int num)
125 {
126   int i;
127   char *tmp;
128 
129   for (i = 0; i < (num >> 1); i++)
130     {
131       tmp = argv[i];
132       argv[i] = argv[num - i - 1];
133       argv[num - i - 1] = tmp;
134     }
135 }
136 
137 /* permute: swap two blocks of argv-elements given their lengths */
138 static void
permute(char ** argv,int len1,int len2)139 permute (char **argv, int len1, int len2)
140 {
141   reverse_argv_elements (argv, len1);
142   reverse_argv_elements (argv, len1 + len2);
143   reverse_argv_elements (argv, len2);
144 }
145 
146 /* is_option: is this argv-element an option or the end of the option list? */
147 static int
is_option(char * argv_element,int only)148 is_option (char *argv_element, int only)
149 {
150   return ((argv_element == NULL)
151           || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
152 }
153 
154 /* getopt_internal:  the function that does all the dirty work */
155 static int
getopt_internal(int argc,char ** argv,char * shortopts,GETOPT_LONG_OPTION_T * longopts,int * longind,int only)156 getopt_internal (int argc, char **argv, char *shortopts,
157                  GETOPT_LONG_OPTION_T * longopts, int *longind, int only)
158 {
159   GETOPT_ORDERING_T ordering = PERMUTE;
160   static size_t optwhere = 0;
161   size_t permute_from = 0;
162   int num_nonopts = 0;
163   int optindex = 0;
164   size_t match_chars = 0;
165   char *possible_arg = NULL;
166   int longopt_match = -1;
167   int has_arg = -1;
168   char *cp = NULL;
169   int arg_next = 0;
170 
171   /* first, deal with silly parameters and easy stuff */
172   if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))
173     return (optopt = '?');
174   if (optind >= argc || argv[optind] == NULL)
175     return EOF;
176   if (strcmp (argv[optind], "--") == 0)
177     {
178       optind++;
179       return EOF;
180     }
181   /* if this is our first time through */
182   if (optind == 0) {
183     optind = 1;
184     optwhere = 1;
185   }
186 
187   /* define ordering */
188   if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+'))
189     {
190       ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
191       shortopts++;
192     }
193   else
194     ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE;
195 
196   /*
197    * based on ordering, find our next option, if we're at the beginning of
198    * one
199    */
200   if (optwhere == 1)
201     {
202       switch (ordering)
203         {
204         case PERMUTE:
205           permute_from = optind;
206           num_nonopts = 0;
207           while (!is_option (argv[optind], only))
208             {
209               optind++;
210               num_nonopts++;
211             }
212           if (argv[optind] == NULL)
213             {
214               /* no more options */
215               optind = (int)permute_from;
216               return EOF;
217             }
218           else if (strcmp (argv[optind], "--") == 0)
219             {
220               /* no more options, but have to get `--' out of the way */
221               permute (argv + permute_from, num_nonopts, 1);
222               optind = (int)(permute_from + 1);
223               return EOF;
224             }
225           break;
226         case RETURN_IN_ORDER:
227           if (!is_option (argv[optind], only))
228             {
229               optarg = argv[optind++];
230               return (optopt = 1);
231             }
232           break;
233         case REQUIRE_ORDER:
234           if (!is_option (argv[optind], only))
235             return EOF;
236           break;
237         }
238     }
239   /* we've got an option, so parse it */
240 
241   /* first, is it a long option? */
242   if (longopts != NULL
243       && (strncmp (argv[optind], "--", 2) == 0
244           || (only && argv[optind][0] == '+')) && optwhere == 1)
245     {
246       /* handle long options */
247       if (strncmp (argv[optind], "--", 2) == 0)
248         optwhere = 2;
249       longopt_match = -1;
250       possible_arg = strchr (argv[optind] + optwhere, '=');
251       if (possible_arg == NULL)
252         {
253           /* no =, so next argv might be arg */
254           match_chars = strlen (argv[optind]);
255           possible_arg = argv[optind] + match_chars;
256           match_chars = match_chars - optwhere;
257         }
258       else
259         match_chars = (possible_arg - argv[optind]) - optwhere;
260       for (optindex = 0; longopts[optindex].name != NULL; optindex++)
261         {
262           if (strncmp (argv[optind] + optwhere,
263                        longopts[optindex].name, match_chars) == 0)
264             {
265               /* do we have an exact match? */
266               if (match_chars == strlen (longopts[optindex].name))
267                 {
268                   longopt_match = optindex;
269                   break;
270                 }
271               /* do any characters match? */
272               else
273                 {
274                   if (longopt_match < 0)
275                     longopt_match = optindex;
276                   else
277                     {
278                       /* we have ambiguous options */
279                       if (opterr)
280                         fprintf (stderr, "%s: option `%s' is ambiguous "
281                                  "(could be `--%s' or `--%s')\n",
282                                  argv[0],
283                                  argv[optind],
284                                  longopts[longopt_match].name,
285                                  longopts[optindex].name);
286                       return (optopt = '?');
287                     }
288                 }
289             }
290         }
291       if (longopt_match >= 0)
292         has_arg = longopts[longopt_match].has_arg;
293     }
294   /* if we didn't find a long option, is it a short option? */
295   if (longopt_match < 0 && shortopts != NULL)
296     {
297       cp = strchr (shortopts, argv[optind][optwhere]);
298       if (cp == NULL)
299         {
300           /* couldn't find option in shortopts */
301           if (opterr)
302             fprintf (stderr,
303                      "%s: invalid option -- `-%c'\n",
304                      argv[0], argv[optind][optwhere]);
305           optwhere++;
306           if (argv[optind][optwhere] == '\0')
307             {
308               optind++;
309               optwhere = 1;
310             }
311           return (optopt = '?');
312         }
313         has_arg = ((cp[1] == ':') ? ((cp[2] == ':') ? optional_argument
314                                                     : required_argument)
315                                   : no_argument);
316         possible_arg = argv[optind] + optwhere + 1;
317         optopt = *cp;
318     }
319   /* get argument and reset optwhere */
320   arg_next = 0;
321   switch (has_arg)
322     {
323     case optional_argument:
324       if (*possible_arg == '=')
325         possible_arg++;
326       if (*possible_arg != '\0')
327         {
328           optarg = possible_arg;
329           optwhere = 1;
330         }
331       else
332         optarg = NULL;
333       break;
334     case required_argument:
335       if (*possible_arg == '=')
336         possible_arg++;
337       if (*possible_arg != '\0')
338         {
339           optarg = possible_arg;
340           optwhere = 1;
341         }
342       else if (optind + 1 >= argc)
343         {
344           if (opterr)
345             {
346               fprintf (stderr, "%s: argument required for option `", argv[0]);
347               if (longopt_match >= 0)
348                 fprintf (stderr, "--%s'\n", longopts[longopt_match].name);
349               else
350                 fprintf (stderr, "-%c'\n", *cp);
351             }
352           optind++;
353           return (optopt = ':');
354         }
355       else
356         {
357           optarg = argv[optind + 1];
358           arg_next = 1;
359           optwhere = 1;
360         }
361       break;
362     case no_argument:
363       if (longopt_match < 0)
364         {
365           optwhere++;
366           if (argv[optind][optwhere] == '\0')
367             optwhere = 1;
368         }
369       else
370         optwhere = 1;
371       optarg = NULL;
372       break;
373     }
374 
375   /* do we have to permute or otherwise modify optind? */
376   if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0)
377     {
378       permute (argv + permute_from, num_nonopts, 1 + arg_next);
379       optind = (int)(permute_from + 1 + arg_next);
380     }
381   else if (optwhere == 1)
382     optind = optind + 1 + arg_next;
383 
384   /* finally return */
385   if (longopt_match >= 0)
386     {
387       if (longind != NULL)
388         *longind = longopt_match;
389       if (longopts[longopt_match].flag != NULL)
390         {
391           *(longopts[longopt_match].flag) = longopts[longopt_match].val;
392           return 0;
393         }
394       else
395         return longopts[longopt_match].val;
396     }
397   else
398     return optopt;
399 }
400 
401 int
getopt(int argc,char ** argv,char * optstring)402 getopt (int argc, char **argv, char *optstring)
403 {
404   return getopt_internal (argc, argv, optstring, NULL, NULL, 0);
405 }
406 
407 int
getopt_long(int argc,char ** argv,const char * shortopts,const GETOPT_LONG_OPTION_T * longopts,int * longind)408 getopt_long (int argc, char **argv, const char *shortopts,
409              const GETOPT_LONG_OPTION_T * longopts, int *longind)
410 {
411   return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0);
412 }
413 
414 int
getopt_long_only(int argc,char ** argv,const char * shortopts,const GETOPT_LONG_OPTION_T * longopts,int * longind)415 getopt_long_only (int argc, char **argv, const char *shortopts,
416                   const GETOPT_LONG_OPTION_T * longopts, int *longind)
417 {
418   return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1);
419 }
420 
421 }  // namespace crashpad
422 
423 /* end of file GETOPT.C */
424