Annotation of src/usr.bin/tmux/arguments.c, Revision 1.26
1.26 ! nicm 1: /* $OpenBSD: arguments.c,v 1.25 2019/05/29 20:05:14 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.11 nicm 41: RB_ENTRY(args_entry) entry;
42: };
1.5 nicm 43:
1.14 nicm 44: static struct args_entry *args_find(struct args *, u_char);
1.5 nicm 45:
1.14 nicm 46: static int args_cmp(struct args_entry *, struct args_entry *);
47: RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
1.5 nicm 48:
49: /* Arguments tree comparison function. */
1.14 nicm 50: static int
1.5 nicm 51: args_cmp(struct args_entry *a1, struct args_entry *a2)
52: {
53: return (a1->flag - a2->flag);
54: }
55:
56: /* Find a flag in the arguments tree. */
1.14 nicm 57: static struct args_entry *
1.5 nicm 58: args_find(struct args *args, u_char ch)
59: {
60: struct args_entry entry;
61:
62: entry.flag = ch;
63: return (RB_FIND(args_tree, &args->tree, &entry));
64: }
65:
1.1 nicm 66: /* Parse an argv and argc into a new argument set. */
67: struct args *
68: args_parse(const char *template, int argc, char **argv)
69: {
70: struct args *args;
71: int opt;
72:
73: args = xcalloc(1, sizeof *args);
74:
75: optreset = 1;
76: optind = 1;
77:
78: while ((opt = getopt(argc, argv, template)) != -1) {
1.5 nicm 79: if (opt < 0)
1.1 nicm 80: continue;
1.8 nicm 81: if (opt == '?' || strchr(template, opt) == NULL) {
1.5 nicm 82: args_free(args);
1.1 nicm 83: return (NULL);
84: }
1.5 nicm 85: args_set(args, opt, optarg);
1.1 nicm 86: }
87: argc -= optind;
88: argv += optind;
89:
90: args->argc = argc;
91: args->argv = cmd_copy_argv(argc, argv);
92:
93: return (args);
94: }
95:
96: /* Free an arguments set. */
97: void
98: args_free(struct args *args)
99: {
1.5 nicm 100: struct args_entry *entry;
101: struct args_entry *entry1;
1.21 nicm 102: struct args_value *value;
103: struct args_value *value1;
1.1 nicm 104:
105: cmd_free_argv(args->argc, args->argv);
106:
1.5 nicm 107: RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
108: RB_REMOVE(args_tree, &args->tree, entry);
1.21 nicm 109: TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
110: TAILQ_REMOVE(&entry->values, value, entry);
111: free(value->value);
112: free(value);
113: }
1.5 nicm 114: free(entry);
115: }
1.1 nicm 116:
1.4 nicm 117: free(args);
1.1 nicm 118: }
119:
1.12 nicm 120: /* Add to string. */
121: static void printflike(3, 4)
122: args_print_add(char **buf, size_t *len, const char *fmt, ...)
123: {
124: va_list ap;
125: char *s;
126: size_t slen;
127:
128: va_start(ap, fmt);
129: slen = xvasprintf(&s, fmt, ap);
130: va_end(ap);
131:
132: *len += slen;
133: *buf = xrealloc(*buf, *len);
134:
135: strlcat(*buf, s, *len);
136: free(s);
137: }
138:
1.21 nicm 139: /* Add value to string. */
140: static void
141: args_print_add_value(char **buf, size_t *len, struct args_entry *entry,
142: struct args_value *value)
143: {
1.22 nicm 144: char *escaped;
1.21 nicm 145:
146: if (**buf != '\0')
147: args_print_add(buf, len, " -%c ", entry->flag);
148: else
149: args_print_add(buf, len, "-%c ", entry->flag);
150:
1.22 nicm 151: escaped = args_escape(value->value);
152: args_print_add(buf, len, "%s", escaped);
1.21 nicm 153: free(escaped);
154: }
155:
156: /* Add argument to string. */
157: static void
158: args_print_add_argument(char **buf, size_t *len, const char *argument)
159: {
1.22 nicm 160: char *escaped;
1.21 nicm 161:
162: if (**buf != '\0')
163: args_print_add(buf, len, " ");
164:
1.22 nicm 165: escaped = args_escape(argument);
166: args_print_add(buf, len, "%s", escaped);
1.21 nicm 167: free(escaped);
168: }
169:
1.1 nicm 170: /* Print a set of arguments. */
1.12 nicm 171: char *
172: args_print(struct args *args)
1.1 nicm 173: {
1.12 nicm 174: size_t len;
1.21 nicm 175: char *buf;
176: int i;
1.5 nicm 177: struct args_entry *entry;
1.21 nicm 178: struct args_value *value;
1.1 nicm 179:
1.12 nicm 180: len = 1;
181: buf = xcalloc(1, len);
1.1 nicm 182:
183: /* Process the flags first. */
1.5 nicm 184: RB_FOREACH(entry, args_tree, &args->tree) {
1.21 nicm 185: if (!TAILQ_EMPTY(&entry->values))
1.1 nicm 186: continue;
187:
1.12 nicm 188: if (*buf == '\0')
189: args_print_add(&buf, &len, "-");
190: args_print_add(&buf, &len, "%c", entry->flag);
1.1 nicm 191: }
192:
193: /* Then the flags with arguments. */
1.5 nicm 194: RB_FOREACH(entry, args_tree, &args->tree) {
1.21 nicm 195: TAILQ_FOREACH(value, &entry->values, entry)
196: args_print_add_value(&buf, &len, entry, value);
1.1 nicm 197: }
198:
199: /* And finally the argument vector. */
1.21 nicm 200: for (i = 0; i < args->argc; i++)
201: args_print_add_argument(&buf, &len, args->argv[i]);
1.1 nicm 202:
1.12 nicm 203: return (buf);
1.22 nicm 204: }
205:
206: /* Escape an argument. */
207: char *
208: args_escape(const char *s)
209: {
1.23 nicm 210: static const char quoted[] = " #\"';${}";
1.22 nicm 211: char *escaped, *result;
212: int flags;
213:
1.24 nicm 214: if (*s == '\0')
215: return (xstrdup(s));
1.26 ! nicm 216: if (s[0] != ' ' &&
! 217: (strchr(quoted, s[0]) != NULL || s[0] == '~') &&
! 218: s[1] == '\0') {
1.22 nicm 219: xasprintf(&escaped, "\\%c", s[0]);
220: return (escaped);
221: }
222:
1.25 nicm 223: flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
1.22 nicm 224: if (s[strcspn(s, quoted)] != '\0')
225: flags |= VIS_DQ;
226: utf8_stravis(&escaped, s, flags);
227:
228: if (flags & VIS_DQ) {
229: if (*escaped == '~')
230: xasprintf(&result, "\"\\%s\"", escaped);
231: else
232: xasprintf(&result, "\"%s\"", escaped);
233: } else {
234: if (*escaped == '~')
235: xasprintf(&result, "\\%s", escaped);
236: else
237: result = xstrdup(escaped);
238: }
239: free(escaped);
240: return (result);
1.1 nicm 241: }
242:
243: /* Return if an argument is present. */
244: int
245: args_has(struct args *args, u_char ch)
246: {
1.20 nicm 247: return (args_find(args, ch) != NULL);
1.1 nicm 248: }
249:
1.5 nicm 250: /* Set argument value in the arguments tree. */
1.19 nicm 251: void
1.21 nicm 252: args_set(struct args *args, u_char ch, const char *s)
1.1 nicm 253: {
1.5 nicm 254: struct args_entry *entry;
1.21 nicm 255: struct args_value *value;
1.5 nicm 256:
1.21 nicm 257: entry = args_find(args, ch);
258: if (entry == NULL) {
1.7 nicm 259: entry = xcalloc(1, sizeof *entry);
260: entry->flag = ch;
1.21 nicm 261: TAILQ_INIT(&entry->values);
1.7 nicm 262: RB_INSERT(args_tree, &args->tree, entry);
1.5 nicm 263: }
264:
1.21 nicm 265: if (s != NULL) {
266: value = xcalloc(1, sizeof *value);
267: value->value = xstrdup(s);
268: TAILQ_INSERT_TAIL(&entry->values, value, entry);
269: }
1.1 nicm 270: }
271:
272: /* Get argument value. Will be NULL if it isn't present. */
273: const char *
274: args_get(struct args *args, u_char ch)
275: {
1.5 nicm 276: struct args_entry *entry;
277:
278: if ((entry = args_find(args, ch)) == NULL)
279: return (NULL);
1.21 nicm 280: return (TAILQ_LAST(&entry->values, args_values)->value);
281: }
282:
283: /* Get first value in argument. */
284: const char *
285: args_first_value(struct args *args, u_char ch, struct args_value **value)
286: {
287: struct args_entry *entry;
288:
289: if ((entry = args_find(args, ch)) == NULL)
290: return (NULL);
291:
292: *value = TAILQ_FIRST(&entry->values);
293: if (*value == NULL)
294: return (NULL);
295: return ((*value)->value);
296: }
297:
298: /* Get next value in argument. */
299: const char *
300: args_next_value(struct args_value **value)
301: {
302: if (*value == NULL)
303: return (NULL);
304: *value = TAILQ_NEXT(*value, entry);
305: if (*value == NULL)
306: return (NULL);
307: return ((*value)->value);
1.1 nicm 308: }
309:
310: /* Convert an argument value to a number. */
311: long long
1.5 nicm 312: args_strtonum(struct args *args, u_char ch, long long minval, long long maxval,
313: char **cause)
1.1 nicm 314: {
1.5 nicm 315: const char *errstr;
316: long long ll;
317: struct args_entry *entry;
1.21 nicm 318: struct args_value *value;
1.1 nicm 319:
1.5 nicm 320: if ((entry = args_find(args, ch)) == NULL) {
1.1 nicm 321: *cause = xstrdup("missing");
322: return (0);
323: }
1.21 nicm 324: value = TAILQ_LAST(&entry->values, args_values);
1.1 nicm 325:
1.21 nicm 326: ll = strtonum(value->value, minval, maxval, &errstr);
1.1 nicm 327: if (errstr != NULL) {
328: *cause = xstrdup(errstr);
329: return (0);
330: }
331:
332: *cause = NULL;
333: return (ll);
334: }