xref: /netbsd/external/bsd/tmux/dist/arguments.c (revision e0fe29fb)
1 /* $OpenBSD$ */
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 <unistd.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 	RB_ENTRY(args_entry)	 entry;
41 };
42 
43 /* Parsed argument flags and values. */
44 struct args {
45 	struct args_tree	 tree;
46 	u_int			 count;
47 	struct args_value	*values;
48 };
49 
50 /* Prepared command state. */
51 struct args_command_state {
52 	struct cmd_list		*cmdlist;
53 	char			*cmd;
54 	struct cmd_parse_input	 pi;
55 };
56 
57 static struct args_entry	*args_find(struct args *, u_char);
58 
59 static int	args_cmp(struct args_entry *, struct args_entry *);
60 RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
61 
62 /* Arguments tree comparison function. */
63 static int
args_cmp(struct args_entry * a1,struct args_entry * a2)64 args_cmp(struct args_entry *a1, struct args_entry *a2)
65 {
66 	return (a1->flag - a2->flag);
67 }
68 
69 /* Find a flag in the arguments tree. */
70 static struct args_entry *
args_find(struct args * args,u_char flag)71 args_find(struct args *args, u_char flag)
72 {
73 	struct args_entry	entry;
74 
75 	entry.flag = flag;
76 	return (RB_FIND(args_tree, &args->tree, &entry));
77 }
78 
79 /* Copy value. */
80 static void
args_copy_value(struct args_value * to,struct args_value * from)81 args_copy_value(struct args_value *to, struct args_value *from)
82 {
83 	to->type = from->type;
84 	switch (from->type) {
85 	case ARGS_NONE:
86 		break;
87 	case ARGS_COMMANDS:
88 		to->cmdlist = from->cmdlist;
89 		to->cmdlist->references++;
90 		break;
91 	case ARGS_STRING:
92 		to->string = xstrdup(from->string);
93 		break;
94 	}
95 }
96 
97 /* Get value as string. */
98 static const char *
args_value_as_string(struct args_value * value)99 args_value_as_string(struct args_value *value)
100 {
101 	switch (value->type) {
102 	case ARGS_NONE:
103 		return ("");
104 	case ARGS_COMMANDS:
105 		if (value->cached == NULL)
106 			value->cached = cmd_list_print(value->cmdlist, 0);
107 		return (value->cached);
108 	case ARGS_STRING:
109 		return (value->string);
110 	}
111 	fatalx("unexpected argument type");
112 }
113 
114 /* Create an empty arguments set. */
115 struct args *
args_create(void)116 args_create(void)
117 {
118 	struct args	 *args;
119 
120 	args = xcalloc(1, sizeof *args);
121 	RB_INIT(&args->tree);
122 	return (args);
123 }
124 
125 /* Parse arguments into a new argument set. */
126 struct args *
args_parse(const struct args_parse * parse,struct args_value * values,u_int count,char ** cause)127 args_parse(const struct args_parse *parse, struct args_value *values,
128     u_int count, char **cause)
129 {
130 	struct args		*args;
131 	u_int			 i;
132 	enum args_parse_type	 type;
133 	struct args_value	*value, *new;
134 	u_char			 flag;
135 	const char		*found, *string, *s;
136 	int			 optional_argument;
137 
138 	if (count == 0)
139 		return (args_create());
140 
141 	args = args_create();
142 	for (i = 1; i < count; /* nothing */) {
143 		value = &values[i];
144 		if (value->type != ARGS_STRING)
145 			break;
146 
147 		string = value->string;
148 		if (*string++ != '-' || *string == '\0')
149 			break;
150 		i++;
151 		if (string[0] == '-' && string[1] == '\0')
152 			break;
153 
154 		for (;;) {
155 			flag = *string++;
156 			if (flag == '\0')
157 				break;
158 			if (flag == '?') {
159 				args_free(args);
160 				return (NULL);
161 			}
162 			if (!isalnum(flag)) {
163 				xasprintf(cause, "invalid flag -%c", flag);
164 				args_free(args);
165 				return (NULL);
166 			}
167 			found = strchr(parse->template, flag);
168 			if (found == NULL) {
169 				xasprintf(cause, "unknown flag -%c", flag);
170 				args_free(args);
171 				return (NULL);
172 			}
173 			if (*++found != ':') {
174 				log_debug("%s: -%c", __func__, flag);
175 				args_set(args, flag, NULL);
176 				continue;
177 			}
178 			if (*found == ':') {
179 				optional_argument = 1;
180 				found++;
181 			}
182 			new = xcalloc(1, sizeof *new);
183 			if (*string != '\0') {
184 				new->type = ARGS_STRING;
185 				new->string = xstrdup(string);
186 			} else {
187 				if (i == count) {
188 					if (optional_argument) {
189 						log_debug("%s: -%c", __func__,
190 						    flag);
191 						args_set(args, flag, NULL);
192 						continue;
193 					}
194 					xasprintf(cause,
195 					    "-%c expects an argument",
196 					    flag);
197 					args_free(args);
198 					return (NULL);
199 				}
200 				if (values[i].type != ARGS_STRING) {
201 					xasprintf(cause,
202 					    "-%c argument must be a string",
203 					    flag);
204 					args_free(args);
205 					return (NULL);
206 				}
207 				args_copy_value(new, &values[i++]);
208 			}
209 			s = args_value_as_string(new);
210 			log_debug("%s: -%c = %s", __func__, flag, s);
211 			args_set(args, flag, new);
212 			break;
213 		}
214 	}
215 	log_debug("%s: flags end at %u of %u", __func__, i, count);
216 	if (i != count) {
217 		for (/* nothing */; i < count; i++) {
218 			value = &values[i];
219 
220 			s = args_value_as_string(value);
221 			log_debug("%s: %u = %s (type %d)", __func__, i, s,
222 			    value->type);
223 
224 			if (parse->cb != NULL) {
225 				type = parse->cb(args, args->count, cause);
226 				if (type == ARGS_PARSE_INVALID) {
227 					args_free(args);
228 					return (NULL);
229 				}
230 			} else
231 				type = ARGS_PARSE_STRING;
232 
233 			args->values = xrecallocarray(args->values,
234 			    args->count, args->count + 1, sizeof *args->values);
235 			new = &args->values[args->count++];
236 
237 			switch (type) {
238 			case ARGS_PARSE_INVALID:
239 				fatalx("unexpected argument type");
240 			case ARGS_PARSE_STRING:
241 				if (value->type != ARGS_STRING) {
242 					xasprintf(cause,
243 					    "argument %u must be \"string\"",
244 					    args->count);
245 					args_free(args);
246 					return (NULL);
247 				}
248 				args_copy_value(new, value);
249 				break;
250 			case ARGS_PARSE_COMMANDS_OR_STRING:
251 				args_copy_value(new, value);
252 				break;
253 			case ARGS_PARSE_COMMANDS:
254 				if (value->type != ARGS_COMMANDS) {
255 					xasprintf(cause,
256 					    "argument %u must be { commands }",
257 					    args->count);
258 					args_free(args);
259 					return (NULL);
260 				}
261 				args_copy_value(new, value);
262 				break;
263 			}
264 		}
265 	}
266 
267 	if (parse->lower != -1 && args->count < (u_int)parse->lower) {
268 		xasprintf(cause,
269 		    "too few arguments (need at least %u)",
270 		    parse->lower);
271 		args_free(args);
272 		return (NULL);
273 	}
274 	if (parse->upper != -1 && args->count > (u_int)parse->upper) {
275 		xasprintf(cause,
276 		    "too many arguments (need at most %u)",
277 		    parse->upper);
278 		args_free(args);
279 		return (NULL);
280 	}
281 	return (args);
282 }
283 
284 /* Copy and expand a value. */
285 static void
args_copy_copy_value(struct args_value * to,struct args_value * from,int argc,char ** argv)286 args_copy_copy_value(struct args_value *to, struct args_value *from, int argc,
287     char **argv)
288 {
289 	char	*s, *expanded;
290 	int	 i;
291 
292 	to->type = from->type;
293 	switch (from->type) {
294 	case ARGS_NONE:
295 		break;
296 	case ARGS_STRING:
297 		expanded = xstrdup(from->string);
298 		for (i = 0; i < argc; i++) {
299 			s = cmd_template_replace(expanded, argv[i], i + 1);
300 			free(expanded);
301 			expanded = s;
302 		}
303 		to->string = expanded;
304 		break;
305 	case ARGS_COMMANDS:
306 		to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv);
307 		break;
308 	}
309 }
310 
311 /* Copy an arguments set. */
312 struct args *
args_copy(struct args * args,int argc,char ** argv)313 args_copy(struct args *args, int argc, char **argv)
314 {
315 	struct args		*new_args;
316 	struct args_entry	*entry;
317 	struct args_value	*value, *new_value;
318 	u_int			 i;
319 
320 	cmd_log_argv(argc, argv, "%s", __func__);
321 
322 	new_args = args_create();
323 	RB_FOREACH(entry, args_tree, &args->tree) {
324 		if (TAILQ_EMPTY(&entry->values)) {
325 			for (i = 0; i < entry->count; i++)
326 				args_set(new_args, entry->flag, NULL);
327 			continue;
328 		}
329 		TAILQ_FOREACH(value, &entry->values, entry) {
330 			new_value = xcalloc(1, sizeof *new_value);
331 			args_copy_copy_value(new_value, value, argc, argv);
332 			args_set(new_args, entry->flag, new_value);
333 		}
334 	}
335 	if (args->count == 0)
336 		return (new_args);
337 	new_args->count = args->count;
338 	new_args->values = xcalloc(args->count, sizeof *new_args->values);
339 	for (i = 0; i < args->count; i++) {
340 		new_value = &new_args->values[i];
341 		args_copy_copy_value(new_value, &args->values[i], argc, argv);
342 	}
343 	return (new_args);
344 }
345 
346 /* Free a value. */
347 void
args_free_value(struct args_value * value)348 args_free_value(struct args_value *value)
349 {
350 	switch (value->type) {
351 	case ARGS_NONE:
352 		break;
353 	case ARGS_STRING:
354 		free(value->string);
355 		break;
356 	case ARGS_COMMANDS:
357 		cmd_list_free(value->cmdlist);
358 		break;
359 	}
360 	free(value->cached);
361 }
362 
363 /* Free values. */
364 void
args_free_values(struct args_value * values,u_int count)365 args_free_values(struct args_value *values, u_int count)
366 {
367 	u_int	i;
368 
369 	for (i = 0; i < count; i++)
370 		args_free_value(&values[i]);
371 }
372 
373 /* Free an arguments set. */
374 void
args_free(struct args * args)375 args_free(struct args *args)
376 {
377 	struct args_entry	*entry;
378 	struct args_entry	*entry1;
379 	struct args_value	*value;
380 	struct args_value	*value1;
381 
382 	args_free_values(args->values, args->count);
383 	free(args->values);
384 
385 	RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
386 		RB_REMOVE(args_tree, &args->tree, entry);
387 		TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
388 			TAILQ_REMOVE(&entry->values, value, entry);
389 			args_free_value(value);
390 			free(value);
391 		}
392 		free(entry);
393 	}
394 
395 	free(args);
396 }
397 
398 /* Convert arguments to vector. */
399 void
args_to_vector(struct args * args,int * argc,char *** argv)400 args_to_vector(struct args *args, int *argc, char ***argv)
401 {
402 	char	*s;
403 	u_int	 i;
404 
405 	*argc = 0;
406 	*argv = NULL;
407 
408 	for (i = 0; i < args->count; i++) {
409 		switch (args->values[i].type) {
410 		case ARGS_NONE:
411 			break;
412 		case ARGS_STRING:
413 			cmd_append_argv(argc, argv, args->values[i].string);
414 			break;
415 		case ARGS_COMMANDS:
416 			s = cmd_list_print(args->values[i].cmdlist, 0);
417 			cmd_append_argv(argc, argv, s);
418 			free(s);
419 			break;
420 		}
421 	}
422 }
423 
424 /* Convert arguments from vector. */
425 struct args_value *
args_from_vector(int argc,char ** argv)426 args_from_vector(int argc, char **argv)
427 {
428 	struct args_value	*values;
429 	int			 i;
430 
431 	values = xcalloc(argc, sizeof *values);
432 	for (i = 0; i < argc; i++) {
433 		values[i].type = ARGS_STRING;
434 		values[i].string = xstrdup(argv[i]);
435 	}
436 	return (values);
437 }
438 
439 /* Add to string. */
440 static void printflike(3, 4)
args_print_add(char ** buf,size_t * len,const char * fmt,...)441 args_print_add(char **buf, size_t *len, const char *fmt, ...)
442 {
443 	va_list	 ap;
444 	char	*s;
445 	size_t	 slen;
446 
447 	va_start(ap, fmt);
448 	slen = xvasprintf(&s, fmt, ap);
449 	va_end(ap);
450 
451 	*len += slen;
452 	*buf = xrealloc(*buf, *len);
453 
454 	strlcat(*buf, s, *len);
455 	free(s);
456 }
457 
458 /* Add value to string. */
459 static void
args_print_add_value(char ** buf,size_t * len,struct args_value * value)460 args_print_add_value(char **buf, size_t *len, struct args_value *value)
461 {
462 	char	*expanded = NULL;
463 
464 	if (**buf != '\0')
465 		args_print_add(buf, len, " ");
466 
467 	switch (value->type) {
468 	case ARGS_NONE:
469 		break;
470 	case ARGS_COMMANDS:
471 		expanded = cmd_list_print(value->cmdlist, 0);
472 		args_print_add(buf, len, "{ %s }", expanded);
473 		break;
474 	case ARGS_STRING:
475 		expanded = args_escape(value->string);
476 		args_print_add(buf, len, "%s", expanded);
477 		break;
478 	}
479 	free(expanded);
480 }
481 
482 /* Print a set of arguments. */
483 char *
args_print(struct args * args)484 args_print(struct args *args)
485 {
486 	size_t			 len;
487 	char			*buf;
488 	u_int			 i, j;
489 	struct args_entry	*entry;
490 	struct args_value	*value;
491 
492 	len = 1;
493 	buf = xcalloc(1, len);
494 
495 	/* Process the flags first. */
496 	RB_FOREACH(entry, args_tree, &args->tree) {
497 		if (!TAILQ_EMPTY(&entry->values))
498 			continue;
499 
500 		if (*buf == '\0')
501 			args_print_add(&buf, &len, "-");
502 		for (j = 0; j < entry->count; j++)
503 			args_print_add(&buf, &len, "%c", entry->flag);
504 	}
505 
506 	/* Then the flags with arguments. */
507 	RB_FOREACH(entry, args_tree, &args->tree) {
508 		TAILQ_FOREACH(value, &entry->values, entry) {
509 			if (*buf != '\0')
510 				args_print_add(&buf, &len, " -%c", entry->flag);
511 			else
512 				args_print_add(&buf, &len, "-%c", entry->flag);
513 			args_print_add_value(&buf, &len, value);
514 		}
515 	}
516 
517 	/* And finally the argument vector. */
518 	for (i = 0; i < args->count; i++)
519 		args_print_add_value(&buf, &len, &args->values[i]);
520 
521 	return (buf);
522 }
523 
524 /* Escape an argument. */
525 char *
args_escape(const char * s)526 args_escape(const char *s)
527 {
528 	static const char	 dquoted[] = " #';${}%";
529 	static const char	 squoted[] = " \"";
530 	char			*escaped, *result;
531 	int			 flags, quotes = 0;
532 
533 	if (*s == '\0') {
534 		xasprintf(&result, "''");
535 		return (result);
536 	}
537 	if (s[strcspn(s, dquoted)] != '\0')
538 		quotes = '"';
539 	else if (s[strcspn(s, squoted)] != '\0')
540 		quotes = '\'';
541 
542 	if (s[0] != ' ' &&
543 	    s[1] == '\0' &&
544 	    (quotes != 0 || s[0] == '~')) {
545 		xasprintf(&escaped, "\\%c", s[0]);
546 		return (escaped);
547 	}
548 
549 	flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
550 	if (quotes == '"')
551 		flags |= VIS_DQ;
552 	utf8_stravis(&escaped, s, flags);
553 
554 	if (quotes == '\'')
555 		xasprintf(&result, "'%s'", escaped);
556 	else if (quotes == '"') {
557 		if (*escaped == '~')
558 			xasprintf(&result, "\"\\%s\"", escaped);
559 		else
560 			xasprintf(&result, "\"%s\"", escaped);
561 	} else {
562 		if (*escaped == '~')
563 			xasprintf(&result, "\\%s", escaped);
564 		else
565 			result = xstrdup(escaped);
566 	}
567 	free(escaped);
568 	return (result);
569 }
570 
571 /* Return if an argument is present. */
572 int
args_has(struct args * args,u_char flag)573 args_has(struct args *args, u_char flag)
574 {
575 	struct args_entry	*entry;
576 
577 	entry = args_find(args, flag);
578 	if (entry == NULL)
579 		return (0);
580 	return (entry->count);
581 }
582 
583 /* Set argument value in the arguments tree. */
584 void
args_set(struct args * args,u_char flag,struct args_value * value)585 args_set(struct args *args, u_char flag, struct args_value *value)
586 {
587 	struct args_entry	*entry;
588 
589 	entry = args_find(args, flag);
590 	if (entry == NULL) {
591 		entry = xcalloc(1, sizeof *entry);
592 		entry->flag = flag;
593 		entry->count = 1;
594 		TAILQ_INIT(&entry->values);
595 		RB_INSERT(args_tree, &args->tree, entry);
596 	} else
597 		entry->count++;
598 	if (value != NULL && value->type != ARGS_NONE)
599 		TAILQ_INSERT_TAIL(&entry->values, value, entry);
600 }
601 
602 /* Get argument value. Will be NULL if it isn't present. */
603 const char *
args_get(struct args * args,u_char flag)604 args_get(struct args *args, u_char flag)
605 {
606 	struct args_entry	*entry;
607 
608 	if ((entry = args_find(args, flag)) == NULL)
609 		return (NULL);
610 	if (TAILQ_EMPTY(&entry->values))
611 		return (NULL);
612 	return (TAILQ_LAST(&entry->values, args_values)->string);
613 }
614 
615 /* Get first argument. */
616 u_char
args_first(struct args * args,struct args_entry ** entry)617 args_first(struct args *args, struct args_entry **entry)
618 {
619 	*entry = RB_MIN(args_tree, &args->tree);
620 	if (*entry == NULL)
621 		return (0);
622 	return ((*entry)->flag);
623 }
624 
625 /* Get next argument. */
626 u_char
args_next(struct args_entry ** entry)627 args_next(struct args_entry **entry)
628 {
629 	*entry = RB_NEXT(args_tree, &args->tree, *entry);
630 	if (*entry == NULL)
631 		return (0);
632 	return ((*entry)->flag);
633 }
634 
635 /* Get argument count. */
636 u_int
args_count(struct args * args)637 args_count(struct args *args)
638 {
639 	return (args->count);
640 }
641 
642 /* Get argument values. */
643 struct args_value *
args_values(struct args * args)644 args_values(struct args *args)
645 {
646 	return (args->values);
647 }
648 
649 /* Get argument value. */
650 struct args_value *
args_value(struct args * args,u_int idx)651 args_value(struct args *args, u_int idx)
652 {
653 	if (idx >= args->count)
654 		return (NULL);
655 	return (&args->values[idx]);
656 }
657 
658 /* Return argument as string. */
659 const char *
args_string(struct args * args,u_int idx)660 args_string(struct args *args, u_int idx)
661 {
662 	if (idx >= args->count)
663 		return (NULL);
664 	return (args_value_as_string(&args->values[idx]));
665 }
666 
667 /* Make a command now. */
668 struct cmd_list *
args_make_commands_now(struct cmd * self,struct cmdq_item * item,u_int idx,int expand)669 args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
670     int expand)
671 {
672 	struct args_command_state	*state;
673 	char				*error;
674 	struct cmd_list			*cmdlist;
675 
676 	state = args_make_commands_prepare(self, item, idx, NULL, 0, expand);
677 	cmdlist = args_make_commands(state, 0, NULL, &error);
678 	if (cmdlist == NULL) {
679 		cmdq_error(item, "%s", error);
680 		free(error);
681 	}
682 	else
683 		cmdlist->references++;
684 	args_make_commands_free(state);
685 	return (cmdlist);
686 }
687 
688 /* Save bits to make a command later. */
689 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)690 args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
691     const char *default_command, int wait, int expand)
692 {
693 	struct args			*args = cmd_get_args(self);
694 	struct cmd_find_state		*target = cmdq_get_target(item);
695 	struct client			*tc = cmdq_get_target_client(item);
696 	struct args_value		*value;
697 	struct args_command_state	*state;
698 	const char			*cmd;
699 
700 	state = xcalloc(1, sizeof *state);
701 
702 	if (idx < args->count) {
703 		value = &args->values[idx];
704 		if (value->type == ARGS_COMMANDS) {
705 			state->cmdlist = value->cmdlist;
706 			state->cmdlist->references++;
707 			return (state);
708 		}
709 		cmd = value->string;
710 	} else {
711 		if (default_command == NULL)
712 			fatalx("argument out of range");
713 		cmd = default_command;
714 	}
715 
716 
717 	if (expand)
718 		state->cmd = format_single_from_target(item, cmd);
719 	else
720 		state->cmd = xstrdup(cmd);
721 	log_debug("%s: %s", __func__, state->cmd);
722 
723 	if (wait)
724 		state->pi.item = item;
725 	cmd_get_source(self, &state->pi.file, &state->pi.line);
726 	state->pi.c = tc;
727 	if (state->pi.c != NULL)
728 		state->pi.c->references++;
729 	cmd_find_copy_state(&state->pi.fs, target);
730 
731 	return (state);
732 }
733 
734 /* Return argument as command. */
735 struct cmd_list *
args_make_commands(struct args_command_state * state,int argc,char ** argv,char ** error)736 args_make_commands(struct args_command_state *state, int argc, char **argv,
737     char **error)
738 {
739 	struct cmd_parse_result	*pr;
740 	char			*cmd, *new_cmd;
741 	int			 i;
742 
743 	if (state->cmdlist != NULL) {
744 		if (argc == 0)
745 			return (state->cmdlist);
746 		return (cmd_list_copy(state->cmdlist, argc, argv));
747 	}
748 
749 	cmd = xstrdup(state->cmd);
750 	for (i = 0; i < argc; i++) {
751 		new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
752 		log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
753 		free(cmd);
754 		cmd = new_cmd;
755 	}
756 	log_debug("%s: %s", __func__, cmd);
757 
758 	pr = cmd_parse_from_string(cmd, &state->pi);
759 	free(cmd);
760 	switch (pr->status) {
761 	case CMD_PARSE_ERROR:
762 		*error = pr->error;
763 		return (NULL);
764 	case CMD_PARSE_SUCCESS:
765 		return (pr->cmdlist);
766 	}
767 	fatalx("invalid parse return state");
768 }
769 
770 /* Free commands state. */
771 void
args_make_commands_free(struct args_command_state * state)772 args_make_commands_free(struct args_command_state *state)
773 {
774 	if (state->cmdlist != NULL)
775 		cmd_list_free(state->cmdlist);
776 	if (state->pi.c != NULL)
777 		server_client_unref(state->pi.c);
778 	free(state->cmd);
779 	free(state);
780 }
781 
782 /* Get prepared command. */
783 char *
args_make_commands_get_command(struct args_command_state * state)784 args_make_commands_get_command(struct args_command_state *state)
785 {
786 	struct cmd	*first;
787 	int		 n;
788 	char		*s;
789 
790 	if (state->cmdlist != NULL) {
791 		first = cmd_list_first(state->cmdlist);
792 		if (first == NULL)
793 			return (xstrdup(""));
794 		return (xstrdup(cmd_get_entry(first)->name));
795 	}
796 	n = strcspn(state->cmd, " ,");
797 	xasprintf(&s, "%.*s", n, state->cmd);
798 	return (s);
799 }
800 
801 /* Get first value in argument. */
802 struct args_value *
args_first_value(struct args * args,u_char flag)803 args_first_value(struct args *args, u_char flag)
804 {
805 	struct args_entry	*entry;
806 
807 	if ((entry = args_find(args, flag)) == NULL)
808 		return (NULL);
809 	return (TAILQ_FIRST(&entry->values));
810 }
811 
812 /* Get next value in argument. */
813 struct args_value *
args_next_value(struct args_value * value)814 args_next_value(struct args_value *value)
815 {
816 	return (TAILQ_NEXT(value, entry));
817 }
818 
819 /* Convert an argument value to a number. */
820 long long
args_strtonum(struct args * args,u_char flag,long long minval,long long maxval,char ** cause)821 args_strtonum(struct args *args, u_char flag, long long minval,
822     long long maxval, char **cause)
823 {
824 	const char		*errstr;
825 	long long		 ll;
826 	struct args_entry	*entry;
827 	struct args_value	*value;
828 
829 	if ((entry = args_find(args, flag)) == NULL) {
830 		*cause = xstrdup("missing");
831 		return (0);
832 	}
833 	value = TAILQ_LAST(&entry->values, args_values);
834 	if (value == NULL ||
835 	    value->type != ARGS_STRING ||
836 	    value->string == NULL) {
837 		*cause = xstrdup("missing");
838 		return (0);
839 	}
840 
841 	ll = strtonum(value->string, minval, maxval, &errstr);
842 	if (errstr != NULL) {
843 		*cause = xstrdup(errstr);
844 		return (0);
845 	}
846 
847 	*cause = NULL;
848 	return (ll);
849 }
850 
851 /* Convert an argument to a number which may be a percentage. */
852 long long
args_percentage(struct args * args,u_char flag,long long minval,long long maxval,long long curval,char ** cause)853 args_percentage(struct args *args, u_char flag, long long minval,
854     long long maxval, long long curval, char **cause)
855 {
856 	const char		*value;
857 	struct args_entry	*entry;
858 
859 	if ((entry = args_find(args, flag)) == NULL) {
860 		*cause = xstrdup("missing");
861 		return (0);
862 	}
863 	value = TAILQ_LAST(&entry->values, args_values)->string;
864 	return (args_string_percentage(value, minval, maxval, curval, cause));
865 }
866 
867 /* Convert a string to a number which may be a percentage. */
868 long long
args_string_percentage(const char * value,long long minval,long long maxval,long long curval,char ** cause)869 args_string_percentage(const char *value, long long minval, long long maxval,
870     long long curval, char **cause)
871 {
872 	const char	*errstr;
873 	long long	 ll;
874 	size_t		 valuelen = strlen(value);
875 	char		*copy;
876 
877 	if (value[valuelen - 1] == '%') {
878 		copy = xstrdup(value);
879 		copy[valuelen - 1] = '\0';
880 
881 		ll = strtonum(copy, 0, 100, &errstr);
882 		free(copy);
883 		if (errstr != NULL) {
884 			*cause = xstrdup(errstr);
885 			return (0);
886 		}
887 		ll = (curval * ll) / 100;
888 		if (ll < minval) {
889 			*cause = xstrdup("too small");
890 			return (0);
891 		}
892 		if (ll > maxval) {
893 			*cause = xstrdup("too large");
894 			return (0);
895 		}
896 	} else {
897 		ll = strtonum(value, minval, maxval, &errstr);
898 		if (errstr != NULL) {
899 			*cause = xstrdup(errstr);
900 			return (0);
901 		}
902 	}
903 
904 	*cause = NULL;
905 	return (ll);
906 }
907