BIGNUM *BN_dup(const BIGNUM *a)
{
    BIGNUM *t;

    if (a == NULL)
        return NULL;
    bn_check_top(a);

    t = BN_new();
    if (t == NULL)
        return NULL;
    if (!BN_copy(t, a)) {
        BN_free(t);
        return NULL;
    }
    bn_check_top(t);
    return t;
}


sv_combegin (value)
     const char *value;
{
  if (value && *value)
    {
      FREE (_rl_comment_begin);
      _rl_comment_begin = savestring (value);
      return 0;
    }
  return 1;
}


close_all_files ()
{
  register int i, fd_table_size;

  fd_table_size = getdtablesize ();
  if (fd_table_size > 256)	/* clamp to a reasonable value */
    fd_table_size = 256;

  for (i = 3; i < fd_table_size; i++)
    close (i);
}


setup_async_signals ()
{
#if defined (__BEOS__)
  set_signal_handler (SIGHUP, SIG_IGN);	/* they want csh-like behavior */
#endif

#if defined (JOB_CONTROL)
  if (job_control == 0)
#endif
    {
      /* Make sure we get the original signal dispositions now so we don't
	 confuse the trap builtin later if the subshell tries to use it to
	 reset SIGINT/SIGQUIT.  Don't call set_signal_ignored; that sets
	 the value of original_signals to SIG_IGN. Posix interpretation 751. */
      get_original_signal (SIGINT);
      set_signal_handler (SIGINT, SIG_IGN);

      get_original_signal (SIGQUIT);
      set_signal_handler (SIGQUIT, SIG_IGN);
    }
}


set_signal (sig, string)
     int sig;
     char *string;
{
  sigset_t set, oset;

  if (SPECIAL_TRAP (sig))
    {
      change_signal (sig, savestring (string));
      if (sig == EXIT_TRAP && interactive == 0)
	initialize_terminating_signals ();
      return;
    }

  /* A signal ignored on entry to the shell cannot be trapped or reset, but
     no error is reported when attempting to do so.  -- Posix.2 */
  if (sigmodes[sig] & SIG_HARD_IGNORE)
    return;

  /* Make sure we have original_signals[sig] if the signal has not yet
     been trapped. */
  if ((sigmodes[sig] & SIG_TRAPPED) == 0)
    {
      /* If we aren't sure of the original value, check it. */
      if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER)
        GETORIGSIG (sig);
      if (original_signals[sig] == SIG_IGN)
	return;
    }

  /* Only change the system signal handler if SIG_NO_TRAP is not set.
     The trap command string is changed in either case.  The shell signal
     handlers for SIGINT and SIGCHLD run the user specified traps in an
     environment in which it is safe to do so. */
  if ((sigmodes[sig] & SIG_NO_TRAP) == 0)
    {
      BLOCK_SIGNAL (sig, set, oset);
      change_signal (sig, savestring (string));
      set_signal_handler (sig, trap_handler);
      UNBLOCK_SIGNAL (oset);
    }
  else
    change_signal (sig, savestring (string));
}


decode_signal (string, flags)
     char *string;
     int flags;
{
  intmax_t sig;
  char *name;

  if (legal_number (string, &sig))
    return ((sig >= 0 && sig < NSIG) ? (int)sig : NO_SIG);

  /* A leading `SIG' may be omitted. */
  for (sig = 0; sig < BASH_NSIG; sig++)
    {
      name = signal_names[sig];
      if (name == 0 || name[0] == '\0')
	continue;

      /* Check name without the SIG prefix first case sensitively or
	 insensitively depending on whether flags includes DSIG_NOCASE */
      if (STREQN (name, "SIG", 3))
	{
	  name += 3;

	  if ((flags & DSIG_NOCASE) && strcasecmp (string, name) == 0)
	    return ((int)sig);
	  else if ((flags & DSIG_NOCASE) == 0 && strcmp (string, name) == 0)
	    return ((int)sig);
	  /* If we can't use the `SIG' prefix to match, punt on this
	     name now. */
	  else if ((flags & DSIG_SIGPREFIX) == 0)
	    continue;
	}

      /* Check name with SIG prefix case sensitively or insensitively
	 depending on whether flags includes DSIG_NOCASE */
      name = signal_names[sig];
      if ((flags & DSIG_NOCASE) && strcasecmp (string, name) == 0)
	return ((int)sig);
      else if ((flags & DSIG_NOCASE) == 0 && strcmp (string, name) == 0)
	return ((int)sig);
    }

  return (NO_SIG);
}


assign_array_element_internal (entry, name, vname, sub, sublen, value, flags)
     SHELL_VAR *entry;
     char *name;		/* only used for error messages */
     char *vname;
     char *sub;
     int sublen;
     char *value;
     int flags;
{
  char *akey;
  arrayind_t ind;

  if (entry && assoc_p (entry))
    {
      sub[sublen-1] = '\0';
      akey = expand_assignment_string_to_string (sub, 0);	/* [ */
      sub[sublen-1] = ']';
      if (akey == 0 || *akey == 0)
	{
	  err_badarraysub (name);
	  FREE (akey);
	  return ((SHELL_VAR *)NULL);
	}
      entry = bind_assoc_variable (entry, vname, akey, value, flags);
    }
  else
    {
      ind = array_expand_index (entry, sub, sublen);
      /* negative subscripts to indexed arrays count back from end */
      if (entry && ind < 0)
	ind = (array_p (entry) ? array_max_index (array_cell (entry)) : 0) + 1 + ind;
      if (ind < 0)
	{
	  err_badarraysub (name);
	  return ((SHELL_VAR *)NULL);
	}
      entry = bind_array_variable (vname, ind, value, flags);
    }

  return (entry);
}


check_signals ()
{
  CHECK_ALRM;		/* set by the read builtin */
  QUIT;
}


coproc_reap ()
{
#if MULTIPLE_COPROCS
  cpl_reap ();
#else
  struct coproc *cp;

  cp = &sh_coproc;		/* XXX - will require changes for multiple coprocs */
  if (cp && (cp->c_flags & COPROC_DEAD))
    coproc_dispose (cp);
#endif
}


pipe_read (pp)
     int *pp;
{
  char ch;

  if (pp[1] >= 0)
    {
      close (pp[1]);
      pp[1] = -1;
    }

  if (pp[0] >= 0)
    {
      while (read (pp[0], &ch, 1) == -1 && errno == EINTR)
	;
    }
}


making_children ()
{
  if (already_making_children)
    return;

  already_making_children = 1;
  start_pipeline ();
}


nohup_job (job_index)
     int job_index;
{
  register JOB *temp;

  if (js.j_jobslots == 0)
    return;

  if (temp = jobs[job_index])
    temp->flags |= J_NOHUP;
}


coproc_alloc (name, pid)
     char *name;
     pid_t pid;
{
  struct coproc *cp;

#if MULTIPLE_COPROCS
  cp = (struct coproc *)xmalloc (sizeof (struct coproc));
#else
  cp = &sh_coproc;
#endif
  coproc_init (cp);
  cp->c_lock = 2;

  cp->c_pid = pid;
  cp->c_name = savestring (name);
#if MULTIPLE_COPROCS
  cpl_add (cp);
#endif
  cp->c_lock = 0;
  return (cp);
}


array_keys (s, quoted)
     char *s;
     int quoted;
{
  int len;
  char *retval, *t, *temp;
  WORD_LIST *l;
  SHELL_VAR *var;

  var = array_variable_part (s, &t, &len);

  /* [ */
  if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
    return (char *)NULL;

  if (var_isset (var) == 0 || invisible_p (var))
    return (char *)NULL;

  if (array_p (var) == 0 && assoc_p (var) == 0)
    l = add_string_to_list ("0", (WORD_LIST *)NULL);
  else if (assoc_p (var))
    l = assoc_keys_to_word_list (assoc_cell (var));
  else
    l = array_keys_to_word_list (array_cell (var));
  if (l == (WORD_LIST *)NULL)
    return ((char *) NULL);

  if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
    {
      temp = string_list_dollar_star (l);
      retval = quote_string (temp);
      free (temp);
    }
  else	/* ${!name[@]} or unquoted ${!name[*]} */
    retval = string_list_dollar_at (l, quoted, 0);

  dispose_words (l);
  return retval;
}


parser_include (args)
     char *args;
{
  const char *old_init_file;
  char *e;
  int old_line_number, old_include_level, r;

  if (_rl_parsing_conditionalized_out)
    return (0);

  old_init_file = current_readline_init_file;
  old_line_number = current_readline_init_lineno;
  old_include_level = current_readline_init_include_level;

  e = strchr (args, '\n');
  if (e)
    *e = '\0';
  r = _rl_read_init_file ((const char *)args, old_include_level + 1);

  current_readline_init_file = old_init_file;
  current_readline_init_lineno = old_line_number;
  current_readline_init_include_level = old_include_level;

  return r;
}


rl_get_keymap_by_name (name)
     const char *name;
{
  register int i;

  for (i = 0; keymap_names[i].name; i++)
    if (_rl_stricmp (name, keymap_names[i].name) == 0)
      return (keymap_names[i].map);
  return ((Keymap) NULL);
}


trim_pathname (name, maxlen)
     char *name;
     int maxlen;
{
  int nlen, ndirs;
  intmax_t nskip;
  char *nbeg, *nend, *ntail, *v;

  if (name == 0 || (nlen = strlen (name)) == 0)
    return name;
  nend = name + nlen;

  v = get_string_value ("PROMPT_DIRTRIM");
  if (v == 0 || *v == 0)
    return name;
  if (legal_number (v, &nskip) == 0 || nskip <= 0)
    return name;

  /* Skip over tilde prefix */
  nbeg = name;
  if (name[0] == '~')
    for (nbeg = name; *nbeg; nbeg++)
      if (*nbeg == '/')
	{
	  nbeg++;
	  break;
	}
  if (*nbeg == 0)
    return name;

  for (ndirs = 0, ntail = nbeg; *ntail; ntail++)
    if (*ntail == '/')
      ndirs++;
  if (ndirs < nskip)
    return name;

  for (ntail = (*nend == '/') ? nend : nend - 1; ntail > nbeg; ntail--)
    {
      if (*ntail == '/')
	nskip--;
      if (nskip == 0)
	break;
    }
  if (ntail == nbeg)
    return name;

  /* Now we want to return name[0..nbeg]+"..."+ntail, modifying name in place */
  nlen = ntail - nbeg;
  if (nlen <= 3)
    return name;

  *nbeg++ = '.';
  *nbeg++ = '.';
  *nbeg++ = '.';

  nlen = nend - ntail;
  memmove (nbeg, ntail, nlen);
  nbeg[nlen] = '\0';

  return name;
}


posix_initialize (on)
     int on;
{
  /* Things that should be turned on when posix mode is enabled. */
  if (on != 0)
    {
      interactive_comments = source_uses_path = expand_aliases = 1;
      inherit_errexit = 1;
      source_searches_cwd = 0;
    }

  /* Things that should be turned on when posix mode is disabled. */
  if (on == 0)
    {
      source_searches_cwd = 1;
      expand_aliases = interactive_shell;
    }
}


bgp_search (pid)
     pid_t pid;
{
  ps_index_t psi;

  if (bgpids.storage == 0 || bgpids.nalloc == 0 || bgpids.npid == 0)
    return -1;

  /* Search chain using hash to find bucket in pidstat_table */
  for (psi = *(pshash_getbucket (pid)); psi != NO_PIDSTAT; psi = bgpids.storage[psi].bucket_next)
    if (bgpids.storage[psi].pid == pid)
      return (bgpids.storage[psi].status);

  return -1;
}


unquoted_member (character, string)
     int character;
     char *string;
{
  size_t slen;
  int sindex, c;
  DECLARE_MBSTATE;

  slen = strlen (string);
  sindex = 0;
  while (c = string[sindex])
    {
      if (c == character)
	return (1);

      switch (c)
	{
	default:
	  ADVANCE_CHAR (string, slen, sindex);
	  break;

	case '\\':
	  sindex++;
	  if (string[sindex])
	    ADVANCE_CHAR (string, slen, sindex);
	  break;

	case '\'':
	  sindex = skip_single_quoted (string, slen, ++sindex, 0);
	  break;

	case '"':
	  sindex = skip_double_quoted (string, slen, ++sindex, 0);
	  break;
	}
    }
  return (0);
}


queue_sigchld_trap (nchild)
     int nchild;
{
  if (nchild > 0)
    {
      catch_flag = 1;
      pending_traps[SIGCHLD] += nchild;
      trapped_signal_received = SIGCHLD;
    }
}


