xref: /openbsd-src/usr.bin/tmux/arguments.c (revision 28f73f25b5125f4bb05a96ce90cd4e3c2a91bb2f)
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