This file is shopt.def, from which is created shopt.c.
It implements the Bash `shopt' builtin.

Copyright (C) 1994-2025 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Bash.  If not, see <http://www.gnu.org/licenses/>.

$PRODUCES shopt.c

$BUILTIN shopt
$FUNCTION shopt_builtin
$SHORT_DOC shopt [-pqsu] [-o] [optname ...]
Set and unset shell options.

Change the setting of each shell option OPTNAME.  Without any option
arguments, list each supplied OPTNAME, or all shell options if no
OPTNAMEs are given, with an indication of whether or not each is set.

Options:
  -o	restrict OPTNAMEs to those defined for use with `set -o'
  -p	print each shell option with an indication of its status
  -q	suppress output
  -s	enable (set) each OPTNAME
  -u	disable (unset) each OPTNAME

Exit Status:
Returns success if OPTNAME is enabled; fails if an invalid option is
given or OPTNAME is disabled.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>

#include "../bashintl.h"

#include "../shell.h"
#include "../flags.h"
#include "common.h"
#include "bashgetopt.h"

#if defined (READLINE)
#  include "../bashline.h"
#endif

#if defined (HISTORY)
#  include "../bashhist.h"
#endif

#define UNSETOPT	0
#define SETOPT		1

#define OPTFMT		"%-20s\t%s\n"

extern int allow_null_glob_expansion, fail_glob_expansion, glob_dot_filenames;
extern int cdable_vars, mail_warning, source_uses_path;
extern int no_exit_on_failed_exec, print_shift_error;
extern int check_hashed_filenames, promptvars;
extern int cdspelling, expand_aliases;
extern int extended_quote;
extern int check_window_size;
extern int glob_ignore_case, match_ignore_case;
extern int hup_on_exit;
extern int xpg_echo;
extern int gnu_error_format;
extern int check_jobs_at_exit;
extern int autocd;
extern int glob_star;
extern int glob_asciirange;
extern int glob_always_skip_dot_and_dotdot;
extern int lastpipe_opt;
extern int inherit_errexit;
extern int localvar_inherit;
extern int localvar_unset;
extern int varassign_redir_autoclose;
extern int singlequote_translations;
extern int patsub_replacement;
extern int bash_source_fullpath;

#if defined (EXTENDED_GLOB)
extern int extended_glob;
#endif

#if defined (READLINE)
extern int hist_verify, history_reediting, perform_hostname_completion;
extern int no_empty_command_completion;
extern int force_fignore;
extern int dircomplete_spelling, dircomplete_expand;
extern int complete_fullquote;

extern int enable_hostname_completion (int);
#endif

#if defined (PROGRAMMABLE_COMPLETION)
extern int prog_completion_enabled;
extern int progcomp_alias;
#endif

#if defined (DEBUGGER)
extern int debugging_mode;
#endif

#if defined (ARRAY_VARS)
extern int array_expand_once;
int expand_once_flag;
#endif

#if defined (SYSLOG_HISTORY)
extern int syslog_history;
#endif

static void shopt_error (char *);

static int set_shellopts_after_change (char *, int);
static int set_compatibility_level (char *, int);

#if defined (RESTRICTED_SHELL)
static int set_restricted_shell (char *, int);
#endif

#if defined (READLINE)
static int shopt_enable_hostname_completion (char *, int);
static int shopt_set_complete_direxpand (char *, int);
#endif

#if defined (ARRAY_VARS)
static int set_array_expand (char *, int);
#endif

#if defined (EXTENDED_GLOB)
int extglob_flag = EXTGLOB_DEFAULT;
static int shopt_set_extglob (char *, int);
#endif

int expaliases_flag = 0;
static int shopt_set_expaliases (char *, int);

static int shopt_set_debug_mode (char *, int);

static int shopt_login_shell;
static int shopt_compat31;
static int shopt_compat32;
static int shopt_compat40;
static int shopt_compat41;
static int shopt_compat42;
static int shopt_compat43;
static int shopt_compat44;

typedef int shopt_set_func_t (char *, int);

/* If you add a new variable name here, make sure to set the default value
   appropriately in reset_shopt_options. */

