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