xref: /openbsd/usr.bin/tmux/arguments.c (revision 28f73f25)
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. */
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
args_cmp(struct args_entry * a1,struct args_entry * a2)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 *
args_find(struct args * args,u_char flag)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
args_copy_value(struct args_value * to,struct args_value * from)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 *
args_type_to_string(enum args_type type)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 *
args_value_as_string(struct args_value * value)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 *
args_create(void)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
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)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 			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 
186 out:
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. */
194 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)195 args_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. */
243 struct args *
args_parse(const struct args_parse * parse,struct args_value * values,u_int count,char ** cause)244 args_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. */
337 static void
args_copy_copy_value(struct args_value * to,struct args_value * from,int argc,char ** argv)338 args_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. */
364 struct args *
args_copy(struct args * args,int argc,char ** argv)365 args_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. */
399 void
args_free_value(struct args_value * value)400 args_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. */
416 void
args_free_values(struct args_value * values,u_int count)417 args_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. */
426 void
args_free(struct args * args)427 args_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. */
451 void
args_to_vector(struct args * args,int * argc,char *** argv)452 args_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. */
477 struct args_value *
args_from_vector(int argc,char ** argv)478 args_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. */
492 static void printflike(3, 4)
args_print_add(char ** buf,size_t * len,const char * fmt,...)493 args_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. */
511 static void
args_print_add_value(char ** buf,size_t * len,struct args_value * value)512 args_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. */
535 char *
args_print(struct args * args)536 args_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. */
593 char *
args_escape(const char * s)594 args_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. */
640 int
args_has(struct args * args,u_char flag)641 args_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. */
652 void
args_set(struct args * args,u_char flag,struct args_value * value,int flags)653 args_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. */
674 const char *
args_get(struct args * args,u_char flag)675 args_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. */
687 u_char
args_first(struct args * args,struct args_entry ** entry)688 args_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. */
697 u_char
args_next(struct args_entry ** entry)698 args_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. */
707 u_int
args_count(struct args * args)708 args_count(struct args *args)
709 {
710 	return (args->count);
711 }
712 
713 /* Get argument values. */
714 struct args_value *
args_values(struct args * args)715 args_values(struct args *args)
716 {
717 	return (args->values);
718 }
719 
720 /* Get argument value. */
721 struct args_value *
args_value(struct args * args,u_int idx)722 args_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. */
730 const char *
args_string(struct args * args,u_int idx)731 args_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. */
739 struct cmd_list *
args_make_commands_now(struct cmd * self,struct cmdq_item * item,u_int idx,int expand)740 args_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. */
760 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)761 args_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. */
809 struct cmd_list *
args_make_commands(struct args_command_state * state,int argc,char ** argv,char ** error)810 args_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. */
847 void
args_make_commands_free(struct args_command_state * state)848 args_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. */
860 char *
args_make_commands_get_command(struct args_command_state * state)861 args_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. */
879 struct args_value *
args_first_value(struct args * args,u_char flag)880 args_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. */
890 struct args_value *
args_next_value(struct args_value * value)891 args_next_value(struct args_value *value)
892 {
893 	return (TAILQ_NEXT(value, entry));
894 }
895 
896 /* Convert an argument value to a number. */
897 long long
args_strtonum(struct args * args,u_char flag,long long minval,long long maxval,char ** cause)898 args_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. */
929 long long
args_strtonum_and_expand(struct args * args,u_char flag,long long minval,long long maxval,struct cmdq_item * item,char ** cause)930 args_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. */
964 long long
args_percentage(struct args * args,u_char flag,long long minval,long long maxval,long long curval,char ** cause)965 args_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. */
984 long long
args_string_percentage(const char * value,long long minval,long long maxval,long long curval,char ** cause)985 args_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  */
1032 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)1033 args_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  */
1055 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)1056 args_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