xref: /openbsd-src/usr.bin/tmux/arguments.c (revision fcde59b201a29a2b4570b00b71e7aa25d61cb5c1)
1 /* $OpenBSD: arguments.c,v 1.35 2020/06/12 07:10:43 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
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>
23 #include <unistd.h>
24 #include <vis.h>
25 
26 #include "tmux.h"
27 
28 /*
29  * Manipulate command arguments.
30  */
31 
32 struct args_value {
33 	char			*value;
34 	TAILQ_ENTRY(args_value)	 entry;
35 };
36 TAILQ_HEAD(args_values, args_value);
37 
38 struct args_entry {
39 	u_char			 flag;
40 	struct args_values	 values;
41 	u_int			 count;
42 	RB_ENTRY(args_entry)	 entry;
43 };
44 
45 static struct args_entry	*args_find(struct args *, u_char);
46 
47 static int	args_cmp(struct args_entry *, struct args_entry *);
48 RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
49 
50 /* Arguments tree comparison function. */
51 static int
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. */
58 static struct args_entry *
59 args_find(struct args *args, u_char flag)
60 {
61 	struct args_entry	entry;
62 
63 	entry.flag = flag;
64 	return (RB_FIND(args_tree, &args->tree, &entry));
65 }
66 
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;
78 	optarg = NULL;
79 
80 	while ((opt = getopt(argc, argv, template)) != -1) {
81 		if (opt < 0)
82 			continue;
83 		if (opt == '?' || strchr(template, opt) == NULL) {
84 			args_free(args);
85 			return (NULL);
86 		}
87 		args_set(args, opt, optarg);
88 		optarg = NULL;
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 {
103 	struct args_entry	*entry;
104 	struct args_entry	*entry1;
105 	struct args_value	*value;
106 	struct args_value	*value1;
107 
108 	cmd_free_argv(args->argc, args->argv);
109 
110 	RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
111 		RB_REMOVE(args_tree, &args->tree, entry);
112 		TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
113 			TAILQ_REMOVE(&entry->values, value, entry);
114 			free(value->value);
115 			free(value);
116 		}
117 		free(entry);
118 	}
119 
120 	free(args);
121 }
122 
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 
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 {
147 	char	*escaped;
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 
154 	escaped = args_escape(value->value);
155 	args_print_add(buf, len, "%s", escaped);
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 {
163 	char	*escaped;
164 
165 	if (**buf != '\0')
166 		args_print_add(buf, len, " ");
167 
168 	escaped = args_escape(argument);
169 	args_print_add(buf, len, "%s", escaped);
170 	free(escaped);
171 }
172 
173 /* Print a set of arguments. */
174 char *
175 args_print(struct args *args)
176 {
177 	size_t		 	 len;
178 	char			*buf;
179 	int			 i;
180 	u_int			 j;
181 	struct args_entry	*entry;
182 	struct args_value	*value;
183 
184 	len = 1;
185 	buf = xcalloc(1, len);
186 
187 	/* Process the flags first. */
188 	RB_FOREACH(entry, args_tree, &args->tree) {
189 		if (!TAILQ_EMPTY(&entry->values))
190 			continue;
191 
192 		if (*buf == '\0')
193 			args_print_add(&buf, &len, "-");
194 		for (j = 0; j < entry->count; j++)
195 			args_print_add(&buf, &len, "%c", entry->flag);
196 	}
197 
198 	/* Then the flags with arguments. */
199 	RB_FOREACH(entry, args_tree, &args->tree) {
200 		TAILQ_FOREACH(value, &entry->values, entry)
201 			args_print_add_value(&buf, &len, entry, value);
202 	}
203 
204 	/* And finally the argument vector. */
205 	for (i = 0; i < args->argc; i++)
206 		args_print_add_argument(&buf, &len, args->argv[i]);
207 
208 	return (buf);
209 }
210 
211 /* Escape an argument. */
212 char *
213 args_escape(const char *s)
214 {
215 	static const char	 dquoted[] = " #';${}";
216 	static const char	 squoted[] = " \"";
217 	char			*escaped, *result;
218 	int			 flags, quotes = 0;
219 
220 	if (*s == '\0') {
221 		xasprintf(&result, "''");
222 		return (result);
223 	}
224 	if (s[strcspn(s, dquoted)] != '\0')
225 		quotes = '"';
226 	else if (s[strcspn(s, squoted)] != '\0')
227 		quotes = '\'';
228 
229 	if (s[0] != ' ' &&
230 	    s[1] == '\0' &&
231 	    (quotes != 0 || s[0] == '~')) {
232 		xasprintf(&escaped, "\\%c", s[0]);
233 		return (escaped);
234 	}
235 
236 	flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
237 	if (quotes == '"')
238 		flags |= VIS_DQ;
239 	utf8_stravis(&escaped, s, flags);
240 
241 	if (quotes == '\'')
242 		xasprintf(&result, "'%s'", escaped);
243 	else if (quotes == '"') {
244 		if (*escaped == '~')
245 			xasprintf(&result, "\"\\%s\"", escaped);
246 		else
247 			xasprintf(&result, "\"%s\"", escaped);
248 	} else {
249 		if (*escaped == '~')
250 			xasprintf(&result, "\\%s", escaped);
251 		else
252 			result = xstrdup(escaped);
253 	}
254 	free(escaped);
255 	return (result);
256 }
257 
258 /* Return if an argument is present. */
259 int
260 args_has(struct args *args, u_char flag)
261 {
262 	struct args_entry	*entry;
263 
264 	entry = args_find(args, flag);
265 	if (entry == NULL)
266 		return (0);
267 	return (entry->count);
268 }
269 
270 /* Set argument value in the arguments tree. */
271 void
272 args_set(struct args *args, u_char flag, const char *s)
273 {
274 	struct args_entry	*entry;
275 	struct args_value	*value;
276 
277 	entry = args_find(args, flag);
278 	if (entry == NULL) {
279 		entry = xcalloc(1, sizeof *entry);
280 		entry->flag = flag;
281 		entry->count = 1;
282 		TAILQ_INIT(&entry->values);
283 		RB_INSERT(args_tree, &args->tree, entry);
284 	} else
285 		entry->count++;
286 
287 	if (s != NULL) {
288 		value = xcalloc(1, sizeof *value);
289 		value->value = xstrdup(s);
290 		TAILQ_INSERT_TAIL(&entry->values, value, entry);
291 	}
292 }
293 
294 /* Get argument value. Will be NULL if it isn't present. */
295 const char *
296 args_get(struct args *args, u_char flag)
297 {
298 	struct args_entry	*entry;
299 
300 	if ((entry = args_find(args, flag)) == NULL)
301 		return (NULL);
302 	if (TAILQ_EMPTY(&entry->values))
303 		return (NULL);
304 	return (TAILQ_LAST(&entry->values, args_values)->value);
305 }
306 
307 /* Get first argument. */
308 u_char
309 args_first(struct args *args, struct args_entry **entry)
310 {
311 	*entry = RB_MIN(args_tree, &args->tree);
312 	if (*entry == NULL)
313 		return (0);
314 	return ((*entry)->flag);
315 }
316 
317 /* Get next argument. */
318 u_char
319 args_next(struct args_entry **entry)
320 {
321 	*entry = RB_NEXT(args_tree, &args->tree, *entry);
322 	if (*entry == NULL)
323 		return (0);
324 	return ((*entry)->flag);
325 }
326 
327 /* Get first value in argument. */
328 const char *
329 args_first_value(struct args *args, u_char flag, struct args_value **value)
330 {
331 	struct args_entry	*entry;
332 
333 	if ((entry = args_find(args, flag)) == NULL)
334 		return (NULL);
335 
336 	*value = TAILQ_FIRST(&entry->values);
337 	if (*value == NULL)
338 		return (NULL);
339 	return ((*value)->value);
340 }
341 
342 /* Get next value in argument. */
343 const char *
344 args_next_value(struct args_value **value)
345 {
346 	if (*value == NULL)
347 		return (NULL);
348 	*value = TAILQ_NEXT(*value, entry);
349 	if (*value == NULL)
350 		return (NULL);
351 	return ((*value)->value);
352 }
353 
354 /* Convert an argument value to a number. */
355 long long
356 args_strtonum(struct args *args, u_char flag, long long minval,
357     long long maxval, char **cause)
358 {
359 	const char		*errstr;
360 	long long 	 	 ll;
361 	struct args_entry	*entry;
362 	struct args_value	*value;
363 
364 	if ((entry = args_find(args, flag)) == NULL) {
365 		*cause = xstrdup("missing");
366 		return (0);
367 	}
368 	value = TAILQ_LAST(&entry->values, args_values);
369 
370 	ll = strtonum(value->value, minval, maxval, &errstr);
371 	if (errstr != NULL) {
372 		*cause = xstrdup(errstr);
373 		return (0);
374 	}
375 
376 	*cause = NULL;
377 	return (ll);
378 }
379 
380 /* Convert an argument to a number which may be a percentage. */
381 long long
382 args_percentage(struct args *args, u_char flag, long long minval,
383     long long maxval, long long curval, char **cause)
384 {
385 	const char		*value;
386 	struct args_entry	*entry;
387 
388 	if ((entry = args_find(args, flag)) == NULL) {
389 		*cause = xstrdup("missing");
390 		return (0);
391 	}
392 	value = TAILQ_LAST(&entry->values, args_values)->value;
393 	return (args_string_percentage(value, minval, maxval, curval, cause));
394 }
395 
396 /* Convert a string to a number which may be a percentage. */
397 long long
398 args_string_percentage(const char *value, long long minval, long long maxval,
399     long long curval, char **cause)
400 {
401 	const char	*errstr;
402 	long long 	 ll;
403 	size_t		 valuelen = strlen(value);
404 	char		*copy;
405 
406 	if (value[valuelen - 1] == '%') {
407 		copy = xstrdup(value);
408 		copy[valuelen - 1] = '\0';
409 
410 		ll = strtonum(copy, 0, 100, &errstr);
411 		free(copy);
412 		if (errstr != NULL) {
413 			*cause = xstrdup(errstr);
414 			return (0);
415 		}
416 		ll = (curval * ll) / 100;
417 		if (ll < minval) {
418 			*cause = xstrdup("too small");
419 			return (0);
420 		}
421 		if (ll > maxval) {
422 			*cause = xstrdup("too large");
423 			return (0);
424 		}
425 	} else {
426 		ll = strtonum(value, minval, maxval, &errstr);
427 		if (errstr != NULL) {
428 			*cause = xstrdup(errstr);
429 			return (0);
430 		}
431 	}
432 
433 	*cause = NULL;
434 	return (ll);
435 }
436