1/* expandargv test program,
2   Copyright (C) 2006 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