(root)/
diffutils-3.10/
lib/
system-quote.c
       1  /* Quoting for a system command.
       2     Copyright (C) 2012-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2012.
       4  
       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.
       9  
      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.
      14  
      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/>.  */
      17  
      18  #include <config.h>
      19  
      20  /* Specification.  */
      21  #include "system-quote.h"
      22  
      23  #include <stdlib.h>
      24  #include <string.h>
      25  
      26  #include "sh-quote.h"
      27  #include "xalloc.h"
      28  
      29  #if defined _WIN32 && ! defined __CYGWIN__
      30  
      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
      37       characters.
      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):
      41         \" -> "
      42         \\\" -> \"
      43         \\\\\" -> \\"
      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.
      51   */
      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"
      54  
      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)
      57     bytes at p.  */
      58  static size_t
      59  windows_createprocess_quote (char *p, const char *string)
      60  {
      61    size_t len = strlen (string);
      62    bool quote_around =
      63      (len == 0 || strpbrk (string, SHELL_SPECIAL_CHARS) != NULL);
      64    size_t backslashes = 0;
      65    size_t i = 0;
      66  # define STORE(c) \
      67    do                 \
      68      {                \
      69        if (p != NULL) \
      70          p[i] = (c);  \
      71        i++;           \
      72      }                \
      73    while (0)
      74  
      75    if (quote_around)
      76      STORE ('"');
      77    for (; len > 0; string++, len--)
      78      {
      79        char c = *string;
      80  
      81        if (c == '"')
      82          {
      83            size_t j;
      84  
      85            for (j = backslashes + 1; j > 0; j--)
      86              STORE ('\\');
      87          }
      88        STORE (c);
      89        if (c == '\\')
      90          backslashes++;
      91        else
      92          backslashes = 0;
      93      }
      94    if (quote_around)
      95      {
      96        size_t j;
      97  
      98        for (j = backslashes; j > 0; j--)
      99          STORE ('\\');
     100        STORE ('"');
     101      }
     102  # undef STORE
     103    return i;
     104  }
     105  
     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.
     118   */
     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"
     121  
     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)
     124     bytes at p.  */
     125  static size_t
     126  windows_cmd_quote (char *p, const char *string)
     127  {
     128    size_t len = strlen (string);
     129    bool quote_around =
     130      (len == 0 || strpbrk (string, CMD_SPECIAL_CHARS) != NULL);
     131    size_t backslashes = 0;
     132    size_t i = 0;
     133  # define STORE(c) \
     134    do                 \
     135      {                \
     136        if (p != NULL) \
     137          p[i] = (c);  \
     138        i++;           \
     139      }                \
     140    while (0)
     141  
     142    if (quote_around)
     143      STORE ('"');
     144    for (; len > 0; string++, len--)
     145      {
     146        char c = *string;
     147  
     148        if (c == '"')
     149          {
     150            size_t j;
     151  
     152            for (j = backslashes + 1; j > 0; j--)
     153              STORE ('\\');
     154          }
     155        if (c == '%')
     156          {
     157            size_t j;
     158  
     159            for (j = backslashes; j > 0; j--)
     160              STORE ('\\');
     161            STORE ('"');
     162          }
     163        STORE (c);
     164        if (c == '%')
     165          STORE ('"');
     166        if (c == '\\')
     167          backslashes++;
     168        else
     169          backslashes = 0;
     170      }
     171    if (quote_around)
     172      {
     173        size_t j;
     174  
     175        for (j = backslashes; j > 0; j--)
     176          STORE ('\\');
     177        STORE ('"');
     178      }
     179    return i;
     180  }
     181  
     182  #endif
     183  
     184  size_t
     185  system_quote_length (enum system_command_interpreter interpreter,
     186                       const char *string)
     187  {
     188    switch (interpreter)
     189      {
     190  #if !(defined _WIN32 && ! defined __CYGWIN__)
     191      case SCI_SYSTEM:
     192  #endif
     193      case SCI_POSIX_SH:
     194        return shell_quote_length (string);
     195  
     196  #if defined _WIN32 && ! defined __CYGWIN__
     197      case SCI_WINDOWS_CREATEPROCESS:
     198        return windows_createprocess_quote (NULL, string);
     199  
     200      case SCI_SYSTEM:
     201      case SCI_WINDOWS_CMD:
     202        return windows_cmd_quote (NULL, string);
     203  #endif
     204  
     205      default:
     206        /* Invalid interpreter.  */
     207        abort ();
     208      }
     209  }
     210  
     211  char *
     212  system_quote_copy (char *p,
     213                     enum system_command_interpreter interpreter,
     214                     const char *string)
     215  {
     216    switch (interpreter)
     217      {
     218  #if !(defined _WIN32 && ! defined __CYGWIN__)
     219      case SCI_SYSTEM:
     220  #endif
     221      case SCI_POSIX_SH:
     222        return shell_quote_copy (p, string);
     223  
     224  #if defined _WIN32 && ! defined __CYGWIN__
     225      case SCI_WINDOWS_CREATEPROCESS:
     226        p += windows_createprocess_quote (p, string);
     227        *p = '\0';
     228        return p;
     229  
     230      case SCI_SYSTEM:
     231      case SCI_WINDOWS_CMD:
     232        p += windows_cmd_quote (p, string);
     233        *p = '\0';
     234        return p;
     235  #endif
     236  
     237      default:
     238        /* Invalid interpreter.  */
     239        abort ();
     240      }
     241  }
     242  
     243  char *
     244  system_quote (enum system_command_interpreter interpreter,
     245                const char *string)
     246  {
     247    switch (interpreter)
     248      {
     249  #if !(defined _WIN32 && ! defined __CYGWIN__)
     250      case SCI_SYSTEM:
     251  #endif
     252      case SCI_POSIX_SH:
     253        return shell_quote (string);
     254  
     255  #if defined _WIN32 && ! defined __CYGWIN__
     256      case SCI_WINDOWS_CREATEPROCESS:
     257      case SCI_SYSTEM:
     258      case SCI_WINDOWS_CMD:
     259        {
     260          size_t length = system_quote_length (interpreter, string) + 1;
     261          char *quoted = XNMALLOC (length, char);
     262          system_quote_copy (quoted, interpreter, string);
     263          return quoted;
     264        }
     265  #endif
     266  
     267      default:
     268        /* Invalid interpreter.  */
     269        abort ();
     270      }
     271  }
     272  
     273  char *
     274  system_quote_argv (enum system_command_interpreter interpreter,
     275                     char * const *argv)
     276  {
     277    if (*argv != NULL)
     278      {
     279        char * const *argp;
     280        size_t length;
     281        char *command;
     282        char *p;
     283  
     284        length = 0;
     285        for (argp = argv; ; )
     286          {
     287            length += system_quote_length (interpreter, *argp) + 1;
     288            argp++;
     289            if (*argp == NULL)
     290              break;
     291          }
     292  
     293        command = XNMALLOC (length, char);
     294  
     295        p = command;
     296        for (argp = argv; ; )
     297          {
     298            p = system_quote_copy (p, interpreter, *argp);
     299            argp++;
     300            if (*argp == NULL)
     301              break;
     302            *p++ = ' ';
     303          }
     304        *p = '\0';
     305  
     306        return command;
     307      }
     308    else
     309      return xstrdup ("");
     310  }