cleanup_redirects (list)
     REDIRECT *list;
{
  do_redirections (list, RX_ACTIVE);
  dispose_redirects (list);
}


expand_word_list_internal (list, eflags)
     WORD_LIST *list;
     int eflags;
{
  WORD_LIST *new_list, *temp_list;
  int tint;
  char *savecmd;

  tempenv_assign_error = 0;
  if (list == 0)
    return ((WORD_LIST *)NULL);

  garglist = new_list = copy_word_list (list);
  if (eflags & WEXP_VARASSIGN)
    {
      garglist = new_list = separate_out_assignments (new_list);
      if (new_list == 0)
	{
	  if (subst_assign_varlist)
	    {
	      /* All the words were variable assignments, so they are placed
		 into the shell's environment. */
	      for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
		{
		  savecmd = this_command_name;
		  this_command_name = (char *)NULL;	/* no arithmetic errors */
		  tint = do_word_assignment (temp_list->word, 0);
		  this_command_name = savecmd;
		  /* Variable assignment errors in non-interactive shells
		     running in Posix.2 mode cause the shell to exit. */
		  if (tint == 0)
		    {
		      last_command_exit_value = EXECUTION_FAILURE;
		      if (interactive_shell == 0 && posixly_correct)
			exp_jump_to_top_level (FORCE_EOF);
		      else
			exp_jump_to_top_level (DISCARD);
		    }
		}
	      dispose_words (subst_assign_varlist);
	      subst_assign_varlist = (WORD_LIST *)NULL;
	    }
	  return ((WORD_LIST *)NULL);
	}
    }

  /* Begin expanding the words that remain.  The expansions take place on
     things that aren't really variable assignments. */

#if defined (BRACE_EXPANSION)
  /* Do brace expansion on this word if there are any brace characters
     in the string. */
  if ((eflags & WEXP_BRACEEXP) && brace_expansion && new_list)
    new_list = brace_expand_word_list (new_list, eflags);
#endif /* BRACE_EXPANSION */

  /* Perform the `normal' shell expansions: tilde expansion, parameter and
     variable substitution, command substitution, arithmetic expansion,
     and word splitting. */
  new_list = shell_expand_word_list (new_list, eflags);

  /* Okay, we're almost done.  Now let's just do some filename
     globbing. */
  if (new_list)
    {
      if ((eflags & WEXP_PATHEXP) && disallow_filename_globbing == 0)
	/* Glob expand the word list unless globbing has been disabled. */
	new_list = glob_expand_word_list (new_list, eflags);
      else
	/* Dequote the words, because we're not performing globbing. */
	new_list = dequote_list (new_list);
    }

  if ((eflags & WEXP_VARASSIGN) && subst_assign_varlist)
    {
      sh_wassign_func_t *assign_func;
      int is_special_builtin, is_builtin_or_func;

      /* If the remainder of the words expand to nothing, Posix.2 requires
	 that the variable and environment assignments affect the shell's
	 environment. */
      assign_func = new_list ? assign_in_env : do_word_assignment;
      tempenv_assign_error = 0;

      is_builtin_or_func = (new_list && new_list->word && (find_shell_builtin (new_list->word->word) || find_function (new_list->word->word)));
      /* Posix says that special builtins exit if a variable assignment error
	 occurs in an assignment preceding it. */
      is_special_builtin = (posixly_correct && new_list && new_list->word && find_special_builtin (new_list->word->word));

      for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
	{
	  savecmd = this_command_name;
	  this_command_name = (char *)NULL;
	  assigning_in_environment = (assign_func == assign_in_env);
	  tint = (*assign_func) (temp_list->word, is_builtin_or_func);
	  assigning_in_environment = 0;
	  this_command_name = savecmd;
	  /* Variable assignment errors in non-interactive shells running
	     in Posix.2 mode cause the shell to exit. */
	  if (tint == 0)
	    {
	      if (assign_func == do_word_assignment)
		{
		  last_command_exit_value = EXECUTION_FAILURE;
		  if (interactive_shell == 0 && posixly_correct && is_special_builtin)
		    exp_jump_to_top_level (FORCE_EOF);
		  else
		    exp_jump_to_top_level (DISCARD);
		}
	      else
		tempenv_assign_error++;
	    }
	}

      dispose_words (subst_assign_varlist);
      subst_assign_varlist = (WORD_LIST *)NULL;
    }

  return (new_list);
}


list_one_job (job, format, ignore, job_index)
     JOB *job;
     int format, ignore, job_index;
{
  pretty_print_job (job_index, format, stdout);
}


parameter_list_remove_pattern (itype, pattern, patspec, quoted)
     int itype;
     char *pattern;
     int patspec, quoted;
{
  char *ret;
  WORD_LIST *list;

  list = list_rest_of_args ();
  if (list == 0)
    return ((char *)NULL);
  ret = list_remove_pattern (list, pattern, patspec, itype, quoted);
  dispose_words (list);
  return (ret);
}


_rl_clear_screen ()
{
#ifndef __DJGPP__
  if (_rl_term_clrpag)
    tputs (_rl_term_clrpag, 1, _rl_output_character_function);
  else
    rl_crlf ();
#else
  ScreenClear ();
  ScreenSetCursor (0, 0);
#endif /* __DJGPP__ */
}


print_select_list (list, list_len, max_elem_len, indices_len)
     WORD_LIST *list;
     int list_len, max_elem_len, indices_len;
{
  int ind, row, elem_len, pos, cols, rows;
  int first_column_indices_len, other_indices_len;

  if (list == 0)
    {
      putc ('\n', stderr);
      return;
    }

  cols = max_elem_len ? COLS / max_elem_len : 1;
  if (cols == 0)
    cols = 1;
  rows = list_len ? list_len / cols + (list_len % cols != 0) : 1;
  cols = list_len ? list_len / rows + (list_len % rows != 0) : 1;

  if (rows == 1)
    {
      rows = cols;
      cols = 1;
    }

  first_column_indices_len = NUMBER_LEN (rows);
  other_indices_len = indices_len;

  for (row = 0; row < rows; row++)
    {
      ind = row;
      pos = 0;
      while (1)
	{
	  indices_len = (pos == 0) ? first_column_indices_len : other_indices_len;
	  elem_len = print_index_and_element (indices_len, ind + 1, list);
	  elem_len += indices_len + RP_SPACE_LEN;
	  ind += rows;
	  if (ind >= list_len)
	    break;
	  indent (pos + elem_len, pos + max_elem_len);
	  pos += max_elem_len;
	}
      putc ('\n', stderr);
    }
}


_rl_untranslate_macro_value (seq, use_escapes)
     char *seq;
     int use_escapes;
{
  char *ret, *r, *s;
  int c;

  r = ret = (char *)xmalloc (7 * strlen (seq) + 1);
  for (s = seq; *s; s++)
    {
      c = *s;
      if (META_CHAR (c))
	{
	  *r++ = '\\';
	  *r++ = 'M';
	  *r++ = '-';
	  c = UNMETA (c);
	}
      else if (c == ESC)
	{
	  *r++ = '\\';
	  c = 'e';
	}
      else if (CTRL_CHAR (c))
	{
	  *r++ = '\\';
	  if (use_escapes && _rl_isescape (c))
	    c = _rl_escchar (c);
	  else
	    {
	      *r++ = 'C';
	      *r++ = '-';
	      c = _rl_to_lower (UNCTRL (c));
	    }
	}
      else if (c == RUBOUT)
 	{
 	  *r++ = '\\';
 	  *r++ = 'C';
 	  *r++ = '-';
 	  c = '?';
 	}

      if (c == ESC)
	{
	  *r++ = '\\';
	  c = 'e';
	}
      else if (c == '\\' || c == '"')
	*r++ = '\\';

      *r++ = (unsigned char)c;
    }
  *r = '\0';
  return ret;
}


expand_words_no_vars (list)
     WORD_LIST *list;
{
  return (expand_word_list_internal (list, WEXP_NOVARS));
}


array_var_assignment (v, itype, quoted)
     SHELL_VAR *v;
     int itype, quoted;
{
  char *ret, *val, flags[MAX_ATTRIBUTES];
  int i;

  if (v == 0)
    return (char *)NULL;
  val = array_p (v) ? array_to_assign (array_cell (v), 0)
		    : assoc_to_assign (assoc_cell (v), 0);
  if (val == 0)
    {
      val = (char *)xmalloc (3);
      val[0] = '(';
      val[1] = ')';
      val[2] = 0;
    }
  else
    {
      ret = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) ? quote_string (val) : quote_escapes (val);
      free (val);
      val = ret;
    }
  i = var_attribute_string (v, 0, flags);
  ret = (char *)xmalloc (i + strlen (val) + strlen (v->name) + 16);
  sprintf (ret, "declare -%s %s=%s", flags, v->name, val);
  free (val);
  return ret;
}


_rl_release_sigint ()
{
  if (sigint_blocked == 0)
    return;

  sigint_blocked = 0;
  RL_CHECK_SIGNALS ();
}


find_last_proc (job, block)
     int job;
     int block;
{
  register PROCESS *p;
  sigset_t set, oset;

  if (block)
    BLOCK_CHILD (set, oset);

  p = jobs[job]->pipe;
  while (p && p->next != jobs[job]->pipe)
    p = p->next;

  if (block)
    UNBLOCK_CHILD (oset);

  return (p);
}


without_job_control ()
{
  stop_making_children ();
  last_made_pid = NO_PID;	/* XXX */
}


rl_named_function (string)
     const char *string;
{
  register int i;

  rl_initialize_funmap ();

  for (i = 0; funmap[i]; i++)
    if (_rl_stricmp (funmap[i]->name, string) == 0)
      return (funmap[i]->function);
  return ((rl_command_func_t *)NULL);
}


bind_array_variable (name, ind, value, flags)
     char *name;
     arrayind_t ind;
     char *value;
     int flags;
{
  SHELL_VAR *entry;

  entry = find_shell_variable (name);

  if (entry == (SHELL_VAR *) 0)
    {
      /* Is NAME a nameref variable that points to an unset variable? */
      entry = find_variable_nameref_for_create (name, 0);
      if (entry == INVALID_NAMEREF_VALUE)
	return ((SHELL_VAR *)0);
      if (entry && nameref_p (entry))
	entry = make_new_array_variable (nameref_cell (entry));
    }
  if (entry == (SHELL_VAR *) 0)
    entry = make_new_array_variable (name);
  else if ((readonly_p (entry) && (flags&ASS_FORCE) == 0) || noassign_p (entry))
    {
      if (readonly_p (entry))
	err_readonly (name);
      return (entry);
    }
  else if (array_p (entry) == 0)
    entry = convert_var_to_array (entry);

  /* ENTRY is an array variable, and ARRAY points to the value. */
  return (bind_array_var_internal (entry, ind, 0, value, flags));
}


do_assignment_internal (word, expand)
     const WORD_DESC *word;
     int expand;
{
  int offset, appendop, assign_list, aflags, retval;
  char *name, *value, *temp;
  SHELL_VAR *entry;
#if defined (ARRAY_VARS)
  char *t;
  int ni;
#endif
  const char *string;

  if (word == 0 || word->word == 0)
    return 0;

  appendop = assign_list = aflags = 0;
  string = word->word;
  offset = assignment (string, 0);
  name = savestring (string);
  value = (char *)NULL;

  if (name[offset] == '=')
    {
      if (name[offset - 1] == '+')
	{
	  appendop = 1;
	  name[offset - 1] = '\0';
	}

      name[offset] = 0;		/* might need this set later */
      temp = name + offset + 1;

#if defined (ARRAY_VARS)
      if (expand && (word->flags & W_COMPASSIGN))
	{
	  assign_list = ni = 1;
	  value = extract_array_assignment_list (temp, &ni);
	}
      else
#endif
      if (expand && temp[0])
	value = expand_string_if_necessary (temp, 0, expand_string_assignment);
      else
	value = savestring (temp);
    }

  if (value == 0)
    {
      value = (char *)xmalloc (1);
      value[0] = '\0';
    }

  if (echo_command_at_execute)
    {
      if (appendop)
	name[offset - 1] = '+';
      xtrace_print_assignment (name, value, assign_list, 1);
      if (appendop)
	name[offset - 1] = '\0';
    }

#define ASSIGN_RETURN(r)	do { FREE (value); free (name); return (r); } while (0)

  if (appendop)
    aflags |= ASS_APPEND;

#if defined (ARRAY_VARS)
  if (t = mbschr (name, '['))	/*]*/
    {
      if (assign_list)
	{
	  report_error (_("%s: cannot assign list to array member"), name);
	  ASSIGN_RETURN (0);
	}
      entry = assign_array_element (name, value, aflags);
      if (entry == 0)
	ASSIGN_RETURN (0);
    }
  else if (assign_list)
    {
      if ((word->flags & W_ASSIGNARG) && (word->flags & W_ASSNGLOBAL) == 0)
	aflags |= ASS_MKLOCAL;
      if ((word->flags & W_ASSIGNARG) && (word->flags & W_ASSNGLOBAL))
	aflags |= ASS_MKGLOBAL;
      if (word->flags & W_ASSIGNASSOC)
	aflags |= ASS_MKASSOC;
      entry = do_compound_assignment (name, value, aflags);
    }
  else
#endif /* ARRAY_VARS */
  entry = bind_variable (name, value, aflags);

  stupidly_hack_special_variables (name);

  /* Return 1 if the assignment seems to have been performed correctly. */
  if (entry == 0 || readonly_p (entry))
    retval = 0;		/* assignment failure */
  else if (noassign_p (entry))
    {
      last_command_exit_value = EXECUTION_FAILURE;
      retval = 1;	/* error status, but not assignment failure */
    }
  else
    retval = 1;

  if (entry && retval != 0 && noassign_p (entry) == 0)
    VUNSETATTR (entry, att_invisible);

  ASSIGN_RETURN (retval);
}


