root/maint/gnulib/tests/test-system-quote-main.c

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

DEFINITIONS

This source file includes following definitions.
  1. check_one
  2. check_all
  3. main

   1 /* Test of system-quote module.
   2    Copyright (C) 2012-2021 Free Software Foundation, Inc.
   3 
   4    This program is free software; you can redistribute it and/or modify
   5    it under the terms of the GNU General Public License as published by
   6    the Free Software Foundation; either version 3, or (at your option)
   7    any later version.
   8 
   9    This program is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU General Public License for more details.
  13 
  14    You should have received a copy of the GNU General Public License
  15    along with this program; if not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* Written by Bruno Haible <bruno@clisp.org>, 2012.  */
  18 
  19 #include <config.h>
  20 
  21 /* Specification.  */
  22 #include "system-quote.h"
  23 
  24 #if defined _WIN32 && !defined __CYGWIN__
  25 # define WINDOWS_NATIVE
  26 #endif
  27 
  28 #include <limits.h>
  29 #include <stdbool.h>
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <unistd.h>
  34 #ifdef WINDOWS_NATIVE
  35 # define WIN32_LEAN_AND_MEAN
  36 # include <windows.h>
  37 #endif
  38 
  39 #include "macros.h"
  40 
  41 #define EXPECTED_DATA_FILE "t-sq-data.tmp"
  42 
  43 static int failed;
  44 
  45 static void
  46 check_one (enum system_command_interpreter interpreter, const char *prog,
     /* [previous][next][first][last][top][bottom][index][help] */
  47            const char *input)
  48 {
  49   char buf[1000];
  50   size_t output_len;
  51   char *output;
  52   char *bufend;
  53 
  54   output_len = system_quote_length (interpreter, input);
  55 
  56   output = system_quote (interpreter, input);
  57   ASSERT (strlen (output) == output_len);
  58 
  59   ASSERT (output_len <= sizeof (buf) - 2);
  60   memset (buf, '\0', output_len + 1);
  61   buf[output_len + 1] = '%';
  62   bufend = system_quote_copy (buf, interpreter, input);
  63   ASSERT (bufend == buf + output_len);
  64   ASSERT (memcmp (buf, output, output_len + 1) == 0);
  65   ASSERT (buf[output_len + 1] == '%');
  66 
  67   /* Store INPUT in EXPECTED_DATA_FILE, for verification by the child
  68      process.  */
  69   {
  70     FILE *fp = fopen (EXPECTED_DATA_FILE, "wb");
  71     if (fp == NULL)
  72       exit (3);
  73     if (fwrite (input, 1, strlen (input), fp) != strlen (input))
  74       exit (4);
  75     if (fclose (fp))
  76       exit (5);
  77   }
  78 
  79   /* Invoke the child process through system() and popen().  */
  80   {
  81     char command[1000];
  82 
  83     sprintf (command, "%s %s", prog, output);
  84 
  85     switch (interpreter)
  86       {
  87       case SCI_SYSTEM:
  88 #ifdef WINDOWS_NATIVE
  89       case SCI_WINDOWS_CMD:
  90 #endif
  91         {
  92           int exitcode = system (command);
  93           if (exitcode != 0)
  94             {
  95               fprintf (stderr, "for input = |%s|: system() command failed with status %d: %s\n",
  96                        input, exitcode, command);
  97               failed = 1;
  98             }
  99         }
 100         {
 101           FILE *fp = popen (command, "r");
 102           int exitcode = pclose (fp);
 103           if (exitcode != 0)
 104             {
 105               fprintf (stderr, "for input = |%s|: popen() command failed with status %d: %s\n",
 106                        input, exitcode, command);
 107               failed = 1;
 108             }
 109         }
 110         break;
 111 #ifdef WINDOWS_NATIVE
 112       case SCI_WINDOWS_CREATEPROCESS:
 113         {
 114           PROCESS_INFORMATION pinfo;
 115           STARTUPINFO sinfo;
 116           sinfo.cb = sizeof (STARTUPINFO);
 117           sinfo.lpReserved = NULL;
 118           sinfo.lpDesktop = NULL;
 119           sinfo.lpTitle = NULL;
 120           sinfo.cbReserved2 = 0;
 121           sinfo.lpReserved2 = NULL;
 122           sinfo.dwFlags = STARTF_USESTDHANDLES;
 123           sinfo.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
 124           sinfo.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
 125           sinfo.hStdError = GetStdHandle (STD_ERROR_HANDLE);
 126 
 127           if (CreateProcess (NULL, command, NULL, NULL, TRUE, 0, NULL, NULL,
 128                              &sinfo, &pinfo))
 129             {
 130               DWORD exitcode;
 131               CloseHandle (pinfo.hThread);
 132               if (WaitForSingleObject (pinfo.hProcess, INFINITE) == WAIT_OBJECT_0)
 133                 {
 134                   if (GetExitCodeProcess (pinfo.hProcess, &exitcode))
 135                     {
 136                       if (exitcode != 0)
 137                         {
 138                           fprintf (stderr, "for input = |%s|: CreateProcess() command failed with status %d: %s\n",
 139                                    input, exitcode, command);
 140                           failed = 1;
 141                         }
 142                     }
 143                   else
 144                     {
 145                       fprintf (stderr, "for input = |%s|: GetExitCodeProcess failed, GetLastError() = %u\n",
 146                                input, GetLastError ());
 147                       failed = 1;
 148                     }
 149                 }
 150               else
 151                 {
 152                   fprintf (stderr, "for input = |%s|: WaitForSingleObject failed\n",
 153                            input);
 154                   failed = 1;
 155                 }
 156               CloseHandle (pinfo.hProcess);
 157             }
 158           else
 159             {
 160               fprintf (stderr, "for input = |%s|: CreateProcess failed, GetLastError() = %u\n",
 161                        input, GetLastError ());
 162               failed = 1;
 163             }
 164         }
 165         break;
 166 #endif
 167       default:
 168         break;
 169       }
 170   }
 171 
 172   free (output);
 173 }
 174 
 175 static void
 176 check_all (enum system_command_interpreter interpreter,
     /* [previous][next][first][last][top][bottom][index][help] */
 177            bool windows_cmd_limitations,
 178            const char *prog)
 179 {
 180   /* Check the system_quote_length, system_quote_copy, system_quote
 181      functions.  */
 182   {
 183     int c;
 184 
 185     /* Empty argument.  */
 186     check_one (interpreter, prog, "");
 187 
 188     /* Identifier or number.  */
 189     check_one (interpreter, prog, "foo");
 190     check_one (interpreter, prog, "phr0ck");
 191 
 192     /* Whitespace would be interpreted as argument separator by the shell.  */
 193     check_one (interpreter, prog, "foo\tbar");
 194     if (!windows_cmd_limitations)
 195       {
 196         check_one (interpreter, prog, "foo\nbar");
 197         check_one (interpreter, prog, "foo\rbar");
 198       }
 199     check_one (interpreter, prog, "foo bar");
 200 
 201     /* '!' at the beginning of argv[0] would introduce a negated command.  */
 202     check_one (interpreter, prog, "!foo");
 203 
 204     /* '"' would be interpreted as the start of a string.  */
 205     check_one (interpreter, prog, "\"foo\"bar");
 206 
 207     /* '#' at the beginning of an argument would be interpreted as the start
 208        of a comment.  */
 209     check_one (interpreter, prog, "#foo");
 210 
 211     /* '$' at the beginning of an argument would be interpreted as a variable
 212        reference.  */
 213     check_one (interpreter, prog, "$foo");
 214 
 215     /* '&' at the beginning of an argument would be interpreted as a background
 216        task indicator.  */
 217     check_one (interpreter, prog, "&");
 218 
 219     /* "'" would be interpreted as the start of a string.  */
 220     check_one (interpreter, prog, "'foo'bar");
 221 
 222     /* '(' at the beginning of argv[0] would introduce a subshell command.  */
 223     check_one (interpreter, prog, "(");
 224 
 225     /* ')' at the beginning of an argument would be interpreted as the end of
 226        the command.  */
 227     check_one (interpreter, prog, ")");
 228 
 229     /* '*' would be interpreted as a wildcard character.  */
 230     check_one (interpreter, prog, "*");
 231     check_one (interpreter, prog, "*foo");
 232 
 233     /* ';' at the beginning of an argument would be interpreted as an empty
 234        statement in argv[0] and as the end of the command otherwise.  */
 235     check_one (interpreter, prog, ";");
 236     check_one (interpreter, prog, "foo;");
 237 
 238     /* '<' would be interpreted as a redirection of stdin.  */
 239     check_one (interpreter, prog, "<");
 240 
 241     /* '=' inside argv[0] would be interpreted as an environment variable
 242        assignment.  */
 243     check_one (interpreter, prog, "foo=bar");
 244 
 245     /* '>' would be interpreted as a redirection of stdout.  */
 246     check_one (interpreter, prog, ">");
 247 
 248     /* '?' would be interpreted as a wildcard character.  */
 249     check_one (interpreter, prog, "?");
 250     check_one (interpreter, prog, "??");
 251     check_one (interpreter, prog, "???");
 252     check_one (interpreter, prog, "????");
 253     check_one (interpreter, prog, "?????");
 254     check_one (interpreter, prog, "??????");
 255     check_one (interpreter, prog, "???????");
 256     check_one (interpreter, prog, "????????");
 257     check_one (interpreter, prog, "?????????");
 258     check_one (interpreter, prog, "??????????");
 259     check_one (interpreter, prog, "foo?bar");
 260 
 261     /* '^' would be interpreted in old /bin/sh, e.g. SunOS 4.1.4.  */
 262     check_one (interpreter, prog, "^");
 263 
 264     /* "[...]" would be interpreted as a wildcard pattern.  */
 265     check_one (interpreter, prog, "[");
 266     check_one (interpreter, prog, "]");
 267 
 268     /* '\' would be interpreted as an escape character.  */
 269     check_one (interpreter, prog, "\\foo");
 270 
 271     /* '`' would be interpreted as the start of a command substitution.  */
 272     check_one (interpreter, prog, "`foo");
 273 
 274     /* '{' at the beginning of argv[0] would introduce a complex command.  */
 275     check_one (interpreter, prog, "{");
 276 
 277     /* '|' at the beginning of an argument would be interpreted as a pipe
 278        between commands.  */
 279     check_one (interpreter, prog, "|");
 280 
 281     /* '}' at the beginning of an argument would be interpreted as the end of
 282        the command.  */
 283     check_one (interpreter, prog, "}");
 284 
 285     /* '~' at the beginning of an argument would be interpreted as a reference
 286        to a user's home directory.  */
 287     check_one (interpreter, prog, "~");
 288     check_one (interpreter, prog, "~foo");
 289 
 290     /* A string that contains both ' and ".  */
 291     check_one (interpreter, prog, "foo'bar\"baz");
 292 
 293     /* '%' is used for environment variable references in Windows cmd.exe.  */
 294     check_one (interpreter, prog, "%");
 295     check_one (interpreter, prog, "%%");
 296     check_one (interpreter, prog, "%foo%");
 297     check_one (interpreter, prog, "%PATH%");
 298 
 299     /* All other characters don't need quoting.  */
 300     for (c = 1; c <= UCHAR_MAX; c++)
 301       if (strchr ("\t\n\r !\"#$&'()*;<=>?^[\\]`{|}~", c) == NULL)
 302         {
 303           char s[5];
 304           s[0] = 'a';
 305           s[1] = (char) c;
 306           s[2] = 'z';
 307           s[3] = (char) c;
 308           s[4] = '\0';
 309 
 310           check_one (interpreter, prog, s);
 311         }
 312   }
 313 }
 314 
 315 int
 316 main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
 317 {
 318   char *prog;
 319 
 320   if (argc != 2)
 321     {
 322       fprintf (stderr, "%s: need 1 argument\n", argv[0]);
 323       return 2;
 324     }
 325   prog = argv[1];
 326 
 327 #ifdef WINDOWS_NATIVE
 328   /* Make PROG suitable for native Windows system calls and cmd.exe:
 329      Replace '/' with '\\'.  */
 330   {
 331     char *p;
 332     for (p = prog; *p != '\0'; p++)
 333       if (*p == '/')
 334         *p = '\\';
 335   }
 336 #endif
 337 
 338 #ifdef WINDOWS_NATIVE
 339   check_all (SCI_SYSTEM, true, prog); /* equivalent to SCI_WINDOWS_CMD */
 340   check_all (SCI_WINDOWS_CREATEPROCESS, false, prog);
 341   check_all (SCI_WINDOWS_CMD, true, prog);
 342 #else
 343   check_all (SCI_SYSTEM, false, prog); /* equivalent to SCI_POSIX_SH */
 344 #endif
 345 
 346   /* Clean up.  */
 347   unlink (EXPECTED_DATA_FILE);
 348 
 349   return failed;
 350 }

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