goto success;  case ACT_NULL_ASSIGN: if (value && *value)  break;  if (!colon_seen && value) {  if (free_value) free (value); goto success; }   goto subst_word;
value = pattern ? __strdup (pattern) : pattern; free_value = 1;  if (pattern && !value) goto no_space;  setenv (env, value, 1); break;
parse_param (char **word, size_t *word_length, size_t *max_length,	     const char *words, size_t *offset, int flags, wordexp_t *pwordexp,	     const char *ifs, const char *ifs_white, int quoted){  /* We are poised just after "$" */  enum action  {    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 env_length;  size_t env_maxlen;  size_t pat_length;  size_t pat_maxlen;  size_t start = *offset;  char *env;  char *pattern;  char *value = NULL;  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 special = 0;  char buffer[21];  int brace = words[*offset] == '{';  env = w_newword (&env_length, &env_maxlen);  pattern = w_newword (&pat_length, &pat_maxlen);  if (brace)    ++*offset;  /* First collect the parameter name. */  if (words[*offset] == '#')    {      seen_hash = 1;      if (!brace)	goto envsubst;      ++*offset;    }  if (isalpha (words[*offset]) || words[*offset] == '_')    {      /* Normal parameter name. */      do	{	  env = w_addchar (env, &env_length, &env_maxlen,			   words[*offset]);	  if (env == NULL)	    goto no_space;	}      while (isalnum (words[++*offset]) || words[*offset] == '_');    }  else if (isdigit (words[*offset]))    {      /* Numeric parameter name. */      special = 1;      do	{	  env = w_addchar (env, &env_length, &env_maxlen,			   words[*offset]);	  if (env == NULL)	    goto no_space;	  if (!brace)	    goto envsubst;	}      while (isdigit(words[++*offset]));    }  else if (strchr ("*@$", words[*offset]) != NULL)    {      /* Special parameter. */      special = 1;      env = w_addchar (env, &env_length, &env_maxlen,		       words[*offset]);      if (env == NULL)	goto no_space;      ++*offset;    }  else    {      if (brace)	goto syntax;    }  if (brace)    {      /* Check for special action to be applied to the value. */      switch (words[*offset])	{	case '}':	  /* Evaluate. */	  goto envsubst;	case '#':	  action = ACT_RP_SHORT_LEFT;	  if (words[1 + *offset] == '#')	    {	      ++*offset;	      action = ACT_RP_LONG_LEFT;	    }	  break;	case '%':	  action = ACT_RP_SHORT_RIGHT;	  if (words[1 + *offset] == '%')	    {	      ++*offset;	      action = ACT_RP_LONG_RIGHT;	    }	  break;	case ':':	  if (strchr ("-=?+", words[1 + *offset]) == NULL)	    goto syntax;	  colon_seen = 1;	  action = words[++*offset];	  break;	case '-':	case '=':	case '?':	case '+':	  action = words[*offset];	  break;	default:	  goto syntax;	}      /* Now collect the pattern. */      ++*offset;      for (; words[*offset]; ++(*offset))	{	  switch (words[*offset])	    {	    case '{':	      if (!pattern_is_quoted)		++depth;	      break;	    case '}':	      if (!pattern_is_quoted)		{		  if (depth == 0)		    goto envsubst;		  --depth;		}	      break;	    case '\\':	      if (!pattern_is_quoted && words[++*offset] == '\0')		goto syntax;	      break;	    case '\'':	      if (pattern_is_quoted == 0)		pattern_is_quoted = 1;	      else if (pattern_is_quoted == 1)		pattern_is_quoted = 0;	      break;	    case '"':	      if (pattern_is_quoted == 0)		pattern_is_quoted = 2;	      else if (pattern_is_quoted == 2)		pattern_is_quoted = 0;	      break;	    }	  pattern = w_addchar (pattern, &pat_length, &pat_maxlen,			       words[*offset]);	  if (pattern == NULL)	    goto no_space;	}    }  /* End of input string -- remember to reparse the character that we   * stopped at.  */  --(*offset);envsubst:  if (words[start] == '{' && words[*offset] != '}')    goto syntax;  if (env == NULL)    {      if (seen_hash)	{	  /* $# expands to the number of positional parameters */	  buffer[20] = '\0';	  value = _itoa_word (__libc_argc - 1, &buffer[20], 10, 0);	  seen_hash = 0;	}      else	{	  /* Just $ on its own */	  *offset = start - 1;	  *word = w_addchar (*word, word_length, max_length, '$');	  return *word ? 0 : WRDE_NOSPACE;	}    }  /* Is it a numeric parameter? */  else if (isdigit (env[0]))    {      int n = atoi (env);      if (n >= __libc_argc)	/* Substitute NULL. */	value = NULL;      else	/* Replace with appropriate positional parameter. */	value = __libc_argv[n];    }  /* Is it a special parameter? */  else if (special)    {      /* Is it `$$'? */      if (*env == '$')	{	  buffer[20] = '\0';	  value = _itoa_word (__getpid (), &buffer[20], 10, 0);	}      /* Is it `${#*}' or `${#@}'? */      else if ((*env == '*' || *env == '@') && seen_hash)	{	  buffer[20] = '\0';	  value = _itoa_word (__libc_argc > 0 ? __libc_argc - 1 : 0,			      &buffer[20], 10, 0);	  *word = w_addstr (*word, word_length, max_length, value);	  free (env);	  if (pattern)	    free (pattern);	  return *word ? 0 : WRDE_NOSPACE;	}      /* Is it `$*' or `$@' (unquoted) ? */      else if (*env == '*' || (*env == '@' && !quoted))	{	  size_t plist_len = 0;	  int p;	  char *end;	  /* Build up value parameter by parameter (copy them) */	  for (p = 1; __libc_argv[p]; ++p)	    plist_len += strlen (__libc_argv[p]) + 1; /* for space */	  value = malloc (plist_len);	  if (value == NULL)	    goto no_space;	  end = value;	  *end = 0;	  for (p = 1; __libc_argv[p]; ++p)	    {	      if (p > 1)		*end++ = ' ';	      end = __stpcpy (end, __libc_argv[p]);	    }	  free_value = 1;	}      else	{	  /* Must be a quoted `$@' */	  assert (*env == '@' && quoted);	  /* Each parameter is a separate word ("$@") */	  if (__libc_argc == 2)	    value = __libc_argv[1];	  else if (__libc_argc > 2)	    {	      int p;	      /* Append first parameter to current word. */	      value = w_addstr (*word, word_length, max_length,				__libc_argv[1]);	      if (value == NULL || w_addword (pwordexp, value))		goto no_space;	      for (p = 2; __libc_argv[p + 1]; p++)		{		  char *newword = __strdup (__libc_argv[p]);		  if (newword == NULL || w_addword (pwordexp, newword))		    goto no_space;		}	      /* Start a new word with the last parameter. */	      *word = w_newword (word_length, max_length);	      value = __libc_argv[p];	    }	  else	    {	      free (env);	      free (pattern);	      return 0;	    }	}    }  else    value = getenv (env);  if (value == NULL && (flags & WRDE_UNDEF))    {      /* Variable not defined. */      error = WRDE_BADVAL;      goto do_error;    }  if (action != ACT_NONE)    {      switch (action)	{	case ACT_RP_SHORT_LEFT:	case ACT_RP_LONG_LEFT:	case ACT_RP_SHORT_RIGHT:	case ACT_RP_LONG_RIGHT:	  {	    char *p;	    char c;	    char *end;	    if (value == NULL || pattern == NULL || *pattern == '\0')	      break;	    end = value + strlen (value);	    switch (action)	      {	      case ACT_RP_SHORT_LEFT:		for (p = value; p <= end; ++p)		  {		    c = *p;		    *p = '\0';		    if (fnmatch (pattern, value, 0) != FNM_NOMATCH)		      {			*p = c;			if (free_value)			  {			    char *newval = __strdup (p);			    if (newval == NULL)			      {				free (value);				goto no_space;			      }			    free (value);			    value = newval;			  }			else			  value = p;			break;		      }		    *p = c;		  }		break;	      case ACT_RP_LONG_LEFT:		for (p = end; p >= value; --p)		  {		    c = *p;		    *p = '\0';		    if (fnmatch (pattern, value, 0) != FNM_NOMATCH)		      {			*p = c;			if (free_value)			  {			    char *newval = __strdup (p);			    if (newval == NULL)			      {				free (value);				goto no_space;			      }			    free (value);			    value = newval;			  }			else			  value = p;			break;		      }		    *p = c;		  }		break;	      case ACT_RP_SHORT_RIGHT:		for (p = end; p >= value; --p)		  {		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)		      {			char *newval;			newval = malloc (p - value + 1);			if (newval == NULL)			  {			    if (free_value)			      free (value);			    goto no_space;			  }			*(char *) __mempcpy (newval, value, p - value) = '\0';			if (free_value)			  free (value);			value = newval;			free_value = 1;			break;		      }		  }		break;	      case ACT_RP_LONG_RIGHT:		for (p = value; p <= end; ++p)		  {		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)		      {			char *newval;			newval = malloc (p - value + 1);			if (newval == NULL)			  {			    if (free_value)			      free (value);			    goto no_space;			  }			*(char *) __mempcpy (newval, value, p - value) = '\0';			if (free_value)			  free (value);			value = newval;			free_value = 1;			break;		      }		  }		break;	      default:		break;	      }	    break;	  }	case ACT_NULL_ERROR:	  if (value && *value)	    /* Substitute parameter */	    break;	  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);	    }	  else	    {	      fprintf (stderr, "%s: parameter null or not set\n", env);	      error = WRDE_BADVAL;	    }	  if (free_value)	    free (value);	  goto do_error;	case ACT_NULL_SUBST:	  if (value && *value)	    /* 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 (special)		  /* Cannot assign special parameters. */		  goto syntax;		for (i = 0; i < we.we_wordc; i++)		  words_size += strlen (we.we_wordv[i]) + 1; /* for <space> */		words_size++;		cp = words = __alloca (words_size);		*words = 0;		for (i = 0; i < we.we_wordc - 1; i++)		  {		    cp = __stpcpy (cp, we.we_wordv[i]);		    *cp++ = ' ';		  }		strcpy (cp, we.we_wordv[i]);		/* Also assign */		setenv (env, words, 1);	      }	    wordfree (&we);	    goto success;	  }	case ACT_NONNULL_SUBST:	  if (value && *value)	    goto subst_word;	  if (!colon_seen && value)	    goto subst_word;	  /* Substitute NULL */	  if (free_value)	    free (value);	  goto success;	case ACT_NULL_ASSIGN:	  if (value && *value)	    /* Substitute parameter */	    break;	  if (!colon_seen && value)	    {	      /* Substitute NULL */	      if (free_value)		free (value);	      goto success;	    }	  /* This checks for '=' so it knows to assign */	  goto subst_word;	default:	  assert (! "Unrecognised action!");	}    }  free (env); env = NULL;  free (pattern); pattern = NULL;  if (seen_hash)    {      char param_length[21];      param_length[20] = '\0';      *word = w_addstr (*word, word_length, max_length,			_itoa_word (value ? strlen (value) : 0,				    &param_length[20], 10, 0));      if (free_value)	{	  assert (value != NULL);	  free (value);	}      return *word ? 0 : WRDE_NOSPACE;    }  if (value == NULL)    return 0;  if (quoted || !pwordexp)    {      /* Quoted - no field split */      *word = w_addstr (*word, word_length, max_length, value);      if (free_value)	free (value);      return *word ? 0 : WRDE_NOSPACE;    }  else    {      /* Need to field-split */      char *value_copy = __strdup (value); /* Don't modify value */      char *field_begin = value_copy;      int seen_nonws_ifs = 0;      if (free_value)	free (value);      if (value_copy == NULL)	goto no_space;      do	{	  char *field_end = field_begin;	  char *next_field;	  /* If this isn't the first field, start a new word */	  if (field_begin != value_copy)	    {	      if (w_addword (pwordexp, *word) == WRDE_NOSPACE)		{		  free (value_copy);		  goto no_space;		}	      *word = w_newword (word_length, max_length);	    }	  /* Skip IFS whitespace before the field */	  field_begin += strspn (field_begin, ifs_white);	  if (!seen_nonws_ifs && *field_begin == 0)	    /* Nothing but whitespace */	    break;	  /* Search for the end of the field */	  field_end = field_begin + strcspn (field_begin, ifs);	  /* Set up pointer to the character after end of field and             skip whitespace IFS after it. */	  next_field = field_end + strspn (field_end, ifs_white);	  /* Skip at most one non-whitespace IFS character after the field */	  seen_nonws_ifs = 0;	  if (*next_field && strchr (ifs, *next_field))	    {	      seen_nonws_ifs = 1;	      next_field++;	    }	  /* Null-terminate it */	  *field_end = 0;	  /* Tag a copy onto the current word */	  *word = w_addstr (*word, word_length, max_length, field_begin);	  if (*word == NULL && *field_begin != '\0')	    {	      free (value_copy);	      goto no_space;	    }	  field_begin = next_field;	}      while (seen_nonws_ifs || *field_begin);      free (value_copy);    }  return 0;success:  error = 0;  goto do_error;no_space:  error = WRDE_NOSPACE;  goto do_error;syntax:  error = WRDE_SYNTAX;do_error:  if (env)    free (env);  if (pattern)    free (pattern);  return error;}