1*28f73f25Snicm /* $OpenBSD: arguments.c,v 1.64 2024/05/13 11:45:05 nicm Exp $ */
2ca7befccSnicm
3ca7befccSnicm /*
498ca8272Snicm * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
5ca7befccSnicm *
6ca7befccSnicm * Permission to use, copy, modify, and distribute this software for any
7ca7befccSnicm * purpose with or without fee is hereby granted, provided that the above
8ca7befccSnicm * copyright notice and this permission notice appear in all copies.
9ca7befccSnicm *
10ca7befccSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11ca7befccSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12ca7befccSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13ca7befccSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14ca7befccSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15ca7befccSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16ca7befccSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17ca7befccSnicm */
18ca7befccSnicm
19ca7befccSnicm #include <sys/types.h>
20ca7befccSnicm
21fb147d85Snicm #include <ctype.h>
22ca7befccSnicm #include <stdlib.h>
23ca7befccSnicm #include <string.h>
24416d2631Snicm #include <vis.h>
25ca7befccSnicm
26ca7befccSnicm #include "tmux.h"
27ca7befccSnicm
281a5e800bSnicm /*
291a5e800bSnicm * Manipulate command arguments.
301a5e800bSnicm */
311a5e800bSnicm
322db6a388Snicm /* List of argument values. */
33d0772b58Snicm TAILQ_HEAD(args_values, args_value);
34d0772b58Snicm
352db6a388Snicm /* Single arguments flag. */
3672c2f7dfSnicm struct args_entry {
3772c2f7dfSnicm u_char flag;
38d0772b58Snicm struct args_values values;
39a02c6cc0Snicm u_int count;
4083a0a8d9Snicm
4183a0a8d9Snicm int flags;
4283a0a8d9Snicm #define ARGS_ENTRY_OPTIONAL_VALUE 0x1
4383a0a8d9Snicm
4472c2f7dfSnicm RB_ENTRY(args_entry) entry;
4572c2f7dfSnicm };
4672c2f7dfSnicm
472db6a388Snicm /* Parsed argument flags and values. */
481693b10bSnicm struct args {
491693b10bSnicm struct args_tree tree;
50bee784faSnicm u_int count;
51bee784faSnicm struct args_value *values;
521693b10bSnicm };
531693b10bSnicm
542db6a388Snicm /* Prepared command state. */
552db6a388Snicm struct args_command_state {
562db6a388Snicm struct cmd_list *cmdlist;
572db6a388Snicm char *cmd;
582db6a388Snicm struct cmd_parse_input pi;
592db6a388Snicm };
602db6a388Snicm
618d2662b6Snicm static struct args_entry *args_find(struct args *, u_char);
621a5e800bSnicm
638d2662b6Snicm static int args_cmp(struct args_entry *, struct args_entry *);
648d2662b6Snicm RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
651a5e800bSnicm
661a5e800bSnicm /* Arguments tree comparison function. */
678d2662b6Snicm static int
args_cmp(struct args_entry * a1,struct args_entry * a2)681a5e800bSnicm args_cmp(struct args_entry *a1, struct args_entry *a2)
691a5e800bSnicm {
701a5e800bSnicm return (a1->flag - a2->flag);
711a5e800bSnicm }
721a5e800bSnicm
731a5e800bSnicm /* Find a flag in the arguments tree. */
748d2662b6Snicm static struct args_entry *
args_find(struct args * args,u_char flag)75ef07bfa2Snicm args_find(struct args *args, u_char flag)
761a5e800bSnicm {
771a5e800bSnicm struct args_entry entry;
781a5e800bSnicm
79ef07bfa2Snicm entry.flag = flag;
801a5e800bSnicm return (RB_FIND(args_tree, &args->tree, &entry));
811a5e800bSnicm }
821a5e800bSnicm
83bee784faSnicm /* Copy value. */
84bee784faSnicm static void
args_copy_value(struct args_value * to,struct args_value * from)85bee784faSnicm args_copy_value(struct args_value *to, struct args_value *from)
86bee784faSnicm {
87bee784faSnicm to->type = from->type;
88bee784faSnicm switch (from->type) {
89bee784faSnicm case ARGS_NONE:
90bee784faSnicm break;
91bee784faSnicm case ARGS_COMMANDS:
92bee784faSnicm to->cmdlist = from->cmdlist;
93bee784faSnicm to->cmdlist->references++;
94bee784faSnicm break;
95bee784faSnicm case ARGS_STRING:
96bee784faSnicm to->string = xstrdup(from->string);
97bee784faSnicm break;
98bee784faSnicm }
99bee784faSnicm }
100bee784faSnicm
101a01f743bSnicm /* Type to string. */
102a01f743bSnicm static const char *
args_type_to_string(enum args_type type)103a01f743bSnicm args_type_to_string (enum args_type type)
104a01f743bSnicm {
105a01f743bSnicm switch (type)
106a01f743bSnicm {
107a01f743bSnicm case ARGS_NONE:
108a01f743bSnicm return "NONE";
109a01f743bSnicm case ARGS_STRING:
110a01f743bSnicm return "STRING";
111a01f743bSnicm case ARGS_COMMANDS:
112a01f743bSnicm return "COMMANDS";
113a01f743bSnicm }
114a01f743bSnicm return "INVALID";
115a01f743bSnicm }
116a01f743bSnicm
117fb147d85Snicm /* Get value as string. */
118bee784faSnicm static const char *
args_value_as_string(struct args_value * value)119fb147d85Snicm args_value_as_string(struct args_value *value)
120fb147d85Snicm {
121fb147d85Snicm switch (value->type) {
122fb147d85Snicm case ARGS_NONE:
123bee784faSnicm return ("");
124fb147d85Snicm case ARGS_COMMANDS:
125bee784faSnicm if (value->cached == NULL)
126bee784faSnicm value->cached = cmd_list_print(value->cmdlist, 0);
127bee784faSnicm return (value->cached);
128fb147d85Snicm case ARGS_STRING:
129bee784faSnicm return (value->string);
130fb147d85Snicm }
131432017adSnicm fatalx("unexpected argument type");
132fb147d85Snicm }
133fb147d85Snicm
134eee51546Snicm /* Create an empty arguments set. */
135eee51546Snicm struct args *
args_create(void)136eee51546Snicm args_create(void)
137eee51546Snicm {
138eee51546Snicm struct args *args;
139eee51546Snicm
140eee51546Snicm args = xcalloc(1, sizeof *args);
141eee51546Snicm RB_INIT(&args->tree);
142eee51546Snicm return (args);
143eee51546Snicm }
144eee51546Snicm
14583a0a8d9Snicm /* Parse a single flag. */
14683a0a8d9Snicm static int
args_parse_flag_argument(struct args_value * values,u_int count,char ** cause,struct args * args,u_int * i,const char * string,int flag,int optional_argument)14783a0a8d9Snicm args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
14883a0a8d9Snicm struct args *args, u_int *i, const char *string, int flag,
14983a0a8d9Snicm int optional_argument)
15083a0a8d9Snicm {
15183a0a8d9Snicm struct args_value *argument, *new;
15283a0a8d9Snicm const char *s;
15383a0a8d9Snicm
15483a0a8d9Snicm new = xcalloc(1, sizeof *new);
15583a0a8d9Snicm if (*string != '\0') {
15683a0a8d9Snicm new->type = ARGS_STRING;
15783a0a8d9Snicm new->string = xstrdup(string);
15883a0a8d9Snicm goto out;
15983a0a8d9Snicm }
16083a0a8d9Snicm
16183a0a8d9Snicm if (*i == count)
16283a0a8d9Snicm argument = NULL;
16383a0a8d9Snicm else {
16483a0a8d9Snicm argument = &values[*i];
16583a0a8d9Snicm if (argument->type != ARGS_STRING) {
16683a0a8d9Snicm xasprintf(cause, "-%c argument must be a string", flag);
167*28f73f25Snicm args_free_value(new);
168*28f73f25Snicm free(new);
16983a0a8d9Snicm return (-1);
17083a0a8d9Snicm }
17183a0a8d9Snicm }
17283a0a8d9Snicm if (argument == NULL) {
173*28f73f25Snicm args_free_value(new);
174*28f73f25Snicm free(new);
17583a0a8d9Snicm if (optional_argument) {
17683a0a8d9Snicm log_debug("%s: -%c (optional)", __func__, flag);
17783a0a8d9Snicm args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
17883a0a8d9Snicm return (0); /* either - or end */
17983a0a8d9Snicm }
18083a0a8d9Snicm xasprintf(cause, "-%c expects an argument", flag);
18183a0a8d9Snicm return (-1);
18283a0a8d9Snicm }
18383a0a8d9Snicm args_copy_value(new, argument);
18483a0a8d9Snicm (*i)++;
18583a0a8d9Snicm
18683a0a8d9Snicm out:
18783a0a8d9Snicm s = args_value_as_string(new);
18883a0a8d9Snicm log_debug("%s: -%c = %s", __func__, flag, s);
18983a0a8d9Snicm args_set(args, flag, new, 0);
19083a0a8d9Snicm return (0);
19183a0a8d9Snicm }
19283a0a8d9Snicm
19383a0a8d9Snicm /* Parse flags argument. */
19483a0a8d9Snicm static int
args_parse_flags(const struct args_parse * parse,struct args_value * values,u_int count,char ** cause,struct args * args,u_int * i)19583a0a8d9Snicm args_parse_flags(const struct args_parse *parse, struct args_value *values,
1966af87e9aSnicm u_int count, char **cause, struct args *args, u_int *i)
19783a0a8d9Snicm {
19883a0a8d9Snicm struct args_value *value;
19983a0a8d9Snicm u_char flag;
20083a0a8d9Snicm const char *found, *string;
20183a0a8d9Snicm int optional_argument;
20283a0a8d9Snicm
20383a0a8d9Snicm value = &values[*i];
20483a0a8d9Snicm if (value->type != ARGS_STRING)
20583a0a8d9Snicm return (1);
20683a0a8d9Snicm
20783a0a8d9Snicm string = value->string;
20883a0a8d9Snicm log_debug("%s: next %s", __func__, string);
20983a0a8d9Snicm if (*string++ != '-' || *string == '\0')
21083a0a8d9Snicm return (1);
21183a0a8d9Snicm (*i)++;
21283a0a8d9Snicm if (string[0] == '-' && string[1] == '\0')
21383a0a8d9Snicm return (1);
21483a0a8d9Snicm
21583a0a8d9Snicm for (;;) {
21683a0a8d9Snicm flag = *string++;
21783a0a8d9Snicm if (flag == '\0')
21883a0a8d9Snicm return (0);
21983a0a8d9Snicm if (flag == '?')
22083a0a8d9Snicm return (-1);
22183a0a8d9Snicm if (!isalnum(flag)) {
22283a0a8d9Snicm xasprintf(cause, "invalid flag -%c", flag);
22383a0a8d9Snicm return (-1);
22483a0a8d9Snicm }
22583a0a8d9Snicm
22683a0a8d9Snicm found = strchr(parse->template, flag);
22783a0a8d9Snicm if (found == NULL) {
22883a0a8d9Snicm xasprintf(cause, "unknown flag -%c", flag);
22983a0a8d9Snicm return (-1);
23083a0a8d9Snicm }
231461a14bcSnicm if (found[1] != ':') {
23283a0a8d9Snicm log_debug("%s: -%c", __func__, flag);
23383a0a8d9Snicm args_set(args, flag, NULL, 0);
23483a0a8d9Snicm continue;
23583a0a8d9Snicm }
236461a14bcSnicm optional_argument = (found[2] == ':');
23783a0a8d9Snicm return (args_parse_flag_argument(values, count, cause, args, i,
23883a0a8d9Snicm string, flag, optional_argument));
23983a0a8d9Snicm }
24083a0a8d9Snicm }
24183a0a8d9Snicm
242fb147d85Snicm /* Parse arguments into a new argument set. */
243ca7befccSnicm struct args *
args_parse(const struct args_parse * parse,struct args_value * values,u_int count,char ** cause)244fb147d85Snicm args_parse(const struct args_parse *parse, struct args_value *values,
245e4c0b811Snicm u_int count, char **cause)
246ca7befccSnicm {
247ca7befccSnicm struct args *args;
248fb147d85Snicm u_int i;
249e4c0b811Snicm enum args_parse_type type;
250bee784faSnicm struct args_value *value, *new;
25183a0a8d9Snicm const char *s;
25283a0a8d9Snicm int stop;
253ca7befccSnicm
254fb147d85Snicm if (count == 0)
255fb147d85Snicm return (args_create());
256ca7befccSnicm
257eee51546Snicm args = args_create();
258fb147d85Snicm for (i = 1; i < count; /* nothing */) {
25983a0a8d9Snicm stop = args_parse_flags(parse, values, count, cause, args, &i);
26083a0a8d9Snicm if (stop == -1) {
261dddb32eeSnicm args_free(args);
262dddb32eeSnicm return (NULL);
263dddb32eeSnicm }
26483a0a8d9Snicm if (stop == 1)
265fb147d85Snicm break;
266fb147d85Snicm }
267fb147d85Snicm log_debug("%s: flags end at %u of %u", __func__, i, count);
268fb147d85Snicm if (i != count) {
269fb147d85Snicm for (/* nothing */; i < count; i++) {
270fb147d85Snicm value = &values[i];
271ca7befccSnicm
272fb147d85Snicm s = args_value_as_string(value);
273a01f743bSnicm log_debug("%s: %u = %s (type %s)", __func__, i, s,
274a01f743bSnicm args_type_to_string (value->type));
275bee784faSnicm
276e4c0b811Snicm if (parse->cb != NULL) {
277e4c0b811Snicm type = parse->cb(args, args->count, cause);
278e4c0b811Snicm if (type == ARGS_PARSE_INVALID) {
279e4c0b811Snicm args_free(args);
280e4c0b811Snicm return (NULL);
281e4c0b811Snicm }
282e4c0b811Snicm } else
283e4c0b811Snicm type = ARGS_PARSE_STRING;
284e4c0b811Snicm
285bee784faSnicm args->values = xrecallocarray(args->values,
286bee784faSnicm args->count, args->count + 1, sizeof *args->values);
287e4c0b811Snicm new = &args->values[args->count++];
288e4c0b811Snicm
289e4c0b811Snicm switch (type) {
290e4c0b811Snicm case ARGS_PARSE_INVALID:
291e4c0b811Snicm fatalx("unexpected argument type");
292e4c0b811Snicm case ARGS_PARSE_STRING:
293e4c0b811Snicm if (value->type != ARGS_STRING) {
294e4c0b811Snicm xasprintf(cause,
295e4c0b811Snicm "argument %u must be \"string\"",
296e4c0b811Snicm args->count);
297e4c0b811Snicm args_free(args);
298e4c0b811Snicm return (NULL);
299e4c0b811Snicm }
300e4c0b811Snicm args_copy_value(new, value);
301e4c0b811Snicm break;
302e4c0b811Snicm case ARGS_PARSE_COMMANDS_OR_STRING:
303e4c0b811Snicm args_copy_value(new, value);
304e4c0b811Snicm break;
305e4c0b811Snicm case ARGS_PARSE_COMMANDS:
306e4c0b811Snicm if (value->type != ARGS_COMMANDS) {
307e4c0b811Snicm xasprintf(cause,
308e4c0b811Snicm "argument %u must be { commands }",
309e4c0b811Snicm args->count);
310e4c0b811Snicm args_free(args);
311e4c0b811Snicm return (NULL);
312e4c0b811Snicm }
313e4c0b811Snicm args_copy_value(new, value);
314e4c0b811Snicm break;
315e4c0b811Snicm }
316fb147d85Snicm }
317fb147d85Snicm }
318ca7befccSnicm
319e4c0b811Snicm if (parse->lower != -1 && args->count < (u_int)parse->lower) {
320e4c0b811Snicm xasprintf(cause,
321e4c0b811Snicm "too few arguments (need at least %u)",
322e4c0b811Snicm parse->lower);
323e4c0b811Snicm args_free(args);
324e4c0b811Snicm return (NULL);
325e4c0b811Snicm }
326e4c0b811Snicm if (parse->upper != -1 && args->count > (u_int)parse->upper) {
327e4c0b811Snicm xasprintf(cause,
328e4c0b811Snicm "too many arguments (need at most %u)",
329e4c0b811Snicm parse->upper);
3301693b10bSnicm args_free(args);
3311693b10bSnicm return (NULL);
3321693b10bSnicm }
333ca7befccSnicm return (args);
334ca7befccSnicm }
335ca7befccSnicm
336d8b32369Snicm /* Copy and expand a value. */
337d8b32369Snicm static void
args_copy_copy_value(struct args_value * to,struct args_value * from,int argc,char ** argv)338d8b32369Snicm args_copy_copy_value(struct args_value *to, struct args_value *from, int argc,
339d8b32369Snicm char **argv)
340d8b32369Snicm {
341d8b32369Snicm char *s, *expanded;
342d8b32369Snicm int i;
343d8b32369Snicm
344d8b32369Snicm to->type = from->type;
345d8b32369Snicm switch (from->type) {
346d8b32369Snicm case ARGS_NONE:
347d8b32369Snicm break;
348d8b32369Snicm case ARGS_STRING:
349d8b32369Snicm expanded = xstrdup(from->string);
350d8b32369Snicm for (i = 0; i < argc; i++) {
351d8b32369Snicm s = cmd_template_replace(expanded, argv[i], i + 1);
352d8b32369Snicm free(expanded);
353d8b32369Snicm expanded = s;
354d8b32369Snicm }
355d8b32369Snicm to->string = expanded;
356d8b32369Snicm break;
357d8b32369Snicm case ARGS_COMMANDS:
358d8b32369Snicm to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv);
359d8b32369Snicm break;
360d8b32369Snicm }
361d8b32369Snicm }
362d8b32369Snicm
363d8b32369Snicm /* Copy an arguments set. */
364d8b32369Snicm struct args *
args_copy(struct args * args,int argc,char ** argv)365d8b32369Snicm args_copy(struct args *args, int argc, char **argv)
366d8b32369Snicm {
367d8b32369Snicm struct args *new_args;
368d8b32369Snicm struct args_entry *entry;
369d8b32369Snicm struct args_value *value, *new_value;
370d8b32369Snicm u_int i;
371d8b32369Snicm
372a8e38936Snicm cmd_log_argv(argc, argv, "%s", __func__);
373a8e38936Snicm
374d8b32369Snicm new_args = args_create();
375d8b32369Snicm RB_FOREACH(entry, args_tree, &args->tree) {
376a8e38936Snicm if (TAILQ_EMPTY(&entry->values)) {
377a8e38936Snicm for (i = 0; i < entry->count; i++)
37883a0a8d9Snicm args_set(new_args, entry->flag, NULL, 0);
379d8b32369Snicm continue;
380d8b32369Snicm }
381d8b32369Snicm TAILQ_FOREACH(value, &entry->values, entry) {
382d8b32369Snicm new_value = xcalloc(1, sizeof *new_value);
383d8b32369Snicm args_copy_copy_value(new_value, value, argc, argv);
38483a0a8d9Snicm args_set(new_args, entry->flag, new_value, 0);
385d8b32369Snicm }
386d8b32369Snicm }
387a8e38936Snicm if (args->count == 0)
388a8e38936Snicm return (new_args);
389d8b32369Snicm new_args->count = args->count;
390d8b32369Snicm new_args->values = xcalloc(args->count, sizeof *new_args->values);
391d8b32369Snicm for (i = 0; i < args->count; i++) {
392d8b32369Snicm new_value = &new_args->values[i];
393d8b32369Snicm args_copy_copy_value(new_value, &args->values[i], argc, argv);
394d8b32369Snicm }
395d8b32369Snicm return (new_args);
396d8b32369Snicm }
397d8b32369Snicm
398fb147d85Snicm /* Free a value. */
399fb147d85Snicm void
args_free_value(struct args_value * value)400fb147d85Snicm args_free_value(struct args_value *value)
401fb147d85Snicm {
402fb147d85Snicm switch (value->type) {
403fb147d85Snicm case ARGS_NONE:
404fb147d85Snicm break;
405fb147d85Snicm case ARGS_STRING:
406fb147d85Snicm free(value->string);
407fb147d85Snicm break;
408fb147d85Snicm case ARGS_COMMANDS:
409fb147d85Snicm cmd_list_free(value->cmdlist);
410fb147d85Snicm break;
411fb147d85Snicm }
412bee784faSnicm free(value->cached);
413fb147d85Snicm }
414fb147d85Snicm
415d8b32369Snicm /* Free values. */
416d8b32369Snicm void
args_free_values(struct args_value * values,u_int count)417d8b32369Snicm args_free_values(struct args_value *values, u_int count)
418d8b32369Snicm {
419d8b32369Snicm u_int i;
420d8b32369Snicm
421d8b32369Snicm for (i = 0; i < count; i++)
422d8b32369Snicm args_free_value(&values[i]);
423d8b32369Snicm }
424d8b32369Snicm
425ca7befccSnicm /* Free an arguments set. */
426ca7befccSnicm void
args_free(struct args * args)427ca7befccSnicm args_free(struct args *args)
428ca7befccSnicm {
4291a5e800bSnicm struct args_entry *entry;
4301a5e800bSnicm struct args_entry *entry1;
431d0772b58Snicm struct args_value *value;
432d0772b58Snicm struct args_value *value1;
433ca7befccSnicm
434d8b32369Snicm args_free_values(args->values, args->count);
435bee784faSnicm free(args->values);
436ca7befccSnicm
4371a5e800bSnicm RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
4381a5e800bSnicm RB_REMOVE(args_tree, &args->tree, entry);
439d0772b58Snicm TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
440d0772b58Snicm TAILQ_REMOVE(&entry->values, value, entry);
441d28d1e33Snicm args_free_value(value);
442d0772b58Snicm free(value);
443d0772b58Snicm }
4441a5e800bSnicm free(entry);
4451a5e800bSnicm }
446ca7befccSnicm
4477d053cf9Snicm free(args);
448ca7befccSnicm }
449ca7befccSnicm
4501693b10bSnicm /* Convert arguments to vector. */
4511693b10bSnicm void
args_to_vector(struct args * args,int * argc,char *** argv)452d8b32369Snicm args_to_vector(struct args *args, int *argc, char ***argv)
4531693b10bSnicm {
454e4c0b811Snicm char *s;
455bee784faSnicm u_int i;
456bee784faSnicm
457bee784faSnicm *argc = 0;
458bee784faSnicm *argv = NULL;
459bee784faSnicm
460bee784faSnicm for (i = 0; i < args->count; i++) {
461e4c0b811Snicm switch (args->values[i].type) {
462e4c0b811Snicm case ARGS_NONE:
463e4c0b811Snicm break;
464e4c0b811Snicm case ARGS_STRING:
465e4c0b811Snicm cmd_append_argv(argc, argv, args->values[i].string);
466e4c0b811Snicm break;
467e4c0b811Snicm case ARGS_COMMANDS:
468e4c0b811Snicm s = cmd_list_print(args->values[i].cmdlist, 0);
469e4c0b811Snicm cmd_append_argv(argc, argv, s);
470e4c0b811Snicm free(s);
471e4c0b811Snicm break;
472e4c0b811Snicm }
473bee784faSnicm }
4741693b10bSnicm }
4751693b10bSnicm
476d8b32369Snicm /* Convert arguments from vector. */
477d8b32369Snicm struct args_value *
args_from_vector(int argc,char ** argv)478d8b32369Snicm args_from_vector(int argc, char **argv)
479d8b32369Snicm {
480d8b32369Snicm struct args_value *values;
481d8b32369Snicm int i;
482d8b32369Snicm
483d8b32369Snicm values = xcalloc(argc, sizeof *values);
484d8b32369Snicm for (i = 0; i < argc; i++) {
485d8b32369Snicm values[i].type = ARGS_STRING;
486d8b32369Snicm values[i].string = xstrdup(argv[i]);
487d8b32369Snicm }
488d8b32369Snicm return (values);
489d8b32369Snicm }
490d8b32369Snicm
4918ae71cbbSnicm /* Add to string. */
4928ae71cbbSnicm static void printflike(3, 4)
args_print_add(char ** buf,size_t * len,const char * fmt,...)4938ae71cbbSnicm args_print_add(char **buf, size_t *len, const char *fmt, ...)
494ca7befccSnicm {
4958ae71cbbSnicm va_list ap;
4968ae71cbbSnicm char *s;
4978ae71cbbSnicm size_t slen;
4988ae71cbbSnicm
4998ae71cbbSnicm va_start(ap, fmt);
5008ae71cbbSnicm slen = xvasprintf(&s, fmt, ap);
5018ae71cbbSnicm va_end(ap);
5028ae71cbbSnicm
5038ae71cbbSnicm *len += slen;
5048ae71cbbSnicm *buf = xrealloc(*buf, *len);
5058ae71cbbSnicm
5068ae71cbbSnicm strlcat(*buf, s, *len);
5078ae71cbbSnicm free(s);
5088ae71cbbSnicm }
5098ae71cbbSnicm
510bee784faSnicm /* Add value to string. */
511d0772b58Snicm static void
args_print_add_value(char ** buf,size_t * len,struct args_value * value)512bee784faSnicm args_print_add_value(char **buf, size_t *len, struct args_value *value)
513d0772b58Snicm {
514bee784faSnicm char *expanded = NULL;
515d0772b58Snicm
516d0772b58Snicm if (**buf != '\0')
517d0772b58Snicm args_print_add(buf, len, " ");
518d0772b58Snicm
519bee784faSnicm switch (value->type) {
520bee784faSnicm case ARGS_NONE:
521bee784faSnicm break;
522bee784faSnicm case ARGS_COMMANDS:
523bee784faSnicm expanded = cmd_list_print(value->cmdlist, 0);
524bee784faSnicm args_print_add(buf, len, "{ %s }", expanded);
525bee784faSnicm break;
526bee784faSnicm case ARGS_STRING:
527bee784faSnicm expanded = args_escape(value->string);
528bee784faSnicm args_print_add(buf, len, "%s", expanded);
529bee784faSnicm break;
530bee784faSnicm }
531bee784faSnicm free(expanded);
532d0772b58Snicm }
533d0772b58Snicm
5348ae71cbbSnicm /* Print a set of arguments. */
5358ae71cbbSnicm char *
args_print(struct args * args)5368ae71cbbSnicm args_print(struct args *args)
5378ae71cbbSnicm {
5388ae71cbbSnicm size_t len;
539d0772b58Snicm char *buf;
540bee784faSnicm u_int i, j;
5411a5e800bSnicm struct args_entry *entry;
54283a0a8d9Snicm struct args_entry *last = NULL;
543d0772b58Snicm struct args_value *value;
544ca7befccSnicm
5458ae71cbbSnicm len = 1;
5468ae71cbbSnicm buf = xcalloc(1, len);
547ca7befccSnicm
548ca7befccSnicm /* Process the flags first. */
5491a5e800bSnicm RB_FOREACH(entry, args_tree, &args->tree) {
55083a0a8d9Snicm if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
55183a0a8d9Snicm continue;
552d0772b58Snicm if (!TAILQ_EMPTY(&entry->values))
553ca7befccSnicm continue;
554ca7befccSnicm
5558ae71cbbSnicm if (*buf == '\0')
5568ae71cbbSnicm args_print_add(&buf, &len, "-");
557a02c6cc0Snicm for (j = 0; j < entry->count; j++)
5588ae71cbbSnicm args_print_add(&buf, &len, "%c", entry->flag);
559ca7befccSnicm }
560ca7befccSnicm
561ca7befccSnicm /* Then the flags with arguments. */
5621a5e800bSnicm RB_FOREACH(entry, args_tree, &args->tree) {
56383a0a8d9Snicm if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
56483a0a8d9Snicm if (*buf != '\0')
56583a0a8d9Snicm args_print_add(&buf, &len, " -%c", entry->flag);
56683a0a8d9Snicm else
56783a0a8d9Snicm args_print_add(&buf, &len, "-%c", entry->flag);
56883a0a8d9Snicm last = entry;
56983a0a8d9Snicm continue;
57083a0a8d9Snicm }
57183a0a8d9Snicm if (TAILQ_EMPTY(&entry->values))
57283a0a8d9Snicm continue;
5731693b10bSnicm TAILQ_FOREACH(value, &entry->values, entry) {
5741693b10bSnicm if (*buf != '\0')
5751693b10bSnicm args_print_add(&buf, &len, " -%c", entry->flag);
5761693b10bSnicm else
5771693b10bSnicm args_print_add(&buf, &len, "-%c", entry->flag);
578bee784faSnicm args_print_add_value(&buf, &len, value);
5791693b10bSnicm }
58083a0a8d9Snicm last = entry;
581ca7befccSnicm }
58283a0a8d9Snicm if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
58383a0a8d9Snicm args_print_add(&buf, &len, " --");
584ca7befccSnicm
585ca7befccSnicm /* And finally the argument vector. */
586bee784faSnicm for (i = 0; i < args->count; i++)
587bee784faSnicm args_print_add_value(&buf, &len, &args->values[i]);
588ca7befccSnicm
5898ae71cbbSnicm return (buf);
590ca7befccSnicm }
591ca7befccSnicm
5925c131106Snicm /* Escape an argument. */
5935c131106Snicm char *
args_escape(const char * s)5945c131106Snicm args_escape(const char *s)
5955c131106Snicm {
596d8b32369Snicm static const char dquoted[] = " #';${}%";
597ad2a1e09Snicm static const char squoted[] = " \"";
5985c131106Snicm char *escaped, *result;
599ad2a1e09Snicm int flags, quotes = 0;
6005c131106Snicm
6010f5e308bSnicm if (*s == '\0') {
6020f5e308bSnicm xasprintf(&result, "''");
6030f5e308bSnicm return (result);
6040f5e308bSnicm }
605ad2a1e09Snicm if (s[strcspn(s, dquoted)] != '\0')
606ad2a1e09Snicm quotes = '"';
607ad2a1e09Snicm else if (s[strcspn(s, squoted)] != '\0')
608ad2a1e09Snicm quotes = '\'';
609ad2a1e09Snicm
610f8016429Snicm if (s[0] != ' ' &&
611ad2a1e09Snicm s[1] == '\0' &&
612ad2a1e09Snicm (quotes != 0 || s[0] == '~')) {
6135c131106Snicm xasprintf(&escaped, "\\%c", s[0]);
6145c131106Snicm return (escaped);
6155c131106Snicm }
6165c131106Snicm
61706feda64Snicm flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
618ad2a1e09Snicm if (quotes == '"')
6195c131106Snicm flags |= VIS_DQ;
6205c131106Snicm utf8_stravis(&escaped, s, flags);
6215c131106Snicm
622ad2a1e09Snicm if (quotes == '\'')
623ad2a1e09Snicm xasprintf(&result, "'%s'", escaped);
624ad2a1e09Snicm else if (quotes == '"') {
6255c131106Snicm if (*escaped == '~')
6265c131106Snicm xasprintf(&result, "\"\\%s\"", escaped);
6275c131106Snicm else
6285c131106Snicm xasprintf(&result, "\"%s\"", escaped);
6295c131106Snicm } else {
6305c131106Snicm if (*escaped == '~')
6315c131106Snicm xasprintf(&result, "\\%s", escaped);
6325c131106Snicm else
6335c131106Snicm result = xstrdup(escaped);
6345c131106Snicm }
6355c131106Snicm free(escaped);
6365c131106Snicm return (result);
6375c131106Snicm }
6385c131106Snicm
639ca7befccSnicm /* Return if an argument is present. */
640ca7befccSnicm int
args_has(struct args * args,u_char flag)641ef07bfa2Snicm args_has(struct args *args, u_char flag)
642ca7befccSnicm {
643a02c6cc0Snicm struct args_entry *entry;
644a02c6cc0Snicm
645ef07bfa2Snicm entry = args_find(args, flag);
646a02c6cc0Snicm if (entry == NULL)
647a02c6cc0Snicm return (0);
648a02c6cc0Snicm return (entry->count);
649ca7befccSnicm }
650ca7befccSnicm
6511a5e800bSnicm /* Set argument value in the arguments tree. */
652a42faf7dSnicm void
args_set(struct args * args,u_char flag,struct args_value * value,int flags)65383a0a8d9Snicm args_set(struct args *args, u_char flag, struct args_value *value, int flags)
654ca7befccSnicm {
6551a5e800bSnicm struct args_entry *entry;
6561a5e800bSnicm
657ef07bfa2Snicm entry = args_find(args, flag);
658d0772b58Snicm if (entry == NULL) {
6591a5e800bSnicm entry = xcalloc(1, sizeof *entry);
660ef07bfa2Snicm entry->flag = flag;
661a02c6cc0Snicm entry->count = 1;
66283a0a8d9Snicm entry->flags = flags;
663d0772b58Snicm TAILQ_INIT(&entry->values);
66412cb8663Snicm RB_INSERT(args_tree, &args->tree, entry);
665a02c6cc0Snicm } else
666a02c6cc0Snicm entry->count++;
667bee784faSnicm if (value != NULL && value->type != ARGS_NONE)
668d0772b58Snicm TAILQ_INSERT_TAIL(&entry->values, value, entry);
66910e0aa77Snicm else
67010e0aa77Snicm free(value);
671d0772b58Snicm }
672ca7befccSnicm
673ca7befccSnicm /* Get argument value. Will be NULL if it isn't present. */
674ca7befccSnicm const char *
args_get(struct args * args,u_char flag)675ef07bfa2Snicm args_get(struct args *args, u_char flag)
676ca7befccSnicm {
6771a5e800bSnicm struct args_entry *entry;
6781a5e800bSnicm
679ef07bfa2Snicm if ((entry = args_find(args, flag)) == NULL)
680ef07bfa2Snicm return (NULL);
681ef07bfa2Snicm if (TAILQ_EMPTY(&entry->values))
6821a5e800bSnicm return (NULL);
683825f884aSnicm return (TAILQ_LAST(&entry->values, args_values)->string);
684d0772b58Snicm }
685d0772b58Snicm
686ef07bfa2Snicm /* Get first argument. */
687ef07bfa2Snicm u_char
args_first(struct args * args,struct args_entry ** entry)688ef07bfa2Snicm args_first(struct args *args, struct args_entry **entry)
689ef07bfa2Snicm {
690ef07bfa2Snicm *entry = RB_MIN(args_tree, &args->tree);
691ef07bfa2Snicm if (*entry == NULL)
692ef07bfa2Snicm return (0);
693ef07bfa2Snicm return ((*entry)->flag);
694ef07bfa2Snicm }
695ef07bfa2Snicm
696ef07bfa2Snicm /* Get next argument. */
697ef07bfa2Snicm u_char
args_next(struct args_entry ** entry)698ef07bfa2Snicm args_next(struct args_entry **entry)
699ef07bfa2Snicm {
700ef07bfa2Snicm *entry = RB_NEXT(args_tree, &args->tree, *entry);
701ef07bfa2Snicm if (*entry == NULL)
702ef07bfa2Snicm return (0);
703ef07bfa2Snicm return ((*entry)->flag);
704ef07bfa2Snicm }
705ef07bfa2Snicm
7061693b10bSnicm /* Get argument count. */
7071693b10bSnicm u_int
args_count(struct args * args)7081693b10bSnicm args_count(struct args *args)
7091693b10bSnicm {
710bee784faSnicm return (args->count);
711bee784faSnicm }
712bee784faSnicm
713d8b32369Snicm /* Get argument values. */
714d8b32369Snicm struct args_value *
args_values(struct args * args)715d8b32369Snicm args_values(struct args *args)
716d8b32369Snicm {
717d8b32369Snicm return (args->values);
718d8b32369Snicm }
719d8b32369Snicm
720bee784faSnicm /* Get argument value. */
721bee784faSnicm struct args_value *
args_value(struct args * args,u_int idx)722bee784faSnicm args_value(struct args *args, u_int idx)
723bee784faSnicm {
724bee784faSnicm if (idx >= args->count)
725bee784faSnicm return (NULL);
726bee784faSnicm return (&args->values[idx]);
7271693b10bSnicm }
7281693b10bSnicm
7291693b10bSnicm /* Return argument as string. */
7301693b10bSnicm const char *
args_string(struct args * args,u_int idx)7311693b10bSnicm args_string(struct args *args, u_int idx)
7321693b10bSnicm {
733d28d1e33Snicm if (idx >= args->count)
7341693b10bSnicm return (NULL);
735bee784faSnicm return (args_value_as_string(&args->values[idx]));
7361693b10bSnicm }
7371693b10bSnicm
7382db6a388Snicm /* Make a command now. */
7392db6a388Snicm struct cmd_list *
args_make_commands_now(struct cmd * self,struct cmdq_item * item,u_int idx,int expand)7400b5b9347Snicm args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
7410b5b9347Snicm int expand)
7422db6a388Snicm {
7432db6a388Snicm struct args_command_state *state;
7442db6a388Snicm char *error;
7452db6a388Snicm struct cmd_list *cmdlist;
7462db6a388Snicm
7470b5b9347Snicm state = args_make_commands_prepare(self, item, idx, NULL, 0, expand);
7482db6a388Snicm cmdlist = args_make_commands(state, 0, NULL, &error);
7492db6a388Snicm if (cmdlist == NULL) {
7502db6a388Snicm cmdq_error(item, "%s", error);
7512db6a388Snicm free(error);
7522db6a388Snicm }
75349390c5bSnicm else
75449390c5bSnicm cmdlist->references++;
75549390c5bSnicm args_make_commands_free(state);
7562db6a388Snicm return (cmdlist);
7572db6a388Snicm }
7582db6a388Snicm
7592db6a388Snicm /* Save bits to make a command later. */
7602db6a388Snicm struct args_command_state *
args_make_commands_prepare(struct cmd * self,struct cmdq_item * item,u_int idx,const char * default_command,int wait,int expand)7612db6a388Snicm args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
7622db6a388Snicm const char *default_command, int wait, int expand)
7632db6a388Snicm {
7642db6a388Snicm struct args *args = cmd_get_args(self);
7652db6a388Snicm struct cmd_find_state *target = cmdq_get_target(item);
7662db6a388Snicm struct client *tc = cmdq_get_target_client(item);
7672db6a388Snicm struct args_value *value;
7682db6a388Snicm struct args_command_state *state;
7692db6a388Snicm const char *cmd;
77031e1eab0Snicm const char *file;
7712db6a388Snicm
7722db6a388Snicm state = xcalloc(1, sizeof *state);
7732db6a388Snicm
7742db6a388Snicm if (idx < args->count) {
7752db6a388Snicm value = &args->values[idx];
7762db6a388Snicm if (value->type == ARGS_COMMANDS) {
7772db6a388Snicm state->cmdlist = value->cmdlist;
7782db6a388Snicm state->cmdlist->references++;
7792db6a388Snicm return (state);
7802db6a388Snicm }
7812db6a388Snicm cmd = value->string;
7822db6a388Snicm } else {
7832db6a388Snicm if (default_command == NULL)
7842db6a388Snicm fatalx("argument out of range");
7852db6a388Snicm cmd = default_command;
7862db6a388Snicm }
7872db6a388Snicm
7882db6a388Snicm
7892db6a388Snicm if (expand)
7902db6a388Snicm state->cmd = format_single_from_target(item, cmd);
7912db6a388Snicm else
7922db6a388Snicm state->cmd = xstrdup(cmd);
7932db6a388Snicm log_debug("%s: %s", __func__, state->cmd);
7942db6a388Snicm
7952db6a388Snicm if (wait)
7962db6a388Snicm state->pi.item = item;
79731e1eab0Snicm cmd_get_source(self, &file, &state->pi.line);
798d88178aeSnicm if (file != NULL)
79931e1eab0Snicm state->pi.file = xstrdup(file);
8002db6a388Snicm state->pi.c = tc;
8012db6a388Snicm if (state->pi.c != NULL)
8022db6a388Snicm state->pi.c->references++;
8032db6a388Snicm cmd_find_copy_state(&state->pi.fs, target);
8042db6a388Snicm
8052db6a388Snicm return (state);
8062db6a388Snicm }
8072db6a388Snicm
8082db6a388Snicm /* Return argument as command. */
8092db6a388Snicm struct cmd_list *
args_make_commands(struct args_command_state * state,int argc,char ** argv,char ** error)8102db6a388Snicm args_make_commands(struct args_command_state *state, int argc, char **argv,
8112db6a388Snicm char **error)
8122db6a388Snicm {
8132db6a388Snicm struct cmd_parse_result *pr;
8142db6a388Snicm char *cmd, *new_cmd;
8152db6a388Snicm int i;
8162db6a388Snicm
817d8b32369Snicm if (state->cmdlist != NULL) {
818d8b32369Snicm if (argc == 0)
8192db6a388Snicm return (state->cmdlist);
820d8b32369Snicm return (cmd_list_copy(state->cmdlist, argc, argv));
821d8b32369Snicm }
8222db6a388Snicm
8232db6a388Snicm cmd = xstrdup(state->cmd);
824a01f743bSnicm log_debug("%s: %s", __func__, cmd);
825a01f743bSnicm cmd_log_argv(argc, argv, __func__);
8262db6a388Snicm for (i = 0; i < argc; i++) {
8272db6a388Snicm new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
8282db6a388Snicm log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
8292db6a388Snicm free(cmd);
8302db6a388Snicm cmd = new_cmd;
8312db6a388Snicm }
8322db6a388Snicm log_debug("%s: %s", __func__, cmd);
8332db6a388Snicm
8342db6a388Snicm pr = cmd_parse_from_string(cmd, &state->pi);
8352db6a388Snicm free(cmd);
8362db6a388Snicm switch (pr->status) {
8372db6a388Snicm case CMD_PARSE_ERROR:
8382db6a388Snicm *error = pr->error;
8392db6a388Snicm return (NULL);
8402db6a388Snicm case CMD_PARSE_SUCCESS:
8412db6a388Snicm return (pr->cmdlist);
8422db6a388Snicm }
843432017adSnicm fatalx("invalid parse return state");
8442db6a388Snicm }
8452db6a388Snicm
8462db6a388Snicm /* Free commands state. */
8472db6a388Snicm void
args_make_commands_free(struct args_command_state * state)8482db6a388Snicm args_make_commands_free(struct args_command_state *state)
8492db6a388Snicm {
8502db6a388Snicm if (state->cmdlist != NULL)
8512db6a388Snicm cmd_list_free(state->cmdlist);
8522db6a388Snicm if (state->pi.c != NULL)
8532db6a388Snicm server_client_unref(state->pi.c);
85431e1eab0Snicm free((void *)state->pi.file);
8552db6a388Snicm free(state->cmd);
8562db6a388Snicm free(state);
8572db6a388Snicm }
8582db6a388Snicm
8592db6a388Snicm /* Get prepared command. */
8602db6a388Snicm char *
args_make_commands_get_command(struct args_command_state * state)8612db6a388Snicm args_make_commands_get_command(struct args_command_state *state)
8622db6a388Snicm {
8632db6a388Snicm struct cmd *first;
8642db6a388Snicm int n;
8652db6a388Snicm char *s;
8662db6a388Snicm
8672db6a388Snicm if (state->cmdlist != NULL) {
8682db6a388Snicm first = cmd_list_first(state->cmdlist);
8692db6a388Snicm if (first == NULL)
8702db6a388Snicm return (xstrdup(""));
8712db6a388Snicm return (xstrdup(cmd_get_entry(first)->name));
8722db6a388Snicm }
8732db6a388Snicm n = strcspn(state->cmd, " ,");
8742db6a388Snicm xasprintf(&s, "%.*s", n, state->cmd);
8752db6a388Snicm return (s);
8762db6a388Snicm }
8772db6a388Snicm
878d0772b58Snicm /* Get first value in argument. */
87905b80794Snicm struct args_value *
args_first_value(struct args * args,u_char flag)88005b80794Snicm args_first_value(struct args *args, u_char flag)
881d0772b58Snicm {
882d0772b58Snicm struct args_entry *entry;
883d0772b58Snicm
884ef07bfa2Snicm if ((entry = args_find(args, flag)) == NULL)
885d0772b58Snicm return (NULL);
88605b80794Snicm return (TAILQ_FIRST(&entry->values));
887d0772b58Snicm }
888d0772b58Snicm
889d0772b58Snicm /* Get next value in argument. */
89005b80794Snicm struct args_value *
args_next_value(struct args_value * value)89105b80794Snicm args_next_value(struct args_value *value)
892d0772b58Snicm {
89305b80794Snicm return (TAILQ_NEXT(value, entry));
894ca7befccSnicm }
895ca7befccSnicm
896ca7befccSnicm /* Convert an argument value to a number. */
897ca7befccSnicm long long
args_strtonum(struct args * args,u_char flag,long long minval,long long maxval,char ** cause)898ef07bfa2Snicm args_strtonum(struct args *args, u_char flag, long long minval,
899ef07bfa2Snicm long long maxval, char **cause)
900ca7befccSnicm {
901ca7befccSnicm const char *errstr;
902ca7befccSnicm long long ll;
9031a5e800bSnicm struct args_entry *entry;
904d0772b58Snicm struct args_value *value;
905ca7befccSnicm
906ef07bfa2Snicm if ((entry = args_find(args, flag)) == NULL) {
907ca7befccSnicm *cause = xstrdup("missing");
908ca7befccSnicm return (0);
909ca7befccSnicm }
910d0772b58Snicm value = TAILQ_LAST(&entry->values, args_values);
911f2587b2dSnicm if (value == NULL ||
912f2587b2dSnicm value->type != ARGS_STRING ||
913f2587b2dSnicm value->string == NULL) {
914f2587b2dSnicm *cause = xstrdup("missing");
915f2587b2dSnicm return (0);
916f2587b2dSnicm }
917ca7befccSnicm
918825f884aSnicm ll = strtonum(value->string, minval, maxval, &errstr);
919ca7befccSnicm if (errstr != NULL) {
920ca7befccSnicm *cause = xstrdup(errstr);
921ca7befccSnicm return (0);
922ca7befccSnicm }
923ca7befccSnicm
924ca7befccSnicm *cause = NULL;
925ca7befccSnicm return (ll);
926ca7befccSnicm }
92794300b02Snicm
928113211eaSnicm /* Convert an argument value to a number, and expand formats. */
929113211eaSnicm long long
args_strtonum_and_expand(struct args * args,u_char flag,long long minval,long long maxval,struct cmdq_item * item,char ** cause)930113211eaSnicm args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
931113211eaSnicm long long maxval, struct cmdq_item *item, char **cause)
932113211eaSnicm {
933113211eaSnicm const char *errstr;
934113211eaSnicm char *formatted;
935113211eaSnicm long long ll;
936113211eaSnicm struct args_entry *entry;
937113211eaSnicm struct args_value *value;
938113211eaSnicm
939113211eaSnicm if ((entry = args_find(args, flag)) == NULL) {
940113211eaSnicm *cause = xstrdup("missing");
941113211eaSnicm return (0);
942113211eaSnicm }
943113211eaSnicm value = TAILQ_LAST(&entry->values, args_values);
944113211eaSnicm if (value == NULL ||
945113211eaSnicm value->type != ARGS_STRING ||
946113211eaSnicm value->string == NULL) {
947113211eaSnicm *cause = xstrdup("missing");
948113211eaSnicm return (0);
949113211eaSnicm }
950113211eaSnicm
951113211eaSnicm formatted = format_single_from_target(item, value->string);
952113211eaSnicm ll = strtonum(formatted, minval, maxval, &errstr);
953113211eaSnicm free(formatted);
954113211eaSnicm if (errstr != NULL) {
955113211eaSnicm *cause = xstrdup(errstr);
956113211eaSnicm return (0);
957113211eaSnicm }
958113211eaSnicm
959113211eaSnicm *cause = NULL;
960113211eaSnicm return (ll);
961113211eaSnicm }
962113211eaSnicm
96394300b02Snicm /* Convert an argument to a number which may be a percentage. */
96494300b02Snicm long long
args_percentage(struct args * args,u_char flag,long long minval,long long maxval,long long curval,char ** cause)965ef07bfa2Snicm args_percentage(struct args *args, u_char flag, long long minval,
96694300b02Snicm long long maxval, long long curval, char **cause)
96794300b02Snicm {
968c6e6a0b3Snicm const char *value;
96994300b02Snicm struct args_entry *entry;
97094300b02Snicm
971ef07bfa2Snicm if ((entry = args_find(args, flag)) == NULL) {
97294300b02Snicm *cause = xstrdup("missing");
97394300b02Snicm return (0);
97494300b02Snicm }
975a7507044Snicm if (TAILQ_EMPTY(&entry->values)) {
976a7507044Snicm *cause = xstrdup("empty");
977a7507044Snicm return (0);
978a7507044Snicm }
979825f884aSnicm value = TAILQ_LAST(&entry->values, args_values)->string;
980c6e6a0b3Snicm return (args_string_percentage(value, minval, maxval, curval, cause));
981c6e6a0b3Snicm }
98294300b02Snicm
983c6e6a0b3Snicm /* Convert a string to a number which may be a percentage. */
984c6e6a0b3Snicm long long
args_string_percentage(const char * value,long long minval,long long maxval,long long curval,char ** cause)985c6e6a0b3Snicm args_string_percentage(const char *value, long long minval, long long maxval,
986c6e6a0b3Snicm long long curval, char **cause)
987c6e6a0b3Snicm {
988c6e6a0b3Snicm const char *errstr;
989c6e6a0b3Snicm long long ll;
990c6e6a0b3Snicm size_t valuelen = strlen(value);
991c6e6a0b3Snicm char *copy;
992c6e6a0b3Snicm
993a7507044Snicm if (valuelen == 0) {
994a7507044Snicm *cause = xstrdup("empty");
995a7507044Snicm return (0);
996a7507044Snicm }
997c6e6a0b3Snicm if (value[valuelen - 1] == '%') {
998c6e6a0b3Snicm copy = xstrdup(value);
99994300b02Snicm copy[valuelen - 1] = '\0';
100094300b02Snicm
100194300b02Snicm ll = strtonum(copy, 0, 100, &errstr);
100294300b02Snicm free(copy);
100394300b02Snicm if (errstr != NULL) {
100494300b02Snicm *cause = xstrdup(errstr);
100594300b02Snicm return (0);
100694300b02Snicm }
100794300b02Snicm ll = (curval * ll) / 100;
100894300b02Snicm if (ll < minval) {
1009c4b5b9d7Snicm *cause = xstrdup("too small");
101094300b02Snicm return (0);
101194300b02Snicm }
101294300b02Snicm if (ll > maxval) {
1013c4b5b9d7Snicm *cause = xstrdup("too large");
101494300b02Snicm return (0);
101594300b02Snicm }
101694300b02Snicm } else {
1017c6e6a0b3Snicm ll = strtonum(value, minval, maxval, &errstr);
101894300b02Snicm if (errstr != NULL) {
101994300b02Snicm *cause = xstrdup(errstr);
102094300b02Snicm return (0);
102194300b02Snicm }
102294300b02Snicm }
102394300b02Snicm
102494300b02Snicm *cause = NULL;
102594300b02Snicm return (ll);
102694300b02Snicm }
1027113211eaSnicm
1028113211eaSnicm /*
1029113211eaSnicm * Convert an argument to a number which may be a percentage, and expand
1030113211eaSnicm * formats.
1031113211eaSnicm */
1032113211eaSnicm long long
args_percentage_and_expand(struct args * args,u_char flag,long long minval,long long maxval,long long curval,struct cmdq_item * item,char ** cause)1033113211eaSnicm args_percentage_and_expand(struct args *args, u_char flag, long long minval,
1034113211eaSnicm long long maxval, long long curval, struct cmdq_item *item, char **cause)
1035113211eaSnicm {
1036113211eaSnicm const char *value;
1037113211eaSnicm struct args_entry *entry;
1038113211eaSnicm
1039113211eaSnicm if ((entry = args_find(args, flag)) == NULL) {
1040113211eaSnicm *cause = xstrdup("missing");
1041113211eaSnicm return (0);
1042113211eaSnicm }
1043a7507044Snicm if (TAILQ_EMPTY(&entry->values)) {
1044a7507044Snicm *cause = xstrdup("empty");
1045a7507044Snicm return (0);
1046a7507044Snicm }
1047113211eaSnicm value = TAILQ_LAST(&entry->values, args_values)->string;
1048113211eaSnicm return (args_string_percentage_and_expand(value, minval, maxval, curval,
1049113211eaSnicm item, cause));
1050113211eaSnicm }
1051113211eaSnicm
1052113211eaSnicm /*
1053113211eaSnicm * Convert a string to a number which may be a percentage, and expand formats.
1054113211eaSnicm */
1055113211eaSnicm long long
args_string_percentage_and_expand(const char * value,long long minval,long long maxval,long long curval,struct cmdq_item * item,char ** cause)1056113211eaSnicm args_string_percentage_and_expand(const char *value, long long minval,
1057113211eaSnicm long long maxval, long long curval, struct cmdq_item *item, char **cause)
1058113211eaSnicm {
1059113211eaSnicm const char *errstr;
1060113211eaSnicm long long ll;
1061113211eaSnicm size_t valuelen = strlen(value);
1062113211eaSnicm char *copy, *f;
1063113211eaSnicm
1064113211eaSnicm if (value[valuelen - 1] == '%') {
1065113211eaSnicm copy = xstrdup(value);
1066113211eaSnicm copy[valuelen - 1] = '\0';
1067113211eaSnicm
1068113211eaSnicm f = format_single_from_target(item, copy);
1069113211eaSnicm ll = strtonum(f, 0, 100, &errstr);
1070113211eaSnicm free(f);
1071113211eaSnicm free(copy);
1072113211eaSnicm if (errstr != NULL) {
1073113211eaSnicm *cause = xstrdup(errstr);
1074113211eaSnicm return (0);
1075113211eaSnicm }
1076113211eaSnicm ll = (curval * ll) / 100;
1077113211eaSnicm if (ll < minval) {
1078113211eaSnicm *cause = xstrdup("too small");
1079113211eaSnicm return (0);
1080113211eaSnicm }
1081113211eaSnicm if (ll > maxval) {
1082113211eaSnicm *cause = xstrdup("too large");
1083113211eaSnicm return (0);
1084113211eaSnicm }
1085113211eaSnicm } else {
1086113211eaSnicm f = format_single_from_target(item, value);
1087113211eaSnicm ll = strtonum(f, minval, maxval, &errstr);
1088113211eaSnicm free(f);
1089113211eaSnicm if (errstr != NULL) {
1090113211eaSnicm *cause = xstrdup(errstr);
1091113211eaSnicm return (0);
1092113211eaSnicm }
1093113211eaSnicm }
1094113211eaSnicm
1095113211eaSnicm *cause = NULL;
1096113211eaSnicm return (ll);
1097113211eaSnicm }
1098