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