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