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