processes_in_job (job)
     int job;
{
  int nproc;
  register PROCESS *p;

  nproc = 0;
  p = jobs[job]->pipe;
  do
    {
      p = p->next;
      nproc++;
    }
  while (p != jobs[job]->pipe);

  return nproc;
}


file_iswdir (fn)
     char *fn;
{
  return (file_isdir (fn) && sh_eaccess (fn, W_OK) == 0);
}


reset_or_restore_signal_handlers (reset)
     sh_resetsig_func_t *reset;
{
  register int i;

  /* Take care of the exit trap first */
  if (sigmodes[EXIT_TRAP] & SIG_TRAPPED)
    {
      sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED;
      if (reset != reset_signal)
	{
	  free_trap_command (EXIT_TRAP);
	  trap_list[EXIT_TRAP] = (char *)NULL;
	}
    }

  for (i = 1; i < NSIG; i++)
    {
      if (sigmodes[i] & SIG_TRAPPED)
	{
	  if (trap_list[i] == (char *)IGNORE_SIG)
	    set_signal_handler (i, SIG_IGN);
	  else
	    (*reset) (i);
	}
      else if (sigmodes[i] & SIG_SPECIAL)
	(*reset) (i);
      pending_traps[i] = 0;	/* XXX */
    }

  /* Command substitution and other child processes don't inherit the
     debug, error, or return traps.  If we're in the debugger, and the
     `functrace' or `errtrace' options have been set, then let command
     substitutions inherit them.  Let command substitution inherit the
     RETURN trap if we're in the debugger and tracing functions. */
  if (function_trace_mode == 0)
    {
      sigmodes[DEBUG_TRAP] &= ~SIG_TRAPPED;
      sigmodes[RETURN_TRAP] &= ~SIG_TRAPPED;
    }
  if (error_trace_mode == 0)
    sigmodes[ERROR_TRAP] &= ~SIG_TRAPPED;
}


bash_tilde_find_word (s, flags, lenp)
     const char *s;
     int flags, *lenp;
{
  const char *r;
  char *ret;
  int l;

  for (r = s; *r && *r != '/'; r++)
    {
      /* Short-circuit immediately if we see a quote character.  Even though
	 POSIX says that `the first unquoted slash' (or `:') terminates the
	 tilde-prefix, in practice, any quoted portion of the tilde prefix
	 will cause it to not be expanded. */
      if (*r == '\\' || *r == '\'' || *r == '"')
	{
	  ret = savestring (s);
	  if (lenp)
	    *lenp = 0;
	  return ret;
	}
      else if (flags && *r == ':')
	break;
    }
  l = r - s;
  ret = xmalloc (l + 1);
  strncpy (ret, s, l);
  ret[l] = '\0';
  if (lenp)
    *lenp = l;
  return ret;
}


terminate_stopped_jobs ()
{
  register int i;

  /* XXX could use js.j_firstj here */
  for (i = 0; i < js.j_jobslots; i++)
    {
      if (jobs[i] && STOPPED (i))
	{
	  killpg (jobs[i]->pgrp, SIGTERM);
	  killpg (jobs[i]->pgrp, SIGCONT);
	}
    }
}


reset_current ()
{
  int candidate;

  if (js.j_jobslots && js.j_current != NO_JOB && jobs[js.j_current] && STOPPED (js.j_current))
    candidate = js.j_current;
  else
    {
      candidate = NO_JOB;

      /* First choice: the previous job. */
      if (js.j_previous != NO_JOB && jobs[js.j_previous] && STOPPED (js.j_previous))
	candidate = js.j_previous;

      /* Second choice: the most recently stopped job. */
      if (candidate == NO_JOB)
	candidate = job_last_stopped (js.j_jobslots);

      /* Third choice: the newest running job. */
      if (candidate == NO_JOB)
	candidate = job_last_running (js.j_jobslots);
    }

  /* If we found a job to use, then use it.  Otherwise, there
     are no jobs period. */
  if (candidate != NO_JOB)
    set_current_job (candidate);
  else
    js.j_current = js.j_previous = NO_JOB;
}


skip_matched_pair (string, start, open, close, flags)
     const char *string;
     int start, open, close, flags;
{
  int i, pass_next, backq, si, c, count, oldjmp;
  size_t slen;
  char *temp, *ss;
  DECLARE_MBSTATE;

  slen = strlen (string + start) + start;
  oldjmp = no_longjmp_on_fatal_error;
  no_longjmp_on_fatal_error = 1;

  i = start + 1;		/* skip over leading bracket */
  count = 1;
  pass_next = backq = 0;
  ss = (char *)string;
  while (c = string[i])
    {
      if (pass_next)
	{
	  pass_next = 0;
	  if (c == 0)
	    CQ_RETURN(i);
	  ADVANCE_CHAR (string, slen, i);
	  continue;
	}
      else if (c == '\\')
	{
	  pass_next = 1;
	  i++;
	  continue;
	}
      else if (backq)
	{
	  if (c == '`')
	    backq = 0;
	  ADVANCE_CHAR (string, slen, i);
	  continue;
	}
      else if ((flags & 1) == 0 && c == '`')
	{
	  backq = 1;
	  i++;
	  continue;
	}
      else if ((flags & 1) == 0 && c == open)
	{
	  count++;
	  i++;
	  continue;
	}
      else if (c == close)
	{
	  count--;
	  if (count == 0)
	    break;
	  i++;
	  continue;
	}
      else if ((flags & 1) == 0 && (c == '\'' || c == '"'))
	{
	  i = (c == '\'') ? skip_single_quoted (ss, slen, ++i, 0)
			  : skip_double_quoted (ss, slen, ++i, 0);
	  /* no increment, the skip functions increment past the closing quote. */
	}
      else if ((flags&1) == 0 && c == '$' && (string[i+1] == LPAREN || string[i+1] == LBRACE))
	{
	  si = i + 2;
	  if (string[si] == '\0')
	    CQ_RETURN(si);

	  if (string[i+1] == LPAREN)
	    temp = extract_delimited_string (ss, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */
	  else
	    temp = extract_dollar_brace_string (ss, &si, 0, SX_NOALLOC);
	  i = si;
	  if (string[i] == '\0')	/* don't increment i past EOS in loop */
	    break;
	  i++;
	  continue;
	}
      else
	ADVANCE_CHAR (string, slen, i);
    }

  CQ_RETURN(i);
}


pos_params_modcase (string, pat, modop, mflags)
     char *string, *pat;
     int modop;
     int mflags;
{
  WORD_LIST *save, *params;
  WORD_DESC *w;
  char *ret;
  int pchar, qflags;

  save = params = list_rest_of_args ();
  if (save == 0)
    return ((char *)NULL);

  for ( ; params; params = params->next)
    {
      ret = sh_modcase (params->word->word, pat, modop);
      w = alloc_word_desc ();
      w->word = ret ? ret : savestring ("");
      dispose_word (params->word);
      params->word = w;
    }

  pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
  qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;

  ret = string_list_pos_params (pchar, save, qflags);
  dispose_words (save);

  return (ret);
}


job_exit_status (job)
     int job;
{
  return (process_exit_status (raw_job_exit_status (job)));
}


close_fd_bitmap (fdbp)
     struct fd_bitmap *fdbp;
{
  register int i;

  if (fdbp)
    {
      for (i = 0; i < fdbp->size; i++)
	if (fdbp->bitmap[i])
	  {
	    close (i);
	    fdbp->bitmap[i] = 0;
	  }
    }
}


find_termsig_by_pid (pid)
     pid_t pid;
{
  int i;

  i = find_index_by_pid (pid);
  if (i == NO_PID)
    return (0);
  if (pid_list[i].flags & PROC_RUNNING)
    return (0);
  return (get_termsig ((WAIT)pid_list[i].status));
}


sh_closepipe (pv)
     int *pv;
{
  if (pv[0] >= 0)
    close (pv[0]);

  if (pv[1] >= 0)
    close (pv[1]);

  pv[0] = pv[1] = -1;
  return 0;
}


conf_standard_path ()
{
#if defined (_CS_PATH) && defined (HAVE_CONFSTR)
  char *p;
  size_t len;

  len = (size_t)confstr (_CS_PATH, (char *)NULL, (size_t)0);
  if (len > 0)
    {
      p = (char *)xmalloc (len + 2);
      *p = '\0';
      confstr (_CS_PATH, p, len);
      return (p);
    }
  else
    return (savestring (STANDARD_UTILS_PATH));
#else /* !_CS_PATH || !HAVE_CONFSTR  */
#  if defined (CS_PATH)
  return (savestring (CS_PATH));
#  else
  return (savestring (STANDARD_UTILS_PATH));
#  endif /* !CS_PATH */
#endif /* !_CS_PATH || !HAVE_CONFSTR */
}


string_quote_removal (string, quoted)
     char *string;
     int quoted;
{
  size_t slen;
  char *r, *result_string, *temp, *send;
  int sindex, tindex, dquote;
  unsigned char c;
  DECLARE_MBSTATE;

  /* The result can be no longer than the original string. */
  slen = strlen (string);
  send = string + slen;

  r = result_string = (char *)xmalloc (slen + 1);

  for (dquote = sindex = 0; c = string[sindex];)
    {
      switch (c)
	{
	case '\\':
	  c = string[++sindex];
	  if (c == 0)
	    {
	      *r++ = '\\';
	      break;
	    }
	  if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && (sh_syntaxtab[c] & CBSDQUOTE) == 0)
	    *r++ = '\\';
	  /* FALLTHROUGH */

	default:
	  SCOPY_CHAR_M (r, string, send, sindex);
	  break;

	case '\'':
	  if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote)
	    {
	      *r++ = c;
	      sindex++;
	      break;
	    }
	  tindex = sindex + 1;
	  temp = string_extract_single_quoted (string, &tindex);
	  if (temp)
	    {
	      strcpy (r, temp);
	      r += strlen (r);
	      free (temp);
	    }
	  sindex = tindex;
	  break;

	case '"':
	  dquote = 1 - dquote;
	  sindex++;
	  break;
	}
    }
    *r = '\0';
    return (result_string);
}


valid_array_reference (name, flags)
     char *name;
     int flags;
{
  char *t;
  int r, len;

  t = mbschr (name, '[');	/* ] */
  if (t)
    {
      *t = '\0';
      r = legal_identifier (name);
      *t = '[';
      if (r == 0)
	return 0;
      /* Check for a properly-terminated non-blank subscript. */
      len = skipsubscript (t, 0, 0);
      if (t[len] != ']' || len == 1)
	return 0;
      if (t[len+1] != '\0')
	return 0;
      for (r = 1; r < len; r++)
	if (whitespace (t[r]) == 0)
	  return 1;
      return 0;
    }
  return 0;
}


insert_some_chars (string, count, col)
     char *string;
     int count, col;
{
  open_some_spaces (col);
  _rl_output_some_chars (string, count);
}


chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at)
     char *name;
     int quoted;
     int *quoted_dollar_atp, *contains_dollar_at;
{
  char *temp1;

  if (name == 0)
    {
      if (quoted_dollar_atp)
	*quoted_dollar_atp = 0;
      if (contains_dollar_at)
	*contains_dollar_at = 0;
      return 0;
    }

  /* check for $@ and $* */
  if (name[0] == '@' && name[1] == 0)
    {
      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
	*quoted_dollar_atp = 1;
      if (contains_dollar_at)
	*contains_dollar_at = 1;
      return 1;
    }
  else if (name[0] == '*' && name[1] == '\0' && quoted == 0)
    {
      if (contains_dollar_at)
	*contains_dollar_at = 1;
      return 1;
    }

  /* Now check for ${array[@]} and ${array[*]} */
#if defined (ARRAY_VARS)
  else if (valid_array_reference (name, 0))
    {
      temp1 = mbschr (name, '[');
      if (temp1 && temp1[1] == '@' && temp1[2] == ']')
	{
	  if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
	    *quoted_dollar_atp = 1;
	  if (contains_dollar_at)
	    *contains_dollar_at = 1;
	  return 1;
	}	/* [ */
      /* ${array[*]}, when unquoted, should be treated like ${array[@]},
	 which should result in separate words even when IFS is unset. */
      if (temp1 && temp1[1] == '*' && temp1[2] == ']' && quoted == 0)
	{
	  if (contains_dollar_at)
	    *contains_dollar_at = 1;
	  return 1;
	}
    }
#endif
  return 0;
}


find_proc_slot (pid)
     pid_t pid;
{
  register int i;

  for (i = 0; i < pid_list_size; i++)
    if (pid_list[i].pid == NO_PID || pid_list[i].pid == pid)
      return (i);

  if (i == pid_list_size)
    alloc_pid_list ();

  return (i);
}


param_expand (string, sindex, quoted, expanded_something,
	      contains_dollar_at, quoted_dollar_at_p, had_quoted_null_p,
	      pflags)
     char *string;
     int *sindex, quoted, *expanded_something, *contains_dollar_at;
     int *quoted_dollar_at_p, *had_quoted_null_p, pflags;
{
  char *temp, *temp1, uerror[3], *savecmd;
  int zindex, t_index, expok;
  unsigned char c;
  intmax_t number;
  SHELL_VAR *var;
  WORD_LIST *list;
  WORD_DESC *tdesc, *ret;
  int tflag;

/*itrace("param_expand: `%s' pflags = %d", string+*sindex, pflags);*/
  zindex = *sindex;
  c = string[++zindex];

  temp = (char *)NULL;
  ret = tdesc = (WORD_DESC *)NULL;
  tflag = 0;

  /* Do simple cases first. Switch on what follows '$'. */
  switch (c)
    {
    /* $0 .. $9? */
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      temp1 = dollar_vars[TODIGIT (c)];
      if (unbound_vars_is_error && temp1 == (char *)NULL)
	{
	  uerror[0] = '$';
	  uerror[1] = c;
	  uerror[2] = '\0';
	  last_command_exit_value = EXECUTION_FAILURE;
	  err_unboundvar (uerror);
	  return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
	}
      if (temp1)
	temp = (*temp1 && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
		  ? quote_string (temp1)
		  : quote_escapes (temp1);
      else
	temp = (char *)NULL;

      break;

    /* $$ -- pid of the invoking shell. */
    case '$':
      temp = itos (dollar_dollar_pid);
      break;

    /* $# -- number of positional parameters. */
    case '#':
      temp = itos (number_of_args ());
      break;

    /* $? -- return value of the last synchronous command. */
    case '?':
      temp = itos (last_command_exit_value);
      break;

    /* $- -- flags supplied to the shell on invocation or by `set'. */
    case '-':
      temp = which_set_flags ();
      break;

      /* $! -- Pid of the last asynchronous command. */
    case '!':
      /* If no asynchronous pids have been created, expand to nothing.
	 If `set -u' has been executed, and no async processes have
	 been created, this is an expansion error. */
      if (last_asynchronous_pid == NO_PID)
	{
	  if (expanded_something)
	    *expanded_something = 0;
	  temp = (char *)NULL;
	  if (unbound_vars_is_error)
	    {
	      uerror[0] = '$';
	      uerror[1] = c;
	      uerror[2] = '\0';
	      last_command_exit_value = EXECUTION_FAILURE;
	      err_unboundvar (uerror);
	      return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
	    }
	}
      else
	temp = itos (last_asynchronous_pid);
      break;

    /* The only difference between this and $@ is when the arg is quoted. */
    case '*':		/* `$*' */
      list = list_rest_of_args ();

#if 0
      /* According to austin-group posix proposal by Geoff Clare in
	 <20090505091501.GA10097@squonk.masqnet> of 5 May 2009:

 	"The shell shall write a message to standard error and
 	 immediately exit when it tries to expand an unset parameter
 	 other than the '@' and '*' special parameters."
      */

      if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0)
	{
	  uerror[0] = '$';
	  uerror[1] = '*';
	  uerror[2] = '\0';
	  last_command_exit_value = EXECUTION_FAILURE;
	  err_unboundvar (uerror);
	  return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
	}
#endif

      /* If there are no command-line arguments, this should just
	 disappear if there are other characters in the expansion,
	 even if it's quoted. */
      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && list == 0)
	temp = (char *)NULL;
      else if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE))
	{
	  /* If we have "$*" we want to make a string of the positional
	     parameters, separated by the first character of $IFS, and
	     quote the whole string, including the separators.  If IFS
	     is unset, the parameters are separated by ' '; if $IFS is
	     null, the parameters are concatenated. */
	  temp = (quoted & (Q_DOUBLE_QUOTES|Q_PATQUOTE)) ? string_list_dollar_star (list) : string_list (list);
	  if (temp)
	    {
	      temp1 = (quoted & Q_DOUBLE_QUOTES) ? quote_string (temp) : temp;
	      if (*temp == 0)
		tflag |= W_HASQUOTEDNULL;
	      if (temp != temp1)
		free (temp);
	      temp = temp1;
	    }
	}
      else
	{
	  /* We check whether or not we're eventually going to split $* here,
	     for example when IFS is empty and we are processing the rhs of
	     an assignment statement.  In that case, we don't separate the
	     arguments at all.  Otherwise, if the $* is not quoted it is
	     identical to $@ */
#  if defined (HANDLE_MULTIBYTE)
	  if (expand_no_split_dollar_star && ifs_firstc[0] == 0)
#  else
	  if (expand_no_split_dollar_star && ifs_firstc == 0)
#  endif
	    temp = string_list_dollar_star (list);
	  else
	    {
	      temp = string_list_dollar_at (list, quoted, 0);
	      if (quoted == 0 && (ifs_is_set == 0 || ifs_is_null))
		tflag |= W_SPLITSPACE;
	      /* If we're not quoted but we still don't want word splitting, make
		 we quote the IFS characters to protect them from splitting (e.g.,
		 when $@ is in the string as well). */
	      else if (temp && quoted == 0 && ifs_is_set && (pflags & PF_ASSIGNRHS))
		{
		  temp1 = quote_string (temp);
		  free (temp);
		  temp = temp1;
		}
	    }

	  if (expand_no_split_dollar_star == 0 && contains_dollar_at)
	    *contains_dollar_at = 1;
	}

      dispose_words (list);
      break;

    /* When we have "$@" what we want is "$1" "$2" "$3" ... This
       means that we have to turn quoting off after we split into
       the individually quoted arguments so that the final split
       on the first character of $IFS is still done.  */
    case '@':		/* `$@' */
      list = list_rest_of_args ();

#if 0
      /* According to austin-group posix proposal by Geoff Clare in
	 <20090505091501.GA10097@squonk.masqnet> of 5 May 2009:

 	"The shell shall write a message to standard error and
 	 immediately exit when it tries to expand an unset parameter
 	 other than the '@' and '*' special parameters."
      */

      if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0)
	{
	  uerror[0] = '$';
	  uerror[1] = '@';
	  uerror[2] = '\0';
	  last_command_exit_value = EXECUTION_FAILURE;
	  err_unboundvar (uerror);
	  return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal);
	}
#endif

      /* We want to flag the fact that we saw this.  We can't turn
	 off quoting entirely, because other characters in the
	 string might need it (consider "\"$@\""), but we need some
	 way to signal that the final split on the first character
	 of $IFS should be done, even though QUOTED is 1. */
      /* XXX - should this test include Q_PATQUOTE? */
      if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
	*quoted_dollar_at_p = 1;
      if (contains_dollar_at)
	*contains_dollar_at = 1;

      /* We want to separate the positional parameters with the first
	 character of $IFS in case $IFS is something other than a space.
	 We also want to make sure that splitting is done no matter what --
	 according to POSIX.2, this expands to a list of the positional
	 parameters no matter what IFS is set to. */
      /* XXX - what to do when in a context where word splitting is not
	 performed? Even when IFS is not the default, posix seems to imply
	 that we behave like unquoted $* ?  Maybe we should use PF_NOSPLIT2
	 here. */
      /* XXX - bash-4.4/bash-5.0 passing PFLAGS */
      temp = string_list_dollar_at (list, (pflags & PF_ASSIGNRHS) ? (quoted|Q_DOUBLE_QUOTES) : quoted, pflags);

      tflag |= W_DOLLARAT;
      dispose_words (list);
      break;

    case LBRACE:
      tdesc = parameter_brace_expand (string, &zindex, quoted, pflags,
				      quoted_dollar_at_p,
				      contains_dollar_at);

      if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal)
	return (tdesc);
      temp = tdesc ? tdesc->word : (char *)0;

      /* XXX */
      /* Quoted nulls should be removed if there is anything else
	 in the string. */
      /* Note that we saw the quoted null so we can add one back at
	 the end of this function if there are no other characters
	 in the string, discard TEMP, and go on.  The exception to
	 this is when we have "${@}" and $1 is '', since $@ needs
	 special handling. */
      if (tdesc && tdesc->word && (tdesc->flags & W_HASQUOTEDNULL) && QUOTED_NULL (temp))
	{
	  if (had_quoted_null_p)
	    *had_quoted_null_p = 1;
	  if (*quoted_dollar_at_p == 0)
	    {
	      free (temp);
	      tdesc->word = temp = (char *)NULL;
	    }

	}

      ret = tdesc;
      goto return0;

    /* Do command or arithmetic substitution. */
    case LPAREN:
      /* We have to extract the contents of this paren substitution. */
      t_index = zindex + 1;
      /* XXX - might want to check for string[t_index+2] == LPAREN and parse
	 as arithmetic substitution immediately. */
      temp = extract_command_subst (string, &t_index, (pflags&PF_COMPLETE) ? SX_COMPLETE : 0);
      zindex = t_index;

      /* For Posix.2-style `$(( ))' arithmetic substitution,
	 extract the expression and pass it to the evaluator. */
      if (temp && *temp == LPAREN)
	{
	  char *temp2;
	  temp1 = temp + 1;
	  temp2 = savestring (temp1);
	  t_index = strlen (temp2) - 1;

	  if (temp2[t_index] != RPAREN)
	    {
	      free (temp2);
	      goto comsub;
	    }

	  /* Cut off ending `)' */
	  temp2[t_index] = '\0';

	  if (chk_arithsub (temp2, t_index) == 0)
	    {
	      free (temp2);
#if 0
	      internal_warning (_("future versions of the shell will force evaluation as an arithmetic substitution"));
#endif
	      goto comsub;
	    }

	  /* Expand variables found inside the expression. */
	  temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES|Q_ARITH);
	  free (temp2);

arithsub:
	  /* No error messages. */
	  savecmd = this_command_name;
	  this_command_name = (char *)NULL;
	  number = evalexp (temp1, &expok);
	  this_command_name = savecmd;
	  free (temp);
	  free (temp1);
	  if (expok == 0)
	    {
	      if (interactive_shell == 0 && posixly_correct)
		{
		  last_command_exit_value = EXECUTION_FAILURE;
		  return (&expand_wdesc_fatal);
		}
	      else
		return (&expand_wdesc_error);
	    }
	  temp = itos (number);
	  break;
	}

