mirror of
https://git.savannah.gnu.org/git/gnulib.git
synced 2025-08-16 01:22:18 +03:00
* build-aux/gendocs.sh (version): * doc/gendocs_template: * doc/gendocs_template_min: * doc/gnulib.texi: * lib/version-etc.c (COPYRIGHT_YEAR): Update copyright dates by hand in templates and the like. * all files: Run 'make update-copyright'.
351 lines
11 KiB
C
351 lines
11 KiB
C
/* 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 <http://www.gnu.org/licenses/>. */
|
|
|
|
/* Written by Bruno Haible <bruno@clisp.org>, 2012. */
|
|
|
|
#include <config.h>
|
|
|
|
/* Specification. */
|
|
#include "system-quote.h"
|
|
|
|
#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
|
|
# define WINDOWS_NATIVE
|
|
#endif
|
|
|
|
#include <limits.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#ifdef WINDOWS_NATIVE
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# include <windows.h>
|
|
#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;
|
|
}
|