test-expandargv.c revision 1.11 1 1.1 christos /* expandargv test program,
2 1.10 christos Copyright (C) 2006-2024 Free Software Foundation, Inc.
3 1.1 christos Written by Carlos O'Donell <carlos (at) codesourcery.com>
4 1.1 christos
5 1.1 christos This file is part of the libiberty library, which is part of GCC.
6 1.1 christos
7 1.1 christos This file is free software; you can redistribute it and/or modify
8 1.1 christos it under the terms of the GNU General Public License as published by
9 1.1 christos the Free Software Foundation; either version 2 of the License, or
10 1.1 christos (at your option) any later version.
11 1.1 christos
12 1.1 christos In addition to the permissions in the GNU General Public License, the
13 1.1 christos Free Software Foundation gives you unlimited permission to link the
14 1.1 christos compiled version of this file into combinations with other programs,
15 1.1 christos and to distribute those combinations without any restriction coming
16 1.1 christos from the use of this file. (The General Public License restrictions
17 1.1 christos do apply in other respects; for example, they cover modification of
18 1.1 christos the file, and distribution when not linked into a combined
19 1.1 christos executable.)
20 1.1 christos
21 1.1 christos This program is distributed in the hope that it will be useful,
22 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
23 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 1.1 christos GNU General Public License for more details.
25 1.1 christos
26 1.1 christos You should have received a copy of the GNU General Public License
27 1.1 christos along with this program; if not, write to the Free Software
28 1.1 christos Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
29 1.1 christos */
30 1.1 christos
31 1.1 christos #ifdef HAVE_CONFIG_H
32 1.1 christos #include "config.h"
33 1.1 christos #endif
34 1.1 christos #include "libiberty.h"
35 1.1 christos #include <stdio.h>
36 1.1 christos #include <errno.h>
37 1.1 christos #ifdef HAVE_STDLIB_H
38 1.1 christos #include <stdlib.h>
39 1.1 christos #endif
40 1.1 christos #ifdef HAVE_STRING_H
41 1.1 christos #include <string.h>
42 1.1 christos #endif
43 1.1 christos #ifdef HAVE_UNISTD_H
44 1.1 christos #include <unistd.h>
45 1.1 christos #endif
46 1.1 christos
47 1.1 christos #ifndef EXIT_SUCCESS
48 1.1 christos #define EXIT_SUCCESS 0
49 1.1 christos #endif
50 1.1 christos
51 1.1 christos #ifndef EXIT_FAILURE
52 1.1 christos #define EXIT_FAILURE 1
53 1.1 christos #endif
54 1.1 christos
55 1.1 christos static void fatal_error (int, const char *, int) ATTRIBUTE_NORETURN;
56 1.1 christos void writeout_test (int, const char *);
57 1.1 christos void run_replaces (char *);
58 1.1 christos void hook_char_replace (char *, size_t, char, char);
59 1.1 christos int run_tests (const char **);
60 1.1 christos void erase_test (int);
61 1.1 christos
62 1.1 christos /* Test input data, argv before, and argv after:
63 1.1 christos
64 1.1 christos The \n is an important part of test_data since expandargv
65 1.1 christos may have to work in environments where \n is translated
66 1.1 christos as \r\n. Thus \n is included in the test data for the file.
67 1.1 christos
68 1.1 christos We use \b to indicate that the test data is the null character.
69 1.1 christos This is because we use \0 normally to represent the end of the
70 1.1 christos file data, so we need something else for this. */
71 1.1 christos
72 1.1 christos #define FILENAME_PATTERN "test-expandargv-%d.lst"
73 1.1 christos #define ARGV0 "test-expandargv"
74 1.1 christos
75 1.1 christos const char *test_data[] = {
76 1.1 christos /* Test 0 - Check for expansion with \r\n */
77 1.1 christos "a\r\nb", /* Test 0 data */
78 1.1 christos ARGV0,
79 1.1 christos "@test-expandargv-0.lst",
80 1.1 christos 0, /* End of argv[] before expansion */
81 1.1 christos ARGV0,
82 1.1 christos "a",
83 1.1 christos "b",
84 1.1 christos 0, /* End of argv[] after expansion */
85 1.1 christos
86 1.1 christos /* Test 1 - Check for expansion with \n */
87 1.1 christos "a\nb", /* Test 1 data */
88 1.1 christos ARGV0,
89 1.1 christos "@test-expandargv-1.lst",
90 1.1 christos 0,
91 1.1 christos ARGV0,
92 1.1 christos "a",
93 1.1 christos "b",
94 1.1 christos 0,
95 1.1 christos
96 1.1 christos /* Test 2 - Check for expansion with \0 */
97 1.1 christos "a\bb", /* Test 2 data */
98 1.1 christos ARGV0,
99 1.1 christos "@test-expandargv-2.lst",
100 1.1 christos 0,
101 1.1 christos ARGV0,
102 1.1 christos "a",
103 1.1 christos 0,
104 1.1 christos
105 1.1 christos /* Test 3 - Check for expansion with only \0 */
106 1.1 christos "\b", /* Test 3 data */
107 1.1 christos ARGV0,
108 1.1 christos "@test-expandargv-3.lst",
109 1.1 christos 0,
110 1.1 christos ARGV0,
111 1.1 christos 0,
112 1.1 christos
113 1.1 christos /* Test 4 - Check for options beginning with an empty line. */
114 1.1 christos "\na\nb", /* Test 4 data */
115 1.1 christos ARGV0,
116 1.1 christos "@test-expandargv-4.lst",
117 1.1 christos 0,
118 1.1 christos ARGV0,
119 1.1 christos "a",
120 1.1 christos "b",
121 1.1 christos 0,
122 1.1 christos
123 1.1 christos /* Test 5 - Check for options containing an empty argument. */
124 1.1 christos "a\n''\nb", /* Test 5 data */
125 1.1 christos ARGV0,
126 1.1 christos "@test-expandargv-5.lst",
127 1.1 christos 0,
128 1.1 christos ARGV0,
129 1.1 christos "a",
130 1.1 christos "",
131 1.1 christos "b",
132 1.1 christos 0,
133 1.1 christos
134 1.1 christos /* Test 6 - Check for options containing a quoted newline. */
135 1.1 christos "a\n'a\n\nb'\nb", /* Test 6 data */
136 1.1 christos ARGV0,
137 1.1 christos "@test-expandargv-6.lst",
138 1.1 christos 0,
139 1.1 christos ARGV0,
140 1.1 christos "a",
141 1.1 christos "a\n\nb",
142 1.1 christos "b",
143 1.1 christos 0,
144 1.1 christos
145 1.11 christos /* Test 7 - No backslash removal within single quotes. */
146 1.11 christos "'a\\$VAR' '\\\"'", /* Test 7 data */
147 1.11 christos ARGV0,
148 1.11 christos "@test-expandargv-7.lst",
149 1.11 christos 0,
150 1.11 christos ARGV0,
151 1.11 christos "a\\$VAR",
152 1.11 christos "\\\"",
153 1.11 christos 0,
154 1.11 christos
155 1.11 christos /* Test 8 - Remove backslash / newline pairs. */
156 1.11 christos "\"ab\\\ncd\" ef\\\ngh", /* Test 8 data */
157 1.11 christos ARGV0,
158 1.11 christos "@test-expandargv-8.lst",
159 1.11 christos 0,
160 1.11 christos ARGV0,
161 1.11 christos "abcd",
162 1.11 christos "efgh",
163 1.11 christos 0,
164 1.11 christos
165 1.11 christos /* Test 9 - Backslash within double quotes. */
166 1.11 christos "\"\\$VAR\" \"\\`\" \"\\\"\" \"\\\\\" \"\\n\" \"\\t\"", /* Test 9 data */
167 1.11 christos ARGV0,
168 1.11 christos "@test-expandargv-9.lst",
169 1.11 christos 0,
170 1.11 christos ARGV0,
171 1.11 christos "$VAR",
172 1.11 christos "`",
173 1.11 christos "\"",
174 1.11 christos "\\",
175 1.11 christos "\\n",
176 1.11 christos "\\t",
177 1.11 christos 0,
178 1.11 christos
179 1.11 christos /* Test 10 - Mixed white space characters. */
180 1.11 christos "\t \n \t ", /* Test 10 data */
181 1.11 christos ARGV0,
182 1.11 christos "@test-expandargv-10.lst",
183 1.11 christos 0,
184 1.11 christos ARGV0,
185 1.11 christos 0,
186 1.11 christos
187 1.11 christos /* Test 11 - Single ' ' character. */
188 1.11 christos " ", /* Test 11 data */
189 1.11 christos ARGV0,
190 1.11 christos "@test-expandargv-11.lst",
191 1.11 christos 0,
192 1.11 christos ARGV0,
193 1.11 christos 0,
194 1.11 christos
195 1.11 christos /* Test 12 - Multiple ' ' characters. */
196 1.11 christos " ", /* Test 12 data */
197 1.11 christos ARGV0,
198 1.11 christos "@test-expandargv-12.lst",
199 1.11 christos 0,
200 1.11 christos ARGV0,
201 1.11 christos 0,
202 1.11 christos
203 1.1 christos 0 /* Test done marker, don't remove. */
204 1.1 christos };
205 1.1 christos
206 1.1 christos /* Print a fatal error and exit. LINE is the line number where we
207 1.1 christos detected the error, ERRMSG is the error message to print, and ERR
208 1.1 christos is 0 or an errno value to print. */
209 1.1 christos
210 1.1 christos static void
211 1.1 christos fatal_error (int line, const char *errmsg, int err)
212 1.1 christos {
213 1.1 christos fprintf (stderr, "test-expandargv:%d: %s", line, errmsg);
214 1.1 christos if (errno != 0)
215 1.1 christos fprintf (stderr, ": %s", xstrerror (err));
216 1.1 christos fprintf (stderr, "\n");
217 1.1 christos exit (EXIT_FAILURE);
218 1.1 christos }
219 1.1 christos
220 1.1 christos /* hook_char_replace:
221 1.1 christos Replace 'replacethis' with 'withthis' */
222 1.1 christos
223 1.1 christos void
224 1.1 christos hook_char_replace (char *string, size_t len, char replacethis, char withthis)
225 1.1 christos {
226 1.1 christos int i = 0;
227 1.1 christos for (i = 0; i < len; i++)
228 1.1 christos if (string[i] == replacethis)
229 1.1 christos string[i] = withthis;
230 1.1 christos }
231 1.1 christos
232 1.1 christos /* run_replaces:
233 1.1 christos Hook here all the character for character replaces.
234 1.1 christos Be warned that expanding the string or contracting the string
235 1.1 christos should be handled with care. */
236 1.1 christos
237 1.1 christos void
238 1.1 christos run_replaces (char * string)
239 1.1 christos {
240 1.1 christos /* Store original string size */
241 1.1 christos size_t len = strlen (string);
242 1.1 christos hook_char_replace (string, len, '\b', '\0');
243 1.1 christos }
244 1.1 christos
245 1.1 christos /* write_test:
246 1.1 christos Write test datafile */
247 1.1 christos
248 1.1 christos void
249 1.1 christos writeout_test (int test, const char * test_data)
250 1.1 christos {
251 1.1 christos char filename[256];
252 1.1 christos FILE *fd;
253 1.1 christos size_t len, sys_fwrite;
254 1.1 christos char * parse;
255 1.1 christos
256 1.1 christos /* Unique filename per test */
257 1.1 christos sprintf (filename, FILENAME_PATTERN, test);
258 1.1 christos fd = fopen (filename, "w");
259 1.1 christos if (fd == NULL)
260 1.1 christos fatal_error (__LINE__, "Failed to create test file.", errno);
261 1.1 christos
262 1.1 christos /* Generate RW copy of data for replaces */
263 1.1 christos len = strlen (test_data);
264 1.1 christos parse = malloc (sizeof (char) * (len + 1));
265 1.1 christos if (parse == NULL)
266 1.1 christos fatal_error (__LINE__, "Failed to malloc parse.", errno);
267 1.1 christos
268 1.1 christos memcpy (parse, test_data, sizeof (char) * (len + 1));
269 1.1 christos /* Run all possible replaces */
270 1.1 christos run_replaces (parse);
271 1.1 christos
272 1.1 christos sys_fwrite = fwrite (parse, sizeof (char), len, fd);
273 1.1 christos if (sys_fwrite != len)
274 1.1 christos fatal_error (__LINE__, "Failed to write to test file.", errno);
275 1.1 christos
276 1.1 christos free (parse);
277 1.1 christos fclose (fd);
278 1.1 christos }
279 1.1 christos
280 1.1 christos /* erase_test:
281 1.1 christos Erase the test file */
282 1.1 christos
283 1.1 christos void
284 1.1 christos erase_test (int test)
285 1.1 christos {
286 1.1 christos char filename[256];
287 1.1 christos sprintf (filename, FILENAME_PATTERN, test);
288 1.1 christos if (unlink (filename) != 0)
289 1.1 christos fatal_error (__LINE__, "Failed to erase test file.", errno);
290 1.1 christos }
291 1.1 christos
292 1.11 christos /* compare_argv:
293 1.11 christos TEST is the current test number, and NAME is a short string to identify
294 1.11 christos which libibery function is being tested. ARGC_A and ARGV_A describe an
295 1.11 christos argument array, and this is compared to ARGC_B and ARGV_B, return 0 if
296 1.11 christos the two arrays match, otherwise return 1. */
297 1.11 christos
298 1.11 christos static int
299 1.11 christos compare_argv (int test, const char *name, int argc_a, char *argv_a[],
300 1.11 christos int argc_b, char *argv_b[])
301 1.11 christos {
302 1.11 christos int failed = 0, k;
303 1.11 christos
304 1.11 christos if (argc_a != argc_b)
305 1.11 christos {
306 1.11 christos printf ("FAIL: test-%s-%d. Argument count didn't match\n", name, test);
307 1.11 christos failed = 1;
308 1.11 christos }
309 1.11 christos /* Compare each of the argv's ... */
310 1.11 christos else
311 1.11 christos for (k = 0; k < argc_a; k++)
312 1.11 christos if (strcmp (argv_a[k], argv_b[k]) != 0)
313 1.11 christos {
314 1.11 christos printf ("FAIL: test-%s-%d. Arguments don't match.\n", name, test);
315 1.11 christos failed = 1;
316 1.11 christos break;
317 1.11 christos }
318 1.11 christos
319 1.11 christos if (!failed)
320 1.11 christos printf ("PASS: test-%s-%d.\n", name, test);
321 1.11 christos
322 1.11 christos return failed;
323 1.11 christos }
324 1.11 christos
325 1.11 christos /* test_buildargv
326 1.11 christos Test the buildargv function from libiberty. TEST is the current test
327 1.11 christos number and TEST_INPUT is the string to pass to buildargv (after calling
328 1.11 christos run_replaces on it). ARGC_AFTER and ARGV_AFTER are the expected
329 1.11 christos results. Return 0 if the test passes, otherwise return 1. */
330 1.11 christos
331 1.11 christos static int
332 1.11 christos test_buildargv (int test, const char * test_input, int argc_after,
333 1.11 christos char *argv_after[])
334 1.11 christos {
335 1.11 christos char * input, ** argv;
336 1.11 christos size_t len;
337 1.11 christos int argc, failed;
338 1.11 christos
339 1.11 christos /* Generate RW copy of data for replaces */
340 1.11 christos len = strlen (test_input);
341 1.11 christos input = malloc (sizeof (char) * (len + 1));
342 1.11 christos if (input == NULL)
343 1.11 christos fatal_error (__LINE__, "Failed to malloc buildargv input buffer.", errno);
344 1.11 christos
345 1.11 christos memcpy (input, test_input, sizeof (char) * (len + 1));
346 1.11 christos /* Run all possible replaces */
347 1.11 christos run_replaces (input);
348 1.11 christos
349 1.11 christos /* Split INPUT into separate arguments. */
350 1.11 christos argv = buildargv (input);
351 1.11 christos
352 1.11 christos /* Count the arguments we got back. */
353 1.11 christos argc = 0;
354 1.11 christos while (argv[argc])
355 1.11 christos ++argc;
356 1.11 christos
357 1.11 christos failed = compare_argv (test, "buildargv", argc_after, argv_after, argc, argv);
358 1.11 christos
359 1.11 christos free (input);
360 1.11 christos freeargv (argv);
361 1.11 christos
362 1.11 christos return failed;
363 1.11 christos }
364 1.1 christos
365 1.1 christos /* run_tests:
366 1.1 christos Run expandargv
367 1.1 christos Compare argv before and after.
368 1.1 christos Return number of fails */
369 1.1 christos
370 1.1 christos int
371 1.1 christos run_tests (const char **test_data)
372 1.1 christos {
373 1.1 christos int argc_after, argc_before;
374 1.1 christos char ** argv_before, ** argv_after;
375 1.11 christos int i, j, k, fails;
376 1.11 christos const char * input_str;
377 1.1 christos
378 1.1 christos i = j = fails = 0;
379 1.1 christos /* Loop over all the tests */
380 1.1 christos while (test_data[j])
381 1.1 christos {
382 1.11 christos /* Save original input in case we run a buildargv test. */
383 1.11 christos input_str = test_data[j];
384 1.11 christos
385 1.1 christos /* Write test data */
386 1.1 christos writeout_test (i, test_data[j++]);
387 1.1 christos /* Copy argv before */
388 1.1 christos argv_before = dupargv ((char **) &test_data[j]);
389 1.1 christos
390 1.1 christos /* Count argc before/after */
391 1.1 christos argc_before = 0;
392 1.1 christos argc_after = 0;
393 1.1 christos while (test_data[j + argc_before])
394 1.1 christos argc_before++;
395 1.1 christos j += argc_before + 1; /* Skip null */
396 1.1 christos while (test_data[j + argc_after])
397 1.1 christos argc_after++;
398 1.1 christos
399 1.1 christos /* Copy argv after */
400 1.1 christos argv_after = dupargv ((char **) &test_data[j]);
401 1.1 christos
402 1.1 christos /* Run all possible replaces */
403 1.1 christos for (k = 0; k < argc_before; k++)
404 1.1 christos run_replaces (argv_before[k]);
405 1.1 christos for (k = 0; k < argc_after; k++)
406 1.1 christos run_replaces (argv_after[k]);
407 1.1 christos
408 1.11 christos /* If the test input is just a file to expand then we can also test
409 1.11 christos calling buildargv directly as the expected output is equivalent to
410 1.11 christos calling buildargv on the contents of the file.
411 1.11 christos
412 1.11 christos The results of calling buildargv will not include the ARGV0 constant,
413 1.11 christos which is why we pass 'argc_after - 1' and 'argv_after + 1', this skips
414 1.11 christos over the ARGV0 in the expected results. */
415 1.11 christos if (argc_before == 2)
416 1.11 christos fails += test_buildargv (i, input_str, argc_after - 1, argv_after + 1);
417 1.11 christos else
418 1.11 christos printf ("SKIP: test-buildargv-%d. This test isn't for buildargv\n", i);
419 1.11 christos
420 1.1 christos /* Run test: Expand arguments */
421 1.1 christos expandargv (&argc_before, &argv_before);
422 1.1 christos
423 1.11 christos fails += compare_argv (i, "expandargv", argc_before, argv_before,
424 1.11 christos argc_after, argv_after);
425 1.1 christos
426 1.1 christos freeargv (argv_before);
427 1.1 christos freeargv (argv_after);
428 1.1 christos /* Advance to next test */
429 1.1 christos j += argc_after + 1;
430 1.1 christos /* Erase test file */
431 1.1 christos erase_test (i);
432 1.1 christos i++;
433 1.1 christos }
434 1.1 christos return fails;
435 1.1 christos }
436 1.1 christos
437 1.1 christos /* main:
438 1.1 christos Run tests.
439 1.1 christos Check result and exit with appropriate code. */
440 1.1 christos
441 1.1 christos int
442 1.1 christos main(int argc, char **argv)
443 1.1 christos {
444 1.1 christos int fails;
445 1.1 christos /* Repeat for all the tests:
446 1.1 christos - Parse data array and write into file.
447 1.1 christos - Run replace hooks before writing to file.
448 1.1 christos - Parse data array and build argv before/after.
449 1.1 christos - Run replace hooks on argv before/after
450 1.1 christos - Run expandargv.
451 1.1 christos - Compare output of expandargv argv to after argv.
452 1.1 christos - If they compare the same then test passes
453 1.1 christos else the test fails.
454 1.1 christos - Erase test file. */
455 1.1 christos
456 1.1 christos fails = run_tests (test_data);
457 1.1 christos if (!fails)
458 1.1 christos exit (EXIT_SUCCESS);
459 1.1 christos else
460 1.1 christos exit (EXIT_FAILURE);
461 1.1 christos }
462 1.1 christos
463