comsub:
      if (pflags & PF_NOCOMSUB)
	/* we need zindex+1 because string[zindex] == RPAREN */
	temp1 = substring (string, *sindex, zindex+1);
      else
	{
	  tdesc = command_substitute (temp, quoted);
	  temp1 = tdesc ? tdesc->word : (char *)NULL;
	  if (tdesc)
	    dispose_word_desc (tdesc);
	}
      FREE (temp);
      temp = temp1;
      break;

    /* Do POSIX.2d9-style arithmetic substitution.  This will probably go
       away in a future bash release. */
    case '[':
      /* Extract the contents of this arithmetic substitution. */
      t_index = zindex + 1;
      temp = extract_arithmetic_subst (string, &t_index);
      zindex = t_index;
      if (temp == 0)
	{
	  temp = savestring (string);
	  if (expanded_something)
	    *expanded_something = 0;
	  goto return0;
	}

       /* Do initial variable expansion. */
      temp1 = expand_arith_string (temp, Q_DOUBLE_QUOTES|Q_ARITH);

      goto arithsub;

    default:
      /* Find the variable in VARIABLE_LIST. */
      temp = (char *)NULL;

      for (t_index = zindex; (c = string[zindex]) && legal_variable_char (c); zindex++)
	;
      temp1 = (zindex > t_index) ? substring (string, t_index, zindex) : (char *)NULL;

      /* If this isn't a variable name, then just output the `$'. */
      if (temp1 == 0 || *temp1 == '\0')
	{
	  FREE (temp1);
	  temp = (char *)xmalloc (2);
	  temp[0] = '$';
	  temp[1] = '\0';
	  if (expanded_something)
	    *expanded_something = 0;
	  goto return0;
	}

      /* If the variable exists, return its value cell. */
      var = find_variable (temp1);

      if (var && invisible_p (var) == 0 && var_isset (var))
	{
#if defined (ARRAY_VARS)
	  if (assoc_p (var) || array_p (var))
	    {
	      temp = array_p (var) ? array_reference (array_cell (var), 0)
				   : assoc_reference (assoc_cell (var), "0");
	      if (temp)
		temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
			  ? quote_string (temp)
			  : quote_escapes (temp);
	      else if (unbound_vars_is_error)
		goto unbound_variable;
	    }
	  else
#endif
	    {
	      temp = value_cell (var);

	      temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
			? quote_string (temp)
			: quote_escapes (temp);
	    }

	  free (temp1);

	  goto return0;
	}
      else if (var && (invisible_p (var) || var_isset (var) == 0))
	temp = (char *)NULL;
      else if ((var = find_variable_last_nameref (temp1, 0)) && var_isset (var) && invisible_p (var) == 0)
	{
	  temp = nameref_cell (var);
#if defined (ARRAY_VARS)
	  if (temp && *temp && valid_array_reference (temp, 0))
	    {
	      tdesc = parameter_brace_expand_word (temp, SPECIAL_VAR (temp, 0), quoted, pflags, (arrayind_t *)NULL);
	      if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal)
		return (tdesc);
	      ret = tdesc;
	      goto return0;
	    }
	  else
#endif
	  /* y=2 ; typeset -n x=y; echo $x is not the same as echo $2 in ksh */
	  if (temp && *temp && legal_identifier (temp) == 0)
	    {
	      last_command_exit_value = EXECUTION_FAILURE;
	      report_error (_("%s: invalid variable name for name reference"), temp);
	      return (&expand_wdesc_error);	/* XXX */
	    }
	  else
	    temp = (char *)NULL;
	}

      temp = (char *)NULL;

unbound_variable:
      if (unbound_vars_is_error)
	{
	  last_command_exit_value = EXECUTION_FAILURE;
	  err_unboundvar (temp1);
	}
      else
	{
	  free (temp1);
	  goto return0;
	}

      free (temp1);
      last_command_exit_value = EXECUTION_FAILURE;
      return ((unbound_vars_is_error && interactive_shell == 0)
		? &expand_wdesc_fatal
		: &expand_wdesc_error);
    }

  if (string[zindex])
    zindex++;

return0:
  *sindex = zindex;

  if (ret == 0)
    {
      ret = alloc_word_desc ();
      ret->flags = tflag;	/* XXX */
      ret->word = temp;
    }
  return ret;
}


sv_keymap (value)
     const char *value;
{
  Keymap kmap;

  kmap = rl_get_keymap_by_name (value);
  if (kmap)
    {
      rl_set_keymap (kmap);
      return 0;
    }
  return 1;
}


describe_pid (pid)
     pid_t pid;
{
  fprintf (stderr, "%ld\n", (long) pid);
}


sv_compwidth (value)
     const char *value;
{
  int nval = -1;

  if (value && *value)
    nval = atoi (value);

  _rl_completion_columns = nval;
  return 0;
}


coproc_setvars (cp)
     struct coproc *cp;
{
  SHELL_VAR *v;
  char *namevar, *t;
  int l;
  WORD_DESC w;
#if defined (ARRAY_VARS)
  arrayind_t ind;
#endif

  if (cp->c_name == 0)
    return;

  /* We could do more here but right now we only check the name, warn if it's
     not a valid identifier, and refuse to create variables with invalid names
     if a coproc with such a name is supplied. */
  w.word = cp->c_name;
  w.flags = 0;
  if (check_identifier (&w, 1) == 0)
    return;

  l = strlen (cp->c_name);
  namevar = xmalloc (l + 16);

#if defined (ARRAY_VARS)
  v = find_variable (cp->c_name);

  /* This is the same code as in find_or_make_array_variable */
  if (v == 0)
    {
      v = find_variable_nameref_for_create (cp->c_name, 1);
      if (v == INVALID_NAMEREF_VALUE)
	return;
      if (v && nameref_p (v))
	{
	  free (cp->c_name);
	  cp->c_name = savestring (nameref_cell (v));
	  v = make_new_array_variable (cp->c_name);
	}
    }

  if (v && (readonly_p (v) || noassign_p (v)))
    {
      if (readonly_p (v))
	err_readonly (cp->c_name);
      return;
    }
  if (v == 0)
    v = make_new_array_variable (cp->c_name);
  if (array_p (v) == 0)
    v = convert_var_to_array (v);

  t = itos (cp->c_rfd);
  ind = 0;
  v = bind_array_variable (cp->c_name, ind, t, 0);
  free (t);

  t = itos (cp->c_wfd);
  ind = 1;
  bind_array_variable (cp->c_name, ind, t, 0);
  free (t);
#else
  sprintf (namevar, "%s_READ", cp->c_name);
  t = itos (cp->c_rfd);
  bind_variable (namevar, t, 0);
  free (t);
  sprintf (namevar, "%s_WRITE", cp->c_name);
  t = itos (cp->c_wfd);
  bind_variable (namevar, t, 0);
  free (t);
#endif

  sprintf (namevar, "%s_PID", cp->c_name);
  t = itos (cp->c_pid);
  bind_variable (namevar, t, 0);
  free (t);

  free (namevar);
}


_rl_clean_up_for_exit ()
{
  if (_rl_echoing_p)
    {
      if (_rl_vis_botlin > 0)	/* minor optimization plus bug fix */
	_rl_move_vert (_rl_vis_botlin);
      _rl_vis_botlin = 0;
      fflush (rl_outstream);
      rl_restart_output (1, 0);
    }
}


rl_set_signals ()
{
  sighandler_cxt dummy;
  SigHandler *oh;
#if defined (HAVE_POSIX_SIGNALS)
  static int sigmask_set = 0;
  static sigset_t bset, oset;
#endif

#if defined (HAVE_POSIX_SIGNALS)
  if (rl_catch_signals && sigmask_set == 0)
    {
      sigemptyset (&bset);

      sigaddset (&bset, SIGINT);
      sigaddset (&bset, SIGTERM);
      sigaddset (&bset, SIGHUP);
#if defined (SIGQUIT)
      sigaddset (&bset, SIGQUIT);
#endif
#if defined (SIGALRM)
      sigaddset (&bset, SIGALRM);
#endif
#if defined (SIGTSTP)
      sigaddset (&bset, SIGTSTP);
#endif
#if defined (SIGTTIN)
      sigaddset (&bset, SIGTTIN);
#endif
#if defined (SIGTTOU)
      sigaddset (&bset, SIGTTOU);
#endif
      sigmask_set = 1;
    }
#endif /* HAVE_POSIX_SIGNALS */

  if (rl_catch_signals && signals_set_flag == 0)
    {
#if defined (HAVE_POSIX_SIGNALS)
      sigemptyset (&oset);
      sigprocmask (SIG_BLOCK, &bset, &oset);
#endif

      rl_maybe_set_sighandler (SIGINT, rl_signal_handler, &old_int);
      rl_maybe_set_sighandler (SIGTERM, rl_signal_handler, &old_term);
      rl_maybe_set_sighandler (SIGHUP, rl_signal_handler, &old_hup);
#if defined (SIGQUIT)
      rl_maybe_set_sighandler (SIGQUIT, rl_signal_handler, &old_quit);
#endif

#if defined (SIGALRM)
      oh = rl_set_sighandler (SIGALRM, rl_signal_handler, &old_alrm);
      if (oh == (SigHandler *)SIG_IGN)
	rl_sigaction (SIGALRM, &old_alrm, &dummy);
#if defined (HAVE_POSIX_SIGNALS) && defined (SA_RESTART)
      /* If the application using readline has already installed a signal
	 handler with SA_RESTART, SIGALRM will cause reads to be restarted
	 automatically, so readline should just get out of the way.  Since
	 we tested for SIG_IGN above, we can just test for SIG_DFL here. */
      if (oh != (SigHandler *)SIG_DFL && (old_alrm.sa_flags & SA_RESTART))
	rl_sigaction (SIGALRM, &old_alrm, &dummy);
#endif /* HAVE_POSIX_SIGNALS */
#endif /* SIGALRM */

#if defined (SIGTSTP)
      rl_maybe_set_sighandler (SIGTSTP, rl_signal_handler, &old_tstp);
#endif /* SIGTSTP */

#if defined (SIGTTOU)
      rl_maybe_set_sighandler (SIGTTOU, rl_signal_handler, &old_ttou);
#endif /* SIGTTOU */

#if defined (SIGTTIN)
      rl_maybe_set_sighandler (SIGTTIN, rl_signal_handler, &old_ttin);
#endif /* SIGTTIN */

      signals_set_flag = 1;

#if defined (HAVE_POSIX_SIGNALS)
      sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
#endif
    }

#if defined (SIGWINCH)
  if (rl_catch_sigwinch && sigwinch_set_flag == 0)
    {
      rl_maybe_set_sighandler (SIGWINCH, rl_sigwinch_handler, &old_winch);
      sigwinch_set_flag = 1;
    }
#endif /* SIGWINCH */

  return 0;
}


cpe_dispose (cpe)
      struct cpelement *cpe;
{
  free (cpe);
}


list_string_with_quotes (string)
     char *string;
{
  WORD_LIST *list;
  char *token, *s;
  size_t s_len;
  int c, i, tokstart, len;

  for (s = string; s && *s && spctabnl (*s); s++)
    ;
  if (s == 0 || *s == 0)
    return ((WORD_LIST *)NULL);

  s_len = strlen (s);
  tokstart = i = 0;
  list = (WORD_LIST *)NULL;
  while (1)
    {
      c = s[i];
      if (c == '\\')
	{
	  i++;
	  if (s[i])
	    i++;
	}
      else if (c == '\'')
	i = skip_single_quoted (s, s_len, ++i, 0);
      else if (c == '"')
	i = skip_double_quoted (s, s_len, ++i, 0);
      else if (c == 0 || spctabnl (c))
	{
	  /* We have found the end of a token.  Make a word out of it and
	     add it to the word list. */
	  token = substring (s, tokstart, i);
	  list = add_string_to_list (token, list);
	  free (token);
	  while (spctabnl (s[i]))
	    i++;
	  if (s[i])
	    tokstart = i;
	  else
	    break;
	}
      else
	i++;	/* normal character */
    }
  return (REVERSE_LIST (list, WORD_LIST *));
}


give_terminal_to (pgrp, force)
     pid_t pgrp;
     int force;
{
  return 0;
}


legal_alias_name (string, flags)
     char *string;
     int flags;
{
  register char *s;

  for (s = string; *s; s++)
    if (shellbreak (*s) || shellxquote (*s) || shellexp (*s) || (*s == '/'))
      return 0;
  return 1;
}


cleanup_the_pipeline ()
{
  PROCESS *disposer;
  sigset_t set, oset;

  BLOCK_CHILD (set, oset);
  disposer = the_pipeline;
  the_pipeline = (PROCESS *)NULL;
  UNBLOCK_CHILD (oset);

  if (disposer)
    discard_pipeline (disposer);
}


parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at)
     char *name;
     int var_is_special, quoted;
     int *quoted_dollar_atp, *contains_dollar_at;
{
  char *temp, *t;
  WORD_DESC *w;
  SHELL_VAR *v;

  /* See if it's a nameref first, behave in ksh93-compatible fashion.
     There is at least one incompatibility: given ${!foo[0]} where foo=bar,
     bash performs an indirect lookup on foo[0] and expands the result;
     ksh93 expands bar[0].  We could do that here -- there are enough usable
     primitives to do that -- but do not at this point. */
  if (var_is_special == 0 && (v = find_variable_last_nameref (name, 0)))
    {
      if (nameref_p (v) && (t = nameref_cell (v)) && *t)
	{
	  w = alloc_word_desc ();
	  w->word = savestring (t);
	  w->flags = 0;
	  return w;
	}
    }

  t = parameter_brace_find_indir (name, var_is_special, quoted, 0);

  chk_atstar (t, quoted, quoted_dollar_atp, contains_dollar_at);
  if (t == 0)
    return (WORD_DESC *)NULL;

  if (valid_brace_expansion_word (t, SPECIAL_VAR (t, 0)) == 0)
    {
      report_error (_("%s: bad substitution"), t);
      free (t);
      w = alloc_word_desc ();
      w->word = &expand_param_error;
      w->flags = 0;
      return (w);
    }

  w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0, 0);
  free (t);

  return w;
}


int agpopdisc(Agraph_t * g, Agcbdisc_t * cbd)
{
    Agcbstack_t *stack_ent;

    stack_ent = g->clos->cb;
    if (stack_ent) {
	if (stack_ent->f == cbd)
	    g->clos->cb = stack_ent->prev;
	else {
	    while (stack_ent && (stack_ent->prev->f != cbd))
		stack_ent = stack_ent->prev;
	    if (stack_ent && stack_ent->prev)
		stack_ent->prev = stack_ent->prev->prev;
	}
	if (stack_ent) {
	    agfree(g, stack_ent);
	    return SUCCESS;
	}
    }
    return FAILURE;
}


static void freeUserdata(userdata_t * ud)
{
    dtclose(ud->nameMap);
    agxbfree(&(ud->xml_attr_name));
    agxbfree(&(ud->xml_attr_value));
    agxbfree(&(ud->composite_buffer));
    freeString(ud->elements);
    free(ud);
}


static void freeString(slist * stk)
{
    slist *sp;

    while (stk) {
	sp = stk->next;
	free(stk);
	stk = sp;
    }
}


static void initargs(int argc, char **argv)
{
    int c;

    CmdName = cmdName(argv[0]);
    opterr = 0;
    while ((c = getopt(argc, argv, ":vg:o:")) != -1) {
	switch (c) {
	case 'g':
	    gname = optarg;
	    break;
	case 'v':
	    Verbose = 1;
	    break;
	case 'o':
	    outFile = openFile(optarg, "w");
	    break;
	case ':':
	    fprintf(stderr, "%s: option -%c missing argument\n", CmdName, optopt);
	    usage(1);
	    break;
	case '?':
	    if (optopt == '?')
		usage(0);
	    else {
		fprintf(stderr, "%s: option -%c unrecognized\n", CmdName,
			optopt);
		usage(1);
	    }
	}
    }

    argv += optind;
    argc -= optind;

    if (argc)
	Files = argv;
    if (!outFile)
	outFile = stdout;
}


static char *cmdName(char *path)
{
    char *sp;

    sp = strrchr(path, '/');
    if (sp)
	sp++;
    else
	sp = path;
    return sp;
}


setGlobalNodeAttr(Agraph_t * g, char *name, char *value, userdata_t * ud)
{
    if (strncmp(name, NODELBL, NLBLLEN))
	fprintf(stderr,
		"Warning: global node attribute %s in graph %s does not begin with the prefix %s\n",
		name, agnameof(g), NODELBL);
    else
	name += NLBLLEN;
    if ((g != root) && !agattr(root, AGNODE, name, 0))
	agattr(root, AGNODE, name, defval);
    agattr(G, AGNODE, name, value);
}


static void json_strinput_destroyed(struct json_parser *parser)
{
	i_assert(parser->strinput != NULL);

	parser->strinput = NULL;
}


test_json_parse_input(const char *test_input, enum json_parser_flags flags)
{
	struct json_parser *parser;
	struct istream *input;
	enum json_type type;
	const char *value, *error;
	int ret = 0;

	input = test_istream_create_data(test_input, strlen(test_input));
	parser = json_parser_init_flags(input, flags);
	while (json_parse_next(parser, &type, &value) > 0)
		ret++;
	if (json_parser_deinit(&parser, &error) < 0)
		ret = -1;
	i_stream_unref(&input);
	return ret;
}


static int json_parse_string(struct json_parser *parser, bool allow_skip,
			     const char **value_r)
{
	int ret;

	if (*parser->data != '"')
		return -1;
	parser->data++;

	if (parser->skipping && allow_skip) {
		*value_r = NULL;
		return json_skip_string(parser);
	}

	str_truncate(parser->value, 0);
	for (; parser->data != parser->end; parser->data++) {
		if (*parser->data == '"') {
			parser->data++;
			*value_r = str_c(parser->value);
			return 1;
		}
		if (*parser->data != '\\')
			str_append_c(parser->value, *parser->data);
		else {
			if (++parser->data == parser->end)
				return 0;
			switch (*parser->data) {
			case '"':
			case '\\':
			case '/':
				str_append_c(parser->value, *parser->data);
				break;
			case 'b':
				str_append_c(parser->value, '\b');
				break;
			case 'f':
				str_append_c(parser->value, '\f');
				break;
			case 'n':
				str_append_c(parser->value, '\n');
				break;
			case 'r':
				str_append_c(parser->value, '\r');
				break;
			case 't':
				str_append_c(parser->value, '\t');
				break;
			case 'u':
				if ((ret=json_parse_unicode_escape(parser)) <= 0)
					return ret;
				break;
			default:
				return -1;
			}
		}
	}
	return 0;
}


sv_histchars (name)
     char *name;
{
  char *temp;

  temp = get_string_value (name);
  if (temp)
    {
      history_expansion_char = *temp;
      if (temp[0] && temp[1])
	{
	  history_subst_char = temp[1];
	  if (temp[2])
	      history_comment_char = temp[2];
	}
    }
  else
    {
      history_expansion_char = '!';
      history_subst_char = '^';
      history_comment_char = '#';
    }
}


command_errstr (code)
     int code;
{
  if (code > CMDERR_LAST)
    code = CMDERR_DEFAULT;

  return (_(cmd_error_table[code]));
}


main()
{

union {
	long int l;
	char c[sizeof(long int)];
} u;
	int x0, x1, x2, x3;

	u.l = 1;
	x0 = u.c[0];
	x3 = u.c[sizeof (long int) - 1];
	printf ("x0 = %d x3 = %d (%s)\n", x0, x3, x3 == 1 ? "bigendian" : "littleendian");
	x0 = u.l >> 24;
	x1 = u.l >> 16;
	x2 = u.l >> 8;
	x3 = u.l & 0xff;
	printf ("x0:x3: %d %d %d %d\n", x0, x1, x2, x3);
}


all_array_variables ()
{
  return (vapply (visible_array_vars));
}


execute_select_command (select_command)
     SELECT_COM *select_command;
{
  WORD_LIST *releaser, *list;
  SHELL_VAR *v;
  char *identifier, *ps3_prompt, *selection;
  int retval, list_len, show_menu, save_line_number;

  if (check_identifier (select_command->name, 1) == 0)
    return (EXECUTION_FAILURE);

  save_line_number = line_number;
  line_number = select_command->line;

  command_string_index = 0;
  print_select_command_head (select_command);

  if (echo_command_at_execute)
    xtrace_print_select_command_head (select_command);

#if 0
  if (signal_in_progress (DEBUG_TRAP) == 0 && (this_command_name == 0 || (STREQ (this_command_name, "trap") == 0)))
#else
  if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
#endif
    {
      FREE (the_printed_command_except_trap);
      the_printed_command_except_trap = savestring (the_printed_command);
    }

  retval = run_debug_trap ();
#if defined (DEBUGGER)
  /* In debugging mode, if the DEBUG trap returns a non-zero status, we
     skip the command. */
  if (debugging_mode && retval != EXECUTION_SUCCESS)
    return (EXECUTION_SUCCESS);
#endif

  loop_level++;
  identifier = select_command->name->word;

  /* command and arithmetic substitution, parameter and variable expansion,
     word splitting, pathname expansion, and quote removal. */
  list = releaser = expand_words_no_vars (select_command->map_list);
  list_len = list_length (list);
  if (list == 0 || list_len == 0)
    {
      if (list)
	dispose_words (list);
      line_number = save_line_number;
      return (EXECUTION_SUCCESS);
    }

  begin_unwind_frame ("select");
  add_unwind_protect (dispose_words, releaser);

  if (select_command->flags & CMD_IGNORE_RETURN)
    select_command->action->flags |= CMD_IGNORE_RETURN;

  retval = EXECUTION_SUCCESS;
  show_menu = 1;

  while (1)
    {
      line_number = select_command->line;
      ps3_prompt = get_string_value ("PS3");
      if (ps3_prompt == 0)
	ps3_prompt = "#? ";

      QUIT;
      selection = select_query (list, list_len, ps3_prompt, show_menu);
      QUIT;
      if (selection == 0)
	{
	  /* select_query returns EXECUTION_FAILURE if the read builtin
	     fails, so we want to return failure in this case. */
	  retval = EXECUTION_FAILURE;
	  break;
	}

      v = bind_variable (identifier, selection, 0);
      if (readonly_p (v) || noassign_p (v))
	{
	  if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
	    {
	      last_command_exit_value = EXECUTION_FAILURE;
	      jump_to_top_level (FORCE_EOF);
	    }
	  else
	    {
	      dispose_words (releaser);
	      discard_unwind_frame ("select");
	      loop_level--;
	      line_number = save_line_number;
	      return (EXECUTION_FAILURE);
	    }
	}

      retval = execute_command (select_command->action);

      REAP ();
      QUIT;

      if (breaking)
	{
	  breaking--;
	  break;
	}

      if (continuing)
	{
	  continuing--;
	  if (continuing)
	    break;
	}

#if defined (KSH_COMPATIBLE_SELECT)
      show_menu = 0;
      selection = get_string_value ("REPLY");
      if (selection && *selection == '\0')
        show_menu = 1;
#endif
    }

  loop_level--;
  line_number = save_line_number;

  dispose_words (releaser);
  discard_unwind_frame ("select");
  return (retval);
}


find_function_def (name)
     const char *name;
{
#if defined (DEBUGGER)
  return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
#else
  return ((FUNCTION_DEF *)0);
#endif
}


propagate_temp_var (data)
     PTR_T data;
{
  SHELL_VAR *var;

  var = (SHELL_VAR *)data;
  if (tempvar_p (var) && (var->attributes & att_propagate))
    push_temp_var (data);
  else
    {
      if (find_special_var (var->name) >= 0)
	tempvar_list[tvlist_ind++] = savestring (var->name);
      dispose_variable (var);
    }
}


bind_tempenv_variable (name, value)
     const char *name;
     char *value;
{
  SHELL_VAR *var;

  var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;

  if (var)
    {
      FREE (value_cell (var));
      var_setvalue (var, savestring (value));
      INVALIDATE_EXPORTSTR (var);
    }

  return (var);
}


execute_command_internal (command, asynchronous, pipe_in, pipe_out,
			  fds_to_close)
     COMMAND *command;
     int asynchronous;
     int pipe_in, pipe_out;
     struct fd_bitmap *fds_to_close;
{
  int exec_result, user_subshell, invert, ignore_return, was_error_trap;
  REDIRECT *my_undo_list, *exec_undo_list;
  volatile int last_pid;
  volatile int save_line_number;
#if defined (PROCESS_SUBSTITUTION)
  volatile int ofifo, nfifo, osize, saved_fifo;
  volatile char *ofifo_list;
#endif

#if 0
  if (command == 0 || breaking || continuing || read_but_dont_execute)
    return (EXECUTION_SUCCESS);
#else
  if (breaking || continuing)
    return (last_command_exit_value);
  if (command == 0 || read_but_dont_execute)
    return (EXECUTION_SUCCESS);
#endif

  QUIT;
  run_pending_traps ();

#if 0
  if (running_trap == 0)
#endif
    currently_executing_command = command;

  invert = (command->flags & CMD_INVERT_RETURN) != 0;

  /* If we're inverting the return value and `set -e' has been executed,
     we don't want a failing command to inadvertently cause the shell
     to exit. */
  if (exit_immediately_on_error && invert)	/* XXX */
    command->flags |= CMD_IGNORE_RETURN;	/* XXX */

  exec_result = EXECUTION_SUCCESS;

