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
*) calloc((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
&& long_option
->name
&& 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
);
195 bool command_triggered
= false;
196 size_t positional_argument_index
= 0;
198 for(int parg
= optind
; parg
< argc
; parg
++) {
199 for(size_t i
= 0; i
< noptions
; i
++) {
200 if(realargs
.arguments
[i
].command
) {
201 if(strcmp(argv
[parg
], realargs
.arguments
[i
].command
) == 0) {
202 trigger(realargs
.arguments
[i
], NULL
);
203 command_triggered
= true;
209 if(command_triggered
) {
213 while(positional_argument_index
< noptions
&& !realargs
.arguments
[positional_argument_index
].positional_name
) {
214 positional_argument_index
++;
217 if(positional_argument_index
>= noptions
) {
218 // no positional argument found to save
222 if(realargs
.arguments
[positional_argument_index
].argument
) {
223 *(realargs
.arguments
[positional_argument_index
].argument
) = argv
[parg
];
224 positional_argument_index
++;
234 free(realargs
.arguments
);
241 void print_usage(struct arguments
* args
) {
245 printf("usage: %s", args
->programname
? args
->programname
: "command");
247 size_t num_args
= num_arguments(args
);
249 // Print all short options
250 for(size_t i
= 0; i
< num_args
; i
++) {
251 if(args
->arguments
[i
].shortname
) {
252 printf(" [-%c", args
->arguments
[i
].shortname
);
254 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
255 printf(" %s", args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg");
262 // Print all long args->arguments that don't have short args->arguments
263 for(size_t i
= 0; i
< num_args
; i
++) {
264 if(args
->arguments
[i
].longname
&& !args
->arguments
[i
].shortname
) {
266 printf(" [--%s", args
->arguments
[i
].longname
);
268 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
269 printf(" %s", args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg");
276 // Print all commands
277 for(size_t i
= 0; i
< num_args
; i
++) {
278 if(args
->arguments
[i
].command
) {
279 printf(" [%s]", args
->arguments
[i
].command
);
283 // Print all positional arguments
284 for(size_t i
= 0; i
< num_args
; i
++) {
285 if(args
->arguments
[i
].positional_name
) {
286 if(args
->arguments
[i
].positional_optional
) {
287 printf(" [<%s>]", args
->arguments
[i
].positional_name
);
289 printf(" <%s>", args
->arguments
[i
].positional_name
);
296 if(args
->description
) {
297 printf("\n%s\n", args
->description
);
300 printf("\npositional arguments:\n");
301 for(size_t i
= 0; i
< num_args
; i
++) {
302 if(args
->arguments
[i
].positional_name
) {
303 printf(" %-31s %s\n", args
->arguments
[i
].positional_name
, args
->arguments
[i
].description
);
307 printf("\noptional arguments:\n");
308 // List all short args->arguments
309 for(size_t i
= 0; i
< num_args
; i
++) {
310 if(args
->arguments
[i
].shortname
) {
311 if(!args
->arguments
[i
].longname
) {
313 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
314 printf(" -%c %-*s", args
->arguments
[i
].shortname
, 28, "arg");
316 printf(" -%-30c", args
->arguments
[i
].shortname
);
320 printf(" -%c", args
->arguments
[i
].shortname
);
322 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
323 printf(" %s", args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg");
326 if(args
->arguments
[i
].longname
) {
327 if(argument_status(&args
->arguments
[i
]) == no_argument
) {
328 printf(", --%-*s", 28 - (int) strlen(args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg"), args
->arguments
[i
].longname
);
330 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");
335 printf("%s\n", args
->arguments
[i
].description
);
339 // List all long args->arguments
340 for(size_t i
= 0; i
< num_args
; i
++) {
341 if(args
->arguments
[i
].longname
&& !args
->arguments
[i
].shortname
) {
342 if(argument_status(&args
->arguments
[i
]) != no_argument
) {
343 printf(" --%s %-*s %s\n",
344 args
->arguments
[i
].longname
,
345 28 - (int) strlen(args
->arguments
[i
].longname
),
346 args
->arguments
[i
].argname
? args
->arguments
[i
].argname
: "arg",
347 args
->arguments
[i
].description
);
349 printf(" --%-29s %s\n", args
->arguments
[i
].longname
, args
->arguments
[i
].description
);
354 printf("\noptional commands:\n");
355 // Print all commands
356 for(size_t i
= 0; i
< num_args
; i
++) {
357 if(args
->arguments
[i
].command
) {
358 printf(" %-30s %s\n", args
->arguments
[i
].command
, args
->arguments
[i
].description
);