static struct {
  char *name;
  int  *value;
  shopt_set_func_t *set_func;
} shopt_vars[] = {
#if defined (ARRAY_VARS)
  { "array_expand_once", &expand_once_flag, set_array_expand },
  { "assoc_expand_once", &expand_once_flag, set_array_expand },
#endif
  { "autocd", &autocd, (shopt_set_func_t *)NULL },
#if defined (ARRAY_VARS)
  { "bash_source_fullpath", &bash_source_fullpath, (shopt_set_func_t *)NULL },
#endif
  { "cdable_vars", &cdable_vars, (shopt_set_func_t *)NULL },
  { "cdspell", &cdspelling, (shopt_set_func_t *)NULL },
  { "checkhash", &check_hashed_filenames, (shopt_set_func_t *)NULL },
#if defined (JOB_CONTROL)
  { "checkjobs", &check_jobs_at_exit, (shopt_set_func_t *)NULL },
#endif
  { "checkwinsize", &check_window_size, (shopt_set_func_t *)NULL },
#if defined (HISTORY)
  { "cmdhist", &command_oriented_history, (shopt_set_func_t *)NULL },
#endif
  { "compat31", &shopt_compat31, set_compatibility_level },
  { "compat32", &shopt_compat32, set_compatibility_level },
  { "compat40", &shopt_compat40, set_compatibility_level },
  { "compat41", &shopt_compat41, set_compatibility_level },
  { "compat42", &shopt_compat42, set_compatibility_level },
  { "compat43", &shopt_compat43, set_compatibility_level },
  { "compat44", &shopt_compat44, set_compatibility_level },
#if defined (READLINE)
  { "complete_fullquote", &complete_fullquote, (shopt_set_func_t *)NULL},
  { "direxpand", &dircomplete_expand, shopt_set_complete_direxpand },
  { "dirspell", &dircomplete_spelling, (shopt_set_func_t *)NULL },
#endif
  { "dotglob", &glob_dot_filenames, (shopt_set_func_t *)NULL },
  { "execfail", &no_exit_on_failed_exec, (shopt_set_func_t *)NULL },
  { "expand_aliases", &expaliases_flag, shopt_set_expaliases },
#if defined (DEBUGGER)
  { "extdebug", &debugging_mode, shopt_set_debug_mode },
#endif
#if defined (EXTENDED_GLOB)
  { "extglob", &extglob_flag, shopt_set_extglob },
#endif
  { "extquote", &extended_quote, (shopt_set_func_t *)NULL },
  { "failglob", &fail_glob_expansion, (shopt_set_func_t *)NULL },
#if defined (READLINE)
  { "force_fignore", &force_fignore, (shopt_set_func_t *)NULL },
#endif
  { "globasciiranges", &glob_asciirange, (shopt_set_func_t *)NULL },
  { "globskipdots", &glob_always_skip_dot_and_dotdot, (shopt_set_func_t *)NULL },
  { "globstar", &glob_star, (shopt_set_func_t *)NULL },
  { "gnu_errfmt", &gnu_error_format, (shopt_set_func_t *)NULL },
#if defined (HISTORY)
  { "histappend", &force_append_history, (shopt_set_func_t *)NULL },
#endif
#if defined (READLINE)
  { "histreedit", &history_reediting, (shopt_set_func_t *)NULL },
  { "histverify", &hist_verify, (shopt_set_func_t *)NULL },
  { "hostcomplete", &perform_hostname_completion, shopt_enable_hostname_completion },
#endif
  { "huponexit", &hup_on_exit, (shopt_set_func_t *)NULL },
  { "inherit_errexit", &inherit_errexit, (shopt_set_func_t *)NULL },
  { "interactive_comments", &interactive_comments, set_shellopts_after_change },
  { "lastpipe", &lastpipe_opt, (shopt_set_func_t *)NULL },
#if defined (HISTORY)
  { "lithist", &literal_history, (shopt_set_func_t *)NULL },
#endif
  { "localvar_inherit", &localvar_inherit, (shopt_set_func_t *)NULL },
  { "localvar_unset", &localvar_unset, (shopt_set_func_t *)NULL },
  { "login_shell", &shopt_login_shell, set_login_shell },
  { "mailwarn", &mail_warning, (shopt_set_func_t *)NULL },
#if defined (READLINE)
  { "no_empty_cmd_completion", &no_empty_command_completion, (shopt_set_func_t *)NULL },
#endif
  { "nocaseglob", &glob_ignore_case, (shopt_set_func_t *)NULL },
  { "nocasematch", &match_ignore_case, (shopt_set_func_t *)NULL },
  { "noexpand_translation", &singlequote_translations, (shopt_set_func_t *)NULL },
  { "nullglob",	&allow_null_glob_expansion, (shopt_set_func_t *)NULL },
  { "patsub_replacement", &patsub_replacement, (shopt_set_func_t *)NULL },
#if defined (PROGRAMMABLE_COMPLETION)
  { "progcomp", &prog_completion_enabled, (shopt_set_func_t *)NULL },
#  if defined (ALIAS)
  { "progcomp_alias", &progcomp_alias, (shopt_set_func_t *)NULL },
#  endif
#endif
  { "promptvars", &promptvars, (shopt_set_func_t *)NULL },
#if defined (RESTRICTED_SHELL)
  { "restricted_shell", &restricted_shell, set_restricted_shell },
#endif
  { "shift_verbose", &print_shift_error, (shopt_set_func_t *)NULL },
  { "sourcepath", &source_uses_path, (shopt_set_func_t *)NULL },
#if defined (SYSLOG_HISTORY) && defined (SYSLOG_SHOPT)
  { "syslog_history", &syslog_history, (shopt_set_func_t *)NULL },
#endif
  { "varredir_close", &varassign_redir_autoclose, (shopt_set_func_t *)NULL },
  { "xpg_echo", &xpg_echo, (shopt_set_func_t *)NULL },
  { (char *)0, (int *)0, (shopt_set_func_t *)NULL }
};

