if (*offset == start + 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){  /* We are poised just after "$" */  enum remove_pattern_enum  {    RP_NONE = 0,    RP_SHORT_LEFT,    RP_LONG_LEFT,    RP_SHORT_RIGHT,    RP_LONG_RIGHT  };  size_t start = *offset;  size_t env_length = 0;  size_t env_maxlen = 0;  size_t pat_length = 0;  size_t pat_maxlen = 0;  char *env = NULL;  char *pattern = NULL;  char *value;  char action = '\0';  enum remove_pattern_enum remove = RP_NONE;  int colon_seen = 0;  int depth = 0;  int error;  for (; words[*offset]; ++(*offset))    {      switch (words[*offset])	{	case '{':	  ++depth;	  if (action != '\0' || remove != RP_NONE)	    {	      pattern = w_addchar (pattern, &pat_length, &pat_maxlen,				   words[*offset]);	      if (pattern == NULL)		goto no_space;	      break;	    }	  if (*offset == start)	    break;	  /* Otherwise evaluate */	  /* (and re-parse this character) */	  --(*offset);	  goto envsubst;	case '}':	  if (words[start] != '{')	      --(*offset);	  if (action != '\0' || remove != RP_NONE)	    {	      if (--depth)		{		  pattern = w_addchar (pattern, &pat_length, &pat_maxlen,				       words[*offset]);		  if (pattern == NULL)		    goto no_space;		  break;		}	    }	  /* Evaluate */	  goto envsubst;	case '#':	  /* At the start?  (ie. 'string length') */	  if (*offset == start + 1)	    /* FIXME: This isn't written yet! */	    break;	  if (words[start] != '{')	    {	      /* Evaluate */	      /* (and re-parse this character) */	      --(*offset);	      goto envsubst;	    }	  /* Separating variable name from prefix pattern? */	  if (remove == RP_NONE)	    {	      remove = RP_SHORT_LEFT;	      break;	    }	  else if (remove == RP_SHORT_LEFT)	    {	      remove = RP_LONG_LEFT;	      break;	    }	  /* Must be part of prefix/suffix pattern. */	  pattern = w_addchar (pattern, &pat_length, &pat_maxlen,			       words[*offset]);	  if (pattern == NULL)	    goto no_space;	  break;	case '%':	  if (!*env)	    goto syntax;	  /* Separating variable name from suffix pattern? */	  if (remove == RP_NONE)	    {	      remove = RP_SHORT_RIGHT;	      break;	    }	  else if (remove == RP_SHORT_RIGHT)	    {	      remove = RP_LONG_RIGHT;	      break;	    }	  /* Must be part of prefix/suffix pattern. */	  pattern = w_addchar (pattern, &pat_length, &pat_maxlen,			       words[*offset]);	  if (pattern == NULL)	    goto no_space;	  break;	case ':':	  if (!*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 ((words[1 + *offset] == '-') || (words[1 + *offset] == '=') ||	      (words[1 + *offset] == '?') || (words[1 + *offset] == '+'))	    {	      colon_seen = 1;	      break;	    }	  goto syntax;	case '-':	case '=':	case '?':	case '+':	  if (!*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;	    }	  if ((words[start] == '{') || isalpha (words[*offset]))	    {	      env = w_addchar (env, &env_length, &env_maxlen, words[*offset]);	      if (env == NULL)		goto no_space;	      break;	    }	  --(*offset);	  goto envsubst;	}    }  /* End of input string */  --(*offset);envsubst:  if (words[start] == '{' && words[*offset] != '}')    goto syntax;  if (!env || !*env)    {      *offset = start - 1;      *word = w_addchar (*word, word_length, max_length, '$');      free (env);      free (pattern);      return *word ? 0 : WRDE_NOSPACE;    }  value = getenv (env);  if (action != '\0' || remove != RP_NONE)    {      switch (action)	{	case 0:	  {	    char *p;	    char c;	    char *end;	    if (!pattern || !*pattern)	      break;	    end = value + strlen (value);	    if (value == NULL)	      break;	    switch (remove)	      {	      case 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 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 RP_SHORT_RIGHT:		for (p = end; p >= value; p--)		  {		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)		      {			*p = '\0';			break;		      }		  }		break;	      case RP_LONG_RIGHT:		for (p = value; p <= end; p++)		  {		    if (fnmatch (pattern, p, 0) != FNM_NOMATCH)		      {			*p = '\0';			break;		      }		  }		break;	      default:		assert (! "Unexpected `remove' value\n");	      }	    break;	  }	case '?':	  if (value && *value)	    break;	  if (!colon_seen && value)	    {	      /* Substitute NULL */	      free (env);	      free (pattern);	      return 0;	    }	  /* Error - exit */	  fprintf (stderr, "%s: ", env);	  if (*pattern)	    {	      /* Expand 'pattern' and write it to stderr */	      wordexp_t	we;	      error = wordexp (pattern, &we, flags);	      if (error == 0)		{		  int i;		  for (i = 0; i < we.we_wordc; ++i)		    {		      fprintf (stderr, "%s%s", i ? " " : "", we.we_wordv[i]);		    }		  fprintf (stderr, "\n");		  error = WRDE_BADVAL;		}	      wordfree (&we);	      free (env);	      free (pattern);	      return error;	    }	  fprintf (stderr, "parameter null or not set\n");	  free (env);	  free (pattern);	  return WRDE_BADVAL;	default:	  printf ("warning: parameter substitution does not yet support \"%s%c\"\n", colon_seen?":":"", action);	}    }  free (env);  free (pattern);  if (value == NULL)    {      /* Variable not defined */      if (flags & WRDE_UNDEF)	return WRDE_SYNTAX;      return 0;    }  if (pwordexp == NULL)    /* Quoted - no field split */    *word = w_addstr (*word, word_length, max_length, value);  else    {      /* Need to field-split */      char *field_begin = value;      int seen_nonws_ifs = 0;      do	{	  char *field_end = field_begin;	  char *field;	  char *next_field;	  char ch;	  /* 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 */	    return 0;	  /* 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 */	  ch = *field_end;	  next_field = ch ? 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 and make a copy */	  *field_end = 0;	  field = __strdup (field_begin);	  if (field == NULL)	    goto no_space;	  /* Tag the field onto the word list */	  if (w_addword (pwordexp, field) == WRDE_NOSPACE)	    return WRDE_NOSPACE;	  *word = NULL;	  *word_length = *max_length = 0;	  field_begin = next_field;	} while (seen_nonws_ifs || (field_begin && *field_begin));      return 0;    }  return *word ? 0 : WRDE_NOSPACE;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;}