blob: 6e8abc0678d1280f415f9c2d4e20f79778a36982 [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 */
129 if (argv[optind + 1]) {
130 optarg = (char *)argv[optind+1];
131 optind += 2;
132 } else {
133 /* Missing argument */
134 optind++;
135 return (optstring[0] == ':')
136 ? ':' : '?';
137 }
138 }
139 return opt;
140 } else {
141 /* Non-argument-taking option */
142 /* pvt.optptr will remember the exact position to
143 resume at */
144 if (!*pvt.optptr)
145 optind++;
146 return opt;
147 }
148 } else {
149 /* Unknown option */
150 optopt = opt;
151 if (!*pvt.optptr)
152 optind++;
153 return '?';
154 }
155}