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