2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #define __STDC_WANT_LIB_EXT1__ 1
27 #include "SecArgParse.h"
30 // If flag is set and argument is not, it has no_argument
31 // If flag is set and argument is set, it is optional_argument
32 // If flag is not set and argument is set, it is required_argument
33 // If flag is not set and argument is not set, what are you doing? There's no output.
35 static int argument_status(struct argument
* option
) {
39 return (!!(option
->flag
) && !(option
->argument
) ? no_argument
:
40 (!!(option
->flag
) && !!(option
->argument
) ? optional_argument
:
41 ( !(option
->flag
) && !!(option
->argument
) ? required_argument
:
45 static bool fill_long_option_array(struct argument
* options
, size_t noptions
, struct option long_options
[], size_t nloptions
) {
49 for(i
= 0; i
<= noptions
; i
++) {
50 if(longi
>= nloptions
) {
54 if(options
[i
].longname
) {
55 long_options
[longi
].name
= options
[i
].longname
;
56 long_options
[longi
].has_arg
= argument_status(&options
[i
]);
57 long_options
[longi
].flag
= options
[i
].flag
;
58 long_options
[longi
].val
= options
[i
].flagval
;
63 if(longi
>= nloptions
) {
67 long_options
[longi
].name
= NULL
;
68 long_options
[longi
].has_arg
= 0;
69 long_options
[longi
].flag
= 0;
70 long_options
[longi
].val
= 0;
75 static bool fill_short_option_array(struct argument
* options
, size_t noptions
, char* short_options
, size_t nshort_options
) {
77 for(size_t i
= 0; i
< noptions
; i
++) {
78 if(options
[i
].shortname
!= '\0') {
79 if(index
>= nshort_options
) {
82 short_options
[index
] = options
[i
].shortname
;
85 if(argument_status(&options
[i
]) == required_argument
) {
86 if(index
>= nshort_options
) {
89 short_options
[index
] = ':';
94 short_options
[index
] = '\0';
98 static void trigger(struct argument option
, char* argument
) {
100 *(option
.flag
) = option
.flagval
;
102 if(option
.argument
) {
103 asprintf(option
.argument
, "%.1048576s", argument
);
107 static size_t num_arguments(struct arguments
* args
) {
114 struct argument
* a
= args
->arguments
;
115 // Make an all-zero struct
116 struct argument final
= {};
118 // Only 1024 arguments allowed.
119 while(a
&& n
< 1024 && (memcmp(a
, &final
, sizeof(struct argument
)) != 0)) {
127 bool options_parse(int argc
, char * const *argv
, struct arguments
* args
) {
131 bool success
= false;
133 struct arguments realargs
;
134 realargs
.programname
= args
->programname
;
135 realargs
.description
= args
->description
;
136 size_t num_args
= num_arguments(args
);
137 size_t noptions
= num_args
+ 1;
138 realargs
.arguments
= (struct argument
*) malloc((noptions
+1) * sizeof(struct argument
)); // extra array slot for null array
140 struct argument help
= {.shortname
= 'h', .longname
="help", .description
="show this help message and exit"};
142 realargs
.arguments
[0] = help
;
143 // Adds one to include the null terminator struct!
144 for(size_t i
= 0; i
< num_args
+ 1; i
++) {
145 realargs
.arguments
[i
+1] = args
->arguments
[i
];
148 struct option
* long_options
= (struct option
*) malloc((noptions
+1) * sizeof(struct option
));
149 size_t short_options_length
= 2* noptions
* sizeof(char) + 2; // 2: one for -h, one for the null terminator
150 char* short_options
= (char*) malloc(short_options_length
);
152 fill_long_option_array(realargs
.arguments
, noptions
, long_options
, noptions
);
153 fill_short_option_array(realargs
.arguments
, noptions
, short_options
, short_options_length
);
156 int option_index
= 0;
157 while((c
= getopt_long(argc
, argv
, short_options
, long_options
, &option_index
)) != -1) {
158 // We have a short arg or an arg with an argument. Parse it.
160 if(option_index
== 0) {
161 // This is the --help option
162 print_usage(&realargs
);
165 struct option
* long_option
= &long_options
[option_index
];
167 for(size_t i
= 0; i
< noptions
; i
++) {
168 if(realargs
.arguments
[i
].longname
&& strncmp(long_option
->name
, realargs
.arguments
[i
].longname
, strlen(realargs
.arguments
[i
].longname
)) == 0) {
169 trigger(realargs
.arguments
[i
], optarg
);
176 // This is the --help option
177 print_usage(&realargs
);
181 for(i
= 0; i
< noptions
; i
++) {
182 if(realargs
.arguments
[i
].shortname
== c
) {
183 trigger(realargs
.arguments
[i
], optarg
);
194 bool command_triggered
= false;
195 size_t positional_argument_index
= 0;
197 for(int parg
= optind
; parg
< argc
; parg
++) {
198 for(size_t i
= 0; i
< noptions
; i
++) {
199 if(realargs
.arguments
[i
].command
) {
200 if(strcmp(argv
[parg
], realargs
.arguments
[i
].command
) == 0) {
201 trigger(realargs
.arguments
[i
], NULL
);
202 command_triggered
= true;
208 if(command_triggered
) {
212 while(positional_argument_index
< noptions
&& !realargs
.arguments
[positional_argument_index
].positional_name
) {
213 positional_argument_index
++;
216 if(positional_argument_index
>= noptions
) {
217 // no positional argument found to save
221 if(realargs
.arguments
[positional_argument_index
].argument
) {
222 *(realargs
.arguments
[positional_argument_index
].argument
) = argv
[parg
];
223 positional_argument_index
++;
233 free(realargs
.arguments
);
240 void print_usage(struct arguments
* args
) {
244 printf("usage: %s", args
->programname
? args
->programname
: "command");
246 size_t num_args
= num_arguments(args
);
248 // Print all short options
249 for(size_t i
= 0; i
< num_args
; i
++) {
250 if(args
->arguments
[i
].shortname
) {
251 printf(" [-%c", args
->arguments
[i
].shortname
);
253 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
254 printf(" %s", args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg");
261 // Print all long args->arguments that don't have short args->arguments
262 for(size_t i
= 0; i
< num_args
; i
++) {
263 if(args
->arguments
[i
].longname
&& !args
->arguments
[i
].shortname
) {
265 printf(" [--%s", args
->arguments
[i
].longname
);
267 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
268 printf(" %s", args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg");
275 // Print all commands
276 for(size_t i
= 0; i
< num_args
; i
++) {
277 if(args
->arguments
[i
].command
) {
278 printf(" [%s]", args
->arguments
[i
].command
);
282 // Print all positional arguments
283 for(size_t i
= 0; i
< num_args
; i
++) {
284 if(args
->arguments
[i
].positional_name
) {
285 if(args
->arguments
[i
].positional_optional
) {
286 printf(" [<%s>]", args
->arguments
[i
].positional_name
);
288 printf(" <%s>", args
->arguments
[i
].positional_name
);
295 if(args
->description
) {
296 printf("\n%s\n", args
->description
);
299 printf("\npositional arguments:\n");
300 for(size_t i
= 0; i
< num_args
; i
++) {
301 if(args
->arguments
[i
].positional_name
) {
302 printf(" %-31s %s\n", args
->arguments
[i
].positional_name
, args
->arguments
[i
].description
);
306 printf("\noptional arguments:\n");
307 // List all short args->arguments
308 for(size_t i
= 0; i
< num_args
; i
++) {
309 if(args
->arguments
[i
].shortname
) {
310 if(!args
->arguments
[i
].longname
) {
312 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
313 printf(" -%c %-*s", args
->arguments
[i
].shortname
, 28, "arg");
315 printf(" -%-30c", args
->arguments
[i
].shortname
);
319 printf(" -%c", args
->arguments
[i
].shortname
);
321 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
322 printf(" %s", args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg");
325 if(args
->arguments
[i
].longname
) {
326 if(argument_status(&args
->arguments
[i
]) == no_argument
) {
327 printf(", --%-*s", 28 - (int) strlen(args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg"), args
->arguments
[i
].longname
);
329 printf(", --%s %-*s", args
->arguments
[i
].longname
, 28 -5 - (int) strlen(args
->arguments
[i
].longname
) - (int) strlen(args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg"), "arg");
334 printf("%s\n", args
->arguments
[i
].description
);
338 // List all long args->arguments
339 for(size_t i
= 0; i
< num_args
; i
++) {
340 if(args
->arguments
[i
].longname
&& !args
->arguments
[i
].shortname
) {
341 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
342 printf(" --%s %-*s %s\n",
343 args
->arguments
[i
].longname
,
344 28 - (int) strlen(args
->arguments
[i
].longname
),
345 args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg",
346 args
->arguments
[i
].description
);
348 printf(" --%-29s %s\n", args
->arguments
[i
].longname
, args
->arguments
[i
].description
);
353 printf("\noptional commands:\n");
354 // Print all commands
355 for(size_t i
= 0; i
< num_args
; i
++) {
356 if(args
->arguments
[i
].command
) {
357 printf(" %-30s %s\n", args
->arguments
[i
].command
, args
->arguments
[i
].description
);