/* Test of system-quote module. Copyright (C) 2012-2017 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ /* Written by Bruno Haible , 2012. */ #include /* Specification. */ #include "system-quote.h" #if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__ # define WINDOWS_NATIVE #endif #include #include #include #include #include #include #ifdef WINDOWS_NATIVE # define WIN32_LEAN_AND_MEAN # include #endif #include "macros.h" #define EXPECTED_DATA_FILE "t-sq-data.tmp" static int failed; static void check_one (enum system_command_interpreter interpreter, const char *prog, const char *input) { char buf[1000]; size_t output_len; char *output; char *bufend; output_len = system_quote_length (interpreter, input); output = system_quote (interpreter, input); ASSERT (strlen (output) == output_len); ASSERT (output_len <= sizeof (buf) - 2); memset (buf, '\0', output_len + 1); buf[output_len + 1] = '%'; bufend = system_quote_copy (buf, interpreter, input); ASSERT (bufend == buf + output_len); ASSERT (memcmp (buf, output, output_len + 1) == 0); ASSERT (buf[output_len + 1] == '%'); /* Store INPUT in EXPECTED_DATA_FILE, for verification by the child process. */ { FILE *fp = fopen (EXPECTED_DATA_FILE, "wb"); if (fp == NULL) exit (3); if (fwrite (input, 1, strlen (input), fp) != strlen (input)) exit (4); if (fclose (fp)) exit (5); } /* Invoke the child process through system() and popen(). */ { char command[1000]; sprintf (command, "%s %s", prog, output); switch (interpreter) { case SCI_SYSTEM: #ifdef WINDOWS_NATIVE case SCI_WINDOWS_CMD: #endif { int exitcode = system (command); if (exitcode != 0) { fprintf (stderr, "for input = |%s|: system() command failed with status %d: %s\n", input, exitcode, command); failed = 1; } } { FILE *fp = popen (command, "r"); int exitcode = pclose (fp); if (exitcode != 0) { fprintf (stderr, "for input = |%s|: popen() command failed with status %d: %s\n", input, exitcode, command); failed = 1; } } break; #ifdef WINDOWS_NATIVE case SCI_WINDOWS_CREATEPROCESS: { PROCESS_INFORMATION pinfo; STARTUPINFO sinfo; sinfo.cb = sizeof (STARTUPINFO); sinfo.lpReserved = NULL; sinfo.lpDesktop = NULL; sinfo.lpTitle = NULL; sinfo.cbReserved2 = 0; sinfo.lpReserved2 = NULL; sinfo.dwFlags = STARTF_USESTDHANDLES; sinfo.hStdInput = GetStdHandle (STD_INPUT_HANDLE); sinfo.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); sinfo.hStdError = GetStdHandle (STD_ERROR_HANDLE); if (CreateProcess (NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &sinfo, &pinfo)) { DWORD exitcode; CloseHandle (pinfo.hThread); if (WaitForSingleObject (pinfo.hProcess, INFINITE) == WAIT_OBJECT_0) { if (GetExitCodeProcess (pinfo.hProcess, &exitcode)) { if (exitcode != 0) { fprintf (stderr, "for input = |%s|: CreateProcess() command failed with status %d: %s\n", input, exitcode, command); failed = 1; } } else { fprintf (stderr, "for input = |%s|: GetExitCodeProcess failed, GetLastError() = %u\n", input, GetLastError ()); failed = 1; } } else { fprintf (stderr, "for input = |%s|: WaitForSingleObject failed\n", input); failed = 1; } CloseHandle (pinfo.hProcess); } else { fprintf (stderr, "for input = |%s|: CreateProcess failed, GetLastError() = %u\n", input, GetLastError ()); failed = 1; } } break; #endif default: break; } } free (output); } static void check_all (enum system_command_interpreter interpreter, bool windows_cmd_limitations, const char *prog) { /* Check the system_quote_length, system_quote_copy, system_quote functions. */ { int c; /* Empty argument. */ check_one (interpreter, prog, ""); /* Identifier or number. */ check_one (interpreter, prog, "foo"); check_one (interpreter, prog, "phr0ck"); /* Whitespace would be interpreted as argument separator by the shell. */ check_one (interpreter, prog, "foo\tbar"); if (!windows_cmd_limitations) { check_one (interpreter, prog, "foo\nbar"); check_one (interpreter, prog, "foo\rbar"); } check_one (interpreter, prog, "foo bar"); /* '!' at the beginning of argv[0] would introduce a negated command. */ check_one (interpreter, prog, "!foo"); /* '"' would be interpreted as the start of a string. */ check_one (interpreter, prog, "\"foo\"bar"); /* '#' at the beginning of an argument would be interpreted as the start of a comment. */ check_one (interpreter, prog, "#foo"); /* '$' at the beginning of an argument would be interpreted as a variable reference. */ check_one (interpreter, prog, "$foo"); /* '&' at the beginning of an argument would be interpreted as a background task indicator. */ check_one (interpreter, prog, "&"); /* "'" would be interpreted as the start of a string. */ check_one (interpreter, prog, "'foo'bar"); /* '(' at the beginning of argv[0] would introduce a subshell command. */ check_one (interpreter, prog, "("); /* ')' at the beginning of an argument would be interpreted as the end of the command. */ check_one (interpreter, prog, ")"); /* '*' would be interpreted as a wildcard character. */ check_one (interpreter, prog, "*"); check_one (interpreter, prog, "*foo"); /* ';' at the beginning of an argument would be interpreted as an empty statement in argv[0] and as the end of the command otherwise. */ check_one (interpreter, prog, ";"); check_one (interpreter, prog, "foo;"); /* '<' would be interpreted as a redirection of stdin. */ check_one (interpreter, prog, "<"); /* '=' inside argv[0] would be interpreted as an environment variable assignment. */ check_one (interpreter, prog, "foo=bar"); /* '>' would be interpreted as a redirection of stdout. */ check_one (interpreter, prog, ">"); /* '?' would be interpreted as a wildcard character. */ check_one (interpreter, prog, "?"); check_one (interpreter, prog, "??"); check_one (interpreter, prog, "???"); check_one (interpreter, prog, "????"); check_one (interpreter, prog, "?????"); check_one (interpreter, prog, "??????"); check_one (interpreter, prog, "???????"); check_one (interpreter, prog, "????????"); check_one (interpreter, prog, "?????????"); check_one (interpreter, prog, "??????????"); check_one (interpreter, prog, "foo?bar"); /* '^' would be interpreted in old /bin/sh, e.g. SunOS 4.1.4. */ check_one (interpreter, prog, "^"); /* "[...]" would be interpreted as a wildcard pattern. */ check_one (interpreter, prog, "["); check_one (interpreter, prog, "]"); /* '\' would be interpreted as an escape character. */ check_one (interpreter, prog, "\\foo"); /* '`' would be interpreted as the start of a command substitution. */ check_one (interpreter, prog, "`foo"); /* '{' at the beginning of argv[0] would introduce a complex command. */ check_one (interpreter, prog, "{"); /* '|' at the beginning of an argument would be interpreted as a pipe between commands. */ check_one (interpreter, prog, "|"); /* '}' at the beginning of an argument would be interpreted as the end of the command. */ check_one (interpreter, prog, "}"); /* '~' at the beginning of an argument would be interpreted as a reference to a user's home directory. */ check_one (interpreter, prog, "~"); check_one (interpreter, prog, "~foo"); /* A string that contains both ' and ". */ check_one (interpreter, prog, "foo'bar\"baz"); /* '%' is used for environment variable references in Windows cmd.exe. */ check_one (interpreter, prog, "%"); check_one (interpreter, prog, "%%"); check_one (interpreter, prog, "%foo%"); check_one (interpreter, prog, "%PATH%"); /* All other characters don't need quoting. */ for (c = 1; c <= UCHAR_MAX; c++) if (strchr ("\t\n\r !\"#$&'()*;<=>?^[\\]`{|}~", c) == NULL) { char s[5]; s[0] = 'a'; s[1] = (char) c; s[2] = 'z'; s[3] = (char) c; s[4] = '\0'; check_one (interpreter, prog, s); } } } int main (int argc, char *argv[]) { char *prog; if (argc != 2) { fprintf (stderr, "%s: need 1 argument\n", argv[0]); return 2; } prog = argv[1]; #ifdef WINDOWS_NATIVE /* Make PROG suitable for native Windows system calls and cmd.exe: Replace '/' with '\\'. */ { char *p; for (p = prog; *p != '\0'; p++) if (*p == '/') *p = '\\'; } #endif #ifdef WINDOWS_NATIVE check_all (SCI_SYSTEM, true, prog); /* equivalent to SCI_WINDOWS_CMD */ check_all (SCI_WINDOWS_CREATEPROCESS, false, prog); check_all (SCI_WINDOWS_CMD, true, prog); #else check_all (SCI_SYSTEM, false, prog); /* equivalent to SCI_POSIX_SH */ #endif /* Clean up. */ unlink (EXPECTED_DATA_FILE); return failed; }