about summary refs log tree commit diff
path: root/posix
diff options
context:
space:
mode:
Diffstat (limited to 'posix')
-rw-r--r--posix/wordexp.c430
1 files changed, 202 insertions, 228 deletions
diff --git a/posix/wordexp.c b/posix/wordexp.c
index 443dc4697d..54f830cc2c 100644
--- a/posix/wordexp.c
+++ b/posix/wordexp.c
@@ -973,45 +973,63 @@ parse_comm (char **word, size_t *word_length, size_t *max_length,
 {
   /* We are poised just after "$(" */
   int paren_depth = 1;
-  int error;
+  int error = 0;
   size_t comm_length = 0;
   size_t comm_maxlen = 0;
   char *comm = NULL;
+  int quoted = 0; /* 1 for singly-quoted, 2 for doubly-quoted */
 
   for (; words[*offset]; ++(*offset))
     {
       switch (words[*offset])
 	{
+	case '\'':
+	  if (quoted == 0)
+	    quoted = 1;
+	  else if (quoted == 1)
+	    quoted = 0;
+
+	  break;
+
+	case '"':
+	  if (quoted == 0)
+	    quoted = 2;
+	  else if (quoted == 2)
+	    quoted = 0;
+
+	  break;
+
 	case ')':
-	  if (--paren_depth == 0)
+	  if (!quoted && --paren_depth == 0)
 	    {
 	      /* Go -- give script to the shell */
-	      error = exec_comm (comm, word, word_length, max_length, flags,
-				 pwordexp, ifs, ifs_white);
-	      free (comm);
+	      if (comm)
+		{
+		  error = exec_comm (comm, word, word_length, max_length,
+				     flags, pwordexp, ifs, ifs_white);
+		  free (comm);
+		}
+
 	      return error;
 	    }
 
 	  /* This is just part of the script */
-	  comm = w_addchar (comm, &comm_length, &comm_maxlen, words[*offset]);
-	  if (comm == NULL)
-	    return WRDE_NOSPACE;
-
 	  break;
 
 	case '(':
-	  ++paren_depth;
-	default:
-	  comm = w_addchar (comm, &comm_length, &comm_maxlen, words[*offset]);
-	  if (comm == NULL)
-	    return WRDE_NOSPACE;
-
-	  break;
+	  if (!quoted)
+	    ++paren_depth;
 	}
+
+      comm = w_addchar (comm, &comm_length, &comm_maxlen, words[*offset]);
+      if (comm == NULL)
+	return WRDE_NOSPACE;
     }
 
   /* Premature end */
-  free (comm);
+  if (comm)
+    free (comm);
+
   return WRDE_SYNTAX;
 }
 
@@ -1022,73 +1040,100 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
 	     const char *ifs, const char *ifs_white, int quoted)
 {
   /* We are poised just after "$" */
-  enum remove_pattern_enum
+  enum action
   {
-    RP_NONE = 0,
-    RP_SHORT_LEFT,
-    RP_LONG_LEFT,
-    RP_SHORT_RIGHT,
-    RP_LONG_RIGHT
+    ACT_NONE,
+    ACT_RP_SHORT_LEFT = '#',
+    ACT_RP_LONG_LEFT = 'L',
+    ACT_RP_SHORT_RIGHT = '%',
+    ACT_RP_LONG_RIGHT = 'R',
+    ACT_NULL_ERROR = '?',
+    ACT_NULL_SUBST = '-',
+    ACT_NONNULL_SUBST = '+',
+    ACT_NULL_ASSIGN = '='
   };
-  size_t start = *offset;
   size_t env_length = 0;
   size_t env_maxlen = 0;
   size_t pat_length = 0;
   size_t pat_maxlen = 0;
+  size_t start = *offset;
   char *env = NULL;
   char *pattern = NULL;
   char *value = NULL;
-  char action = '\0';
-  enum remove_pattern_enum remove = RP_NONE;
-  int colon_seen = 0;
+  enum action action = ACT_NONE;
   int depth = 0;
+  int colon_seen = 0;
   int seen_hash = 0;
   int free_value = 0;
+  int pattern_is_quoted = 0; /* 1 for singly-quoted, 2 for doubly-quoted */
   int error;
+  int brace = words[*offset] == '{';
+
+  if (brace)
+    ++*offset;
 
   for (; words[*offset]; ++(*offset))
     {
-      switch (words[*offset])
-	{
-	case '{':
-	  ++depth;
+      int special;
 
-	  if (action != '\0' || remove != RP_NONE)
+      if (action != ACT_NONE)
+	{
+	  switch (words[*offset])
 	    {
-	      pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
-				   words[*offset]);
-	      if (pattern == NULL)
-		goto no_space;
+	    case '{':
+	      if (!pattern_is_quoted)
+		++depth;
+	      break;
 
+	    case '}':
+	      if (!pattern_is_quoted)
+		{
+		  if (depth == 0)
+		    goto envsubst;
+		  --depth;
+		}
 	      break;
-	    }
 
-	  if (*offset == start)
-	    break;
+	    case '\\':
+	      if (!pattern_is_quoted && words[++*offset] == '\0')
+		goto syntax;
+	      break;
 
-	  /* Otherwise evaluate */
-	  /* (and re-parse this character) */
-	  --(*offset);
-	  goto envsubst;
+	    case '\'':
+	      if (pattern_is_quoted == 0)
+		pattern_is_quoted = 1;
+	      else if (pattern_is_quoted == 1)
+		pattern_is_quoted = 0;
 
-	case '}':
-	  if (words[start] != '{')
-	      --(*offset);
+	      break;
 
-	  if (action != '\0' || remove != RP_NONE)
-	    {
-	      if (--depth)
-		{
-		  pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
-				       words[*offset]);
-		  if (pattern == NULL)
-		    goto no_space;
+	    case '"':
+	      if (pattern_is_quoted == 0)
+		pattern_is_quoted = 2;
+	      else if (pattern_is_quoted == 2)
+		pattern_is_quoted = 0;
 
-		  break;
-		}
+	      break;
 	    }
 
-	  /* Evaluate */
+	  pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
+			       words[*offset]);
+	  if (pattern == NULL)
+	    goto no_space;
+
+	  continue;
+	}
+
+      switch (words[*offset])
+	{
+	case '}':
+	  if (!brace)
+	    goto end_of_word;
+
+	  if (env == NULL)
+	    goto syntax;
+
+	  /* Evaluate. */
 	  goto envsubst;
 
 	case '#':
@@ -1100,173 +1145,104 @@ parse_param (char **word, size_t *word_length, size_t *max_length,
 	      goto envsubst;
 	    }
 
-	  if (words[start] != '{')
-	    {
-	      /* Evaluate */
-	      /* (and re-parse this character) */
-	      --(*offset);
-	      goto envsubst;
-	    }
+	  if (!brace)
+	    /* Evaluate */
+	    /* (and re-parse this character) */
+	    goto end_of_word;
 
 	  /* At the start? (i.e. 'string length') */
-	  if (*offset == start + 1)
+	  if (env == NULL)
 	    {
 	      seen_hash = 1;
-	      break;
+	      continue;
 	    }
 	  else if (seen_hash)
 	    goto syntax;
 
-	  /* Separating variable name from prefix pattern? */
-	  if (remove == RP_NONE)
-	    {
-	      remove = RP_SHORT_LEFT;
-	      break;
-	    }
-	  else if (remove == RP_SHORT_LEFT)
+	  action = ACT_RP_SHORT_LEFT;
+	  if (words[1 + *offset] == '#')
 	    {
-	      remove = RP_LONG_LEFT;
-	      break;
+	      ++*offset;
+	      action = ACT_RP_LONG_LEFT;
 	    }
 
-	  /* Must be part of prefix/suffix pattern. */
-	  pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
-			       words[*offset]);
-	  if (pattern == NULL)
-	    goto no_space;
-
-	  break;
+	  continue;
 
 	case '%':
