1 /* Quoting for a system command.
2 Copyright (C) 2012-2025 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2012.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
21 #include "system-quote.h"
29 #if defined _WIN32 && ! defined __CYGWIN__
31 /* The native Windows CreateProcess() function interprets characters like
32 ' ', '\t', '\\', '"' (but not '<' and '>') in a special way:
33 - Space and tab are interpreted as delimiters. They are not treated as
34 delimiters if they are surrounded by double quotes: "...".
35 - Unescaped double quotes are removed from the input. Their only effect is
36 that within double quotes, space and tab are treated like normal
38 - Backslashes not followed by double quotes are not special.
39 - But 2*n+1 backslashes followed by a double quote become
40 n backslashes followed by a double quote (n >= 0):
44 - '*', '?' characters may get expanded through wildcard expansion in the
45 callee: By default, in the callee, the initialization code before main()
46 takes the result of GetCommandLine(), wildcard-expands it, and passes it
47 to main(). The exceptions to this rule are:
48 - programs that inspect GetCommandLine() and ignore argv,
49 - mingw programs that have a global variable 'int _CRT_glob = 0;',
50 - Cygwin programs, when invoked from a Cygwin program.
52 # 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*?"
53 # 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"
55 /* Copies the quoted string to p and returns the number of bytes needed.
56 If p is non-NULL, there must be room for system_quote_length (string)
59 windows_createprocess_quote (char *p
, const char *string
)
61 size_t len
= strlen (string
);
63 (len
== 0 || strpbrk (string
, SHELL_SPECIAL_CHARS
) != NULL
);
64 size_t backslashes
= 0;
77 for (; len
> 0; string
++, len
--)
85 for (j
= backslashes
+ 1; j
> 0; j
--)
98 for (j
= backslashes
; j
> 0; j
--)
106 /* The native Windows cmd.exe command interpreter also interprets:
107 - '\n', '\r' as a command terminator - no way to escape it,
108 - '<', '>' as redirections,
109 - '|' as pipe operator,
110 - '%var%' as a reference to the environment variable VAR (uppercase),
111 even inside quoted strings,
112 - '&' '[' ']' '{' '}' '^' '=' ';' '!' '\'' '+' ',' '`' '~' for other
113 purposes, according to
114 <https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cmd.mspx?mfr=true>
115 We quote a string like '%var%' by putting the '%' characters outside of
116 double-quotes and the rest of the string inside double-quotes: %"var"%.
117 This is guaranteed to not be a reference to an environment variable.
119 # 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!%&'*+,;<=>?[]^`{|}~"
120 # define CMD_FORBIDDEN_CHARS "\n\r"
122 /* Copies the quoted string to p and returns the number of bytes needed.
123 If p is non-NULL, there must be room for system_quote_length (string)
126 windows_cmd_quote (char *p
, const char *string
)
128 size_t len
= strlen (string
);
130 (len
== 0 || strpbrk (string
, CMD_SPECIAL_CHARS
) != NULL
);
131 size_t backslashes
= 0;
144 for (; len
> 0; string
++, len
--)
152 for (j
= backslashes
+ 1; j
> 0; j
--)
159 for (j
= backslashes
; j
> 0; j
--)
175 for (j
= backslashes
; j
> 0; j
--)
185 system_quote_length (enum system_command_interpreter interpreter
,
190 #if !(defined _WIN32 && ! defined __CYGWIN__)
194 return shell_quote_length (string
);
196 #if defined _WIN32 && ! defined __CYGWIN__
197 case SCI_WINDOWS_CREATEPROCESS
:
198 return windows_createprocess_quote (NULL
, string
);
201 case SCI_WINDOWS_CMD
:
202 return windows_cmd_quote (NULL
, string
);
206 /* Invalid interpreter. */
212 system_quote_copy (char *p
,
213 enum system_command_interpreter interpreter
,
218 #if !(defined _WIN32 && ! defined __CYGWIN__)
222 return shell_quote_copy (p
, string
);
224 #if defined _WIN32 && ! defined __CYGWIN__
225 case SCI_WINDOWS_CREATEPROCESS
:
226 p
+= windows_createprocess_quote (p
, string
);
231 case SCI_WINDOWS_CMD
:
232 p
+= windows_cmd_quote (p
, string
);
238 /* Invalid interpreter. */
244 system_quote (enum system_command_interpreter interpreter
,
249 #if !(defined _WIN32 && ! defined __CYGWIN__)
253 return shell_quote (string
);
255 #if defined _WIN32 && ! defined __CYGWIN__
256 case SCI_WINDOWS_CREATEPROCESS
:
258 case SCI_WINDOWS_CMD
:
260 size_t length
= system_quote_length (interpreter
, string
) + 1;
261 char *quoted
= XNMALLOC (length
, char);
262 system_quote_copy (quoted
, interpreter
, string
);
268 /* Invalid interpreter. */
274 system_quote_argv (enum system_command_interpreter interpreter
,
285 for (argp
= argv
; ; )
287 length
+= system_quote_length (interpreter
, *argp
) + 1;
293 command
= XNMALLOC (length
, char);
296 for (argp
= argv
; ; )
298 p
= system_quote_copy (p
, interpreter
, *argp
);