#define N_SHOPT_OPTIONS		(sizeof (shopt_vars) / sizeof (shopt_vars[0]))

#define GET_SHOPT_OPTION_VALUE(i)	(*shopt_vars[i].value)

static const char * const on = "on";
static const char * const off = "off";

static int find_shopt (char *);
static int toggle_shopts (int, WORD_LIST *, int);
static void print_shopt (char *, int, int);
static int list_shopts (WORD_LIST *, int);
static int list_some_shopts (int, int);
static int list_shopt_o_options (WORD_LIST *, int);
static int list_some_o_options (int, int);
static int set_shopt_o_options (int, WORD_LIST *, int);

#define SFLAG	0x01
#define UFLAG	0x02
#define QFLAG	0x04
#define OFLAG	0x08
#define PFLAG	0x10

int
shopt_builtin (WORD_LIST *list)
{
  int opt, flags, rval;

  flags = 0;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "psuoq")) != -1)
    {
      switch (opt)
	{
	case 's':
	  flags |= SFLAG;
	  break;
	case 'u':
	  flags |= UFLAG;
	  break;
	case 'q':
	  flags |= QFLAG;
	  break;
	case 'o':
	  flags |= OFLAG;
	  break;
	case 'p':
	  flags |= PFLAG;
	  break;
	CASE_HELPOPT;
	default:
	  builtin_usage ();
	  return (EX_USAGE);
	}
    }
  list = loptend;

  if ((flags & (SFLAG|UFLAG)) == (SFLAG|UFLAG))
    {
      builtin_error (_("cannot set and unset shell options simultaneously"));
      return (EXECUTION_FAILURE);
    }

  rval = EXECUTION_SUCCESS;
  if ((flags & OFLAG) && ((flags & (SFLAG|UFLAG)) == 0))	/* shopt -o */
    rval = list_shopt_o_options (list, flags);
  else if (list && (flags & OFLAG))		/* shopt -so args */
    rval = set_shopt_o_options ((flags & SFLAG) ? FLAG_ON : FLAG_OFF, list, flags & QFLAG);
  else if (flags & OFLAG)	/* shopt -so */
    rval = list_some_o_options ((flags & SFLAG) ? 1 : 0, flags);
  else if (list && (flags & (SFLAG|UFLAG)))	/* shopt -su args */
    rval = toggle_shopts ((flags & SFLAG) ? SETOPT : UNSETOPT, list, flags & QFLAG);
  else if ((flags & (SFLAG|UFLAG)) == 0)	/* shopt [args] */
    rval = list_shopts (list, flags);
  else						/* shopt -su */
    rval = list_some_shopts ((flags & SFLAG) ? SETOPT : UNSETOPT, flags);
  return (rval);
}