+	  if (!brace)
+	    /* Re-parse this character after substitution */
+	    goto end_of_word;
+
 	  if (!env || !*env)
 	    goto syntax;
 
-	  /* Separating variable name from suffix pattern? */
-	  if (remove == RP_NONE)
-	    {
-	      remove = RP_SHORT_RIGHT;
-	      break;
-	    }
-	  else if (remove == RP_SHORT_RIGHT)
+	  if (seen_hash)
+	    goto syntax;
+
+	  action = ACT_RP_SHORT_RIGHT;
+	  if (words[1 + *offset] == '%')
 	    {
-	      remove = RP_LONG_RIGHT;
-	      break;
+	      ++*offset;
+	      action = ACT_RP_LONG_RIGHT;
 	    }
 
-	  /* Must be part of prefix/suffix pattern. */
-	  pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
-			       words[*offset]);
-	  if (pattern == NULL)
-	    goto no_space;
-
-	  break;
+	  continue;
 
 	case ':':
+	  if (!brace)
+	    goto end_of_word;
+
 	  if (!env || !*env)
 	    goto syntax;
 
-	  if (action != '\0' || remove != RP_NONE)
-	    {
-	      pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
-				   words[*offset]);
-	      if (pattern == NULL)
-		goto no_space;
-
-	      break;
-	    }
+	  if (seen_hash)
+	    goto syntax;
 
