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