/* Reset the options managed by `shopt' to the values they would have at
   shell startup.  Variables from shopt_vars. */
void
reset_shopt_options (void)
{
  autocd = cdable_vars = cdspelling = 0;
  check_hashed_filenames = CHECKHASH_DEFAULT;
  check_window_size = CHECKWINSIZE_DEFAULT;
  allow_null_glob_expansion = glob_dot_filenames = 0;
  no_exit_on_failed_exec = 0;
  expand_aliases = expaliases_flag = 0;
  extended_quote = 1;
  fail_glob_expansion = 0;
  glob_asciirange = GLOBASCII_DEFAULT;
  glob_star = 0;
  gnu_error_format = 0;
  hup_on_exit = 0;
  inherit_errexit = 0;
  interactive_comments = 1;
  lastpipe_opt = 0;
  localvar_inherit = localvar_unset = 0;
  mail_warning = 0;
  glob_ignore_case = match_ignore_case = 0;
  print_shift_error = 0;
  source_uses_path = promptvars = 1;
  varassign_redir_autoclose = 0;
  singlequote_translations = 0;
  patsub_replacement = PATSUB_REPLACE_DEFAULT;
  bash_source_fullpath = BASH_SOURCE_FULLPATH_DEFAULT;

#if defined (JOB_CONTROL)
  check_jobs_at_exit = 0;
#endif

#if defined (EXTENDED_GLOB)
  extended_glob = extglob_flag = EXTGLOB_DEFAULT;
#endif
  glob_always_skip_dot_and_dotdot = 1;		/* new default as of bash-5.2 */

#if defined (ARRAY_VARS)
  expand_once_flag = array_expand_once = 0;
#endif

#if defined (HISTORY)
  literal_history = 0;
  force_append_history = 0;
  command_oriented_history = 1;
#endif

#if defined (SYSLOG_HISTORY)
#  if defined (SYSLOG_SHOPT)
  syslog_history = SYSLOG_SHOPT;
#  else
  syslog_history = 1;
#  endif /* SYSLOG_SHOPT */
#endif

#if defined (READLINE)
  complete_fullquote = 1;
  force_fignore = 1;
  hist_verify = history_reediting = 0;
  perform_hostname_completion = 1;
#  if DIRCOMPLETE_EXPAND_DEFAULT
  dircomplete_expand = 1;
#  else
  dircomplete_expand = 0;
#endif
  dircomplete_spelling = 0;
  no_empty_command_completion = 0;
#endif

#if defined (PROGRAMMABLE_COMPLETION)
  prog_completion_enabled = 1;
#  if defined (ALIAS)
  progcomp_alias = 0;
#  endif
#endif

#if defined (DEFAULT_ECHO_TO_XPG) || defined (STRICT_POSIX)
  xpg_echo = 1;
#else
  xpg_echo = 0;
#endif /* DEFAULT_ECHO_TO_XPG */

#if defined (DEBUGGER)
  debugging_mode = 0;
#endif

  shopt_login_shell = login_shell;
}

static int
find_shopt (char *name)
{
  int i;

  for (i = 0; shopt_vars[i].name; i++)
    if (STREQ (name, shopt_vars[i].name))
      return i;
  return -1;
}

static void
shopt_error (char *s)
{
  builtin_error (_("%s: invalid shell option name"), s);
}

