OILS / vendor / souffle / utility / GetOptLongImpl.h View on Github | oilshell.org

289 lines, 164 significant
1/*
2 * Souffle - A Datalog Compiler
3 * Copyright (c) 2022, The Souffle Developers. All rights reserved
4 * Licensed under the Universal Permissive License v 1.0 as shown at:
5 * - https://opensource.org/licenses/UPL
6 * - <souffle root>/licenses/SOUFFLE-UPL.txt
7 */
8
9// Implementation of getopt_long for Windows.
10#pragma once
11#ifdef USE_CUSTOM_GETOPTLONG
12
13#include "GetOptLong.h"
14#include <stdio.h>
15#include <string.h>
16
17char* optarg = nullptr;
18
19// the index of the next element to be processed in argv
20int optind = 0;
21int opterr = 1;
22int optopt = 0;
23
24enum { no_argument = 0, required_argument = 1, optional_argument = 2 };
25
26namespace {
27
28// nextchar points to the next option character in an element of argv
29char* nextchar = nullptr;
30
31// value of optind at the previous call of getopt_long
32int previous_optind = -1;
33
34// the number of non-options elements of argv skipped last time getopt was called
35int nonopt_count = 0;
36
37int parse_long_option(const int argc, char* const argv[], const struct option* longopts, int* longindex,
38 const int print_error_message, const int missing_argument) {
39 char* const current = nextchar;
40 ++optind;
41
42 char* const hasequal = strchr(current, '=');
43 size_t namelength = (hasequal ? (hasequal - current) : strlen(current));
44
45 int i;
46 int match = -1;
47 for (i = 0; longopts[i].name != nullptr; ++i) {
48 if (strncmp(longopts[i].name, current, namelength)) {
49 continue;
50 }
51 if (strlen(longopts[i].name) != namelength) {
52 continue;
53 }
54
55 match = i;
56 break;
57 }
58
59 if (match == -1) {
60 // cannot find long option
61 if (print_error_message) {
62 fprintf(stderr, "unknown option -- %.*s\n", static_cast<int>(namelength), current);
63 }
64 optopt = 0;
65 return (int)'?';
66 }
67
68 if (longopts[match].has_arg == no_argument) {
69 // no argument expected
70 if (hasequal) {
71 if (print_error_message) {
72 fprintf(stderr, "unexpected argument -- %.*s\n", static_cast<int>(namelength), current);
73 }
74 if (longopts[match].flag == nullptr) {
75 optopt = longopts[match].val;
76 } else {
77 optopt = 0;
78 }
79 return (int)'?';
80 }
81 }
82
83 if (longopts[match].has_arg == required_argument || longopts[match].has_arg == optional_argument) {
84 if (hasequal) {
85 // argument is in the same argv after '=' sign
86 optarg = hasequal + 1;
87 } else if (optind < argc) {
88 // Argument may be in next argv
89 // If argument is optional, leave optarg to null, user is in charge
90 // of verifying the value of argv[optind] and increment optind
91 // if the argument is valid.
92 if (longopts[match].has_arg == required_argument) {
93 // mandatory argument
94 optarg = argv[optind++];
95 }
96 } else {
97 // no argument found
98 if (longopts[match].has_arg == required_argument) {
99 if (print_error_message) {
100 fprintf(stderr, "missing mandatory argument -- %.*s\n", static_cast<int>(namelength),
101 current);
102 }
103 optopt = 0;
104 return missing_argument;
105 }
106 }
107 } // unexpected value of has_arg is not verified
108
109 if (longindex) *longindex = match;
110 if (longopts[match].flag) {
111 *longopts[match].flag = longopts[match].val;
112 return 0;
113 } else {
114 return longopts[match].val;
115 }
116}
117
118// permute argv[last] and argv[last-1] and recurse
119void permute(char* argv[], int first, int last) {
120 if (first >= last) return;
121 char* tmp = argv[last];
122 argv[last] = argv[last - 1];
123 argv[last - 1] = tmp;
124 permute(argv, first, last - 1);
125}
126
127void shift(char* argv[]) {
128 // done with reading options from argv[previous_optind]..argv[optind-1]
129
130 int start = previous_optind;
131 for (int mv = previous_optind + nonopt_count; mv < optind; ++mv) {
132 permute(argv, start, mv);
133 ++start;
134 }
135
136 optind -= nonopt_count;
137 previous_optind = optind;
138 nonopt_count = 0;
139}
140
141} // anonymous namespace
142
143int getopt_long(
144 int argc, char* const argv[], const char* optstring, const struct option* longopts, int* longindex) {
145 if (optind == 0) { // full reset
146 nextchar = nullptr;
147 nonopt_count = 0;
148 optarg = nullptr;
149 optind = 1;
150 previous_optind = optind;
151 }
152
153 int missing_argument = (int)'?';
154 int print_error_message = opterr;
155
156 optarg = nullptr;
157
158 if (*optstring == '+' || *optstring == '-') {
159 throw "Mode +/- of optstring is not supported.";
160 ++optstring;
161 }
162
163 if (*optstring == ':') {
164 missing_argument = (int)':';
165 print_error_message = 0;
166 ++optstring;
167 }
168
169 if (nextchar == nullptr) { // scan starting at argv[optind]
170 if (nonopt_count > 0) { // previous scan skipped over some non-option arguments
171 shift((char**)argv);
172 } else {
173 previous_optind = optind;
174 }
175 }
176
177 if (optind >= argc) {
178 // all command-line arguments have been scanned
179 return -1;
180 }
181
182 if (nextchar == nullptr) { // scan starting at argv[optind], skip over any non-option elements
183 while ((optind + nonopt_count < argc) &&
184 (argv[optind + nonopt_count][0] != '-' || argv[optind + nonopt_count][1] == 0)) {
185 ++nonopt_count;
186 }
187
188 if (optind + nonopt_count == argc) {
189 // no more options
190 nonopt_count = 0;
191 return -1;
192 }
193
194 optind += nonopt_count;
195 }
196
197 if (nextchar == nullptr && optind < argc) { // scan starting at argv[optind]
198 nextchar = argv[optind];
199 }
200
201 if (nextchar == argv[optind] && *nextchar == '-') {
202 ++nextchar;
203 if (*nextchar == '-' && nextchar[1] == 0) {
204 // double-dash marks the end of the option scan
205 nextchar = nullptr;
206 shift((char**)argv);
207 return -1;
208 } else if (*nextchar == '-' && *(++nextchar)) {
209 // search long option
210 optopt =
211 parse_long_option(argc, argv, longopts, longindex, print_error_message, missing_argument);
212 nextchar = nullptr;
213 return optopt;
214 } else if (*nextchar == 0) {
215 // missing option character
216 optind += 1;
217 nextchar = nullptr;
218 return -1;
219 }
220 }
221
222 // search short option
223 const char* option;
224 optopt = *nextchar++;
225 if ((option = strchr(optstring, optopt)) == nullptr) {
226 // cannot find option
227 if (print_error_message) {
228 fprintf(stderr, "unknown option -- %c\n", optopt);
229 }
230 return (int)'?';
231 }
232 ++option;
233
234 if (*option++ != ':') {
235 // no argument required
236 if (!*nextchar) {
237 ++optind;
238 nextchar = nullptr;
239 }
240 } else {
241 if (*nextchar) {
242 // if argument is in the same argv, always set optarg
243 optarg = nextchar;
244 ++optind;
245 nextchar = nullptr;
246 } else if (argc <= ++optind) {
247 // no argument found
248 nextchar = nullptr;
249 optarg = nullptr;
250
251 if (*option != ':') {
252 // mandatory argument is missing
253 if (print_error_message) {
254 fprintf(stderr, "missing mandatory argument -- %c\n", optopt);
255 }
256 return missing_argument;
257 }
258 } else {
259 // argument is in next argv
260 nextchar = nullptr;
261
262 if (*option != ':' &&
263 ((argv[optind][0] == '-' && (argv[optind][1] != 0 && argv[optind][1] != '-')) ||
264 (argv[optind][0] == '-' && argv[optind][1] == '-' && argv[optind][2] != 0))) {
265 // argument is mandatory, but must not start with a dash or a double-dash
266 // or must be exactly dash or double-dash.
267 if (print_error_message) {
268 fprintf(stderr, "missing mandatory argument -- %c\n", optopt);
269 }
270 optarg = nullptr;
271 return missing_argument;
272 }
273
274 if (*option != ':') {
275 // argument is mandatory
276 optarg = argv[optind++];
277 } else {
278 // Argument is optional but not in the same argv, set optarg to null.
279 // User is in charge of interpreting argv[optind] and increment it
280 // if it considers its a valid argument.
281 optarg = nullptr;
282 }
283 }
284 }
285
286 return optopt;
287}
288
289#endif