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