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)
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. */
22 #include "system-quote.h"
24 #if defined _WIN32 && !defined __CYGWIN__
25 # define WINDOWS_NATIVE
34 # define WIN32_LEAN_AND_MEAN
40 #define EXPECTED_DATA_FILE "t-sq-data.tmp"
43 check_one (enum system_command_interpreter interpreter
, const char *prog
,
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
67 FILE *fp
= fopen (EXPECTED_DATA_FILE
, "wb");
70 if (fwrite (input
, 1, strlen (input
), fp
) != strlen (input
))
76 /* Invoke the child process through system() and popen(). */
80 sprintf (command
, "%s %s", prog
, output
);
89 int exitcode
= system (command
);
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
);
102 fprintf (stderr
, "for input = |%s|: popen() command failed with status %d: %s\n",
103 input
, exitcode
, command
);
104 test_exit_status
= EXIT_FAILURE
;
108 #ifdef WINDOWS_NATIVE
109 case SCI_WINDOWS_CREATEPROCESS
:
111 PROCESS_INFORMATION pinfo
;
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
,
128 CloseHandle (pinfo
.hThread
);
129 if (WaitForSingleObject (pinfo
.hProcess
, INFINITE
) == WAIT_OBJECT_0
)
131 if (GetExitCodeProcess (pinfo
.hProcess
, &exitcode
))
135 fprintf (stderr
, "for input = |%s|: CreateProcess() command failed with status %d: %s\n",
136 input
, exitcode
, command
);
137 test_exit_status
= EXIT_FAILURE
;
142 fprintf (stderr
, "for input = |%s|: GetExitCodeProcess failed, GetLastError() = %u\n",
143 input
, GetLastError ());
144 test_exit_status
= EXIT_FAILURE
;
149 fprintf (stderr
, "for input = |%s|: WaitForSingleObject failed\n",
151 test_exit_status
= EXIT_FAILURE
;
153 CloseHandle (pinfo
.hProcess
);
157 fprintf (stderr
, "for input = |%s|: CreateProcess failed, GetLastError() = %u\n",
158 input
, GetLastError ());
159 test_exit_status
= EXIT_FAILURE
;
173 check_all (enum system_command_interpreter interpreter
,
174 bool windows_cmd_limitations
,
177 /* Check the system_quote_length, system_quote_copy, system_quote
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
206 check_one (interpreter
, prog
, "#foo");
208 /* '$' at the beginning of an argument would be interpreted as a variable
210 check_one (interpreter
, prog
, "$foo");
212 /* '&' at the beginning of an argument would be interpreted as a background
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
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
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
276 check_one (interpreter
, prog
, "|");
278 /* '}' at the beginning of an argument would be interpreted as the end of
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
)
307 check_one (interpreter
, prog
, s
);
313 main (int argc
, char *argv
[])
319 fprintf (stderr
, "%s: need 1 argument\n", argv
[0]);
324 #ifdef WINDOWS_NATIVE
325 /* Make PROG suitable for native Windows system calls and cmd.exe:
326 Replace '/' with '\\'. */
329 for (p
= prog
; *p
!= '\0'; p
++)
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
);
340 check_all (SCI_SYSTEM
, false, prog
); /* equivalent to SCI_POSIX_SH */
344 unlink (EXPECTED_DATA_FILE
);
346 return test_exit_status
;