} }
*word = memcpy (s, __libc_argv[p], len); *max_length = *word_length = len; } }
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 = 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;  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))    {      int special;      if (action != ACT_NONE)	{	  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;	  continue;	}      switch (words[*offset])	{	case '}':	  if (!brace)	    goto end_of_word;	  if (env == NULL)	    goto syntax;	  /* Evaluate. */	  goto envsubst;	case '#':	  /* '#' only has special meaning inside braces or as the very	   * first character after $ */	  if (*offset == start)	    {	      seen_hash = 1;	      goto envsubst;	    }	  if (!brace)	    /* Evaluate */	    /* (and re-parse this character) */	    goto end_of_word;	  /* At the start? (i.e. 'string length') */	  if (env == NULL)	    {	      seen_hash = 1;	      continue;	    }	  else if (seen_hash)	    goto syntax;	  action = ACT_RP_SHORT_LEFT;	  if (words[1 + *offset] == '#')	    {	      ++*offset;	      action = ACT_RP_LONG_LEFT;	    }	  continue;	case '%':	  if (!brace)	    /* Re-parse this character after substitution */	    goto end_of_word;	  if (!env || !*env)	    goto syntax;	  if (seen_hash)	    goto syntax;	  action = ACT_RP_SHORT_RIGHT;	  if (words[1 + *offset] == '%')	    {	      ++*offset;	      action = ACT_RP_LONG_RIGHT;	    }	  continue;	case ':':	  if (!brace)	    goto end_of_word;	  if (!env || !*env)	    goto syntax;	  if (seen_hash)	    goto syntax;	  if (words[1 + *offset] != '-' && words[1 + *offset] != '='	      && words[1 + *offset] != '?' && words[1 + *offset] != '+')	    goto syntax;	  colon_seen = 1;	  action = words[++*offset];	  continue;	case '-':	case '=':	case '?':	case '+':	  if (!brace)	    goto end_of_word;	  if (!env || !*env)	    goto syntax;	  action = words[*offset];	  continue;	}      special = (strchr ("*@$", words[*offset]) != NULL		 || isdigit (words[*offset]));      if (!isalpha (words[*offset]) && !special)	/* Stop and evaluate, remembering char we stopped at */	break;      env = w_addchar (env, &env_length, &env_maxlen,			   words[*offset]);      if (env == NULL)	goto no_space;      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:  if (words[start] == '{' && words[*offset] != '}')    goto syntax;  if (!env || !*env)    {      if (seen_hash)	{	  /* $# expands to the number of positional parameters */	  char buffer[21];	  buffer[20] = '\0';	  *word = w_addstr (*word, word_length, max_length,			    _itoa_word (__libc_argc - 1, &buffer[20], 10, 0));	}      else	{	  /* Just $ on its own */	  *offset = start - 1;	  *word = w_addchar (*word, word_length, max_length, '$');	}      if (env)	free (env);      return *word ? 0 : WRDE_NOSPACE;    }  /* Is it a special parameter? */  if (strpbrk (env, "0123456789*@$"))    {      if (env[1])	{	  /* Bad substitution if there is more than one character */	  free (env);	  fprintf (stderr, "${%s}: bad substitution\n", env);	  return WRDE_SYNTAX;	}      /* Is it a digit? */      if (isdigit(*env))	{	  int n = *env - '0';	  free (env);	  if (n >= __libc_argc)	    /* Substitute NULL */	    return 0;	  /* Replace with the appropriate positional parameter */	  value = __libc_argv[n];	  goto maybe_fieldsplit;	}      /* Is it `$$' ? */      else if (*env == '$')	{	  char pidstr[21];	  free (env);	  pidstr[20] = '\0';	  *word = w_addstr (*word, word_length, max_length,			    _itoa_word (getpid(), &pidstr[20], 10, 0));	  return *word ? 0 : WRDE_NOSPACE;	}      /* Is it `$*' or `$@' (unquoted) ? */      else if (*env == '*' || (*env == '@' && !quoted))	{	  size_t plist_len = 1;	  int p;	  /* Build up value parameter by parameter (copy them) */	  free (env);	  for (p = 1; __libc_argv[p]; ++p)	    {	      char *old_pointer = value;	      size_t argv_len = strlen (__libc_argv[p]);	      size_t old_plist_len = plist_len;	      if (value)		value[plist_len - 1] = 0;	      plist_len += 1 + argv_len;	      /* First realloc will act as malloc because value is	       * initialised to NULL. */	      value = realloc (value, plist_len);	      if (value == NULL)		{		  free (old_pointer);		  return WRDE_NOSPACE;		}	      memcpy (&value[old_plist_len - 1], __libc_argv[p], argv_len + 1);	      if (__libc_argv[p + 1])		{		  value[plist_len - 1] = '\0';		  value[plist_len - 2] = ' ';		}	    }	  free_value = 1;	  if (value)	    goto maybe_fieldsplit;	  return 0;	}      /* Must be a quoted `$@' */      assert (*env == '@');      assert (quoted);      free (env);      /* Each parameter is a separate word ("$@") */      if (__libc_argv[0] != NULL && __libc_argv[1] != NULL)	{	  /* Append first parameter to current word. */	  int p;	  *word = w_addstr (*word, word_length, max_length, __libc_argv[1]);	  if (*word == NULL)	    return WRDE_NOSPACE;	  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;	    }	}            return 0;    }  value = getenv (env);  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 (!pattern || !*pattern)	      break;	    end = value + strlen (value);	    if (value == NULL)	      break;	    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;			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;			value = p;			break;		      }		    *p = c;		  }		break;	      case ACT_RP_SHORT_RIGHT:		for (p = end; p >= value; --p)		  {		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)		      {			*p = '\0';			break;		      }		  }		break;	      case ACT_RP_LONG_RIGHT:		for (p = value; p <= end; ++p)		  {		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)		      {			*p = '\0';			break;		      }		  }		break;	      default:		break;	      }	    break;	  }	case ACT_NULL_ERROR:	  if (value && *value)	    /* Substitute parameter */	    break;	  if (!colon_seen && value)	    {	      /* Substitute NULL */	      free (env);	      free (pattern);	      return 0;	    }	  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);	      free (env);	      free (pattern);	      return error;	    }	  fprintf (stderr, "%s: parameter null or not set\n", env);	  free (env);	  free (pattern);	  return WRDE_BADVAL;	case ACT_NULL_SUBST:	  if (value && *value)	    /* Substitute parameter */	    break;	  if (!colon_seen && value)	    {	      /* Substitute NULL */	      free (env);	      free (pattern);	      return 0;	    }	subst_word:	  {	    /* Substitute word */	    wordexp_t we;	    int i;	    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)	      {		free (env);		free (pattern);		return error;	      }	    /* Fingers crossed that the quotes worked.. */	    assert (!quoted || we.we_wordc == 1);	    /* Substitute */	    for (i = 0; i < we.we_wordc; i++)	      if (w_addword (pwordexp, __strdup(we.we_wordv[i]))		  == WRDE_NOSPACE)		break;	    if (i < we.we_wordc)	      {		/* Ran out of space */		wordfree (&we);		goto no_space;	      }	    if (action == ACT_NULL_ASSIGN)	      {		char *words;		char *cp;		size_t words_size = 0;		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++ = ' ';		  }		__stpcpy (cp, we.we_wordv[i]);		/* Also assign */		setenv (env, words, 1);	      }	    wordfree (&we);	    return 0;	  }	case ACT_NONNULL_SUBST:	  if (value && *value)	    goto subst_word;	  if (!colon_seen && value)	    goto subst_word;	  /* Substitute NULL */	  free (env);	  free (pattern);	  return 0;	case ACT_NULL_ASSIGN:	  if (value && *value)	    /* Substitute parameter */	    break;	  if (!colon_seen && value)	    {	      /* Substitute NULL */	      free (env);	      free (pattern);	      return 0;	    }	  /* This checks for '=' so it knows to assign */	  goto subst_word;	default:	  assert (! "Unrecognised action!");	}    }  free (env);  free (pattern);  if (value == NULL)    {      /* Variable not defined */      if (flags & WRDE_UNDEF)	return WRDE_BADVAL;      return 0;    }  if (seen_hash)    {      char param_length[21];      param_length[20] = '\0';      *word = w_addstr (*word, word_length, max_length,			_itoa_word (strlen (value), &param_length[20], 10, 0));      return *word ? 0 : WRDE_NOSPACE;    } maybe_fieldsplit:  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)	return WRDE_NOSPACE;      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);		  return WRDE_NOSPACE;		}	      *word = NULL;	      *word_length = *max_length = 0;	    }	  /* Skip IFS whitespace before the field */	  while (*field_begin && strchr (ifs_white, *field_begin) != NULL)	    field_begin++;	  if (!seen_nonws_ifs && *field_begin == 0)	    /* Nothing but whitespace */	    break;	  /* Search for the end of the field */	  field_end = field_begin;	  while (*field_end && strchr (ifs, *field_end) == NULL)	    field_end++;	  /* Set up pointer to the character after end of field */	  next_field = *field_end ? field_end : NULL;	  /* Skip whitespace IFS after the field */	  while (next_field && *next_field && strchr (ifs_white, *next_field))	    next_field++;	  /* Skip at most one non-whitespace IFS character after the field */	  seen_nonws_ifs = 0;	  if (next_field && *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)	    {	      free (value_copy);	      return WRDE_NOSPACE;	    }	  field_begin = next_field;	}      while (seen_nonws_ifs || (field_begin != NULL && *field_begin));      free (value_copy);    }  return 0;no_space:  if (env)    free (env);  if (pattern)    free (pattern);  return WRDE_NOSPACE;syntax:  if (env)    free (env);  if (pattern)    free (pattern);  return WRDE_SYNTAX;}