|  | /* expandargv test program, | 
|  | Copyright (C) 2006-2017 Free Software Foundation, Inc. | 
|  | Written by Carlos O'Donell <carlos@codesourcery.com> | 
|  |  | 
|  | This file is part of the libiberty library, which is part of GCC. | 
|  |  | 
|  | This file is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 2 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | In addition to the permissions in the GNU General Public License, the | 
|  | Free Software Foundation gives you unlimited permission to link the | 
|  | compiled version of this file into combinations with other programs, | 
|  | and to distribute those combinations without any restriction coming | 
|  | from the use of this file.  (The General Public License restrictions | 
|  | do apply in other respects; for example, they cover modification of | 
|  | the file, and distribution when not linked into a combined | 
|  | executable.) | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; if not, write to the Free Software | 
|  | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. | 
|  | */ | 
|  |  | 
|  | #ifdef HAVE_CONFIG_H | 
|  | #include "config.h" | 
|  | #endif | 
|  | #include "libiberty.h" | 
|  | #include <stdio.h> | 
|  | #include <errno.h> | 
|  | #ifdef HAVE_STDLIB_H | 
|  | #include <stdlib.h> | 
|  | #endif | 
|  | #ifdef HAVE_STRING_H | 
|  | #include <string.h> | 
|  | #endif | 
|  | #ifdef HAVE_UNISTD_H | 
|  | #include <unistd.h> | 
|  | #endif | 
|  |  | 
|  | #ifndef EXIT_SUCCESS | 
|  | #define EXIT_SUCCESS 0 | 
|  | #endif | 
|  |  | 
|  | #ifndef EXIT_FAILURE | 
|  | #define EXIT_FAILURE 1 | 
|  | #endif | 
|  |  | 
|  | static void fatal_error (int, const char *, int) ATTRIBUTE_NORETURN; | 
|  | void writeout_test (int, const char *); | 
|  | void run_replaces (char *); | 
|  | void hook_char_replace (char *, size_t, char, char); | 
|  | int run_tests (const char **); | 
|  | void erase_test (int); | 
|  |  | 
|  | /* Test input data, argv before, and argv after: | 
|  |  | 
|  | The \n is an important part of test_data since expandargv | 
|  | may have to work in environments where \n is translated | 
|  | as \r\n. Thus \n is included in the test data for the file. | 
|  |  | 
|  | We use \b to indicate that the test data is the null character. | 
|  | This is because we use \0 normally to represent the end of the | 
|  | file data, so we need something else for this. */ | 
|  |  | 
|  | #define FILENAME_PATTERN "test-expandargv-%d.lst" | 
|  | #define ARGV0 "test-expandargv" | 
|  |  | 
|  | const char *test_data[] = { | 
|  | /* Test 0 - Check for expansion with \r\n */ | 
|  | "a\r\nb",	/* Test 0 data */ | 
|  | ARGV0, | 
|  | "@test-expandargv-0.lst", | 
|  | 0, /* End of argv[] before expansion */ | 
|  | ARGV0, | 
|  | "a", | 
|  | "b", | 
|  | 0, /* End of argv[] after expansion */ | 
|  |  | 
|  | /* Test 1 - Check for expansion with \n */ | 
|  | "a\nb",	/* Test 1 data */ | 
|  | ARGV0, | 
|  | "@test-expandargv-1.lst", | 
|  | 0, | 
|  | ARGV0, | 
|  | "a", | 
|  | "b", | 
|  | 0, | 
|  |  | 
|  | /* Test 2 - Check for expansion with \0 */ | 
|  | "a\bb",	/* Test 2 data */ | 
|  | ARGV0, | 
|  | "@test-expandargv-2.lst", | 
|  | 0, | 
|  | ARGV0, | 
|  | "a", | 
|  | 0, | 
|  |  | 
|  | /* Test 3 - Check for expansion with only \0 */ | 
|  | "\b",		/* Test 3 data */ | 
|  | ARGV0, | 
|  | "@test-expandargv-3.lst", | 
|  | 0, | 
|  | ARGV0, | 
|  | 0, | 
|  |  | 
|  | /* Test 4 - Check for options beginning with an empty line.  */ | 
|  | "\na\nb",	/* Test 4 data */ | 
|  | ARGV0, | 
|  | "@test-expandargv-4.lst", | 
|  | 0, | 
|  | ARGV0, | 
|  | "a", | 
|  | "b", | 
|  | 0, | 
|  |  | 
|  | /* Test 5 - Check for options containing an empty argument.  */ | 
|  | "a\n''\nb",    /* Test 5 data */ | 
|  | ARGV0, | 
|  | "@test-expandargv-5.lst", | 
|  | 0, | 
|  | ARGV0, | 
|  | "a", | 
|  | "", | 
|  | "b", | 
|  | 0, | 
|  |  | 
|  | /* Test 6 - Check for options containing a quoted newline.  */ | 
|  | "a\n'a\n\nb'\nb",    /* Test 6 data */ | 
|  | ARGV0, | 
|  | "@test-expandargv-6.lst", | 
|  | 0, | 
|  | ARGV0, | 
|  | "a", | 
|  | "a\n\nb", | 
|  | "b", | 
|  | 0, | 
|  |  | 
|  | 0 /* Test done marker, don't remove. */ | 
|  | }; | 
|  |  | 
|  | /* Print a fatal error and exit.  LINE is the line number where we | 
|  | detected the error, ERRMSG is the error message to print, and ERR | 
|  | is 0 or an errno value to print.  */ | 
|  |  | 
|  | static void | 
|  | fatal_error (int line, const char *errmsg, int err) | 
|  | { | 
|  | fprintf (stderr, "test-expandargv:%d: %s", line, errmsg); | 
|  | if (errno != 0) | 
|  | fprintf (stderr, ": %s", xstrerror (err)); | 
|  | fprintf (stderr, "\n"); | 
|  | exit (EXIT_FAILURE); | 
|  | } | 
|  |  | 
|  | /* hook_char_replace: | 
|  | Replace 'replacethis' with 'withthis' */ | 
|  |  | 
|  | void | 
|  | hook_char_replace (char *string, size_t len, char replacethis, char withthis) | 
|  | { | 
|  | int i = 0; | 
|  | for (i = 0; i < len; i++) | 
|  | if (string[i] == replacethis) | 
|  | string[i] = withthis; | 
|  | } | 
|  |  | 
|  | /* run_replaces: | 
|  | Hook here all the character for character replaces. | 
|  | Be warned that expanding the string or contracting the string | 
|  | should be handled with care. */ | 
|  |  | 
|  | void | 
|  | run_replaces (char * string) | 
|  | { | 
|  | /* Store original string size */ | 
|  | size_t len = strlen (string); | 
|  | hook_char_replace (string, len, '\b', '\0'); | 
|  | } | 
|  |  | 
|  | /* write_test: | 
|  | Write test datafile */ | 
|  |  | 
|  | void | 
|  | writeout_test (int test, const char * test_data) | 
|  | { | 
|  | char filename[256]; | 
|  | FILE *fd; | 
|  | size_t len, sys_fwrite; | 
|  | char * parse; | 
|  |  | 
|  | /* Unique filename per test */ | 
|  | sprintf (filename, FILENAME_PATTERN, test); | 
|  | fd = fopen (filename, "w"); | 
|  | if (fd == NULL) | 
|  | fatal_error (__LINE__, "Failed to create test file.", errno); | 
|  |  | 
|  | /* Generate RW copy of data for replaces */ | 
|  | len = strlen (test_data); | 
|  | parse = malloc (sizeof (char) * (len + 1)); | 
|  | if (parse == NULL) | 
|  | fatal_error (__LINE__, "Failed to malloc parse.", errno); | 
|  |  | 
|  | memcpy (parse, test_data, sizeof (char) * (len + 1)); | 
|  | /* Run all possible replaces */ | 
|  | run_replaces (parse); | 
|  |  | 
|  | sys_fwrite = fwrite (parse, sizeof (char), len, fd); | 
|  | if (sys_fwrite != len) | 
|  | fatal_error (__LINE__, "Failed to write to test file.", errno); | 
|  |  | 
|  | free (parse); | 
|  | fclose (fd); | 
|  | } | 
|  |  | 
|  | /* erase_test: | 
|  | Erase the test file */ | 
|  |  | 
|  | void | 
|  | erase_test (int test) | 
|  | { | 
|  | char filename[256]; | 
|  | sprintf (filename, FILENAME_PATTERN, test); | 
|  | if (unlink (filename) != 0) | 
|  | fatal_error (__LINE__, "Failed to erase test file.", errno); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* run_tests: | 
|  | Run expandargv | 
|  | Compare argv before and after. | 
|  | Return number of fails */ | 
|  |  | 
|  | int | 
|  | run_tests (const char **test_data) | 
|  | { | 
|  | int argc_after, argc_before; | 
|  | char ** argv_before, ** argv_after; | 
|  | int i, j, k, fails, failed; | 
|  |  | 
|  | i = j = fails = 0; | 
|  | /* Loop over all the tests */ | 
|  | while (test_data[j]) | 
|  | { | 
|  | /* Write test data */ | 
|  | writeout_test (i, test_data[j++]); | 
|  | /* Copy argv before */ | 
|  | argv_before = dupargv ((char **) &test_data[j]); | 
|  |  | 
|  | /* Count argc before/after */ | 
|  | argc_before = 0; | 
|  | argc_after = 0; | 
|  | while (test_data[j + argc_before]) | 
|  | argc_before++; | 
|  | j += argc_before + 1; /* Skip null */ | 
|  | while (test_data[j + argc_after]) | 
|  | argc_after++; | 
|  |  | 
|  | /* Copy argv after */ | 
|  | argv_after = dupargv ((char **) &test_data[j]); | 
|  |  | 
|  | /* Run all possible replaces */ | 
|  | for (k = 0; k < argc_before; k++) | 
|  | run_replaces (argv_before[k]); | 
|  | for (k = 0; k < argc_after; k++) | 
|  | run_replaces (argv_after[k]); | 
|  |  | 
|  | /* Run test: Expand arguments */ | 
|  | expandargv (&argc_before, &argv_before); | 
|  |  | 
|  | failed = 0; | 
|  | /* Compare size first */ | 
|  | if (argc_before != argc_after) | 
|  | { | 
|  | printf ("FAIL: test-expandargv-%d. Number of arguments don't match.\n", i); | 
|  | failed++; | 
|  | } | 
|  | /* Compare each of the argv's ... */ | 
|  | else | 
|  | for (k = 0; k < argc_after; k++) | 
|  | if (strcmp (argv_before[k], argv_after[k]) != 0) | 
|  | { | 
|  | printf ("FAIL: test-expandargv-%d. Arguments don't match.\n", i); | 
|  | failed++; | 
|  | } | 
|  |  | 
|  | if (!failed) | 
|  | printf ("PASS: test-expandargv-%d.\n", i); | 
|  | else | 
|  | fails++; | 
|  |  | 
|  | freeargv (argv_before); | 
|  | freeargv (argv_after); | 
|  | /* Advance to next test */ | 
|  | j += argc_after + 1; | 
|  | /* Erase test file */ | 
|  | erase_test (i); | 
|  | i++; | 
|  | } | 
|  | return fails; | 
|  | } | 
|  |  | 
|  | /* main: | 
|  | Run tests. | 
|  | Check result and exit with appropriate code. */ | 
|  |  | 
|  | int | 
|  | main(int argc, char **argv) | 
|  | { | 
|  | int fails; | 
|  | /* Repeat for all the tests: | 
|  | - Parse data array and write into file. | 
|  | - Run replace hooks before writing to file. | 
|  | - Parse data array and build argv before/after. | 
|  | - Run replace hooks on argv before/after | 
|  | - Run expandargv. | 
|  | - Compare output of expandargv argv to after argv. | 
|  | - If they compare the same then test passes | 
|  | else the test fails. | 
|  | - Erase test file. */ | 
|  |  | 
|  | fails = run_tests (test_data); | 
|  | if (!fails) | 
|  | exit (EXIT_SUCCESS); | 
|  | else | 
|  | exit (EXIT_FAILURE); | 
|  | } | 
|  |  |