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