From: Bruno Haible Date: Wed, 9 May 2012 01:37:24 +0000 (+0200) Subject: New module 'system-quote'. X-Git-Tag: v0.1~685 X-Git-Url: http://erislabs.net/gitweb/?p=gnulib.git;a=commitdiff_plain;h=83142d08d8fa02a5e133f4b5c0f989584c0427bb New module 'system-quote'. * lib/system-quote.h: New file. * lib/system-quote.c: New file. * modules/system-quote: New file. --- diff --git a/ChangeLog b/ChangeLog index f6029666f..164bbe1ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2012-05-08 Bruno Haible + New module 'system-quote'. + * lib/system-quote.h: New file. + * lib/system-quote.c: New file. + * modules/system-quote: New file. + +2012-05-08 Bruno Haible + sh-quote: Make C++ safe and allow multiple inclusion. * lib/sh-quote.h: Add double-inclusion guard. For C++, wrap function declarations in extern "C". diff --git a/lib/system-quote.c b/lib/system-quote.c new file mode 100644 index 000000000..facbea6db --- /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 , 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 . */ + +#include + +/* Specification. */ +#include "system-quote.h" + +#include +#include +#include + +#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 + + 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 (""); +} diff --git a/lib/system-quote.h b/lib/system-quote.h new file mode 100644 index 000000000..c2c50c5a2 --- /dev/null +++ b/lib/system-quote.h @@ -0,0 +1,82 @@ +/* Quoting for a system command. + Copyright (C) 2001-2012 Free Software Foundation, Inc. + Written by Bruno Haible , 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 . */ + +#ifndef _SYSTEM_QUOTE_H +#define _SYSTEM_QUOTE_H + +/* When passing a command the system's command interpreter, we must quote the + program name and arguments, since + - Unix shells interpret characters like " ", "'", "<", ">", "$" etc. in a + special way, + - Windows CreateProcess() interprets characters like ' ', '\t', '\\', '"' + etc. (but not '<' and '>') in a special way, + - Windows cmd.exe also interprets characters like '<', '>', '&', '%', etc. + in a special way. Note that it is impossible to pass arguments that + contain newlines or carriage return characters to programs through + cmd.exe. */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Identifier for the kind of interpreter of the command. */ +enum system_command_interpreter +{ + /* The interpreter used by the system() and popen() functions. + This is equivalent to SCI_POSIX_SH on Unix platforms and + SCI_WINDOWS_CMD on native Windows platforms. */ + SCI_SYSTEM = 0 + /* The POSIX /bin/sh. */ + , SCI_POSIX_SH = 1 +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + /* The native Windows CreateProcess() function. */ + , SCI_WINDOWS_CREATEPROCESS = 2 + /* The native Windows cmd.exe interpreter. */ + , SCI_WINDOWS_CMD = 3 +#endif +}; + +/* Returns the number of bytes needed for the quoted string. */ +extern size_t + system_quote_length (enum system_command_interpreter interpreter, + const char *string); + +/* Copies the quoted string to p and returns the incremented p. + There must be room for shell_quote_length (string) + 1 bytes at p. */ +extern char * + system_quote_copy (char *p, + enum system_command_interpreter interpreter, + const char *string); + +/* Returns the freshly allocated quoted string. */ +extern char * + system_quote (enum system_command_interpreter interpreter, + const char *string); + +/* Returns a freshly allocated string containing all argument strings, quoted, + separated through spaces. */ +extern char * + system_quote_argv (enum system_command_interpreter interpreter, + char * const *argv); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYSTEM_QUOTE_H */ diff --git a/modules/system-quote b/modules/system-quote new file mode 100644 index 000000000..def4ecb46 --- /dev/null +++ b/modules/system-quote @@ -0,0 +1,24 @@ +Description: +Quoting for a system command. + +Files: +lib/system-quote.h +lib/system-quote.c + +Depends-on: +sh-quote +xalloc + +configure.ac: + +Makefile.am: +lib_SOURCES += system-quote.h system-quote.c + +Include: +"system-quote.h" + +License: +GPL + +Maintainer: +Bruno Haible