xref: /openbsd-src/usr.bin/tmux/arguments.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /* $OpenBSD: arguments.c,v 1.20 2017/08/23 09:14:21 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_entry {
33 	u_char			 flag;
34 	char			*value;
35 	RB_ENTRY(args_entry)	 entry;
36 };
37 
38 static struct args_entry	*args_find(struct args *, u_char);
39 
40 static int	args_cmp(struct args_entry *, struct args_entry *);
41 RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
42 
43 /* Arguments tree comparison function. */
44 static int
45 args_cmp(struct args_entry *a1, struct args_entry *a2)
46 {
47 	return (a1->flag - a2->flag);
48 }
49 
50 /* Find a flag in the arguments tree. */
51 static struct args_entry *
52 args_find(struct args *args, u_char ch)
53 {
54 	struct args_entry	entry;
55 
56 	entry.flag = ch;
57 	return (RB_FIND(args_tree, &args->tree, &entry));
58 }
59 
60 /* Parse an argv and argc into a new argument set. */
61 struct args *
62 args_parse(const char *template, int argc, char **argv)
63 {
64 	struct args	*args;
65 	int		 opt;
66 
67 	args = xcalloc(1, sizeof *args);
68 
69 	optreset = 1;
70 	optind = 1;
71 
72 	while ((opt = getopt(argc, argv, template)) != -1) {
73 		if (opt < 0)
74 			continue;
75 		if (opt == '?' || strchr(template, opt) == NULL) {
76 			args_free(args);
77 			return (NULL);
78 		}
79 		args_set(args, opt, optarg);
80 	}
81 	argc -= optind;
82 	argv += optind;
83 
84 	args->argc = argc;
85 	args->argv = cmd_copy_argv(argc, argv);
86 
87 	return (args);
88 }
89 
90 /* Free an arguments set. */
91 void
92 args_free(struct args *args)
93 {
94 	struct args_entry	*entry;
95 	struct args_entry	*entry1;
96 
97 	cmd_free_argv(args->argc, args->argv);
98 
99 	RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
100 		RB_REMOVE(args_tree, &args->tree, entry);
101 		free(entry->value);
102 		free(entry);
103 	}
104 
105 	free(args);
106 }
107 
108 /* Add to string. */
109 static void printflike(3, 4)
110 args_print_add(char **buf, size_t *len, const char *fmt, ...)
111 {
112 	va_list  ap;
113 	char	*s;
114 	size_t	 slen;
115 
116 	va_start(ap, fmt);
117 	slen = xvasprintf(&s, fmt, ap);
118 	va_end(ap);
119 
120 	*len += slen;
121 	*buf = xrealloc(*buf, *len);
122 
123 	strlcat(*buf, s, *len);
124 	free(s);
125 }
126 
127 /* Print a set of arguments. */
128 char *
129 args_print(struct args *args)
130 {
131 	size_t		 	 len;
132 	char			*buf, *escaped;
133 	int			 i, flags;
134 	struct args_entry	*entry;
135 	static const char	 quoted[] = " #\"';$";
136 
137 	len = 1;
138 	buf = xcalloc(1, len);
139 
140 	/* Process the flags first. */
141 	RB_FOREACH(entry, args_tree, &args->tree) {
142 		if (entry->value != NULL)
143 			continue;
144 
145 		if (*buf == '\0')
146 			args_print_add(&buf, &len, "-");
147 		args_print_add(&buf, &len, "%c", entry->flag);
148 	}
149 
150 	/* Then the flags with arguments. */
151 	RB_FOREACH(entry, args_tree, &args->tree) {
152 		if (entry->value == NULL)
153 			continue;
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 
160 		flags = VIS_OCTAL|VIS_TAB|VIS_NL;
161 		if (entry->value[strcspn(entry->value, quoted)] != '\0')
162 			flags |= VIS_DQ;
163 		utf8_stravis(&escaped, entry->value, flags);
164 		if (flags & VIS_DQ)
165 			args_print_add(&buf, &len, "\"%s\"", escaped);
166 		else
167 			args_print_add(&buf, &len, "%s", escaped);
168 		free(escaped);
169 	}
170 
171 	/* And finally the argument vector. */
172 	for (i = 0; i < args->argc; i++) {
173 		if (*buf != '\0')
174 			args_print_add(&buf, &len, " ");
175 
176 		flags = VIS_OCTAL|VIS_TAB|VIS_NL;
177 		if (args->argv[i][strcspn(args->argv[i], quoted)] != '\0')
178 			flags |= VIS_DQ;
179 		utf8_stravis(&escaped, args->argv[i], flags);
180 		if (flags & VIS_DQ)
181 			args_print_add(&buf, &len, "\"%s\"", escaped);
182 		else
183 			args_print_add(&buf, &len, "%s", escaped);
184 		free(escaped);
185 	}
186 
187 	return (buf);
188 }
189 
190 /* Return if an argument is present. */
191 int
192 args_has(struct args *args, u_char ch)
193 {
194 	return (args_find(args, ch) != NULL);
195 }
196 
197 /* Set argument value in the arguments tree. */
198 void
199 args_set(struct args *args, u_char ch, const char *value)
200 {
201 	struct args_entry	*entry;
202 
203 	/* Replace existing argument. */
204 	if ((entry = args_find(args, ch)) != NULL) {
205 		free(entry->value);
206 		entry->value = NULL;
207 	} else {
208 		entry = xcalloc(1, sizeof *entry);
209 		entry->flag = ch;
210 		RB_INSERT(args_tree, &args->tree, entry);
211 	}
212 
213 	if (value != NULL)
214 		entry->value = xstrdup(value);
215 }
216 
217 /* Get argument value. Will be NULL if it isn't present. */
218 const char *
219 args_get(struct args *args, u_char ch)
220 {
221 	struct args_entry	*entry;
222 
223 	if ((entry = args_find(args, ch)) == NULL)
224 		return (NULL);
225 	return (entry->value);
226 }
227 
228 /* Convert an argument value to a number. */
229 long long
230 args_strtonum(struct args *args, u_char ch, long long minval, long long maxval,
231     char **cause)
232 {
233 	const char		*errstr;
234 	long long 	 	 ll;
235 	struct args_entry	*entry;
236 
237 	if ((entry = args_find(args, ch)) == NULL) {
238 		*cause = xstrdup("missing");
239 		return (0);
240 	}
241 
242 	ll = strtonum(entry->value, minval, maxval, &errstr);
243 	if (errstr != NULL) {
244 		*cause = xstrdup(errstr);
245 		return (0);
246 	}
247 
248 	*cause = NULL;
249 	return (ll);
250 }
251