1/* expandargv test program, 2 Copyright (C) 2006-2024 Free Software Foundation, Inc. 3 Written by Carlos O'Donell <carlos@codesourcery.com> 4 5 This file is part of the libiberty library, which is part of GCC. 6 7 This file is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 In addition to the permissions in the GNU General Public License, the 13 Free Software Foundation gives you unlimited permission to link the 14 compiled version of this file into combinations with other programs, 15 and to distribute those combinations without any restriction coming 16 from the use of this file. (The General Public License restrictions 17 do apply in other respects; for example, they cover modification of 18 the file, and distribution when not linked into a combined 19 executable.) 20 21 This program is distributed in the hope that it will be useful, 22 but WITHOUT ANY WARRANTY; without even the implied warranty of 23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 GNU General Public License for more details. 25 26 You should have received a copy of the GNU General Public License 27 along with this program; if not, write to the Free Software 28 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. 29*/ 30 31#ifdef HAVE_CONFIG_H 32#include "config.h" 33#endif 34#include "libiberty.h" 35#include <stdio.h> 36#include <errno.h> 37#ifdef HAVE_STDLIB_H 38#include <stdlib.h> 39#endif 40#ifdef HAVE_STRING_H 41#include <string.h> 42#endif 43#ifdef HAVE_UNISTD_H 44#include <unistd.h> 45#endif 46 47#ifndef EXIT_SUCCESS 48#define EXIT_SUCCESS 0 49#endif 50 51#ifndef EXIT_FAILURE 52#define EXIT_FAILURE 1 53#endif 54 55static void fatal_error (int, const char *, int) ATTRIBUTE_NORETURN; 56void writeout_test (int, const char *); 57void run_replaces (char *); 58void hook_char_replace (char *, size_t, char, char); 59int run_tests (const char **); 60void erase_test (int); 61 62/* Test input data, argv before, and argv after: 63 64 The \n is an important part of test_data since expandargv 65 may have to work in environments where \n is translated 66 as \r\n. Thus \n is included in the test data for the file. 67 68 We use \b to indicate that the test data is the null character. 69 This is because we use \0 normally to represent the end of the 70 file data, so we need something else for this. */ 71 72#define FILENAME_PATTERN "test-expandargv-%d.lst" 73#define ARGV0 "test-expandargv" 74 75const char *test_data[] = { 76 /* Test 0 - Check for expansion with \r\n */ 77 "a\r\nb", /* Test 0 data */ 78 ARGV0, 79 "@test-expandargv-0.lst", 80 0, /* End of argv[] before expansion */ 81 ARGV0, 82 "a", 83 "b", 84 0, /* End of argv[] after expansion */ 85 86 /* Test 1 - Check for expansion with \n */ 87 "a\nb", /* Test 1 data */ 88 ARGV0, 89 "@test-expandargv-1.lst", 90 0, 91 ARGV0, 92 "a", 93 "b", 94 0, 95 96 /* Test 2 - Check for expansion with \0 */ 97 "a\bb", /* Test 2 data */ 98 ARGV0, 99 "@test-expandargv-2.lst", 100 0, 101 ARGV0, 102 "a", 103 0, 104 105 /* Test 3 - Check for expansion with only \0 */ 106 "\b", /* Test 3 data */ 107 ARGV0, 108 "@test-expandargv-3.lst", 109 0, 110 ARGV0, 111 0, 112 113 /* Test 4 - Check for options beginning with an empty line. */ 114 "\na\nb", /* Test 4 data */ 115 ARGV0, 116 "@test-expandargv-4.lst", 117 0, 118 ARGV0, 119 "a", 120 "b", 121 0, 122 123 /* Test 5 - Check for options containing an empty argument. */ 124 "a\n''\nb", /* Test 5 data */ 125 ARGV0, 126 "@test-expandargv-5.lst", 127 0, 128 ARGV0, 129 "a", 130 "", 131 "b", 132 0, 133 134 /* Test 6 - Check for options containing a quoted newline. */ 135 "a\n'a\n\nb'\nb", /* Test 6 data */ 136 ARGV0, 137 "@test-expandargv-6.lst", 138 0, 139 ARGV0, 140 "a", 141 "a\n\nb", 142 "b", 143 0, 144 145 0 /* Test done marker, don't remove. */ 146}; 147 148/* Print a fatal error and exit. LINE is the line number where we 149 detected the error, ERRMSG is the error message to print, and ERR 150 is 0 or an errno value to print. */ 151 152static void 153fatal_error (int line, const char *errmsg, int err) 154{ 155 fprintf (stderr, "test-expandargv:%d: %s", line, errmsg); 156 if (errno != 0) 157 fprintf (stderr, ": %s", xstrerror (err)); 158 fprintf (stderr, "\n"); 159 exit (EXIT_FAILURE); 160} 161 162/* hook_char_replace: 163 Replace 'replacethis' with 'withthis' */ 164 165void 166hook_char_replace (char *string, size_t len, char replacethis, char withthis) 167{ 168 int i = 0; 169 for (i = 0; i < len; i++) 170 if (string[i] == replacethis) 171 string[i] = withthis; 172} 173 174/* run_replaces: 175 Hook here all the character for character replaces. 176 Be warned that expanding the string or contracting the string 177 should be handled with care. */ 178 179void 180run_replaces (char * string) 181{ 182 /* Store original string size */ 183 size_t len = strlen (string); 184 hook_char_replace (string, len, '\b', '\0'); 185} 186 187/* write_test: 188 Write test datafile */ 189 190void 191writeout_test (int test, const char * test_data) 192{ 193 char filename[256]; 194 FILE *fd; 195 size_t len, sys_fwrite; 196 char * parse; 197 198 /* Unique filename per test */ 199 sprintf (filename, FILENAME_PATTERN, test); 200 fd = fopen (filename, "w"); 201 if (fd == NULL) 202 fatal_error (__LINE__, "Failed to create test file.", errno); 203 204 /* Generate RW copy of data for replaces */ 205 len = strlen (test_data); 206 parse = malloc (sizeof (char) * (len + 1)); 207 if (parse == NULL) 208 fatal_error (__LINE__, "Failed to malloc parse.", errno); 209 210 memcpy (parse, test_data, sizeof (char) * (len + 1)); 211 /* Run all possible replaces */ 212 run_replaces (parse); 213 214 sys_fwrite = fwrite (parse, sizeof (char), len, fd); 215 if (sys_fwrite != len) 216 fatal_error (__LINE__, "Failed to write to test file.", errno); 217 218 free (parse); 219 fclose (fd); 220} 221 222/* erase_test: 223 Erase the test file */ 224 225void 226erase_test (int test) 227{ 228 char filename[256]; 229 sprintf (filename, FILENAME_PATTERN, test); 230 if (unlink (filename) != 0) 231 fatal_error (__LINE__, "Failed to erase test file.", errno); 232} 233 234 235/* run_tests: 236 Run expandargv 237 Compare argv before and after. 238 Return number of fails */ 239 240int 241run_tests (const char **test_data) 242{ 243 int argc_after, argc_before; 244 char ** argv_before, ** argv_after; 245 int i, j, k, fails, failed; 246 247 i = j = fails = 0; 248 /* Loop over all the tests */ 249 while (test_data[j]) 250 { 251 /* Write test data */ 252 writeout_test (i, test_data[j++]); 253 /* Copy argv before */ 254 argv_before = dupargv ((char **) &test_data[j]); 255 256 /* Count argc before/after */ 257 argc_before = 0; 258 argc_after = 0; 259 while (test_data[j + argc_before]) 260 argc_before++; 261 j += argc_before + 1; /* Skip null */ 262 while (test_data[j + argc_after]) 263 argc_after++; 264 265 /* Copy argv after */ 266 argv_after = dupargv ((char **) &test_data[j]); 267 268 /* Run all possible replaces */ 269 for (k = 0; k < argc_before; k++) 270 run_replaces (argv_before[k]); 271 for (k = 0; k < argc_after; k++) 272 run_replaces (argv_after[k]); 273 274 /* Run test: Expand arguments */ 275 expandargv (&argc_before, &argv_before); 276 277 failed = 0; 278 /* Compare size first */ 279 if (argc_before != argc_after) 280 { 281 printf ("FAIL: test-expandargv-%d. Number of arguments don't match.\n", i); 282 failed++; 283 } 284 /* Compare each of the argv's ... */ 285 else 286 for (k = 0; k < argc_after; k++) 287 if (strcmp (argv_before[k], argv_after[k]) != 0) 288 { 289 printf ("FAIL: test-expandargv-%d. Arguments don't match.\n", i); 290 failed++; 291 } 292 293 if (!failed) 294 printf ("PASS: test-expandargv-%d.\n", i); 295 else 296 fails++; 297 298 freeargv (argv_before); 299 freeargv (argv_after); 300 /* Advance to next test */ 301 j += argc_after + 1; 302 /* Erase test file */ 303 erase_test (i); 304 i++; 305 } 306 return fails; 307} 308 309/* main: 310 Run tests. 311 Check result and exit with appropriate code. */ 312 313int 314main(int argc, char **argv) 315{ 316 int fails; 317 /* Repeat for all the tests: 318 - Parse data array and write into file. 319 - Run replace hooks before writing to file. 320 - Parse data array and build argv before/after. 321 - Run replace hooks on argv before/after 322 - Run expandargv. 323 - Compare output of expandargv argv to after argv. 324 - If they compare the same then test passes 325 else the test fails. 326 - Erase test file. */ 327 328 fails = run_tests (test_data); 329 if (!fails) 330 exit (EXIT_SUCCESS); 331 else 332 exit (EXIT_FAILURE); 333} 334 335