OpenVPN
test_argv.c
Go to the documentation of this file.
1#include "config.h"
2#include "syshead.h"
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <stdarg.h>
7#include <string.h>
8#include <setjmp.h>
9#include <cmocka.h>
10#include <assert.h>
11#include <stdbool.h>
12
13#include "argv.h"
14#include "buffer.h"
15#include "test_common.h"
16
17#ifdef _WIN32
18#include "win32-util.h"
19#endif
20
21/* Defines for use in the tests and the mock parse_line() */
22#define PATH1 "/s p a c e"
23#define PATH2 "/foo bar/baz"
24#define PARAM1 "param1"
25#define PARAM2 "param two"
26#define SCRIPT_CMD "\"" PATH1 PATH2 "\"" PARAM1 "\"" PARAM2 "\""
27
28int
29parse_line(const char *line, char **p, const int n, const char *file, const int line_num,
30 msglvl_t msglevel, struct gc_arena *gc)
31{
32 p[0] = PATH1 PATH2;
33 p[1] = PARAM1;
34 p[2] = PARAM2;
35 return 3;
36}
37
38static void
40{
41 struct argv a = argv_new();
42
43 argv_printf(&a, " %s %s %d ", PATH1, PATH2, 42);
44 assert_int_equal(a.argc, 3);
45
46 argv_free(&a);
47}
48
49static void
51{
52 struct argv a = argv_new();
53
54 argv_printf(&a, "%s ", PATH1);
55 argv_printf_cat(&a, " %s %s", PATH2, PARAM1);
56 assert_int_equal(a.argc, 3);
57
58 argv_free(&a);
59}
60
61static void
63{
64 struct argv a = argv_new();
65
66 argv_printf(&a, "<p1:%s>", PATH1);
67 assert_int_equal(a.argc, 1);
68 assert_string_equal(a.argv[0], "<p1:" PATH1 ">");
69
70 argv_free(&a);
71}
72
73static void
75{
76 struct argv a = argv_new();
77
78 assert_false(argv_printf(&a, "tool --do %s", "this\035--harmful"));
79 assert_int_equal(a.argc, 0);
80
81 argv_free(&a);
82}
83
84static void
86{
87 struct argv a = argv_new();
88
89 argv_printf(&a, "%s%s", PATH1, PATH2);
90 assert_int_equal(a.argc, 1);
91
92 argv_printf(&a, "%s%s %d", PATH1, PATH2, 42);
93 assert_int_equal(a.argc, 2);
94
95 argv_printf(&a, "foo %s%s %s x y", PATH2, PATH1, "foo");
96 assert_int_equal(a.argc, 5);
97
98 argv_free(&a);
99}
100
101static void
103{
104 struct argv a = argv_new();
105
106 argv_printf(&a, "%s", "");
107 assert_int_equal(a.argc, 1);
108
109 argv_printf(&a, "%s %s", PATH1, "");
110 assert_int_equal(a.argc, 2);
111
112 argv_printf(&a, "%s %s %s", PATH1, "", PARAM1);
113 assert_int_equal(a.argc, 3);
114
115 argv_printf(&a, "%s %s %s %s", PATH1, "", "", PARAM1);
116 assert_int_equal(a.argc, 4);
117
118 argv_printf(&a, "%s %s", "", PARAM1);
119 assert_int_equal(a.argc, 2);
120
121 argv_free(&a);
122}
123
124static void
126{
127 struct argv a = argv_new();
128 const char *args[] = {
129 "good_tools_have_good_names_even_though_it_might_impair_typing",
130 "--long-opt=looooooooooooooooooooooooooooooooooooooooooooooooong",
131 "--long-cat=loooooooooooooooooooooooooooooooooooooooooooooooooooonger",
132 "file_with_very_descriptive_filename_that_leaves_no_questions_open.jpg.exe"
133 };
134
135 argv_printf(&a, "%s %s %s %s", args[0], args[1], args[2], args[3]);
136 assert_int_equal(a.argc, 4);
137 for (size_t i = 0; i < a.argc; i++)
138 {
139 assert_string_equal(a.argv[i], args[i]);
140 }
141
142 argv_free(&a);
143}
144
145static void
147{
148 struct argv a = argv_new();
149
151 assert_int_equal(a.argc, 3);
152
153 argv_free(&a);
154}
155
156static void
158{
159 struct argv a = argv_new();
160
162 argv_printf_cat(&a, "bar baz %d %s", 42, PATH1);
163 assert_int_equal(a.argc, 7);
164
165 argv_free(&a);
166}
167
168static void
170{
171 struct argv a = argv_new();
172
173 argv_printf(&a, "%s %s %s", PATH1, PATH2, PARAM1);
174 argv_printf_cat(&a, "%s", PARAM2);
175 argv_printf_cat(&a, "foo");
176 assert_int_equal(a.argc, 5);
177
178 argv_free(&a);
179}
180
181static void
183{
184 struct argv a = argv_new();
185 struct gc_arena gc = gc_new();
186 const char *output;
187
188 output = argv_str(&a, &gc, PA_BRACKET);
189 assert_string_equal(output, "");
190
191 argv_free(&a);
192 gc_free(&gc);
193}
194
195static void
197{
198 struct argv a = argv_new();
199 struct gc_arena gc = gc_new();
200 const char *output;
201
202 argv_printf(&a, "%s%s", PATH1, PATH2);
203 argv_printf_cat(&a, "%s", PARAM1);
204 argv_printf_cat(&a, "%s", PARAM2);
205 argv_printf_cat(&a, "%d", -1);
206 argv_printf_cat(&a, "%u", -1);
207 argv_printf_cat(&a, "%lu", 1L);
208 output = argv_str(&a, &gc, PA_BRACKET);
209 assert_string_equal(output, "[" PATH1 PATH2 "] [" PARAM1 "] [" PARAM2 "]"
210 " [-1] [4294967295] [1]");
211
212 argv_free(&a);
213 gc_free(&gc);
214}
215
216static void
218{
219 struct argv a = argv_new();
220 struct argv b;
221
222 b = argv_insert_head(&a, PATH1);
223 assert_int_equal(b.argc, 1);
224 assert_string_equal(b.argv[0], PATH1);
225 argv_free(&b);
226
227 argv_free(&a);
228}
229
230static void
232{
233 struct argv a = argv_new();
234 struct argv b;
235
236 argv_printf(&a, "%s", PATH2);
237 b = argv_insert_head(&a, PATH1);
238 assert_int_equal(b.argc, a.argc + 1);
239 for (size_t i = 0; i < b.argc; i++)
240 {
241 if (i == 0)
242 {
243 assert_string_equal(b.argv[i], PATH1);
244 }
245 else
246 {
247 assert_string_equal(b.argv[i], a.argv[i - 1]);
248 }
249 }
250 argv_free(&b);
251
252 argv_free(&a);
253}
254
255#ifdef _WIN32
256/*
257 * An argument is quoted if and only if it holds a space or a character that
258 * cmd.exe would act on when CreateProcess() runs a .bat/.cmd target.
259 */
260static void
262{
263 static const struct
264 {
265 const char *arg;
266 const WCHAR *expected;
267 } cases[] = {
268 /* nothing special - must stay unquoted, or existing scripts break */
269 { "CN=user1", L"script.bat 0 CN=user1" },
270 /* the batch delimiters are deliberately not triggers */
271 { "CN=a,b;c=d", L"script.bat 0 CN=a,b;c=d" },
272 /* a space has always forced quoting */
273 { "O=Ctrl, CN=y", L"script.bat 0 \"O=Ctrl, CN=y\"" },
274 /* cmd.exe operators */
275 { "CN=x&ver", L"script.bat 0 \"CN=x&ver\"" },
276 { "CN=x|ver", L"script.bat 0 \"CN=x|ver\"" },
277 { "CN=x>f", L"script.bat 0 \"CN=x>f\"" },
278 { "CN=x<f", L"script.bat 0 \"CN=x<f\"" },
279 { "CN=x^f", L"script.bat 0 \"CN=x^f\"" },
280 { "CN=x%f", L"script.bat 0 \"CN=x%f\"" },
281 { "CN=x(f)", L"script.bat 0 \"CN=x(f)\"" },
282 { "CN=x!f", L"script.bat 0 \"CN=x!f\"" },
283 /* a double quote is replaced, so quoting cannot be broken out of */
284 { "CN=a\"b", L"script.bat 0 CN=a_b" },
285 };
286
287 for (size_t i = 0; i < SIZE(cases); i++)
288 {
289 struct gc_arena gc = gc_new();
290 struct argv a = argv_new();
291
292 argv_printf(&a, "%s %d %s", "script.bat", 0, cases[i].arg);
293 assert_int_equal(a.argc, 3);
294
295 WCHAR *cmd_line = wide_cmd_line(&a, &gc);
296 assert_non_null(cmd_line);
297 assert_int_equal(wcscmp(cmd_line, cases[i].expected), 0);
298
299 argv_free(&a);
300 gc_free(&gc);
301 }
302}
303#endif /* _WIN32 */
304
305int
306main(void)
307{
309 const struct CMUnitTest tests[] = {
316 cmocka_unit_test(argv_printf__long_args__data_correct),
320 cmocka_unit_test(argv_str__empty_argv__empty_output),
324#ifdef _WIN32
326#endif
327 };
328
329 return cmocka_run_group_tests_name("argv", tests, NULL, NULL);
330}
const char * argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags)
Generate a single string with all the arguments in a struct argv concatenated.
Definition argv.c:230
void argv_parse_cmd(struct argv *argres, const char *cmdstr)
Parses a command string, tokenizes it and puts each element into a separate struct argv argument slot...
Definition argv.c:481
void argv_free(struct argv *a)
Frees all memory allocations allocated by the struct argv related functions.
Definition argv.c:101
bool argv_printf(struct argv *argres, const char *format,...)
printf() variant which populates a struct argv.
Definition argv.c:438
bool argv_printf_cat(struct argv *argres, const char *format,...)
printf() inspired argv concatenation.
Definition argv.c:462
struct argv argv_new(void)
Allocates a new struct argv and ensures it is initialised.
Definition argv.c:87
struct argv argv_insert_head(const struct argv *a, const char *head)
Inserts an argument string in front of all other argument slots.
Definition argv.c:207
Buffer management functions and garbage collection.
#define PA_BRACKET
Flag for print_argv(): wrap each argument in square brackets.
Definition buffer.h:221
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1912
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1896
#define SIZE(x)
Definition basic.h:29
unsigned int msglvl_t
Definition error.h:77
Definition argv.h:35
char ** argv
Definition argv.h:39
size_t argc
Definition argv.h:38
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
static void argv_parse_cmd__command_and_extra_options__argc_correct(void **state)
Definition test_argv.c:157
int parse_line(const char *line, char **p, const int n, const char *file, const int line_num, msglvl_t msglevel, struct gc_arena *gc)
Definition test_argv.c:29
static void argv_printf_cat__used_twice__argc_correct(void **state)
Definition test_argv.c:169
static void argv_str__multiple_argv__correct_output(void **state)
Definition test_argv.c:196
#define PATH1
Definition test_argv.c:22
static void argv_printf__group_sep_in_arg__fail_no_ouput(void **state)
Definition test_argv.c:74
static void argv_printf_cat__multiple_spaces_in_format__parsed_as_one(void **state)
Definition test_argv.c:50
static void argv_str__empty_argv__empty_output(void **state)
Definition test_argv.c:182
static void argv_insert_head__empty_argv__head_only(void **state)
Definition test_argv.c:217
static void argv_printf__embedded_format_directive__replaced_in_output(void **state)
Definition test_argv.c:62
static void argv_printf__empty_parameter__argc_correct(void **state)
Definition test_argv.c:102
#define SCRIPT_CMD
Definition test_argv.c:26
#define PARAM1
Definition test_argv.c:24
static void argv_printf__multiple_spaces_in_format__parsed_as_one(void **state)
Definition test_argv.c:39
int main(void)
Definition test_argv.c:306
#define PATH2
Definition test_argv.c:23
static void argv_parse_cmd__command_string__argc_correct(void **state)
Definition test_argv.c:146
static void argv_printf__combined_path_with_spaces__argc_correct(void **state)
Definition test_argv.c:85
#define PARAM2
Definition test_argv.c:25
static void wide_cmd_line__quotes_only_what_cmd_would_reinterpret(void **state)
Definition test_argv.c:261
static void argv_printf__long_args__data_correct(void **state)
Definition test_argv.c:125
static void argv_insert_head__non_empty_argv__head_added(void **state)
Definition test_argv.c:231
static void openvpn_unit_test_setup(void)
Sets up the environment for unit tests like making both stderr and stdout non-buffered to avoid messa...
Definition test_common.h:61
struct gc_arena gc
Definition test_ssl.c:122
WCHAR * wide_cmd_line(const struct argv *a, struct gc_arena *gc)
Definition win32-util.c:65