diff --git a/arguments.c b/arguments.c index 2f9c0c31..4fc83a0a 100644 --- a/arguments.c +++ b/arguments.c @@ -18,9 +18,9 @@ #include +#include #include #include -#include #include #include "tmux.h" @@ -66,6 +66,20 @@ args_find(struct args *args, u_char flag) return (RB_FIND(args_tree, &args->tree, &entry)); } +/* Get value as string. */ +static char * +args_value_as_string(struct args_value *value) +{ + switch (value->type) { + case ARGS_NONE: + return (xstrdup("")); + case ARGS_COMMANDS: + return (cmd_list_print(value->cmdlist, 0)); + case ARGS_STRING: + return (xstrdup(value->string)); + } +} + /* Create an empty arguments set. */ struct args * args_create(void) @@ -77,42 +91,109 @@ args_create(void) return (args); } -/* Parse an argv and argc into a new argument set. */ +/* Parse arguments into a new argument set. */ struct args * -args_parse(const struct args_parse *parse, int argc, char **argv) +args_parse(const struct args_parse *parse, struct args_value *values, + u_int count) { - struct args *args; - int opt; + struct args *args; + u_int i; + struct args_value *value; + u_char flag, argument; + const char *found, *string; + char *s; - optreset = 1; - optind = 1; - optarg = NULL; + if (count == 0) + return (args_create()); args = args_create(); - while ((opt = getopt(argc, argv, parse->template)) != -1) { - if (opt < 0) - continue; - if (opt == '?' || strchr(parse->template, opt) == NULL) { - args_free(args); - return (NULL); + for (i = 1; i < count; /* nothing */) { + value = &values[i]; + + s = args_value_as_string(value); + log_debug("%s: %u = %s", __func__, i, s); + free(s); + + if (value->type != ARGS_STRING) + break; + + string = value->string; + if (*string++ != '-' || *string == '\0') + break; + i++; + if (string[0] == '-' && string[1] == '\0') + break; + + for (;;) { + flag = *string++; + if (flag == '\0') + break; + if (!isalnum(flag)) { + args_free(args); + return (NULL); + } + found = strchr(parse->template, flag); + if (found == NULL) { + args_free(args); + return (NULL); + } + argument = *++found; + if (argument != ':') { + log_debug("%s: add -%c", __func__, flag); + args_set(args, flag, NULL); + continue; + } + if (*string != '\0') + s = xstrdup(string); + else { + if (i == count) { + args_free(args); + return (NULL); + } + s = args_value_as_string(&values[i++]); + } + log_debug("%s: add -%c = %s", __func__, flag, s); + args_set(args, flag, s); + free(s); + break; } - args_set(args, opt, optarg); - optarg = NULL; } - argc -= optind; - argv += optind; + log_debug("%s: flags end at %u of %u", __func__, i, count); + if (i != count) { + for (/* nothing */; i < count; i++) { + value = &values[i]; - args->argc = argc; - args->argv = cmd_copy_argv(argc, argv); + s = args_value_as_string(value); + log_debug("%s: %u = %s", __func__, i, s); + cmd_append_argv(&args->argc, &args->argv, s); + free(s); + } + } - if ((parse->lower != -1 && argc < parse->lower) || - (parse->upper != -1 && argc > parse->upper)) { + if ((parse->lower != -1 && args->argc < parse->lower) || + (parse->upper != -1 && args->argc > parse->upper)) { args_free(args); return (NULL); } return (args); } +/* Free a value. */ +void +args_free_value(struct args_value *value) +{ + switch (value->type) { + case ARGS_NONE: + break; + case ARGS_STRING: + free(value->string); + break; + case ARGS_COMMANDS: + cmd_list_free(value->cmdlist); + break; + } +} + /* Free an arguments set. */ void args_free(struct args *args) diff --git a/cmd-parse.y b/cmd-parse.y index 6be5d8a0..57d0e84e 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -794,39 +794,48 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, struct cmd_parse_input *pi, struct cmd_parse_result *pr) { struct cmd_parse_argument *arg; - struct cmd_list *cmdlist; + struct cmd_list *cmdlist = NULL; struct cmd *add; - char *s, **argv = NULL, *cause; - int argc = 0; + char *cause; + struct args_value *values = NULL; + u_int count = 0, idx; if (cmd_parse_expand_alias(cmd, pi, pr, &cmdlist)) return (cmdlist); TAILQ_FOREACH(arg, &cmd->arguments, entry) { + values = xreallocarray(values, count + 1, sizeof *values); switch (arg->type) { case CMD_PARSE_STRING: - cmd_append_argv(&argc, &argv, arg->string); + values[count].type = ARGS_STRING; + values[count].string = xstrdup(arg->string); break; case CMD_PARSE_COMMANDS: cmd_parse_build_commands(arg->commands, pi, pr); if (pr->status != CMD_PARSE_SUCCESS) - return (NULL); - s = cmd_list_print(pr->cmdlist, 0); - cmd_append_argv(&argc, &argv, s); - free(s); + goto out; + values[count].type = ARGS_COMMANDS; + values[count].cmdlist = pr->cmdlist; + values[count].cmdlist->references++; break; } + count++; } - add = cmd_parse(argc, argv, pi->file, pi->line, &cause); + add = cmd_parse(values, count, pi->file, pi->line, &cause); if (add == NULL) { pr->status = CMD_PARSE_ERROR; pr->error = cmd_parse_get_error(pi->file, pi->line, cause); free(cause); - return (NULL); + goto out; } cmdlist = cmd_list_new(); cmd_list_append(cmdlist, add); + +out: + for (idx = 0; idx < count; idx++) + args_free_value(&values[idx]); + free(values); return (cmdlist); } diff --git a/cmd.c b/cmd.c index ce04ce2a..930ee56c 100644 --- a/cmd.c +++ b/cmd.c @@ -496,27 +496,26 @@ ambiguous: /* Parse a single command from an argument vector. */ struct cmd * -cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) +cmd_parse(struct args_value *values, u_int count, const char *file, u_int line, + char **cause) { const struct cmd_entry *entry; - const char *name; struct cmd *cmd; struct args *args; - if (argc == 0) { + if (count == 0 || values[0].type != ARGS_STRING) { xasprintf(cause, "no command"); return (NULL); } - name = argv[0]; - - entry = cmd_find(name, cause); + entry = cmd_find(values[0].string, cause); if (entry == NULL) return (NULL); - cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name); - args = args_parse(&entry->args, argc, argv); - if (args == NULL) - goto usage; + args = args_parse(&entry->args, values, count); + if (args == NULL) { + xasprintf(cause, "usage: %s %s", entry->name, entry->usage); + return (NULL); + } cmd = xcalloc(1, sizeof *cmd); cmd->entry = entry; @@ -527,12 +526,6 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) cmd->line = line; return (cmd); - -usage: - if (args != NULL) - args_free(args); - xasprintf(cause, "usage: %s %s", entry->name, entry->usage); - return (NULL); } /* Free a command. */ diff --git a/tmux.h b/tmux.h index f8dcac49..34c66493 100644 --- a/tmux.h +++ b/tmux.h @@ -1355,9 +1355,20 @@ struct message_entry { }; TAILQ_HEAD(message_list, message_entry); +/* Argument type. */ +enum args_type { + ARGS_NONE, + ARGS_STRING, + ARGS_COMMANDS +}; + /* Argument value. */ struct args_value { - char *string; + enum args_type type; + union { + char *string; + struct cmd_list *cmdlist; + }; TAILQ_ENTRY(args_value) entry; }; @@ -2187,8 +2198,10 @@ int tty_keys_next(struct tty *); /* arguments.c */ void args_set(struct args *, u_char, const char *); struct args *args_create(void); -struct args *args_parse(const struct args_parse *, int, char **); +struct args *args_parse(const struct args_parse *, struct args_value *, + u_int); void args_vector(struct args *, int *, char ***); +void args_free_value(struct args_value *); void args_free(struct args *); char *args_print(struct args *); char *args_escape(const char *); @@ -2250,7 +2263,8 @@ const struct cmd_entry *cmd_get_entry(struct cmd *); struct args *cmd_get_args(struct cmd *); u_int cmd_get_group(struct cmd *); void cmd_get_source(struct cmd *, const char **, u_int *); -struct cmd *cmd_parse(int, char **, const char *, u_int, char **); +struct cmd *cmd_parse(struct args_value *, u_int, const char *, u_int, + char **); void cmd_free(struct cmd *); char *cmd_print(struct cmd *); struct cmd_list *cmd_list_new(void);