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