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