1 /****************************************************************************
2 
3 getopt.c - Read command line options
4 
5 AUTHOR: Gregory Pietsch
6 CREATED Fri Jan 10 21:13:05 1997
7 
8 DESCRIPTION:
9 
10 The getopt() function parses the command line arguments.  Its arguments argc
11 and argv are the argument count and array as passed to the main() function
12 on program invocation.  The argument optstring is a list of available option
13 characters.  If such a character is followed by a colon (`:'), the option
14 takes an argument, which is placed in optarg.  If such a character is
15 followed by two colons, the option takes an optional argument, which is
16 placed in optarg.  If the option does not take an argument, optarg is NULL.
17 
18 The external variable optind is the index of the next array element of argv
19 to be processed; it communicates from one call to the next which element to
20 process.
21 
22 The getopt_long() function works like getopt() except that it also accepts
23 long options started by two dashes `--'.  If these take values, it is either
24 in the form
25 
26 --arg=value
27 
28  or
29 
30 --arg value
31 
32 It takes the additional arguments longopts which is a pointer to the first
33 element of an array of type struct option.  The last element of the array
34 has to be filled with NULL for the name field.
35 
36 The longind pointer points to the index of the current long option relative
37 to longopts if it is non-NULL.
38 
39 The getopt() function returns the option character if the option was found
40 successfully, `:' if there was a missing parameter for one of the options,
41 `?' for an unknown option character, and EOF for the end of the option list.
42 
43 The getopt_long() function's return value is described in the header file.
44 
45 The function getopt_long_only() is identical to getopt_long(), except that a
46 plus sign `+' can introduce long options as well as `--'.
47 
48 The following describes how to deal with options that follow non-option
49 argv-elements.
50 
51 If the caller did not specify anything, the default is REQUIRE_ORDER if the
52 environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
53 
54 REQUIRE_ORDER means don't recognize them as options; stop option processing
55 when the first non-option is seen.  This is what Unix does.  This mode of
56 operation is selected by either setting the environment variable
57 POSIXLY_CORRECT, or using `+' as the first character of the optstring
58 parameter.
59 
60 PERMUTE is the default.  We permute the contents of ARGV as we scan, so that
61 eventually all the non-options are at the end.  This allows options to be
62 given in any order, even with programs that were not written to expect this.
63 
64 RETURN_IN_ORDER is an option available to programs that were written to
65 expect options and other argv-elements in any order and that care about the
66 ordering of the two.  We describe each non-option argv-element as if it were
67 the argument of an option with character code 1.  Using `-' as the first
68 character of the optstring parameter selects this mode of operation.
69 
70 The special argument `--' forces an end of option-scanning regardless of the
71 value of ordering.  In the case of RETURN_IN_ORDER, only `--' can cause
72 getopt() and friends to return EOF with optind != argc.
73 
74 2012-08-26: Tried to make the error handling more sus4-like. The functions
75 return a colon if getopt() and friends detect a missing argument and the
76 first character of shortopts/optstring starts with a colon (`:'). If getopt()
77 and friends detect a missing argument and shortopts/optstring does not start
78 with a colon, the function returns a question mark (`?'). If it was a missing
79 argument to a short option, optopt is set to the character in question. The
80 colon goes after the ordering character (`+' or `-').
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 #ifndef HAVE_GETOPT
95 
96 /* include files */
97 #include <stdio.h>
98 #include <stdlib.h>
99 #include <string.h>
100 #define __need_getopt_newlib
101 #include <getopt.h>
102 
103 /* macros */
104 
105 /* types */
106 typedef enum GETOPT_ORDERING_T
107 {
108   PERMUTE,
109   RETURN_IN_ORDER,
110   REQUIRE_ORDER
111 } GETOPT_ORDERING_T;
112 
113 /* globally-defined variables */
114 char *optarg = 0;
115 int optind = 0;
116 int opterr = 1;
117 int optopt = '?';
118 
119 /* static variables */
120 static int optwhere = 0;
121 static int permute_from = 0;
122 static int num_nonopts = 0;
123 
124 /* functions */
125 
126 /* reverse_argv_elements:  reverses num elements starting at argv */
127 static void
reverse_argv_elements(char ** argv,int num)128 reverse_argv_elements (char **argv, int num)
129 {
130   int i;
131   char *tmp;
132 
133   for (i = 0; i < (num >> 1); i++)
134     {
135       tmp = argv[i];
136       argv[i] = argv[num - i - 1];
137       argv[num - i - 1] = tmp;
138     }
139 }
140 
141 /* permute: swap two blocks of argv-elements given their lengths */
142 static void
permute(char * const argv[],int len1,int len2)143 permute (char *const argv[], int len1, int len2)
144 {
145   reverse_argv_elements ((char **) argv, len1);
146   reverse_argv_elements ((char **) argv, len1 + len2);
147   reverse_argv_elements ((char **) argv, len2);
148 }
149 
150 /* is_option: is this argv-element an option or the end of the option list? */
151 static int
is_option(char * argv_element,int only)152 is_option (char *argv_element, int only)
153 {
154   return ((argv_element == 0)
155 	  || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
156 }
157 
158 /* read_globals: read the values from the globals into a getopt_data
159    structure */
160 static void
read_globals(struct getopt_data * data)161 read_globals (struct getopt_data *data)
162 {
163   data->optarg = optarg;
164   data->optind = optind;
165   data->opterr = opterr;
166   data->optopt = optopt;
167   data->optwhere = optwhere;
168   data->permute_from = permute_from;
169   data->num_nonopts = num_nonopts;
170 }
171 
172 /* write_globals: write the values into the globals from a getopt_data
173    structure */
174 static void
write_globals(struct getopt_data * data)175 write_globals (struct getopt_data *data)
176 {
177   optarg = data->optarg;
178   optind = data->optind;
179   opterr = data->opterr;
180   optopt = data->optopt;
181   optwhere = data->optwhere;
182   permute_from = data->permute_from;
183   num_nonopts = data->num_nonopts;
184 }
185 
186 /* getopt_internal:  the function that does all the dirty work
187    NOTE: to reduce the code and RAM footprint this function uses
188    fputs()/fputc() to do output to stderr instead of fprintf(). */
189 static int
getopt_internal(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind,int only,struct getopt_data * data)190 getopt_internal (int argc, char *const argv[], const char *shortopts,
191 		 const struct option *longopts, int *longind, int only,
192 		 struct getopt_data *data)
193 {
194   GETOPT_ORDERING_T ordering = PERMUTE;
195   int optindex = 0;
196   size_t match_chars = 0;
197   char *possible_arg = 0;
198   int longopt_match = -1;
199   int has_arg = -1;
200   char *cp = 0;
201   int arg_next = 0;
202   int initial_colon = 0;
203 
204   /* if this is our first time through */
205   if (data->optind <= 0)
206     {
207       data->optind = 1;
208       data->optwhere = 1;
209       data->permute_from = 0;
210       data->num_nonopts = 0;
211     }
212 
213   /* first, deal with silly parameters and easy stuff */
214   if (argc == 0 || argv == 0 || (shortopts == 0 && longopts == 0)
215       || data->optind >= argc || argv[data->optind] == 0)
216     return EOF;
217 
218   if (strcmp (argv[data->optind], "--") == 0)
219     {
220       data->optind++;
221       return EOF;
222     }
223 
224   /* define ordering */
225   if (shortopts != 0 && (*shortopts == '-' || *shortopts == '+'))
226     {
227       ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
228       shortopts++;
229     }
230   else
231     ordering = (getenv ("POSIXLY_CORRECT") != 0) ? REQUIRE_ORDER : PERMUTE;
232 
233   /* check for initial colon in shortopts */
234   if (shortopts != 0 && *shortopts == ':')
235     {
236       ++shortopts;
237       initial_colon = 1;
238     }
239 
240   /*
241    * based on ordering, find our next option, if we're at the beginning of
242    * one
243    */
244   if (data->optwhere == 1)
245     {
246       switch (ordering)
247 	{
248 	default:		/* shouldn't happen */
249 	case PERMUTE:
250 	  data->permute_from = data->optind;
251 	  data->num_nonopts = 0;
252 	  while (!is_option (argv[data->optind], only))
253 	    {
254 	      data->optind++;
255 	      data->num_nonopts++;
256 	    }
257 	  if (argv[data->optind] == 0)
258 	    {
259 	      /* no more options */
260 	      data->optind = data->permute_from;
261 	      return EOF;
262 	    }
263 	  else if (strcmp (argv[data->optind], "--") == 0)
264 	    {
265 	      /* no more options, but have to get `--' out of the way */
266 	      permute (argv + data->permute_from, data->num_nonopts, 1);
267 	      data->optind = data->permute_from + 1;
268 	      return EOF;
269 	    }
270 	  break;
271 	case RETURN_IN_ORDER:
272 	  if (!is_option (argv[data->optind], only))
273 	    {
274 	      data->optarg = argv[data->optind++];
275 	      return (data->optopt = 1);
276 	    }
277 	  break;
278 	case REQUIRE_ORDER:
279 	  if (!is_option (argv[data->optind], only))
280 	    return EOF;
281 	  break;
282 	}
283     }
284   /* End of option list? */
285   if (argv[data->optind] == 0)
286     return EOF;
287 
288   /* we've got an option, so parse it */
289 
290   /* first, is it a long option? */
291   if (longopts != 0
292       && (memcmp (argv[data->optind], "--", 2) == 0
293 	  || (only && argv[data->optind][0] == '+')) && data->optwhere == 1)
294     {
295       /* handle long options */
296       if (memcmp (argv[data->optind], "--", 2) == 0)
297 	data->optwhere = 2;
298       longopt_match = -1;
299       possible_arg = strchr (argv[data->optind] + data->optwhere, '=');
300       if (possible_arg == 0)
301 	{
302 	  /* no =, so next argv might be arg */
303 	  match_chars = strlen (argv[data->optind]);
304 	  possible_arg = argv[data->optind] + match_chars;
305 	  match_chars = match_chars - data->optwhere;
306 	}
307       else
308 	match_chars = (possible_arg - argv[data->optind]) - data->optwhere;
309       for (optindex = 0; longopts[optindex].name != 0; ++optindex)
310 	{
311 	  if (memcmp
312 	      (argv[data->optind] + data->optwhere, longopts[optindex].name,
313 	       match_chars) == 0)
314 	    {
315 	      /* do we have an exact match? */
316 	      if (match_chars == strlen (longopts[optindex].name))
317 		{
318 		  longopt_match = optindex;
319 		  break;
320 		}
321 	      /* do any characters match? */
322 	      else
323 		{
324 		  if (longopt_match < 0)
325 		    longopt_match = optindex;
326 		  else
327 		    {
328 		      /* we have ambiguous options */
329 		      if (data->opterr)
330 			{
331 			  fputs (argv[0], stderr);
332 			  fputs (": option `", stderr);
333 			  fputs (argv[data->optind], stderr);
334 			  fputs ("' is ambiguous (could be `--", stderr);
335 			  fputs (longopts[longopt_match].name, stderr);
336 			  fputs ("' or `--", stderr);
337 			  fputs (longopts[optindex].name, stderr);
338 			  fputs ("')\n", stderr);
339 			}
340 		      return (data->optopt = '?');
341 		    }
342 		}
343 	    }
344 	}
345       if (longopt_match >= 0)
346 	has_arg = longopts[longopt_match].has_arg;
347     }
348 
349   /* if we didn't find a long option, is it a short option? */
350   if (longopt_match < 0 && shortopts != 0)
351     {
352       cp = strchr (shortopts, argv[data->optind][data->optwhere]);
353       if (cp == 0)
354 	{
355 	  /* couldn't find option in shortopts */
356 	  if (data->opterr)
357 	    {
358 	      fputs (argv[0], stderr);
359 	      fputs (": invalid option -- `-", stderr);
360 	      fputc (argv[data->optind][data->optwhere], stderr);
361 	      fputs ("'\n", stderr);
362 	    }
363 	  data->optwhere++;
364 	  if (argv[data->optind][data->optwhere] == '\0')
365 	    {
366 	      data->optind++;
367 	      data->optwhere = 1;
368 	    }
369 	  return (data->optopt = '?');
370 	}
371       has_arg = ((cp[1] == ':')
372 		 ? ((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : NO_ARG);
373       possible_arg = argv[data->optind] + data->optwhere + 1;
374       data->optopt = *cp;
375     }
376 
377   /* get argument and reset data->optwhere */
378   arg_next = 0;
379   switch (has_arg)
380     {
381     case OPTIONAL_ARG:
382       if (*possible_arg == '=')
383 	possible_arg++;
384       data->optarg = (*possible_arg != '\0') ? possible_arg : 0;
385       data->optwhere = 1;
386       break;
387     case REQUIRED_ARG:
388       if (*possible_arg == '=')
389 	possible_arg++;
390       if (*possible_arg != '\0')
391 	{
392 	  data->optarg = possible_arg;
393 	  data->optwhere = 1;
394 	}
395       else if (data->optind + 1 >= argc)
396 	{
397 	  if (data->opterr)
398 	    {
399 	      fputs (argv[0], stderr);
400 	      fputs (": argument required for option `-", stderr);
401 	      if (longopt_match >= 0)
402 		{
403 		  fputc ('-', stderr);
404 		  fputs (longopts[longopt_match].name, stderr);
405 		  data->optopt = initial_colon ? ':' : '\?';
406 		}
407 	      else
408 		{
409 		  fputc (*cp, stderr);
410 		  data->optopt = *cp;
411 		}
412 	      fputs ("'\n", stderr);
413 	    }
414 	  data->optind++;
415 	  return initial_colon ? ':' : '\?';
416 	}
417       else
418 	{
419 	  data->optarg = argv[data->optind + 1];
420 	  arg_next = 1;
421 	  data->optwhere = 1;
422 	}
423       break;
424     default:			/* shouldn't happen */
425     case NO_ARG:
426       if (longopt_match < 0)
427 	{
428 	  data->optwhere++;
429 	  if (argv[data->optind][data->optwhere] == '\0')
430 	    data->optwhere = 1;
431 	}
432       else
433 	data->optwhere = 1;
434       data->optarg = 0;
435       break;
436     }
437 
438   /* do we have to permute or otherwise modify data->optind? */
439   if (ordering == PERMUTE && data->optwhere == 1 && data->num_nonopts != 0)
440     {
441       permute (argv + data->permute_from, data->num_nonopts, 1 + arg_next);
442       data->optind = data->permute_from + 1 + arg_next;
443     }
444   else if (data->optwhere == 1)
445     data->optind = data->optind + 1 + arg_next;
446 
447   /* finally return */
448   if (longopt_match >= 0)
449     {
450       if (longind != 0)
451 	*longind = longopt_match;
452       if (longopts[longopt_match].flag != 0)
453 	{
454 	  *(longopts[longopt_match].flag) = longopts[longopt_match].val;
455 	  return 0;
456 	}
457       else
458 	return longopts[longopt_match].val;
459     }
460   else
461     return data->optopt;
462 }
463 
464 int
getopt(int argc,char * const argv[],const char * optstring)465 getopt (int argc, char *const argv[], const char *optstring)
466 {
467   struct getopt_data data;
468   int r;
469 
470   read_globals (&data);
471   r = getopt_internal (argc, argv, optstring, 0, 0, 0, &data);
472   write_globals (&data);
473   return r;
474 }
475 
476 int
getopt_long(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind)477 getopt_long (int argc, char *const argv[], const char *shortopts,
478 	     const struct option *longopts, int *longind)
479 {
480   struct getopt_data data;
481   int r;
482 
483   read_globals (&data);
484   r = getopt_internal (argc, argv, shortopts, longopts, longind, 0, &data);
485   write_globals (&data);
486   return r;
487 }
488 
489 int
getopt_long_only(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind)490 getopt_long_only (int argc, char *const argv[], const char *shortopts,
491 		  const struct option *longopts, int *longind)
492 {
493   struct getopt_data data;
494   int r;
495 
496   read_globals (&data);
497   r = getopt_internal (argc, argv, shortopts, longopts, longind, 1, &data);
498   write_globals (&data);
499   return r;
500 }
501 
502 int
__getopt_r(int argc,char * const argv[],const char * optstring,struct getopt_data * data)503 __getopt_r (int argc, char *const argv[], const char *optstring,
504 	    struct getopt_data *data)
505 {
506   return getopt_internal (argc, argv, optstring, 0, 0, 0, data);
507 }
508 
509 int
__getopt_long_r(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind,struct getopt_data * data)510 __getopt_long_r (int argc, char *const argv[], const char *shortopts,
511 		 const struct option *longopts, int *longind,
512 		 struct getopt_data *data)
513 {
514   return getopt_internal (argc, argv, shortopts, longopts, longind, 0, data);
515 }
516 
517 int
__getopt_long_only_r(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind,struct getopt_data * data)518 __getopt_long_only_r (int argc, char *const argv[], const char *shortopts,
519 		      const struct option *longopts, int *longind,
520 		      struct getopt_data *data)
521 {
522   return getopt_internal (argc, argv, shortopts, longopts, longind, 1, data);
523 }
524 
525 #endif /* !HAVE_GETOPT */
526 
527 /* end of file GETOPT.C */
528