root/maint/gnulib/lib/system-quote.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. windows_createprocess_quote
  2. windows_cmd_quote
  3. system_quote_length
  4. system_quote_copy
  5. system_quote
  6. system_quote_argv

   1 /* Quoting for a system command.
   2    Copyright (C) 2012-2021 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 <stdbool.h>
  24 #include <stdlib.h>
  25 #include <string.h>
  26 
  27 #include "sh-quote.h"
  28 #include "xalloc.h"
  29 
  30 #if defined _WIN32 && ! defined __CYGWIN__
  31 
  32 /* The native Windows CreateProcess() function interprets characters like
  33    ' ', '\t', '\\', '"' (but not '<' and '>') in a special way:
  34    - Space and tab are interpreted as delimiters. They are not treated as
  35      delimiters if they are surrounded by double quotes: "...".
  36    - Unescaped double quotes are removed from the input. Their only effect is
  37      that within double quotes, space and tab are treated like normal
  38      characters.
  39    - Backslashes not followed by double quotes are not special.
  40    - But 2*n+1 backslashes followed by a double quote become
  41      n backslashes followed by a double quote (n >= 0):
  42        \" -> "
  43        \\\" -> \"
  44        \\\\\" -> \\"
  45    - '*', '?' characters may get expanded through wildcard expansion in the
  46      callee: By default, in the callee, the initialization code before main()
  47      takes the result of GetCommandLine(), wildcard-expands it, and passes it
  48      to main(). The exceptions to this rule are:
  49        - programs that inspect GetCommandLine() and ignore argv,
  50        - mingw programs that have a global variable 'int _CRT_glob = 0;',
  51        - Cygwin programs, when invoked from a Cygwin program.
  52  */
  53 # 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*?"
  54 # 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 
  56 /* Copies the quoted string to p and returns the number of bytes needed.
  57    If p is non-NULL, there must be room for system_quote_length (string)
  58    bytes at p.  */
  59 static size_t
  60 windows_createprocess_quote (char *p, const char *string)
     /* [previous][next][first][last][top][bottom][index][help] */
  61 {
  62   size_t len = strlen (string);
  63   bool quote_around =
  64     (len == 0 || strpbrk (string, SHELL_SPECIAL_CHARS) != NULL);
  65   size_t backslashes = 0;
  66   size_t i = 0;
  67 # define STORE(c) \
  68   do                 \
  69     {                \
  70       if (p != NULL) \
  71         p[i] = (c);  \
  72       i++;           \
  73     }                \
  74   while (0)
  75 
  76   if (quote_around)
  77     STORE ('"');
  78   for (; len > 0; string++, len--)
  79     {
  80       char c = *string;
  81 
  82       if (c == '"')
  83         {
  84           size_t j;
  85 
  86           for (j = backslashes + 1; j > 0; j--)
  87             STORE ('\\');
  88         }
  89       STORE (c);
  90       if (c == '\\')
  91         backslashes++;
  92       else
  93         backslashes = 0;
  94     }
  95   if (quote_around)
  96     {
  97       size_t j;
  98 
  99       for (j = backslashes; j > 0; j--)
 100         STORE ('\\');
 101       STORE ('"');
 102     }
 103 # undef STORE
 104   return i;
 105 }
 106 
 107 /* The native Windows cmd.exe command interpreter also interprets:
 108    - '\n', '\r' as a command terminator - no way to escape it,
 109    - '<', '>' as redirections,
 110    - '|' as pipe operator,
 111    - '%var%' as a reference to the environment variable VAR (uppercase),
 112      even inside quoted strings,
 113    - '&' '[' ']' '{' '}' '^' '=' ';' '!' '\'' '+' ',' '`' '~' for other
 114      purposes, according to
 115      <https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cmd.mspx?mfr=true>
 116    We quote a string like '%var%' by putting the '%' characters outside of
 117    double-quotes and the rest of the string inside double-quotes: %"var"%.
 118    This is guaranteed to not be a reference to an environment variable.
 119  */
 120 # 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!%&'*+,;<=>?[]^`{|}~"
 121 # define CMD_FORBIDDEN_CHARS "\n\r"
 122 
 123 /* Copies the quoted string to p and returns the number of bytes needed.
 124    If p is non-NULL, there must be room for system_quote_length (string)
 125    bytes at p.  */
 126 static size_t
 127 windows_cmd_quote (char *p, const char *string)
     /* [previous][next][first][last][top][bottom][index][help] */
 128 {
 129   size_t len = strlen (string);
 130   bool quote_around =
 131     (len == 0 || strpbrk (string, CMD_SPECIAL_CHARS) != NULL);
 132   size_t backslashes = 0;
 133   size_t i = 0;
 134 # define STORE(c) \
 135   do                 \
 136     {                \
 137       if (p != NULL) \
 138         p[i] = (c);  \
 139       i++;           \
 140     }                \
 141   while (0)
 142 
 143   if (quote_around)
 144     STORE ('"');
 145   for (; len > 0; string++, len--)
 146     {
 147       char c = *string;
 148 
 149       if (c == '"')
 150         {
 151           size_t j;
 152 
 153           for (j = backslashes + 1; j > 0; j--)
 154             STORE ('\\');
 155         }
 156       if (c == '%')
 157         {
 158           size_t j;
 159 
 160           for (j = backslashes; j > 0; j--)
 161             STORE ('\\');
 162           STORE ('"');
 163         }
 164       STORE (c);
 165       if (c == '%')
 166         STORE ('"');
 167       if (c == '\\')
 168         backslashes++;
 169       else
 170         backslashes = 0;
 171     }
 172   if (quote_around)
 173     {
 174       size_t j;
 175 
 176       for (j = backslashes; j > 0; j--)
 177         STORE ('\\');
 178       STORE ('"');
 179     }
 180   return i;
 181 }
 182 
 183 #endif
 184 
 185 size_t
 186 system_quote_length (enum system_command_interpreter interpreter,
     /* [previous][next][first][last][top][bottom][index][help] */
 187                      const char *string)
 188 {
 189   switch (interpreter)
 190     {
 191 #if !(defined _WIN32 && ! defined __CYGWIN__)
 192     case SCI_SYSTEM:
 193 #endif
 194     case SCI_POSIX_SH:
 195       return shell_quote_length (string);
 196 
 197 #if defined _WIN32 && ! defined __CYGWIN__
 198     case SCI_WINDOWS_CREATEPROCESS:
 199       return windows_createprocess_quote (NULL, string);
 200 
 201     case SCI_SYSTEM:
 202     case SCI_WINDOWS_CMD:
 203       return windows_cmd_quote (NULL, string);
 204 #endif
 205 
 206     default:
 207       /* Invalid interpreter.  */
 208       abort ();
 209     }
 210 }
 211 
 212 char *
 213 system_quote_copy (char *p,
     /* [previous][next][first][last][top][bottom][index][help] */
 214                    enum system_command_interpreter interpreter,
 215                    const char *string)
 216 {
 217   switch (interpreter)
 218     {
 219 #if !(defined _WIN32 && ! defined __CYGWIN__)
 220     case SCI_SYSTEM:
 221 #endif
 222     case SCI_POSIX_SH:
 223       return shell_quote_copy (p, string);
 224 
 225 #if defined _WIN32 && ! defined __CYGWIN__
 226     case SCI_WINDOWS_CREATEPROCESS:
 227       p += windows_createprocess_quote (p, string);
 228       *p = '\0';
 229       return p;
 230 
 231     case SCI_SYSTEM:
 232     case SCI_WINDOWS_CMD:
 233       p += windows_cmd_quote (p, string);
 234       *p = '\0';
 235       return p;
 236 #endif
 237 
 238     default:
 239       /* Invalid interpreter.  */
 240       abort ();
 241     }
 242 }
 243 
 244 char *
 245 system_quote (enum system_command_interpreter interpreter,
     /* [previous][next][first][last][top][bottom][index][help] */
 246               const char *string)
 247 {
 248   switch (interpreter)
 249     {
 250 #if !(defined _WIN32 && ! defined __CYGWIN__)
 251     case SCI_SYSTEM:
 252 #endif
 253     case SCI_POSIX_SH:
 254       return shell_quote (string);
 255 
 256 #if defined _WIN32 && ! defined __CYGWIN__
 257     case SCI_WINDOWS_CREATEPROCESS:
 258     case SCI_SYSTEM:
 259     case SCI_WINDOWS_CMD:
 260       {
 261         size_t length = system_quote_length (interpreter, string);
 262         char *quoted = XNMALLOC (length, char);
 263         system_quote_copy (quoted, interpreter, string);
 264         return quoted;
 265       }
 266 #endif
 267 
 268     default:
 269       /* Invalid interpreter.  */
 270       abort ();
 271     }
 272 }
 273 
 274 char *
 275 system_quote_argv (enum system_command_interpreter interpreter,
     /* [previous][next][first][last][top][bottom][index][help] */
 276                    char * const *argv)
 277 {
 278   if (*argv != NULL)
 279     {
 280       char * const *argp;
 281       size_t length;
 282       char *command;
 283       char *p;
 284 
 285       length = 0;
 286       for (argp = argv; ; )
 287         {
 288           length += system_quote_length (interpreter, *argp) + 1;
 289           argp++;
 290           if (*argp == NULL)
 291             break;
 292         }
 293 
 294       command = XNMALLOC (length, char);
 295 
 296       p = command;
 297       for (argp = argv; ; )
 298         {
 299           p = system_quote_copy (p, interpreter, *argp);
 300           argp++;
 301           if (*argp == NULL)
 302             break;
 303           *p++ = ' ';
 304         }
 305       *p = '\0';
 306 
 307       return command;
 308     }
 309   else
 310     return xstrdup ("");
 311 }

/* [previous][next][first][last][top][bottom][index][help] */