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