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