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