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