static char *
parameter_brace_patsub (varname, value, ind, patsub, quoted, pflags, flags)
     char *varname, *value;
     int ind;
     char *patsub;
     int quoted, pflags, flags;
{
  int vtype, mflags, starsub, delim;
  char *val, *temp, *pat, *rep, *p, *lpatsub, *tt;
  SHELL_VAR *v;

  if (value == 0)
    return ((char *)NULL);

  this_command_name = varname;

  vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val);
  if (vtype == -1)
    return ((char *)NULL);

  starsub = vtype & VT_STARSUB;
  vtype &= ~VT_STARSUB;

  mflags = 0;
  /* PATSUB is never NULL when this is called. */
  if (*patsub == '/')
    {
      mflags |= MATCH_GLOBREP;
      patsub++;
    }

  /* Malloc this because expand_string_if_necessary or one of the expansion
     functions in its call chain may free it on a substitution error. */
  lpatsub = savestring (patsub);

  if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
    mflags |= MATCH_QUOTED;

  if (starsub)
    mflags |= MATCH_STARSUB;

  if (pflags & PF_ASSIGNRHS)
    mflags |= MATCH_ASSIGNRHS;

  /* If the pattern starts with a `/', make sure we skip over it when looking
     for the replacement delimiter. */
  delim = skip_to_delim (lpatsub, ((*patsub == '/') ? 1 : 0), "/", 0);
  if (lpatsub[delim] == '/')
    {
      lpatsub[delim] = 0;
      rep = lpatsub + delim + 1;
    }
  else
    rep = (char *)NULL;

  if (rep && *rep == '\0')
    rep = (char *)NULL;

  /* Perform the same expansions on the pattern as performed by the
     pattern removal expansions. */
  pat = getpattern (lpatsub, quoted, 1);

  if (rep)
    {
      /* We want to perform quote removal on the expanded replacement even if
	 the entire expansion is double-quoted because the parser and string
	 extraction functions treated quotes in the replacement string as
	 special.  THIS IS NOT BACKWARDS COMPATIBLE WITH BASH-4.2. */
      if (shell_compatibility_level > 42)
	rep = expand_string_if_necessary (rep, quoted & ~(Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT), expand_string_unsplit);
      /* This is the bash-4.2 code. */      
      else if ((mflags & MATCH_QUOTED) == 0)
	rep = expand_string_if_necessary (rep, quoted, expand_string_unsplit);
      else
	rep = expand_string_to_string_internal (rep, quoted, expand_string_unsplit);
    }

  /* ksh93 doesn't allow the match specifier to be a part of the expanded
     pattern.  This is an extension.  Make sure we don't anchor the pattern
     at the beginning or end of the string if we're doing global replacement,
     though. */
  p = pat;
  if (mflags & MATCH_GLOBREP)
    mflags |= MATCH_ANY;
  else if (pat && pat[0] == '#')
    {
      mflags |= MATCH_BEG;
      p++;
    }
  else if (pat && pat[0] == '%')
    {
      mflags |= MATCH_END;
      p++;
    }
  else
    mflags |= MATCH_ANY;

  /* OK, we now want to substitute REP for PAT in VAL.  If
     flags & MATCH_GLOBREP is non-zero, the substitution is done
     everywhere, otherwise only the first occurrence of PAT is
     replaced.  The pattern matching code doesn't understand
     CTLESC quoting CTLESC and CTLNUL so we use the dequoted variable
     values passed in (VT_VARIABLE) so the pattern substitution
     code works right.  We need to requote special chars after
     we're done for VT_VARIABLE and VT_ARRAYMEMBER, and for the
     other cases if QUOTED == 0, since the posparams and arrays
     indexed by * or @ do special things when QUOTED != 0. */

  switch (vtype)
    {
    case VT_VARIABLE:
    case VT_ARRAYMEMBER:
      temp = pat_subst (val, p, rep, mflags);
      if (vtype == VT_VARIABLE)
	FREE (val);
      if (temp)
	{
	  tt = (mflags & MATCH_QUOTED) ? quote_string (temp) : quote_escapes (temp);
	  free (temp);
	  temp = tt;
	}
      break;
    case VT_POSPARMS:
      temp = pos_params_pat_subst (val, p, rep, mflags);
      if (temp && (mflags & MATCH_QUOTED) == 0)
	{
	  tt = quote_escapes (temp);
	  free (temp);
	  temp = tt;
	}
      break;
#if defined (ARRAY_VARS)
    case VT_ARRAYVAR:
      temp = assoc_p (v) ? assoc_patsub (assoc_cell (v), p, rep, mflags)
			 : array_patsub (array_cell (v), p, rep, mflags);
      /* Don't call quote_escapes anymore; array_patsub calls
	 array_quote_escapes as appropriate before adding the
	 space separators; ditto for assoc_patsub. */
      break;
#endif
    }

  FREE (pat);
  FREE (rep);
  free (lpatsub);

  return temp;