Annotation of src/usr.bin/tmux/arguments.c, Revision 1.43
1.43 ! nicm 1: /* $OpenBSD: arguments.c,v 1.42 2021/08/21 18:39:07 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.21 nicm 32: TAILQ_HEAD(args_values, args_value);
33:
1.11 nicm 34: struct args_entry {
35: u_char flag;
1.21 nicm 36: struct args_values values;
1.27 nicm 37: u_int count;
1.11 nicm 38: RB_ENTRY(args_entry) entry;
39: };
1.5 nicm 40:
1.38 nicm 41: struct args {
42: struct args_tree tree;
1.43 ! nicm 43: u_int count;
! 44: struct args_value *values;
1.38 nicm 45: };
46:
1.14 nicm 47: static struct args_entry *args_find(struct args *, u_char);
1.5 nicm 48:
1.14 nicm 49: static int args_cmp(struct args_entry *, struct args_entry *);
50: RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
1.5 nicm 51:
52: /* Arguments tree comparison function. */
1.14 nicm 53: static int
1.5 nicm 54: args_cmp(struct args_entry *a1, struct args_entry *a2)
55: {
56: return (a1->flag - a2->flag);
57: }
58:
59: /* Find a flag in the arguments tree. */
1.14 nicm 60: static struct args_entry *
1.32 nicm 61: args_find(struct args *args, u_char flag)
1.5 nicm 62: {
63: struct args_entry entry;
64:
1.32 nicm 65: entry.flag = flag;
1.5 nicm 66: return (RB_FIND(args_tree, &args->tree, &entry));
67: }
68:
1.43 ! nicm 69: /* Copy value. */
! 70: static void
! 71: args_copy_value(struct args_value *to, struct args_value *from)
! 72: {
! 73: to->type = from->type;
! 74: switch (from->type) {
! 75: case ARGS_NONE:
! 76: break;
! 77: case ARGS_COMMANDS:
! 78: to->cmdlist = from->cmdlist;
! 79: to->cmdlist->references++;
! 80: break;
! 81: case ARGS_STRING:
! 82: to->string = xstrdup(from->string);
! 83: break;
! 84: }
! 85: }
! 86:
1.42 nicm 87: /* Get value as string. */
1.43 ! nicm 88: static const char *
1.42 nicm 89: args_value_as_string(struct args_value *value)
90: {
91: switch (value->type) {
92: case ARGS_NONE:
1.43 ! nicm 93: return ("");
1.42 nicm 94: case ARGS_COMMANDS:
1.43 ! nicm 95: if (value->cached == NULL)
! 96: value->cached = cmd_list_print(value->cmdlist, 0);
! 97: return (value->cached);
1.42 nicm 98: case ARGS_STRING:
1.43 ! nicm 99: return (value->string);
1.42 nicm 100: }
101: }
102:
1.36 nicm 103: /* Create an empty arguments set. */
104: struct args *
105: args_create(void)
106: {
107: struct args *args;
108:
109: args = xcalloc(1, sizeof *args);
110: RB_INIT(&args->tree);
111: return (args);
112: }
113:
1.42 nicm 114: /* Parse arguments into a new argument set. */
1.1 nicm 115: struct args *
1.42 nicm 116: args_parse(const struct args_parse *parse, struct args_value *values,
117: u_int count)
1.1 nicm 118: {
1.42 nicm 119: struct args *args;
120: u_int i;
1.43 ! nicm 121: struct args_value *value, *new;
1.42 nicm 122: u_char flag, argument;
1.43 ! nicm 123: const char *found, *string, *s;
1.1 nicm 124:
1.42 nicm 125: if (count == 0)
126: return (args_create());
1.1 nicm 127:
1.36 nicm 128: args = args_create();
1.42 nicm 129: for (i = 1; i < count; /* nothing */) {
130: value = &values[i];
131: if (value->type != ARGS_STRING)
132: break;
133:
134: string = value->string;
135: if (*string++ != '-' || *string == '\0')
136: break;
137: i++;
138: if (string[0] == '-' && string[1] == '\0')
139: break;
140:
141: for (;;) {
142: flag = *string++;
143: if (flag == '\0')
144: break;
145: if (!isalnum(flag)) {
146: args_free(args);
147: return (NULL);
148: }
149: found = strchr(parse->template, flag);
150: if (found == NULL) {
151: args_free(args);
152: return (NULL);
153: }
154: argument = *++found;
155: if (argument != ':') {
156: log_debug("%s: add -%c", __func__, flag);
157: args_set(args, flag, NULL);
158: continue;
159: }
1.43 ! nicm 160: new = xcalloc(1, sizeof *value);
! 161: if (*string != '\0') {
! 162: new->type = ARGS_STRING;
! 163: new->string = xstrdup(string);
! 164: } else {
1.42 nicm 165: if (i == count) {
166: args_free(args);
167: return (NULL);
168: }
1.43 ! nicm 169: args_copy_value(new, &values[i++]);
1.42 nicm 170: }
1.43 ! nicm 171: s = args_value_as_string(new);
1.42 nicm 172: log_debug("%s: add -%c = %s", __func__, flag, s);
1.43 ! nicm 173: args_set(args, flag, new);
1.42 nicm 174: break;
175: }
176: }
177: log_debug("%s: flags end at %u of %u", __func__, i, count);
178: if (i != count) {
179: for (/* nothing */; i < count; i++) {
180: value = &values[i];
181:
182: s = args_value_as_string(value);
183: log_debug("%s: %u = %s", __func__, i, s);
1.43 ! nicm 184:
! 185: args->values = xrecallocarray(args->values,
! 186: args->count, args->count + 1, sizeof *args->values);
! 187: args_copy_value(&args->values[args->count++], value);
1.1 nicm 188: }
189: }
190:
1.43 ! nicm 191: if ((parse->lower != -1 && args->count < (u_int)parse->lower) ||
! 192: (parse->upper != -1 && args->count > (u_int)parse->upper)) {
1.38 nicm 193: args_free(args);
194: return (NULL);
195: }
1.1 nicm 196: return (args);
1.42 nicm 197: }
198:
199: /* Free a value. */
200: void
201: args_free_value(struct args_value *value)
202: {
203: switch (value->type) {
204: case ARGS_NONE:
205: break;
206: case ARGS_STRING:
207: free(value->string);
208: break;
209: case ARGS_COMMANDS:
210: cmd_list_free(value->cmdlist);
211: break;
212: }
1.43 ! nicm 213: free(value->cached);
1.1 nicm 214: }
215:
216: /* Free an arguments set. */
217: void
218: args_free(struct args *args)
219: {
1.5 nicm 220: struct args_entry *entry;
221: struct args_entry *entry1;
1.21 nicm 222: struct args_value *value;
223: struct args_value *value1;
1.43 ! nicm 224: u_int i;
1.1 nicm 225:
1.43 ! nicm 226: for (i = 0; i < args->count; i++)
! 227: args_free_value(&args->values[i]);
! 228: free(args->values);
1.1 nicm 229:
1.5 nicm 230: RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
231: RB_REMOVE(args_tree, &args->tree, entry);
1.21 nicm 232: TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
233: TAILQ_REMOVE(&entry->values, value, entry);
1.41 nicm 234: free(value->string);
1.21 nicm 235: free(value);
236: }
1.5 nicm 237: free(entry);
238: }
1.1 nicm 239:
1.4 nicm 240: free(args);
1.1 nicm 241: }
242:
1.38 nicm 243: /* Convert arguments to vector. */
244: void
245: args_vector(struct args *args, int *argc, char ***argv)
246: {
1.43 ! nicm 247: struct args_value *value;
! 248: u_int i;
! 249:
! 250: *argc = 0;
! 251: *argv = NULL;
! 252:
! 253: for (i = 0; i < args->count; i++) {
! 254: value = &args->values[i];
! 255: cmd_append_argv(argc, argv, args_value_as_string(value));
! 256: }
1.38 nicm 257: }
258:
1.12 nicm 259: /* Add to string. */
260: static void printflike(3, 4)
261: args_print_add(char **buf, size_t *len, const char *fmt, ...)
262: {
1.39 nicm 263: va_list ap;
1.12 nicm 264: char *s;
265: size_t slen;
266:
267: va_start(ap, fmt);
268: slen = xvasprintf(&s, fmt, ap);
269: va_end(ap);
270:
271: *len += slen;
272: *buf = xrealloc(*buf, *len);
273:
274: strlcat(*buf, s, *len);
275: free(s);
276: }
277:
1.43 ! nicm 278: /* Add value to string. */
1.21 nicm 279: static void
1.43 ! nicm 280: args_print_add_value(char **buf, size_t *len, struct args_value *value)
1.21 nicm 281: {
1.43 ! nicm 282: char *expanded = NULL;
1.21 nicm 283:
284: if (**buf != '\0')
285: args_print_add(buf, len, " ");
286:
1.43 ! nicm 287: switch (value->type) {
! 288: case ARGS_NONE:
! 289: break;
! 290: case ARGS_COMMANDS:
! 291: expanded = cmd_list_print(value->cmdlist, 0);
! 292: args_print_add(buf, len, "{ %s }", expanded);
! 293: break;
! 294: case ARGS_STRING:
! 295: expanded = args_escape(value->string);
! 296: args_print_add(buf, len, "%s", expanded);
! 297: break;
! 298: }
! 299: free(expanded);
1.21 nicm 300: }
301:
1.1 nicm 302: /* Print a set of arguments. */
1.12 nicm 303: char *
304: args_print(struct args *args)
1.1 nicm 305: {
1.39 nicm 306: size_t len;
1.21 nicm 307: char *buf;
1.43 ! nicm 308: u_int i, j;
1.5 nicm 309: struct args_entry *entry;
1.21 nicm 310: struct args_value *value;
1.1 nicm 311:
1.12 nicm 312: len = 1;
313: buf = xcalloc(1, len);
1.1 nicm 314:
315: /* Process the flags first. */
1.5 nicm 316: RB_FOREACH(entry, args_tree, &args->tree) {
1.21 nicm 317: if (!TAILQ_EMPTY(&entry->values))
1.1 nicm 318: continue;
319:
1.12 nicm 320: if (*buf == '\0')
321: args_print_add(&buf, &len, "-");
1.27 nicm 322: for (j = 0; j < entry->count; j++)
323: args_print_add(&buf, &len, "%c", entry->flag);
1.1 nicm 324: }
325:
326: /* Then the flags with arguments. */
1.5 nicm 327: RB_FOREACH(entry, args_tree, &args->tree) {
1.38 nicm 328: TAILQ_FOREACH(value, &entry->values, entry) {
329: if (*buf != '\0')
330: args_print_add(&buf, &len, " -%c", entry->flag);
331: else
332: args_print_add(&buf, &len, "-%c", entry->flag);
1.43 ! nicm 333: args_print_add_value(&buf, &len, value);
1.38 nicm 334: }
1.1 nicm 335: }
336:
337: /* And finally the argument vector. */
1.43 ! nicm 338: for (i = 0; i < args->count; i++)
! 339: args_print_add_value(&buf, &len, &args->values[i]);
1.1 nicm 340:
1.12 nicm 341: return (buf);
1.22 nicm 342: }
343:
344: /* Escape an argument. */
345: char *
346: args_escape(const char *s)
347: {
1.35 nicm 348: static const char dquoted[] = " #';${}";
349: static const char squoted[] = " \"";
1.22 nicm 350: char *escaped, *result;
1.35 nicm 351: int flags, quotes = 0;
1.22 nicm 352:
1.30 nicm 353: if (*s == '\0') {
354: xasprintf(&result, "''");
355: return (result);
356: }
1.35 nicm 357: if (s[strcspn(s, dquoted)] != '\0')
358: quotes = '"';
359: else if (s[strcspn(s, squoted)] != '\0')
360: quotes = '\'';
361:
1.26 nicm 362: if (s[0] != ' ' &&
1.35 nicm 363: s[1] == '\0' &&
364: (quotes != 0 || s[0] == '~')) {
1.22 nicm 365: xasprintf(&escaped, "\\%c", s[0]);
1.34 nicm 366: return (escaped);
367: }
368:
1.25 nicm 369: flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
1.35 nicm 370: if (quotes == '"')
1.22 nicm 371: flags |= VIS_DQ;
372: utf8_stravis(&escaped, s, flags);
373:
1.35 nicm 374: if (quotes == '\'')
375: xasprintf(&result, "'%s'", escaped);
376: else if (quotes == '"') {
1.22 nicm 377: if (*escaped == '~')
378: xasprintf(&result, "\"\\%s\"", escaped);
379: else
380: xasprintf(&result, "\"%s\"", escaped);
381: } else {
382: if (*escaped == '~')
383: xasprintf(&result, "\\%s", escaped);
384: else
385: result = xstrdup(escaped);
386: }
387: free(escaped);
388: return (result);
1.1 nicm 389: }
390:
391: /* Return if an argument is present. */
392: int
1.32 nicm 393: args_has(struct args *args, u_char flag)
1.1 nicm 394: {
1.27 nicm 395: struct args_entry *entry;
396:
1.32 nicm 397: entry = args_find(args, flag);
1.27 nicm 398: if (entry == NULL)
399: return (0);
400: return (entry->count);
1.1 nicm 401: }
402:
1.5 nicm 403: /* Set argument value in the arguments tree. */
1.19 nicm 404: void
1.43 ! nicm 405: args_set(struct args *args, u_char flag, struct args_value *value)
1.1 nicm 406: {
1.5 nicm 407: struct args_entry *entry;
408:
1.32 nicm 409: entry = args_find(args, flag);
1.21 nicm 410: if (entry == NULL) {
1.7 nicm 411: entry = xcalloc(1, sizeof *entry);
1.32 nicm 412: entry->flag = flag;
1.27 nicm 413: entry->count = 1;
1.21 nicm 414: TAILQ_INIT(&entry->values);
1.7 nicm 415: RB_INSERT(args_tree, &args->tree, entry);
1.27 nicm 416: } else
417: entry->count++;
1.43 ! nicm 418: if (value != NULL && value->type != ARGS_NONE)
1.21 nicm 419: TAILQ_INSERT_TAIL(&entry->values, value, entry);
1.1 nicm 420: }
421:
422: /* Get argument value. Will be NULL if it isn't present. */
423: const char *
1.32 nicm 424: args_get(struct args *args, u_char flag)
1.1 nicm 425: {
1.5 nicm 426: struct args_entry *entry;
427:
1.32 nicm 428: if ((entry = args_find(args, flag)) == NULL)
429: return (NULL);
430: if (TAILQ_EMPTY(&entry->values))
1.5 nicm 431: return (NULL);
1.41 nicm 432: return (TAILQ_LAST(&entry->values, args_values)->string);
1.21 nicm 433: }
434:
1.32 nicm 435: /* Get first argument. */
436: u_char
437: args_first(struct args *args, struct args_entry **entry)
438: {
439: *entry = RB_MIN(args_tree, &args->tree);
440: if (*entry == NULL)
441: return (0);
442: return ((*entry)->flag);
443: }
444:
445: /* Get next argument. */
446: u_char
447: args_next(struct args_entry **entry)
448: {
449: *entry = RB_NEXT(args_tree, &args->tree, *entry);
450: if (*entry == NULL)
451: return (0);
452: return ((*entry)->flag);
1.38 nicm 453: }
454:
455: /* Get argument count. */
456: u_int
457: args_count(struct args *args)
458: {
1.43 ! nicm 459: return (args->count);
! 460: }
! 461:
! 462: /* Get argument value. */
! 463: struct args_value *
! 464: args_value(struct args *args, u_int idx)
! 465: {
! 466: if (idx >= args->count)
! 467: return (NULL);
! 468: return (&args->values[idx]);
1.38 nicm 469: }
470:
471: /* Return argument as string. */
472: const char *
473: args_string(struct args *args, u_int idx)
474: {
1.43 ! nicm 475: if (idx >= (u_int)args->count)
1.38 nicm 476: return (NULL);
1.43 ! nicm 477: return (args_value_as_string(&args->values[idx]));
1.32 nicm 478: }
479:
1.21 nicm 480: /* Get first value in argument. */
1.37 nicm 481: struct args_value *
482: args_first_value(struct args *args, u_char flag)
1.21 nicm 483: {
484: struct args_entry *entry;
485:
1.32 nicm 486: if ((entry = args_find(args, flag)) == NULL)
1.21 nicm 487: return (NULL);
1.37 nicm 488: return (TAILQ_FIRST(&entry->values));
1.21 nicm 489: }
490:
491: /* Get next value in argument. */
1.37 nicm 492: struct args_value *
493: args_next_value(struct args_value *value)
1.21 nicm 494: {
1.37 nicm 495: return (TAILQ_NEXT(value, entry));
1.1 nicm 496: }
497:
498: /* Convert an argument value to a number. */
499: long long
1.32 nicm 500: args_strtonum(struct args *args, u_char flag, long long minval,
501: long long maxval, char **cause)
1.1 nicm 502: {
1.5 nicm 503: const char *errstr;
1.39 nicm 504: long long ll;
1.5 nicm 505: struct args_entry *entry;
1.21 nicm 506: struct args_value *value;
1.1 nicm 507:
1.32 nicm 508: if ((entry = args_find(args, flag)) == NULL) {
1.1 nicm 509: *cause = xstrdup("missing");
510: return (0);
511: }
1.21 nicm 512: value = TAILQ_LAST(&entry->values, args_values);
1.1 nicm 513:
1.41 nicm 514: ll = strtonum(value->string, minval, maxval, &errstr);
1.1 nicm 515: if (errstr != NULL) {
516: *cause = xstrdup(errstr);
517: return (0);
1.29 nicm 518: }
519:
520: *cause = NULL;
521: return (ll);
522: }
523:
524: /* Convert an argument to a number which may be a percentage. */
525: long long
1.32 nicm 526: args_percentage(struct args *args, u_char flag, long long minval,
1.29 nicm 527: long long maxval, long long curval, char **cause)
528: {
1.31 nicm 529: const char *value;
1.29 nicm 530: struct args_entry *entry;
531:
1.32 nicm 532: if ((entry = args_find(args, flag)) == NULL) {
1.29 nicm 533: *cause = xstrdup("missing");
534: return (0);
535: }
1.41 nicm 536: value = TAILQ_LAST(&entry->values, args_values)->string;
1.31 nicm 537: return (args_string_percentage(value, minval, maxval, curval, cause));
538: }
539:
540: /* Convert a string to a number which may be a percentage. */
541: long long
542: args_string_percentage(const char *value, long long minval, long long maxval,
543: long long curval, char **cause)
544: {
545: const char *errstr;
1.39 nicm 546: long long ll;
1.31 nicm 547: size_t valuelen = strlen(value);
548: char *copy;
1.29 nicm 549:
1.31 nicm 550: if (value[valuelen - 1] == '%') {
551: copy = xstrdup(value);
1.29 nicm 552: copy[valuelen - 1] = '\0';
553:
554: ll = strtonum(copy, 0, 100, &errstr);
555: free(copy);
556: if (errstr != NULL) {
557: *cause = xstrdup(errstr);
558: return (0);
559: }
560: ll = (curval * ll) / 100;
561: if (ll < minval) {
1.33 nicm 562: *cause = xstrdup("too small");
1.29 nicm 563: return (0);
564: }
565: if (ll > maxval) {
1.33 nicm 566: *cause = xstrdup("too large");
1.29 nicm 567: return (0);
568: }
569: } else {
1.31 nicm 570: ll = strtonum(value, minval, maxval, &errstr);
1.29 nicm 571: if (errstr != NULL) {
572: *cause = xstrdup(errstr);
573: return (0);
574: }
1.1 nicm 575: }
576:
577: *cause = NULL;
578: return (ll);
579: }