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
31struct args_value {
32 char *value;
33 TAILQ_ENTRY(args_value) entry;
34};
35TAILQ_HEAD(args_values, args_value);
36
37struct args_entry {
38 u_char flag;
39 struct args_values values;
40 u_int count;
41 RB_ENTRY(args_entry) entry;
42};
43
44static struct args_entry *args_find(struct args *, u_char);
45
46static int args_cmp(struct args_entry *, struct args_entry *);
47RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
48
49/* Arguments tree comparison function. */
50static int
51args_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. */
57static struct args_entry *
58args_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. */
67struct args *
68args_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. */
99void
100args_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. */
123static void printflike(3, 4)
124args_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. */
142static void
143args_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. */
159static void
160args_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. */
173char *
174args_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. */
211char *
212args_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. */
248int
249args_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. */
260void
261args_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. */
284const char *
285args_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. */
295const char *
296args_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. */
310const char *
311args_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. */
322long long
323args_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