configure: add gettid() test
[fio.git] / oslib / getopt_long.c
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, 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
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 }