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