(root)/
gettext-0.22.4/
gettext-tools/
gnulib-tests/
test-sh-quote.c
       1  /* Test of sh-quote module.
       2     Copyright (C) 2012-2023 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 of the License, or
       7     (at your option) 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 "sh-quote.h"
      23  
      24  #include <limits.h>
      25  #include <string.h>
      26  
      27  #include "macros.h"
      28  
      29  static void
      30  check_one (const char *input, const char *expected)
      31  {
      32    char buf[1000];
      33    size_t output_len;
      34    char *output;
      35    char *bufend;
      36  
      37    output_len = shell_quote_length (input);
      38  
      39    output = shell_quote (input);
      40    ASSERT (strlen (output) == output_len);
      41  
      42    ASSERT (output_len <= sizeof (buf) - 2);
      43    memset (buf, '\0', output_len + 1);
      44    buf[output_len + 1] = '%';
      45    bufend = shell_quote_copy (buf, input);
      46    ASSERT (bufend == buf + output_len);
      47    ASSERT (memcmp (buf, output, output_len + 1) == 0);
      48    ASSERT (buf[output_len + 1] == '%');
      49  
      50    ASSERT (strcmp (output, expected) == 0);
      51  
      52    free (output);
      53  }
      54  
      55  int
      56  main (void)
      57  {
      58    /* Check the shell_quote_length, shell_quote_copy, shell_quote functions.  */
      59    {
      60      int c;
      61  
      62      /* Empty argument.  */
      63      check_one ("", "''");
      64  
      65      /* Identifier or number.  */
      66      check_one ("foo", "foo");
      67      check_one ("phr0ck", "phr0ck");
      68  
      69      /* Whitespace would be interpreted as argument separator by the shell.  */
      70      check_one ("foo\tbar", "'foo\tbar'");
      71      check_one ("foo\nbar", "'foo\nbar'");
      72      check_one ("foo\rbar", "'foo\rbar'");
      73      check_one ("foo bar", "'foo bar'");
      74  
      75      /* '!' at the beginning of argv[0] would introduce a negated command.  */
      76      check_one ("!foo", "'!foo'");
      77  
      78      /* '"' would be interpreted as the start of a string.  */
      79      check_one ("\"foo\"bar", "'\"foo\"bar'");
      80  
      81      /* '#' at the beginning of an argument would be interpreted as the start
      82         of a comment.  */
      83      check_one ("#foo", "'#foo'");
      84  
      85      /* '$' at the beginning of an argument would be interpreted as a variable
      86         reference.  */
      87      check_one ("$foo", "'$foo'");
      88  
      89      /* '&' at the beginning of an argument would be interpreted as a background
      90         task indicator.  */
      91      check_one ("&", "'&'");
      92  
      93      /* "'" would be interpreted as the start of a string.  */
      94      check_one ("'foo'bar", "\"'foo'bar\"");
      95  
      96      /* '(' at the beginning of argv[0] would introduce a subshell command.  */
      97      check_one ("(", "'('");
      98  
      99      /* ')' at the beginning of an argument would be interpreted as the end of
     100         the command.  */
     101      check_one (")", "')'");
     102  
     103      /* '*' would be interpreted as a wildcard character.  */
     104      check_one ("*", "'*'");
     105      check_one ("*foo", "'*foo'");
     106  
     107      /* ';' at the beginning of an argument would be interpreted as an empty
     108         statement in argv[0] and as the end of the command otherwise.  */
     109      check_one (";", "';'");
     110      check_one ("foo;", "'foo;'");
     111  
     112      /* '<' would be interpreted as a redirection of stdin.  */
     113      check_one ("<", "'<'");
     114  
     115      /* '=' inside argv[0] would be interpreted as an environment variable
     116         assignment.  */
     117      check_one ("foo=bar", "'foo=bar'");
     118  
     119      /* '>' would be interpreted as a redirection of stdout.  */
     120      check_one (">", "'>'");
     121  
     122      /* '?' would be interpreted as a wildcard character.  */
     123      check_one ("?", "'?'");
     124      check_one ("foo?bar", "'foo?bar'");
     125  
     126      /* '^' would be interpreted in old /bin/sh, e.g. SunOS 4.1.4.  */
     127      check_one ("^", "'^'");
     128  
     129      /* "[...]" would be interpreted as a wildcard pattern.  */
     130      check_one ("[", "'['");
     131      check_one ("]", "]"); /* or "']'" */
     132  
     133      /* '\' would be interpreted as an escape character.  */
     134      check_one ("\\foo", "'\\foo'");
     135  
     136      /* '`' would be interpreted as the start of a command substitution.  */
     137      check_one ("`foo", "'`foo'");
     138  
     139      /* '{' at the beginning of argv[0] would introduce a complex command.  */
     140      check_one ("{", "'{'");
     141  
     142      /* '|' at the beginning of an argument would be interpreted as a pipe
     143         between commands.  */
     144      check_one ("|", "'|'");
     145  
     146      /* '}' at the beginning of an argument would be interpreted as the end of
     147         the command.  */
     148      check_one ("}", "'}'");
     149  
     150      /* '~' at the beginning of an argument would be interpreted as a reference
     151         to a user's home directory.  */
     152      check_one ("~", "'~'");
     153      check_one ("~foo", "'~foo'");
     154  
     155      /* A string that contains both ' and ".  */
     156      check_one ("foo'bar\"baz", "'foo'\\''bar\"baz'"); /* or "\"foo'bar\\\"baz\"" */
     157  
     158      /* All other characters don't need quoting.  */
     159      for (c = 1; c <= UCHAR_MAX; c++)
     160        if (strchr ("\t\n\r !\"#$&'()*;<=>?^[\\]`{|}~", c) == NULL)
     161          {
     162            char s[5];
     163            s[0] = 'a';
     164            s[1] = (char) c;
     165            s[2] = 'z';
     166            s[3] = (char) c;
     167            s[4] = '\0';
     168  
     169            check_one (s, s);
     170          }
     171    }
     172  
     173    /* Check the shell_quote_argv function.  */
     174    {
     175      const char *argv[1];
     176      char *result;
     177      argv[0] = NULL;
     178      result = shell_quote_argv (argv);
     179      ASSERT (strcmp (result, "") == 0);
     180      free (result);
     181    }
     182    {
     183      const char *argv[2];
     184      char *result;
     185      argv[0] = "foo bar/baz";
     186      argv[1] = NULL;
     187      result = shell_quote_argv (argv);
     188      ASSERT (strcmp (result, "'foo bar/baz'") == 0); /* or "\"foo bar/baz\"" */
     189      free (result);
     190    }
     191    {
     192      const char *argv[3];
     193      char *result;
     194      argv[0] = "foo bar/baz";
     195      argv[1] = "$";
     196      argv[2] = NULL;
     197      result = shell_quote_argv (argv);
     198      ASSERT (strcmp (result, "'foo bar/baz' '$'") == 0); /* or "\"foo bar/baz\" \"\\$\"" */
     199      free (result);
     200    }
     201  
     202    return 0;
     203  }