-
Notifications
You must be signed in to change notification settings - Fork 11
/
gggetopt.c
596 lines (458 loc) · 17 KB
/
gggetopt.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
/*
Copyright (c) 2010 Guillaume Gravier
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
This is a non GPL implementation of GNU getopt() and getopt_long(),
provided as a non contaminating replacement for GNU functions in
proprietary software.
For a detailed description of GNU getopt functions, see related man
pages, a copy/paste of which are provided below (though this
probably violates the GPL license).
Note that *not* all features of GNU getopt are implemented in
gggetopt, in particular the following things differs:
- only the POSIXLY_CORRECT implementation is provided by
gggetopt, which means that arguments are not permutted and the
optional initial '+' sign in optstring is not supported ('+' will
be treated as the declaration of a regular short option);
- the optional initial '-' sign in optstring, which tells getopt
to handle each non-option argument as the argument of the special
option with character 1, is not supported;
- getopt_long_only() features are not supported, thus not
permitting to reference long options with a single dash;
- the special short option switch W is not supported;
Return codes in gggetopt are as for GNU getopt, i.e.:
For short options: If a valid (correctly formed and properly
declared in optstring) short option is found, returns the
option's character. The value '?' is returned when an undeclared
option is found. In case of missing argument, '?' is returned
unless optstring starts with ':' in which case ':' is returned.
For long options: return the value declared in the longopts table
if flag is NULL; otherwise return 0. In case of an ambiguous
abbreviation, return '?'.
*/
/*
Extract of the GNU getopt manpage, describing features which apply
to gggetopt:
===============
#include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
#define _GNU_SOURCE
#include <getopt.h>
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
[...]
The getopt() function parses the command-line arguments. Its
arguments argc and argv are the argument count and array as
passed to the main() function on program invocation. An
element of argv that starts with '-' (and is not exactly "-" or
"--") is an option element. The charac- ters of this element
(aside from the initial '-') are option charac- ters. If
getopt() is called repeatedly, it returns successively each of
the option characters from each of the option elements.
If getopt() finds another option character, it returns that
character, updating the external variable optind and a static
variable nextchar so that the next call to getopt() can resume
the scan with the following option character or argv-element.
If there are no more option characters, getopt() returns -1.
Then optind is the index in argv of the first argv-element that
is not an option.
optstring is a string containing the legitimate option
characters. If such a character is followed by a colon, the
option requires an argu- ment, so getopt() places a pointer to
the following text in the same argv-element, or the text of the
following argv-element, in optarg. Two colons mean an option
takes an optional arg; if there is text in the current
argv-element (i.e., in the same word as the option name itself,
for example, "-oarg"), then it is returned in optarg, otherwise
optarg is set to zero.
[...]
If getopt() does not recognize an option character, it prints
an error message to stderr, stores the character in optopt, and
returns '?'. The calling program may prevent the error message
by setting opterr to 0.
If getopt() finds an option character in argv that was not
included in optstring, or if it detects a missing option
argument, it returns '?' and sets the external variable optopt
to the actual option character. If the first character [...]
of optstring is a colon (':'), then getopt() returns ':'
instead of '?' to indicate a missing option argument. If an
error was detected, and the first character of optstring is not
a colon, and the external variable opterr is non-zero (which is
the default), getopt() prints an error message.
The getopt_long() function works like getopt() except that it
also accepts long options, started out by two dashes. (If the
program accepts only long options, then optstring should be
specified as an empty string (""), not NULL.) Long option
names may be abbreviated if the abbreviation is unique or is an
exact match for some defined option. A long option may take a
parameter, of the form --arg=param or --arg param.
longopts is a pointer to the first element of an array of
struct option declared in <getopt.h> as
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
The meanings of the different fields are:
name is the name of the long option.
has_arg is: no_argument (or 0) if the option does not take an
argument; required_argument (or 1) if the option requires an
argument; or optional_argument (or 2) if the option takes an
optional argu- ment.
flag specifies how results are returned for a long option. If
flag is NULL, then getopt_long() returns val. (For example,
the calling program may set val to the equivalent short option
char- acter.) Otherwise, getopt_long() returns 0, and flag
points to a variable which is set to val if the option is
found, but left unchanged if the option is not found.
val is the value to return, or to load into the variable
pointed to by flag.
The last element of the array has to be filled with zeroes.
If longindex is not NULL, it points to a variable which is set
to the index of the long option relative to longopts.
[...]
===============
*/
#ifndef __GNU_LIBRARY__ /* or should it be __GNU_SOURCE */
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <stdio.h>
# include <gggetopt.h>
char * optarg = NULL;
int optind = 1;
int opterr = 1;
int optopt = 0;
/*
current index in the current argv entry
*/
char * _nextchar = NULL;
/* ---------------------------------------------------------- */
/* ----- int getopt (int, char * const *, const char *) ----- */
/* ---------------------------------------------------------- */
int getopt (int argc, char * const * argv, const char * options)
{
int _getopt_internal (int, char * const *, const char *, const struct option *, int *, int);
return _getopt_internal(argc, argv, options, NULL, NULL, 0);
}
/* --------------------------------------------------------------------------------------------- */
/* ----- int getopt_long(int, char * const *, const char *, const struct option *, int *) ----- */
/* --------------------------------------------------------------------------------------------- */
int getopt_long(int argc, char * const * argv, const char * options, const struct option * longopts, int * longidx)
{
int _getopt_internal (int, char * const *, const char *, const struct option *, int *, int);
return _getopt_internal(argc, argv, options, longopts, longidx, 0);
}
/* ----------------------------------------------------------------------------------------------------- */
/* ----- int _getopt_internal (int, char * const, const char *, const struct option *, int *, int) ----- */
/* ----------------------------------------------------------------------------------------------------- */
/*
This is the generic getopt function.
*/
int _getopt_internal (int argc, char * const * argv, const char * options, const struct option * longopts, int * longidx, int long_only)
{
const char * p;
int c;
int _getopt_long(int, char * const *, const struct option *, int *, const char *);
optarg = NULL;
/*
Check we're not in the middle of scanning bundled short
options. If we're not, then we shift optind and process.
*/
if ( _nextchar == NULL || *_nextchar == 0 ) {
if ( optind == argc)
return -1;
p = argv[optind];
/*
check we have an option
*/
if ( ( *p != '-' ) || ( *p == '-' && p[1] == 0 ) )
return (-1);
/*
we do have an option! now, we must check it's not '--' which is
not truly an option, before deciding to process that as a long
option or as a short option.
*/
if ( p[1] == '-' ) {
if ( p[2] == 0 ) { /* found '--' special option */
optind++;
return -1;
}
if ( longopts ) { /* found long option (--x*) */
optind++;
return _getopt_long(argc, argv, longopts, longidx, p + 2);
}
}
/*
so now it is a short option: simply initialize _nextchar to the
first option switch and proceed as if we were following up on
previously seen bundled option switches.
*/
_nextchar = (char *) (p + 1);
}
/*
we're here because we are searching for a short option pointed to by _nextchar
*/
c = *_nextchar++;
if ( *_nextchar == 0 ) /* end of current argv entry -> shift to next one */
optind++;
/* search if switch is declared as an option */
p = options;
while ( *p ) {
if ( *p == c )
break;
p++;
}
/* if not found, then we're out */
if ( *p == 0 ) {
if ( opterr )
fprintf(stderr, "%s: illegal option -- %c\n", argv[0], c);
optopt = c;
return '?';
}
if ( p[1] == ':' ) {
if ( p[2] == ':' ) { /* optional argument */
if ( *_nextchar ) {
optarg = _nextchar;
_nextchar = NULL;
optind++;
}
else
optarg = NULL;
}
else { /* required argument */
if ( *_nextchar ) {
optarg = _nextchar;
_nextchar = NULL;
optind++;
}
else {
if ( optind == argc ) {
if ( opterr )
fprintf(stderr, "%s: option requires an argument -- %c\n", argv[0], c);
optopt = c;
return ( *options == ':' ) ? ':' : '?'; // might also return ':'
}
optarg = argv[optind++];
_nextchar = NULL;
}
}
}
/* else no argument */
return (c);
}
/* --------------------------------------------------------------------------------------------- */
/* ----- int _getopt_long(int, char * const *, const struct option *, int *, const char *) ----- */
/* --------------------------------------------------------------------------------------------- */
/*
*/
int _getopt_long(int argc, char * const * argv, const struct option * longopts, int * longidx, const char * optstr)
{
int i = -1;
int ifound = -1;
int nfounds = 0;
int len = -1;
const struct option * pfound = NULL;
*longidx = -1;
/*
search for the entry in longops which (unambiguously) corresponds to optstr
*/
while ( longopts[++i].name ) {
int k = 0;
char * p = longopts[i].name;
while ( *p && optstr[k] ) { /* find longest common prefix between longopts[i].name and optstr */
if ( *p != optstr[k] )
break;
k++;
p++;
}
if ( optstr[k] != 0 && optstr[k] != '=' ) /* optstr is not a prefix of longopts[i].name -> not a match */
continue;
len = k;
if ( *p == 0 ) { /* exact match */
nfounds = 1;
ifound = i;
break;
}
/* if we get to that line, it's because optstr is a prefix of
longopts[i].name: but we have to keep on scanning longopts to
make sure that (i) optstr is a unambiguous prefix and that (ii)
there is no exact match for optstr somewhere after. */
nfounds++;
ifound = i;
}
/*
if optstr was not found or if it is ambiguous, print error message and return -1
*/
if ( ! nfounds ) {
if ( opterr )
fprintf(stderr, "unknown long option name '--%s'\n", optstr); // should remove the =val (if any) to be real clean!
return '?';
}
if ( nfounds > 1 ) {
if ( opterr )
fprintf(stderr, "%s: option `%s' is ambiguous\n", argv[0], optstr);
_nextchar = NULL;
// optind++;
optopt = 0;
return '?';
}
/*
ok, here we go, we found it! process argument of any and wrap it
up nicely.
*/
pfound = longopts + ifound;
// optind++;
_nextchar = NULL;
// fprintf (stderr, "_getopt_long(): optstr=%s matches %s (%d:%x:%d)\n", optstr, pfound->name, pfound->type, pfound->address, pfound->val);
switch ( pfound->type ) {
case optional_argument:
optarg = ( optstr[len] == '=' ) ? (char *) ( optstr + len + 1 ) : (char *) 0;
break;
case required_argument:
if ( optstr[len] == '=' )
optarg = (char *) (optstr + len + 1);
else if ( optind != argc ) {
optarg = argv[optind++];
/* check however that next arg is not '--' */
if ( strcmp(optarg, "--") == 0 ) {
if ( opterr )
fprintf(stderr, "%s: option `%s' requires an argument\n", argv[0], pfound->name);
optopt = pfound->val;
return ( *optstr == ':' ) ? ':' : '?';
}
}
else {
if ( opterr )
fprintf(stderr, "%s: option `%s' requires an argument\n", argv[0], pfound->name);
optopt = pfound->val;
return ( *optstr == ':' ) ? ':' : '?';
}
break;
case no_argument:
if ( optstr[len] == '=' ) { /* extraneous argument */
if ( opterr )
fprintf(stderr, "%s: option `--%s' doesn't allow an argument", argv[0], pfound->name);
optopt = pfound->val;
return '?';
}
break;
default: /* to please some nasty compilers */
;
}
if ( pfound->address )
*(pfound->address) = pfound->val;
if ( longidx )
*longidx = ifound;
return pfound->val;
}
#endif /* __GNU_LIBRARY__ */
#ifdef TEST
# include <stdio.h>
# include <stdlib.h>
int main (int argc, char ** argv)
{
int c;
int digit_optind = 0;
opterr = 1;
if ( argc == 1 )
{
fprintf(stderr, "valid short options: abc:d:0123456789\n");
fprintf(stderr, "valid long options with no arguments: append, create\n");
fprintf(stderr, "valid long options with optional arguments: verbose\n");
fprintf(stderr, "valid long options with required arguments: add, file\n");
}
while (1)
{
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] =
{
{"add", required_argument, 0, 0},
{"append", no_argument, 0, 0},
{"delete", required_argument, 0, 0},
{"verbose", optional_argument, 0, 0},
{"create", no_argument, 0, 0},
{"file", required_argument, 0, 0},
{0, 0, 0, 0}
};
c = getopt_long (argc, argv, ":abc:d:v::0123456789", long_options, &option_index);
fprintf(stderr, "getopt_long returned %d (optind=%d optopt=%d)\n", c, optind, optopt);
if (c == -1)
break;
switch (c)
{
case 0:
printf ("option %s", long_options[option_index].name);
if (optarg)
printf (" with arg %s", optarg);
printf ("\n");
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (digit_optind != 0 && digit_optind != this_option_optind)
printf ("digits occur in two different argv-elements.\n");
digit_optind = this_option_optind;
printf ("option %c\n", c);
break;
case 'a':
printf ("option a\n");
break;
case 'b':
printf ("option b\n");
break;
case 'c':
printf ("option c with value `%s'\n", optarg);
break;
case 'd':
printf ("option d with value `%s'\n", optarg);
break;
case 'v':
printf ("option with value `%s'\n", optarg ? optarg : "(null)");
break;
case '?':
break;
default:
printf ("?? getopt returned character code 0%o (%c) ??\n", c, c);
}
}
if (optind < argc)
{
printf ("non-option ARGV-elements: ");
while (optind < argc)
printf ("%s ", argv[optind++]);
printf ("\n");
}
exit (0);
}
#endif /* TEST */