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