about summary refs log tree commit diff
path: root/posix
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>1999-04-12 09:07:36 +0000
committerUlrich Drepper <drepper@redhat.com>1999-04-12 09:07:36 +0000
commitbac660f87b7559d89d25d3522563525d6c5e9ebf (patch)
treeb04eb7a5d70421659c57dac8d516c66fcb02a4f8 /posix
parenta64e578b6fdb18c530d142a68a7e57eb04038b5d (diff)
downloadglibc-bac660f87b7559d89d25d3522563525d6c5e9ebf.tar.gz
glibc-bac660f87b7559d89d25d3522563525d6c5e9ebf.tar.xz
glibc-bac660f87b7559d89d25d3522563525d6c5e9ebf.zip
Update.
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.
Diffstat (limited to 'posix')
-rw-r--r--posix/wordexp-test.c9
-rw-r--r--posix/wordexp.c311
2 files changed, 205 insertions, 115 deletions
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 */