Home | History | Annotate | Download | only in lib
      1 /*
      2  * getopt.c
      3  *
      4  * getopt_long(), or at least a common subset thereof:
      5  *
      6  * - Option reordering is not supported
      7  * - -W foo is not supported
      8  * - First optstring character "-" not supported.
      9  *
     10  * This file was imported from the klibc library from hpa
     11  */
     12 
     13 #include <stdint.h>
     14 #include <unistd.h>
     15 #include <string.h>
     16 
     17 #include "getopt.h"
     18 
     19 char *optarg = NULL;
     20 int optind = 0, opterr = 0, optopt = 0;
     21 
     22 static struct getopt_private_state {
     23 	const char *optptr;
     24 	const char *last_optstring;
     25 	char *const *last_argv;
     26 } pvt;
     27 
     28 static inline const char *option_matches(const char *arg_str,
     29 					 const char *opt_name)
     30 {
     31 	while (*arg_str != '\0' && *arg_str != '=') {
     32 		if (*arg_str++ != *opt_name++)
     33 			return NULL;
     34 	}
     35 
     36 	if (*opt_name)
     37 		return NULL;
     38 
     39 	return arg_str;
     40 }
     41 
     42 int getopt_long_only(int argc, char *const *argv, const char *optstring,
     43 		const struct option *longopts, int *longindex)
     44 {
     45 	const char *carg;
     46 	const char *osptr;
     47 	int opt;
     48 
     49 	optarg = NULL;
     50 
     51 	/* getopt() relies on a number of different global state
     52 	   variables, which can make this really confusing if there is
     53 	   more than one use of getopt() in the same program.  This
     54 	   attempts to detect that situation by detecting if the
     55 	   "optstring" or "argv" argument have changed since last time
     56 	   we were called; if so, reinitialize the query state. */
     57 
     58 	if (optstring != pvt.last_optstring || argv != pvt.last_argv ||
     59 	    optind < 1 || optind > argc) {
     60 		/* optind doesn't match the current query */
     61 		pvt.last_optstring = optstring;
     62 		pvt.last_argv = argv;
     63 		optind = 1;
     64 		pvt.optptr = NULL;
     65 	}
     66 
     67 	carg = argv[optind];
     68 
     69 	/* First, eliminate all non-option cases */
     70 
     71 	if (!carg || carg[0] != '-' || !carg[1])
     72 		return -1;
     73 
     74 	if (carg[1] == '-') {
     75 		const struct option *lo;
     76 		const char *opt_end = NULL;
     77 
     78 		optind++;
     79 
     80 		/* Either it's a long option, or it's -- */
     81 		if (!carg[2]) {
     82 			/* It's -- */
     83 			return -1;
     84 		}
     85 
     86 		for (lo = longopts; lo->name; lo++) {
     87 			if ((opt_end = option_matches(carg+2, lo->name)))
     88 			    break;
     89 		}
     90 		if (!opt_end)
     91 			return '?';
     92 
     93 		if (longindex)
     94 			*longindex = lo-longopts;
     95 
     96 		if (*opt_end == '=') {
     97 			if (lo->has_arg)
     98 				optarg = (char *)opt_end+1;
     99 			else
    100 				return '?';
    101 		} else if (lo->has_arg == 1) {
    102 			if (!(optarg = argv[optind]))
    103 				return '?';
    104 			optind++;
    105 		}
    106 
    107 		if (lo->flag) {
    108 			*lo->flag = lo->val;
    109 			return 0;
    110 		} else {
    111 			return lo->val;
    112 		}
    113 	}
    114 
    115 	if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) {
    116 		/* Someone frobbed optind, change to new opt. */
    117 		pvt.optptr = carg + 1;
    118 	}
    119 
    120 	opt = *pvt.optptr++;
    121 
    122 	if (opt != ':' && (osptr = strchr(optstring, opt))) {
    123 		if (osptr[1] == ':') {
    124 			if (*pvt.optptr) {
    125 				/* Argument-taking option with attached
    126 				   argument */
    127 				optarg = (char *)pvt.optptr;
    128 				optind++;
    129 			} else {
    130 				/* Argument-taking option with non-attached
    131 				   argument */
    132 				if (osptr[2] == ':') {
    133 					if (argv[optind + 1]) {
    134 						optarg = (char *)argv[optind+1];
    135 						optind += 2;
    136 					} else {
    137 						optarg = NULL;
    138 						optind++;
    139 					}
    140 					return opt;
    141 				} else if (argv[optind + 1]) {
    142 					optarg = (char *)argv[optind+1];
    143 					optind += 2;
    144 				} else {
    145 					/* Missing argument */
    146 					optind++;
    147 					return (optstring[0] == ':')
    148 						? ':' : '?';
    149 				}
    150 			}
    151 			return opt;
    152 		} else {
    153 			/* Non-argument-taking option */
    154 			/* pvt.optptr will remember the exact position to
    155 			   resume at */
    156 			if (!*pvt.optptr)
    157 				optind++;
    158 			return opt;
    159 		}
    160 	} else {
    161 		/* Unknown option */
    162 		optopt = opt;
    163 		if (!*pvt.optptr)
    164 			optind++;
    165 		return '?';
    166 	}
    167 }
    168