maint: update copyright
[gnulib.git] / tests / test-system-quote-main.c
1 /* Test of system-quote module.
2    Copyright (C) 2012-2014 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by Bruno Haible <bruno@clisp.org>, 2012.  */
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include "system-quote.h"
23
24 #if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
25 # define WINDOWS_NATIVE
26 #endif
27
28 #include <limits.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #ifdef WINDOWS_NATIVE
35 # define WIN32_LEAN_AND_MEAN
36 # include <windows.h>
37 #endif
38
39 #include "progname.h"
40 #include "macros.h"
41
42 #define EXPECTED_DATA_FILE "t-sq-data.tmp"
43
44 static int failed;
45
46 static void
47 check_one (enum system_command_interpreter interpreter, const char *prog,
48            const char *input)
49 {
50   char buf[1000];
51   size_t output_len;
52   char *output;
53   char *bufend;
54
55   output_len = system_quote_length (interpreter, input);
56
57   output = system_quote (interpreter, input);
58   ASSERT (strlen (output) == output_len);
59
60   ASSERT (output_len <= sizeof (buf) - 2);
61   memset (buf, '\0', output_len + 1);
62   buf[output_len + 1] = '%';
63   bufend = system_quote_copy (buf, interpreter, input);
64   ASSERT (bufend == buf + output_len);
65   ASSERT (memcmp (buf, output, output_len + 1) == 0);
66   ASSERT (buf[output_len + 1] == '%');
67
68   /* Store INPUT in EXPECTED_DATA_FILE, for verification by the child
69      process.  */
70   {
71     FILE *fp = fopen (EXPECTED_DATA_FILE, "wb");
72     if (fp == NULL)
73       exit (3);
74     if (fwrite (input, 1, strlen (input), fp) != strlen (input))
75       exit (4);
76     if (fclose (fp))
77       exit (5);
78   }
79
80   /* Invoke the child process through system() and popen().  */
81   {
82     char command[1000];
83
84     sprintf (command, "%s %s", prog, output);
85
86     switch (interpreter)
87       {
88       case SCI_SYSTEM:
89 #ifdef WINDOWS_NATIVE
90       case SCI_WINDOWS_CMD:
91 #endif
92         {
93           int exitcode = system (command);
94           if (exitcode != 0)
95             {
96               fprintf (stderr, "for input = |%s|: system() command failed with status %d: %s\n",
97                        input, exitcode, command);
98               failed = 1;
99             }
100         }
101         {
102           FILE *fp = popen (command, "r");
103           int exitcode = pclose (fp);
104           if (exitcode != 0)
105             {
106               fprintf (stderr, "for input = |%s|: popen() command failed with status %d: %s\n",
107                        input, exitcode, command);
108               failed = 1;
109             }
110         }
111         break;
112 #ifdef WINDOWS_NATIVE
113       case SCI_WINDOWS_CREATEPROCESS:
114         {
115           PROCESS_INFORMATION pinfo;
116           STARTUPINFO sinfo;
117           sinfo.cb = sizeof (STARTUPINFO);
118           sinfo.lpReserved = NULL;
119           sinfo.lpDesktop = NULL;
120           sinfo.lpTitle = NULL;
121           sinfo.cbReserved2 = 0;
122           sinfo.lpReserved2 = NULL;
123           sinfo.dwFlags = STARTF_USESTDHANDLES;
124           sinfo.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
125           sinfo.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
126           sinfo.hStdError = GetStdHandle (STD_ERROR_HANDLE);
127
128           if (CreateProcess (NULL, command, NULL, NULL, TRUE, 0, NULL, NULL,
129                              &sinfo, &pinfo))
130             {
131               DWORD exitcode;
132               CloseHandle (pinfo.hThread);
133               if (WaitForSingleObject (pinfo.hProcess, INFINITE) == WAIT_OBJECT_0)
134                 {
135                   if (GetExitCodeProcess (pinfo.hProcess, &exitcode))
136                     {
137                       if (exitcode != 0)
138                         {
139                           fprintf (stderr, "for input = |%s|: CreateProcess() command failed with status %d: %s\n",
140                                    input, exitcode, command);
141                           failed = 1;
142                         }
143                     }
144                   else
145                     {
146                       fprintf (stderr, "for input = |%s|: GetExitCodeProcess failed, GetLastError() = %u\n",
147                                input, GetLastError ());
148                       failed = 1;
149                     }
150                 }
151               else
152                 {
153                   fprintf (stderr, "for input = |%s|: WaitForSingleObject failed\n",
154                            input);
155                   failed = 1;
156                 }
157               CloseHandle (pinfo.hProcess);
158             }
159           else
160             {
161               fprintf (stderr, "for input = |%s|: CreateProcess failed, GetLastError() = %u\n",
162                        input, GetLastError ());
163               failed = 1;
164             }
165         }
166         break;
167 #endif
168       default:
169         break;
170       }
171   }
172
173   free (output);
174 }
175
176 static void
177 check_all (enum system_command_interpreter interpreter,
178            bool windows_cmd_limitations,
179            const char *prog)
180 {
181   /* Check the system_quote_length, system_quote_copy, system_quote
182      functions.  */
183   {
184     int c;
185
186     /* Empty argument.  */
187     check_one (interpreter, prog, "");
188
189     /* Identifier or number.  */
190     check_one (interpreter, prog, "foo");
191     check_one (interpreter, prog, "phr0ck");
192
193     /* Whitespace would be interpreted as argument separator by the shell.  */
194     check_one (interpreter, prog, "foo\tbar");
195     if (!windows_cmd_limitations)
196       {
197         check_one (interpreter, prog, "foo\nbar");
198         check_one (interpreter, prog, "foo\rbar");
199       }
200     check_one (interpreter, prog, "foo bar");
201
202     /* '!' at the beginning of argv[0] would introduce a negated command.  */
203     check_one (interpreter, prog, "!foo");
204
205     /* '"' would be interpreted as the start of a string.  */
206     check_one (interpreter, prog, "\"foo\"bar");
207
208     /* '#' at the beginning of an argument would be interpreted as the start
209        of a comment.  */
210     check_one (interpreter, prog, "#foo");
211
212     /* '$' at the beginning of an argument would be interpreted as a variable
213        reference.  */
214     check_one (interpreter, prog, "$foo");
215
216     /* '&' at the beginning of an argument would be interpreted as a background
217        task indicator.  */
218     check_one (interpreter, prog, "&");
219
220     /* "'" would be interpreted as the start of a string.  */
221     check_one (interpreter, prog, "'foo'bar");
222
223     /* '(' at the beginning of argv[0] would introduce a subshell command.  */
224     check_one (interpreter, prog, "(");
225
226     /* ')' at the beginning of an argument would be interpreted as the end of
227        the command.  */
228     check_one (interpreter, prog, ")");
229
230     /* '*' would be interpreted as a wildcard character.  */
231     check_one (interpreter, prog, "*");
232     check_one (interpreter, prog, "*foo");
233
234     /* ';' at the beginning of an argument would be interpreted as an empty
235        statement in argv[0] and as the end of the command otherwise.  */
236     check_one (interpreter, prog, ";");
237     check_one (interpreter, prog, "foo;");
238
239     /* '<' would be interpreted as a redirection of stdin.  */
240     check_one (interpreter, prog, "<");
241
242     /* '=' inside argv[0] would be interpreted as an environment variable
243        assignment.  */
244     check_one (interpreter, prog, "foo=bar");
245
246     /* '>' would be interpreted as a redirection of stdout.  */
247     check_one (interpreter, prog, ">");
248
249     /* '?' would be interpreted as a wildcard character.  */
250     check_one (interpreter, prog, "?");
251     check_one (interpreter, prog, "??");
252     check_one (interpreter, prog, "???");
253     check_one (interpreter, prog, "????");
254     check_one (interpreter, prog, "?????");
255     check_one (interpreter, prog, "??????");
256     check_one (interpreter, prog, "???????");
257     check_one (interpreter, prog, "????????");
258     check_one (interpreter, prog, "?????????");
259     check_one (interpreter, prog, "??????????");
260     check_one (interpreter, prog, "foo?bar");
261
262     /* '^' would be interpreted in old /bin/sh, e.g. SunOS 4.1.4.  */
263     check_one (interpreter, prog, "^");
264
265     /* "[...]" would be interpreted as a wildcard pattern.  */
266     check_one (interpreter, prog, "[");
267     check_one (interpreter, prog, "]");
268
269     /* '\' would be interpreted as an escape character.  */
270     check_one (interpreter, prog, "\\foo");
271
272     /* '`' would be interpreted as the start of a command substitution.  */
273     check_one (interpreter, prog, "`foo");
274
275     /* '{' at the beginning of argv[0] would introduce a complex command.  */
276     check_one (interpreter, prog, "{");
277
278     /* '|' at the beginning of an argument would be interpreted as a pipe
279        between commands.  */
280     check_one (interpreter, prog, "|");
281
282     /* '}' at the beginning of an argument would be interpreted as the end of
283        the command.  */
284     check_one (interpreter, prog, "}");
285
286     /* '~' at the beginning of an argument would be interpreted as a reference
287        to a user's home directory.  */
288     check_one (interpreter, prog, "~");
289     check_one (interpreter, prog, "~foo");
290
291     /* A string that contains both ' and ".  */
292     check_one (interpreter, prog, "foo'bar\"baz");
293
294     /* '%' is used for environment variable references in Windows cmd.exe.  */
295     check_one (interpreter, prog, "%");
296     check_one (interpreter, prog, "%%");
297     check_one (interpreter, prog, "%foo%");
298     check_one (interpreter, prog, "%PATH%");
299
300     /* All other characters don't need quoting.  */
301     for (c = 1; c <= UCHAR_MAX; c++)
302       if (strchr ("\t\n\r !\"#$&'()*;<=>?^[\\]`{|}~", c) == NULL)
303         {
304           char s[5];
305           s[0] = 'a';
306           s[1] = (char) c;
307           s[2] = 'z';
308           s[3] = (char) c;
309           s[4] = '\0';
310
311           check_one (interpreter, prog, s);
312         }
313   }
314 }
315
316 int
317 main (int argc, char *argv[])
318 {
319   char *prog;
320
321   set_program_name (argv[0]);
322
323   if (argc != 2)
324     {
325       fprintf (stderr, "%s: need 1 argument\n", argv[0]);
326       return 2;
327     }
328   prog = argv[1];
329
330 #ifdef WINDOWS_NATIVE
331   /* Make PROG suitable for native Windows system calls and cmd.exe:
332      Replace '/' with '\\'.  */
333   {
334     char *p;
335     for (p = prog; *p != '\0'; p++)
336       if (*p == '/')
337         *p = '\\';
338   }
339 #endif
340
341 #ifdef WINDOWS_NATIVE
342   check_all (SCI_SYSTEM, true, prog); /* equivalent to SCI_WINDOWS_CMD */
343   check_all (SCI_WINDOWS_CREATEPROCESS, false, prog);
344   check_all (SCI_WINDOWS_CMD, true, prog);
345 #else
346   check_all (SCI_SYSTEM, false, prog); /* equivalent to SCI_POSIX_SH */
347 #endif
348
349   /* Clean up.  */
350   unlink (EXPECTED_DATA_FILE);
351
352   return failed;
353 }