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