• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
option_matches(const char * arg_str,const char * opt_name,int smatch)28 static inline const char *option_matches(const char *arg_str,
29 					 const char *opt_name, int smatch)
30 {
31 	while (*arg_str != '\0' && *arg_str != '=') {
32 		if (*arg_str++ != *opt_name++)
33 			return NULL;
34 	}
35 
36 	if (*opt_name && !smatch)
37 		return NULL;
38 
39 	return arg_str;
40 }
41 
getopt_long_only(int argc,char * const * argv,const char * optstring,const struct option * longopts,int * longindex)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 			opt_end = option_matches(carg+2, lo->name, 0);
88 			if (opt_end)
89 			    break;
90 		}
91 		/*
92 		 * The GNU getopt_long_only() apparently allows a short match,
93 		 * if it's unique and if we don't have a full match. Let's
94 		 * do the same here, search and see if there is one (and only
95 		 * one) short match.
96 		 */
97 		if (!opt_end) {
98 			const struct option *lo_match = NULL;
99 
100 			for (lo = longopts; lo->name; lo++) {
101 				const char *ret;
102 
103 				ret = option_matches(carg+2, lo->name, 1);
104 				if (!ret)
105 					continue;
106 				if (!opt_end) {
107 					opt_end = ret;
108 					lo_match = lo;
109 				} else {
110 					opt_end = NULL;
111 					break;
112 				}
113 			}
114 			if (!opt_end)
115 				return '?';
116 			lo = lo_match;
117 		}
118 
119 		if (longindex)
120 			*longindex = lo-longopts;
121 
122 		if (*opt_end == '=') {
123 			if (lo->has_arg)
124 				optarg = (char *)opt_end+1;
125 			else
126 				return '?';
127 		} else if (lo->has_arg == 1) {
128 			if (!(optarg = argv[optind]))
129 				return '?';
130 			optind++;
131 		}
132 
133 		if (lo->flag) {
134 			*lo->flag = lo->val;
135 			return 0;
136 		} else {
137 			return lo->val;
138 		}
139 	}
140 
141 	if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) {
142 		/* Someone frobbed optind, change to new opt. */
143 		pvt.optptr = carg + 1;
144 	}
145 
146 	opt = *pvt.optptr++;
147 
148 	if (opt != ':' && (osptr = strchr(optstring, opt))) {
149 		if (osptr[1] == ':') {
150 			if (*pvt.optptr) {
151 				/* Argument-taking option with attached
152 				   argument */
153 				optarg = (char *)pvt.optptr;
154 				optind++;
155 			} else {
156 				/* Argument-taking option with non-attached
157 				   argument */
158 				if (osptr[2] == ':') {
159 					if (argv[optind + 1]) {
160 						optarg = (char *)argv[optind+1];
161 						optind += 2;
162 					} else {
163 						optarg = NULL;
164 						optind++;
165 					}
166 					return opt;
167 				} else if (argv[optind + 1]) {
168 					optarg = (char *)argv[optind+1];
169 					optind += 2;
170 				} else {
171 					/* Missing argument */
172 					optind++;
173 					return (optstring[0] == ':')
174 						? ':' : '?';
175 				}
176 			}
177 			return opt;
178 		} else {
179 			/* Non-argument-taking option */
180 			/* pvt.optptr will remember the exact position to
181 			   resume at */
182 			if (!*pvt.optptr)
183 				optind++;
184 			return opt;
185 		}
186 	} else {
187 		/* Unknown option */
188 		optopt = opt;
189 		if (!*pvt.optptr)
190 			optind++;
191 		return '?';
192 	}
193 }
194