/** * @file fg_argparser.c * @brief Command line argument parser */ /* * Copyright (C) 2014 Felix Rietig * Copyright (C) 2006-2013 Antonio Diaz Diaz * * This file is part of Flowgrind. It is based on the POSIX/GNU * command line argument parser 'arg_parser' origninally written by * Antonio Diaz Diaz. * * Flowgrind is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Flowgrind is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Flowgrind. If not, see . * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include #include #include "fg_definitions.h" #include "fg_argparser.h" /** * Assure at least a minimum size for buffer @p buf. * * @param[in] buf pointer to buffer * @param[in] min_size minimum size @p buf should hold in bytes * @return pointer to the newly allocated buffer */ static void *ap_resize_buffer(void *buf, const int min_size) { if (buf) buf = realloc(buf, min_size); else buf = malloc(min_size); return buf; } /** * Store a parsed option in the state of the arg-parser given by @p ap. * * @param[in] ap pointer to the arg-parser state * @param[in] option_index index of the option to store * @param[in] long_opt true if this option was a long option * @param[in] argument argument string for this option (may be empty) * @return return true for success, or false for failure */ static bool push_back_record(struct arg_parser *const ap, const int option_index, bool long_opt, const char *const argument) { const int len = strlen(argument); struct ap_Record *p; void *tmp = ap_resize_buffer(ap->data, (ap->data_size + 1) * sizeof(struct ap_Record)); if (!tmp) return false; ap->data = (struct ap_Record *)tmp; p = &(ap->data[ap->data_size]); p->option_index = option_index; p->argument = 0; tmp = ap_resize_buffer(p->argument, len + 1); if (!tmp) return false; p->argument = (char *)tmp; strncpy(p->argument, argument, len + 1); if (long_opt) { if (!asprintf(&p->opt_string, "--%s", ap->options[option_index].name)) return false; } else { if (!asprintf(&p->opt_string, "-%c", ap->options[option_index].code)) return false; } ++ap->data_size; return true; } /** * Add an error message to the arg-parser @p ap. * * @param[in] ap pointer to the arg-parser state * @param[in] msg error string * @return return true for success, or false for failure */ static bool add_error(struct arg_parser *const ap, const char *const msg) { const int len = strlen(msg); void *tmp = ap_resize_buffer(ap->error, ap->error_size + len + 1); if (!tmp) return false; ap->error = (char *)tmp; strncpy(ap->error + ap->error_size, msg, len + 1); ap->error_size += len; return true; } /** * Free all space required by the arg-parser @p ap. * * @param[in] ap pointer to the arg-parser state */ static void free_data(struct arg_parser *const ap) { for (int i = 0; i < ap->data_size; ++i) { free(ap->data[i].argument); free(ap->data[i].opt_string); } if (ap->data) { free(ap->data); ap->data = 0; } ap->data_size = 0; for (int i = 0; i < ap->num_options; ++i) { free(ap->options[i].name); free(ap->options[i].mutex); } free(ap->options); ap->num_options = 0; } /** * Parses a long option and adds it to the record of arg-parser @p ap. * * @param[in] ap pointer to the arg-parser state * @param[in] opt long option string * @param[in] arg option argument string * @param[in] options array containing all defined options which may be parsed * @param[in] argindp pointer to the index in the command line argument array. * The value will be automatically updated * @return return true for success, or false for failure */ static bool parse_long_option(struct arg_parser *const ap, const char *const opt, const char *const arg, const struct ap_Option options[], int *const argindp) { unsigned len; int index = -1; char exact = 0, ambig = 0; for (len = 0; opt[len + 2] && opt[len + 2] != '='; ++len) ; /* Test all long options for either exact match or abbreviated matches */ for (int i = 0; options[i].code != 0; ++i) if (options[i].name && strncmp(options[i].name, &opt[2], len) == 0) { /* Exact match found */ if (strlen(options[i].name) == len) { index = i; exact = 1; break; /* First nonexact match found */ } else if (index < 0) { index = i; /* Second or later nonexact match found */ } else if (options[index].code != options[i].code || options[index].has_arg != options[i].has_arg) { ambig = 1; } } if (ambig && !exact) { add_error(ap, "option '"); add_error(ap, opt); add_error(ap, "' is ambiguous"); return true; } /* nothing found */ if (index < 0) { add_error(ap, "unrecognized option '"); add_error(ap, opt); add_error(ap, "'"); return true; } ++*argindp; /* '--=' syntax */ if (opt[len + 2]) { if (options[index].has_arg == ap_no) { add_error(ap, "option '--"); add_error(ap, options[index].name); add_error(ap, "' doesn't allow an argument"); return true; } if (options[index].has_arg == ap_yes && !opt[len + 3]) { add_error(ap, "option '--"); add_error(ap, options[index].name); add_error(ap, "' requires an argument"); return true; } return push_back_record(ap, index, true, &opt[len + 3]); } if (options[index].has_arg == ap_yes) { if (!arg || !arg[0]) { add_error(ap, "option '--"); add_error(ap, options[index].name); add_error(ap, "' requires an argument"); return true; } ++*argindp; return push_back_record(ap, index, true, arg); } return push_back_record(ap, index, true, ""); } /** * Parses a short option and adds it to the record of arg-parser @p ap. * * @param[in] ap pointer to the arg-parser state * @param[in] opt long option string * @param[in] arg option argument string * @param[in] options array containing all defined options which may be parsed * @param[in] argindp pointer to the index in the command line argument array. * The value will be automatically updated * @return return true for success, or false for failure */ static bool parse_short_option(struct arg_parser *const ap, const char *const opt, const char *const arg, const struct ap_Option options[], int *const argindp) { int cind = 1; /* character index in opt */ while (cind > 0) { int index = -1; const unsigned char code = opt[cind]; char code_str[2]; code_str[0] = code; code_str[1] = 0; if (code != 0) for (int i = 0; options[i].code; ++i) if (code == options[i].code) { index = i; break; } if (index < 0) { add_error(ap, "invalid option -- "); add_error(ap, code_str); return true; } /* opt finished */ if (opt[++cind] == 0) { ++*argindp; cind = 0; } if (options[index].has_arg != ap_no && cind > 0 && opt[cind]) { if (!push_back_record(ap, index, false, &opt[cind])) return false; ++*argindp; cind = 0; } else if (options[index].has_arg == ap_yes) { if (!arg || !arg[0]) { add_error(ap, "option requires an argument -- "); add_error(ap, code_str); return true; } ++*argindp; cind = 0; if (!push_back_record(ap, index, false, arg)) return false; } else if (!push_back_record(ap, index, false, "")) { return false; } } return true; } /** * Determines number of options in @p options. * * Counting all options until an option with code 0 is found. * * @param[in] options array of user-defined options * @return number of options in @p options */ static int get_num_options(const struct ap_Option options[]) { int i; for (i=0; options[i].code; i++){} return i; } /** * Get the number of mutex in the option definitions. * * Searching for the greatest mutex ID in all options. * * @param[in] options array of user-defined options * @return number of mutex in the option definitions */ static int get_mutex_count(const struct ap_Option options[]) { int num = 0; for (int i=0; options[i].code; i++) for (int *mutex = options[i].mutex; mutex && *mutex; mutex++) ASSIGN_MAX(num, *mutex); return num; } /** * Copy @p options into the arg-parser @p ap. * * This is a deep copy including strings and arrays. * * @param[in] ap arg-parser * @param[in] options options struct to copy * @return return true for success, or false for failure */ static bool copy_options(struct arg_parser *const ap, const struct ap_Option options[]) { ap->num_options = get_num_options(options); if (!ap->num_options) return false; ap->options = malloc(sizeof(struct ap_Option)*ap->num_options); if (!ap->options) return false; for (int i=0; i < ap->num_options; i++) { ap->options[i] = options[i]; if (options[i].name) ap->options[i].name = strdup(options[i].name); if (options[i].mutex) { /* get number of mutex items */ int num; for (num = 0; options[i].mutex[num]; num++); /* now copy into new array */ ap->options[i].mutex = malloc(sizeof(int)* (num+1)); if (!ap->options[i].mutex) return false; for (int e=0; e<=num; e++) ap->options[i].mutex[e] = options[i].mutex[e]; } } return true; } bool ap_init(struct arg_parser *const ap, const int argc, const char *const argv[], const struct ap_Option options[], const char in_order) { const char **non_options = 0; /* skipped non-options */ int non_options_size = 0; /* number of skipped non-options */ int argind = 1; /* index in argv */ if (!copy_options(ap,options)) return false; ap->num_mutex = get_mutex_count(options); ap->data = 0; ap->error = 0; ap->data_size = 0; ap->error_size = 0; if (argc < 2 || !argv || !options) return true; while (argind < argc) { const unsigned char ch1 = argv[argind][0]; const unsigned char ch2 = (ch1 ? argv[argind][1] : 0); if (ch1 == '-' && ch2) { /* we found an option */ const char *const opt = argv[argind]; const char *const arg = (argind + 1 < argc) ? argv[argind + 1] : 0; if (ch2 == '-') { if (!argv[argind][2]) { ++argind; /* we found "--" */ break; } else { if (!parse_long_option (ap, opt, arg, options, &argind)) return false; } } else { if (!parse_short_option (ap, opt, arg, options, &argind)) return false; } if (ap->error) break; } else { if (!in_order) { void *tmp = ap_resize_buffer(non_options, (non_options_size + 1) * sizeof *non_options); if (!tmp) return false; non_options = (const char **)tmp; non_options[non_options_size++] = argv[argind++]; } else if (!push_back_record(ap, ap->num_options, false, argv[argind++])) { return false; } } } if (ap->error) { free_data(ap); } else { for (int i = 0; i < non_options_size; ++i) if (!push_back_record(ap, ap->num_options, false, non_options[i])) return false; while (argind < argc) if (!push_back_record(ap, ap->num_options, false, argv[argind++])) return false; } if (non_options) free(non_options); return true; } void ap_free(struct arg_parser *const ap) { free_data(ap); if (ap->error) { free(ap->error); ap->error = 0; } ap->error_size = 0; } const char *ap_error(const struct arg_parser *const ap) { return ap->error; } int ap_arguments(const struct arg_parser *const ap) { return ap->data_size; } int ap_code(const struct arg_parser *const ap, const int i) { if (i >= 0 && i < ap_arguments(ap)) { int index = ap->data[i].option_index; return ap->options[index].code; } else { return false; } } const char *ap_argument(const struct arg_parser *const ap, const int i) { if (i >= 0 && i < ap_arguments(ap)) return ap->data[i].argument; else return ""; } const char *ap_opt_string(const struct arg_parser *const ap, const int i) { if (i >= 0 && i < ap_arguments(ap)) return ap->data[i].opt_string; else return ""; } const struct ap_Option *ap_option(const struct arg_parser *const ap, const int i) { if (i >= 0 && i < ap_arguments(ap)) return &ap->options[ap->data[i].option_index]; else return false; } bool ap_is_used(const struct arg_parser *const ap, int code) { bool ret = false; for (int i=0; i < ap->data_size; i++) if (ap_code(ap, i) == code) { ret = true; break; } return ret; } bool ap_init_mutex_state(const struct arg_parser *const ap, struct ap_Mutex_state *const ms) { ms->seen_records = malloc(sizeof(int)*ap->num_mutex); if(!ap->num_mutex || !ms->seen_records) return false; memset(ms->seen_records,0,sizeof(int)*ap->num_mutex); ms->num_mutex = ap->num_mutex; return true; } bool ap_check_mutex(const struct arg_parser *const ap, const struct ap_Mutex_state *const ms, const int i, int *conflict) { if(ap->num_mutex != ms->num_mutex) return false; *conflict = 0; if (i < 0 || i >= ap_arguments(ap) || !ap->num_mutex) return false; int index = ap->data[i].option_index; for (int *mutex = ap->options[index].mutex; mutex && *mutex; mutex++) { if (ms->seen_records[*mutex-1]) { *conflict = ms->seen_records[*mutex-1]-1; if (ap->data[*conflict].option_index != index) return true; else *conflict = 0; } } return false; } bool ap_set_mutex(const struct arg_parser *const ap, struct ap_Mutex_state *const ms, const int i) { if(ap->num_mutex != ms->num_mutex) return false; if (i < 0 || i >= ap_arguments(ap) || !ap->num_mutex) return false; int index = ap->data[i].option_index; for (int *mutex = ap->options[index].mutex; mutex && *mutex; mutex++) ms->seen_records[*mutex-1] = i+1; return true; } bool ap_set_check_mutex(const struct arg_parser *const ap, struct ap_Mutex_state *const ms, const int i, int *conflict) { bool ret = ap_check_mutex(ap, ms, i, conflict); ap_set_mutex(ap, ms, i); return ret; } void ap_reset_mutex(struct ap_Mutex_state *const ms) { memset(ms->seen_records,0,sizeof(int)*ms->num_mutex); } void ap_free_mutex_state(struct ap_Mutex_state *const ms) { if (ms->seen_records) { free(ms->seen_records); ms->seen_records = 0; ms->num_mutex = 0; } }