sl@0
|
1 |
/***************************************************************************
|
sl@0
|
2 |
*
|
sl@0
|
3 |
* $Id: cmdopt.cpp 348342 2005-11-23 02:03:26Z sebor $
|
sl@0
|
4 |
*
|
sl@0
|
5 |
************************************************************************
|
sl@0
|
6 |
*
|
sl@0
|
7 |
* Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave
|
sl@0
|
8 |
* Software division. Licensed under the Apache License, Version 2.0 (the
|
sl@0
|
9 |
* "License"); you may not use this file except in compliance with the
|
sl@0
|
10 |
* License. You may obtain a copy of the License at
|
sl@0
|
11 |
* http://www.apache.org/licenses/LICENSE-2.0. Unless required by
|
sl@0
|
12 |
* applicable law or agreed to in writing, software distributed under
|
sl@0
|
13 |
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
sl@0
|
14 |
* CONDITIONS OF ANY KIND, either express or implied. See the License
|
sl@0
|
15 |
* for the specific language governing permissions and limitations under
|
sl@0
|
16 |
* the License.
|
sl@0
|
17 |
*
|
sl@0
|
18 |
**************************************************************************/
|
sl@0
|
19 |
|
sl@0
|
20 |
// expand _TEST_EXPORT macros
|
sl@0
|
21 |
#define _RWSTD_TEST_SRC
|
sl@0
|
22 |
|
sl@0
|
23 |
#include <cmdopt.h>
|
sl@0
|
24 |
|
sl@0
|
25 |
#include <assert.h> // for assert
|
sl@0
|
26 |
#include <errno.h> // for errno
|
sl@0
|
27 |
#include <stdarg.h> // for va_arg, ...
|
sl@0
|
28 |
#include <stdio.h> // for fprintf
|
sl@0
|
29 |
#include <stdlib.h> // for atexit, free, malloc
|
sl@0
|
30 |
#include <string.h> // for memcpy, strcpy, strcmp, ...
|
sl@0
|
31 |
|
sl@0
|
32 |
#ifdef __ARMCC__
|
sl@0
|
33 |
#pragma diag_suppress 61
|
sl@0
|
34 |
#pragma diag_suppress 63
|
sl@0
|
35 |
#endif
|
sl@0
|
36 |
|
sl@0
|
37 |
#ifndef EINVAL
|
sl@0
|
38 |
# define EINVAL 22 /* e.g., HP-UX, Linux, Solaris */
|
sl@0
|
39 |
#endif // EINVAL
|
sl@0
|
40 |
|
sl@0
|
41 |
/**************************************************************************/
|
sl@0
|
42 |
|
sl@0
|
43 |
typedef int (optcallback_t)(int, char*[]);
|
sl@0
|
44 |
|
sl@0
|
45 |
struct cmdopts_t
|
sl@0
|
46 |
{
|
sl@0
|
47 |
char loptbuf_ [32]; // buffer for long option name
|
sl@0
|
48 |
optcallback_t *callback_; // function to call to process option
|
sl@0
|
49 |
|
sl@0
|
50 |
// counter to increment for each occurrence of an option
|
sl@0
|
51 |
// or to set to the numeric argument (when specified)
|
sl@0
|
52 |
int *pcntr_;
|
sl@0
|
53 |
|
sl@0
|
54 |
char *lopt_; // long option name
|
sl@0
|
55 |
char sopt_; // short option name
|
sl@0
|
56 |
|
sl@0
|
57 |
size_t maxcalls_; // how many times option can be invoked
|
sl@0
|
58 |
size_t ncalls_; // how many times it has been invoked
|
sl@0
|
59 |
|
sl@0
|
60 |
unsigned arg_ : 1; // option takes an argument?
|
sl@0
|
61 |
unsigned inv_ : 1; // callback invocation inverted
|
sl@0
|
62 |
unsigned envseen_ : 1; // environment option already processed
|
sl@0
|
63 |
};
|
sl@0
|
64 |
|
sl@0
|
65 |
|
sl@0
|
66 |
// total number of registered options
|
sl@0
|
67 |
static size_t ncmdopts;
|
sl@0
|
68 |
|
sl@0
|
69 |
// number of default (always defined) options
|
sl@0
|
70 |
static size_t ndefopts;
|
sl@0
|
71 |
static cmdopts_t cmdoptbuf [32];
|
sl@0
|
72 |
static cmdopts_t *cmdopts = cmdoptbuf;
|
sl@0
|
73 |
static size_t optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf;
|
sl@0
|
74 |
|
sl@0
|
75 |
/**************************************************************************/
|
sl@0
|
76 |
|
sl@0
|
77 |
static int
|
sl@0
|
78 |
rw_print_help (int argc, char *argv[])
|
sl@0
|
79 |
{
|
sl@0
|
80 |
if (1 == argc && argv && 0 == argv [0]) {
|
sl@0
|
81 |
static const char helpstr[] = {
|
sl@0
|
82 |
"Without an argument, prints this help to stdout and exits with\n"
|
sl@0
|
83 |
"a status of 0 without further processing.\n"
|
sl@0
|
84 |
"With the optional argument prints help on the option with that\n"
|
sl@0
|
85 |
"name, if one exists, and exits with a status of 0. If an option\n"
|
sl@0
|
86 |
"with the specified name does not exist, prints an error message\n"
|
sl@0
|
87 |
"to stderr and exits with a status of 1.\n"
|
sl@0
|
88 |
"The leading underscores in the name of an option are optional.\n"
|
sl@0
|
89 |
};
|
sl@0
|
90 |
|
sl@0
|
91 |
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
|
sl@0
|
92 |
|
sl@0
|
93 |
return 0;
|
sl@0
|
94 |
}
|
sl@0
|
95 |
|
sl@0
|
96 |
// the option name to get help on, if any
|
sl@0
|
97 |
const char* opthelp = 1 < argc ? argv [1] : 0;
|
sl@0
|
98 |
|
sl@0
|
99 |
// remove the optional one or two leading underscores
|
sl@0
|
100 |
if (opthelp && '-' == opthelp [0] && opthelp [1])
|
sl@0
|
101 |
opthelp += 1 + ('-' == opthelp [1]);
|
sl@0
|
102 |
|
sl@0
|
103 |
if (0 == opthelp)
|
sl@0
|
104 |
printf ("OPTIONS\n");
|
sl@0
|
105 |
|
sl@0
|
106 |
// set to a non-zero when the specified option is found
|
sl@0
|
107 |
int option_found = 0;
|
sl@0
|
108 |
|
sl@0
|
109 |
for (size_t i = 0; i != ncmdopts; ++i) {
|
sl@0
|
110 |
|
sl@0
|
111 |
// get a pointer to the name of the long option, if any
|
sl@0
|
112 |
const char* const lopt =
|
sl@0
|
113 |
cmdopts [i].lopt_ ? cmdopts [i].lopt_ : cmdopts [i].loptbuf_;
|
sl@0
|
114 |
|
sl@0
|
115 |
if (opthelp && *opthelp) {
|
sl@0
|
116 |
|
sl@0
|
117 |
if ( cmdopts [i].sopt_ == opthelp [0] && '\0' == opthelp [1]
|
sl@0
|
118 |
|| *lopt && 0 == strcmp (lopt + 1, opthelp)) {
|
sl@0
|
119 |
|
sl@0
|
120 |
// remember that we found the option whose (short
|
sl@0
|
121 |
// or long) name we're to give help on; after printing
|
sl@0
|
122 |
// the help text on the option keep looping in case
|
sl@0
|
123 |
// there is another option and callback with the same
|
sl@0
|
124 |
// name (unlikely but possible)
|
sl@0
|
125 |
option_found = 1;
|
sl@0
|
126 |
}
|
sl@0
|
127 |
else {
|
sl@0
|
128 |
// the option doesn't match, continue searching
|
sl@0
|
129 |
continue;
|
sl@0
|
130 |
}
|
sl@0
|
131 |
}
|
sl@0
|
132 |
|
sl@0
|
133 |
printf (" ");
|
sl@0
|
134 |
|
sl@0
|
135 |
if (cmdopts [i].sopt_) {
|
sl@0
|
136 |
printf ("-%c", cmdopts [i].sopt_);
|
sl@0
|
137 |
|
sl@0
|
138 |
if (lopt)
|
sl@0
|
139 |
printf (" | ");
|
sl@0
|
140 |
}
|
sl@0
|
141 |
|
sl@0
|
142 |
const char *pfx = "";
|
sl@0
|
143 |
const char *sfx = pfx;
|
sl@0
|
144 |
|
sl@0
|
145 |
if (lopt) {
|
sl@0
|
146 |
printf ("-%s", lopt);
|
sl@0
|
147 |
if ( cmdopts [i].arg_
|
sl@0
|
148 |
&& '=' != lopt [strlen (lopt) - 1]) {
|
sl@0
|
149 |
pfx = " [ ";
|
sl@0
|
150 |
sfx = " ]";
|
sl@0
|
151 |
}
|
sl@0
|
152 |
}
|
sl@0
|
153 |
|
sl@0
|
154 |
printf ("%s%s%s", pfx, cmdopts [i].arg_ ? "<arg>" : "", sfx);
|
sl@0
|
155 |
|
sl@0
|
156 |
if (_RWSTD_SIZE_MAX == cmdopts [i].maxcalls_)
|
sl@0
|
157 |
printf (" (each occurrence evaluated)\n");
|
sl@0
|
158 |
else if (1 < cmdopts [i].maxcalls_)
|
sl@0
|
159 |
printf (" (at most %u occurrences evaluated)\n",
|
sl@0
|
160 |
unsigned (cmdopts [i].maxcalls_));
|
sl@0
|
161 |
else
|
sl@0
|
162 |
printf (" (only the first occurrence evaluated)\n");
|
sl@0
|
163 |
|
sl@0
|
164 |
// invoke callback with the "--help" option
|
sl@0
|
165 |
if (cmdopts [i].callback_) {
|
sl@0
|
166 |
|
sl@0
|
167 |
char* help [2] = { 0, 0 };
|
sl@0
|
168 |
|
sl@0
|
169 |
cmdopts [i].callback_ (1, help);
|
sl@0
|
170 |
|
sl@0
|
171 |
for (const char *line = help [0]; line; ) {
|
sl@0
|
172 |
|
sl@0
|
173 |
const char* const nl = strchr (line, '\n');
|
sl@0
|
174 |
const int len = nl ? int (nl - line) : int (strlen (line));
|
sl@0
|
175 |
|
sl@0
|
176 |
printf (" %.*s\n", len, line);
|
sl@0
|
177 |
|
sl@0
|
178 |
line = nl;
|
sl@0
|
179 |
if (nl)
|
sl@0
|
180 |
++line;
|
sl@0
|
181 |
}
|
sl@0
|
182 |
}
|
sl@0
|
183 |
}
|
sl@0
|
184 |
|
sl@0
|
185 |
if (opthelp && !option_found) {
|
sl@0
|
186 |
fprintf (stderr, "Unknown option \"%s\".\n", opthelp);
|
sl@0
|
187 |
exit (1);
|
sl@0
|
188 |
}
|
sl@0
|
189 |
|
sl@0
|
190 |
exit (0);
|
sl@0
|
191 |
|
sl@0
|
192 |
return 0;
|
sl@0
|
193 |
}
|
sl@0
|
194 |
|
sl@0
|
195 |
/**************************************************************************/
|
sl@0
|
196 |
|
sl@0
|
197 |
static int
|
sl@0
|
198 |
rw_set_ignenv (int argc, char *argv[])
|
sl@0
|
199 |
{
|
sl@0
|
200 |
if (1 == argc && argv && 0 == argv [0]) {
|
sl@0
|
201 |
static const char helpstr[] = {
|
sl@0
|
202 |
"Prevents options specified in the RWSTD_TESTOPTS environment\n"
|
sl@0
|
203 |
"variable from taking effect.\n"
|
sl@0
|
204 |
"Unless this option is specified, the RWSTD_TESTOPTS environment\n"
|
sl@0
|
205 |
"variable is processed as if its value were specified on the \n"
|
sl@0
|
206 |
"command line.\n"
|
sl@0
|
207 |
"For example, setting the value of the variable to the string\n"
|
sl@0
|
208 |
"\"--verbose --no-wchar\" and invoking this program with no\n"
|
sl@0
|
209 |
"command line arguments will have the same effect as invoking\n"
|
sl@0
|
210 |
"it with the two arguments on the command line.\n"
|
sl@0
|
211 |
};
|
sl@0
|
212 |
|
sl@0
|
213 |
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
|
sl@0
|
214 |
|
sl@0
|
215 |
return 0;
|
sl@0
|
216 |
}
|
sl@0
|
217 |
|
sl@0
|
218 |
return 0;
|
sl@0
|
219 |
}
|
sl@0
|
220 |
|
sl@0
|
221 |
extern "C" {
|
sl@0
|
222 |
|
sl@0
|
223 |
static void
|
sl@0
|
224 |
rw_clear_opts ()
|
sl@0
|
225 |
{
|
sl@0
|
226 |
// reset all options, deallocating dynamically allocated storage
|
sl@0
|
227 |
|
sl@0
|
228 |
for (size_t i = 0; i != ncmdopts; ++i) {
|
sl@0
|
229 |
|
sl@0
|
230 |
// free any storage allocated for the option name
|
sl@0
|
231 |
free (cmdopts [i].lopt_);
|
sl@0
|
232 |
}
|
sl@0
|
233 |
|
sl@0
|
234 |
if (cmdopts != cmdoptbuf) {
|
sl@0
|
235 |
// free the storage allocated for all the options
|
sl@0
|
236 |
free (cmdopts);
|
sl@0
|
237 |
}
|
sl@0
|
238 |
|
sl@0
|
239 |
// reset the options pointer to point at the statically
|
sl@0
|
240 |
// allocated buffer and the count back to 0
|
sl@0
|
241 |
ncmdopts = 0;
|
sl@0
|
242 |
cmdopts = cmdoptbuf;
|
sl@0
|
243 |
optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf;
|
sl@0
|
244 |
}
|
sl@0
|
245 |
|
sl@0
|
246 |
}
|
sl@0
|
247 |
|
sl@0
|
248 |
static void
|
sl@0
|
249 |
rw_set_myopts ()
|
sl@0
|
250 |
{
|
sl@0
|
251 |
static int cleanup_handler_registered;
|
sl@0
|
252 |
|
sl@0
|
253 |
if (0 == cleanup_handler_registered) {
|
sl@0
|
254 |
atexit (rw_clear_opts);
|
sl@0
|
255 |
cleanup_handler_registered = 1;
|
sl@0
|
256 |
}
|
sl@0
|
257 |
|
sl@0
|
258 |
if (0 != ncmdopts)
|
sl@0
|
259 |
return;
|
sl@0
|
260 |
|
sl@0
|
261 |
static int recursive;
|
sl@0
|
262 |
|
sl@0
|
263 |
if (recursive)
|
sl@0
|
264 |
return;
|
sl@0
|
265 |
|
sl@0
|
266 |
++recursive;
|
sl@0
|
267 |
|
sl@0
|
268 |
rw_setopts ("|-help: " // argument optional
|
sl@0
|
269 |
"|-ignenv ",
|
sl@0
|
270 |
rw_print_help,
|
sl@0
|
271 |
rw_set_ignenv);
|
sl@0
|
272 |
|
sl@0
|
273 |
ndefopts = ncmdopts;
|
sl@0
|
274 |
|
sl@0
|
275 |
recursive = 0;
|
sl@0
|
276 |
}
|
sl@0
|
277 |
|
sl@0
|
278 |
/**************************************************************************/
|
sl@0
|
279 |
|
sl@0
|
280 |
//////////////////////////////////////////////////////////////////////
|
sl@0
|
281 |
// syntax of the option description string:
|
sl@0
|
282 |
//
|
sl@0
|
283 |
// opts ::= opt [ ':' | '=' | '#' ] [ @N | @* | '!' ] [ opts ]
|
sl@0
|
284 |
// opt ::= <sopt> [ '|' <lopt>]
|
sl@0
|
285 |
// ::= '|' <lopt>
|
sl@0
|
286 |
// sopt ::= char
|
sl@0
|
287 |
// lopt ::= char char*
|
sl@0
|
288 |
// char ::= A-Z a-z _ 0-9
|
sl@0
|
289 |
|
sl@0
|
290 |
_TEST_EXPORT int
|
sl@0
|
291 |
rw_vsetopts (const char *opts, va_list va)
|
sl@0
|
292 |
{
|
sl@0
|
293 |
if (0 == opts) {
|
sl@0
|
294 |
|
sl@0
|
295 |
rw_clear_opts ();
|
sl@0
|
296 |
return 0;
|
sl@0
|
297 |
}
|
sl@0
|
298 |
|
sl@0
|
299 |
rw_set_myopts ();
|
sl@0
|
300 |
|
sl@0
|
301 |
const char *next = opts;
|
sl@0
|
302 |
|
sl@0
|
303 |
for ( ; ; ++ncmdopts) {
|
sl@0
|
304 |
|
sl@0
|
305 |
while (' ' == *next)
|
sl@0
|
306 |
++next;
|
sl@0
|
307 |
|
sl@0
|
308 |
if ('\0' == *next) {
|
sl@0
|
309 |
break;
|
sl@0
|
310 |
}
|
sl@0
|
311 |
|
sl@0
|
312 |
if (ncmdopts == optbufsize) {
|
sl@0
|
313 |
|
sl@0
|
314 |
const size_t newbufsize = 2 * ncmdopts + 1;
|
sl@0
|
315 |
|
sl@0
|
316 |
cmdopts_t* const newopts =
|
sl@0
|
317 |
(cmdopts_t*)malloc (newbufsize * sizeof (cmdopts_t));
|
sl@0
|
318 |
|
sl@0
|
319 |
if (0 == newopts) {
|
sl@0
|
320 |
fprintf (stderr, "%s%d: failed to allocate memory\n",
|
sl@0
|
321 |
__FILE__, __LINE__);
|
sl@0
|
322 |
abort ();
|
sl@0
|
323 |
}
|
sl@0
|
324 |
|
sl@0
|
325 |
memcpy (newopts, cmdopts, ncmdopts * sizeof (cmdopts_t));
|
sl@0
|
326 |
|
sl@0
|
327 |
if (cmdopts != cmdoptbuf)
|
sl@0
|
328 |
free (cmdopts);
|
sl@0
|
329 |
|
sl@0
|
330 |
cmdopts = newopts;
|
sl@0
|
331 |
optbufsize = newbufsize;
|
sl@0
|
332 |
}
|
sl@0
|
333 |
|
sl@0
|
334 |
// clear the next option info
|
sl@0
|
335 |
memset (cmdopts + ncmdopts, 0, sizeof *cmdopts);
|
sl@0
|
336 |
|
sl@0
|
337 |
if ('|' != *next)
|
sl@0
|
338 |
cmdopts [ncmdopts].sopt_ = *next++;
|
sl@0
|
339 |
|
sl@0
|
340 |
if ('|' == *next) {
|
sl@0
|
341 |
const char* end = strpbrk (++next, "|@:=*!# ");
|
sl@0
|
342 |
if (0 == end)
|
sl@0
|
343 |
end = next + strlen (next);
|
sl@0
|
344 |
|
sl@0
|
345 |
// copy the option name up to but not including the delimiter
|
sl@0
|
346 |
// (except when the delimiter is the equals sign ('='), which
|
sl@0
|
347 |
// becomes the last character of the option name
|
sl@0
|
348 |
const size_t optlen = size_t (end - next) + ('=' == *end);
|
sl@0
|
349 |
|
sl@0
|
350 |
char *lopt = 0;
|
sl@0
|
351 |
|
sl@0
|
352 |
if (optlen < sizeof cmdopts [ncmdopts].loptbuf_)
|
sl@0
|
353 |
lopt = cmdopts [ncmdopts].loptbuf_;
|
sl@0
|
354 |
else {
|
sl@0
|
355 |
lopt = (char*)malloc (optlen + 1);
|
sl@0
|
356 |
cmdopts [ncmdopts].lopt_ = lopt;
|
sl@0
|
357 |
}
|
sl@0
|
358 |
|
sl@0
|
359 |
memcpy (lopt, next, optlen);
|
sl@0
|
360 |
lopt [optlen] = '\0';
|
sl@0
|
361 |
|
sl@0
|
362 |
next = end;
|
sl@0
|
363 |
}
|
sl@0
|
364 |
|
sl@0
|
365 |
// only the first occurrence of each command line option
|
sl@0
|
366 |
// causes an invocation of the callback, all subsequent
|
sl@0
|
367 |
// ones will be ignored by default
|
sl@0
|
368 |
cmdopts [ncmdopts].maxcalls_ = 1;
|
sl@0
|
369 |
|
sl@0
|
370 |
int arg_is_callback = true;
|
sl@0
|
371 |
|
sl@0
|
372 |
if ('#' == *next) {
|
sl@0
|
373 |
// insead of a pointer to a callback, the argument
|
sl@0
|
374 |
// is a pointer to an int counter that is to be
|
sl@0
|
375 |
// incremented for each occurrence of the option
|
sl@0
|
376 |
// during processing; when the option is immediately
|
sl@0
|
377 |
// followed by the equals sign ('=') and a numeric
|
sl@0
|
378 |
// argument the value of the argument will be stored
|
sl@0
|
379 |
arg_is_callback = false;
|
sl@0
|
380 |
++next;
|
sl@0
|
381 |
|
sl@0
|
382 |
// an unlimited number of occurrences of the option
|
sl@0
|
383 |
// are allowed and will be counted
|
sl@0
|
384 |
cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
|
sl@0
|
385 |
}
|
sl@0
|
386 |
else if (':' == *next || '=' == *next) {
|
sl@0
|
387 |
// ':' : argument optional
|
sl@0
|
388 |
// '=' : argument required
|
sl@0
|
389 |
cmdopts [ncmdopts].arg_ = true;
|
sl@0
|
390 |
++next;
|
sl@0
|
391 |
}
|
sl@0
|
392 |
|
sl@0
|
393 |
if ('@' == *next) {
|
sl@0
|
394 |
|
sl@0
|
395 |
++next;
|
sl@0
|
396 |
|
sl@0
|
397 |
// at most how many occurrences of an option can be processed?
|
sl@0
|
398 |
if ('*' == *next) {
|
sl@0
|
399 |
// unlimited
|
sl@0
|
400 |
cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
|
sl@0
|
401 |
++next;
|
sl@0
|
402 |
}
|
sl@0
|
403 |
else {
|
sl@0
|
404 |
// at most this many
|
sl@0
|
405 |
char *end;
|
sl@0
|
406 |
cmdopts [ncmdopts].maxcalls_ = strtoul (next, &end, 10);
|
sl@0
|
407 |
next = end;
|
sl@0
|
408 |
}
|
sl@0
|
409 |
}
|
sl@0
|
410 |
else if ('!' == *next) {
|
sl@0
|
411 |
cmdopts [ncmdopts].inv_ = true;
|
sl@0
|
412 |
++next;
|
sl@0
|
413 |
}
|
sl@0
|
414 |
|
sl@0
|
415 |
if (arg_is_callback) {
|
sl@0
|
416 |
// retrieve the callback and verify it's not null
|
sl@0
|
417 |
// (null callback is permitted in the special case when
|
sl@0
|
418 |
// the short option is '-', i.e., when setting up or
|
sl@0
|
419 |
// resetting an "unknown option" handler)
|
sl@0
|
420 |
cmdopts [ncmdopts].callback_ = va_arg (va, optcallback_t*);
|
sl@0
|
421 |
}
|
sl@0
|
422 |
else {
|
sl@0
|
423 |
// retrieve the address of the int counter where to keep
|
sl@0
|
424 |
// track of the number of occurrences of the option, or
|
sl@0
|
425 |
// where to store the value of the numeric argument of
|
sl@0
|
426 |
// the option
|
sl@0
|
427 |
cmdopts [ncmdopts].pcntr_ = va_arg (va, int*);
|
sl@0
|
428 |
}
|
sl@0
|
429 |
|
sl@0
|
430 |
if ( '-' != cmdopts [ncmdopts].sopt_
|
sl@0
|
431 |
&& 0 == cmdopts [ncmdopts].callback_
|
sl@0
|
432 |
&& 0 == cmdopts [ncmdopts].pcntr_) {
|
sl@0
|
433 |
|
sl@0
|
434 |
// get a pointer to the long option name
|
sl@0
|
435 |
const char* const lopt = cmdopts [ncmdopts].lopt_
|
sl@0
|
436 |
? cmdopts [ncmdopts].lopt_ : cmdopts [ncmdopts].loptbuf_;
|
sl@0
|
437 |
|
sl@0
|
438 |
if (*lopt)
|
sl@0
|
439 |
fprintf (stderr, "null handler for option -%s\n", lopt);
|
sl@0
|
440 |
else
|
sl@0
|
441 |
fprintf (stderr, "null handler for option -%c\n",
|
sl@0
|
442 |
cmdopts [ncmdopts].sopt_);
|
sl@0
|
443 |
|
sl@0
|
444 |
abort ();
|
sl@0
|
445 |
}
|
sl@0
|
446 |
}
|
sl@0
|
447 |
|
sl@0
|
448 |
return int (ncmdopts - ndefopts);
|
sl@0
|
449 |
}
|
sl@0
|
450 |
|
sl@0
|
451 |
/**************************************************************************/
|
sl@0
|
452 |
|
sl@0
|
453 |
_TEST_EXPORT int
|
sl@0
|
454 |
rw_setopts (const char *opts, ...)
|
sl@0
|
455 |
{
|
sl@0
|
456 |
va_list va;
|
sl@0
|
457 |
va_start (va, opts);
|
sl@0
|
458 |
const int result = rw_vsetopts (opts, va);
|
sl@0
|
459 |
va_end (va);
|
sl@0
|
460 |
return result;
|
sl@0
|
461 |
}
|
sl@0
|
462 |
|
sl@0
|
463 |
/**************************************************************************/
|
sl@0
|
464 |
|
sl@0
|
465 |
_TEST_EXPORT int
|
sl@0
|
466 |
rw_runopts (int argc, char *argv[])
|
sl@0
|
467 |
{
|
sl@0
|
468 |
rw_set_myopts ();
|
sl@0
|
469 |
|
sl@0
|
470 |
static int recursive = false;
|
sl@0
|
471 |
|
sl@0
|
472 |
// ignore options set in the environment?
|
sl@0
|
473 |
int ignenv = recursive;
|
sl@0
|
474 |
|
sl@0
|
475 |
// return status
|
sl@0
|
476 |
int status = 0;
|
sl@0
|
477 |
|
sl@0
|
478 |
// number of options processed
|
sl@0
|
479 |
int nopts = 0;
|
sl@0
|
480 |
|
sl@0
|
481 |
// index of registered option whose callback should be invoked
|
sl@0
|
482 |
// for command line options that do not match any other
|
sl@0
|
483 |
size_t not_found_inx = _RWSTD_SIZE_MAX;
|
sl@0
|
484 |
|
sl@0
|
485 |
// iterate over the command line arguments until a callback
|
sl@0
|
486 |
// returns a non-zero value or until all options have been
|
sl@0
|
487 |
// successfully processed
|
sl@0
|
488 |
for (int i = 0; i < argc && argv [i] && 0 == status; ++i) {
|
sl@0
|
489 |
|
sl@0
|
490 |
if (0 == strcmp ("--ignore-environment", argv [i])) {
|
sl@0
|
491 |
// ignore options set in the environment
|
sl@0
|
492 |
ignenv = true;
|
sl@0
|
493 |
continue;
|
sl@0
|
494 |
}
|
sl@0
|
495 |
|
sl@0
|
496 |
if (0 == strcmp ("--", argv [i])) {
|
sl@0
|
497 |
// "--" terminates options, everything
|
sl@0
|
498 |
// after it is treated as an argument
|
sl@0
|
499 |
break;
|
sl@0
|
500 |
}
|
sl@0
|
501 |
|
sl@0
|
502 |
// the name of the option without the leading dash
|
sl@0
|
503 |
const char* const optname = argv [i] + 1;
|
sl@0
|
504 |
|
sl@0
|
505 |
// look for the first equals sign
|
sl@0
|
506 |
const char* const eq = strchr (optname, '=');
|
sl@0
|
507 |
|
sl@0
|
508 |
// compute the length of the option including the equals sign (if any)
|
sl@0
|
509 |
const size_t optlen = eq ? size_t (eq - optname + 1) : strlen (optname);
|
sl@0
|
510 |
|
sl@0
|
511 |
int found = false;
|
sl@0
|
512 |
|
sl@0
|
513 |
// look up each command line option (i.e., a string that starts
|
sl@0
|
514 |
// with a dash ('-')) and invoke the callback associated with it
|
sl@0
|
515 |
for (size_t j = 0; j != ncmdopts; ++j) {
|
sl@0
|
516 |
|
sl@0
|
517 |
if ('-' == cmdopts [j].sopt_)
|
sl@0
|
518 |
not_found_inx = j;
|
sl@0
|
519 |
|
sl@0
|
520 |
if ('-' == argv [i][0]) {
|
sl@0
|
521 |
|
sl@0
|
522 |
const size_t cmplen =
|
sl@0
|
523 |
eq && cmdopts [j].pcntr_ ? optlen - 1 : optlen;
|
sl@0
|
524 |
|
sl@0
|
525 |
// get a pointer to the (possibly empty) name
|
sl@0
|
526 |
// of the long option
|
sl@0
|
527 |
const char* const lopt = cmdopts [j].lopt_ ?
|
sl@0
|
528 |
cmdopts [j].lopt_ : cmdopts [j].loptbuf_;
|
sl@0
|
529 |
|
sl@0
|
530 |
// try to match the long option first, and only if it
|
sl@0
|
531 |
// doesn't match try the short single-character option
|
sl@0
|
532 |
if ( cmplen == strlen (lopt)
|
sl@0
|
533 |
&& 0 == memcmp (optname, lopt, cmplen)
|
sl@0
|
534 |
|| cmdopts [j].sopt_
|
sl@0
|
535 |
&& optname [0] == cmdopts [j].sopt_
|
sl@0
|
536 |
&& (1 == optlen || cmdopts [j].arg_)) {
|
sl@0
|
537 |
|
sl@0
|
538 |
// matching option has been found
|
sl@0
|
539 |
found = true;
|
sl@0
|
540 |
|
sl@0
|
541 |
// ignore the option if invoked recursively (by processing
|
sl@0
|
542 |
// options set in the environment) and the option has
|
sl@0
|
543 |
// already been seen (this prevents duplicate processing
|
sl@0
|
544 |
// of options that are set both on the command line and
|
sl@0
|
545 |
// in the environment and allows option with an argument
|
sl@0
|
546 |
// set on the command line to override those set in the
|
sl@0
|
547 |
// environment)
|
sl@0
|
548 |
|
sl@0
|
549 |
if (cmdopts [j].ncalls_ && recursive)
|
sl@0
|
550 |
continue;
|
sl@0
|
551 |
|
sl@0
|
552 |
// if the option has been evaluated the maximum number
|
sl@0
|
553 |
// of times, avoid evaluating it and continue processing
|
sl@0
|
554 |
if (cmdopts [j].maxcalls_ <= cmdopts [j].ncalls_)
|
sl@0
|
555 |
continue;
|
sl@0
|
556 |
|
sl@0
|
557 |
if (cmdopts [j].callback_) {
|
sl@0
|
558 |
if (!cmdopts [j].inv_) {
|
sl@0
|
559 |
// when the command line argument matched
|
sl@0
|
560 |
// the option, invoke the callback function
|
sl@0
|
561 |
status = cmdopts [j].callback_ (argc - i, argv + i);
|
sl@0
|
562 |
}
|
sl@0
|
563 |
}
|
sl@0
|
564 |
else if (eq) {
|
sl@0
|
565 |
assert (0 != cmdopts [j].pcntr_);
|
sl@0
|
566 |
|
sl@0
|
567 |
// obtain the numeric argument
|
sl@0
|
568 |
char *end = 0;
|
sl@0
|
569 |
const long optval = strtol (eq + 1, &end, 0);
|
sl@0
|
570 |
|
sl@0
|
571 |
if (end && '\0' != *end) {
|
sl@0
|
572 |
fprintf (stderr, "expected numeric argument: %s\n",
|
sl@0
|
573 |
optname);
|
sl@0
|
574 |
ignenv = true;
|
sl@0
|
575 |
errno = EINVAL;
|
sl@0
|
576 |
status = 1;
|
sl@0
|
577 |
}
|
sl@0
|
578 |
|
sl@0
|
579 |
#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
|
sl@0
|
580 |
|
sl@0
|
581 |
else if ( optval < _RWSTD_INT_MIN
|
sl@0
|
582 |
|| _RWSTD_INT_MAX < optval) {
|
sl@0
|
583 |
fprintf (stderr, "numeric argument %ld out of range"
|
sl@0
|
584 |
" [%d, %d]: %s\n", optval,
|
sl@0
|
585 |
_RWSTD_INT_MIN, _RWSTD_INT_MAX, optname);
|
sl@0
|
586 |
ignenv = true;
|
sl@0
|
587 |
errno = EINVAL;
|
sl@0
|
588 |
status = 1;
|
sl@0
|
589 |
}
|
sl@0
|
590 |
|
sl@0
|
591 |
#endif // _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
|
sl@0
|
592 |
|
sl@0
|
593 |
else {
|
sl@0
|
594 |
*cmdopts [j].pcntr_ = optval;
|
sl@0
|
595 |
}
|
sl@0
|
596 |
}
|
sl@0
|
597 |
else {
|
sl@0
|
598 |
assert (0 != cmdopts [j].pcntr_);
|
sl@0
|
599 |
++*cmdopts [j].pcntr_;
|
sl@0
|
600 |
}
|
sl@0
|
601 |
|
sl@0
|
602 |
++cmdopts [j].ncalls_;
|
sl@0
|
603 |
|
sl@0
|
604 |
if (recursive)
|
sl@0
|
605 |
cmdopts [j].envseen_ = true;
|
sl@0
|
606 |
|
sl@0
|
607 |
++nopts;
|
sl@0
|
608 |
|
sl@0
|
609 |
if (status) {
|
sl@0
|
610 |
// when the status returned from the last callback
|
sl@0
|
611 |
// is non-0 stop further processing (including
|
sl@0
|
612 |
// any options set in the environment) and return
|
sl@0
|
613 |
// status to the caller
|
sl@0
|
614 |
ignenv = true;
|
sl@0
|
615 |
break;
|
sl@0
|
616 |
}
|
sl@0
|
617 |
}
|
sl@0
|
618 |
}
|
sl@0
|
619 |
}
|
sl@0
|
620 |
|
sl@0
|
621 |
if (!found && '-' == argv [i][0]) {
|
sl@0
|
622 |
|
sl@0
|
623 |
// invoke the appropriate error handler for an option
|
sl@0
|
624 |
// that was not found
|
sl@0
|
625 |
if (_RWSTD_SIZE_MAX != not_found_inx) {
|
sl@0
|
626 |
|
sl@0
|
627 |
// invoke the error handler set up through rw_setopts()
|
sl@0
|
628 |
// and let the handler decide whether to go on processing
|
sl@0
|
629 |
// other options or whether to abort
|
sl@0
|
630 |
status = cmdopts [not_found_inx].callback_ (argc - i, argv + i);
|
sl@0
|
631 |
if (status) {
|
sl@0
|
632 |
// no further processing done
|
sl@0
|
633 |
ignenv = true;
|
sl@0
|
634 |
break;
|
sl@0
|
635 |
}
|
sl@0
|
636 |
}
|
sl@0
|
637 |
else {
|
sl@0
|
638 |
// print an error message to stderr when no error
|
sl@0
|
639 |
// handler has been set up
|
sl@0
|
640 |
fprintf (stderr, "unknown option: %s\n", argv [i]);
|
sl@0
|
641 |
ignenv = true;
|
sl@0
|
642 |
errno = EINVAL;
|
sl@0
|
643 |
status = 1;
|
sl@0
|
644 |
break;
|
sl@0
|
645 |
}
|
sl@0
|
646 |
}
|
sl@0
|
647 |
}
|
sl@0
|
648 |
|
sl@0
|
649 |
if (!ignenv) {
|
sl@0
|
650 |
// process options from the environment
|
sl@0
|
651 |
const char* const envar = getenv ("RWSTD_TESTOPTS");
|
sl@0
|
652 |
if (envar) {
|
sl@0
|
653 |
recursive = true;
|
sl@0
|
654 |
rw_runopts (envar);
|
sl@0
|
655 |
recursive = false;
|
sl@0
|
656 |
}
|
sl@0
|
657 |
}
|
sl@0
|
658 |
|
sl@0
|
659 |
// invoke any inverted callbacks or bump their user-specified counters,
|
sl@0
|
660 |
// and reset internal counters indicating if/how many times each option
|
sl@0
|
661 |
// has been processed
|
sl@0
|
662 |
for (size_t j = 0; j != ncmdopts; ++j) {
|
sl@0
|
663 |
|
sl@0
|
664 |
if (cmdopts [j].inv_ && 0 == cmdopts [j].ncalls_ && 0 == status) {
|
sl@0
|
665 |
|
sl@0
|
666 |
if (cmdopts [j].callback_)
|
sl@0
|
667 |
status = cmdopts [j].callback_ (0, 0);
|
sl@0
|
668 |
else {
|
sl@0
|
669 |
assert (0 != cmdopts [j].pcntr_);
|
sl@0
|
670 |
++*cmdopts [j].pcntr_;
|
sl@0
|
671 |
}
|
sl@0
|
672 |
}
|
sl@0
|
673 |
|
sl@0
|
674 |
cmdopts [j].ncalls_ = 0;
|
sl@0
|
675 |
cmdopts [j].envseen_ = false;
|
sl@0
|
676 |
}
|
sl@0
|
677 |
|
sl@0
|
678 |
return status;
|
sl@0
|
679 |
}
|
sl@0
|
680 |
|
sl@0
|
681 |
/**************************************************************************/
|
sl@0
|
682 |
|
sl@0
|
683 |
_TEST_EXPORT int
|
sl@0
|
684 |
rw_runopts (const char *str)
|
sl@0
|
685 |
{
|
sl@0
|
686 |
assert (0 != str);
|
sl@0
|
687 |
|
sl@0
|
688 |
rw_set_myopts ();
|
sl@0
|
689 |
|
sl@0
|
690 |
char buf [80]; // fixed size buffer to copy `str' into
|
sl@0
|
691 |
char *pbuf = buf; // a modifiable copy of `str'
|
sl@0
|
692 |
|
sl@0
|
693 |
size_t len = strlen (str);
|
sl@0
|
694 |
if (len >= sizeof buf) {
|
sl@0
|
695 |
// allocate if necessary
|
sl@0
|
696 |
pbuf = (char*)malloc (len + 1);
|
sl@0
|
697 |
if (!pbuf)
|
sl@0
|
698 |
return -1;
|
sl@0
|
699 |
}
|
sl@0
|
700 |
|
sl@0
|
701 |
// copy `str' to modifiable buffer
|
sl@0
|
702 |
memcpy (pbuf, str, len + 1);
|
sl@0
|
703 |
|
sl@0
|
704 |
char *tmp_argv_buf [32] = { 0 }; // fixed size argv buffer
|
sl@0
|
705 |
char **argv = tmp_argv_buf; // array of arguments
|
sl@0
|
706 |
|
sl@0
|
707 |
// initial size of argument array (will grow as necessary)
|
sl@0
|
708 |
size_t argv_size = sizeof tmp_argv_buf / sizeof *tmp_argv_buf;
|
sl@0
|
709 |
size_t argc = 0; // number of arguments in array
|
sl@0
|
710 |
|
sl@0
|
711 |
int in_quotes = 0; // quoted argument being processed
|
sl@0
|
712 |
|
sl@0
|
713 |
for (char *s = pbuf; *s; ++s) {
|
sl@0
|
714 |
if ('"' == *s) {
|
sl@0
|
715 |
in_quotes = !in_quotes;
|
sl@0
|
716 |
continue;
|
sl@0
|
717 |
}
|
sl@0
|
718 |
|
sl@0
|
719 |
if (in_quotes)
|
sl@0
|
720 |
continue;
|
sl@0
|
721 |
|
sl@0
|
722 |
// split up unquoted space-separated arguments
|
sl@0
|
723 |
if (argc == 0 || ' ' == *s) {
|
sl@0
|
724 |
if (argc > 0)
|
sl@0
|
725 |
*s = 0;
|
sl@0
|
726 |
|
sl@0
|
727 |
// skip over leading spaces
|
sl@0
|
728 |
if (argc > 0 || ' ' == *s)
|
sl@0
|
729 |
while (' ' == *++s);
|
sl@0
|
730 |
|
sl@0
|
731 |
if (*s) {
|
sl@0
|
732 |
if (argc == argv_size) {
|
sl@0
|
733 |
// grow `argv' as necessary
|
sl@0
|
734 |
char **tmp = (char**)malloc (sizeof *tmp * argv_size * 2);
|
sl@0
|
735 |
if (!tmp) {
|
sl@0
|
736 |
if (argv != tmp_argv_buf)
|
sl@0
|
737 |
free (argv);
|
sl@0
|
738 |
return -1;
|
sl@0
|
739 |
}
|
sl@0
|
740 |
|
sl@0
|
741 |
// copy existing elementes and zero out any new entries
|
sl@0
|
742 |
memcpy (tmp, argv, sizeof *tmp * argv_size);
|
sl@0
|
743 |
memset (tmp + argv_size, 0, sizeof *tmp * argv_size);
|
sl@0
|
744 |
|
sl@0
|
745 |
// free existing buffer if necessary
|
sl@0
|
746 |
if (argv != tmp_argv_buf)
|
sl@0
|
747 |
free (argv);
|
sl@0
|
748 |
|
sl@0
|
749 |
// reassign buffer and increase size
|
sl@0
|
750 |
argv = tmp;
|
sl@0
|
751 |
argv_size *= 2;
|
sl@0
|
752 |
}
|
sl@0
|
753 |
|
sl@0
|
754 |
// add argument to array
|
sl@0
|
755 |
argv [argc++] = s;
|
sl@0
|
756 |
}
|
sl@0
|
757 |
}
|
sl@0
|
758 |
}
|
sl@0
|
759 |
|
sl@0
|
760 |
// process `argc' options pointed to by `argv'
|
sl@0
|
761 |
const int status = rw_runopts (int (argc), argv);
|
sl@0
|
762 |
|
sl@0
|
763 |
// free buffers if necessary
|
sl@0
|
764 |
if (argv != tmp_argv_buf)
|
sl@0
|
765 |
free (argv);
|
sl@0
|
766 |
|
sl@0
|
767 |
if (pbuf != buf)
|
sl@0
|
768 |
free (pbuf);
|
sl@0
|
769 |
|
sl@0
|
770 |
return status;
|
sl@0
|
771 |
}
|