static int
toggle_shopts (int mode, WORD_LIST *list, int quiet)
{
  WORD_LIST *l;
  int ind, rval;
  SHELL_VAR *v;

  for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next)
    {
      ind = find_shopt (l->word->word);
      if (ind < 0)
	{
	  shopt_error (l->word->word);
	  rval = EXECUTION_FAILURE;
	}
      else
	{
	  *shopt_vars[ind].value = mode;	/* 1 for set, 0 for unset */
	  if (shopt_vars[ind].set_func)
	    (*shopt_vars[ind].set_func) (shopt_vars[ind].name, mode);
	}
    }

  /* Don't set $BASHOPTS here if it hasn't already been initialized */
  if (v = find_variable ("BASHOPTS"))
    set_bashopts ();
  return (rval);
}

static void
print_shopt (char *name, int val, int flags)
{
  if (flags & PFLAG)
    printf ("shopt %s %s\n", val ? "-s" : "-u", name);
  else
    printf (OPTFMT, name, val ? on : off);
}

/* List the values of all or any of the `shopt' options.  Returns 0 if
   all were listed or all variables queried were on; 1 otherwise. */
static int
list_shopts (WORD_LIST *list, int flags)
{
  WORD_LIST *l;
  int i, val, rval;

  if (list == 0)
    {
      for (i = 0; shopt_vars[i].name; i++)
	{
	  val = *shopt_vars[i].value;
	  if ((flags & QFLAG) == 0)
	    print_shopt (shopt_vars[i].name, val, flags);
	}
      return (sh_chkwrite (EXECUTION_SUCCESS));
    }

  for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next)
    {
      i = find_shopt (l->word->word);
      if (i < 0)
	{
	  shopt_error (l->word->word);
	  rval = EXECUTION_FAILURE;
	  continue;
	}
      val = *shopt_vars[i].value;
      if (val == 0)
	rval = EXECUTION_FAILURE;
      if ((flags & QFLAG) == 0)
	print_shopt (l->word->word, val, flags);
    }

  return (sh_chkwrite (rval));
}

static int
list_some_shopts (int mode, int flags)
{
  int val, i;

  for (i = 0; shopt_vars[i].name; i++)
    {
      val = *shopt_vars[i].value;
      if (((flags & QFLAG) == 0) && mode == val)
	print_shopt (shopt_vars[i].name, val, flags);
    }
  return (sh_chkwrite (EXECUTION_SUCCESS));
}

static int
list_shopt_o_options (WORD_LIST *list, int flags)
{
  WORD_LIST *l;
  int val, rval;

  if (list == 0)
    {
      if ((flags & QFLAG) == 0)
	list_minus_o_opts (-1, (flags & PFLAG));
      return (sh_chkwrite (EXECUTION_SUCCESS));
    }

  for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next)
    {
      val = minus_o_option_value (l->word->word);
      if (val == -1)
	{
	  sh_invalidoptname (l->word->word);
	  rval = EXECUTION_FAILURE;
	  continue;
	}
      if (val == 0)
	rval = EXECUTION_FAILURE;
      if ((flags & QFLAG) == 0)
	{
	  if (flags & PFLAG)
	    printf ("set %co %s\n", val ? '-' : '+', l->word->word);
	  else
	    printf (OPTFMT, l->word->word, val ? on : off);
	}
    }
  return (sh_chkwrite (rval));
}

static int
list_some_o_options (int mode, int flags)
{
  if ((flags & QFLAG) == 0)
    list_minus_o_opts (mode, (flags & PFLAG));
  return (sh_chkwrite (EXECUTION_SUCCESS));
}

static int
set_shopt_o_options (int mode, WORD_LIST *list, int quiet)
{
  WORD_LIST *l;
  int rval;

  for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next)
    {
      if (set_minus_o_option (mode, l->word->word) == EXECUTION_FAILURE)
	rval = EXECUTION_FAILURE;
    }
  set_shellopts ();
  return rval;
}

/* If we set or unset interactive_comments with shopt, make sure the
   change is reflected in $SHELLOPTS. */
static int
set_shellopts_after_change (char *option_name, int mode)
{
  set_shellopts ();
  return (0);
}

static int
shopt_set_debug_mode (char *option_name, int mode)
{
#if defined (DEBUGGER)
  error_trace_mode = function_trace_mode = debugging_mode;
  set_shellopts ();
  if (debugging_mode)
    init_bash_argv ();
#endif
  return (0);
}

