1 /*	$OpenBSD: optionstest.c,v 1.9 2017/04/16 14:40:47 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include <openssl/bio.h>
23 #include <openssl/conf.h>
24 
25 #include "apps.h"
26 
27 /* Needed to keep apps.c happy... */
28 BIO *bio_err;
29 CONF *config;
30 
31 static int argfunc(char *arg);
32 static int defaultarg(int argc, char **argv, int *argsused);
33 static int multiarg(int argc, char **argv, int *argsused);
34 
35 static struct {
36 	char *arg;
37 	int flag;
38 } test_config;
39 
40 static struct option test_options[] = {
41 	{
42 		.name = "arg",
43 		.argname = "argname",
44 		.type = OPTION_ARG,
45 		.opt.arg = &test_config.arg,
46 	},
47 	{
48 		.name = "argfunc",
49 		.argname = "argname",
50 		.type = OPTION_ARG_FUNC,
51 		.opt.argfunc = argfunc,
52 	},
53 	{
54 		.name = "flag",
55 		.type = OPTION_FLAG,
56 		.opt.flag = &test_config.flag,
57 	},
58 	{
59 		.name = "multiarg",
60 		.type = OPTION_ARGV_FUNC,
61 		.opt.argvfunc = multiarg,
62 	},
63 	{
64 		.name = NULL,
65 		.type = OPTION_ARGV_FUNC,
66 		.opt.argvfunc = defaultarg,
67 	},
68 	{ NULL },
69 };
70 
71 char *args1[] = { "opts" };
72 char *args2[] = { "opts", "-arg", "arg", "-flag" };
73 char *args3[] = { "opts", "-arg", "arg", "-flag", "unnamed" };
74 char *args4[] = { "opts", "-arg", "arg", "unnamed", "-flag" };
75 char *args5[] = { "opts", "unnamed1", "-arg", "arg", "-flag", "unnamed2" };
76 char *args6[] = { "opts", "-argfunc", "arg", "-flag" };
77 char *args7[] = { "opts", "-arg", "arg", "-flag", "-", "-unnamed" };
78 char *args8[] = { "opts", "-arg", "arg", "-flag", "file1", "file2", "file3" };
79 char *args9[] = { "opts", "-arg", "arg", "-flag", "file1", "-file2", "file3" };
80 char *args10[] = { "opts", "-arg", "arg", "-flag", "-", "file1", "file2" };
81 char *args11[] = { "opts", "-arg", "arg", "-flag", "-", "-file1", "-file2" };
82 char *args12[] = { "opts", "-multiarg", "arg1", "arg2", "-flag", "unnamed" };
83 char *args13[] = { "opts", "-multiargz", "arg1", "arg2", "-flagz", "unnamed" };
84 
85 struct options_test {
86 	int argc;
87 	char **argv;
88 	enum {
89 		OPTIONS_TEST_NONE,
90 		OPTIONS_TEST_UNNAMED,
91 		OPTIONS_TEST_ARGSUSED,
92 	} type;
93 	char *unnamed;
94 	int used;
95 	int want;
96 	char *wantarg;
97 	int wantflag;
98 };
99 
100 struct options_test options_tests[] = {
101 	{
102 		/* Test 1 - No arguments (only program name). */
103 		.argc = 1,
104 		.argv = args1,
105 		.type = OPTIONS_TEST_NONE,
106 		.want = 0,
107 		.wantarg = NULL,
108 		.wantflag = 0,
109 	},
110 	{
111 		/* Test 2 - Named arguments (unnamed not permitted). */
112 		.argc = 4,
113 		.argv = args2,
114 		.type = OPTIONS_TEST_NONE,
115 		.want = 0,
116 		.wantarg = "arg",
117 		.wantflag = 1,
118 	},
119 	{
120 		/* Test 3 - Named arguments (unnamed permitted). */
121 		.argc = 4,
122 		.argv = args2,
123 		.type = OPTIONS_TEST_UNNAMED,
124 		.unnamed = NULL,
125 		.want = 0,
126 		.wantarg = "arg",
127 		.wantflag = 1,
128 	},
129 	{
130 		/* Test 4 - Named and single unnamed (unnamed not permitted). */
131 		.argc = 5,
132 		.argv = args3,
133 		.type = OPTIONS_TEST_NONE,
134 		.want = 1,
135 	},
136 	{
137 		/* Test 5 - Named and single unnamed (unnamed permitted). */
138 		.argc = 5,
139 		.argv = args3,
140 		.type = OPTIONS_TEST_UNNAMED,
141 		.unnamed = "unnamed",
142 		.want = 0,
143 		.wantarg = "arg",
144 		.wantflag = 1,
145 	},
146 	{
147 		/* Test 6 - Named and single unnamed (different sequence). */
148 		.argc = 5,
149 		.argv = args4,
150 		.type = OPTIONS_TEST_UNNAMED,
151 		.unnamed = "unnamed",
152 		.want = 0,
153 		.wantarg = "arg",
154 		.wantflag = 1,
155 	},
156 	{
157 		/* Test 7 - Multiple unnamed arguments (should fail). */
158 		.argc = 6,
159 		.argv = args5,
160 		.type = OPTIONS_TEST_UNNAMED,
161 		.want = 1,
162 	},
163 	{
164 		/* Test 8 - Function. */
165 		.argc = 4,
166 		.argv = args6,
167 		.type = OPTIONS_TEST_NONE,
168 		.want = 0,
169 		.wantarg = "arg",
170 		.wantflag = 1,
171 	},
172 	{
173 		/* Test 9 - Named and single unnamed (hyphen separated). */
174 		.argc = 6,
175 		.argv = args7,
176 		.type = OPTIONS_TEST_UNNAMED,
177 		.unnamed = "-unnamed",
178 		.want = 0,
179 		.wantarg = "arg",
180 		.wantflag = 1,
181 	},
182 	{
183 		/* Test 10 - Named and multiple unnamed. */
184 		.argc = 7,
185 		.argv = args8,
186 		.used = 4,
187 		.type = OPTIONS_TEST_ARGSUSED,
188 		.want = 0,
189 		.wantarg = "arg",
190 		.wantflag = 1,
191 	},
192 	{
193 		/* Test 11 - Named and multiple unnamed. */
194 		.argc = 7,
195 		.argv = args9,
196 		.used = 4,
197 		.type = OPTIONS_TEST_ARGSUSED,
198 		.want = 0,
199 		.wantarg = "arg",
200 		.wantflag = 1,
201 	},
202 	{
203 		/* Test 12 - Named and multiple unnamed. */
204 		.argc = 7,
205 		.argv = args10,
206 		.used = 5,
207 		.type = OPTIONS_TEST_ARGSUSED,
208 		.want = 0,
209 		.wantarg = "arg",
210 		.wantflag = 1,
211 	},
212 	{
213 		/* Test 13 - Named and multiple unnamed. */
214 		.argc = 7,
215 		.argv = args11,
216 		.used = 5,
217 		.type = OPTIONS_TEST_ARGSUSED,
218 		.want = 0,
219 		.wantarg = "arg",
220 		.wantflag = 1,
221 	},
222 	{
223 		/* Test 14 - Named only. */
224 		.argc = 4,
225 		.argv = args2,
226 		.used = 4,
227 		.type = OPTIONS_TEST_ARGSUSED,
228 		.want = 0,
229 		.wantarg = "arg",
230 		.wantflag = 1,
231 	},
232 	{
233 		/* Test 15 - Multiple argument callback. */
234 		.argc = 6,
235 		.argv = args12,
236 		.unnamed = "unnamed",
237 		.type = OPTIONS_TEST_UNNAMED,
238 		.want = 0,
239 		.wantarg = NULL,
240 		.wantflag = 1,
241 	},
242 	{
243 		/* Test 16 - Multiple argument callback. */
244 		.argc = 6,
245 		.argv = args12,
246 		.used = 5,
247 		.type = OPTIONS_TEST_ARGSUSED,
248 		.want = 0,
249 		.wantarg = NULL,
250 		.wantflag = 1,
251 	},
252 	{
253 		/* Test 17 - Default callback. */
254 		.argc = 6,
255 		.argv = args13,
256 		.unnamed = "unnamed",
257 		.type = OPTIONS_TEST_UNNAMED,
258 		.want = 0,
259 		.wantarg = NULL,
260 		.wantflag = 1,
261 	},
262 	{
263 		/* Test 18 - Default callback. */
264 		.argc = 6,
265 		.argv = args13,
266 		.used = 5,
267 		.type = OPTIONS_TEST_ARGSUSED,
268 		.want = 0,
269 		.wantarg = NULL,
270 		.wantflag = 1,
271 	},
272 };
273 
274 #define N_OPTIONS_TESTS \
275     (sizeof(options_tests) / sizeof(*options_tests))
276 
277 static int
argfunc(char * arg)278 argfunc(char *arg)
279 {
280 	test_config.arg = arg;
281 	return (0);
282 }
283 
284 static int
defaultarg(int argc,char ** argv,int * argsused)285 defaultarg(int argc, char **argv, int *argsused)
286 {
287 	if (argc < 1)
288 		return (1);
289 
290 	if (strcmp(argv[0], "-multiargz") == 0) {
291 		if (argc < 3)
292 			return (1);
293 		*argsused = 3;
294 		return (0);
295 	} else if (strcmp(argv[0], "-flagz") == 0) {
296 		test_config.flag = 1;
297 		*argsused = 1;
298 		return (0);
299 	}
300 
301 	return (1);
302 }
303 
304 static int
multiarg(int argc,char ** argv,int * argsused)305 multiarg(int argc, char **argv, int *argsused)
306 {
307 	if (argc < 3)
308 		return (1);
309 
310 	*argsused = 3;
311 	return (0);
312 }
313 
314 static int
do_options_test(int test_no,struct options_test * ot)315 do_options_test(int test_no, struct options_test *ot)
316 {
317 	int *argsused = NULL;
318 	char *unnamed = NULL;
319 	char **arg = NULL;
320 	int used = 0;
321 	int ret;
322 
323 	if (ot->type == OPTIONS_TEST_UNNAMED)
324 		arg = &unnamed;
325 	else if (ot->type == OPTIONS_TEST_ARGSUSED)
326 		argsused = &used;
327 
328 	memset(&test_config, 0, sizeof(test_config));
329 	ret = options_parse(ot->argc, ot->argv, test_options, arg, argsused);
330 	if (ret != ot->want) {
331 		fprintf(stderr, "FAIL: test %i options_parse() returned %i, "
332 		    "want %i\n", test_no, ret, ot->want);
333 		return (1);
334 	}
335 	if (ret != 0)
336 		return (0);
337 
338 	if ((test_config.arg != NULL || ot->wantarg != NULL) &&
339 	    (test_config.arg == NULL || ot->wantarg == NULL ||
340 	     strcmp(test_config.arg, ot->wantarg) != 0)) {
341 		fprintf(stderr, "FAIL: test %i got arg '%s', want '%s'\n",
342 		    test_no, test_config.arg, ot->wantarg);
343 		return (1);
344 	}
345 	if (test_config.flag != ot->wantflag) {
346 		fprintf(stderr, "FAIL: test %i got flag %i, want %i\n",
347 		    test_no, test_config.flag, ot->wantflag);
348 		return (1);
349 	}
350 	if (ot->type == OPTIONS_TEST_UNNAMED &&
351 	    (unnamed != NULL || ot->unnamed != NULL) &&
352 	    (unnamed == NULL || ot->unnamed == NULL ||
353 	     strcmp(unnamed, ot->unnamed) != 0)) {
354 		fprintf(stderr, "FAIL: test %i got unnamed '%s', want '%s'\n",
355 		    test_no, unnamed, ot->unnamed);
356 		return (1);
357 	}
358 	if (ot->type == OPTIONS_TEST_ARGSUSED && used != ot->used) {
359 		fprintf(stderr, "FAIL: test %i got used %i, want %i\n",
360 		    test_no, used, ot->used);
361 		return (1);
362 	}
363 
364 	return (0);
365 }
366 
367 int
main(int argc,char ** argv)368 main(int argc, char **argv)
369 {
370 	int failed = 0;
371 	size_t i;
372 
373 	for (i = 0; i < N_OPTIONS_TESTS; i++) {
374 		printf("Test %zu%s\n", (i + 1), options_tests[i].want == 0 ?
375 		    "" : " is expected to complain");
376 		failed += do_options_test(i + 1, &options_tests[i]);
377 	}
378 
379 	return (failed);
380 }
381