Blame lib/system-quote.c

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