1/* $OpenBSD: arguments.c,v 1.64 2024/05/13 11:45:05 nicm Exp $ */
2
3/*
4 * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20
21#include <ctype.h>
22#include <stdlib.h>
23#include <string.h>
24#include <vis.h>
25
26#include "tmux.h"
27
28/*
29 * Manipulate command arguments.
30 */
31
32/* List of argument values. */
33TAILQ_HEAD(args_values, args_value);
34
35/* Single arguments flag. */
36struct args_entry {
37	u_char			 flag;
38	struct args_values	 values;
39	u_int			 count;
40
41	int			 flags;
42#define ARGS_ENTRY_OPTIONAL_VALUE 0x1
43
44	RB_ENTRY(args_entry)	 entry;
45};
46
47/* Parsed argument flags and values. */
48struct args {
49	struct args_tree	 tree;
50	u_int			 count;
51	struct args_value	*values;
52};
53
54/* Prepared command state. */
55struct args_command_state {
56	struct cmd_list		*cmdlist;
57	char			*cmd;
58	struct cmd_parse_input	 pi;
59};
60
61static struct args_entry	*args_find(struct args *, u_char);
62
63static int	args_cmp(struct args_entry *, struct args_entry *);
64RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
65
66/* Arguments tree comparison function. */
67static int
68args_cmp(struct args_entry *a1, struct args_entry *a2)
69{
70	return (a1->flag - a2->flag);
71}
72
73/* Find a flag in the arguments tree. */
74static struct args_entry *
75args_find(struct args *args, u_char flag)
76{
77	struct args_entry	entry;
78
79	entry.flag = flag;
80	return (RB_FIND(args_tree, &args->tree, &entry));
81}
82
83/* Copy value. */
84static void
85args_copy_value(struct args_value *to, struct args_value *from)
86{
87	to->type = from->type;
88	switch (from->type) {
89	case ARGS_NONE:
90		break;
91	case ARGS_COMMANDS:
92		to->cmdlist = from->cmdlist;
93		to->cmdlist->references++;
94		break;
95	case ARGS_STRING:
96		to->string = xstrdup(from->string);
97		break;
98	}
99}
100
101/* Type to string. */
102static const char *
103args_type_to_string (enum args_type type)
104{
105	switch (type)
106	{
107	case ARGS_NONE:
108		return "NONE";
109	case ARGS_STRING:
110		return "STRING";
111	case ARGS_COMMANDS:
112		return "COMMANDS";
113	}
114	return "INVALID";
115}
116
117/* Get value as string. */
118static const char *
119args_value_as_string(struct args_value *value)
120{
121	switch (value->type) {
122	case ARGS_NONE:
123		return ("");
124	case ARGS_COMMANDS:
125		if (value->cached == NULL)
126			value->cached = cmd_list_print(value->cmdlist, 0);
127		return (value->cached);
128	case ARGS_STRING:
129		return (value->string);
130	}
131	fatalx("unexpected argument type");
132}
133
134/* Create an empty arguments set. */
135struct args *
136args_create(void)
137{
138	struct args	 *args;
139
140	args = xcalloc(1, sizeof *args);
141	RB_INIT(&args->tree);
142	return (args);
143}
144
145/* Parse a single flag. */
146static int
147args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
148    struct args *args, u_int *i, const char *string, int flag,
149    int optional_argument)
150{
151	struct args_value	*argument, *new;
152	const char		*s;
153
154	new = xcalloc(1, sizeof *new);
155	if (*string != '\0') {
156		new->type = ARGS_STRING;
157		new->string = xstrdup(string);
158		goto out;
159	}
160
161	if (*i == count)
162		argument = NULL;
163	else {
164		argument = &values[*i];
165		if (argument->type != ARGS_STRING) {
166			xasprintf(cause, "-%c argument must be a string", flag);
167			args_free_value(new);
168			free(new);
169			return (-1);
170		}
171	}
172	if (argument == NULL) {
173		args_free_value(new);
174		free(new);
175		if (optional_argument) {
176			log_debug("%s: -%c (optional)", __func__, flag);
177			args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
178			return (0); /* either - or end */
179		}
180		xasprintf(cause, "-%c expects an argument", flag);
181		return (-1);
182	}
183	args_copy_value(new, argument);
184	(*i)++;
185
186out:
187	s = args_value_as_string(new);
188	log_debug("%s: -%c = %s", __func__, flag, s);
189	args_set(args, flag, new, 0);
190	return (0);
191}
192
193/* Parse flags argument. */
194static int
195args_parse_flags(const struct args_parse *parse, struct args_value *values,
196    u_int count, char **cause, struct args *args, u_int *i)
197{
198	struct args_value	*value;
199	u_char			 flag;
200	const char		*found, *string;
201	int			 optional_argument;
202
203	value = &values[*i];
204	if (value->type != ARGS_STRING)
205		return (1);
206
207	string = value->string;
208	log_debug("%s: next %s", __func__, string);
209	if (*string++ != '-' || *string == '\0')
210		return (1);
211	(*i)++;
212	if (string[0] == '-' && string[1] == '\0')
213		return (1);
214
215	for (;;) {
216		flag = *string++;
217		if (flag == '\0')
218			return (0);
219		if (flag == '?')
220			return (-1);
221		if (!isalnum(flag)) {
222			xasprintf(cause, "invalid flag -%c", flag);
223			return (-1);
224		}
225
226		found = strchr(parse->template, flag);
227		if (found == NULL) {
228			xasprintf(cause, "unknown flag -%c", flag);
229			return (-1);
230		}
231		if (found[1] != ':') {
232			log_debug("%s: -%c", __func__, flag);
233			args_set(args, flag, NULL, 0);
234			continue;
235		}
236		optional_argument = (found[2] == ':');
237		return (args_parse_flag_argument(values, count, cause, args, i,
238		    string, flag, optional_argument));
239	}
240}
241
242/* Parse arguments into a new argument set. */
243struct args *
244args_parse(const struct args_parse *parse, struct args_value *values,
245    u_int count, char **cause)
246{
247	struct args		*args;
248	u_int			 i;
249	enum args_parse_type	 type;
250	struct args_value	*value, *new;
251	const char		*s;
252	int			 stop;
253
254	if (count == 0)
255		return (args_create());
256
257	args = args_create();
258	for (i = 1; i < count; /* nothing */) {
259		stop = args_parse_flags(parse, values, count, cause, args, &i);
260		if (stop == -1) {
261			args_free(args);
262			return (NULL);
263		}
264		if (stop == 1)
265			break;
266	}
267	log_debug("%s: flags end at %u of %u", __func__, i, count);
268	if (i != count) {
269		for (/* nothing */; i < count; i++) {
270			value = &values[i];
271
272			s = args_value_as_string(value);
273			log_debug("%s: %u = %s (type %s)", __func__, i, s,
274			    args_type_to_string (value->type));
275
276			if (parse->cb != NULL) {
277				type = parse->cb(args, args->count, cause);
278				if (type == ARGS_PARSE_INVALID) {
279					args_free(args);
280					return (NULL);
281				}
282			} else
283				type = ARGS_PARSE_STRING;
284
285			args->values = xrecallocarray(args->values,
286			    args->count, args->count + 1, sizeof *args->values);
287			new = &args->values[args->count++];
288
289			switch (type) {
290			case ARGS_PARSE_INVALID:
291				fatalx("unexpected argument type");
292			case ARGS_PARSE_STRING:
293				if (value->type != ARGS_STRING) {
294					xasprintf(cause,
295					    "argument %u must be \"string\"",
296					    args->count);
297					args_free(args);
298					return (NULL);
299				}
300				args_copy_value(new, value);
301				break;
302			case ARGS_PARSE_COMMANDS_OR_STRING:
303				args_copy_value(new, value);
304				break;
305			case ARGS_PARSE_COMMANDS:
306				if (value->type != ARGS_COMMANDS) {
307					xasprintf(cause,
308					    "argument %u must be { commands }",
309					    args->count);
310					args_free(args);
311					return (NULL);
312				}
313				args_copy_value(new, value);
314				break;
315			}
316		}
317	}
318
319	if (parse->lower != -1 && args->count < (u_int)parse->lower) {
320		xasprintf(cause,
321		    "too few arguments (need at least %u)",
322		    parse->lower);
323		args_free(args);
324		return (NULL);
325	}
326	if (parse->upper != -1 && args->count > (u_int)parse->upper) {
327		xasprintf(cause,
328		    "too many arguments (need at most %u)",
329		    parse->upper);
330		args_free(args);
331		return (NULL);
332	}
333	return (args);
334}
335
336/* Copy and expand a value. */
337static void
338args_copy_copy_value(struct args_value *to, struct args_value *from, int argc,
339    char **argv)
340{
341	char	*s, *expanded;
342	int	 i;
343
344	to->type = from->type;
345	switch (from->type) {
346	case ARGS_NONE:
347		break;
348	case ARGS_STRING:
349		expanded = xstrdup(from->string);
350		for (i = 0; i < argc; i++) {
351			s = cmd_template_replace(expanded, argv[i], i + 1);
352			free(expanded);
353			expanded = s;
354		}
355		to->string = expanded;
356		break;
357	case ARGS_COMMANDS:
358		to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv);
359		break;
360	}
361}
362
363/* Copy an arguments set. */
364struct args *
365args_copy(struct args *args, int argc, char **argv)
366{
367	struct args		*new_args;
368	struct args_entry	*entry;
369	struct args_value	*value, *new_value;
370	u_int			 i;
371
372	cmd_log_argv(argc, argv, "%s", __func__);
373
374	new_args = args_create();
375	RB_FOREACH(entry, args_tree, &args->tree) {
376		if (TAILQ_EMPTY(&entry->values)) {
377			for (i = 0; i < entry->count; i++)
378				args_set(new_args, entry->flag, NULL, 0);
379			continue;
380		}
381		TAILQ_FOREACH(value, &entry->values, entry) {
382			new_value = xcalloc(1, sizeof *new_value);
383			args_copy_copy_value(new_value, value, argc, argv);
384			args_set(new_args, entry->flag, new_value, 0);
385		}
386	}
387	if (args->count == 0)
388		return (new_args);
389	new_args->count = args->count;
390	new_args->values = xcalloc(args->count, sizeof *new_args->values);
391	for (i = 0; i < args->count; i++) {
392		new_value = &new_args->values[i];
393		args_copy_copy_value(new_value, &args->values[i], argc, argv);
394	}
395	return (new_args);
396}
397
398/* Free a value. */
399void
400args_free_value(struct args_value *value)
401{
402	switch (value->type) {
403	case ARGS_NONE:
404		break;
405	case ARGS_STRING:
406		free(value->string);
407		break;
408	case ARGS_COMMANDS:
409		cmd_list_free(value->cmdlist);
410		break;
411	}
412	free(value->cached);
413}
414
415/* Free values. */
416void
417args_free_values(struct args_value *values, u_int count)
418{
419	u_int	i;
420
421	for (i = 0; i < count; i++)
422		args_free_value(&values[i]);
423}
424
425/* Free an arguments set. */
426void
427args_free(struct args *args)
428{
429	struct args_entry	*entry;
430	struct args_entry	*entry1;
431	struct args_value	*value;
432	struct args_value	*value1;
433
434	args_free_values(args->values, args->count);
435	free(args->values);
436
437	RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
438		RB_REMOVE(args_tree, &args->tree, entry);
439		TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
440			TAILQ_REMOVE(&entry->values, value, entry);
441			args_free_value(value);
442			free(value);
443		}
444		free(entry);
445	}
446
447	free(args);
448}
449
450/* Convert arguments to vector. */
451void
452args_to_vector(struct args *args, int *argc, char ***argv)
453{
454	char	*s;
455	u_int	 i;
456
457	*argc = 0;
458	*argv = NULL;
459
460	for (i = 0; i < args->count; i++) {
461		switch (args->values[i].type) {
462		case ARGS_NONE:
463			break;
464		case ARGS_STRING:
465			cmd_append_argv(argc, argv, args->values[i].string);
466			break;
467		case ARGS_COMMANDS:
468			s = cmd_list_print(args->values[i].cmdlist, 0);
469			cmd_append_argv(argc, argv, s);
470			free(s);
471			break;
472		}
473	}
474}
475
476/* Convert arguments from vector. */
477struct args_value *
478args_from_vector(int argc, char **argv)
479{
480	struct args_value	*values;
481	int			 i;
482
483	values = xcalloc(argc, sizeof *values);
484	for (i = 0; i < argc; i++) {
485		values[i].type = ARGS_STRING;
486		values[i].string = xstrdup(argv[i]);
487	}
488	return (values);
489}
490
491/* Add to string. */
492static void printflike(3, 4)
493args_print_add(char **buf, size_t *len, const char *fmt, ...)
494{
495	va_list	 ap;
496	char	*s;
497	size_t	 slen;
498
499	va_start(ap, fmt);
500	slen = xvasprintf(&s, fmt, ap);
501	va_end(ap);
502
503	*len += slen;
504	*buf = xrealloc(*buf, *len);
505
506	strlcat(*buf, s, *len);
507	free(s);
508}
509
510/* Add value to string. */
511static void
512args_print_add_value(char **buf, size_t *len, struct args_value *value)
513{
514	char	*expanded = NULL;
515
516	if (**buf != '\0')
517		args_print_add(buf, len, " ");
518
519	switch (value->type) {
520	case ARGS_NONE:
521		break;
522	case ARGS_COMMANDS:
523		expanded = cmd_list_print(value->cmdlist, 0);
524		args_print_add(buf, len, "{ %s }", expanded);
525		break;
526	case ARGS_STRING:
527		expanded = args_escape(value->string);
528		args_print_add(buf, len, "%s", expanded);
529		break;
530	}
531	free(expanded);
532}
533
534/* Print a set of arguments. */
535char *
536args_print(struct args *args)
537{
538	size_t			 len;
539	char			*buf;
540	u_int			 i, j;
541	struct args_entry	*entry;
542	struct args_entry	*last = NULL;
543	struct args_value	*value;
544
545	len = 1;
546	buf = xcalloc(1, len);
547
548	/* Process the flags first. */
549	RB_FOREACH(entry, args_tree, &args->tree) {
550		if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
551			continue;
552		if (!TAILQ_EMPTY(&entry->values))
553			continue;
554
555		if (*buf == '\0')
556			args_print_add(&buf, &len, "-");
557		for (j = 0; j < entry->count; j++)
558			args_print_add(&buf, &len, "%c", entry->flag);
559	}
560
561	/* Then the flags with arguments. */
562	RB_FOREACH(entry, args_tree, &args->tree) {
563		if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
564			if (*buf != '\0')
565				args_print_add(&buf, &len, " -%c", entry->flag);
566			else
567				args_print_add(&buf, &len, "-%c", entry->flag);
568			last = entry;
569			continue;
570		}
571		if (TAILQ_EMPTY(&entry->values))
572			continue;
573		TAILQ_FOREACH(value, &entry->values, entry) {
574			if (*buf != '\0')
575				args_print_add(&buf, &len, " -%c", entry->flag);
576			else
577				args_print_add(&buf, &len, "-%c", entry->flag);
578			args_print_add_value(&buf, &len, value);
579		}
580		last = entry;
581	}
582	if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
583		args_print_add(&buf, &len, " --");
584
585	/* And finally the argument vector. */
586	for (i = 0; i < args->count; i++)
587		args_print_add_value(&buf, &len, &args->values[i]);
588
589	return (buf);
590}
591
592/* Escape an argument. */
593char *
594args_escape(const char *s)
595{
596	static const char	 dquoted[] = " #';${}%";
597	static const char	 squoted[] = " \"";
598	char			*escaped, *result;
599	int			 flags, quotes = 0;
600
601	if (*s == '\0') {
602		xasprintf(&result, "''");
603		return (result);
604	}
605	if (s[strcspn(s, dquoted)] != '\0')
606		quotes = '"';
607	else if (s[strcspn(s, squoted)] != '\0')
608		quotes = '\'';
609
610	if (s[0] != ' ' &&
611	    s[1] == '\0' &&
612	    (quotes != 0 || s[0] == '~')) {
613		xasprintf(&escaped, "\\%c", s[0]);
614		return (escaped);
615	}
616
617	flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
618	if (quotes == '"')
619		flags |= VIS_DQ;
620	utf8_stravis(&escaped, s, flags);
621
622	if (quotes == '\'')
623		xasprintf(&result, "'%s'", escaped);
624	else if (quotes == '"') {
625		if (*escaped == '~')
626			xasprintf(&result, "\"\\%s\"", escaped);
627		else
628			xasprintf(&result, "\"%s\"", escaped);
629	} else {
630		if (*escaped == '~')
631			xasprintf(&result, "\\%s", escaped);
632		else
633			result = xstrdup(escaped);
634	}
635	free(escaped);
636	return (result);
637}
638
639/* Return if an argument is present. */
640int
641args_has(struct args *args, u_char flag)
642{
643	struct args_entry	*entry;
644
645	entry = args_find(args, flag);
646	if (entry == NULL)
647		return (0);
648	return (entry->count);
649}
650
651/* Set argument value in the arguments tree. */
652void
653args_set(struct args *args, u_char flag, struct args_value *value, int flags)
654{
655	struct args_entry	*entry;
656
657	entry = args_find(args, flag);
658	if (entry == NULL) {
659		entry = xcalloc(1, sizeof *entry);
660		entry->flag = flag;
661		entry->count = 1;
662		entry->flags = flags;
663		TAILQ_INIT(&entry->values);
664		RB_INSERT(args_tree, &args->tree, entry);
665	} else
666		entry->count++;
667	if (value != NULL && value->type != ARGS_NONE)
668		TAILQ_INSERT_TAIL(&entry->values, value, entry);
669	else
670		free(value);
671}
672
673/* Get argument value. Will be NULL if it isn't present. */
674const char *
675args_get(struct args *args, u_char flag)
676{
677	struct args_entry	*entry;
678
679	if ((entry = args_find(args, flag)) == NULL)
680		return (NULL);
681	if (TAILQ_EMPTY(&entry->values))
682		return (NULL);
683	return (TAILQ_LAST(&entry->values, args_values)->string);
684}
685
686/* Get first argument. */
687u_char
688args_first(struct args *args, struct args_entry **entry)
689{
690	*entry = RB_MIN(args_tree, &args->tree);
691	if (*entry == NULL)
692		return (0);
693	return ((*entry)->flag);
694}
695
696/* Get next argument. */
697u_char
698args_next(struct args_entry **entry)
699{
700	*entry = RB_NEXT(args_tree, &args->tree, *entry);
701	if (*entry == NULL)
702		return (0);
703	return ((*entry)->flag);
704}
705
706/* Get argument count. */
707u_int
708args_count(struct args *args)
709{
710	return (args->count);
711}
712
713/* Get argument values. */
714struct args_value *
715args_values(struct args *args)
716{
717	return (args->values);
718}
719
720/* Get argument value. */
721struct args_value *
722args_value(struct args *args, u_int idx)
723{
724	if (idx >= args->count)
725		return (NULL);
726	return (&args->values[idx]);
727}
728
729/* Return argument as string. */
730const char *
731args_string(struct args *args, u_int idx)
732{
733	if (idx >= args->count)
734		return (NULL);
735	return (args_value_as_string(&args->values[idx]));
736}
737
738/* Make a command now. */
739struct cmd_list *
740args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
741    int expand)
742{
743	struct args_command_state	*state;
744	char				*error;
745	struct cmd_list			*cmdlist;
746
747	state = args_make_commands_prepare(self, item, idx, NULL, 0, expand);
748	cmdlist = args_make_commands(state, 0, NULL, &error);
749	if (cmdlist == NULL) {
750		cmdq_error(item, "%s", error);
751		free(error);
752	}
753	else
754		cmdlist->references++;
755	args_make_commands_free(state);
756	return (cmdlist);
757}
758
759/* Save bits to make a command later. */
760struct args_command_state *
761args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
762    const char *default_command, int wait, int expand)
763{
764	struct args			*args = cmd_get_args(self);
765	struct cmd_find_state		*target = cmdq_get_target(item);
766	struct client			*tc = cmdq_get_target_client(item);
767	struct args_value		*value;
768	struct args_command_state	*state;
769	const char			*cmd;
770	const char			*file;
771
772	state = xcalloc(1, sizeof *state);
773
774	if (idx < args->count) {
775		value = &args->values[idx];
776		if (value->type == ARGS_COMMANDS) {
777			state->cmdlist = value->cmdlist;
778			state->cmdlist->references++;
779			return (state);
780		}
781		cmd = value->string;
782	} else {
783		if (default_command == NULL)
784			fatalx("argument out of range");
785		cmd = default_command;
786	}
787
788
789	if (expand)
790		state->cmd = format_single_from_target(item, cmd);
791	else
792		state->cmd = xstrdup(cmd);
793	log_debug("%s: %s", __func__, state->cmd);
794
795	if (wait)
796		state->pi.item = item;
797	cmd_get_source(self, &file, &state->pi.line);
798	if (file != NULL)
799		state->pi.file = xstrdup(file);
800	state->pi.c = tc;
801	if (state->pi.c != NULL)
802		state->pi.c->references++;
803	cmd_find_copy_state(&state->pi.fs, target);
804
805	return (state);
806}
807
808/* Return argument as command. */
809struct cmd_list *
810args_make_commands(struct args_command_state *state, int argc, char **argv,
811    char **error)
812{
813	struct cmd_parse_result	*pr;
814	char			*cmd, *new_cmd;
815	int			 i;
816
817	if (state->cmdlist != NULL) {
818		if (argc == 0)
819			return (state->cmdlist);
820		return (cmd_list_copy(state->cmdlist, argc, argv));
821	}
822
823	cmd = xstrdup(state->cmd);
824	log_debug("%s: %s", __func__, cmd);
825	cmd_log_argv(argc, argv, __func__);
826	for (i = 0; i < argc; i++) {
827		new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
828		log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
829		free(cmd);
830		cmd = new_cmd;
831	}
832	log_debug("%s: %s", __func__, cmd);
833
834	pr = cmd_parse_from_string(cmd, &state->pi);
835	free(cmd);
836	switch (pr->status) {
837	case CMD_PARSE_ERROR:
838		*error = pr->error;
839		return (NULL);
840	case CMD_PARSE_SUCCESS:
841		return (pr->cmdlist);
842	}
843	fatalx("invalid parse return state");
844}
845
846/* Free commands state. */
847void
848args_make_commands_free(struct args_command_state *state)
849{
850	if (state->cmdlist != NULL)
851		cmd_list_free(state->cmdlist);
852	if (state->pi.c != NULL)
853		server_client_unref(state->pi.c);
854	free((void *)state->pi.file);
855	free(state->cmd);
856	free(state);
857}
858
859/* Get prepared command. */
860char *
861args_make_commands_get_command(struct args_command_state *state)
862{
863	struct cmd	*first;
864	int		 n;
865	char		*s;
866
867	if (state->cmdlist != NULL) {
868		first = cmd_list_first(state->cmdlist);
869		if (first == NULL)
870			return (xstrdup(""));
871		return (xstrdup(cmd_get_entry(first)->name));
872	}
873	n = strcspn(state->cmd, " ,");
874	xasprintf(&s, "%.*s", n, state->cmd);
875	return (s);
876}
877
878/* Get first value in argument. */
879struct args_value *
880args_first_value(struct args *args, u_char flag)
881{
882	struct args_entry	*entry;
883
884	if ((entry = args_find(args, flag)) == NULL)
885		return (NULL);
886	return (TAILQ_FIRST(&entry->values));
887}
888
889/* Get next value in argument. */
890struct args_value *
891args_next_value(struct args_value *value)
892{
893	return (TAILQ_NEXT(value, entry));
894}
895
896/* Convert an argument value to a number. */
897long long
898args_strtonum(struct args *args, u_char flag, long long minval,
899    long long maxval, char **cause)
900{
901	const char		*errstr;
902	long long		 ll;
903	struct args_entry	*entry;
904	struct args_value	*value;
905
906	if ((entry = args_find(args, flag)) == NULL) {
907		*cause = xstrdup("missing");
908		return (0);
909	}
910	value = TAILQ_LAST(&entry->values, args_values);
911	if (value == NULL ||
912	    value->type != ARGS_STRING ||
913	    value->string == NULL) {
914		*cause = xstrdup("missing");
915		return (0);
916	}
917
918	ll = strtonum(value->string, minval, maxval, &errstr);
919	if (errstr != NULL) {
920		*cause = xstrdup(errstr);
921		return (0);
922	}
923
924	*cause = NULL;
925	return (ll);
926}
927
928/* Convert an argument value to a number, and expand formats. */
929long long
930args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
931    long long maxval, struct cmdq_item *item, char **cause)
932{
933	const char		*errstr;
934	char			*formatted;
935	long long		 ll;
936	struct args_entry	*entry;
937	struct args_value	*value;
938
939	if ((entry = args_find(args, flag)) == NULL) {
940		*cause = xstrdup("missing");
941		return (0);
942	}
943	value = TAILQ_LAST(&entry->values, args_values);
944	if (value == NULL ||
945	    value->type != ARGS_STRING ||
946	    value->string == NULL) {
947		*cause = xstrdup("missing");
948		return (0);
949	}
950
951	formatted = format_single_from_target(item, value->string);
952	ll = strtonum(formatted, minval, maxval, &errstr);
953	free(formatted);
954	if (errstr != NULL) {
955		*cause = xstrdup(errstr);
956		return (0);
957	}
958
959	*cause = NULL;
960	return (ll);
961}
962
963/* Convert an argument to a number which may be a percentage. */
964long long
965args_percentage(struct args *args, u_char flag, long long minval,
966    long long maxval, long long curval, char **cause)
967{
968	const char		*value;
969	struct args_entry	*entry;
970
971	if ((entry = args_find(args, flag)) == NULL) {
972		*cause = xstrdup("missing");
973		return (0);
974	}
975	if (TAILQ_EMPTY(&entry->values)) {
976		*cause = xstrdup("empty");
977		return (0);
978	}
979	value = TAILQ_LAST(&entry->values, args_values)->string;
980	return (args_string_percentage(value, minval, maxval, curval, cause));
981}
982
983/* Convert a string to a number which may be a percentage. */
984long long
985args_string_percentage(const char *value, long long minval, long long maxval,
986    long long curval, char **cause)
987{
988	const char	*errstr;
989	long long	 ll;
990	size_t		 valuelen = strlen(value);
991	char		*copy;
992
993	if (valuelen == 0) {
994		*cause = xstrdup("empty");
995		return (0);
996	}
997	if (value[valuelen - 1] == '%') {
998		copy = xstrdup(value);
999		copy[valuelen - 1] = '\0';
1000
1001		ll = strtonum(copy, 0, 100, &errstr);
1002		free(copy);
1003		if (errstr != NULL) {
1004			*cause = xstrdup(errstr);
1005			return (0);
1006		}
1007		ll = (curval * ll) / 100;
1008		if (ll < minval) {
1009			*cause = xstrdup("too small");
1010			return (0);
1011		}
1012		if (ll > maxval) {
1013			*cause = xstrdup("too large");
1014			return (0);
1015		}
1016	} else {
1017		ll = strtonum(value, minval, maxval, &errstr);
1018		if (errstr != NULL) {
1019			*cause = xstrdup(errstr);
1020			return (0);
1021		}
1022	}
1023
1024	*cause = NULL;
1025	return (ll);
1026}
1027
1028/*
1029 * Convert an argument to a number which may be a percentage, and expand
1030 * formats.
1031 */
1032long long
1033args_percentage_and_expand(struct args *args, u_char flag, long long minval,
1034    long long maxval, long long curval, struct cmdq_item *item, char **cause)
1035{
1036	const char		*value;
1037	struct args_entry	*entry;
1038
1039	if ((entry = args_find(args, flag)) == NULL) {
1040		*cause = xstrdup("missing");
1041		return (0);
1042	}
1043	if (TAILQ_EMPTY(&entry->values)) {
1044		*cause = xstrdup("empty");
1045		return (0);
1046	}
1047	value = TAILQ_LAST(&entry->values, args_values)->string;
1048	return (args_string_percentage_and_expand(value, minval, maxval, curval,
1049		    item, cause));
1050}
1051
1052/*
1053 * Convert a string to a number which may be a percentage, and expand formats.
1054 */
1055long long
1056args_string_percentage_and_expand(const char *value, long long minval,
1057    long long maxval, long long curval, struct cmdq_item *item, char **cause)
1058{
1059	const char	*errstr;
1060	long long	 ll;
1061	size_t		 valuelen = strlen(value);
1062	char		*copy, *f;
1063
1064	if (value[valuelen - 1] == '%') {
1065		copy = xstrdup(value);
1066		copy[valuelen - 1] = '\0';
1067
1068		f = format_single_from_target(item, copy);
1069		ll = strtonum(f, 0, 100, &errstr);
1070		free(f);
1071		free(copy);
1072		if (errstr != NULL) {
1073			*cause = xstrdup(errstr);
1074			return (0);
1075		}
1076		ll = (curval * ll) / 100;
1077		if (ll < minval) {
1078			*cause = xstrdup("too small");
1079			return (0);
1080		}
1081		if (ll > maxval) {
1082			*cause = xstrdup("too large");
1083			return (0);
1084		}
1085	} else {
1086		f = format_single_from_target(item, value);
1087		ll = strtonum(f, minval, maxval, &errstr);
1088		free(f);
1089		if (errstr != NULL) {
1090			*cause = xstrdup(errstr);
1091			return (0);
1092		}
1093	}
1094
1095	*cause = NULL;
1096	return (ll);
1097}
1098