static int
shopt_set_expaliases (char *option_name, int mode)
{
  expand_aliases = expaliases_flag;
  return 0;
}

#if defined (EXTENDED_GLOB)
static int
shopt_set_extglob (char *option_name, int mode)
{
  extended_glob = extglob_flag;
  return 0;
}
#endif

#if defined (READLINE)
static int
shopt_enable_hostname_completion (char *option_name, int mode)
{
  return (enable_hostname_completion (mode));
}
#endif

static int
set_compatibility_level (char *option_name, int mode)
{
  int ind, oldval;
  char *rhs;

  /* If we're unsetting one of the compatibility options, make sure the
     current value is in the range of the compatNN space. */
  oldval = (mode == 0) ? shell_compatibility_level : 0;

  /* If we're setting something, redo some of the work we did above in
     toggle_shopt().  Unset everything and reset the appropriate option
     based on OPTION_NAME. */
  if (mode)
    {
      shopt_compat31 = shopt_compat32 = 0;
      shopt_compat40 = shopt_compat41 = shopt_compat42 = shopt_compat43 = 0;
      shopt_compat44 = 0;
      ind = find_shopt (option_name);
      *shopt_vars[ind].value = mode;
    }

  /* Then set shell_compatibility_level based on what remains */
  if (shopt_compat31)
    shell_compatibility_level = 31;
  else if (shopt_compat32)
    shell_compatibility_level = 32;
  else if (shopt_compat40)
    shell_compatibility_level = 40;
  else if (shopt_compat41)
    shell_compatibility_level = 41;
  else if (shopt_compat42)
    shell_compatibility_level = 42;
  else if (shopt_compat43)
    shell_compatibility_level = 43;
  else if (shopt_compat44)
    shell_compatibility_level = 44;
  else if (oldval > 44 && shell_compatibility_level < default_compatibility_level)
    ;
  else
    shell_compatibility_level = default_compatibility_level;

  /* Make sure the current compatibility level is reflected in BASH_COMPAT */
  rhs = itos (shell_compatibility_level);
  bind_variable ("BASH_COMPAT", rhs, 0);
  free (rhs);

  return 0;
}

/* Set and unset the various compatibility options from the value of
   shell_compatibility_level; used by sv_shcompat */
void
set_compatibility_opts (void)
{
  shopt_compat31 = shopt_compat32 = 0;
  shopt_compat40 = shopt_compat41 = shopt_compat42 = shopt_compat43 = 0;
  shopt_compat44 = 0;
  if (shell_compatibility_level == default_compatibility_level)
    return;

  switch (shell_compatibility_level)
    {
      case 52:			/* completeness */
      case 51:			/* completeness */
      case 50:
	break;
      case 44:
	shopt_compat44 = 1; break;
      case 43:
	shopt_compat43 = 1; break;
      case 42:
	shopt_compat42 = 1; break;
      case 41:
	shopt_compat41 = 1; break;
      case 40:
	shopt_compat40 = 1; break;
      case 32:
	shopt_compat32 = 1; break;
      case 31:
	shopt_compat31 = 1; break;
    }
}

#if defined (READLINE)
static int
shopt_set_complete_direxpand (char *option_name, int mode)
{
  set_directory_hook ();
  return 0;
}
#endif

#if defined (RESTRICTED_SHELL)
/* Don't allow the value of restricted_shell to be modified. */

static int
set_restricted_shell (char *option_name, int mode)
{
  static int save_restricted = -1;

  if (save_restricted == -1)
    save_restricted = shell_is_restricted (shell_name);

  restricted_shell = save_restricted;
  return (0);
}
#endif /* RESTRICTED_SHELL */

/* Not static so shell.c can call it to initialize shopt_login_shell */
int
set_login_shell (char *option_name, int mode)
{
  shopt_login_shell = login_shell != 0;
  return (0);
}

