diff options
author | Bruno Haible <bruno@clisp.org> | 2012-05-09 03:37:24 +0200 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2012-05-09 03:37:24 +0200 |
commit | 83142d08d8fa02a5e133f4b5c0f989584c0427bb (patch) | |
tree | 612cfbefa9fb4a11d97cc72623900cb077d7df6d /lib/system-quote.c | |
parent | e2c7c52c7cbc6299ccd66ef1b9568333266918f4 (diff) | |
download | gnulib-83142d08d8fa02a5e133f4b5c0f989584c0427bb.tar.gz |
New module 'system-quote'.
* lib/system-quote.h: New file.
* lib/system-quote.c: New file.
* modules/system-quote: New file.
Diffstat (limited to 'lib/system-quote.c')
-rw-r--r-- | lib/system-quote.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/lib/system-quote.c b/lib/system-quote.c new file mode 100644 index 0000000000..facbea6db1 --- /dev/null +++ b/lib/system-quote.c @@ -0,0 +1,316 @@ +/* Quoting for a system command. + Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Specification. */ +#include "system-quote.h" + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include "sh-quote.h" +#include "xalloc.h" + +#if (defined _WIN32 || 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 or lead to a failure with error code + ERROR_PATH_NOT_FOUND. + */ +# 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" +/* 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 + <http://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" +#endif + +size_t +system_quote_length (enum system_command_interpreter interpreter, + const char *string) +{ + switch (interpreter) + { +#if !((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__) + case SCI_SYSTEM: +#endif + case SCI_POSIX_SH: + return shell_quote_length (string); + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + case SCI_WINDOWS_CREATEPROCESS: + { + size_t len = strlen (string); + bool quote_around = + (len == 0 || strpbrk (string, SHELL_SPECIAL_CHARS) != NULL); + size_t backslashes = 0; + size_t length = len; + + if (quote_around) + length++; + for (; len > 0; string++, len--) + { + char c = *string; + + if (c == '"') + length += backslashes + 1; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + return length; + } + + case SCI_SYSTEM: + case SCI_WINDOWS_CMD: + { + size_t len = strlen (string); + bool quote_around = + (len == 0 || strpbrk (string, CMD_SPECIAL_CHARS) != NULL); + size_t backslashes = 0; + size_t length = len; + + if (quote_around) + length++; + for (; len > 0; string++, len--) + { + char c = *string; + + if (c == '"') + length += backslashes + 1; + if (c == '%') + length += backslashes + 2; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + return length; + } +#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 __WIN32__) && ! defined __CYGWIN__) + case SCI_SYSTEM: +#endif + case SCI_POSIX_SH: + return shell_quote_copy (p, string); + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + case SCI_WINDOWS_CREATEPROCESS: + { + size_t len = strlen (string); + bool quote_around = + (len == 0 || strpbrk (string, SHELL_SPECIAL_CHARS) != NULL); + size_t backslashes = 0; + + if (quote_around) + *p++ = '"'; + for (; len > 0; string++, len--) + { + char c = *string; + + if (c == '"') + { + size_t j; + + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + size_t j; + + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + return p; + } + + case SCI_SYSTEM: + case SCI_WINDOWS_CMD: + { + size_t len = strlen (string); + bool quote_around = + (len == 0 || strpbrk (string, CMD_SPECIAL_CHARS) != NULL); + size_t backslashes = 0; + + if (quote_around) + *p++ = '"'; + for (; len > 0; string++, len--) + { + char c = *string; + + if (c == '"') + { + size_t j; + + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + if (c == '%') + { + size_t j; + + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p++ = c; + if (c == '%') + *p++ = '"'; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + size_t j; + + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *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 __WIN32__) && ! defined __CYGWIN__) + case SCI_SYSTEM: +#endif + case SCI_POSIX_SH: + return shell_quote (string); + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + case SCI_WINDOWS_CREATEPROCESS: + case SCI_SYSTEM: + case SCI_WINDOWS_CMD: + { + size_t length = system_quote_length (interpreter, string); + 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 (""); +} |