Annotation of src/usr.bin/tmux/arguments.c, Revision 1.54
1.54 ! nicm 1: /* $OpenBSD: arguments.c,v 1.53 2022/03/03 08:24:12 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: }
1.52 nicm 111: fatalx("unexpected argument type");
1.42 nicm 112: }
113:
1.36 nicm 114: /* Create an empty arguments set. */
115: struct args *
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:
1.42 nicm 125: /* Parse arguments into a new argument set. */
1.1 nicm 126: struct args *
1.42 nicm 127: args_parse(const struct args_parse *parse, struct args_value *values,
1.47 nicm 128: u_int count, char **cause)
1.1 nicm 129: {
1.42 nicm 130: struct args *args;
1.46 nicm 131: u_int i;
1.47 nicm 132: enum args_parse_type type;
1.43 nicm 133: struct args_value *value, *new;
1.53 nicm 134: u_char flag;
1.43 nicm 135: const char *found, *string, *s;
1.53 nicm 136: int optional_argument;
1.1 nicm 137:
1.42 nicm 138: if (count == 0)
139: return (args_create());
1.1 nicm 140:
1.36 nicm 141: args = args_create();
1.42 nicm 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;
1.51 nicm 158: if (flag == '?') {
159: args_free(args);
160: return (NULL);
161: }
1.42 nicm 162: if (!isalnum(flag)) {
1.47 nicm 163: xasprintf(cause, "invalid flag -%c", flag);
1.42 nicm 164: args_free(args);
165: return (NULL);
166: }
167: found = strchr(parse->template, flag);
168: if (found == NULL) {
1.47 nicm 169: xasprintf(cause, "unknown flag -%c", flag);
1.42 nicm 170: args_free(args);
171: return (NULL);
172: }
1.53 nicm 173: if (*++found != ':') {
1.48 nicm 174: log_debug("%s: -%c", __func__, flag);
1.42 nicm 175: args_set(args, flag, NULL);
176: continue;
177: }
1.53 nicm 178: if (*found == ':') {
179: optional_argument = 1;
180: found++;
181: }
1.47 nicm 182: new = xcalloc(1, sizeof *new);
1.43 nicm 183: if (*string != '\0') {
184: new->type = ARGS_STRING;
185: new->string = xstrdup(string);
186: } else {
1.42 nicm 187: if (i == count) {
1.53 nicm 188: if (optional_argument) {
189: log_debug("%s: -%c", __func__,
190: flag);
191: args_set(args, flag, NULL);
192: continue;
193: }
1.47 nicm 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);
1.42 nicm 204: args_free(args);
205: return (NULL);
206: }
1.43 nicm 207: args_copy_value(new, &values[i++]);
1.42 nicm 208: }
1.43 nicm 209: s = args_value_as_string(new);
1.48 nicm 210: log_debug("%s: -%c = %s", __func__, flag, s);
1.43 nicm 211: args_set(args, flag, new);
1.42 nicm 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);
1.48 nicm 221: log_debug("%s: %u = %s (type %d)", __func__, i, s,
222: value->type);
1.43 nicm 223:
1.47 nicm 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:
1.43 nicm 233: args->values = xrecallocarray(args->values,
234: args->count, args->count + 1, sizeof *args->values);
1.47 nicm 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: }
1.1 nicm 264: }
265: }
266:
1.47 nicm 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);
1.38 nicm 278: args_free(args);
279: return (NULL);
280: }
1.1 nicm 281: return (args);
1.42 nicm 282: }
283:
1.48 nicm 284: /* Copy and expand a value. */
285: static void
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 *
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:
1.49 nicm 320: cmd_log_argv(argc, argv, "%s", __func__);
321:
1.48 nicm 322: new_args = args_create();
323: RB_FOREACH(entry, args_tree, &args->tree) {
1.49 nicm 324: if (TAILQ_EMPTY(&entry->values)) {
325: for (i = 0; i < entry->count; i++)
326: args_set(new_args, entry->flag, NULL);
1.48 nicm 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: }
1.49 nicm 335: if (args->count == 0)
336: return (new_args);
1.48 nicm 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:
1.42 nicm 346: /* Free a value. */
347: void
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: }
1.43 nicm 360: free(value->cached);
1.1 nicm 361: }
362:
1.48 nicm 363: /* Free values. */
364: void
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:
1.1 nicm 373: /* Free an arguments set. */
374: void
375: args_free(struct args *args)
376: {
1.5 nicm 377: struct args_entry *entry;
378: struct args_entry *entry1;
1.21 nicm 379: struct args_value *value;
380: struct args_value *value1;
1.1 nicm 381:
1.48 nicm 382: args_free_values(args->values, args->count);
1.43 nicm 383: free(args->values);
1.1 nicm 384:
1.5 nicm 385: RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
386: RB_REMOVE(args_tree, &args->tree, entry);
1.21 nicm 387: TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
388: TAILQ_REMOVE(&entry->values, value, entry);
1.44 nicm 389: args_free_value(value);
1.21 nicm 390: free(value);
391: }
1.5 nicm 392: free(entry);
393: }
1.1 nicm 394:
1.4 nicm 395: free(args);
1.1 nicm 396: }
397:
1.38 nicm 398: /* Convert arguments to vector. */
399: void
1.48 nicm 400: args_to_vector(struct args *args, int *argc, char ***argv)
1.38 nicm 401: {
1.47 nicm 402: char *s;
403: u_int i;
1.43 nicm 404:
405: *argc = 0;
406: *argv = NULL;
407:
408: for (i = 0; i < args->count; i++) {
1.47 nicm 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: }
1.43 nicm 421: }
1.38 nicm 422: }
423:
1.48 nicm 424: /* Convert arguments from vector. */
425: struct args_value *
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:
1.12 nicm 439: /* Add to string. */
440: static void printflike(3, 4)
441: args_print_add(char **buf, size_t *len, const char *fmt, ...)
442: {
1.39 nicm 443: va_list ap;
1.12 nicm 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:
1.43 nicm 458: /* Add value to string. */
1.21 nicm 459: static void
1.43 nicm 460: args_print_add_value(char **buf, size_t *len, struct args_value *value)
1.21 nicm 461: {
1.43 nicm 462: char *expanded = NULL;
1.21 nicm 463:
464: if (**buf != '\0')
465: args_print_add(buf, len, " ");
466:
1.43 nicm 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);
1.21 nicm 480: }
481:
1.1 nicm 482: /* Print a set of arguments. */
1.12 nicm 483: char *
484: args_print(struct args *args)
1.1 nicm 485: {
1.39 nicm 486: size_t len;
1.21 nicm 487: char *buf;
1.43 nicm 488: u_int i, j;
1.5 nicm 489: struct args_entry *entry;
1.21 nicm 490: struct args_value *value;
1.1 nicm 491:
1.12 nicm 492: len = 1;
493: buf = xcalloc(1, len);
1.1 nicm 494:
495: /* Process the flags first. */
1.5 nicm 496: RB_FOREACH(entry, args_tree, &args->tree) {
1.21 nicm 497: if (!TAILQ_EMPTY(&entry->values))
1.1 nicm 498: continue;
499:
1.12 nicm 500: if (*buf == '\0')
501: args_print_add(&buf, &len, "-");
1.27 nicm 502: for (j = 0; j < entry->count; j++)
503: args_print_add(&buf, &len, "%c", entry->flag);
1.1 nicm 504: }
505:
506: /* Then the flags with arguments. */
1.5 nicm 507: RB_FOREACH(entry, args_tree, &args->tree) {
1.38 nicm 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);
1.43 nicm 513: args_print_add_value(&buf, &len, value);
1.38 nicm 514: }
1.1 nicm 515: }
516:
517: /* And finally the argument vector. */
1.43 nicm 518: for (i = 0; i < args->count; i++)
519: args_print_add_value(&buf, &len, &args->values[i]);
1.1 nicm 520:
1.12 nicm 521: return (buf);
1.22 nicm 522: }
523:
524: /* Escape an argument. */
525: char *
526: args_escape(const char *s)
527: {
1.48 nicm 528: static const char dquoted[] = " #';${}%";
1.35 nicm 529: static const char squoted[] = " \"";
1.22 nicm 530: char *escaped, *result;
1.35 nicm 531: int flags, quotes = 0;
1.22 nicm 532:
1.30 nicm 533: if (*s == '\0') {
534: xasprintf(&result, "''");
535: return (result);
536: }
1.35 nicm 537: if (s[strcspn(s, dquoted)] != '\0')
538: quotes = '"';
539: else if (s[strcspn(s, squoted)] != '\0')
540: quotes = '\'';
541:
1.26 nicm 542: if (s[0] != ' ' &&
1.35 nicm 543: s[1] == '\0' &&
544: (quotes != 0 || s[0] == '~')) {
1.22 nicm 545: xasprintf(&escaped, "\\%c", s[0]);
1.34 nicm 546: return (escaped);
547: }
548:
1.25 nicm 549: flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
1.35 nicm 550: if (quotes == '"')
1.22 nicm 551: flags |= VIS_DQ;
552: utf8_stravis(&escaped, s, flags);
553:
1.35 nicm 554: if (quotes == '\'')
555: xasprintf(&result, "'%s'", escaped);
556: else if (quotes == '"') {
1.22 nicm 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);
1.1 nicm 569: }
570:
571: /* Return if an argument is present. */
572: int
1.32 nicm 573: args_has(struct args *args, u_char flag)
1.1 nicm 574: {
1.27 nicm 575: struct args_entry *entry;
576:
1.32 nicm 577: entry = args_find(args, flag);
1.27 nicm 578: if (entry == NULL)
579: return (0);
580: return (entry->count);
1.1 nicm 581: }
582:
1.5 nicm 583: /* Set argument value in the arguments tree. */
1.19 nicm 584: void
1.43 nicm 585: args_set(struct args *args, u_char flag, struct args_value *value)
1.1 nicm 586: {
1.5 nicm 587: struct args_entry *entry;
588:
1.32 nicm 589: entry = args_find(args, flag);
1.21 nicm 590: if (entry == NULL) {
1.7 nicm 591: entry = xcalloc(1, sizeof *entry);
1.32 nicm 592: entry->flag = flag;
1.27 nicm 593: entry->count = 1;
1.21 nicm 594: TAILQ_INIT(&entry->values);
1.7 nicm 595: RB_INSERT(args_tree, &args->tree, entry);
1.27 nicm 596: } else
597: entry->count++;
1.43 nicm 598: if (value != NULL && value->type != ARGS_NONE)
1.21 nicm 599: TAILQ_INSERT_TAIL(&entry->values, value, entry);
1.1 nicm 600: }
601:
602: /* Get argument value. Will be NULL if it isn't present. */
603: const char *
1.32 nicm 604: args_get(struct args *args, u_char flag)
1.1 nicm 605: {
1.5 nicm 606: struct args_entry *entry;
607:
1.32 nicm 608: if ((entry = args_find(args, flag)) == NULL)
609: return (NULL);
610: if (TAILQ_EMPTY(&entry->values))
1.5 nicm 611: return (NULL);
1.41 nicm 612: return (TAILQ_LAST(&entry->values, args_values)->string);
1.21 nicm 613: }
614:
1.32 nicm 615: /* Get first argument. */
616: u_char
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
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);
1.38 nicm 633: }
634:
635: /* Get argument count. */
636: u_int
637: args_count(struct args *args)
638: {
1.43 nicm 639: return (args->count);
640: }
641:
1.48 nicm 642: /* Get argument values. */
643: struct args_value *
644: args_values(struct args *args)
645: {
646: return (args->values);
647: }
648:
1.43 nicm 649: /* Get argument value. */
650: struct args_value *
651: args_value(struct args *args, u_int idx)
652: {
653: if (idx >= args->count)
654: return (NULL);
655: return (&args->values[idx]);
1.38 nicm 656: }
657:
658: /* Return argument as string. */
659: const char *
660: args_string(struct args *args, u_int idx)
661: {
1.44 nicm 662: if (idx >= args->count)
1.38 nicm 663: return (NULL);
1.43 nicm 664: return (args_value_as_string(&args->values[idx]));
1.45 nicm 665: }
666:
667: /* Make a command now. */
668: struct cmd_list *
1.50 nicm 669: args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
670: int expand)
1.45 nicm 671: {
672: struct args_command_state *state;
673: char *error;
674: struct cmd_list *cmdlist;
675:
1.50 nicm 676: state = args_make_commands_prepare(self, item, idx, NULL, 0, expand);
1.45 nicm 677: cmdlist = args_make_commands(state, 0, NULL, &error);
678: if (cmdlist == NULL) {
679: cmdq_error(item, "%s", error);
680: free(error);
681: }
1.48 nicm 682: else
683: cmdlist->references++;
1.46 nicm 684: args_make_commands_free(state);
1.45 nicm 685: return (cmdlist);
686: }
687:
688: /* Save bits to make a command later. */
689: struct args_command_state *
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 *
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:
1.48 nicm 743: if (state->cmdlist != NULL) {
744: if (argc == 0)
745: return (state->cmdlist);
746: return (cmd_list_copy(state->cmdlist, argc, argv));
747: }
1.45 nicm 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: }
1.52 nicm 767: fatalx("invalid parse return state");
1.45 nicm 768: }
769:
770: /* Free commands state. */
771: void
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 *
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);
1.32 nicm 799: }
800:
1.21 nicm 801: /* Get first value in argument. */
1.37 nicm 802: struct args_value *
803: args_first_value(struct args *args, u_char flag)
1.21 nicm 804: {
805: struct args_entry *entry;
806:
1.32 nicm 807: if ((entry = args_find(args, flag)) == NULL)
1.21 nicm 808: return (NULL);
1.37 nicm 809: return (TAILQ_FIRST(&entry->values));
1.21 nicm 810: }
811:
812: /* Get next value in argument. */
1.37 nicm 813: struct args_value *
814: args_next_value(struct args_value *value)
1.21 nicm 815: {
1.37 nicm 816: return (TAILQ_NEXT(value, entry));
1.1 nicm 817: }
818:
819: /* Convert an argument value to a number. */
820: long long
1.32 nicm 821: args_strtonum(struct args *args, u_char flag, long long minval,
822: long long maxval, char **cause)
1.1 nicm 823: {
1.5 nicm 824: const char *errstr;
1.39 nicm 825: long long ll;
1.5 nicm 826: struct args_entry *entry;
1.21 nicm 827: struct args_value *value;
1.1 nicm 828:
1.32 nicm 829: if ((entry = args_find(args, flag)) == NULL) {
1.1 nicm 830: *cause = xstrdup("missing");
831: return (0);
832: }
1.21 nicm 833: value = TAILQ_LAST(&entry->values, args_values);
1.54 ! nicm 834: if (value == NULL ||
! 835: value->type != ARGS_STRING ||
! 836: value->string == NULL) {
! 837: *cause = xstrdup("missing");
! 838: return (0);
! 839: }
1.1 nicm 840:
1.41 nicm 841: ll = strtonum(value->string, minval, maxval, &errstr);
1.1 nicm 842: if (errstr != NULL) {
843: *cause = xstrdup(errstr);
844: return (0);
1.29 nicm 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
1.32 nicm 853: args_percentage(struct args *args, u_char flag, long long minval,
1.29 nicm 854: long long maxval, long long curval, char **cause)
855: {
1.31 nicm 856: const char *value;
1.29 nicm 857: struct args_entry *entry;
858:
1.32 nicm 859: if ((entry = args_find(args, flag)) == NULL) {
1.29 nicm 860: *cause = xstrdup("missing");
861: return (0);
862: }
1.41 nicm 863: value = TAILQ_LAST(&entry->values, args_values)->string;
1.31 nicm 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
869: args_string_percentage(const char *value, long long minval, long long maxval,
870: long long curval, char **cause)
871: {
872: const char *errstr;
1.39 nicm 873: long long ll;
1.31 nicm 874: size_t valuelen = strlen(value);
875: char *copy;
1.29 nicm 876:
1.31 nicm 877: if (value[valuelen - 1] == '%') {
878: copy = xstrdup(value);
1.29 nicm 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) {
1.33 nicm 889: *cause = xstrdup("too small");
1.29 nicm 890: return (0);
891: }
892: if (ll > maxval) {
1.33 nicm 893: *cause = xstrdup("too large");
1.29 nicm 894: return (0);
895: }
896: } else {
1.31 nicm 897: ll = strtonum(value, minval, maxval, &errstr);
1.29 nicm 898: if (errstr != NULL) {
899: *cause = xstrdup(errstr);
900: return (0);
901: }
1.1 nicm 902: }
903:
904: *cause = NULL;
905: return (ll);
906: }