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