X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=command-line-arguments.c;h=db73ca5efb896aaafbbe1b8a85437e20ba431b4f;hp=76b185f85be6d04487f6a107be03d820bf6cd234;hb=733ccfabca350f65a1d0ba1f64792a8a436da273;hpb=2ee1d8e1c70ce20728bc3faada389a4802636352 diff --git a/command-line-arguments.c b/command-line-arguments.c index 76b185f8..db73ca5e 100644 --- a/command-line-arguments.c +++ b/command-line-arguments.c @@ -6,69 +6,137 @@ /* Search the array of keywords for a given argument, assigning the - output variable to the corresponding value. Return FALSE if nothing + output variable to the corresponding value. Return false if nothing matches. */ -static notmuch_bool_t -_process_keyword_arg (const notmuch_opt_desc_t *arg_desc, const char *arg_str) { +static bool +_process_keyword_arg (const notmuch_opt_desc_t *arg_desc, char next, const char *arg_str) { - const notmuch_keyword_t *keywords = arg_desc->keywords; + const notmuch_keyword_t *keywords; - while (keywords->name) { - if (strcmp (arg_str, keywords->name) == 0) { - if (arg_desc->output_var) { - *((int *)arg_desc->output_var) = keywords->value; - } - return TRUE; - } - keywords++; + if (next == '\0') { + /* No keyword given */ + arg_str = ""; + } + + for (keywords = arg_desc->keywords; keywords->name; keywords++) { + if (strcmp (arg_str, keywords->name) != 0) + continue; + + if (arg_desc->opt_flags) + *arg_desc->opt_flags |= keywords->value; + else + *arg_desc->opt_keyword = keywords->value; + + return true; } - fprintf (stderr, "unknown keyword: %s\n", arg_str); - return FALSE; + if (next != '\0') + fprintf (stderr, "Unknown keyword argument \"%s\" for option \"%s\".\n", arg_str, arg_desc->name); + else + fprintf (stderr, "Option \"%s\" needs a keyword argument.\n", arg_desc->name); + return false; } -static notmuch_bool_t +static bool _process_boolean_arg (const notmuch_opt_desc_t *arg_desc, char next, const char *arg_str) { + bool value; + + if (next == '\0' || strcmp (arg_str, "true") == 0) { + value = true; + } else if (strcmp (arg_str, "false") == 0) { + value = false; + } else { + fprintf (stderr, "Unknown argument \"%s\" for (boolean) option \"%s\".\n", arg_str, arg_desc->name); + return false; + } + + *arg_desc->opt_bool = value; + + return true; +} - if (next == 0) { - *((notmuch_bool_t *)arg_desc->output_var) = TRUE; - return TRUE; +static bool +_process_int_arg (const notmuch_opt_desc_t *arg_desc, char next, const char *arg_str) { + + char *endptr; + if (next == '\0' || arg_str[0] == '\0') { + fprintf (stderr, "Option \"%s\" needs an integer argument.\n", arg_desc->name); + return false; } - if (strcmp (arg_str, "false") == 0) { - *((notmuch_bool_t *)arg_desc->output_var) = FALSE; - return TRUE; + + *arg_desc->opt_int = strtol (arg_str, &endptr, 10); + if (*endptr == '\0') + return true; + + fprintf (stderr, "Unable to parse argument \"%s\" for option \"%s\" as an integer.\n", + arg_str, arg_desc->name); + return false; +} + +static bool +_process_string_arg (const notmuch_opt_desc_t *arg_desc, char next, const char *arg_str) { + + if (next == '\0') { + fprintf (stderr, "Option \"%s\" needs a string argument.\n", arg_desc->name); + return false; } - if (strcmp (arg_str, "true") == 0) { - *((notmuch_bool_t *)arg_desc->output_var) = TRUE; - return TRUE; + if (arg_str[0] == '\0' && ! arg_desc->allow_empty) { + fprintf (stderr, "String argument for option \"%s\" must be non-empty.\n", arg_desc->name); + return false; } - return FALSE; + *arg_desc->opt_string = arg_str; + return true; +} + +/* Return number of non-NULL opt_* fields in opt_desc. */ +static int _opt_set_count (const notmuch_opt_desc_t *opt_desc) +{ + return + !!opt_desc->opt_inherit + + !!opt_desc->opt_bool + + !!opt_desc->opt_int + + !!opt_desc->opt_keyword + + !!opt_desc->opt_flags + + !!opt_desc->opt_string + + !!opt_desc->opt_position; +} + +/* Return true if opt_desc is valid. */ +static bool _opt_valid (const notmuch_opt_desc_t *opt_desc) +{ + int n = _opt_set_count (opt_desc); + + if (n > 1) + INTERNAL_ERROR ("more than one non-NULL opt_* field for argument \"%s\"", + opt_desc->name); + + return n > 0; } /* - Search for the {pos_arg_index}th position argument, return FALSE if + Search for the {pos_arg_index}th position argument, return false if that does not exist. */ -notmuch_bool_t +bool parse_position_arg (const char *arg_str, int pos_arg_index, const notmuch_opt_desc_t *arg_desc) { int pos_arg_counter = 0; - while (arg_desc->opt_type != NOTMUCH_OPT_END){ - if (arg_desc->opt_type == NOTMUCH_OPT_POSITION) { + while (_opt_valid (arg_desc)) { + if (arg_desc->opt_position) { if (pos_arg_counter == pos_arg_index) { - if (arg_desc->output_var) { - *((const char **)arg_desc->output_var) = arg_str; - } - return TRUE; + *arg_desc->opt_position = arg_str; + if (arg_desc->present) + *arg_desc->present = true; + return true; } pos_arg_counter++; } arg_desc++; } - return FALSE; + return false; } /* @@ -76,64 +144,75 @@ parse_position_arg (const char *arg_str, int pos_arg_index, * parse a possible value, and assign to *output_var */ -notmuch_bool_t -parse_option (const char *arg, - const notmuch_opt_desc_t *options) { +int +parse_option (int argc, char **argv, const notmuch_opt_desc_t *options, int opt_index) +{ + assert(argv); + + const char *_arg = argv[opt_index]; - assert(arg); + assert(_arg); assert(options); - arg += 2; - - const notmuch_opt_desc_t *try = options; - while (try->opt_type != NOTMUCH_OPT_END) { - if (try->name && strncmp (arg, try->name, strlen (try->name)) == 0) { - char next = arg[strlen (try->name)]; - const char *value= arg+strlen(try->name)+1; - - char *endptr; - - /* Everything but boolean arguments (switches) needs a - * delimiter, and a non-zero length value. Boolean - * arguments may take an optional =true or =false value. - */ - if (next != '=' && next != ':' && next != 0) return FALSE; - if (next == 0) { - if (try->opt_type != NOTMUCH_OPT_BOOLEAN) - return FALSE; - } else { - if (value[0] == 0) return FALSE; - } + const char *arg = _arg + 2; /* _arg starts with -- */ + const notmuch_opt_desc_t *try; - if (try->output_var == NULL) - INTERNAL_ERROR ("output pointer NULL for option %s", try->name); - - switch (try->opt_type) { - case NOTMUCH_OPT_KEYWORD: - return _process_keyword_arg (try, value); - break; - case NOTMUCH_OPT_BOOLEAN: - return _process_boolean_arg (try, next, value); - break; - case NOTMUCH_OPT_INT: - *((int *)try->output_var) = strtol (value, &endptr, 10); - return (*endptr == 0); - break; - case NOTMUCH_OPT_STRING: - *((const char **)try->output_var) = value; - return TRUE; - break; - case NOTMUCH_OPT_POSITION: - case NOTMUCH_OPT_END: - default: - INTERNAL_ERROR ("unknown or unhandled option type %d", try->opt_type); - /*UNREACHED*/ - } + const char *next_arg = NULL; + if (opt_index < argc - 1 && strncmp (argv[opt_index + 1], "--", 2) != 0) + next_arg = argv[opt_index + 1]; + + for (try = options; _opt_valid (try); try++) { + if (try->opt_inherit) { + int new_index = parse_option (argc, argv, try->opt_inherit, opt_index); + if (new_index >= 0) + return new_index; + } + + if (! try->name) + continue; + + if (strncmp (arg, try->name, strlen (try->name)) != 0) + continue; + + char next = arg[strlen (try->name)]; + const char *value = arg + strlen(try->name) + 1; + + /* + * If we have not reached the end of the argument (i.e. the + * next character is not a space or delimiter) then the + * argument could still match a longer option name later in + * the option table. + */ + if (next != '=' && next != ':' && next != '\0') + continue; + + if (next == '\0' && next_arg != NULL && ! try->opt_bool) { + next = ' '; + value = next_arg; + opt_index ++; } - try++; + + bool opt_status = false; + if (try->opt_keyword || try->opt_flags) + opt_status = _process_keyword_arg (try, next, value); + else if (try->opt_bool) + opt_status = _process_boolean_arg (try, next, value); + else if (try->opt_int) + opt_status = _process_int_arg (try, next, value); + else if (try->opt_string) + opt_status = _process_string_arg (try, next, value); + else + INTERNAL_ERROR ("unknown or unhandled option \"%s\"", try->name); + + if (! opt_status) + return -1; + + if (try->present) + *try->present = true; + + return opt_index+1; } - fprintf (stderr, "Unrecognized option: --%s\n", arg); - return FALSE; + return -1; } /* See command-line-arguments.h for description */ @@ -142,7 +221,7 @@ parse_arguments (int argc, char **argv, const notmuch_opt_desc_t *options, int opt_index) { int pos_arg_index = 0; - notmuch_bool_t more_args = TRUE; + bool more_args = true; while (more_args && opt_index < argc) { if (strncmp (argv[opt_index],"--",2) != 0) { @@ -155,17 +234,16 @@ parse_arguments (int argc, char **argv, } } else { + int prev_opt_index = opt_index; if (strlen (argv[opt_index]) == 2) return opt_index+1; - more_args = parse_option (argv[opt_index], options); - if (more_args) { - opt_index++; - } else { - opt_index = -1; + opt_index = parse_option (argc, argv, options, opt_index); + if (opt_index < 0) { + fprintf (stderr, "Unrecognized option: %s\n", argv[prev_opt_index]); + more_args = false; } - } }