about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--posix/wordexp-test.c9
-rw-r--r--posix/wordexp.c311
3 files changed, 218 insertions, 115 deletions
diff --git a/ChangeLog b/ChangeLog
index 8988cbb566..cdef8819fb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+1999-04-11  Tim Waugh  <tim@cyberelk.demon.co.uk>
+
+	* posix/wordexp.c (wordexp): Fix a leak when an invalid character
+	is seen, as well as fixing semantics.  Don't reset the word count
+	to zero when an invalid character is seen, but leave it as it was
+	(this makes a difference with WRDE_APPEND).
+
+	* posix/wordexp-test.c: More test cases.
+
+	* posix/wordexp.c (parse_param): In words like ${var#pattern},
+	always expand pattern when it is needed.  Also, handle quoting in
+	pattern properly.
+
 1999-04-12  Philip Blundell  <philb@gnu.org>
 
 	* elf/elf.h: Update ARM definitions to match current gas2.
diff --git a/posix/wordexp-test.c b/posix/wordexp-test.c
index 8e709c7569..21bca96681 100644
--- a/posix/wordexp-test.c
+++ b/posix/wordexp-test.c
@@ -81,6 +81,7 @@ struct test_case_struct
     { 0, NULL, "\"quoted\"", 0, 1, { "quoted", }, IFS },
     { 0, "foo", "\"$var\"\"$var\"", 0, 1, { "foofoo", }, IFS },
     { 0, NULL, "'singly-quoted'", 0, 1, { "singly-quoted", }, IFS },
+    { 0, NULL, "contin\\\nuation", 0, 1, { "continuation", }, IFS },
 
     /* Simple command substitution */
     { 0, NULL, "$(echo hello)", 0, 1, { "hello", }, IFS },
@@ -118,10 +119,16 @@ struct test_case_struct
     { 0, NULL, "${var:-'}'}", 0, 1, { "}", }, IFS },
     { 0, NULL, "${var-}", 0, 0, { NULL }, IFS },
 
+    { 0, "pizza", "${var#${var}}", 0, 0, { NULL }, IFS },
+    { 0, "pepperoni", "${var%$(echo oni)}", 0, 1, { "pepper" }, IFS },
+    { 0, "6pack", "${var#$((6))}", 0, 1, { "pack" }, IFS },
+    { 0, "b*witched", "${var##b*}", 0, 0, { NULL }, IFS },
+    { 0, "b*witched", "${var##\"b*\"}", 0, 1, { "witched" }, IFS },
     { 0, "banana", "${var%na*}", 0, 1, { "bana", }, IFS },
     { 0, "banana", "${var%%na*}", 0, 1, { "ba", }, IFS },
     { 0, "borabora-island", "${var#*bora}", 0, 1, { "bora-island", }, IFS },
-    { 0, "borabora-island", "${var##*bora}", 0, 1, {"-island", }, IFS },
+    { 0, "borabora-island", "${var##*bora}", 0, 1, { "-island", }, IFS },
+    { 0, "coconut", "${var##\\*co}", 0, 1, { "coconut", }, IFS },
     { 0, "100%", "${var%0%}", 0, 1, { "10" }, IFS },
 
     /* Pathname expansion */
diff --git a/posix/wordexp.c b/posix/wordexp.c
index 4a6dd7cbd0..aa5b94c3c0 100644
--- a/posix/wordexp.c
+++ b/posix/wordexp.c
@@ -74,7 +74,7 @@ static int eval_expr (char *expr, long int *result) internal_function;
 
 #define W_CHUNK	(100)
 
-/* Result of w_newword will be ignored if it the last word. */
+/* Result of w_newword will be ignored if it's the last word. */
 static inline char *
 w_newword (size_t *actlen, size_t *maxlen)
 {
@@ -1203,7 +1203,7 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
 	  goto syntax;
 	}
 
-      /* Now collect the pattern. */
+      /* Now collect the pattern, but don't expand it yet. */
       ++*offset;
       for (; words[*offset]; ++(*offset))
 	{
@@ -1224,8 +1224,18 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
 	      break;
 
 	    case '\\':
-	      if (!pattern_is_quoted && words[++*offset] == '\0')
+	      if (pattern_is_quoted)
+		/* Quoted; treat as normal character. */
+		break;
+
+	      /* Otherwise, it's an escape: next character is literal. */
+	      if (words[++*offset] == '\0')
 		goto syntax;
+
+	      pattern = w_addchar (pattern, &pat_length, &pat_maxlen, '\\');
+	      if (pattern == NULL)
+		goto no_space;
+
 	      break;
 
 	    case '\'':
@@ -1383,6 +1393,153 @@ envsubst:
 
   if (action != ACT_NONE)
     {
+      int expand_pattern = 0;
+
+      /* First, find out if we need to expand pattern (i.e. if we will
+       * use it). */
+      switch (action)
+	{
+	case ACT_RP_SHORT_LEFT:
+	case ACT_RP_LONG_LEFT:
+	case ACT_RP_SHORT_RIGHT:
+	case ACT_RP_LONG_RIGHT:
+	  /* Always expand for these. */
+	  expand_pattern = 1;
+	  break;
+
+	case ACT_NULL_ERROR:
+	case ACT_NULL_SUBST:
+	case ACT_NULL_ASSIGN:
+	  if (!value || (!*value && colon_seen))
+	    /* If param is unset, or set but null and a colon has been seen,
+	       the expansion of the pattern will be needed. */
+	    expand_pattern = 1;
+
+	  break;
+
+	case ACT_NONNULL_SUBST:
+	  /* Expansion of word will be needed if parameter is set and not null,
+	     or set null but no colon has been seen. */
+	  if (value && (*value || !colon_seen))
+	    expand_pattern = 1;
+
+	  break;
+
+	default:
+	  assert (! "Unrecognised action!");
+	}
+
+      if (expand_pattern)
+	{
+	  /* We need to perform tilde expansion, parameter expansion,
+             command substitution, and arithmetic expansion.  We also
+	     have to be a bit careful with wildcard characters, as
+	     pattern might be given to fnmatch soon.  To do this, we
+	     convert quotes to escapes. */
+
+	  char *expanded;
+	  size_t exp_len;
+	  size_t exp_maxl;
+	  char *p;
+	  int quoted = 0; /* 1: single quotes; 2: double */
+
+	  expanded = w_newword (&exp_len, &exp_maxl);
+	  for (p = pattern; p && *p; p++)
+	    {
+	      int offset;
+
+	      switch (*p)
+		{
+		case '"':
+		  if (quoted == 2)
+		    quoted = 0;
+		  else if (quoted == 0)
+		    quoted = 2;
+		  else break;
+
+		  continue;
+
+		case '\'':
+		  if (quoted == 1)
+		    quoted = 0;
+		  else if (quoted == 0)
+		    quoted = 1;
+		  else break;
+
+		  continue;
+
+		case '*':
+		case '?':
+		  if (quoted)
+		    {
+		      /* Convert quoted wildchar to escaped wildchar. */
+		      expanded = w_addchar (expanded, &exp_len,
+					    &exp_maxl, '\\');
+
+		      if (expanded == NULL)
+			goto no_space;
+		    }
+		  break;
+
+		case '$':
+		  offset = 0;
+		  error = parse_dollars (&expanded, &exp_len, &exp_maxl, p,
+					 &offset, flags, NULL, NULL, NULL, 1);
+		  if (error)
+		    {
+		      if (free_value)
+			free (value);
+
+		      if (expanded)
+			free (expanded);
+
+		      goto do_error;
+		    }
+
+		  p += offset;
+		  continue;
+
+		case '~':
+		  if (quoted || exp_len)
+		    break;
+
+		  offset = 0;
+		  error = parse_tilde (&expanded, &exp_len, &exp_maxl, p,
+				       &offset, 0);
+		  if (error)
+		    {
+		      if (free_value)
+			free (value);
+
+		      if (expanded)
+			free (expanded);
+
+		      goto do_error;
+		    }
+
+		  p += offset;
+		  continue;
+
+		case '\\':
+		  expanded = w_addchar (expanded, &exp_len, &exp_maxl, '\\');
+		  ++p;
+		  assert (*p); /* checked when extracted initially */
+		  if (expanded == NULL)
+		    goto no_space;
+		}
+
+	      expanded = w_addchar (expanded, &exp_len, &exp_maxl, *p);
+
+	      if (expanded == NULL)
+		goto no_space;
+	    }
+
+	  if (pattern)
+		  free (pattern);
+
+	  pattern = expanded;
+	}
+
       switch (action)
 	{
 	case ACT_RP_SHORT_LEFT:
@@ -1521,33 +1678,12 @@ envsubst:
 	    /* Substitute parameter */
 	    break;
 
+	  error = 0;
 	  if (!colon_seen && value)
 	    /* Substitute NULL */
-	    error = 0;
+	    ;
 	  else if (*pattern)
-	    {
-	      /* Expand 'pattern' and write it to stderr */
-	      wordexp_t	we;
-
-	      error = wordexp (pattern, &we, flags);
-
-	      if (error == 0)
-		{
-		  int i;
-
-		  fprintf (stderr, "%s:", env);
-
-		  for (i = 0; i < we.we_wordc; ++i)
-		    {
-		      fprintf (stderr, " %s", we.we_wordv[i]);
-		    }
-
-		  fprintf (stderr, "\n");
-		  error = WRDE_BADVAL;
-		}
-
-	      wordfree (&we);
-	    }
+	    fprintf (stderr, "%s: %s\n", env, pattern);
 	  else
 	    {
 	      fprintf (stderr, "%s: parameter null or not set\n", env);
@@ -1563,95 +1699,35 @@ envsubst:
 	    /* Substitute parameter */
 	    break;
 
-	  if (!colon_seen && value)
-	    {
-	      /* Substitute NULL */
-	      if (free_value)
-		free (value);
-	      goto success;
-	    }
-
-	subst_word:
-	  {
-	    /* Substitute word */
-	    wordexp_t we;
-	    int i;
-
-	    if (free_value)
-	      free (value);
-
-	    if (quoted)
-	      {
-		/* No field-splitting is allowed, so imagine
-		   quotes around the word.  */
-		char *qtd_pattern = malloc (3 + strlen (pattern));
-		if (qtd_pattern)
-		  sprintf (qtd_pattern, "\"%s\"", pattern);
-		free (pattern);
-		pattern = qtd_pattern;
-	      }
-
-	    if (pattern == NULL && (pattern = __strdup ("")) == NULL)
-	      goto no_space;
-
-	    error = wordexp (pattern, &we, flags);
-	    if (error)
-	      goto do_error;
-
-	    /* Fingers crossed that the quotes worked.. */
-	    assert (!quoted || we.we_wordc == 1);
-
-	    /* Substitute */
-	    for (i = 0; i < we.we_wordc; ++i)
-	      if ((error = w_addword (pwordexp, __strdup (we.we_wordv[i])))
-		  != 0)
-		break;
-
-	    if (i < we.we_wordc)
-	      {
-		/* Ran out of space */
-		wordfree (&we);
-		goto do_error;
-	      }
-
-	    if (action == ACT_NULL_ASSIGN)
-	      {
-		char *words;
-		char *cp;
-		size_t words_size = 0;
+	  if (free_value && value)
+	    free (value);
 
-		if (special)
-		  /* Cannot assign special parameters. */
-		  goto syntax;
+	  if (!colon_seen && value)
+	    /* Substitute NULL */
+	    goto success;
 
-		for (i = 0; i < we.we_wordc; i++)
-		  words_size += strlen (we.we_wordv[i]) + 1; /* for <space> */
-		words_size++;
+	  value = pattern ? __strdup (pattern) : pattern;
+	  free_value = 1;
 
-		cp = words = __alloca (words_size);
-		*words = 0;
-		for (i = 0; i < we.we_wordc - 1; i++)
-		  {
-		    cp = __stpcpy (cp, we.we_wordv[i]);
-		    *cp++ = ' ';
-		  }
+	  if (pattern && !value)
+	    goto no_space;
 
-		strcpy (cp, we.we_wordv[i]);
+	  break;
 
-		/* Also assign */
-		setenv (env, words, 1);
-	      }
+	case ACT_NONNULL_SUBST:
+	  if (value && (*value || !colon_seen))
+	    {
+	      if (free_value && value)
+		free (value);
 
-	    wordfree (&we);
-	    goto success;
-	  }
+	      value = pattern ? __strdup (pattern) : pattern;
+	      free_value = 1;
 
-	case ACT_NONNULL_SUBST:
-	  if (value && *value)
-	    goto subst_word;
+	      if (pattern && !value)
+		goto no_space;
 
-	  if (!colon_seen && value)
-	    goto subst_word;
+	      break;
+	    }
 
 	  /* Substitute NULL */
 	  if (free_value)
@@ -1671,8 +1747,17 @@ envsubst:
 	      goto success;
 	    }
 
-	  /* This checks for '=' so it knows to assign */
-	  goto subst_word;
+	  if (free_value && value)
+	    free (value);
+
+	  value = pattern ? __strdup (pattern) : pattern;
+	  free_value = 1;
+
+	  if (pattern && !value)
+	    goto no_space;
+
+	  setenv (env, value, 1);
+	  break;
 
 	default:
 	  assert (! "Unrecognised action!");
@@ -2190,10 +2275,8 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
 	    if (strchr ("\n|&;<>(){}", ch))
 	      {
 		/* Fail */
-		wordfree (pwordexp);
-		pwordexp->we_wordc = 0;
-		pwordexp->we_wordv = old_wordv;
-		return WRDE_BADCHAR;
+		error = WRDE_BADCHAR;
+		goto do_error;
 	      }
 
 	    /* "Ordinary" character -- add it to word */