11d879ad2bfe70f4ab76d06bcc8aad20d36c4c16
[fio.git] / lib / 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)
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 }