|
Packit |
33f14e |
/* Quoting for a system command.
|
|
Packit |
33f14e |
Copyright (C) 2012-2017 Free Software Foundation, Inc.
|
|
Packit |
33f14e |
Written by Bruno Haible <bruno@clisp.org>, 2012.
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
This program is free software: you can redistribute it and/or modify
|
|
Packit |
33f14e |
it under the terms of the GNU General Public License as published by
|
|
Packit |
33f14e |
the Free Software Foundation; either version 3 of the License, or
|
|
Packit |
33f14e |
(at your option) any later version.
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
This program is distributed in the hope that it will be useful,
|
|
Packit |
33f14e |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
33f14e |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
33f14e |
GNU General Public License for more details.
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
You should have received a copy of the GNU General Public License
|
|
Packit |
33f14e |
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
#include <config.h>
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
/* Specification. */
|
|
Packit |
33f14e |
#include "system-quote.h"
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
#include <stdbool.h>
|
|
Packit |
33f14e |
#include <stdlib.h>
|
|
Packit |
33f14e |
#include <string.h>
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
#include "sh-quote.h"
|
|
Packit |
33f14e |
#include "xalloc.h"
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
/* The native Windows CreateProcess() function interprets characters like
|
|
Packit |
33f14e |
' ', '\t', '\\', '"' (but not '<' and '>') in a special way:
|
|
Packit |
33f14e |
- Space and tab are interpreted as delimiters. They are not treated as
|
|
Packit |
33f14e |
delimiters if they are surrounded by double quotes: "...".
|
|
Packit |
33f14e |
- Unescaped double quotes are removed from the input. Their only effect is
|
|
Packit |
33f14e |
that within double quotes, space and tab are treated like normal
|
|
Packit |
33f14e |
characters.
|
|
Packit |
33f14e |
- Backslashes not followed by double quotes are not special.
|
|
Packit |
33f14e |
- But 2*n+1 backslashes followed by a double quote become
|
|
Packit |
33f14e |
n backslashes followed by a double quote (n >= 0):
|
|
Packit |
33f14e |
\" -> "
|
|
Packit |
33f14e |
\\\" -> \"
|
|
Packit |
33f14e |
\\\\\" -> \\"
|
|
Packit |
33f14e |
- '*', '?' characters may get expanded through wildcard expansion in the
|
|
Packit |
33f14e |
callee: By default, in the callee, the initialization code before main()
|
|
Packit |
33f14e |
takes the result of GetCommandLine(), wildcard-expands it, and passes it
|
|
Packit |
33f14e |
to main(). The exceptions to this rule are:
|
|
Packit |
33f14e |
- programs that inspect GetCommandLine() and ignore argv,
|
|
Packit |
33f14e |
- mingw programs that have a global variable 'int _CRT_glob = 0;',
|
|
Packit |
33f14e |
- Cygwin programs, when invoked from a Cygwin program.
|
|
Packit |
33f14e |
*/
|
|
Packit |
33f14e |
# define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037*?"
|
|
Packit |
33f14e |
# define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
/* Copies the quoted string to p and returns the number of bytes needed.
|
|
Packit |
33f14e |
If p is non-NULL, there must be room for system_quote_length (string)
|
|
Packit |
33f14e |
bytes at p. */
|
|
Packit |
33f14e |
static size_t
|
|
Packit |
33f14e |
windows_createprocess_quote (char *p, const char *string)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
size_t len = strlen (string);
|
|
Packit |
33f14e |
bool quote_around =
|
|
Packit |
33f14e |
(len == 0 || strpbrk (string, SHELL_SPECIAL_CHARS) != NULL);
|
|
Packit |
33f14e |
size_t backslashes = 0;
|
|
Packit |
33f14e |
size_t i = 0;
|
|
Packit |
33f14e |
# define STORE(c) \
|
|
Packit |
33f14e |
do \
|
|
Packit |
33f14e |
{ \
|
|
Packit |
33f14e |
if (p != NULL) \
|
|
Packit |
33f14e |
p[i] = (c); \
|
|
Packit |
33f14e |
i++; \
|
|
Packit |
33f14e |
} \
|
|
Packit |
33f14e |
while (0)
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
if (quote_around)
|
|
Packit |
33f14e |
STORE ('"');
|
|
Packit |
33f14e |
for (; len > 0; string++, len--)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
char c = *string;
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
if (c == '"')
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
size_t j;
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
for (j = backslashes + 1; j > 0; j--)
|
|
Packit |
33f14e |
STORE ('\\');
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
STORE (c);
|
|
Packit |
33f14e |
if (c == '\\')
|
|
Packit |
33f14e |
backslashes++;
|
|
Packit |
33f14e |
else
|
|
Packit |
33f14e |
backslashes = 0;
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
if (quote_around)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
size_t j;
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
for (j = backslashes; j > 0; j--)
|
|
Packit |
33f14e |
STORE ('\\');
|
|
Packit |
33f14e |
STORE ('"');
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
# undef STORE
|
|
Packit |
33f14e |
return i;
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
/* The native Windows cmd.exe command interpreter also interprets:
|
|
Packit |
33f14e |
- '\n', '\r' as a command terminator - no way to escape it,
|
|
Packit |
33f14e |
- '<', '>' as redirections,
|
|
Packit |
33f14e |
- '|' as pipe operator,
|
|
Packit |
33f14e |
- '%var%' as a reference to the environment variable VAR (uppercase),
|
|
Packit |
33f14e |
even inside quoted strings,
|
|
Packit |
33f14e |
- '&' '[' ']' '{' '}' '^' '=' ';' '!' '\'' '+' ',' '`' '~' for other
|
|
Packit |
33f14e |
purposes, according to
|
|
Packit |
33f14e |
<http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cmd.mspx?mfr=true>
|
|
Packit |
33f14e |
We quote a string like '%var%' by putting the '%' characters outside of
|
|
Packit |
33f14e |
double-quotes and the rest of the string inside double-quotes: %"var"%.
|
|
Packit |
33f14e |
This is guaranteed to not be a reference to an environment variable.
|
|
Packit |
33f14e |
*/
|
|
Packit |
33f14e |
# define CMD_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037!%&'*+,;<=>?[]^`{|}~"
|
|
Packit |
33f14e |
# define CMD_FORBIDDEN_CHARS "\n\r"
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
/* Copies the quoted string to p and returns the number of bytes needed.
|
|
Packit |
33f14e |
If p is non-NULL, there must be room for system_quote_length (string)
|
|
Packit |
33f14e |
bytes at p. */
|
|
Packit |
33f14e |
static size_t
|
|
Packit |
33f14e |
windows_cmd_quote (char *p, const char *string)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
size_t len = strlen (string);
|
|
Packit |
33f14e |
bool quote_around =
|
|
Packit |
33f14e |
(len == 0 || strpbrk (string, CMD_SPECIAL_CHARS) != NULL);
|
|
Packit |
33f14e |
size_t backslashes = 0;
|
|
Packit |
33f14e |
size_t i = 0;
|
|
Packit |
33f14e |
# define STORE(c) \
|
|
Packit |
33f14e |
do \
|
|
Packit |
33f14e |
{ \
|
|
Packit |
33f14e |
if (p != NULL) \
|
|
Packit |
33f14e |
p[i] = (c); \
|
|
Packit |
33f14e |
i++; \
|
|
Packit |
33f14e |
} \
|
|
Packit |
33f14e |
while (0)
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
if (quote_around)
|
|
Packit |
33f14e |
STORE ('"');
|
|
Packit |
33f14e |
for (; len > 0; string++, len--)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
char c = *string;
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
if (c == '"')
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
size_t j;
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
for (j = backslashes + 1; j > 0; j--)
|
|
Packit |
33f14e |
STORE ('\\');
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
if (c == '%')
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
size_t j;
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
for (j = backslashes; j > 0; j--)
|
|
Packit |
33f14e |
STORE ('\\');
|
|
Packit |
33f14e |
STORE ('"');
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
STORE (c);
|
|
Packit |
33f14e |
if (c == '%')
|
|
Packit |
33f14e |
STORE ('"');
|
|
Packit |
33f14e |
if (c == '\\')
|
|
Packit |
33f14e |
backslashes++;
|
|
Packit |
33f14e |
else
|
|
Packit |
33f14e |
backslashes = 0;
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
if (quote_around)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
size_t j;
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
for (j = backslashes; j > 0; j--)
|
|
Packit |
33f14e |
STORE ('\\');
|
|
Packit |
33f14e |
STORE ('"');
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
return i;
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
#endif
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
size_t
|
|
Packit |
33f14e |
system_quote_length (enum system_command_interpreter interpreter,
|
|
Packit |
33f14e |
const char *string)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
switch (interpreter)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
#if !((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
|
|
Packit |
33f14e |
case SCI_SYSTEM:
|
|
Packit |
33f14e |
#endif
|
|
Packit |
33f14e |
case SCI_POSIX_SH:
|
|
Packit |
33f14e |
return shell_quote_length (string);
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
|
|
Packit |
33f14e |
case SCI_WINDOWS_CREATEPROCESS:
|
|
Packit |
33f14e |
return windows_createprocess_quote (NULL, string);
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
case SCI_SYSTEM:
|
|
Packit |
33f14e |
case SCI_WINDOWS_CMD:
|
|
Packit |
33f14e |
return windows_cmd_quote (NULL, string);
|
|
Packit |
33f14e |
#endif
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
default:
|
|
Packit |
33f14e |
/* Invalid interpreter. */
|
|
Packit |
33f14e |
abort ();
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
char *
|
|
Packit |
33f14e |
system_quote_copy (char *p,
|
|
Packit |
33f14e |
enum system_command_interpreter interpreter,
|
|
Packit |
33f14e |
const char *string)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
switch (interpreter)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
#if !((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
|
|
Packit |
33f14e |
case SCI_SYSTEM:
|
|
Packit |
33f14e |
#endif
|
|
Packit |
33f14e |
case SCI_POSIX_SH:
|
|
Packit |
33f14e |
return shell_quote_copy (p, string);
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
|
|
Packit |
33f14e |
case SCI_WINDOWS_CREATEPROCESS:
|
|
Packit |
33f14e |
p += windows_createprocess_quote (p, string);
|
|
Packit |
33f14e |
*p = '\0';
|
|
Packit |
33f14e |
return p;
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
case SCI_SYSTEM:
|
|
Packit |
33f14e |
case SCI_WINDOWS_CMD:
|
|
Packit |
33f14e |
p += windows_cmd_quote (p, string);
|
|
Packit |
33f14e |
*p = '\0';
|
|
Packit |
33f14e |
return p;
|
|
Packit |
33f14e |
#endif
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
default:
|
|
Packit |
33f14e |
/* Invalid interpreter. */
|
|
Packit |
33f14e |
abort ();
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
char *
|
|
Packit |
33f14e |
system_quote (enum system_command_interpreter interpreter,
|
|
Packit |
33f14e |
const char *string)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
switch (interpreter)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
#if !((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
|
|
Packit |
33f14e |
case SCI_SYSTEM:
|
|
Packit |
33f14e |
#endif
|
|
Packit |
33f14e |
case SCI_POSIX_SH:
|
|
Packit |
33f14e |
return shell_quote (string);
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
|
|
Packit |
33f14e |
case SCI_WINDOWS_CREATEPROCESS:
|
|
Packit |
33f14e |
case SCI_SYSTEM:
|
|
Packit |
33f14e |
case SCI_WINDOWS_CMD:
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
size_t length = system_quote_length (interpreter, string);
|
|
Packit |
33f14e |
char *quoted = XNMALLOC (length, char);
|
|
Packit |
33f14e |
system_quote_copy (quoted, interpreter, string);
|
|
Packit |
33f14e |
return quoted;
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
#endif
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
default:
|
|
Packit |
33f14e |
/* Invalid interpreter. */
|
|
Packit |
33f14e |
abort ();
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
char *
|
|
Packit |
33f14e |
system_quote_argv (enum system_command_interpreter interpreter,
|
|
Packit |
33f14e |
char * const *argv)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
if (*argv != NULL)
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
char * const *argp;
|
|
Packit |
33f14e |
size_t length;
|
|
Packit |
33f14e |
char *command;
|
|
Packit |
33f14e |
char *p;
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
length = 0;
|
|
Packit |
33f14e |
for (argp = argv; ; )
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
length += system_quote_length (interpreter, *argp) + 1;
|
|
Packit |
33f14e |
argp++;
|
|
Packit |
33f14e |
if (*argp == NULL)
|
|
Packit |
33f14e |
break;
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
command = XNMALLOC (length, char);
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
p = command;
|
|
Packit |
33f14e |
for (argp = argv; ; )
|
|
Packit |
33f14e |
{
|
|
Packit |
33f14e |
p = system_quote_copy (p, interpreter, *argp);
|
|
Packit |
33f14e |
argp++;
|
|
Packit |
33f14e |
if (*argp == NULL)
|
|
Packit |
33f14e |
break;
|
|
Packit |
33f14e |
*p++ = ' ';
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
*p = '\0';
|
|
Packit |
33f14e |
|
|
Packit |
33f14e |
return command;
|
|
Packit |
33f14e |
}
|
|
Packit |
33f14e |
else
|
|
Packit |
33f14e |
return xstrdup ("");
|
|
Packit |
33f14e |
}
|