(root)/
glibc-2.38/
posix/
wordexp-test.c
       1  /* Copyright (C) 1997-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library 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 GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <wordexp.h>
      19  #include <stdio.h>
      20  #include <fcntl.h>
      21  #include <pwd.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  #include <sys/mman.h>
      25  
      26  #include <libc-pointer-arith.h>
      27  #include <array_length.h>
      28  #include <support/xunistd.h>
      29  #include <support/check.h>
      30  #include <support/next_to_fault.h>
      31  
      32  #define IFS " \n\t"
      33  
      34  struct test_case_struct
      35  {
      36    int retval;
      37    const char *env;
      38    const char *words;
      39    int flags;
      40    size_t wordc;
      41    const char *wordv[10];
      42    const char *ifs;
      43  } static test_case[] =
      44    {
      45      /* Simple word- and field-splitting */
      46      { 0, NULL, "one", 0, 1, { "one", }, IFS },
      47      { 0, NULL, "one two", 0, 2, { "one", "two", }, IFS },
      48      { 0, NULL, "one two three", 0, 3, { "one", "two", "three", }, IFS },
      49      { 0, NULL, " \tfoo\t\tbar ", 0, 2, { "foo", "bar", }, IFS },
      50      { 0, NULL, "red , white blue", 0, 4, { "red", ",", "white", "blue", }, " ," },
      51      { 0, NULL, "one two three", 0, 3, { "one", "two", "three", }, "" },
      52      { 0, NULL, "one \"two three\"", 0, 2, { "one", "two three", }, IFS },
      53      { 0, NULL, "one \"two three\"", 0, 2, { "one", "two three", }, "" },
      54      { 0, "two three", "one \"$var\"", 0, 2, { "one", "two three", }, IFS },
      55      { 0, "two three", "one $var", 0, 3, { "one", "two", "three", }, IFS },
      56      { 0, "two three", "one \"$var\"", 0, 2, { "one", "two three", }, "" },
      57      { 0, "two three", "one $var", 0, 2, { "one", "two three", }, "" },
      58  
      59      /* The non-whitespace IFS char at the end delimits the second field
      60       * but does NOT start a new field. */
      61      { 0, ":abc:", "$var", 0, 2, { "", "abc", }, ":" },
      62  
      63      { 0, NULL, "$(echo :abc:)", 0, 2, { "", "abc", }, ":" },
      64      { 0, NULL, "$(echo :abc:\\ )", 0, 2, { "", "abc", }, ": " },
      65      { 0, NULL, "$(echo :abc\\ )", 0, 2, { "", "abc", }, ": " },
      66      { 0, ":abc:", "$(echo $var)", 0, 2, { "", "abc", }, ":" },
      67      { 0, NULL, ":abc:", 0, 1, { ":abc:", }, ":" },
      68      { 0, NULL, "$(echo :abc:)def", 0, 3, { "", "abc", "def", },
      69        ":" },
      70      { 0, NULL, "$(echo abc:de)f", 0, 2, { "abc", "def", }, ":" },
      71      { 0, NULL, "$(echo abc:de)f:ghi", 0, 2, { "abc", "def:ghi", },
      72        ":" },
      73      { 0, NULL, "abc:d$(echo ef:ghi)", 0, 2, { "abc:def", "ghi", },
      74        ":" },
      75      { 0, "abc:", "$var$(echo def:ghi)", 0, 3, { "abc", "def",
      76  							  "ghi", }, ":" },
      77      { 0, "abc:d", "$var$(echo ef:ghi)", 0, 3, { "abc", "def",
      78  							  "ghi", }, ":" },
      79      { 0, "def:ghi", "$(echo abc:)$var", 0, 3, { "abc", "def",
      80  							  "ghi", }, ":" },
      81      { 0, "ef:ghi", "$(echo abc:d)$var", 0, 3, { "abc", "def",
      82  							  "ghi", }, ":" },
      83  
      84      /* Simple parameter expansion */
      85      { 0, "foo", "${var}", 0, 1, { "foo", }, IFS },
      86      { 0, "foo", "$var", 0, 1, { "foo", }, IFS },
      87      { 0, "foo", "\\\"$var\\\"", 0, 1, { "\"foo\"", }, IFS },
      88      { 0, "foo", "%$var%", 0, 1, { "%foo%", }, IFS },
      89      { 0, "foo", "-$var-", 0, 1, { "-foo-", }, IFS },
      90  
      91      /* Simple quote removal */
      92      { 0, NULL, "\"quoted\"", 0, 1, { "quoted", }, IFS },
      93      { 0, "foo", "\"$var\"\"$var\"", 0, 1, { "foofoo", }, IFS },
      94      { 0, NULL, "'singly-quoted'", 0, 1, { "singly-quoted", }, IFS },
      95      { 0, NULL, "contin\\\nuation", 0, 1, { "continuation", }, IFS },
      96      { 0, NULL, "explicit ''", 0, 2, { "explicit", "", }, IFS },
      97      { 0, NULL, "explicit \"\"", 0, 2, { "explicit", "", }, IFS },
      98      { 0, NULL, "explicit ``", 0, 1, { "explicit", }, IFS },
      99  
     100      /* Simple command substitution */
     101      { 0, NULL, "$(echo hello)", 0, 1, { "hello", }, IFS },
     102      { 0, NULL, "$( (echo hello) )", 0, 1, { "hello", }, IFS },
     103      { 0, NULL, "$((echo hello);(echo there))", 0, 2, { "hello", "there", }, IFS },
     104      { 0, NULL, "`echo one two`", 0, 2, { "one", "two", }, IFS },
     105      { 0, NULL, "$(echo ')')", 0, 1, { ")" }, IFS },
     106      { 0, NULL, "$(echo hello; echo)", 0, 1, { "hello", }, IFS },
     107      { 0, NULL, "a$(echo b)c", 0, 1, { "abc", }, IFS },
     108  
     109      /* Simple arithmetic expansion */
     110      { 0, NULL, "$((1 + 1))", 0, 1, { "2", }, IFS },
     111      { 0, NULL, "$((2-3))", 0, 1, { "-1", }, IFS },
     112      { 0, NULL, "$((-1))", 0, 1, { "-1", }, IFS },
     113      { 0, NULL, "$[50+20]", 0, 1, { "70", }, IFS },
     114      { 0, NULL, "$(((2+3)*(4+5)))", 0, 1, { "45", }, IFS },
     115      { 0, NULL, "$((010))", 0, 1, { "8" }, IFS },
     116      { 0, NULL, "$((0x10))", 0, 1, { "16" }, IFS },
     117      { 0, NULL, "$((010+0x10))", 0, 1, { "24" }, IFS },
     118      { 0, NULL, "$((-010+0x10))", 0, 1, { "8" }, IFS },
     119      { 0, NULL, "$((-0x10+010))", 0, 1, { "-8" }, IFS },
     120      { 0, NULL, "$(())", 0, 1, { "0", }, IFS },
     121      { 0, NULL, "$[]", 0, 1, { "0", }, IFS },
     122  
     123      /* Advanced parameter expansion */
     124      { 0, NULL, "${var:-bar}", 0, 1, { "bar", }, IFS },
     125      { 0, NULL, "${var-bar}", 0, 1, { "bar", }, IFS },
     126      { 0, "", "${var:-bar}", 0, 1, { "bar", }, IFS },
     127      { 0, "foo", "${var:-bar}", 0, 1, { "foo", }, IFS },
     128      { 0, "", "${var-bar}", 0, 0, { NULL, }, IFS },
     129      { 0, NULL, "${var:=bar}", 0, 1, { "bar", }, IFS },
     130      { 0, NULL, "${var=bar}", 0, 1, { "bar", }, IFS },
     131      { 0, "", "${var:=bar}", 0, 1, { "bar", }, IFS },
     132      { 0, "foo", "${var:=bar}", 0, 1, { "foo", }, IFS },
     133      { 0, "", "${var=bar}", 0, 0, { NULL, }, IFS },
     134      { 0, "foo", "${var:?bar}", 0, 1, { "foo", }, IFS },
     135      { 0, NULL, "${var:+bar}", 0, 0, { NULL, }, IFS },
     136      { 0, NULL, "${var+bar}", 0, 0, { NULL, }, IFS },
     137      { 0, "", "${var:+bar}", 0, 0, { NULL, }, IFS },
     138      { 0, "foo", "${var:+bar}", 0, 1, { "bar", }, IFS },
     139      { 0, "", "${var+bar}", 0, 1, { "bar", }, IFS },
     140      { 0, "12345", "${#var}", 0, 1, { "5", }, IFS },
     141      { 0, NULL, "${var:-'}'}", 0, 1, { "}", }, IFS },
     142      { 0, NULL, "${var-}", 0, 0, { NULL }, IFS },
     143      { 0, NULL, "${a?}", 0, 0, { NULL, }, IFS },
     144      { 0, NULL, "${#a=}", 0, 1, { "0", }, IFS },
     145  
     146      { 0, "pizza", "${var#${var}}", 0, 0, { NULL }, IFS },
     147      { 0, "pepperoni", "${var%$(echo oni)}", 0, 1, { "pepper" }, IFS },
     148      { 0, "6pack", "${var#$((6))}", 0, 1, { "pack" }, IFS },
     149      { 0, "b*witched", "${var##b*}", 0, 0, { NULL }, IFS },
     150      { 0, "b*witched", "${var##\"b*\"}", 0, 1, { "witched" }, IFS },
     151      { 0, "banana", "${var%na*}", 0, 1, { "bana", }, IFS },
     152      { 0, "banana", "${var%%na*}", 0, 1, { "ba", }, IFS },
     153      { 0, "borabora-island", "${var#*bora}", 0, 1, { "bora-island", }, IFS },
     154      { 0, "borabora-island", "${var##*bora}", 0, 1, { "-island", }, IFS },
     155      { 0, "coconut", "${var##\\*co}", 0, 1, { "coconut", }, IFS },
     156      { 0, "100%", "${var%0%}", 0, 1, { "10" }, IFS },
     157  
     158      /* Pathname expansion */
     159      { 0, NULL, "???", 0, 2, { "one", "two", }, IFS },
     160      { 0, NULL, "[ot]??", 0, 2, { "one", "two", }, IFS },
     161      { 0, NULL, "t*", 0, 2, { "three", "two", }, IFS },
     162      { 0, NULL, "\"t\"*", 0, 2, { "three", "two", }, IFS },
     163  
     164      /* Nested constructs */
     165      { 0, "one two", "$var", 0, 2, { "one", "two", }, IFS },
     166      { 0, "one two three", "$var", 0, 3, { "one", "two", "three", }, IFS },
     167      { 0, " \tfoo\t\tbar ", "$var", 0, 2, { "foo", "bar", }, IFS },
     168      { 0, "  red  , white blue", "$var", 0, 3, { "red", "white", "blue", }, ", \n\t" },
     169      { 0, "  red  , white blue", "\"$var\"", 0, 1, { "  red  , white blue", }, ", \n\t" },
     170      { 0, NULL, "\"$(echo hello there)\"", 0, 1, { "hello there", }, IFS },
     171      { 0, NULL, "\"$(echo \"hello there\")\"", 0, 1, { "hello there", }, IFS },
     172      { 0, NULL, "${var=one two} \"$var\"", 0, 3, { "one", "two", "one two", }, IFS },
     173      { 0, "1", "$(( $(echo 3)+$var ))", 0, 1, { "4", }, IFS },
     174      { 0, NULL, "\"$(echo \"*\")\"", 0, 1, { "*", }, IFS },
     175      { 0, NULL, "\"a\n\n$(echo)b\"", 0, 1, { "a\n\nb", }, IFS },
     176      { 0, "foo", "*$var*", 0, 1, { "*foo*", }, IFS },
     177      { 0, "o thr", "*$var*", 0, 2, { "two", "three" }, IFS },
     178  
     179      /* Different IFS values */
     180      { 0, "a b\tc\nd  ", "$var", 0, 4, { "a", "b", "c", "d" }, NULL /* unset */ },
     181      { 0, "a b\tc d  ", "$var", 0, 1, { "a b\tc d  " }, "" /* `null' */ },
     182      { 0, "a,b c\n, d", "$var", 0, 3, { "a", "b c", " d" }, "\t\n," },
     183  
     184      /* Other things that should succeed */
     185      { 0, NULL, "\\*\"|&;<>\"\\(\\)\\{\\}", 0, 1, { "*|&;<>(){}", }, IFS },
     186      { 0, "???", "$var", 0, 1, { "???", }, IFS },
     187      { 0, NULL, "$var", 0, 0, { NULL, }, IFS },
     188      { 0, NULL, "\"\\n\"", 0, 1, { "\\n", }, IFS },
     189      { 0, NULL, "", 0, 0, { NULL, }, IFS },
     190      { 0, NULL, "${1234567890123456789012}", 0, 0, { NULL, }, IFS },
     191  
     192      /* Flags not already covered (testit() has special handling for these) */
     193      { 0, NULL, "one two", WRDE_DOOFFS, 2, { "one", "two", }, IFS },
     194      { 0, NULL, "appended", WRDE_APPEND, 3, { "pre1", "pre2", "appended", }, IFS },
     195      { 0, NULL, "appended", WRDE_DOOFFS|WRDE_APPEND, 3, { "pre1", "pre2", "appended", }, IFS },
     196  
     197      /* Things that should fail */
     198      { WRDE_BADCHAR, NULL, "new\nline", 0, 0, { NULL, }, "" /* \n not IFS */ },
     199      { WRDE_BADCHAR, NULL, "pipe|symbol", 0, 0, { NULL, }, IFS },
     200      { WRDE_BADCHAR, NULL, "&ampersand", 0, 0, { NULL, }, IFS },
     201      { WRDE_BADCHAR, NULL, "semi;colon", 0, 0, { NULL, }, IFS },
     202      { WRDE_BADCHAR, NULL, "<greater", 0, 0, { NULL, }, IFS },
     203      { WRDE_BADCHAR, NULL, "less>", 0, 0, { NULL, }, IFS },
     204      { WRDE_BADCHAR, NULL, "(open-paren", 0, 0, { NULL, }, IFS },
     205      { WRDE_BADCHAR, NULL, "close-paren)", 0, 0, { NULL, }, IFS },
     206      { WRDE_BADCHAR, NULL, "{open-brace", 0, 0, { NULL, }, IFS },
     207      { WRDE_BADCHAR, NULL, "close-brace}", 0, 0, { NULL, }, IFS },
     208      { WRDE_BADVAL, NULL, "$var", WRDE_UNDEF, 0, { NULL, }, IFS },
     209      { WRDE_BADVAL, NULL, "$9", WRDE_UNDEF, 0, { NULL, }, IFS },
     210      { WRDE_SYNTAX, NULL, "$[50+20))", 0, 0, { NULL, }, IFS },
     211      { WRDE_SYNTAX, NULL, "${%%noparam}", 0, 0, { NULL, }, IFS },
     212      { WRDE_SYNTAX, NULL, "${missing-brace", 0, 0, { NULL, }, IFS },
     213      { WRDE_SYNTAX, NULL, "$(for i in)", 0, 0, { NULL, }, IFS },
     214      { WRDE_SYNTAX, NULL, "$((2+))", 0, 0, { NULL, }, IFS },
     215      { WRDE_SYNTAX, NULL, "`", 0, 0, { NULL, }, IFS },
     216      { WRDE_SYNTAX, NULL, "$((010+4+))", 0, 0, { NULL }, IFS },
     217  
     218      { WRDE_SYNTAX, NULL, "`\\", 0, 0, { NULL, }, IFS },     /* BZ 18042  */
     219      { WRDE_SYNTAX, NULL, "${", 0, 0, { NULL, }, IFS },      /* BZ 18043  */
     220      { WRDE_SYNTAX, NULL, "L${a:", 0, 0, { NULL, }, IFS },   /* BZ 18043#c4  */
     221    };
     222  
     223  static int testit (struct test_case_struct *tc);
     224  static int tests;
     225  
     226  static void
     227  command_line_test (const char *words)
     228  {
     229    wordexp_t we;
     230    int i;
     231    int retval = wordexp (words, &we, 0);
     232    printf ("info: wordexp returned %d\n", retval);
     233    for (i = 0; i < we.we_wordc; i++)
     234      printf ("info: we_wordv[%d] = \"%s\"\n", i, we.we_wordv[i]);
     235  }
     236  
     237  static int
     238  do_test (int argc, char *argv[])
     239  {
     240    const char *globfile[] = { "one", "two", "three" };
     241    char tmpdir[32];
     242    struct passwd *pw;
     243    const char *cwd;
     244    int test;
     245    struct test_case_struct ts;
     246  
     247    if (argc > 1)
     248      {
     249        command_line_test (argv[1]);
     250        return 0;
     251      }
     252  
     253    cwd = getcwd (NULL, 0);
     254  
     255    /* Set up arena for pathname expansion */
     256    if (!tmpnam (tmpdir))
     257      {
     258        printf ("Failed to create a temporary directory with a unique name: %m");
     259        return 1;
     260      }
     261    xmkdir (tmpdir, S_IRWXU);
     262    TEST_VERIFY_EXIT (chdir (tmpdir) == 0);
     263  
     264    for (int i = 0; i < array_length (globfile); ++i)
     265      {
     266        int fd = xopen (globfile[i], O_WRONLY|O_CREAT|O_TRUNC,
     267  		      S_IRUSR | S_IWUSR);
     268        xclose (fd);
     269      }
     270  
     271    for (test = 0; test < array_length (test_case); test++)
     272      TEST_COMPARE (testit (&test_case[test]), 0);
     273  
     274    /* Tilde-expansion tests. */
     275    pw = getpwnam ("root");
     276    if (pw != NULL)
     277      {
     278        ts.retval = 0;
     279        ts.env = NULL;
     280        ts.words = "~root ";
     281        ts.flags = 0;
     282        ts.wordc = 1;
     283        ts.wordv[0] = pw->pw_dir;
     284        ts.ifs = IFS;
     285  
     286        TEST_COMPARE (testit (&ts), 0);
     287  
     288        ts.retval = 0;
     289        ts.env = pw->pw_dir;
     290        ts.words = "${var#~root}x";
     291        ts.flags = 0;
     292        ts.wordc = 1;
     293        ts.wordv[0] = "x";
     294        ts.ifs = IFS;
     295  
     296        TEST_COMPARE (testit (&ts), 0);
     297      }
     298  
     299    /* "~" expands to value of $HOME when HOME is set */
     300  
     301    setenv ("HOME", "/dummy/home", 1);
     302    ts.retval = 0;
     303    ts.env = NULL;
     304    ts.words = "~ ~/foo";
     305    ts.flags = 0;
     306    ts.wordc = 2;
     307    ts.wordv[0] = "/dummy/home";
     308    ts.wordv[1] = "/dummy/home/foo";
     309    ts.ifs = IFS;
     310  
     311    TEST_COMPARE (testit (&ts), 0);
     312  
     313    /* "~" expands to home dir from passwd file if HOME is not set */
     314  
     315    pw = getpwuid (getuid ());
     316    if (pw != NULL)
     317      {
     318        unsetenv ("HOME");
     319        ts.retval = 0;
     320        ts.env = NULL;
     321        ts.words = "~";
     322        ts.flags = 0;
     323        ts.wordc = 1;
     324        ts.wordv[0] = pw->pw_dir;
     325        ts.ifs = IFS;
     326  
     327        TEST_COMPARE (testit (&ts), 0);
     328      }
     329  
     330    puts ("tests completed, now cleaning up");
     331  
     332    /* Clean up */
     333    for (int i = 0; i < array_length (globfile); ++i)
     334      remove (globfile[i]);
     335  
     336    if (cwd == NULL)
     337      cwd = "..";
     338  
     339    xchdir (cwd);
     340    rmdir (tmpdir);
     341  
     342    return 0;
     343  }
     344  
     345  struct support_next_to_fault
     346  at_page_end (const char *words)
     347  {
     348    const size_t words_size = strlen (words) + 1;
     349    struct support_next_to_fault ntf
     350      = support_next_to_fault_allocate (words_size);
     351  
     352    /* Includes terminating NUL.  */
     353    memcpy (ntf.buffer, words, words_size);
     354  
     355    return ntf;
     356  }
     357  
     358  static int
     359  testit (struct test_case_struct *tc)
     360  {
     361    int retval;
     362    wordexp_t we, sav_we;
     363    char *dummy;
     364    int bzzzt = 0;
     365    int start_offs = 0;
     366    int i;
     367  
     368    if (tc->env)
     369      setenv ("var", tc->env, 1);
     370    else
     371      unsetenv ("var");
     372  
     373    if (tc->ifs)
     374      setenv ("IFS", tc->ifs, 1);
     375    else
     376      unsetenv ("IFS");
     377  
     378    sav_we.we_wordc = 99;
     379    sav_we.we_wordv = &dummy;
     380    sav_we.we_offs = 3;
     381    we = sav_we;
     382  
     383    printf ("info: test %d (%s): ", ++tests, tc->words);
     384    fflush (NULL);
     385    struct support_next_to_fault words = at_page_end (tc->words);
     386  
     387    if (tc->flags & WRDE_APPEND)
     388      {
     389        /* initial wordexp() call, to be appended to */
     390        if (wordexp ("pre1 pre2", &we, tc->flags & ~WRDE_APPEND) != 0)
     391          {
     392  	  printf ("info: FAILED setup\n");
     393  	  return 1;
     394  	}
     395      }
     396    retval = wordexp (words.buffer, &we, tc->flags);
     397  
     398    if (tc->flags & WRDE_DOOFFS)
     399        start_offs = sav_we.we_offs;
     400  
     401    if (retval != tc->retval || (retval == 0 && we.we_wordc != tc->wordc))
     402      bzzzt = 1;
     403    else if (retval == 0)
     404      {
     405        for (i = 0; i < start_offs; ++i)
     406  	if (we.we_wordv[i] != NULL)
     407  	  {
     408  	    bzzzt = 1;
     409  	    break;
     410  	  }
     411  
     412        for (i = 0; i < we.we_wordc; ++i)
     413  	if (we.we_wordv[i+start_offs] == NULL
     414  	    || strcmp (tc->wordv[i], we.we_wordv[i+start_offs]) != 0)
     415  	  {
     416  	    bzzzt = 1;
     417  	    break;
     418  	  }
     419      }
     420  
     421    if (bzzzt)
     422      {
     423        printf ("FAILED\n");
     424        printf ("info: Test words: <%s>, need retval %d, wordc %zd\n",
     425  	      tc->words, tc->retval, tc->wordc);
     426        if (start_offs != 0)
     427  	printf ("(preceded by %d NULLs)\n", start_offs);
     428        printf ("Got retval %d, wordc %zd: ", retval, we.we_wordc);
     429        if (retval == 0 || retval == WRDE_NOSPACE)
     430  	{
     431  	  for (i = 0; i < we.we_wordc + start_offs; ++i)
     432  	    if (we.we_wordv[i] == NULL)
     433  	      printf ("NULL ");
     434  	    else
     435  	      printf ("<%s> ", we.we_wordv[i]);
     436  	}
     437        printf ("\n");
     438      }
     439    else if (retval != 0 && retval != WRDE_NOSPACE
     440  	   && (we.we_wordc != sav_we.we_wordc
     441  	       || we.we_wordv != sav_we.we_wordv
     442  	       || we.we_offs != sav_we.we_offs))
     443      {
     444        bzzzt = 1;
     445        printf ("FAILED to restore wordexp_t members\n");
     446      }
     447    else
     448      printf ("OK\n");
     449  
     450    if (retval == 0 || retval == WRDE_NOSPACE)
     451      wordfree (&we);
     452  
     453    support_next_to_fault_free (&words);
     454  
     455    fflush (NULL);
     456    return bzzzt;
     457  }
     458  
     459  #define TEST_FUNCTION_ARGV do_test
     460  #include <support/test-driver.c>