usleep tests: Avoid failure due to known Cygwin 3.5.3 bug.
[gnulib.git] / tests / test-system-quote-main.c
blob86089055fa1d23ab282479be76bcf9259d65fda7
1 /* Test of system-quote module.
2 Copyright (C) 2012-2024 Free Software Foundation, Inc.
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.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2012. */
19 #include <config.h>
21 /* Specification. */
22 #include "system-quote.h"
24 #if defined _WIN32 && !defined __CYGWIN__
25 # define WINDOWS_NATIVE
26 #endif
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #ifdef WINDOWS_NATIVE
34 # define WIN32_LEAN_AND_MEAN
35 # include <windows.h>
36 #endif
38 #include "macros.h"
40 #define EXPECTED_DATA_FILE "t-sq-data.tmp"
42 static void
43 check_one (enum system_command_interpreter interpreter, const char *prog,
44 const char *input)
46 char buf[1000];
47 size_t output_len;
48 char *output;
49 char *bufend;
51 output_len = system_quote_length (interpreter, input);
53 output = system_quote (interpreter, input);
54 ASSERT (strlen (output) == output_len);
56 ASSERT (output_len <= sizeof (buf) - 2);
57 memset (buf, '\0', output_len + 1);
58 buf[output_len + 1] = '%';
59 bufend = system_quote_copy (buf, interpreter, input);
60 ASSERT (bufend == buf + output_len);
61 ASSERT (memcmp (buf, output, output_len + 1) == 0);
62 ASSERT (buf[output_len + 1] == '%');
64 /* Store INPUT in EXPECTED_DATA_FILE, for verification by the child
65 process. */
67 FILE *fp = fopen (EXPECTED_DATA_FILE, "wb");
68 if (fp == NULL)
69 exit (3);
70 if (fwrite (input, 1, strlen (input), fp) != strlen (input))
71 exit (4);
72 if (fclose (fp))
73 exit (5);
76 /* Invoke the child process through system() and popen(). */
78 char command[1000];
80 sprintf (command, "%s %s", prog, output);
82 switch (interpreter)
84 case SCI_SYSTEM:
85 #ifdef WINDOWS_NATIVE
86 case SCI_WINDOWS_CMD:
87 #endif
89 int exitcode = system (command);
90 if (exitcode != 0)
92 fprintf (stderr, "for input = |%s|: system() command failed with status %d: %s\n",
93 input, exitcode, command);
94 test_exit_status = EXIT_FAILURE;
98 FILE *fp = popen (command, "r");
99 int exitcode = pclose (fp);
100 if (exitcode != 0)
102 fprintf (stderr, "for input = |%s|: popen() command failed with status %d: %s\n",
103 input, exitcode, command);
104 test_exit_status = EXIT_FAILURE;
107 break;
108 #ifdef WINDOWS_NATIVE
109 case SCI_WINDOWS_CREATEPROCESS:
111 PROCESS_INFORMATION pinfo;
112 STARTUPINFO sinfo;
113 sinfo.cb = sizeof (STARTUPINFO);
114 sinfo.lpReserved = NULL;
115 sinfo.lpDesktop = NULL;
116 sinfo.lpTitle = NULL;
117 sinfo.cbReserved2 = 0;
118 sinfo.lpReserved2 = NULL;
119 sinfo.dwFlags = STARTF_USESTDHANDLES;
120 sinfo.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
121 sinfo.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
122 sinfo.hStdError = GetStdHandle (STD_ERROR_HANDLE);
124 if (CreateProcess (NULL, command, NULL, NULL, TRUE, 0, NULL, NULL,
125 &sinfo, &pinfo))
127 DWORD exitcode;
128 CloseHandle (pinfo.hThread);
129 if (WaitForSingleObject (pinfo.hProcess, INFINITE) == WAIT_OBJECT_0)
131 if (GetExitCodeProcess (pinfo.hProcess, &exitcode))
133 if (exitcode != 0)
135 fprintf (stderr, "for input = |%s|: CreateProcess() command failed with status %d: %s\n",
136 input, exitcode, command);
137 test_exit_status = EXIT_FAILURE;
140 else
142 fprintf (stderr, "for input = |%s|: GetExitCodeProcess failed, GetLastError() = %u\n",
143 input, GetLastError ());
144 test_exit_status = EXIT_FAILURE;
147 else
149 fprintf (stderr, "for input = |%s|: WaitForSingleObject failed\n",
150 input);
151 test_exit_status = EXIT_FAILURE;
153 CloseHandle (pinfo.hProcess);
155 else
157 fprintf (stderr, "for input = |%s|: CreateProcess failed, GetLastError() = %u\n",
158 input, GetLastError ());
159 test_exit_status = EXIT_FAILURE;
162 break;
163 #endif
164 default:
165 break;
169 free (output);
172 static void
173 check_all (enum system_command_interpreter interpreter,
174 bool windows_cmd_limitations,
175 const char *prog)
177 /* Check the system_quote_length, system_quote_copy, system_quote
178 functions. */
180 int c;
182 /* Empty argument. */
183 check_one (interpreter, prog, "");
185 /* Identifier or number. */
186 check_one (interpreter, prog, "foo");
187 check_one (interpreter, prog, "phr0ck");
189 /* Whitespace would be interpreted as argument separator by the shell. */
190 check_one (interpreter, prog, "foo\tbar");
191 if (!windows_cmd_limitations)
193 check_one (interpreter, prog, "foo\nbar");
194 check_one (interpreter, prog, "foo\rbar");
196 check_one (interpreter, prog, "foo bar");
198 /* '!' at the beginning of argv[0] would introduce a negated command. */
199 check_one (interpreter, prog, "!foo");
201 /* '"' would be interpreted as the start of a string. */
202 check_one (interpreter, prog, "\"foo\"bar");
204 /* '#' at the beginning of an argument would be interpreted as the start
205 of a comment. */
206 check_one (interpreter, prog, "#foo");
208 /* '$' at the beginning of an argument would be interpreted as a variable
209 reference. */
210 check_one (interpreter, prog, "$foo");
212 /* '&' at the beginning of an argument would be interpreted as a background
213 task indicator. */
214 check_one (interpreter, prog, "&");
216 /* "'" would be interpreted as the start of a string. */
217 check_one (interpreter, prog, "'foo'bar");
219 /* '(' at the beginning of argv[0] would introduce a subshell command. */
220 check_one (interpreter, prog, "(");
222 /* ')' at the beginning of an argument would be interpreted as the end of
223 the command. */
224 check_one (interpreter, prog, ")");
226 /* '*' would be interpreted as a wildcard character. */
227 check_one (interpreter, prog, "*");
228 check_one (interpreter, prog, "*foo");
230 /* ';' at the beginning of an argument would be interpreted as an empty
231 statement in argv[0] and as the end of the command otherwise. */
232 check_one (interpreter, prog, ";");
233 check_one (interpreter, prog, "foo;");
235 /* '<' would be interpreted as a redirection of stdin. */
236 check_one (interpreter, prog, "<");
238 /* '=' inside argv[0] would be interpreted as an environment variable
239 assignment. */
240 check_one (interpreter, prog, "foo=bar");
242 /* '>' would be interpreted as a redirection of stdout. */
243 check_one (interpreter, prog, ">");
245 /* '?' would be interpreted as a wildcard character. */
246 check_one (interpreter, prog, "?");
247 check_one (interpreter, prog, "??");
248 check_one (interpreter, prog, "???");
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, "foo?bar");
258 /* '^' would be interpreted in old /bin/sh, e.g. SunOS 4.1.4. */
259 check_one (interpreter, prog, "^");
261 /* "[...]" would be interpreted as a wildcard pattern. */
262 check_one (interpreter, prog, "[");
263 check_one (interpreter, prog, "]");
265 /* '\' would be interpreted as an escape character. */
266 check_one (interpreter, prog, "\\foo");
268 /* '`' would be interpreted as the start of a command substitution. */
269 check_one (interpreter, prog, "`foo");
271 /* '{' at the beginning of argv[0] would introduce a complex command. */
272 check_one (interpreter, prog, "{");
274 /* '|' at the beginning of an argument would be interpreted as a pipe
275 between commands. */
276 check_one (interpreter, prog, "|");
278 /* '}' at the beginning of an argument would be interpreted as the end of
279 the command. */
280 check_one (interpreter, prog, "}");
282 /* '~' at the beginning of an argument would be interpreted as a reference
283 to a user's home directory. */
284 check_one (interpreter, prog, "~");
285 check_one (interpreter, prog, "~foo");
287 /* A string that contains both ' and ". */
288 check_one (interpreter, prog, "foo'bar\"baz");
290 /* '%' is used for environment variable references in Windows cmd.exe. */
291 check_one (interpreter, prog, "%");
292 check_one (interpreter, prog, "%%");
293 check_one (interpreter, prog, "%foo%");
294 check_one (interpreter, prog, "%PATH%");
296 /* All other characters don't need quoting. */
297 for (c = 1; c <= UCHAR_MAX; c++)
298 if (strchr ("\t\n\r !\"#$&'()*;<=>?^[\\]`{|}~", c) == NULL)
300 char s[5];
301 s[0] = 'a';
302 s[1] = (char) c;
303 s[2] = 'z';
304 s[3] = (char) c;
305 s[4] = '\0';
307 check_one (interpreter, prog, s);
313 main (int argc, char *argv[])
315 char *prog;
317 if (argc != 2)
319 fprintf (stderr, "%s: need 1 argument\n", argv[0]);
320 return 2;
322 prog = argv[1];
324 #ifdef WINDOWS_NATIVE
325 /* Make PROG suitable for native Windows system calls and cmd.exe:
326 Replace '/' with '\\'. */
328 char *p;
329 for (p = prog; *p != '\0'; p++)
330 if (*p == '/')
331 *p = '\\';
333 #endif
335 #ifdef WINDOWS_NATIVE
336 check_all (SCI_SYSTEM, true, prog); /* equivalent to SCI_WINDOWS_CMD */
337 check_all (SCI_WINDOWS_CREATEPROCESS, false, prog);
338 check_all (SCI_WINDOWS_CMD, true, prog);
339 #else
340 check_all (SCI_SYSTEM, false, prog); /* equivalent to SCI_POSIX_SH */
341 #endif
343 /* Clean up. */
344 unlink (EXPECTED_DATA_FILE);
346 return test_exit_status;