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