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