-	  if ((words[1 + *offset] == '-') || (words[1 + *offset] == '=')
-	      || (words[1 + *offset] == '?') || (words[1 + *offset] == '+'))
-	    {
-	      colon_seen = 1;
-	      break;
-	    }
+	  if (words[1 + *offset] != '-' && words[1 + *offset] != '='
+	      && words[1 + *offset] != '?' && words[1 + *offset] != '+')
+	    goto syntax;
 
-	  goto syntax;
+	  colon_seen = 1;
+	  action = words[++*offset];
+	  continue;
 
 	case '-':
 	case '=':
 	case '?':
 	case '+':
-	  if (!env || !*env)
-	    goto syntax;
+	  if (!brace)
+	    goto end_of_word;
 
-	  if (seen_hash)
+	  if (!env || !*env)
 	    goto syntax;
 
-	  if (action != '\0' || remove != RP_NONE)
-	    {
-	      pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
-				   words[*offset]);
-	      if (pattern == NULL)
-		goto no_space;
-
-	      break;
-	    }
-
 	  action = words[*offset];
-	  break;
-
-	case '\\':
-	  if (action != '\0' || remove != RP_NONE)
-	    {
-	      /* Um. Is this right? */
-	      error = parse_qtd_backslash (word, word_length, max_length,
-					   words, offset);
-	      if (error == 0)
-		break;
-	    }
-	  else
-	    {
-	      error = WRDE_SYNTAX;
-	    }
-
-	  if (env)
-	    free (env);
-
-	  if (pattern != NULL)
-	    free (pattern);
-
-	  return error;
-
-	default:
-	  if (action != '\0' || remove != RP_NONE)
-	    {
-	      pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
-				   words[*offset]);
-	      if (pattern == NULL)
-		goto no_space;
-
-	      break;
-	    }
-	  else
-	    {
-	      int special = (strchr ("*@$", words[*offset]) != NULL
-			     || isdigit (words[*offset]));
+	  continue;
+	}
 
-	      if (isalpha (words[*offset]) || special)
-		{
-		  env = w_addchar (env, &env_length, &env_maxlen,
-				   words[*offset]);
-		  if (env == NULL)
-		    goto no_space;
+      special = (strchr ("*@$", words[*offset]) != NULL
+		 || isdigit (words[*offset]));
 
-		  if (special && words[start] != '{')
-		    goto envsubst;
+      if (!isalpha (words[*offset]) && !special)
+	/* Stop and evaluate, remembering char we stopped at */
+	break;
 
-		  /* Keep going (get next char) */
-		  break;
-		}
+      env = w_addchar (env, &env_length, &env_maxlen,
+			   words[*offset]);
+      if (env == NULL)
+	goto no_space;
 
-	      /* Stop and evaluate, remembering char we stopped at */
-	      --(*offset);
-	      goto envsubst;
-	    }
+      if (special)
+	{
+	  if (brace)
+	    ++*offset;
+	  goto envsubst;
 	}
     }
 
   /* End of input string -- remember to reparse the character that we stopped
    * at.  */
+end_of_word:
   --(*offset);
 
 envsubst:
@@ -1381,53 +1357,39 @@ envsubst:
       free (env);
 
       /* Each parameter is a separate word ("$@") */
-      if (__libc_argv[0] == NULL)
-	{
-	  /* This can happen if the application is started without any
-	     parameter, not even a name.  This is legal according to
-	     POSIX since the giving parameters is only a "should" rule.  */
-	  *word = __strdup ("");
-	  *max_length = *word_length = 0;
-	}
-      else
+      if (__libc_argv[0] != NULL && __libc_argv[1] != NULL)
 	{
+	  /* Append first parameter to current word. */
 	  int p;
 
-	  for (p = 1; __libc_argv[p + 1]; p++)
-	    {
-	      char *copy = __strdup (__libc_argv[p]);
-	      if (copy == NULL)
-		return WRDE_NOSPACE;
-
-	      error = w_addword (pwordexp, copy);
-	      if (error)
-		{
-		  free (copy);
-		  return error;
-		}
-	    }
+	  *word = w_addstr (*word, word_length, max_length, __libc_argv[1]);
+	  if (*word == NULL)
+	    return WRDE_NOSPACE;
 
-	  /* Last parameter becomes current word */
-	  if (__libc_argv[p])
+	  for (p = 1; __libc_argv[p]; p++)
 	    {
+	      if (w_addword (pwordexp, *word))
+		return WRDE_NOSPACE;
 	      *word = __strdup (__libc_argv[p]);
+	      *max_length = *word_length = strlen (*word);
 	      if (*word == NULL)
 		return WRDE_NOSPACE;
-
-	      *max_length = *word_length = strlen (*word);
 	    }
 	}
-
+      
       return 0;
     }
 
   value = getenv (env);
 
