9
9
#include <stdlib.h>
10
10
#include <string.h>
11
11
#include <stdio.h>
12
+ #include <limits.h>
12
13
#include <assert.h>
13
14
14
15
#include "adopt.h"
26
27
# define INLINE (type ) static inline type
27
28
#endif
28
29
29
- #define spec_is_named_type (x ) \
30
+ #define spec_is_option_type (x ) \
30
31
((x)->type == ADOPT_TYPE_BOOL || \
31
32
(x)->type == ADOPT_TYPE_SWITCH || \
32
33
(x)->type == ADOPT_TYPE_VALUE)
33
34
34
- INLINE (const adopt_spec * ) spec_byname (
35
- adopt_parser * parser ,
36
- const char * name ,
37
- size_t namelen ,
38
- int * is_converse )
35
+ INLINE (const adopt_spec * ) spec_for_long (
36
+ int * is_negated ,
37
+ int * has_value ,
38
+ const char * * value ,
39
+ const adopt_parser * parser ,
40
+ const char * arg )
39
41
{
40
42
const adopt_spec * spec ;
43
+ char * eql ;
44
+ size_t eql_pos ;
41
45
42
- * is_converse = 0 ;
46
+ arg += 2 ;
47
+ eql = strrchr (arg , '=' );
48
+ eql_pos = (eql = strrchr (arg , '=' )) ? (size_t )(eql - arg ) : strlen (arg );
43
49
44
50
for (spec = parser -> specs ; spec -> type ; ++ spec ) {
45
- if (spec -> type == ADOPT_TYPE_LITERAL && namelen == 0 )
51
+ /* Handle -- (everything after this is literal) */
52
+ if (spec -> type == ADOPT_TYPE_LITERAL && arg [0 ] == '\0' )
46
53
return spec ;
47
54
48
- /* Handle " no-" prefix for boolean types */
55
+ /* Handle -- no-option arguments for bool types */
49
56
if (spec -> type == ADOPT_TYPE_BOOL &&
50
- strlen (spec -> name ) + 3 == namelen &&
51
- strncmp (name , "no-" , 3 ) == 0 &&
52
- strncmp (name + 3 , spec -> name , namelen ) == 0 ) {
53
- * is_converse = 1 ;
57
+ strncmp (arg , "no-" , 3 ) == 0 &&
58
+ strcmp (arg + 3 , spec -> name ) == 0 ) {
59
+ * is_negated = 1 ;
54
60
return spec ;
55
61
}
56
62
57
- if (spec_is_named_type (spec ) &&
63
+ /* Handle the typical --option arguments */
64
+ if (spec_is_option_type (spec ) &&
58
65
spec -> name &&
59
- strlen (spec -> name ) == namelen &&
60
- strncmp (name , spec -> name , namelen ) == 0 )
66
+ strcmp (arg , spec -> name ) == 0 )
67
+ return spec ;
68
+
69
+ /* Handle --option=value arguments */
70
+ if (spec -> type == ADOPT_TYPE_VALUE &&
71
+ eql &&
72
+ strncmp (arg , spec -> name , eql_pos ) == 0 &&
73
+ spec -> name [eql_pos ] == '\0' ) {
74
+ * has_value = 1 ;
75
+ * value = arg [eql_pos + 1 ] ? & arg [eql_pos + 1 ] : NULL ;
61
76
return spec ;
77
+ }
62
78
}
63
79
64
80
return NULL ;
65
81
}
66
82
67
- INLINE (const adopt_spec * ) spec_byalias (adopt_parser * parser , char alias )
83
+ INLINE (const adopt_spec * ) spec_for_short (
84
+ const char * * value ,
85
+ const adopt_parser * parser ,
86
+ const char * arg )
68
87
{
69
88
const adopt_spec * spec ;
70
89
71
90
for (spec = parser -> specs ; spec -> type ; ++ spec ) {
72
- if (spec_is_named_type (spec ) && alias == spec -> alias )
91
+ /* Handle -svalue short options with a value */
92
+ if (spec -> type == ADOPT_TYPE_VALUE &&
93
+ arg [1 ] == spec -> alias &&
94
+ arg [2 ] != '\0' ) {
95
+ * value = & arg [2 ];
73
96
return spec ;
97
+ }
98
+
99
+ /* Handle typical -s short options */
100
+ if (arg [1 ] == spec -> alias &&
101
+ arg [2 ] == '\0' ) {
102
+ * value = NULL ;
103
+ return spec ;
104
+ }
74
105
}
75
106
76
107
return NULL ;
77
108
}
78
109
79
- INLINE (const adopt_spec * ) spec_nextarg (adopt_parser * parser )
110
+ INLINE (const adopt_spec * ) spec_for_arg (adopt_parser * parser )
80
111
{
81
112
const adopt_spec * spec ;
82
113
size_t args = 0 ;
@@ -127,15 +158,13 @@ INLINE(void) consume_choices(const adopt_spec *spec, adopt_parser *parser)
127
158
static adopt_status_t parse_long (adopt_opt * opt , adopt_parser * parser )
128
159
{
129
160
const adopt_spec * spec ;
130
- char * arg = parser -> args [parser -> idx ++ ], * name = arg + 2 , * eql ;
131
- int converse = 0 ;
132
- size_t namelen ;
133
-
134
- namelen = (eql = strrchr (arg , '=' )) ? (size_t )(eql - name ) : strlen (name );
161
+ char * arg = parser -> args [parser -> idx ++ ];
162
+ const char * value = NULL ;
163
+ int is_negated = 0 , has_value = 0 ;
135
164
136
165
opt -> arg = arg ;
137
166
138
- if ((spec = spec_byname ( parser , name , namelen , & converse )) == NULL ) {
167
+ if ((spec = spec_for_long ( & is_negated , & has_value , & value , parser , arg )) == NULL ) {
139
168
opt -> spec = NULL ;
140
169
opt -> status = ADOPT_STATUS_UNKNOWN_OPTION ;
141
170
goto done ;
@@ -147,16 +176,18 @@ static adopt_status_t parse_long(adopt_opt *opt, adopt_parser *parser)
147
176
if (spec -> type == ADOPT_TYPE_LITERAL )
148
177
parser -> in_literal = 1 ;
149
178
179
+ /* --bool or --no-bool */
150
180
else if (spec -> type == ADOPT_TYPE_BOOL && spec -> value )
151
- * ((int * )spec -> value ) = !converse ;
181
+ * ((int * )spec -> value ) = !is_negated ;
152
182
183
+ /* --switch */
153
184
else if (spec -> type == ADOPT_TYPE_SWITCH && spec -> value )
154
185
* ((int * )spec -> value ) = spec -> switch_value ;
155
186
156
187
/* Parse values as "--foo=bar" or "--foo bar" */
157
188
else if (spec -> type == ADOPT_TYPE_VALUE ) {
158
- if (eql && * ( eql + 1 ) )
159
- opt -> value = eql + 1 ;
189
+ if (has_value )
190
+ opt -> value = ( char * ) value ;
160
191
else if ((parser -> idx + 1 ) <= parser -> args_len )
161
192
opt -> value = parser -> args [parser -> idx ++ ];
162
193
@@ -181,11 +212,12 @@ static adopt_status_t parse_long(adopt_opt *opt, adopt_parser *parser)
181
212
static adopt_status_t parse_short (adopt_opt * opt , adopt_parser * parser )
182
213
{
183
214
const adopt_spec * spec ;
184
- char * arg = parser -> args [parser -> idx ++ ], alias = * (arg + 1 );
215
+ char * arg = parser -> args [parser -> idx ++ ];
216
+ const char * value ;
185
217
186
218
opt -> arg = arg ;
187
219
188
- if ((spec = spec_byalias ( parser , alias )) == NULL ) {
220
+ if ((spec = spec_for_short ( & value , parser , arg )) == NULL ) {
189
221
opt -> spec = NULL ;
190
222
opt -> status = ADOPT_STATUS_UNKNOWN_OPTION ;
191
223
goto done ;
@@ -201,8 +233,8 @@ static adopt_status_t parse_short(adopt_opt *opt, adopt_parser *parser)
201
233
202
234
/* Parse values as "-ifoo" or "-i foo" */
203
235
if (spec -> type == ADOPT_TYPE_VALUE ) {
204
- if (strlen ( arg ) > 2 )
205
- opt -> value = arg + 2 ;
236
+ if (value )
237
+ opt -> value = ( char * ) value ;
206
238
else if ((parser -> idx + 1 ) <= parser -> args_len )
207
239
opt -> value = parser -> args [parser -> idx ++ ];
208
240
@@ -224,7 +256,7 @@ static adopt_status_t parse_short(adopt_opt *opt, adopt_parser *parser)
224
256
225
257
static adopt_status_t parse_arg (adopt_opt * opt , adopt_parser * parser )
226
258
{
227
- const adopt_spec * spec = spec_nextarg (parser );
259
+ const adopt_spec * spec = spec_for_arg (parser );
228
260
229
261
opt -> spec = spec ;
230
262
opt -> arg = parser -> args [parser -> idx ];
@@ -236,7 +268,10 @@ static adopt_status_t parse_arg(adopt_opt *opt, adopt_parser *parser)
236
268
if (spec -> value )
237
269
* ((char * * * )spec -> value ) = & parser -> args [parser -> idx ];
238
270
239
- /* Args consume all the remaining arguments. */
271
+ /*
272
+ * We have started a list of arguments; the remainder of
273
+ * given arguments need not be examined.
274
+ */
240
275
parser -> in_args = (parser -> args_len - parser -> idx );
241
276
parser -> idx = parser -> args_len ;
242
277
opt -> args_len = parser -> in_args ;
@@ -252,11 +287,32 @@ static adopt_status_t parse_arg(adopt_opt *opt, adopt_parser *parser)
252
287
return opt -> status ;
253
288
}
254
289
290
+ static int support_gnu_style (unsigned int flags )
291
+ {
292
+ if ((flags & ADOPT_PARSE_FORCE_GNU ) != 0 )
293
+ return 1 ;
294
+
295
+ if ((flags & ADOPT_PARSE_GNU ) == 0 )
296
+ return 0 ;
297
+
298
+ /* TODO: Windows */
299
+ #if defined(_WIN32 ) && defined(UNICODE )
300
+ if (_wgetenv (L"POSIXLY_CORRECT" ) != NULL )
301
+ return 0 ;
302
+ #else
303
+ if (getenv ("POSIXLY_CORRECT" ) != NULL )
304
+ return 0 ;
305
+ #endif
306
+
307
+ return 1 ;
308
+ }
309
+
255
310
void adopt_parser_init (
256
311
adopt_parser * parser ,
257
312
const adopt_spec specs [],
258
313
char * * args ,
259
- size_t args_len )
314
+ size_t args_len ,
315
+ unsigned int flags )
260
316
{
261
317
assert (parser );
262
318
@@ -265,6 +321,103 @@ void adopt_parser_init(
265
321
parser -> specs = specs ;
266
322
parser -> args = args ;
267
323
parser -> args_len = args_len ;
324
+ parser -> flags = flags ;
325
+
326
+ parser -> needs_sort = support_gnu_style (flags );
327
+ }
328
+
329
+ INLINE (const adopt_spec * ) spec_for_sort (
330
+ int * needs_value ,
331
+ const adopt_parser * parser ,
332
+ const char * arg )
333
+ {
334
+ int is_negated , has_value = 0 ;
335
+ const char * value ;
336
+ const adopt_spec * spec = NULL ;
337
+
338
+ * needs_value = 0 ;
339
+
340
+ if (strncmp (arg , "--" , 2 ) == 0 ) {
341
+ spec = spec_for_long (& is_negated , & has_value , & value , parser , arg );
342
+ * needs_value = !has_value ;
343
+ }
344
+
345
+ else if (strncmp (arg , "-" , 1 ) == 0 ) {
346
+ spec = spec_for_short (& value , parser , arg );
347
+ * needs_value = (value == NULL );
348
+ }
349
+
350
+ return spec ;
351
+ }
352
+
353
+ /*
354
+ * Some parsers allow for handling arguments like "file1 --help file2";
355
+ * this is done by re-sorting the arguments in-place; emulate that.
356
+ */
357
+ static int sort_gnu_style (adopt_parser * parser )
358
+ {
359
+ size_t i , j , insert_idx = parser -> idx , offset ;
360
+ const adopt_spec * spec ;
361
+ char * option , * value ;
362
+ int needs_value , changed = 0 ;
363
+
364
+ parser -> needs_sort = 0 ;
365
+
366
+ for (i = parser -> idx ; i < parser -> args_len ; i ++ ) {
367
+ spec = spec_for_sort (& needs_value , parser , parser -> args [i ]);
368
+
369
+ /* Not a "-" or "--" prefixed option. No change. */
370
+ if (!spec )
371
+ continue ;
372
+
373
+ /* A "--" alone means remaining args are literal. */
374
+ if (spec -> type == ADOPT_TYPE_LITERAL )
375
+ break ;
376
+
377
+ option = parser -> args [i ];
378
+
379
+ /*
380
+ * If the argument is a value type and doesn't already
381
+ * have a value (eg "--foo=bar" or "-fbar") then we need
382
+ * to copy the next argument as its value.
383
+ */
384
+ if (spec -> type == ADOPT_TYPE_VALUE && needs_value ) {
385
+ /*
386
+ * A required value is not provided; set parser
387
+ * index to this value so that we fail on it.
388
+ */
389
+ if (i + 1 >= parser -> args_len ) {
390
+ parser -> idx = i ;
391
+ return 1 ;
392
+ }
393
+
394
+ value = parser -> args [i + 1 ];
395
+ offset = 1 ;
396
+ } else {
397
+ value = NULL ;
398
+ offset = 0 ;
399
+ }
400
+
401
+ /* Caller error if args[0] is an option. */
402
+ if (i == 0 )
403
+ return 0 ;
404
+
405
+ /* Shift args up one (or two) and insert the option */
406
+ for (j = i ; j > insert_idx ; j -- )
407
+ parser -> args [j + offset ] = parser -> args [j - 1 ];
408
+
409
+ parser -> args [insert_idx ] = option ;
410
+
411
+ if (value )
412
+ parser -> args [insert_idx + 1 ] = value ;
413
+
414
+ insert_idx += (1 + offset );
415
+ i += offset ;
416
+
417
+ changed = 1 ;
418
+ }
419
+
420
+ return changed ;
268
421
}
269
422
270
423
adopt_status_t adopt_parser_next (adopt_opt * opt , adopt_parser * parser )
@@ -278,19 +431,26 @@ adopt_status_t adopt_parser_next(adopt_opt *opt, adopt_parser *parser)
278
431
return ADOPT_STATUS_DONE ;
279
432
}
280
433
281
- /* Handle arguments in long form, those beginning with "--" */
434
+ /* Handle options in long form, those beginning with "--" */
282
435
if (strncmp (parser -> args [parser -> idx ], "--" , 2 ) == 0 &&
283
436
!parser -> in_literal )
284
437
return parse_long (opt , parser );
285
438
286
- /* Handle arguments in short form, those beginning with "-" */
439
+ /* Handle options in short form, those beginning with "-" */
287
440
else if (strncmp (parser -> args [parser -> idx ], "-" , 1 ) == 0 &&
288
441
!parser -> in_literal )
289
442
return parse_short (opt , parser );
290
443
291
- /* Handle "free" arguments, those without a dash */
292
- else
293
- return parse_arg (opt , parser );
444
+ /*
445
+ * We've reached the first "bare" argument. In POSIX mode, all
446
+ * remaining items on the command line are arguments. In GNU
447
+ * mode, there may be long or short options after this. Sort any
448
+ * options up to this position then re-parse the current position.
449
+ */
450
+ if (parser -> needs_sort && sort_gnu_style (parser ))
451
+ return adopt_parser_next (opt , parser );
452
+
453
+ return parse_arg (opt , parser );
294
454
}
295
455
296
456
INLINE (int ) spec_included (const adopt_spec * * specs , const adopt_spec * spec )
@@ -361,7 +521,7 @@ adopt_status_t adopt_parse(
361
521
const adopt_spec * * given_specs ;
362
522
size_t given_idx = 0 ;
363
523
364
- adopt_parser_init (& parser , specs , args , args_len );
524
+ adopt_parser_init (& parser , specs , args , args_len , flags );
365
525
366
526
given_specs = alloca (sizeof (const adopt_spec * ) * (args_len + 1 ));
367
527
0 commit comments