15494e770Schristos /* $OpenBSD$ */ 2d530c4d0Sjmmv 3d530c4d0Sjmmv /* 4ed4e6cd4Schristos * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com> 5d530c4d0Sjmmv * 6d530c4d0Sjmmv * Permission to use, copy, modify, and distribute this software for any 7d530c4d0Sjmmv * purpose with or without fee is hereby granted, provided that the above 8d530c4d0Sjmmv * copyright notice and this permission notice appear in all copies. 9d530c4d0Sjmmv * 10d530c4d0Sjmmv * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11d530c4d0Sjmmv * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12d530c4d0Sjmmv * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13d530c4d0Sjmmv * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14d530c4d0Sjmmv * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15d530c4d0Sjmmv * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16d530c4d0Sjmmv * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17d530c4d0Sjmmv */ 18d530c4d0Sjmmv 19d530c4d0Sjmmv #include <sys/types.h> 20d530c4d0Sjmmv 216db26757Swiz #include <ctype.h> 22d530c4d0Sjmmv #include <stdlib.h> 23d530c4d0Sjmmv #include <string.h> 24928fc495Schristos #include <unistd.h> 25d530c4d0Sjmmv 26d530c4d0Sjmmv #include "tmux.h" 27d530c4d0Sjmmv 28928fc495Schristos /* 29928fc495Schristos * Manipulate command arguments. 30928fc495Schristos */ 31928fc495Schristos 326db26757Swiz /* List of argument values. */ 336483eba0Schristos TAILQ_HEAD(args_values, args_value); 346483eba0Schristos 356db26757Swiz /* Single arguments flag. */ 365494e770Schristos struct args_entry { 375494e770Schristos u_char flag; 386483eba0Schristos struct args_values values; 396483eba0Schristos u_int count; 40c23f9150Swiz 41c23f9150Swiz int flags; 42c23f9150Swiz #define ARGS_ENTRY_OPTIONAL_VALUE 0x1 43c23f9150Swiz 445494e770Schristos RB_ENTRY(args_entry) entry; 455494e770Schristos }; 465494e770Schristos 476db26757Swiz /* Parsed argument flags and values. */ 486db26757Swiz struct args { 496db26757Swiz struct args_tree tree; 506db26757Swiz u_int count; 516db26757Swiz struct args_value *values; 526db26757Swiz }; 536db26757Swiz 546db26757Swiz /* Prepared command state. */ 556db26757Swiz struct args_command_state { 566db26757Swiz struct cmd_list *cmdlist; 576db26757Swiz char *cmd; 586db26757Swiz struct cmd_parse_input pi; 596db26757Swiz }; 606db26757Swiz 614e179ddaSchristos static struct args_entry *args_find(struct args *, u_char); 62928fc495Schristos 634e179ddaSchristos static int args_cmp(struct args_entry *, struct args_entry *); 644e179ddaSchristos RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp); 65928fc495Schristos 66928fc495Schristos /* Arguments tree comparison function. */ 674e179ddaSchristos static int 68928fc495Schristos args_cmp(struct args_entry *a1, struct args_entry *a2) 69928fc495Schristos { 70928fc495Schristos return (a1->flag - a2->flag); 71928fc495Schristos } 72928fc495Schristos 73928fc495Schristos /* Find a flag in the arguments tree. */ 744e179ddaSchristos static struct args_entry * 759fb66d81Schristos args_find(struct args *args, u_char flag) 76928fc495Schristos { 77928fc495Schristos struct args_entry entry; 78928fc495Schristos 799fb66d81Schristos entry.flag = flag; 80928fc495Schristos return (RB_FIND(args_tree, &args->tree, &entry)); 81928fc495Schristos } 82928fc495Schristos 836db26757Swiz /* Copy value. */ 846db26757Swiz static void 856db26757Swiz args_copy_value(struct args_value *to, struct args_value *from) 866db26757Swiz { 876db26757Swiz to->type = from->type; 886db26757Swiz switch (from->type) { 896db26757Swiz case ARGS_NONE: 906db26757Swiz break; 916db26757Swiz case ARGS_COMMANDS: 926db26757Swiz to->cmdlist = from->cmdlist; 936db26757Swiz to->cmdlist->references++; 946db26757Swiz break; 956db26757Swiz case ARGS_STRING: 966db26757Swiz to->string = xstrdup(from->string); 976db26757Swiz break; 986db26757Swiz } 996db26757Swiz } 1006db26757Swiz 101c23f9150Swiz /* Type to string. */ 102c23f9150Swiz static const char * 103c23f9150Swiz args_type_to_string (enum args_type type) 104c23f9150Swiz { 105c23f9150Swiz switch (type) 106c23f9150Swiz { 107c23f9150Swiz case ARGS_NONE: 108c23f9150Swiz return "NONE"; 109c23f9150Swiz case ARGS_STRING: 110c23f9150Swiz return "STRING"; 111c23f9150Swiz case ARGS_COMMANDS: 112c23f9150Swiz return "COMMANDS"; 113c23f9150Swiz } 114c23f9150Swiz return "INVALID"; 115c23f9150Swiz } 116c23f9150Swiz 1176db26757Swiz /* Get value as string. */ 1186db26757Swiz static const char * 1196db26757Swiz args_value_as_string(struct args_value *value) 1206db26757Swiz { 1216db26757Swiz switch (value->type) { 1226db26757Swiz case ARGS_NONE: 1236db26757Swiz return (""); 1246db26757Swiz case ARGS_COMMANDS: 1256db26757Swiz if (value->cached == NULL) 1266db26757Swiz value->cached = cmd_list_print(value->cmdlist, 0); 1276db26757Swiz return (value->cached); 1286db26757Swiz case ARGS_STRING: 1296db26757Swiz return (value->string); 1306db26757Swiz } 1316db26757Swiz fatalx("unexpected argument type"); 1326db26757Swiz } 1336db26757Swiz 1346db26757Swiz /* Create an empty arguments set. */ 135d530c4d0Sjmmv struct args * 1366db26757Swiz args_create(void) 137d530c4d0Sjmmv { 138d530c4d0Sjmmv struct args *args; 139d530c4d0Sjmmv 140d530c4d0Sjmmv args = xcalloc(1, sizeof *args); 1416db26757Swiz RB_INIT(&args->tree); 1426db26757Swiz return (args); 1436db26757Swiz } 144d530c4d0Sjmmv 145c23f9150Swiz /* Parse a single flag. */ 146c23f9150Swiz static int 147c23f9150Swiz args_parse_flag_argument(struct args_value *values, u_int count, char **cause, 148c23f9150Swiz struct args *args, u_int *i, const char *string, int flag, 149c23f9150Swiz int optional_argument) 150c23f9150Swiz { 151c23f9150Swiz struct args_value *argument, *new; 152c23f9150Swiz const char *s; 153c23f9150Swiz 154c23f9150Swiz new = xcalloc(1, sizeof *new); 155c23f9150Swiz if (*string != '\0') { 156c23f9150Swiz new->type = ARGS_STRING; 157c23f9150Swiz new->string = xstrdup(string); 158c23f9150Swiz goto out; 159c23f9150Swiz } 160c23f9150Swiz 161c23f9150Swiz if (*i == count) 162c23f9150Swiz argument = NULL; 163c23f9150Swiz else { 164c23f9150Swiz argument = &values[*i]; 165c23f9150Swiz if (argument->type != ARGS_STRING) { 166c23f9150Swiz xasprintf(cause, "-%c argument must be a string", flag); 167*890b6d91Swiz args_free_value(new); 168*890b6d91Swiz free(new); 169c23f9150Swiz return (-1); 170c23f9150Swiz } 171c23f9150Swiz } 172c23f9150Swiz if (argument == NULL) { 173*890b6d91Swiz args_free_value(new); 174*890b6d91Swiz free(new); 175c23f9150Swiz if (optional_argument) { 176c23f9150Swiz log_debug("%s: -%c (optional)", __func__, flag); 177c23f9150Swiz args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE); 178c23f9150Swiz return (0); /* either - or end */ 179c23f9150Swiz } 180c23f9150Swiz xasprintf(cause, "-%c expects an argument", flag); 181c23f9150Swiz return (-1); 182c23f9150Swiz } 183c23f9150Swiz args_copy_value(new, argument); 184c23f9150Swiz (*i)++; 185c23f9150Swiz 186c23f9150Swiz out: 187c23f9150Swiz s = args_value_as_string(new); 188c23f9150Swiz log_debug("%s: -%c = %s", __func__, flag, s); 189c23f9150Swiz args_set(args, flag, new, 0); 190c23f9150Swiz return (0); 191c23f9150Swiz } 192c23f9150Swiz 193c23f9150Swiz /* Parse flags argument. */ 194c23f9150Swiz static int 195c23f9150Swiz args_parse_flags(const struct args_parse *parse, struct args_value *values, 196c23f9150Swiz u_int count, char **cause, struct args *args, u_int *i) 197c23f9150Swiz { 198c23f9150Swiz struct args_value *value; 199c23f9150Swiz u_char flag; 200c23f9150Swiz const char *found, *string; 201c23f9150Swiz int optional_argument; 202c23f9150Swiz 203c23f9150Swiz value = &values[*i]; 204c23f9150Swiz if (value->type != ARGS_STRING) 205c23f9150Swiz return (1); 206c23f9150Swiz 207c23f9150Swiz string = value->string; 208c23f9150Swiz log_debug("%s: next %s", __func__, string); 209c23f9150Swiz if (*string++ != '-' || *string == '\0') 210c23f9150Swiz return (1); 211c23f9150Swiz (*i)++; 212c23f9150Swiz if (string[0] == '-' && string[1] == '\0') 213c23f9150Swiz return (1); 214c23f9150Swiz 215c23f9150Swiz for (;;) { 216c23f9150Swiz flag = *string++; 217c23f9150Swiz if (flag == '\0') 218c23f9150Swiz return (0); 219c23f9150Swiz if (flag == '?') 220c23f9150Swiz return (-1); 221c23f9150Swiz if (!isalnum(flag)) { 222c23f9150Swiz xasprintf(cause, "invalid flag -%c", flag); 223c23f9150Swiz return (-1); 224c23f9150Swiz } 225c23f9150Swiz 226c23f9150Swiz found = strchr(parse->template, flag); 227c23f9150Swiz if (found == NULL) { 228c23f9150Swiz xasprintf(cause, "unknown flag -%c", flag); 229c23f9150Swiz return (-1); 230c23f9150Swiz } 231c23f9150Swiz if (found[1] != ':') { 232c23f9150Swiz log_debug("%s: -%c", __func__, flag); 233c23f9150Swiz args_set(args, flag, NULL, 0); 234c23f9150Swiz continue; 235c23f9150Swiz } 236c23f9150Swiz optional_argument = (found[2] == ':'); 237c23f9150Swiz return (args_parse_flag_argument(values, count, cause, args, i, 238c23f9150Swiz string, flag, optional_argument)); 239c23f9150Swiz } 240c23f9150Swiz } 241c23f9150Swiz 2426db26757Swiz /* Parse arguments into a new argument set. */ 2436db26757Swiz struct args * 2446db26757Swiz args_parse(const struct args_parse *parse, struct args_value *values, 2456db26757Swiz u_int count, char **cause) 2466db26757Swiz { 2476db26757Swiz struct args *args; 2486db26757Swiz u_int i; 2496db26757Swiz enum args_parse_type type; 2506db26757Swiz struct args_value *value, *new; 251c23f9150Swiz const char *s; 252c23f9150Swiz int stop; 253d530c4d0Sjmmv 2546db26757Swiz if (count == 0) 2556db26757Swiz return (args_create()); 2566db26757Swiz 2576db26757Swiz args = args_create(); 2586db26757Swiz for (i = 1; i < count; /* nothing */) { 259c23f9150Swiz stop = args_parse_flags(parse, values, count, cause, args, &i); 260c23f9150Swiz if (stop == -1) { 261928fc495Schristos args_free(args); 262d530c4d0Sjmmv return (NULL); 263d530c4d0Sjmmv } 264c23f9150Swiz if (stop == 1) 2656db26757Swiz break; 2666db26757Swiz } 2676db26757Swiz log_debug("%s: flags end at %u of %u", __func__, i, count); 2686db26757Swiz if (i != count) { 2696db26757Swiz for (/* nothing */; i < count; i++) { 2706db26757Swiz value = &values[i]; 271d530c4d0Sjmmv 2726db26757Swiz s = args_value_as_string(value); 273c23f9150Swiz log_debug("%s: %u = %s (type %s)", __func__, i, s, 274c23f9150Swiz args_type_to_string (value->type)); 275d530c4d0Sjmmv 2766db26757Swiz if (parse->cb != NULL) { 2776db26757Swiz type = parse->cb(args, args->count, cause); 2786db26757Swiz if (type == ARGS_PARSE_INVALID) { 2796db26757Swiz args_free(args); 2806db26757Swiz return (NULL); 2816db26757Swiz } 2826db26757Swiz } else 2836db26757Swiz type = ARGS_PARSE_STRING; 2846db26757Swiz 2856db26757Swiz args->values = xrecallocarray(args->values, 2866db26757Swiz args->count, args->count + 1, sizeof *args->values); 2876db26757Swiz new = &args->values[args->count++]; 2886db26757Swiz 2896db26757Swiz switch (type) { 2906db26757Swiz case ARGS_PARSE_INVALID: 2916db26757Swiz fatalx("unexpected argument type"); 2926db26757Swiz case ARGS_PARSE_STRING: 2936db26757Swiz if (value->type != ARGS_STRING) { 2946db26757Swiz xasprintf(cause, 2956db26757Swiz "argument %u must be \"string\"", 2966db26757Swiz args->count); 2976db26757Swiz args_free(args); 2986db26757Swiz return (NULL); 2996db26757Swiz } 3006db26757Swiz args_copy_value(new, value); 3016db26757Swiz break; 3026db26757Swiz case ARGS_PARSE_COMMANDS_OR_STRING: 3036db26757Swiz args_copy_value(new, value); 3046db26757Swiz break; 3056db26757Swiz case ARGS_PARSE_COMMANDS: 3066db26757Swiz if (value->type != ARGS_COMMANDS) { 3076db26757Swiz xasprintf(cause, 3086db26757Swiz "argument %u must be { commands }", 3096db26757Swiz args->count); 3106db26757Swiz args_free(args); 3116db26757Swiz return (NULL); 3126db26757Swiz } 3136db26757Swiz args_copy_value(new, value); 3146db26757Swiz break; 3156db26757Swiz } 3166db26757Swiz } 3176db26757Swiz } 3186db26757Swiz 3196db26757Swiz if (parse->lower != -1 && args->count < (u_int)parse->lower) { 3206db26757Swiz xasprintf(cause, 3216db26757Swiz "too few arguments (need at least %u)", 3226db26757Swiz parse->lower); 3236db26757Swiz args_free(args); 3246db26757Swiz return (NULL); 3256db26757Swiz } 3266db26757Swiz if (parse->upper != -1 && args->count > (u_int)parse->upper) { 3276db26757Swiz xasprintf(cause, 3286db26757Swiz "too many arguments (need at most %u)", 3296db26757Swiz parse->upper); 3306db26757Swiz args_free(args); 3316db26757Swiz return (NULL); 3326db26757Swiz } 333d530c4d0Sjmmv return (args); 334d530c4d0Sjmmv } 335d530c4d0Sjmmv 3366db26757Swiz /* Copy and expand a value. */ 3376db26757Swiz static void 3386db26757Swiz args_copy_copy_value(struct args_value *to, struct args_value *from, int argc, 3396db26757Swiz char **argv) 3406db26757Swiz { 3416db26757Swiz char *s, *expanded; 3426db26757Swiz int i; 3436db26757Swiz 3446db26757Swiz to->type = from->type; 3456db26757Swiz switch (from->type) { 3466db26757Swiz case ARGS_NONE: 3476db26757Swiz break; 3486db26757Swiz case ARGS_STRING: 3496db26757Swiz expanded = xstrdup(from->string); 3506db26757Swiz for (i = 0; i < argc; i++) { 3516db26757Swiz s = cmd_template_replace(expanded, argv[i], i + 1); 3526db26757Swiz free(expanded); 3536db26757Swiz expanded = s; 3546db26757Swiz } 3556db26757Swiz to->string = expanded; 3566db26757Swiz break; 3576db26757Swiz case ARGS_COMMANDS: 3586db26757Swiz to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv); 3596db26757Swiz break; 3606db26757Swiz } 3616db26757Swiz } 3626db26757Swiz 3636db26757Swiz /* Copy an arguments set. */ 3646db26757Swiz struct args * 3656db26757Swiz args_copy(struct args *args, int argc, char **argv) 3666db26757Swiz { 3676db26757Swiz struct args *new_args; 3686db26757Swiz struct args_entry *entry; 3696db26757Swiz struct args_value *value, *new_value; 3706db26757Swiz u_int i; 3716db26757Swiz 3726db26757Swiz cmd_log_argv(argc, argv, "%s", __func__); 3736db26757Swiz 3746db26757Swiz new_args = args_create(); 3756db26757Swiz RB_FOREACH(entry, args_tree, &args->tree) { 3766db26757Swiz if (TAILQ_EMPTY(&entry->values)) { 3776db26757Swiz for (i = 0; i < entry->count; i++) 378c23f9150Swiz args_set(new_args, entry->flag, NULL, 0); 3796db26757Swiz continue; 3806db26757Swiz } 3816db26757Swiz TAILQ_FOREACH(value, &entry->values, entry) { 3826db26757Swiz new_value = xcalloc(1, sizeof *new_value); 3836db26757Swiz args_copy_copy_value(new_value, value, argc, argv); 384c23f9150Swiz args_set(new_args, entry->flag, new_value, 0); 3856db26757Swiz } 3866db26757Swiz } 3876db26757Swiz if (args->count == 0) 3886db26757Swiz return (new_args); 3896db26757Swiz new_args->count = args->count; 3906db26757Swiz new_args->values = xcalloc(args->count, sizeof *new_args->values); 3916db26757Swiz for (i = 0; i < args->count; i++) { 3926db26757Swiz new_value = &new_args->values[i]; 3936db26757Swiz args_copy_copy_value(new_value, &args->values[i], argc, argv); 3946db26757Swiz } 3956db26757Swiz return (new_args); 3966db26757Swiz } 3976db26757Swiz 3986db26757Swiz /* Free a value. */ 3996db26757Swiz void 4006db26757Swiz args_free_value(struct args_value *value) 4016db26757Swiz { 4026db26757Swiz switch (value->type) { 4036db26757Swiz case ARGS_NONE: 4046db26757Swiz break; 4056db26757Swiz case ARGS_STRING: 4066db26757Swiz free(value->string); 4076db26757Swiz break; 4086db26757Swiz case ARGS_COMMANDS: 4096db26757Swiz cmd_list_free(value->cmdlist); 4106db26757Swiz break; 4116db26757Swiz } 4126db26757Swiz free(value->cached); 4136db26757Swiz } 4146db26757Swiz 4156db26757Swiz /* Free values. */ 4166db26757Swiz void 4176db26757Swiz args_free_values(struct args_value *values, u_int count) 4186db26757Swiz { 4196db26757Swiz u_int i; 4206db26757Swiz 4216db26757Swiz for (i = 0; i < count; i++) 4226db26757Swiz args_free_value(&values[i]); 4236db26757Swiz } 4246db26757Swiz 425d530c4d0Sjmmv /* Free an arguments set. */ 426d530c4d0Sjmmv void 427d530c4d0Sjmmv args_free(struct args *args) 428d530c4d0Sjmmv { 429928fc495Schristos struct args_entry *entry; 430928fc495Schristos struct args_entry *entry1; 4316483eba0Schristos struct args_value *value; 4326483eba0Schristos struct args_value *value1; 433d530c4d0Sjmmv 4346db26757Swiz args_free_values(args->values, args->count); 4356db26757Swiz free(args->values); 436d530c4d0Sjmmv 437928fc495Schristos RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { 438928fc495Schristos RB_REMOVE(args_tree, &args->tree, entry); 4396483eba0Schristos TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) { 4406483eba0Schristos TAILQ_REMOVE(&entry->values, value, entry); 4416db26757Swiz args_free_value(value); 4426483eba0Schristos free(value); 4436483eba0Schristos } 444928fc495Schristos free(entry); 445d530c4d0Sjmmv } 446d530c4d0Sjmmv 447928fc495Schristos free(args); 448d530c4d0Sjmmv } 449d530c4d0Sjmmv 4506db26757Swiz /* Convert arguments to vector. */ 4516db26757Swiz void 4526db26757Swiz args_to_vector(struct args *args, int *argc, char ***argv) 4536db26757Swiz { 4546db26757Swiz char *s; 4556db26757Swiz u_int i; 4566db26757Swiz 4576db26757Swiz *argc = 0; 4586db26757Swiz *argv = NULL; 4596db26757Swiz 4606db26757Swiz for (i = 0; i < args->count; i++) { 4616db26757Swiz switch (args->values[i].type) { 4626db26757Swiz case ARGS_NONE: 4636db26757Swiz break; 4646db26757Swiz case ARGS_STRING: 4656db26757Swiz cmd_append_argv(argc, argv, args->values[i].string); 4666db26757Swiz break; 4676db26757Swiz case ARGS_COMMANDS: 4686db26757Swiz s = cmd_list_print(args->values[i].cmdlist, 0); 4696db26757Swiz cmd_append_argv(argc, argv, s); 4706db26757Swiz free(s); 4716db26757Swiz break; 4726db26757Swiz } 4736db26757Swiz } 4746db26757Swiz } 4756db26757Swiz 4766db26757Swiz /* Convert arguments from vector. */ 4776db26757Swiz struct args_value * 4786db26757Swiz args_from_vector(int argc, char **argv) 4796db26757Swiz { 4806db26757Swiz struct args_value *values; 4816db26757Swiz int i; 4826db26757Swiz 4836db26757Swiz values = xcalloc(argc, sizeof *values); 4846db26757Swiz for (i = 0; i < argc; i++) { 4856db26757Swiz values[i].type = ARGS_STRING; 4866db26757Swiz values[i].string = xstrdup(argv[i]); 4876db26757Swiz } 4886db26757Swiz return (values); 4896db26757Swiz } 4906db26757Swiz 491ed4e6cd4Schristos /* Add to string. */ 492ed4e6cd4Schristos static void printflike(3, 4) 493ed4e6cd4Schristos args_print_add(char **buf, size_t *len, const char *fmt, ...) 494d530c4d0Sjmmv { 495ed4e6cd4Schristos va_list ap; 496ed4e6cd4Schristos char *s; 497ed4e6cd4Schristos size_t slen; 498ed4e6cd4Schristos 499ed4e6cd4Schristos va_start(ap, fmt); 500ed4e6cd4Schristos slen = xvasprintf(&s, fmt, ap); 501ed4e6cd4Schristos va_end(ap); 502ed4e6cd4Schristos 503ed4e6cd4Schristos *len += slen; 504ed4e6cd4Schristos *buf = xrealloc(*buf, *len); 505ed4e6cd4Schristos 506ed4e6cd4Schristos strlcat(*buf, s, *len); 507ed4e6cd4Schristos free(s); 508ed4e6cd4Schristos } 509ed4e6cd4Schristos 5106483eba0Schristos /* Add value to string. */ 5116483eba0Schristos static void 5126db26757Swiz args_print_add_value(char **buf, size_t *len, struct args_value *value) 5136483eba0Schristos { 5146db26757Swiz char *expanded = NULL; 5156483eba0Schristos 5166483eba0Schristos if (**buf != '\0') 5176483eba0Schristos args_print_add(buf, len, " "); 5186483eba0Schristos 5196db26757Swiz switch (value->type) { 5206db26757Swiz case ARGS_NONE: 5216db26757Swiz break; 5226db26757Swiz case ARGS_COMMANDS: 5236db26757Swiz expanded = cmd_list_print(value->cmdlist, 0); 5246db26757Swiz args_print_add(buf, len, "{ %s }", expanded); 5256db26757Swiz break; 5266db26757Swiz case ARGS_STRING: 5276db26757Swiz expanded = args_escape(value->string); 5286db26757Swiz args_print_add(buf, len, "%s", expanded); 5296db26757Swiz break; 5306db26757Swiz } 5316db26757Swiz free(expanded); 5326483eba0Schristos } 5336483eba0Schristos 534ed4e6cd4Schristos /* Print a set of arguments. */ 535ed4e6cd4Schristos char * 536ed4e6cd4Schristos args_print(struct args *args) 537ed4e6cd4Schristos { 538ed4e6cd4Schristos size_t len; 5396483eba0Schristos char *buf; 5406db26757Swiz u_int i, j; 541928fc495Schristos struct args_entry *entry; 542c23f9150Swiz struct args_entry *last = NULL; 5436483eba0Schristos struct args_value *value; 544d530c4d0Sjmmv 545ed4e6cd4Schristos len = 1; 546ed4e6cd4Schristos buf = xcalloc(1, len); 547d530c4d0Sjmmv 548d530c4d0Sjmmv /* Process the flags first. */ 549928fc495Schristos RB_FOREACH(entry, args_tree, &args->tree) { 550c23f9150Swiz if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) 551c23f9150Swiz continue; 5526483eba0Schristos if (!TAILQ_EMPTY(&entry->values)) 553d530c4d0Sjmmv continue; 554d530c4d0Sjmmv 555ed4e6cd4Schristos if (*buf == '\0') 556ed4e6cd4Schristos args_print_add(&buf, &len, "-"); 5576483eba0Schristos for (j = 0; j < entry->count; j++) 558ed4e6cd4Schristos args_print_add(&buf, &len, "%c", entry->flag); 559d530c4d0Sjmmv } 560d530c4d0Sjmmv 561d530c4d0Sjmmv /* Then the flags with arguments. */ 562928fc495Schristos RB_FOREACH(entry, args_tree, &args->tree) { 563c23f9150Swiz if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) { 564c23f9150Swiz if (*buf != '\0') 565c23f9150Swiz args_print_add(&buf, &len, " -%c", entry->flag); 566c23f9150Swiz else 567c23f9150Swiz args_print_add(&buf, &len, "-%c", entry->flag); 568c23f9150Swiz last = entry; 569c23f9150Swiz continue; 570c23f9150Swiz } 571c23f9150Swiz if (TAILQ_EMPTY(&entry->values)) 572c23f9150Swiz continue; 5736db26757Swiz TAILQ_FOREACH(value, &entry->values, entry) { 5746db26757Swiz if (*buf != '\0') 5756db26757Swiz args_print_add(&buf, &len, " -%c", entry->flag); 5766db26757Swiz else 5776db26757Swiz args_print_add(&buf, &len, "-%c", entry->flag); 5786db26757Swiz args_print_add_value(&buf, &len, value); 5796db26757Swiz } 580c23f9150Swiz last = entry; 581d530c4d0Sjmmv } 582c23f9150Swiz if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE)) 583c23f9150Swiz args_print_add(&buf, &len, " --"); 584d530c4d0Sjmmv 585d530c4d0Sjmmv /* And finally the argument vector. */ 5866db26757Swiz for (i = 0; i < args->count; i++) 5876db26757Swiz args_print_add_value(&buf, &len, &args->values[i]); 588d530c4d0Sjmmv 589ed4e6cd4Schristos return (buf); 590d530c4d0Sjmmv } 591d530c4d0Sjmmv 5926483eba0Schristos /* Escape an argument. */ 5936483eba0Schristos char * 5946483eba0Schristos args_escape(const char *s) 5956483eba0Schristos { 5966db26757Swiz static const char dquoted[] = " #';${}%"; 5979fb66d81Schristos static const char squoted[] = " \""; 5986483eba0Schristos char *escaped, *result; 5999fb66d81Schristos int flags, quotes = 0; 6006483eba0Schristos 6019fb66d81Schristos if (*s == '\0') { 6029fb66d81Schristos xasprintf(&result, "''"); 6039fb66d81Schristos return (result); 6049fb66d81Schristos } 6059fb66d81Schristos if (s[strcspn(s, dquoted)] != '\0') 6069fb66d81Schristos quotes = '"'; 6079fb66d81Schristos else if (s[strcspn(s, squoted)] != '\0') 6089fb66d81Schristos quotes = '\''; 6099fb66d81Schristos 6106483eba0Schristos if (s[0] != ' ' && 6119fb66d81Schristos s[1] == '\0' && 6129fb66d81Schristos (quotes != 0 || s[0] == '~')) { 6136483eba0Schristos xasprintf(&escaped, "\\%c", s[0]); 6146483eba0Schristos return (escaped); 6156483eba0Schristos } 6166483eba0Schristos 6176483eba0Schristos flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; 6189fb66d81Schristos if (quotes == '"') 6196483eba0Schristos flags |= VIS_DQ; 6206483eba0Schristos utf8_stravis(&escaped, s, flags); 6216483eba0Schristos 6229fb66d81Schristos if (quotes == '\'') 6239fb66d81Schristos xasprintf(&result, "'%s'", escaped); 6249fb66d81Schristos else if (quotes == '"') { 6256483eba0Schristos if (*escaped == '~') 6266483eba0Schristos xasprintf(&result, "\"\\%s\"", escaped); 6276483eba0Schristos else 6286483eba0Schristos xasprintf(&result, "\"%s\"", escaped); 6296483eba0Schristos } else { 6306483eba0Schristos if (*escaped == '~') 6316483eba0Schristos xasprintf(&result, "\\%s", escaped); 6326483eba0Schristos else 6336483eba0Schristos result = xstrdup(escaped); 6346483eba0Schristos } 6356483eba0Schristos free(escaped); 6366483eba0Schristos return (result); 6376483eba0Schristos } 6386483eba0Schristos 639d530c4d0Sjmmv /* Return if an argument is present. */ 640d530c4d0Sjmmv int 6419fb66d81Schristos args_has(struct args *args, u_char flag) 642d530c4d0Sjmmv { 6436483eba0Schristos struct args_entry *entry; 6446483eba0Schristos 6459fb66d81Schristos entry = args_find(args, flag); 6466483eba0Schristos if (entry == NULL) 6476483eba0Schristos return (0); 6486483eba0Schristos return (entry->count); 649d530c4d0Sjmmv } 650d530c4d0Sjmmv 651928fc495Schristos /* Set argument value in the arguments tree. */ 652c9ad075bSchristos void 653c23f9150Swiz args_set(struct args *args, u_char flag, struct args_value *value, int flags) 654d530c4d0Sjmmv { 655928fc495Schristos struct args_entry *entry; 656928fc495Schristos 6579fb66d81Schristos entry = args_find(args, flag); 6586483eba0Schristos if (entry == NULL) { 659928fc495Schristos entry = xcalloc(1, sizeof *entry); 6609fb66d81Schristos entry->flag = flag; 6616483eba0Schristos entry->count = 1; 662c23f9150Swiz entry->flags = flags; 6636483eba0Schristos TAILQ_INIT(&entry->values); 664928fc495Schristos RB_INSERT(args_tree, &args->tree, entry); 6656483eba0Schristos } else 6666483eba0Schristos entry->count++; 6676db26757Swiz if (value != NULL && value->type != ARGS_NONE) 6686483eba0Schristos TAILQ_INSERT_TAIL(&entry->values, value, entry); 669*890b6d91Swiz else 670*890b6d91Swiz free(value); 6716483eba0Schristos } 672d530c4d0Sjmmv 673d530c4d0Sjmmv /* Get argument value. Will be NULL if it isn't present. */ 674d530c4d0Sjmmv const char * 6759fb66d81Schristos args_get(struct args *args, u_char flag) 676d530c4d0Sjmmv { 677928fc495Schristos struct args_entry *entry; 678928fc495Schristos 6799fb66d81Schristos if ((entry = args_find(args, flag)) == NULL) 6809fb66d81Schristos return (NULL); 6819fb66d81Schristos if (TAILQ_EMPTY(&entry->values)) 682928fc495Schristos return (NULL); 6836db26757Swiz return (TAILQ_LAST(&entry->values, args_values)->string); 6846483eba0Schristos } 6856483eba0Schristos 6869fb66d81Schristos /* Get first argument. */ 6879fb66d81Schristos u_char 6889fb66d81Schristos args_first(struct args *args, struct args_entry **entry) 6899fb66d81Schristos { 6909fb66d81Schristos *entry = RB_MIN(args_tree, &args->tree); 6919fb66d81Schristos if (*entry == NULL) 6929fb66d81Schristos return (0); 6939fb66d81Schristos return ((*entry)->flag); 6949fb66d81Schristos } 6959fb66d81Schristos 6969fb66d81Schristos /* Get next argument. */ 6979fb66d81Schristos u_char 6989fb66d81Schristos args_next(struct args_entry **entry) 6999fb66d81Schristos { 7009fb66d81Schristos *entry = RB_NEXT(args_tree, &args->tree, *entry); 7019fb66d81Schristos if (*entry == NULL) 7029fb66d81Schristos return (0); 7039fb66d81Schristos return ((*entry)->flag); 7049fb66d81Schristos } 7059fb66d81Schristos 7066db26757Swiz /* Get argument count. */ 7076db26757Swiz u_int 7086db26757Swiz args_count(struct args *args) 7096db26757Swiz { 7106db26757Swiz return (args->count); 7116db26757Swiz } 7126db26757Swiz 7136db26757Swiz /* Get argument values. */ 7146db26757Swiz struct args_value * 7156db26757Swiz args_values(struct args *args) 7166db26757Swiz { 7176db26757Swiz return (args->values); 7186db26757Swiz } 7196db26757Swiz 7206db26757Swiz /* Get argument value. */ 7216db26757Swiz struct args_value * 7226db26757Swiz args_value(struct args *args, u_int idx) 7236db26757Swiz { 7246db26757Swiz if (idx >= args->count) 7256db26757Swiz return (NULL); 7266db26757Swiz return (&args->values[idx]); 7276db26757Swiz } 7286db26757Swiz 7296db26757Swiz /* Return argument as string. */ 7306483eba0Schristos const char * 7316db26757Swiz args_string(struct args *args, u_int idx) 7326db26757Swiz { 7336db26757Swiz if (idx >= args->count) 7346db26757Swiz return (NULL); 7356db26757Swiz return (args_value_as_string(&args->values[idx])); 7366db26757Swiz } 7376db26757Swiz 7386db26757Swiz /* Make a command now. */ 7396db26757Swiz struct cmd_list * 7406db26757Swiz args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx, 7416db26757Swiz int expand) 7426db26757Swiz { 7436db26757Swiz struct args_command_state *state; 7446db26757Swiz char *error; 7456db26757Swiz struct cmd_list *cmdlist; 7466db26757Swiz 7476db26757Swiz state = args_make_commands_prepare(self, item, idx, NULL, 0, expand); 7486db26757Swiz cmdlist = args_make_commands(state, 0, NULL, &error); 7496db26757Swiz if (cmdlist == NULL) { 7506db26757Swiz cmdq_error(item, "%s", error); 7516db26757Swiz free(error); 7526db26757Swiz } 7536db26757Swiz else 7546db26757Swiz cmdlist->references++; 7556db26757Swiz args_make_commands_free(state); 7566db26757Swiz return (cmdlist); 7576db26757Swiz } 7586db26757Swiz 7596db26757Swiz /* Save bits to make a command later. */ 7606db26757Swiz struct args_command_state * 7616db26757Swiz args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx, 7626db26757Swiz const char *default_command, int wait, int expand) 7636db26757Swiz { 7646db26757Swiz struct args *args = cmd_get_args(self); 7656db26757Swiz struct cmd_find_state *target = cmdq_get_target(item); 7666db26757Swiz struct client *tc = cmdq_get_target_client(item); 7676db26757Swiz struct args_value *value; 7686db26757Swiz struct args_command_state *state; 7696db26757Swiz const char *cmd; 770c23f9150Swiz const char *file; 7716db26757Swiz 7726db26757Swiz state = xcalloc(1, sizeof *state); 7736db26757Swiz 7746db26757Swiz if (idx < args->count) { 7756db26757Swiz value = &args->values[idx]; 7766db26757Swiz if (value->type == ARGS_COMMANDS) { 7776db26757Swiz state->cmdlist = value->cmdlist; 7786db26757Swiz state->cmdlist->references++; 7796db26757Swiz return (state); 7806db26757Swiz } 7816db26757Swiz cmd = value->string; 7826db26757Swiz } else { 7836db26757Swiz if (default_command == NULL) 7846db26757Swiz fatalx("argument out of range"); 7856db26757Swiz cmd = default_command; 7866db26757Swiz } 7876db26757Swiz 7886db26757Swiz 7896db26757Swiz if (expand) 7906db26757Swiz state->cmd = format_single_from_target(item, cmd); 7916db26757Swiz else 7926db26757Swiz state->cmd = xstrdup(cmd); 7936db26757Swiz log_debug("%s: %s", __func__, state->cmd); 7946db26757Swiz 7956db26757Swiz if (wait) 7966db26757Swiz state->pi.item = item; 797c23f9150Swiz cmd_get_source(self, &file, &state->pi.line); 798c23f9150Swiz if (file != NULL) 799c23f9150Swiz state->pi.file = xstrdup(file); 8006db26757Swiz state->pi.c = tc; 8016db26757Swiz if (state->pi.c != NULL) 8026db26757Swiz state->pi.c->references++; 8036db26757Swiz cmd_find_copy_state(&state->pi.fs, target); 8046db26757Swiz 8056db26757Swiz return (state); 8066db26757Swiz } 8076db26757Swiz 8086db26757Swiz /* Return argument as command. */ 8096db26757Swiz struct cmd_list * 8106db26757Swiz args_make_commands(struct args_command_state *state, int argc, char **argv, 8116db26757Swiz char **error) 8126db26757Swiz { 8136db26757Swiz struct cmd_parse_result *pr; 8146db26757Swiz char *cmd, *new_cmd; 8156db26757Swiz int i; 8166db26757Swiz 8176db26757Swiz if (state->cmdlist != NULL) { 8186db26757Swiz if (argc == 0) 8196db26757Swiz return (state->cmdlist); 8206db26757Swiz return (cmd_list_copy(state->cmdlist, argc, argv)); 8216db26757Swiz } 8226db26757Swiz 8236db26757Swiz cmd = xstrdup(state->cmd); 824c23f9150Swiz log_debug("%s: %s", __func__, cmd); 825c23f9150Swiz cmd_log_argv(argc, argv, __func__); 8266db26757Swiz for (i = 0; i < argc; i++) { 8276db26757Swiz new_cmd = cmd_template_replace(cmd, argv[i], i + 1); 8286db26757Swiz log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd); 8296db26757Swiz free(cmd); 8306db26757Swiz cmd = new_cmd; 8316db26757Swiz } 8326db26757Swiz log_debug("%s: %s", __func__, cmd); 8336db26757Swiz 8346db26757Swiz pr = cmd_parse_from_string(cmd, &state->pi); 8356db26757Swiz free(cmd); 8366db26757Swiz switch (pr->status) { 8376db26757Swiz case CMD_PARSE_ERROR: 8386db26757Swiz *error = pr->error; 8396db26757Swiz return (NULL); 8406db26757Swiz case CMD_PARSE_SUCCESS: 8416db26757Swiz return (pr->cmdlist); 8426db26757Swiz } 8436db26757Swiz fatalx("invalid parse return state"); 8446db26757Swiz } 8456db26757Swiz 8466db26757Swiz /* Free commands state. */ 8476db26757Swiz void 8486db26757Swiz args_make_commands_free(struct args_command_state *state) 8496db26757Swiz { 8506db26757Swiz if (state->cmdlist != NULL) 8516db26757Swiz cmd_list_free(state->cmdlist); 8526db26757Swiz if (state->pi.c != NULL) 8536db26757Swiz server_client_unref(state->pi.c); 854f844e94eSwiz free(__UNCONST(state->pi.file)); 8556db26757Swiz free(state->cmd); 8566db26757Swiz free(state); 8576db26757Swiz } 8586db26757Swiz 8596db26757Swiz /* Get prepared command. */ 8606db26757Swiz char * 8616db26757Swiz args_make_commands_get_command(struct args_command_state *state) 8626db26757Swiz { 8636db26757Swiz struct cmd *first; 8646db26757Swiz int n; 8656db26757Swiz char *s; 8666db26757Swiz 8676db26757Swiz if (state->cmdlist != NULL) { 8686db26757Swiz first = cmd_list_first(state->cmdlist); 8696db26757Swiz if (first == NULL) 8706db26757Swiz return (xstrdup("")); 8716db26757Swiz return (xstrdup(cmd_get_entry(first)->name)); 8726db26757Swiz } 8736db26757Swiz n = strcspn(state->cmd, " ,"); 8746db26757Swiz xasprintf(&s, "%.*s", n, state->cmd); 8756db26757Swiz return (s); 8766db26757Swiz } 8776db26757Swiz 8786db26757Swiz /* Get first value in argument. */ 8796db26757Swiz struct args_value * 8806db26757Swiz args_first_value(struct args *args, u_char flag) 8816483eba0Schristos { 8826483eba0Schristos struct args_entry *entry; 8836483eba0Schristos 8849fb66d81Schristos if ((entry = args_find(args, flag)) == NULL) 8856483eba0Schristos return (NULL); 8866db26757Swiz return (TAILQ_FIRST(&entry->values)); 8876483eba0Schristos } 8886483eba0Schristos 8896483eba0Schristos /* Get next value in argument. */ 8906db26757Swiz struct args_value * 8916db26757Swiz args_next_value(struct args_value *value) 8926483eba0Schristos { 8936db26757Swiz return (TAILQ_NEXT(value, entry)); 894d530c4d0Sjmmv } 895d530c4d0Sjmmv 896d530c4d0Sjmmv /* Convert an argument value to a number. */ 897d530c4d0Sjmmv long long 8989fb66d81Schristos args_strtonum(struct args *args, u_char flag, long long minval, 8999fb66d81Schristos long long maxval, char **cause) 900d530c4d0Sjmmv { 901d530c4d0Sjmmv const char *errstr; 902d530c4d0Sjmmv long long ll; 903928fc495Schristos struct args_entry *entry; 9046483eba0Schristos struct args_value *value; 905d530c4d0Sjmmv 9069fb66d81Schristos if ((entry = args_find(args, flag)) == NULL) { 907d530c4d0Sjmmv *cause = xstrdup("missing"); 908d530c4d0Sjmmv return (0); 909d530c4d0Sjmmv } 9106483eba0Schristos value = TAILQ_LAST(&entry->values, args_values); 9116db26757Swiz if (value == NULL || 9126db26757Swiz value->type != ARGS_STRING || 9136db26757Swiz value->string == NULL) { 9146db26757Swiz *cause = xstrdup("missing"); 9156db26757Swiz return (0); 9166db26757Swiz } 917d530c4d0Sjmmv 9186db26757Swiz ll = strtonum(value->string, minval, maxval, &errstr); 919d530c4d0Sjmmv if (errstr != NULL) { 920d530c4d0Sjmmv *cause = xstrdup(errstr); 921d530c4d0Sjmmv return (0); 922d530c4d0Sjmmv } 923d530c4d0Sjmmv 924d530c4d0Sjmmv *cause = NULL; 925d530c4d0Sjmmv return (ll); 926d530c4d0Sjmmv } 9279fb66d81Schristos 928c23f9150Swiz /* Convert an argument value to a number, and expand formats. */ 929c23f9150Swiz long long 930c23f9150Swiz args_strtonum_and_expand(struct args *args, u_char flag, long long minval, 931c23f9150Swiz long long maxval, struct cmdq_item *item, char **cause) 932c23f9150Swiz { 933c23f9150Swiz const char *errstr; 934c23f9150Swiz char *formatted; 935c23f9150Swiz long long ll; 936c23f9150Swiz struct args_entry *entry; 937c23f9150Swiz struct args_value *value; 938c23f9150Swiz 939c23f9150Swiz if ((entry = args_find(args, flag)) == NULL) { 940c23f9150Swiz *cause = xstrdup("missing"); 941c23f9150Swiz return (0); 942c23f9150Swiz } 943c23f9150Swiz value = TAILQ_LAST(&entry->values, args_values); 944c23f9150Swiz if (value == NULL || 945c23f9150Swiz value->type != ARGS_STRING || 946c23f9150Swiz value->string == NULL) { 947c23f9150Swiz *cause = xstrdup("missing"); 948c23f9150Swiz return (0); 949c23f9150Swiz } 950c23f9150Swiz 951c23f9150Swiz formatted = format_single_from_target(item, value->string); 952c23f9150Swiz ll = strtonum(formatted, minval, maxval, &errstr); 953c23f9150Swiz free(formatted); 954c23f9150Swiz if (errstr != NULL) { 955c23f9150Swiz *cause = xstrdup(errstr); 956c23f9150Swiz return (0); 957c23f9150Swiz } 958c23f9150Swiz 959c23f9150Swiz *cause = NULL; 960c23f9150Swiz return (ll); 961c23f9150Swiz } 962c23f9150Swiz 9639fb66d81Schristos /* Convert an argument to a number which may be a percentage. */ 9649fb66d81Schristos long long 9659fb66d81Schristos args_percentage(struct args *args, u_char flag, long long minval, 9669fb66d81Schristos long long maxval, long long curval, char **cause) 9679fb66d81Schristos { 9689fb66d81Schristos const char *value; 9699fb66d81Schristos struct args_entry *entry; 9709fb66d81Schristos 9719fb66d81Schristos if ((entry = args_find(args, flag)) == NULL) { 9729fb66d81Schristos *cause = xstrdup("missing"); 9739fb66d81Schristos return (0); 9749fb66d81Schristos } 975c23f9150Swiz if (TAILQ_EMPTY(&entry->values)) { 976c23f9150Swiz *cause = xstrdup("empty"); 977c23f9150Swiz return (0); 978c23f9150Swiz } 9796db26757Swiz value = TAILQ_LAST(&entry->values, args_values)->string; 9809fb66d81Schristos return (args_string_percentage(value, minval, maxval, curval, cause)); 9819fb66d81Schristos } 9829fb66d81Schristos 9839fb66d81Schristos /* Convert a string to a number which may be a percentage. */ 9849fb66d81Schristos long long 9859fb66d81Schristos args_string_percentage(const char *value, long long minval, long long maxval, 9869fb66d81Schristos long long curval, char **cause) 9879fb66d81Schristos { 9889fb66d81Schristos const char *errstr; 9899fb66d81Schristos long long ll; 9909fb66d81Schristos size_t valuelen = strlen(value); 9919fb66d81Schristos char *copy; 9929fb66d81Schristos 993c23f9150Swiz if (valuelen == 0) { 994c23f9150Swiz *cause = xstrdup("empty"); 995c23f9150Swiz return (0); 996c23f9150Swiz } 9979fb66d81Schristos if (value[valuelen - 1] == '%') { 9989fb66d81Schristos copy = xstrdup(value); 9999fb66d81Schristos copy[valuelen - 1] = '\0'; 10009fb66d81Schristos 10019fb66d81Schristos ll = strtonum(copy, 0, 100, &errstr); 10029fb66d81Schristos free(copy); 10039fb66d81Schristos if (errstr != NULL) { 10049fb66d81Schristos *cause = xstrdup(errstr); 10059fb66d81Schristos return (0); 10069fb66d81Schristos } 10079fb66d81Schristos ll = (curval * ll) / 100; 10089fb66d81Schristos if (ll < minval) { 10099fb66d81Schristos *cause = xstrdup("too small"); 10109fb66d81Schristos return (0); 10119fb66d81Schristos } 10129fb66d81Schristos if (ll > maxval) { 10139fb66d81Schristos *cause = xstrdup("too large"); 10149fb66d81Schristos return (0); 10159fb66d81Schristos } 10169fb66d81Schristos } else { 10179fb66d81Schristos ll = strtonum(value, minval, maxval, &errstr); 10189fb66d81Schristos if (errstr != NULL) { 10199fb66d81Schristos *cause = xstrdup(errstr); 10209fb66d81Schristos return (0); 10219fb66d81Schristos } 10229fb66d81Schristos } 10239fb66d81Schristos 10249fb66d81Schristos *cause = NULL; 10259fb66d81Schristos return (ll); 10269fb66d81Schristos } 1027c23f9150Swiz 1028c23f9150Swiz /* 1029c23f9150Swiz * Convert an argument to a number which may be a percentage, and expand 1030c23f9150Swiz * formats. 1031c23f9150Swiz */ 1032c23f9150Swiz long long 1033c23f9150Swiz args_percentage_and_expand(struct args *args, u_char flag, long long minval, 1034c23f9150Swiz long long maxval, long long curval, struct cmdq_item *item, char **cause) 1035c23f9150Swiz { 1036c23f9150Swiz const char *value; 1037c23f9150Swiz struct args_entry *entry; 1038c23f9150Swiz 1039c23f9150Swiz if ((entry = args_find(args, flag)) == NULL) { 1040c23f9150Swiz *cause = xstrdup("missing"); 1041c23f9150Swiz return (0); 1042c23f9150Swiz } 1043c23f9150Swiz if (TAILQ_EMPTY(&entry->values)) { 1044c23f9150Swiz *cause = xstrdup("empty"); 1045c23f9150Swiz return (0); 1046c23f9150Swiz } 1047c23f9150Swiz value = TAILQ_LAST(&entry->values, args_values)->string; 1048c23f9150Swiz return (args_string_percentage_and_expand(value, minval, maxval, curval, 1049c23f9150Swiz item, cause)); 1050c23f9150Swiz } 1051c23f9150Swiz 1052c23f9150Swiz /* 1053c23f9150Swiz * Convert a string to a number which may be a percentage, and expand formats. 1054c23f9150Swiz */ 1055c23f9150Swiz long long 1056c23f9150Swiz args_string_percentage_and_expand(const char *value, long long minval, 1057c23f9150Swiz long long maxval, long long curval, struct cmdq_item *item, char **cause) 1058c23f9150Swiz { 1059c23f9150Swiz const char *errstr; 1060c23f9150Swiz long long ll; 1061c23f9150Swiz size_t valuelen = strlen(value); 1062c23f9150Swiz char *copy, *f; 1063c23f9150Swiz 1064c23f9150Swiz if (value[valuelen - 1] == '%') { 1065c23f9150Swiz copy = xstrdup(value); 1066c23f9150Swiz copy[valuelen - 1] = '\0'; 1067c23f9150Swiz 1068c23f9150Swiz f = format_single_from_target(item, copy); 1069c23f9150Swiz ll = strtonum(f, 0, 100, &errstr); 1070c23f9150Swiz free(f); 1071c23f9150Swiz free(copy); 1072c23f9150Swiz if (errstr != NULL) { 1073c23f9150Swiz *cause = xstrdup(errstr); 1074c23f9150Swiz return (0); 1075c23f9150Swiz } 1076c23f9150Swiz ll = (curval * ll) / 100; 1077c23f9150Swiz if (ll < minval) { 1078c23f9150Swiz *cause = xstrdup("too small"); 1079c23f9150Swiz return (0); 1080c23f9150Swiz } 1081c23f9150Swiz if (ll > maxval) { 1082c23f9150Swiz *cause = xstrdup("too large"); 1083c23f9150Swiz return (0); 1084c23f9150Swiz } 1085c23f9150Swiz } else { 1086c23f9150Swiz f = format_single_from_target(item, value); 1087c23f9150Swiz ll = strtonum(f, minval, maxval, &errstr); 1088c23f9150Swiz free(f); 1089c23f9150Swiz if (errstr != NULL) { 1090c23f9150Swiz *cause = xstrdup(errstr); 1091c23f9150Swiz return (0); 1092c23f9150Swiz } 1093c23f9150Swiz } 1094c23f9150Swiz 1095c23f9150Swiz *cause = NULL; 1096c23f9150Swiz return (ll); 1097c23f9150Swiz } 1098