-  if (action != '\0' || remove != RP_NONE)
+  if (action != ACT_NONE)
     {
       switch (action)
 	{
-	case 0:
+	case ACT_RP_SHORT_LEFT:
+	case ACT_RP_LONG_LEFT:
+	case ACT_RP_SHORT_RIGHT:
+	case ACT_RP_LONG_RIGHT:
 	  {
 	    char *p;
 	    char c;
@@ -1441,9 +1403,9 @@ envsubst:
 	    if (value == NULL)
 	      break;
 
-	    switch (remove)
+	    switch (action)
 	      {
-	      case RP_SHORT_LEFT:
+	      case ACT_RP_SHORT_LEFT:
 		for (p = value; p <= end; ++p)
 		  {
 		    c = *p;
@@ -1459,7 +1421,7 @@ envsubst:
 
 		break;
 
-	      case RP_LONG_LEFT:
+	      case ACT_RP_LONG_LEFT:
 		for (p = end; p >= value; --p)
 		  {
 		    c = *p;
@@ -1475,7 +1437,7 @@ envsubst:
 
 		break;
 
-	      case RP_SHORT_RIGHT:
+	      case ACT_RP_SHORT_RIGHT:
 		for (p = end; p >= value; --p)
 		  {
 		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
@@ -1487,7 +1449,7 @@ envsubst:
 
 		break;
 
-	      case RP_LONG_RIGHT:
+	      case ACT_RP_LONG_RIGHT:
 		for (p = value; p <= end; ++p)
 		  {
 		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
@@ -1500,13 +1462,13 @@ envsubst:
 		break;
 
 	      default:
-		assert (! "Unexpected `remove' value\n");
+		break;
 	      }
 
 	    break;
 	  }
 
-	case '?':
+	case ACT_NULL_ERROR:
 	  if (value && *value)
 	    /* Substitute parameter */
 	    break;
@@ -1519,9 +1481,6 @@ envsubst:
 	      return 0;
 	    }
 
-	  /* Error - exit */
-	  fprintf (stderr, "%s: ", env);
-
 	  if (*pattern)
 	    {
 	      /* Expand 'pattern' and write it to stderr */
@@ -1533,9 +1492,11 @@ envsubst:
 		{
 		  int i;
 
+		  fprintf (stderr, "%s:", env);
+
 		  for (i = 0; i < we.we_wordc; ++i)
 		    {
-		      fprintf (stderr, "%s%s", i ? " " : "", we.we_wordv[i]);
+		      fprintf (stderr, " %s", we.we_wordv[i]);
 		    }
 
 		  fprintf (stderr, "\n");
@@ -1548,12 +1509,12 @@ envsubst:
 	      return error;
 	    }
 
-	  fprintf (stderr, "parameter null or not set\n");
+	  fprintf (stderr, "%s: parameter null or not set\n", env);
 	  free (env);
 	  free (pattern);
 	  return WRDE_BADVAL;
 
-	case '-':
+	case ACT_NULL_SUBST:
 	  if (value && *value)
 	    /* Substitute parameter */
 	    break;
@@ -1577,11 +1538,15 @@ envsubst:
 		/* No field-splitting is allowed, so imagine
 		   quotes around the word.  */
 		char *qtd_pattern = malloc (3 + strlen (pattern));
-		sprintf (qtd_pattern, "\"%s\"", 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)
 	      {
@@ -1606,7 +1571,7 @@ envsubst:
 		goto no_space;
 	      }
 
-	    if (action == '=')
+	    if (action == ACT_NULL_ASSIGN)
 	      {
 		char *words;
 		char *cp;
@@ -1634,7 +1599,7 @@ envsubst:
 	    return 0;
 	  }
 
-	case '+':
+	case ACT_NONNULL_SUBST:
 	  if (value && *value)
 	    goto subst_word;
 
@@ -1646,7 +1611,7 @@ envsubst:
 	  free (pattern);
 	  return 0;
 
-	case '=':
+	case ACT_NULL_ASSIGN:
 	  if (value && *value)
 	    /* Substitute parameter */
 	    break;
@@ -1818,8 +1783,17 @@ parse_dollars (char **word, size_t *word_length, size_t *max_length,
 	{
 	  /* Differentiate between $((1+3)) and $((echo);(ls)) */
 	  int i = 3 + *offset;
-	  while (words[i] && words[i] != ')')
-	    ++i;
+	  int depth = 0;
+	  while (words[i] && !(depth == 0 && words[i] == ')'))
+	    {
+	      if (words[i] == '(')
+		++depth;
+	      else if (words[i] == ')')
+		--depth;
+
+	      ++i;
+	    }
+
 	  if (words[i] == ')' && words[i + 1] == ')')
 	    {
 	      (*offset) += 3;