  /* If a command was being explicitly run in a subshell, or if it is
     a shell control-structure, and it has a pipe, then we do the command
     in a subshell. */
  if (command->type == cm_subshell && (command->flags & CMD_NO_FORK))
    return (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));

#if defined (COPROCESS_SUPPORT)
  if (command->type == cm_coproc)
    return (execute_coproc (command, pipe_in, pipe_out, fds_to_close));
#endif

  user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);

  if (command->type == cm_subshell ||
      (command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
      (shell_control_structure (command->type) &&
       (pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
    {
      pid_t paren_pid;

      /* Fork a subshell, turn off the subshell bit, turn off job
	 control and call execute_command () on the command again. */
      line_number_for_err_trap = line_number;
      paren_pid = make_child (savestring (make_command_string (command)),
			      asynchronous);
      if (paren_pid == 0)
	exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
	/* NOTREACHED */
      else
	{
	  close_pipes (pipe_in, pipe_out);

#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
	  if (variable_context == 0)	/* wait until shell function completes */
	    unlink_fifo_list ();
#endif
	  /* If we are part of a pipeline, and not the end of the pipeline,
	     then we should simply return and let the last command in the
	     pipe be waited for.  If we are not in a pipeline, or are the
	     last command in the pipeline, then we wait for the subshell
	     and return its exit status as usual. */
	  if (pipe_out != NO_PIPE)
	    return (EXECUTION_SUCCESS);

	  stop_pipeline (asynchronous, (COMMAND *)NULL);

	  if (asynchronous == 0)
	    {
	      was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
	      invert = (command->flags & CMD_INVERT_RETURN) != 0;
	      ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;

	      exec_result = wait_for (paren_pid);

	      /* If we have to, invert the return value. */
	      if (invert)
		exec_result = ((exec_result == EXECUTION_SUCCESS)
				? EXECUTION_FAILURE
				: EXECUTION_SUCCESS);

	      last_command_exit_value = exec_result;
	      if (user_subshell && was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
		{
		  save_line_number = line_number;
		  line_number = line_number_for_err_trap;
		  run_error_trap ();
		  line_number = save_line_number;
		}

	      if (user_subshell && ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
		{
		  run_pending_traps ();
		  jump_to_top_level (ERREXIT);
		}

	      return (last_command_exit_value);
	    }
	  else
	    {
	      DESCRIBE_PID (paren_pid);

	      run_pending_traps ();

	      return (EXECUTION_SUCCESS);
	    }
	}
    }

#if defined (COMMAND_TIMING)
  if (command->flags & CMD_TIME_PIPELINE)
    {
      if (asynchronous)
	{
	  command->flags |= CMD_FORCE_SUBSHELL;
	  exec_result = execute_command_internal (command, 1, pipe_in, pipe_out, fds_to_close);
	}
      else
	{
	  exec_result = time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close);
#if 0
	  if (running_trap == 0)
#endif
	    currently_executing_command = (COMMAND *)NULL;
	}
      return (exec_result);
    }
#endif /* COMMAND_TIMING */

  if (shell_control_structure (command->type) && command->redirects)
    stdin_redir = stdin_redirects (command->redirects);

#if defined (PROCESS_SUBSTITUTION)
  if (variable_context != 0)
    {
      ofifo = num_fifos ();
      ofifo_list = copy_fifo_list (&osize);
      saved_fifo = 1;
    }
  else
    saved_fifo = 0;
#endif

  /* Handle WHILE FOR CASE etc. with redirections.  (Also '&' input
     redirection.)  */
  if (do_redirections (command->redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
    {
      cleanup_redirects (redirection_undo_list);
      redirection_undo_list = (REDIRECT *)NULL;
      dispose_exec_redirects ();
#if defined (PROCESS_SUBSTITUTION)
      if (saved_fifo)
	free (ofifo_list);
#endif
      return (last_command_exit_value = EXECUTION_FAILURE);
    }

  if (redirection_undo_list)
    {
      /* XXX - why copy here? */
      my_undo_list = (REDIRECT *)copy_redirects (redirection_undo_list);
      dispose_redirects (redirection_undo_list);
      redirection_undo_list = (REDIRECT *)NULL;
    }
  else
    my_undo_list = (REDIRECT *)NULL;

  if (exec_redirection_undo_list)
    {
      /* XXX - why copy here? */
      exec_undo_list = (REDIRECT *)copy_redirects (exec_redirection_undo_list);
      dispose_redirects (exec_redirection_undo_list);
      exec_redirection_undo_list = (REDIRECT *)NULL;
    }
  else
    exec_undo_list = (REDIRECT *)NULL;

  if (my_undo_list || exec_undo_list)
    begin_unwind_frame ("loop_redirections");

  if (my_undo_list)
    add_unwind_protect ((Function *)cleanup_redirects, my_undo_list);

  if (exec_undo_list)
    add_unwind_protect ((Function *)dispose_redirects, exec_undo_list);

  ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;

  QUIT;

  switch (command->type)
    {
    case cm_simple:
      {
	save_line_number = line_number;
	/* We can't rely on variables retaining their values across a
	   call to execute_simple_command if a longjmp occurs as the
	   result of a `return' builtin.  This is true for sure with gcc. */
#if defined (RECYCLES_PIDS)
	last_made_pid = NO_PID;
#endif
	last_pid = last_made_pid;
	was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;

	if (ignore_return && command->value.Simple)
	  command->value.Simple->flags |= CMD_IGNORE_RETURN;
	if (command->flags & CMD_STDIN_REDIR)
	  command->value.Simple->flags |= CMD_STDIN_REDIR;

	line_number_for_err_trap = line_number = command->value.Simple->line;
	exec_result =
	  execute_simple_command (command->value.Simple, pipe_in, pipe_out,
				  asynchronous, fds_to_close);
	line_number = save_line_number;

	/* The temporary environment should be used for only the simple
	   command immediately following its definition. */
	dispose_used_env_vars ();

#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA)
	/* Reclaim memory allocated with alloca () on machines which
	   may be using the alloca emulation code. */
	(void) alloca (0);
#endif /* (ultrix && mips) || C_ALLOCA */

	/* If we forked to do the command, then we must wait_for ()
	   the child. */

	/* XXX - this is something to watch out for if there are problems
	   when the shell is compiled without job control. */
	if (already_making_children && pipe_out == NO_PIPE &&
	    last_made_pid != last_pid)
	  {
	    stop_pipeline (asynchronous, (COMMAND *)NULL);

	    if (asynchronous)
	      {
		DESCRIBE_PID (last_made_pid);
	      }
	    else
#if !defined (JOB_CONTROL)
	      /* Do not wait for asynchronous processes started from
		 startup files. */
	    if (last_made_pid != last_asynchronous_pid)
#endif
	    /* When executing a shell function that executes other
	       commands, this causes the last simple command in
	       the function to be waited for twice.  This also causes
	       subshells forked to execute builtin commands (e.g., in
	       pipelines) to be waited for twice. */
	      exec_result = wait_for (last_made_pid);
	  }
      }

      /* 2009/02/13 -- pipeline failure is processed elsewhere.  This handles
	 only the failure of a simple command. */
      if (was_error_trap && ignore_return == 0 && invert == 0 && pipe_in == NO_PIPE && pipe_out == NO_PIPE && exec_result != EXECUTION_SUCCESS)
	{
	  last_command_exit_value = exec_result;
	  line_number = line_number_for_err_trap;
	  run_error_trap ();
	  line_number = save_line_number;
	}

      if (ignore_return == 0 && invert == 0 &&
	  ((posixly_correct && interactive == 0 && special_builtin_failed) ||
	   (exit_immediately_on_error && pipe_in == NO_PIPE && pipe_out == NO_PIPE && exec_result != EXECUTION_SUCCESS)))
	{
	  last_command_exit_value = exec_result;
	  run_pending_traps ();
	  jump_to_top_level (ERREXIT);
	}

      break;

    case cm_for:
      if (ignore_return)
	command->value.For->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_for_command (command->value.For);
      break;

#if defined (ARITH_FOR_COMMAND)
    case cm_arith_for:
      if (ignore_return)
	command->value.ArithFor->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_arith_for_command (command->value.ArithFor);
      break;
#endif

#if defined (SELECT_COMMAND)
    case cm_select:
      if (ignore_return)
	command->value.Select->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_select_command (command->value.Select);
      break;
#endif

    case cm_case:
      if (ignore_return)
	command->value.Case->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_case_command (command->value.Case);
      break;

    case cm_while:
      if (ignore_return)
	command->value.While->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_while_command (command->value.While);
      break;

    case cm_until:
      if (ignore_return)
	command->value.While->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_until_command (command->value.While);
      break;

    case cm_if:
      if (ignore_return)
	command->value.If->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_if_command (command->value.If);
      break;

    case cm_group:

      /* This code can be executed from either of two paths: an explicit
	 '{}' command, or via a function call.  If we are executed via a
	 function call, we have already taken care of the function being
	 executed in the background (down there in execute_simple_command ()),
	 and this command should *not* be marked as asynchronous.  If we
	 are executing a regular '{}' group command, and asynchronous == 1,
	 we must want to execute the whole command in the background, so we
	 need a subshell, and we want the stuff executed in that subshell
	 (this group command) to be executed in the foreground of that
	 subshell (i.e. there will not be *another* subshell forked).

	 What we do is to force a subshell if asynchronous, and then call
	 execute_command_internal again with asynchronous still set to 1,
	 but with the original group command, so the printed command will
	 look right.

	 The code above that handles forking off subshells will note that
	 both subshell and async are on, and turn off async in the child
	 after forking the subshell (but leave async set in the parent, so
	 the normal call to describe_pid is made).  This turning off
	 async is *crucial*; if it is not done, this will fall into an
	 infinite loop of executions through this spot in subshell after
	 subshell until the process limit is exhausted. */

      if (asynchronous)
	{
	  command->flags |= CMD_FORCE_SUBSHELL;
	  exec_result =
	    execute_command_internal (command, 1, pipe_in, pipe_out,
				      fds_to_close);
	}
      else
	{
	  if (ignore_return && command->value.Group->command)
	    command->value.Group->command->flags |= CMD_IGNORE_RETURN;
	  exec_result =
	    execute_command_internal (command->value.Group->command,
				      asynchronous, pipe_in, pipe_out,
				      fds_to_close);
	}
      break;

    case cm_connection:
      exec_result = execute_connection (command, asynchronous,
					pipe_in, pipe_out, fds_to_close);
      break;

#if defined (DPAREN_ARITHMETIC)
    case cm_arith:
      was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
      if (ignore_return)
	command->value.Arith->flags |= CMD_IGNORE_RETURN;
      line_number_for_err_trap = save_line_number = line_number;
      exec_result = execute_arith_command (command->value.Arith);
      line_number = save_line_number;

      if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
	{
	  last_command_exit_value = exec_result;
	  save_line_number = line_number;
	  line_number = line_number_for_err_trap;
	  run_error_trap ();
	  line_number = save_line_number;
	}

      if (ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
	{
	  last_command_exit_value = exec_result;
	  run_pending_traps ();
	  jump_to_top_level (ERREXIT);
	}

      break;
#endif

#if defined (COND_COMMAND)
    case cm_cond:
      was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
      if (ignore_return)
	command->value.Cond->flags |= CMD_IGNORE_RETURN;

      line_number_for_err_trap = save_line_number = line_number;
      exec_result = execute_cond_command (command->value.Cond);
      line_number = save_line_number;

      if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
	{
	  last_command_exit_value = exec_result;
	  save_line_number = line_number;
	  line_number = line_number_for_err_trap;
	  run_error_trap ();
	  line_number = save_line_number;
	}

      if (ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
	{
	  last_command_exit_value = exec_result;
	  run_pending_traps ();
	  jump_to_top_level (ERREXIT);
	}

      break;
#endif

    case cm_function_def:
      exec_result = execute_intern_function (command->value.Function_def->name,
					     command->value.Function_def);
      break;

    default:
      command_error ("execute_command", CMDERR_BADTYPE, command->type, 0);
    }

  if (my_undo_list)
    {
      do_redirections (my_undo_list, RX_ACTIVE);
      dispose_redirects (my_undo_list);
    }

  if (exec_undo_list)
    dispose_redirects (exec_undo_list);

  if (my_undo_list || exec_undo_list)
    discard_unwind_frame ("loop_redirections");

#if defined (PROCESS_SUBSTITUTION)
  if (saved_fifo)
    {
      nfifo = num_fifos ();
      if (nfifo > ofifo)
	close_new_fifos (ofifo_list, osize);
      free (ofifo_list);
    }
#endif

  /* Invert the return value if we have to */
  if (invert)
    exec_result = (exec_result == EXECUTION_SUCCESS)
		    ? EXECUTION_FAILURE
		    : EXECUTION_SUCCESS;

#if defined (DPAREN_ARITHMETIC) || defined (COND_COMMAND)
  /* This is where we set PIPESTATUS from the exit status of the appropriate
     compound commands (the ones that look enough like simple commands to
     cause confusion).  We might be able to optimize by not doing this if
     subshell_environment != 0. */
  switch (command->type)
    {
#  if defined (DPAREN_ARITHMETIC)
    case cm_arith:
#  endif
#  if defined (COND_COMMAND)
    case cm_cond:
#  endif
      set_pipestatus_from_exit (exec_result);
      break;
    }
#endif

  last_command_exit_value = exec_result;
  run_pending_traps ();
#if 0
  if (running_trap == 0)
#endif
    currently_executing_command = (COMMAND *)NULL;

  return (last_command_exit_value);
}


pop_context ()
{
  pop_dollar_vars ();
  variable_context--;
  pop_var_context ();

  sv_ifs ("IFS");		/* XXX here for now */
}


coproc_init (cp)
     struct coproc *cp;
{
  cp->c_name = 0;
  cp->c_pid = NO_PID;
  cp->c_rfd = cp->c_wfd = -1;
  cp->c_rsave = cp->c_wsave = -1;
  cp->c_flags = cp->c_status = 0;
}


merge_temporary_env ()
{
  if (temporary_env)
    dispose_temporary_env (push_temp_var);
}


coproc_alloc (name, pid)
     char *name;
     pid_t pid;
{
  struct coproc *cp;

#if MULTIPLE_COPROCS
  cp = (struct coproc *)xmalloc (sizeof (struct coproc));
#else
  cp = &sh_coproc;
#endif
  coproc_init (cp);

  cp->c_name = savestring (name);
  cp->c_pid = pid;

#if MULTIPLE_COPROCS
  cpl_add (cp);
#endif

  return (cp);
}


get_lineno (var)
     SHELL_VAR *var;
{
  char *p;
  int ln;

  ln = executing_line_number ();
  p = itos (ln);
  FREE (value_cell (var));
  var_setvalue (var, p);
  return (var);
}


execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
     COMMAND *command;
     int asynchronous;
     int pipe_in, pipe_out;
     struct fd_bitmap *fds_to_close;
{
  int user_subshell, return_code, function_value, should_redir_stdin, invert;
  int ois, user_coproc;
  int result;
  volatile COMMAND *tcom;

  USE_VAR(user_subshell);
  USE_VAR(user_coproc);
  USE_VAR(invert);
  USE_VAR(tcom);
  USE_VAR(asynchronous);

  subshell_level++;
  should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
			  pipe_in == NO_PIPE &&
			  stdin_redirects (command->redirects) == 0);

  invert = (command->flags & CMD_INVERT_RETURN) != 0;
  user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);
  user_coproc = command->type == cm_coproc;

  command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);

  /* If a command is asynchronous in a subshell (like ( foo ) & or
     the special case of an asynchronous GROUP command where the
     the subshell bit is turned on down in case cm_group: below),
     turn off `asynchronous', so that two subshells aren't spawned.
     XXX - asynchronous used to be set to 0 in this block, but that
     means that setup_async_signals was never run.  Now it's set to
     0 after subshell_environment is set appropriately and setup_async_signals
     is run.

     This seems semantically correct to me.  For example,
     ( foo ) & seems to say ``do the command `foo' in a subshell
     environment, but don't wait for that subshell to finish'',
     and "{ foo ; bar ; } &" seems to me to be like functions or
     builtins in the background, which executed in a subshell
     environment.  I just don't see the need to fork two subshells. */

  /* Don't fork again, we are already in a subshell.  A `doubly
     async' shell is not interactive, however. */
  if (asynchronous)
    {
#if defined (JOB_CONTROL)
      /* If a construct like ( exec xxx yyy ) & is given while job
	 control is active, we want to prevent exec from putting the
	 subshell back into the original process group, carefully
	 undoing all the work we just did in make_child. */
      original_pgrp = -1;
#endif /* JOB_CONTROL */
      ois = interactive_shell;
      interactive_shell = 0;
      /* This test is to prevent alias expansion by interactive shells that
	 run `(command) &' but to allow scripts that have enabled alias
	 expansion with `shopt -s expand_alias' to continue to expand
	 aliases. */
      if (ois != interactive_shell)
	expand_aliases = 0;
    }

  /* Subshells are neither login nor interactive. */
  login_shell = interactive = 0;

  if (user_subshell)
    subshell_environment = SUBSHELL_PAREN;
  else
    {
      subshell_environment = 0;			/* XXX */
      if (asynchronous)
	subshell_environment |= SUBSHELL_ASYNC;
      if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
	subshell_environment |= SUBSHELL_PIPE;
      if (user_coproc)
	subshell_environment |= SUBSHELL_COPROC;
    }

  reset_terminating_signals ();		/* in sig.c */
  /* Cancel traps, in trap.c. */
  /* Reset the signal handlers in the child, but don't free the
     trap strings.  Set a flag noting that we have to free the
     trap strings if we run trap to change a signal disposition. */
  reset_signal_handlers ();
  subshell_environment |= SUBSHELL_RESETTRAP;

  /* Make sure restore_original_signals doesn't undo the work done by
     make_child to ensure that asynchronous children are immune to SIGINT
     and SIGQUIT.  Turn off asynchronous to make sure more subshells are
     not spawned. */
  if (asynchronous)
    {
      setup_async_signals ();
      asynchronous = 0;
    }

#if defined (JOB_CONTROL)
  set_sigchld_handler ();
#endif /* JOB_CONTROL */

  set_sigint_handler ();

#if defined (JOB_CONTROL)
  /* Delete all traces that there were any jobs running.  This is
     only for subshells. */
  without_job_control ();
#endif /* JOB_CONTROL */

  if (fds_to_close)
    close_fd_bitmap (fds_to_close);

  do_piping (pipe_in, pipe_out);

#if defined (COPROCESS_SUPPORT)
  coproc_closeall ();
#endif

  /* If this is a user subshell, set a flag if stdin was redirected.
     This is used later to decide whether to redirect fd 0 to
     /dev/null for async commands in the subshell.  This adds more
     sh compatibility, but I'm not sure it's the right thing to do. */
  if (user_subshell)
    {
      stdin_redir = stdin_redirects (command->redirects);
      restore_default_signal (0);
    }

  /* If this is an asynchronous command (command &), we want to
     redirect the standard input from /dev/null in the absence of
     any specific redirection involving stdin. */
  if (should_redir_stdin && stdin_redir == 0)
    async_redirect_stdin ();

  /* Do redirections, then dispose of them before recursive call. */
  if (command->redirects)
    {
      if (do_redirections (command->redirects, RX_ACTIVE) != 0)
	exit (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE);

      dispose_redirects (command->redirects);
      command->redirects = (REDIRECT *)NULL;
    }

  if (command->type == cm_subshell)
    tcom = command->value.Subshell->command;
  else if (user_coproc)
    tcom = command->value.Coproc->command;
  else
    tcom = command;

  if (command->flags & CMD_TIME_PIPELINE)
    tcom->flags |= CMD_TIME_PIPELINE;
  if (command->flags & CMD_TIME_POSIX)
    tcom->flags |= CMD_TIME_POSIX;

  /* Make sure the subshell inherits any CMD_IGNORE_RETURN flag. */
  if ((command->flags & CMD_IGNORE_RETURN) && tcom != command)
    tcom->flags |= CMD_IGNORE_RETURN;

  /* If this is a simple command, tell execute_disk_command that it
     might be able to get away without forking and simply exec.
     This means things like ( sleep 10 ) will only cause one fork.
     If we're timing the command or inverting its return value, however,
     we cannot do this optimization. */
  if ((user_subshell || user_coproc) && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
      ((tcom->flags & CMD_TIME_PIPELINE) == 0) &&
      ((tcom->flags & CMD_INVERT_RETURN) == 0))
    {
      tcom->flags |= CMD_NO_FORK;
      if (tcom->type == cm_simple)
	tcom->value.Simple->flags |= CMD_NO_FORK;
    }

  invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
  tcom->flags &= ~CMD_INVERT_RETURN;

  result = setjmp (top_level);

  /* If we're inside a function while executing this subshell, we
     need to handle a possible `return'. */
  function_value = 0;
  if (return_catch_flag)
    function_value = setjmp (return_catch);

  /* If we're going to exit the shell, we don't want to invert the return
     status. */
  if (result == EXITPROG)
    invert = 0, return_code = last_command_exit_value;
  else if (result)
    return_code = EXECUTION_FAILURE;
  else if (function_value)
    return_code = return_catch_value;
  else
    return_code = execute_command_internal ((COMMAND *)tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);

  /* If we are asked to, invert the return value. */
  if (invert)
    return_code = (return_code == EXECUTION_SUCCESS) ? EXECUTION_FAILURE
						     : EXECUTION_SUCCESS;

  /* If we were explicitly placed in a subshell with (), we need
     to do the `shell cleanup' things, such as running traps[0]. */
  if (user_subshell && signal_is_trapped (0))
    {
      last_command_exit_value = return_code;
      return_code = run_exit_trap ();
    }

  subshell_level--;
  return (return_code);
  /* NOTREACHED */
}


dispose_used_env_vars ()
{
  if (temporary_env)
    {
      dispose_temporary_env (propagate_temp_var);
      maybe_make_export_env ();
    }
}


add_temp_array_to_env (temp_array, do_alloc, do_supercede)
     char **temp_array;
     int do_alloc, do_supercede;
{
  register int i;

  if (temp_array == 0)
    return;

  for (i = 0; temp_array[i]; i++)
    {
      if (do_supercede)
	export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
      else
	add_to_export_env (temp_array[i], do_alloc);
    }

  free (temp_array);
}


map_over_funcs (function)
     sh_var_map_func_t *function;
{
  VARLIST *vlist;
  SHELL_VAR **ret;

  if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
    return ((SHELL_VAR **)NULL);

  vlist = vlist_alloc (HASH_ENTRIES (shell_functions));

  flatten (shell_functions, function, vlist, 0);

  ret = vlist->list;
  free (vlist);
  return ret;
}


dispose_variable (var)
     SHELL_VAR *var;
{
  if (var == 0)
    return;

  if (nofree_p (var) == 0)
    dispose_variable_value (var);

  FREE_EXPORTSTR (var);

  free (var->name);

  if (exported_p (var))
    array_needs_making = 1;

  free (var);
}


sv_locale (name)
     char *name;
{
  char *v;
  int r;

  v = get_string_value (name);
  if (name[0] == 'L' && name[1] == 'A')	/* LANG */
    r = set_lang (name, v);
  else
    r = set_locale_var (name, v);		/* LC_*, TEXTDOMAIN* */

#if 1
  if (r == 0 && posixly_correct)
    last_command_exit_value = 1;
#endif
}


err_badarraysub (s)
     const char *s;
{
  report_error ("%s: %s", s, _(bash_badsub_errmsg));
}


gst_rtsp_connection_set_http_mode (GstRTSPConnection * conn, gboolean enable)
{
  g_return_if_fail (conn != NULL);

  conn->manual_http = enable;
}


str_case_hash (gconstpointer key)
{
  const char *p = key;
  guint h = g_ascii_toupper (*p);

  if (h)
    for (p += 1; *p != '\0'; p++)
      h = (h << 5) - h + g_ascii_toupper (*p);

  return h;
}


gst_rtsp_source_finalize (GSource * source)
{
  GstRTSPWatch *watch = (GstRTSPWatch *) source;
  GstRTSPSerializedMessage *msg;

  if (watch->notify)
    watch->notify (watch->user_data);

  build_reset (&watch->builder);
  gst_rtsp_message_unset (&watch->message);

  while ((msg = gst_queue_array_pop_head_struct (watch->messages))) {
    gst_rtsp_serialized_message_clear (msg);
  }
  gst_queue_array_free (watch->messages);
  watch->messages = NULL;
  watch->messages_bytes = 0;
  watch->messages_count = 0;

  g_cond_clear (&watch->queue_not_full);

  if (watch->readsrc)
    g_source_unref (watch->readsrc);
  if (watch->writesrc)
    g_source_unref (watch->writesrc);
  if (watch->controlsrc)
    g_source_unref (watch->controlsrc);

  g_mutex_clear (&watch->mutex);
}