1 /**
2 * @file fg_argparser.c
3 * @brief Command line argument parser
4 */
5
6 /*
7 * Copyright (C) 2014 Felix Rietig <felix.rietig@rwth-aachen.de>
8 * Copyright (C) 2006-2013 Antonio Diaz Diaz <antonio@gnu.org>
9 *
10 * This file is part of Flowgrind. It is based on the POSIX/GNU
11 * command line argument parser 'arg_parser' origninally written by
12 * Antonio Diaz Diaz.
13 *
14 * Flowgrind is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Flowgrind is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Flowgrind. If not, see <http://www.gnu.org/licenses/>.
26 *
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif /* HAVE_CONFIG_H */
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdbool.h>
37
38 #include "fg_definitions.h"
39 #include "fg_argparser.h"
40
41 /**
42 * Assure at least a minimum size for buffer @p buf.
43 *
44 * @param[in] buf pointer to buffer
45 * @param[in] min_size minimum size @p buf should hold in bytes
46 * @return pointer to the newly allocated buffer
47 */
ap_resize_buffer(void * buf,const int min_size)48 static void *ap_resize_buffer(void *buf, const int min_size)
49 {
50 if (buf)
51 buf = realloc(buf, min_size);
52 else
53 buf = malloc(min_size);
54 return buf;
55 }
56
57 /**
58 * Store a parsed option in the state of the arg-parser given by @p ap.
59 *
60 * @param[in] ap pointer to the arg-parser state
61 * @param[in] option_index index of the option to store
62 * @param[in] long_opt true if this option was a long option
63 * @param[in] argument argument string for this option (may be empty)
64 * @return return true for success, or false for failure
65 */
push_back_record(struct arg_parser * const ap,const int option_index,bool long_opt,const char * const argument)66 static bool push_back_record(struct arg_parser *const ap, const int option_index,
67 bool long_opt, const char *const argument)
68 {
69 const int len = strlen(argument);
70 struct ap_Record *p;
71 void *tmp = ap_resize_buffer(ap->data,
72 (ap->data_size + 1) * sizeof(struct ap_Record));
73 if (!tmp)
74 return false;
75 ap->data = (struct ap_Record *)tmp;
76 p = &(ap->data[ap->data_size]);
77 p->option_index = option_index;
78 p->argument = 0;
79 tmp = ap_resize_buffer(p->argument, len + 1);
80 if (!tmp)
81 return false;
82 p->argument = (char *)tmp;
83 strncpy(p->argument, argument, len + 1);
84
85 if (long_opt) {
86 if (!asprintf(&p->opt_string, "--%s", ap->options[option_index].name))
87 return false;
88 } else {
89 if (!asprintf(&p->opt_string, "-%c", ap->options[option_index].code))
90 return false;
91 }
92
93 ++ap->data_size;
94 return true;
95 }
96
97 /**
98 * Add an error message to the arg-parser @p ap.
99 *
100 * @param[in] ap pointer to the arg-parser state
101 * @param[in] msg error string
102 * @return return true for success, or false for failure
103 */
add_error(struct arg_parser * const ap,const char * const msg)104 static bool add_error(struct arg_parser *const ap, const char *const msg)
105 {
106 const int len = strlen(msg);
107 void *tmp = ap_resize_buffer(ap->error, ap->error_size + len + 1);
108 if (!tmp)
109 return false;
110 ap->error = (char *)tmp;
111 strncpy(ap->error + ap->error_size, msg, len + 1);
112 ap->error_size += len;
113
114 return true;
115 }
116
117 /**
118 * Free all space required by the arg-parser @p ap.
119 *
120 * @param[in] ap pointer to the arg-parser state
121 */
free_data(struct arg_parser * const ap)122 static void free_data(struct arg_parser *const ap)
123 {
124 for (int i = 0; i < ap->data_size; ++i) {
125 free(ap->data[i].argument);
126 free(ap->data[i].opt_string);
127 }
128 if (ap->data) {
129 free(ap->data);
130 ap->data = 0;
131 }
132 ap->data_size = 0;
133
134 for (int i = 0; i < ap->num_options; ++i) {
135 free(ap->options[i].name);
136 free(ap->options[i].mutex);
137 }
138 free(ap->options);
139 ap->num_options = 0;
140 }
141
142 /**
143 * Parses a long option and adds it to the record of arg-parser @p ap.
144 *
145 * @param[in] ap pointer to the arg-parser state
146 * @param[in] opt long option string
147 * @param[in] arg option argument string
148 * @param[in] options array containing all defined options which may be parsed
149 * @param[in] argindp pointer to the index in the command line argument array.
150 * The value will be automatically updated
151 * @return return true for success, or false for failure
152 */
parse_long_option(struct arg_parser * const ap,const char * const opt,const char * const arg,const struct ap_Option options[],int * const argindp)153 static bool parse_long_option(struct arg_parser *const ap,
154 const char *const opt, const char *const arg,
155 const struct ap_Option options[],
156 int *const argindp)
157 {
158 unsigned len;
159 int index = -1;
160 char exact = 0, ambig = 0;
161
162 for (len = 0; opt[len + 2] && opt[len + 2] != '='; ++len) ;
163
164 /* Test all long options for either exact match or abbreviated matches */
165 for (int i = 0; options[i].code != 0; ++i)
166 if (options[i].name
167 && strncmp(options[i].name, &opt[2], len) == 0) {
168 /* Exact match found */
169 if (strlen(options[i].name) == len) {
170 index = i;
171 exact = 1;
172 break;
173 /* First nonexact match found */
174 } else if (index < 0) {
175 index = i;
176 /* Second or later nonexact match found */
177 } else if (options[index].code != options[i].code ||
178 options[index].has_arg != options[i].has_arg) {
179 ambig = 1;
180 }
181 }
182
183 if (ambig && !exact) {
184 add_error(ap, "option '");
185 add_error(ap, opt);
186 add_error(ap, "' is ambiguous");
187 return true;
188 }
189
190 /* nothing found */
191 if (index < 0) {
192 add_error(ap, "unrecognized option '");
193 add_error(ap, opt);
194 add_error(ap, "'");
195 return true;
196 }
197
198 ++*argindp;
199
200 /* '--<long_option>=<argument>' syntax */
201 if (opt[len + 2]) {
202 if (options[index].has_arg == ap_no) {
203 add_error(ap, "option '--");
204 add_error(ap, options[index].name);
205 add_error(ap, "' doesn't allow an argument");
206 return true;
207 }
208 if (options[index].has_arg == ap_yes && !opt[len + 3]) {
209 add_error(ap, "option '--");
210 add_error(ap, options[index].name);
211 add_error(ap, "' requires an argument");
212 return true;
213 }
214 return push_back_record(ap, index, true, &opt[len + 3]);
215 }
216
217 if (options[index].has_arg == ap_yes) {
218 if (!arg || !arg[0]) {
219 add_error(ap, "option '--");
220 add_error(ap, options[index].name);
221 add_error(ap, "' requires an argument");
222 return true;
223 }
224 ++*argindp;
225 return push_back_record(ap, index, true, arg);
226 }
227
228 return push_back_record(ap, index, true, "");
229 }
230
231 /**
232 * Parses a short option and adds it to the record of arg-parser @p ap.
233 *
234 * @param[in] ap pointer to the arg-parser state
235 * @param[in] opt long option string
236 * @param[in] arg option argument string
237 * @param[in] options array containing all defined options which may be parsed
238 * @param[in] argindp pointer to the index in the command line argument array.
239 * The value will be automatically updated
240 * @return return true for success, or false for failure
241 */
parse_short_option(struct arg_parser * const ap,const char * const opt,const char * const arg,const struct ap_Option options[],int * const argindp)242 static bool parse_short_option(struct arg_parser *const ap,
243 const char *const opt, const char *const arg,
244 const struct ap_Option options[],
245 int *const argindp)
246 {
247 int cind = 1; /* character index in opt */
248
249 while (cind > 0) {
250 int index = -1;
251 const unsigned char code = opt[cind];
252 char code_str[2];
253 code_str[0] = code;
254 code_str[1] = 0;
255
256 if (code != 0)
257 for (int i = 0; options[i].code; ++i)
258 if (code == options[i].code) {
259 index = i;
260 break;
261 }
262
263 if (index < 0) {
264 add_error(ap, "invalid option -- ");
265 add_error(ap, code_str);
266 return true;
267 }
268
269 /* opt finished */
270 if (opt[++cind] == 0) {
271 ++*argindp;
272 cind = 0;
273 }
274
275 if (options[index].has_arg != ap_no && cind > 0 && opt[cind]) {
276 if (!push_back_record(ap, index, false, &opt[cind]))
277 return false;
278 ++*argindp;
279 cind = 0;
280 } else if (options[index].has_arg == ap_yes) {
281 if (!arg || !arg[0]) {
282 add_error(ap, "option requires an argument -- ");
283 add_error(ap, code_str);
284 return true;
285 }
286 ++*argindp;
287 cind = 0;
288 if (!push_back_record(ap, index, false, arg))
289 return false;
290 } else if (!push_back_record(ap, index, false, "")) {
291 return false;
292 }
293 }
294
295 return true;
296 }
297
298 /**
299 * Determines number of options in @p options.
300 *
301 * Counting all options until an option with code 0 is found.
302 *
303 * @param[in] options array of user-defined options
304 * @return number of options in @p options
305 */
get_num_options(const struct ap_Option options[])306 static int get_num_options(const struct ap_Option options[])
307 {
308 int i;
309 for (i=0; options[i].code; i++){}
310 return i;
311 }
312
313 /**
314 * Get the number of mutex in the option definitions.
315 *
316 * Searching for the greatest mutex ID in all options.
317 *
318 * @param[in] options array of user-defined options
319 * @return number of mutex in the option definitions
320 */
get_mutex_count(const struct ap_Option options[])321 static int get_mutex_count(const struct ap_Option options[])
322 {
323 int num = 0;
324
325 for (int i=0; options[i].code; i++)
326 for (int *mutex = options[i].mutex; mutex && *mutex; mutex++)
327 ASSIGN_MAX(num, *mutex);
328
329 return num;
330 }
331
332 /**
333 * Copy @p options into the arg-parser @p ap.
334 *
335 * This is a deep copy including strings and arrays.
336 *
337 * @param[in] ap arg-parser
338 * @param[in] options options struct to copy
339 * @return return true for success, or false for failure
340 */
copy_options(struct arg_parser * const ap,const struct ap_Option options[])341 static bool copy_options(struct arg_parser *const ap,
342 const struct ap_Option options[])
343 {
344
345 ap->num_options = get_num_options(options);
346 if (!ap->num_options)
347 return false;
348 ap->options = malloc(sizeof(struct ap_Option)*ap->num_options);
349 if (!ap->options)
350 return false;
351
352 for (int i=0; i < ap->num_options; i++) {
353 ap->options[i] = options[i];
354 if (options[i].name)
355 ap->options[i].name = strdup(options[i].name);
356
357 if (options[i].mutex) {
358 /* get number of mutex items */
359 int num;
360 for (num = 0; options[i].mutex[num]; num++);
361
362 /* now copy into new array */
363 ap->options[i].mutex = malloc(sizeof(int)*
364 (num+1));
365 if (!ap->options[i].mutex)
366 return false;
367 for (int e=0; e<=num; e++)
368 ap->options[i].mutex[e] = options[i].mutex[e];
369 }
370 }
371 return true;
372 }
373
ap_init(struct arg_parser * const ap,const int argc,const char * const argv[],const struct ap_Option options[],const char in_order)374 bool ap_init(struct arg_parser *const ap,
375 const int argc, const char *const argv[],
376 const struct ap_Option options[], const char in_order)
377 {
378 const char **non_options = 0; /* skipped non-options */
379 int non_options_size = 0; /* number of skipped non-options */
380 int argind = 1; /* index in argv */
381
382 if (!copy_options(ap,options))
383 return false;
384
385 ap->num_mutex = get_mutex_count(options);
386
387 ap->data = 0;
388 ap->error = 0;
389 ap->data_size = 0;
390 ap->error_size = 0;
391 if (argc < 2 || !argv || !options)
392 return true;
393
394 while (argind < argc) {
395 const unsigned char ch1 = argv[argind][0];
396 const unsigned char ch2 = (ch1 ? argv[argind][1] : 0);
397
398 if (ch1 == '-' && ch2) { /* we found an option */
399 const char *const opt = argv[argind];
400 const char *const arg =
401 (argind + 1 < argc) ? argv[argind + 1] : 0;
402 if (ch2 == '-') {
403 if (!argv[argind][2]) {
404 ++argind; /* we found "--" */
405 break;
406 } else {
407 if (!parse_long_option
408 (ap, opt, arg, options, &argind))
409 return false;
410 }
411 } else {
412 if (!parse_short_option
413 (ap, opt, arg, options, &argind))
414 return false;
415 }
416 if (ap->error)
417 break;
418 } else {
419 if (!in_order) {
420 void *tmp = ap_resize_buffer(non_options, (non_options_size + 1) *
421 sizeof *non_options);
422 if (!tmp)
423 return false;
424 non_options = (const char **)tmp;
425 non_options[non_options_size++] = argv[argind++];
426 } else if (!push_back_record(ap, ap->num_options, false, argv[argind++])) {
427 return false;
428 }
429 }
430 }
431
432 if (ap->error) {
433 free_data(ap);
434 } else {
435 for (int i = 0; i < non_options_size; ++i)
436 if (!push_back_record(ap, ap->num_options, false, non_options[i]))
437 return false;
438 while (argind < argc)
439 if (!push_back_record(ap, ap->num_options, false, argv[argind++]))
440 return false;
441 }
442
443 if (non_options)
444 free(non_options);
445 return true;
446 }
447
ap_free(struct arg_parser * const ap)448 void ap_free(struct arg_parser *const ap)
449 {
450 free_data(ap);
451 if (ap->error) {
452 free(ap->error);
453 ap->error = 0;
454 }
455 ap->error_size = 0;
456 }
457
ap_error(const struct arg_parser * const ap)458 const char *ap_error(const struct arg_parser *const ap)
459 {
460 return ap->error;
461 }
462
ap_arguments(const struct arg_parser * const ap)463 int ap_arguments(const struct arg_parser *const ap)
464 {
465 return ap->data_size;
466 }
467
ap_code(const struct arg_parser * const ap,const int i)468 int ap_code(const struct arg_parser *const ap, const int i)
469 {
470 if (i >= 0 && i < ap_arguments(ap)) {
471 int index = ap->data[i].option_index;
472 return ap->options[index].code;
473 } else {
474 return false;
475 }
476 }
477
ap_argument(const struct arg_parser * const ap,const int i)478 const char *ap_argument(const struct arg_parser *const ap, const int i)
479 {
480 if (i >= 0 && i < ap_arguments(ap))
481 return ap->data[i].argument;
482 else
483 return "";
484 }
485
ap_opt_string(const struct arg_parser * const ap,const int i)486 const char *ap_opt_string(const struct arg_parser *const ap, const int i)
487 {
488 if (i >= 0 && i < ap_arguments(ap))
489 return ap->data[i].opt_string;
490 else
491 return "";
492 }
493
ap_option(const struct arg_parser * const ap,const int i)494 const struct ap_Option *ap_option(const struct arg_parser *const ap,
495 const int i)
496 {
497 if (i >= 0 && i < ap_arguments(ap))
498 return &ap->options[ap->data[i].option_index];
499 else
500 return false;
501 }
502
ap_is_used(const struct arg_parser * const ap,int code)503 bool ap_is_used(const struct arg_parser *const ap, int code)
504 {
505 bool ret = false;
506
507 for (int i=0; i < ap->data_size; i++)
508 if (ap_code(ap, i) == code) {
509 ret = true;
510 break;
511 }
512
513 return ret;
514 }
515
ap_init_mutex_state(const struct arg_parser * const ap,struct ap_Mutex_state * const ms)516 bool ap_init_mutex_state(const struct arg_parser *const ap,
517 struct ap_Mutex_state *const ms)
518 {
519 ms->seen_records = malloc(sizeof(int)*ap->num_mutex);
520 if(!ap->num_mutex || !ms->seen_records)
521 return false;
522 memset(ms->seen_records,0,sizeof(int)*ap->num_mutex);
523 ms->num_mutex = ap->num_mutex;
524 return true;
525 }
526
ap_check_mutex(const struct arg_parser * const ap,const struct ap_Mutex_state * const ms,const int i,int * conflict)527 bool ap_check_mutex(const struct arg_parser *const ap,
528 const struct ap_Mutex_state *const ms,
529 const int i, int *conflict)
530 {
531 if(ap->num_mutex != ms->num_mutex)
532 return false;
533
534 *conflict = 0;
535
536 if (i < 0 || i >= ap_arguments(ap) || !ap->num_mutex)
537 return false;
538
539 int index = ap->data[i].option_index;
540 for (int *mutex = ap->options[index].mutex; mutex && *mutex; mutex++) {
541 if (ms->seen_records[*mutex-1]) {
542 *conflict = ms->seen_records[*mutex-1]-1;
543 if (ap->data[*conflict].option_index != index)
544 return true;
545 else
546 *conflict = 0;
547 }
548 }
549
550 return false;
551 }
552
ap_set_mutex(const struct arg_parser * const ap,struct ap_Mutex_state * const ms,const int i)553 bool ap_set_mutex(const struct arg_parser *const ap,
554 struct ap_Mutex_state *const ms, const int i)
555 {
556 if(ap->num_mutex != ms->num_mutex)
557 return false;
558
559 if (i < 0 || i >= ap_arguments(ap) || !ap->num_mutex)
560 return false;
561
562 int index = ap->data[i].option_index;
563 for (int *mutex = ap->options[index].mutex; mutex && *mutex; mutex++)
564 ms->seen_records[*mutex-1] = i+1;
565
566 return true;
567 }
568
ap_set_check_mutex(const struct arg_parser * const ap,struct ap_Mutex_state * const ms,const int i,int * conflict)569 bool ap_set_check_mutex(const struct arg_parser *const ap,
570 struct ap_Mutex_state *const ms, const int i,
571 int *conflict)
572 {
573 bool ret = ap_check_mutex(ap, ms, i, conflict);
574 ap_set_mutex(ap, ms, i);
575 return ret;
576 }
577
ap_reset_mutex(struct ap_Mutex_state * const ms)578 void ap_reset_mutex(struct ap_Mutex_state *const ms)
579 {
580 memset(ms->seen_records,0,sizeof(int)*ms->num_mutex);
581 }
582
ap_free_mutex_state(struct ap_Mutex_state * const ms)583 void ap_free_mutex_state(struct ap_Mutex_state *const ms)
584 {
585 if (ms->seen_records) {
586 free(ms->seen_records);
587 ms->seen_records = 0;
588 ms->num_mutex = 0;
589 }
590 }
591
592