1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
|
/* Quoting for a system command.
Copyright (C) 2012-2023 Free Software Foundation, Inc.
Written by Bruno Haible <bruno@clisp.org>, 2012.
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 of the License, 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 <https://www.gnu.org/licenses/>. */
#include <config.h>
/* Specification. */
#include "system-quote.h"
#include <stdlib.h>
#include <string.h>
#include "sh-quote.h"
#include "xalloc.h"
#if defined _WIN32 && ! defined __CYGWIN__
/* The native Windows CreateProcess() function interprets characters like
' ', '\t', '\\', '"' (but not '<' and '>') in a special way:
- Space and tab are interpreted as delimiters. They are not treated as
delimiters if they are surrounded by double quotes: "...".
- Unescaped double quotes are removed from the input. Their only effect is
that within double quotes, space and tab are treated like normal
characters.
- Backslashes not followed by double quotes are not special.
- But 2*n+1 backslashes followed by a double quote become
n backslashes followed by a double quote (n >= 0):
\" -> "
\\\" -> \"
\\\\\" -> \\"
- '*', '?' characters may get expanded through wildcard expansion in the
callee: By default, in the callee, the initialization code before main()
takes the result of GetCommandLine(), wildcard-expands it, and passes it
to main(). The exceptions to this rule are:
- programs that inspect GetCommandLine() and ignore argv,
- mingw programs that have a global variable 'int _CRT_glob = 0;',
- Cygwin programs, when invoked from a Cygwin program.
*/
# 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*?"
# 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"
/* Copies the quoted string to p and returns the number of bytes needed.
If p is non-NULL, there must be room for system_quote_length (string)
bytes at p. */
static size_t
windows_createprocess_quote (char *p, const char *string)
{
size_t len = strlen (string);
bool quote_around =
(len == 0 || strpbrk (string, SHELL_SPECIAL_CHARS) != NULL);
size_t backslashes = 0;
size_t i = 0;
# define STORE(c) \
do \
{ \
if (p != NULL) \
p[i] = (c); \
i++; \
} \
while (0)
if (quote_around)
STORE ('"');
for (; len > 0; string++, len--)
{
char c = *string;
if (c == '"')
{
size_t j;
for (j = backslashes + 1; j > 0; j--)
STORE ('\\');
}
STORE (c);
if (c == '\\')
backslashes++;
else
backslashes = 0;
}
if (quote_around)
{
size_t j;
for (j = backslashes; j > 0; j--)
STORE ('\\');
STORE ('"');
}
# undef STORE
return i;
}
/* The native Windows cmd.exe command interpreter also interprets:
- '\n', '\r' as a command terminator - no way to escape it,
- '<', '>' as redirections,
- '|' as pipe operator,
- '%var%' as a reference to the environment variable VAR (uppercase),
even inside quoted strings,
- '&' '[' ']' '{' '}' '^' '=' ';' '!' '\'' '+' ',' '`' '~' for other
purposes, according to
<https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cmd.mspx?mfr=true>
We quote a string like '%var%' by putting the '%' characters outside of
double-quotes and the rest of the string inside double-quotes: %"var"%.
This is guaranteed to not be a reference to an environment variable.
*/
# 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!%&'*+,;<=>?[]^`{|}~"
# define CMD_FORBIDDEN_CHARS "\n\r"
/* Copies the quoted string to p and returns the number of bytes needed.
If p is non-NULL, there must be room for system_quote_length (string)
bytes at p. */
static size_t
windows_cmd_quote (char *p, const char *string)
{
size_t len = strlen (string);
bool quote_around =
(len == 0 || strpbrk (string, CMD_SPECIAL_CHARS) != NULL);
size_t backslashes = 0;
size_t i = 0;
# define STORE(c) \
do \
{ \
if (p != NULL) \
p[i] = (c); \
i++; \
} \
while (0)
if (quote_around)
STORE ('"');
for (; len > 0; string++, len--)
{
char c = *string;
if (c == '"')
{
size_t j;
for (j = backslashes + 1; j > 0; j--)
STORE ('\\');
}
if (c == '%')
{
size_t j;
for (j = backslashes; j > 0; j--)
STORE ('\\');
STORE ('"');
}
STORE (c);
if (c == '%')
STORE ('"');
if (c == '\\')
backslashes++;
else
backslashes = 0;
}
if (quote_around)
{
size_t j;
for (j = backslashes; j > 0; j--)
STORE ('\\');
STORE ('"');
}
return i;
}
#endif
size_t
system_quote_length (enum system_command_interpreter interpreter,
const char *string)
{
switch (interpreter)
{
#if !(defined _WIN32 && ! defined __CYGWIN__)
case SCI_SYSTEM:
#endif
case SCI_POSIX_SH:
return shell_quote_length (string);
#if defined _WIN32 && ! defined __CYGWIN__
case SCI_WINDOWS_CREATEPROCESS:
return windows_createprocess_quote (NULL, string);
case SCI_SYSTEM:
case SCI_WINDOWS_CMD:
return windows_cmd_quote (NULL, string);
#endif
default:
/* Invalid interpreter. */
abort ();
}
}
char *
system_quote_copy (char *p,
enum system_command_interpreter interpreter,
const char *string)
{
switch (interpreter)
{
#if !(defined _WIN32 && ! defined __CYGWIN__)
case SCI_SYSTEM:
#endif
case SCI_POSIX_SH:
return shell_quote_copy (p, string);
#if defined _WIN32 && ! defined __CYGWIN__
case SCI_WINDOWS_CREATEPROCESS:
p += windows_createprocess_quote (p, string);
*p = '\0';
return p;
case SCI_SYSTEM:
case SCI_WINDOWS_CMD:
p += windows_cmd_quote (p, string);
*p = '\0';
return p;
#endif
default:
/* Invalid interpreter. */
abort ();
}
}
char *
system_quote (enum system_command_interpreter interpreter,
const char *string)
{
switch (interpreter)
{
#if !(defined _WIN32 && ! defined __CYGWIN__)
case SCI_SYSTEM:
#endif
case SCI_POSIX_SH:
return shell_quote (string);
#if defined _WIN32 && ! defined __CYGWIN__
case SCI_WINDOWS_CREATEPROCESS:
case SCI_SYSTEM:
case SCI_WINDOWS_CMD:
{
size_t length = system_quote_length (interpreter, string) + 1;
char *quoted = XNMALLOC (length, char);
system_quote_copy (quoted, interpreter, string);
return quoted;
}
#endif
default:
/* Invalid interpreter. */
abort ();
}
}
char *
system_quote_argv (enum system_command_interpreter interpreter,
char * const *argv)
{
if (*argv != NULL)
{
char * const *argp;
size_t length;
char *command;
char *p;
length = 0;
for (argp = argv; ; )
{
length += system_quote_length (interpreter, *argp) + 1;
argp++;
if (*argp == NULL)
break;
}
command = XNMALLOC (length, char);
p = command;
for (argp = argv; ; )
{
p = system_quote_copy (p, interpreter, *argp);
argp++;
if (*argp == NULL)
break;
*p++ = ' ';
}
*p = '\0';
return command;
}
else
return xstrdup ("");
}
|