xref: /openbsd-src/usr.bin/tmux/arguments.c (revision 9b9d2a55a62c8e82206c25f94fcc7f4e2765250e)
1 /* $OpenBSD: arguments.c,v 1.11 2015/08/29 23:19:52 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2010 Nicholas Marriott <nicm@users.sourceforge.net>
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 <getopt.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.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 struct args_entry	*args_find(struct args *, u_char);
39 
40 RB_GENERATE(args_tree, args_entry, entry, args_cmp);
41 
42 /* Arguments tree comparison function. */
43 int
44 args_cmp(struct args_entry *a1, struct args_entry *a2)
45 {
46 	return (a1->flag - a2->flag);
47 }
48 
49 /* Create an arguments set with no flags. */
50 struct args *
51 args_create(int argc, ...)
52 {
53 	struct args	*args;
54 	va_list		 ap;
55 	int		 i;
56 
57 	args = xcalloc(1, sizeof *args);
58 
59 	args->argc = argc;
60 	if (argc == 0)
61 		args->argv = NULL;
62 	else
63 		args->argv = xcalloc(argc, sizeof *args->argv);
64 
65 	va_start(ap, argc);
66 	for (i = 0; i < argc; i++)
67 		args->argv[i] = xstrdup(va_arg(ap, char *));
68 	va_end(ap);
69 
70 	return (args);
71 }
72 
73 /* Find a flag in the arguments tree. */
74 struct args_entry *
75 args_find(struct args *args, u_char ch)
76 {
77 	struct args_entry	entry;
78 
79 	entry.flag = ch;
80 	return (RB_FIND(args_tree, &args->tree, &entry));
81 }
82 
83 /* Parse an argv and argc into a new argument set. */
84 struct args *
85 args_parse(const char *template, int argc, char **argv)
86 {
87 	struct args	*args;
88 	int		 opt;
89 
90 	args = xcalloc(1, sizeof *args);
91 
92 	optreset = 1;
93 	optind = 1;
94 
95 	while ((opt = getopt(argc, argv, template)) != -1) {
96 		if (opt < 0)
97 			continue;
98 		if (opt == '?' || strchr(template, opt) == NULL) {
99 			args_free(args);
100 			return (NULL);
101 		}
102 		args_set(args, opt, optarg);
103 	}
104 	argc -= optind;
105 	argv += optind;
106 
107 	args->argc = argc;
108 	args->argv = cmd_copy_argv(argc, argv);
109 
110 	return (args);
111 }
112 
113 /* Free an arguments set. */
114 void
115 args_free(struct args *args)
116 {
117 	struct args_entry	*entry;
118 	struct args_entry	*entry1;
119 
120 	cmd_free_argv(args->argc, args->argv);
121 
122 	RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
123 		RB_REMOVE(args_tree, &args->tree, entry);
124 		free(entry->value);
125 		free(entry);
126 	}
127 
128 	free(args);
129 }
130 
131 /* Print a set of arguments. */
132 size_t
133 args_print(struct args *args, char *buf, size_t len)
134 {
135 	size_t		 	 off, used;
136 	int			 i;
137 	const char		*quotes;
138 	struct args_entry	*entry;
139 
140 	/* There must be at least one byte at the start. */
141 	if (len == 0)
142 		return (0);
143 	off = 0;
144 
145 	/* Process the flags first. */
146 	buf[off++] = '-';
147 	RB_FOREACH(entry, args_tree, &args->tree) {
148 		if (entry->value != NULL)
149 			continue;
150 
151 		if (off == len - 1) {
152 			buf[off] = '\0';
153 			return (len);
154 		}
155 		buf[off++] = entry->flag;
156 		buf[off] = '\0';
157 	}
158 	if (off == 1)
159 		buf[--off] = '\0';
160 
161 	/* Then the flags with arguments. */
162 	RB_FOREACH(entry, args_tree, &args->tree) {
163 		if (entry->value == NULL)
164 			continue;
165 
166 		if (off >= len) {
167 			/* snprintf will have zero terminated. */
168 			return (len);
169 		}
170 
171 		if (strchr(entry->value, ' ') != NULL)
172 			quotes = "\"";
173 		else
174 			quotes = "";
175 		used = xsnprintf(buf + off, len - off, "%s-%c %s%s%s",
176 		    off != 0 ? " " : "", entry->flag, quotes, entry->value,
177 		    quotes);
178 		if (used > len - off)
179 			used = len - off;
180 		off += used;
181 	}
182 
183 	/* And finally the argument vector. */
184 	for (i = 0; i < args->argc; i++) {
185 		if (off >= len) {
186 			/* snprintf will have zero terminated. */
187 			return (len);
188 		}
189 
190 		if (strchr(args->argv[i], ' ') != NULL)
191 			quotes = "\"";
192 		else
193 			quotes = "";
194 		used = xsnprintf(buf + off, len - off, "%s%s%s%s",
195 		    off != 0 ? " " : "", quotes, args->argv[i], quotes);
196 		if (used > len - off)
197 			used = len - off;
198 		off += used;
199 	}
200 
201 	return (off);
202 }
203 
204 /* Return if an argument is present. */
205 int
206 args_has(struct args *args, u_char ch)
207 {
208 	return (args_find(args, ch) == NULL ? 0 : 1);
209 }
210 
211 /* Set argument value in the arguments tree. */
212 void
213 args_set(struct args *args, u_char ch, const char *value)
214 {
215 	struct args_entry	*entry;
216 
217 	/* Replace existing argument. */
218 	if ((entry = args_find(args, ch)) != NULL) {
219 		free(entry->value);
220 		entry->value = NULL;
221 	} else {
222 		entry = xcalloc(1, sizeof *entry);
223 		entry->flag = ch;
224 		RB_INSERT(args_tree, &args->tree, entry);
225 	}
226 
227 	if (value != NULL)
228 		entry->value = xstrdup(value);
229 }
230 
231 /* Get argument value. Will be NULL if it isn't present. */
232 const char *
233 args_get(struct args *args, u_char ch)
234 {
235 	struct args_entry	*entry;
236 
237 	if ((entry = args_find(args, ch)) == NULL)
238 		return (NULL);
239 	return (entry->value);
240 }
241 
242 /* Convert an argument value to a number. */
243 long long
244 args_strtonum(struct args *args, u_char ch, long long minval, long long maxval,
245     char **cause)
246 {
247 	const char		*errstr;
248 	long long 	 	 ll;
249 	struct args_entry	*entry;
250 
251 	if ((entry = args_find(args, ch)) == NULL) {
252 		*cause = xstrdup("missing");
253 		return (0);
254 	}
255 
256 	ll = strtonum(entry->value, minval, maxval, &errstr);
257 	if (errstr != NULL) {
258 		*cause = xstrdup(errstr);
259 		return (0);
260 	}
261 
262 	*cause = NULL;
263 	return (ll);
264 }
265