char **
get_shopt_options (void)
{
  char **ret;
  int i;
  size_t n;

  n = sizeof (shopt_vars) / sizeof (shopt_vars[0]);
  ret = strvec_create (n + 1);
  for (i = 0; shopt_vars[i].name; i++)
    ret[i] = savestring (shopt_vars[i].name);
  ret[i] = (char *)NULL;
  return ret;
}

/*
 * External interface for other parts of the shell.  NAME is a string option;
 * MODE is 0 if we want to unset an option; 1 if we want to set an option.
 * REUSABLE is 1 if we want to print output in a form that may be reused.
 */
int
shopt_setopt (char *name, int mode)
{
  WORD_LIST *wl;
  int r;

  wl = add_string_to_list (name, (WORD_LIST *)NULL);
  r = toggle_shopts (mode, wl, 0);
  dispose_words (wl);
  return r;
}

int
shopt_listopt (char *name, int reusable)
{
  int i;

  if (name == 0)
    return (list_shopts ((WORD_LIST *)NULL, reusable ? PFLAG : 0));

  i = find_shopt (name);
  if (i < 0)
    {
      shopt_error (name);
      return (EXECUTION_FAILURE);
    }

  print_shopt (name, *shopt_vars[i].value, reusable ? PFLAG : 0);
  return (sh_chkwrite (EXECUTION_SUCCESS));
}

void
set_bashopts (void)
{
  char *value;
  char tflag[N_SHOPT_OPTIONS];
  int i, exported;
  size_t vsize, vptr;
  SHELL_VAR *v;

  for (vsize = i = 0; shopt_vars[i].name; i++)
    {
      tflag[i] = 0;
      if (GET_SHOPT_OPTION_VALUE (i))
	{
	  vsize += strlen (shopt_vars[i].name) + 1;
	  tflag[i] = 1;
	}
    }

  value = (char *)xmalloc (vsize + 1);

  for (i = vptr = 0; shopt_vars[i].name; i++)
    {
      if (tflag[i])
	{
	  strcpy (value + vptr, shopt_vars[i].name);
	  vptr += strlen (shopt_vars[i].name);
	  value[vptr++] = ':';
	}
    }

  if (vptr)
    vptr--;			/* cut off trailing colon */
  value[vptr] = '\0';

  v = find_variable ("BASHOPTS");

  /* Note whether or not the variable was exported so we can adjust after the
     assignment. */
  exported = v ? exported_p (v) : 0;

  /* ASS_FORCE so we don't have to temporarily turn off readonly */
  v = bind_variable ("BASHOPTS", value, ASS_FORCE);

  /* Turn the read-only attribute back on, and turn off the export attribute
     if it was set implicitly by mark_modified_vars and SHELLOPTS was not
     exported before we bound the new value. */
  VSETATTR (v, att_readonly);
  if (mark_modified_vars && exported == 0 && exported_p (v))
    VUNSETATTR (v, att_exported);

  free (value);
}

void
parse_bashopts (char *value)
{
  char *vname;
  int vptr, ind;

  vptr = 0;
  while (vname = extract_colon_unit (value, &vptr))
    {
      ind = find_shopt (vname);
      if (ind >= 0)
	{
          *shopt_vars[ind].value = 1;
	  if (shopt_vars[ind].set_func)
	    (*shopt_vars[ind].set_func) (shopt_vars[ind].name, 1);
	}
      free (vname);
    }
}

void
initialize_bashopts (int no_bashopts)
{
  char *temp;
  SHELL_VAR *var;

  if (no_bashopts == 0)
    {
      var = find_variable ("BASHOPTS");
      /* set up any shell options we may have inherited. */
      if (var && imported_p (var))
	{
	  temp = (array_p (var) || assoc_p (var)) ? (char *)NULL : savestring (value_cell (var));
	  if (temp)
	    {
	      parse_bashopts (temp);
	      free (temp);
	    }
	}
    }

  /* Set up the $BASHOPTS variable. */
  set_bashopts ();
}

#if defined (ARRAY_VARS)
static int
set_array_expand (char *option_name, int mode)
{
#if 0 /* leave this disabled */
  if (shell_compatibility_level <= 51)
#endif
    array_expand_once = expand_once_flag;
  return 0;
}
#endif
