view libinterp/corefcn/interpreter.cc @ 29503:3bfec185c9e2

experimental command window widget with server loop This changeset provides a new experimental proof-of-concept cross-platform command window. It is intended to demonstrate how communication between the GUI command window and the Octave interpreter can work when the GUI is completely responsible for user input instead of having the interpreter wait for input inside readline. This initial implementation uses a simple text box for input and a separate text edit area for output. This design is not intended to be the final arrangement, but was easy to implement for demonstration purposes. These changes also make it possible to run the command-line version of Octave in a similar client-server mode with a function gathering input in one thread and the Octave intepreter running in another. The new command window is not intended to provide a general-purpose terminal window. So running something like "system ('emacs')" will not bring up an instance of Emacs running in the command window. This also means that it will no longer be possible to use an external output pager such as "less" in the command window. OTOH, it is also no longer necessary to fork and exec a separate process on some Unixy systems when Octave starts solely to give up the controlling terminal so that "less" will function properly. With the new terminal window, it is now possible to start Octave in command line mode and later open the GUI desktop window and then return to the command line when the GUI window is closed. Some things that need to be finished: * Merge the input and output windows so that command input and output are interleaved as they are in a normal command window. * Provide real readline command editing in the GUI command window and at the client-server CLI prompt. We can use the readline callback interface for the GUI and the normal readline interface for the CLI. With this design, the command widget and the CLI front end hold a copy of the command history. They do not need to communicate with the interpreter to navigate the command history. * Create interpreter_event messages for handling user input so that the interpreter can ask the GUI to gather user input for functions like "input". * Consider passing results back to the command widget as octave_value objects and allowing the command widget to format and display them instead of passing formatted text from the interpreter to the command widget. * Provide an output pager feature for the GUI command window? Maybe this feature is not necessary since we have scroll bars in the GUI and we can also have the GUI ask the user whether to display output if it is large. ChangeLog: * options-usage.h (usage_string, octave_print_verbose_usage_and_exit): Include --experimental-terminal-widget in list of options. (EXPERIMENTAL_TERMINAL_WIDGET_OPTION): New macro. (long_opts): Include "experimental-terminal-widget" in the list. * octave.h, octave.cc (cmdline_options::cmdline_options): Handle EXPERIMENTAL_TERMINAL_WIDGET_OPTION. (cmdline_options::m_experimental_terminal_widget): New data member. (application::experimental_terminal_widget, cmdline_options::experimental_terminal_widget interpreter::experimental_terminal_widget): New functions. * main.in.cc (fork_and_exec): New static variable. (main): Don't fork and exec if argv includes --experimental-terminal-widget. * interpreter-qobject.h, interpreter-qobject.cc (interpreter_qobject::pause, interpreter_qobject::stop, interpreter_qobject::resume): New functions. (interpreter_qobject::execute): Don't set prompt strings here if using the new terminal widget. If using the new terminal widget, call interpreter::shutdown and emit the shutdown_finished signal after interpreter::execute returns. (interpreter_qobject::shutdown): Don't do anything if using the new terminal widget. * main-window.h, main-window.cc (main_window::handle_octave_ready): Set interpreter prompt strings here if using the new terminal widget. (main_window::close_gui_signal): New signal. (main_window::closeEvent): If using new terminal widget and shutdown is confirmed, simply emit close_gui_signal. (main_window::construct_octave_qt_link): If using new terminal widget, connect qt_interpreter_events::interpreter_output_signal to the terminal_dock_widget::interpreter_output slot and the qt_interpreter_events::update_prompt_signal to the terminal_dock_widget::update_prompt slot. * octave-qobject.h, octave-qobject.cc (base_qobject::interpreter_pause, base_qobject::interpreter_stop, base_qobject::interpreter_resume): New functions. (base_qobject::base_qobject): If using new terminal widget: Don't connect interpreter_qobject::execution_finished signal to octave_qobject::handle_interpreter_execution_finished slot Don't connect octave_qobject::request_interpreter_shutdown signal to the interpreter_qobject::shutdown slot. Do connect the qt_interpreter_events::start_gui_signal signal to the octave_qobject::start_gui slot and simply reload settings and call qt_application::setQuitOnLastWindowClosed. There is no need to create the main window here. (base_qobject::exec): If using new terminal widget, wait for main thread to finish after qt_application::exec returns. (base_qojbect::experimental_terminal_widget, base_qobject::gui_running): New functions. (base_qojbect::start_gui, base_qojbect::close_gui, base_qojbect::interpreter_pause, base_qojbect::interpreter_stop, base_qojbect::interpreter_resume): New slots. (base_qojbect::handle_interpreter_execution_finished): Do nothing if using new terminal widget. * qt-application.cc (qt_application::start_gui_p): Return dummy value if using new terminal widget. * qt-interpreter-events.h, qt-interpreter-events.cc (qt_interpreter_events::start_gui, qt_interpreter_events::close_gui, qt_interpreter_events::interpreter_output, qt_interpreter_events::display_exception, qt_interpreter_events::update_prompt): New functions. (qt_interpreter_events::start_gui_signal, qt_interpreter_events::close_gui_signal, qt_interpreter_events::update_prompt_signal, qt_interpreter_events::interpreter_output_signal): New signals. * command-widget.h, command-widget.cc: New files. * libgui/src/module.mk: Update. * terminal-dock-widget.h, terminal-dock-widget.cc (terminal_dock_widget::m_experimental_terminal_widget): New data member. (terminal_dock_widget::terminal_dock_widget): Optionally create and use new terminal widget. If using new terminal widget, connect command_widget::interpreter_event signals to terminal_dock_widget::interpreter_event signals. (terminal_dock_widget::interpreter_output, terminal_dock_widget::update_prompt): New slots. (terminal_dock_widget::update_prompt_signal, terminal_dock_widget::interpreter_output_signal): New signals. * event-manager.h, event-manager.cc (Fdesktop): New function. (interpreter_events::start_gui, interpreter_events::close_gui, interpreter_events::update_prompt): New virtual functions. (interpreter_events::confirm_shutdown): Return true. (event_manager::start_gui, event_manager::close_gui, event_manager::update_prompt): New functions. * interpreter.h, interpreter.cc (interpreter::initialize): If using new terminal widget, only display startup message if not initially starting GUI. (class cli_input_reader): New class. (interpreter::experimental_terminal_widget, interpreter::get_line_and_eval): New functions. (interpreter::execute): If using new terminal widget, start GUI or command line reader and then enter server loop. * pt-eval.cc (tree_evaluator::server_loop): Reset parser at top of loop. also catch exit exception.
author John W. Eaton <jwe@octave.org>
date Thu, 25 Mar 2021 23:06:40 -0400
parents 220c6c4a3533
children b65824235c7f
line wrap: on
line source

////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 1993-2021 The Octave Project Developers
//
// See the file COPYRIGHT.md in the top-level directory of this
// distribution or <https://octave.org/copyright/>.
//
// This file is part of Octave.
//
// Octave 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.
//
// Octave 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 Octave; see the file COPYING.  If not, see
// <https://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#include <cstdio>

#include <set>
#include <string>
#include <iostream>

// The following headers are only needed for the new experimental
// terminal widget.
#include <condition_variable>
#include <mutex>
#include <thread>

#include "cmd-edit.h"
#include "cmd-hist.h"
#include "file-ops.h"
#include "file-stat.h"
#include "file-ops.h"
#include "fpucw-wrappers.h"
#include "lo-blas-proto.h"
#include "lo-error.h"
#include "oct-env.h"
#include "quit.h"
#include "str-vec.h"
#include "signal-wrappers.h"
#include "unistd-wrappers.h"

#include "builtin-defun-decls.h"
#include "defaults.h"
#include "Cell.h"
#include "defun.h"
#include "display.h"
#include "error.h"
#include "event-manager.h"
#include "file-io.h"
#include "graphics.h"
#include "help.h"
#include "input.h"
#include "interpreter-private.h"
#include "interpreter.h"
#include "load-path.h"
#include "load-save.h"
#include "octave.h"
#include "oct-hist.h"
#include "oct-map.h"
#include "oct-mutex.h"
#include "ovl.h"
#include "ov.h"
#include "ov-classdef.h"
#include "parse.h"
#include "pt-classdef.h"
#include "pt-eval.h"
#include "pt-jump.h"
#include "pt-stmt.h"
#include "settings.h"
#include "sighandlers.h"
#include "sysdep.h"
#include "unwind-prot.h"
#include "utils.h"
#include "variables.h"
#include "version.h"

// TRUE means the quit() call is allowed.
bool quit_allowed = true;

// TRUE means we are ready to interpret commands, but not everything
// is ready for interactive use.
bool octave_interpreter_ready = false;

// TRUE means we've processed all the init code and we are good to go.
bool octave_initialized = false;

DEFUN (__version_info__, args, ,
       doc: /* -*- texinfo -*-
@deftypefn {} {retval =} __version_info__ (@var{name}, @var{version}, @var{release}, @var{date})
Undocumented internal function.
@end deftypefn */)
{
  static octave_map vinfo;

  int nargin = args.length ();

  if (nargin != 0 && nargin != 4)
    print_usage ();

  octave_value retval;

  if (nargin == 0)
    retval = vinfo;
  else if (nargin == 4)
    {
      if (vinfo.nfields () == 0)
        {
          vinfo.assign ("Name", args(0));
          vinfo.assign ("Version", args(1));
          vinfo.assign ("Release", args(2));
          vinfo.assign ("Date", args(3));
        }
      else
        {
          octave_idx_type n = vinfo.numel () + 1;

          vinfo.resize (dim_vector (n, 1));

          octave_value idx (n);

          vinfo.assign (idx, "Name", Cell (octave_value (args(0))));
          vinfo.assign (idx, "Version", Cell (octave_value (args(1))));
          vinfo.assign (idx, "Release", Cell (octave_value (args(2))));
          vinfo.assign (idx, "Date", Cell (octave_value (args(3))));
        }
    }

  return retval;
}

DEFMETHOD (quit, interp, args, ,
           doc: /* -*- texinfo -*-
@deftypefn  {} {} quit
@deftypefnx {} {} quit cancel
@deftypefnx {} {} quit force
@deftypefnx {} {} quit ("cancel")
@deftypefnx {} {} quit ("force")
@deftypefnx {} {} quit (@var{status})
@deftypefnx {} {} quit (@var{status}, "force")
Quit the current Octave session.

The @code{exit} function is an alias for @code{quit}.

If the optional integer value @var{status} is supplied, pass that value to
the operating system as Octave's exit status.  The default value is zero.

When exiting, Octave will attempt to run the m-file @file{finish.m} if it
exists.  User commands to save the workspace or clean up temporary files
may be placed in that file.  Alternatively, another m-file may be scheduled
to run using @code{atexit}.  If an error occurs while executing the
@file{finish.m} file, Octave does not exit and control is returned to
the command prompt.

If the optional argument @qcode{"cancel"} is provided, Octave does not
exit and control is returned to the command prompt.  This feature allows
the @code{finish.m} file to cancel the quit process.

If the user preference to request confirmation before exiting, Octave
will display a dialog and give the user an option to cancel the exit
process.

If the optional argument @qcode{"force"} is provided, no confirmation is
requested, and the execution of the @file{finish.m} file is skipped.
@seealso{atexit}
@end deftypefn */)
{
  int numel = args.length ();

  if (numel > 2)
    print_usage ();

  int exit_status = 0;

  bool force = false;
  bool cancel = false;

  if (numel == 2)
    {
      exit_status = args(0).xnint_value ("quit: STATUS must be an integer");
      std::string frc
        = args(1).xstring_value ("quit: second argument must be a string");

      if (frc == "force")
        force = true;
      else
        error (R"(quit: second argument must be string "force")");
    }
  else if (numel == 1)
    {
      if (args(0).is_string ())
        {
          const char *msg
            = R"(quit: option must be string "cancel" or "force")";

          std::string opt = args(0).xstring_value (msg);

          if (opt == "cancel")
            cancel = true;
          else if (opt == "force")
            force = true;
          else
            error ("%s", msg);
        }
      else
        exit_status = args(0).xnint_value ("quit: STATUS must be an integer");
    }

  if (cancel)
    {
      // No effect if "quit cancel" appears outside of finish script.

      if (interp.executing_finish_script ())
        interp.cancel_quit (true);

      return ovl ();
    }

  interp.quit (exit_status, force);

  return ovl ();
}

DEFALIAS (exit, quit);

DEFMETHOD (atexit, interp, args, nargout,
           doc: /* -*- texinfo -*-
@deftypefn  {} {} atexit (@var{fcn})
@deftypefnx {} {} atexit (@var{fcn}, @var{flag})
Register a function to be called when Octave exits.

For example,

@example
@group
function last_words ()
  disp ("Bye bye");
endfunction
atexit ("last_words");
@end group
@end example

@noindent
will print the message @qcode{"Bye bye"} when Octave exits.

The additional argument @var{flag} will register or unregister @var{fcn}
from the list of functions to be called when Octave exits.  If @var{flag} is
true, the function is registered, and if @var{flag} is false, it is
unregistered.  For example, after registering the function @code{last_words}
above,

@example
atexit ("last_words", false);
@end example

@noindent
will remove the function from the list and Octave will not call
@code{last_words} when it exits.

Note that @code{atexit} only removes the first occurrence of a function
from the list, so if a function was placed in the list multiple times with
@code{atexit}, it must also be removed from the list multiple times.
@seealso{quit}
@end deftypefn */)
{
  int nargin = args.length ();

  if (nargin < 1 || nargin > 2)
    print_usage ();

  std::string arg = args(0).xstring_value ("atexit: FCN argument must be a string");

  bool add_mode = (nargin == 2)
    ? args(1).xbool_value ("atexit: FLAG argument must be a logical value")
    : true;

  octave_value_list retval;

  if (add_mode)
    interp.add_atexit_fcn (arg);
  else
    {
      bool found = interp.remove_atexit_fcn (arg);

      if (nargout > 0)
        retval = ovl (found);
    }

  return retval;
}

DEFMETHOD (__traditional__, interp, , ,
           doc: /* -*- texinfo -*-
@deftypefn {} {} __traditional__ ()
Undocumented internal function.
@end deftypefn */)
{
  return ovl (interp.traditional ());
}

namespace octave
{
  temporary_file_list::~temporary_file_list (void)
  {
    cleanup ();
  }

  void temporary_file_list::insert (const std::string& file)
  {
    m_files.insert (file);
  }

  void temporary_file_list::cleanup (void)
  {
    while (! m_files.empty ())
      {
        auto it = m_files.begin ();

        octave_unlink_wrapper (it->c_str ());

        m_files.erase (it);
      }
  }

  // The time we last time we changed directories.
  sys::time Vlast_chdir_time = 0.0;

  // Execute commands from a file and catch potential exceptions in a consistent
  // way.  This function should be called anywhere we might parse and execute
  // commands from a file before we have entered the main loop in
  // toplev.cc.

  static int safe_source_file (const std::string& file_name,
                               const std::string& context = "",
                               bool verbose = false,
                               bool require_file = true)
  {
    interpreter& interp = __get_interpreter__ ("safe_source_file");

    try
      {
        source_file (file_name, context, verbose, require_file);
      }
    catch (const interrupt_exception&)
      {
        interp.recover_from_exception ();

        return 1;
      }
    catch (const execution_exception& ee)
      {
        interp.handle_exception (ee);

        return 1;
      }

    return 0;
  }

  static void initialize_version_info (void)
  {
    octave_value_list args;

    args(3) = OCTAVE_RELEASE_DATE;
    args(2) = config::release ();
    args(1) = OCTAVE_VERSION;
    args(0) = "GNU Octave";

    F__version_info__ (args, 0);
  }

  static void xerbla_abort (void)
  {
    error ("Fortran procedure terminated by call to XERBLA");
  }

  static void initialize_xerbla_error_handler (void)
  {
    // The idea here is to force xerbla to be referenced so that we will
    // link to our own version instead of the one provided by the BLAS
    // library.  But numeric_limits<double>::NaN () should never be -1, so
    // we should never actually call xerbla.  FIXME (again!): If this
    // becomes a constant expression the test might be optimized away and
    // then the reference to the function might also disappear.

    if (numeric_limits<double>::NaN () == -1)
      F77_FUNC (xerbla, XERBLA) ("octave", 13 F77_CHAR_ARG_LEN (6));

    typedef void (*xerbla_handler_ptr) (void);

    typedef void (*octave_set_xerbla_handler_ptr) (xerbla_handler_ptr);

    dynamic_library libs ("");

    if (libs)
      {
        octave_set_xerbla_handler_ptr octave_set_xerbla_handler
          = reinterpret_cast<octave_set_xerbla_handler_ptr>
              (libs.search ("octave_set_xerbla_handler"));

        if (octave_set_xerbla_handler)
          octave_set_xerbla_handler (xerbla_abort);
      }
  }

  OCTAVE_NORETURN static void
  lo_error_handler (const char *fmt, ...)
  {
    va_list args;
    va_start (args, fmt);
    verror_with_cfn (fmt, args);
    va_end (args);

    throw execution_exception ();
  }

  OCTAVE_NORETURN static void
  lo_error_with_id_handler (const char *id, const char *fmt, ...)
  {
    va_list args;
    va_start (args, fmt);
    verror_with_id_cfn (id, fmt, args);
    va_end (args);

    throw execution_exception ();
  }

  static void initialize_error_handlers (void)
  {
    set_liboctave_error_handler (lo_error_handler);
    set_liboctave_error_with_id_handler (lo_error_with_id_handler);
    set_liboctave_warning_handler (warning);
    set_liboctave_warning_with_id_handler (warning_with_id);
  }

  // Create an interpreter object and perform initialization up to the
  // point of setting reading command history and setting the load
  // path.

  interpreter::interpreter (application *app_context)
    : m_app_context (app_context),
      m_tmp_files (),
      m_atexit_fcns (),
      m_display_info (),
      m_environment (),
      m_settings (),
      m_error_system (*this),
      m_help_system (*this),
      m_input_system (*this),
      m_output_system (*this),
      m_history_system (*this),
      m_dynamic_loader (*this),
      m_load_path (*this),
      m_load_save_system (*this),
      m_type_info (),
      m_symbol_table (*this),
      m_evaluator (*this),
      m_stream_list (*this),
      m_child_list (),
      m_url_handle_manager (),
      m_cdef_manager (*this),
      m_gtk_manager (),
      m_event_manager (*this),
      m_gh_manager (nullptr),
      m_interactive (false),
      m_read_site_files (true),
      m_read_init_files (m_app_context != nullptr),
      m_verbose (false),
      m_traditional (false),
      m_inhibit_startup_message (false),
      m_load_path_initialized (false),
      m_history_initialized (false),
      m_interrupt_all_in_process_group (true),
      m_cancel_quit (false),
      m_executing_finish_script (false),
      m_executing_atexit (false),
      m_initialized (false)
  {
    // FIXME: When thread_local storage is used by default, this message
    // should change to say something like
    //
    //   only one Octave interpreter may be active in any given thread

    if (instance)
      throw std::runtime_error
        ("only one Octave interpreter may be active");

    instance = this;

    // Matlab uses "C" locale for LC_NUMERIC class regardless of local setting
    setlocale (LC_ALL, "");
    setlocale (LC_NUMERIC, "C");
    setlocale (LC_TIME, "C");
    sys::env::putenv ("LC_NUMERIC", "C");
    sys::env::putenv ("LC_TIME", "C");

    // Initialize the default floating point unit control state.
    octave_set_default_fpucw ();

    thread::init ();

    octave_ieee_init ();

    initialize_xerbla_error_handler ();

    initialize_error_handlers ();

    if (m_app_context)
      {
        install_signal_handlers ();
        octave_unblock_signal_by_name ("SIGTSTP");
      }
    else
      quit_allowed = false;

    if (! m_app_context)
      m_display_info.initialize ();

    bool line_editing = false;

    if (m_app_context)
      {
        // Embedded interpreters don't execute command line options.
        const cmdline_options& options = m_app_context->options ();

        // Make all command-line arguments available to startup files,
        // including PKG_ADD files.

        string_vector args = options.all_args ();

        m_app_context->intern_argv (args);
        intern_nargin (args.numel () - 1);

        bool is_octave_program = m_app_context->is_octave_program ();

        std::list<std::string> command_line_path = options.command_line_path ();

        for (const auto& pth : command_line_path)
          m_load_path.set_command_line_path (pth);

        std::string exec_path = options.exec_path ();
        if (! exec_path.empty ())
          m_environment.exec_path (exec_path);

        std::string image_path = options.image_path ();
        if (! image_path.empty ())
          m_environment.image_path (image_path);

        if (! options.no_window_system ())
          m_display_info.initialize ();

        // Is input coming from a terminal?  If so, we are probably
        // interactive.

        // If stdin is not a tty, then we are reading commands from a
        // pipe or a redirected file.
        bool stdin_is_tty = octave_isatty_wrapper (fileno (stdin));

        m_interactive = (! is_octave_program && stdin_is_tty
                         && octave_isatty_wrapper (fileno (stdout)));

        // Check if the user forced an interactive session.
        if (options.forced_interactive ())
          m_interactive = true;

        line_editing = options.line_editing ();
        if ((! m_interactive || options.forced_interactive ())
            && ! options.forced_line_editing ())
          line_editing = false;

        m_traditional = options.traditional ();

        // FIXME: if possible, perform the following actions directly
        // instead of using the interpreter-level functions.

        if (options.echo_commands ())
          m_evaluator.echo
            (tree_evaluator::ECHO_SCRIPTS | tree_evaluator::ECHO_FUNCTIONS
             | tree_evaluator::ECHO_ALL);

        std::string docstrings_file = options.docstrings_file ();
        if (! docstrings_file.empty ())
          Fbuilt_in_docstrings_file (*this, octave_value (docstrings_file));

        std::string doc_cache_file = options.doc_cache_file ();
        if (! doc_cache_file.empty ())
          Fdoc_cache_file (*this, octave_value (doc_cache_file));

        std::string info_file = options.info_file ();
        if (! info_file.empty ())
          Finfo_file (*this, octave_value (info_file));

        std::string info_program = options.info_program ();
        if (! info_program.empty ())
          Finfo_program (*this, octave_value (info_program));

        if (options.debug_jit ())
          Fdebug_jit (octave_value (true));

        if (options.jit_compiler ())
          Fjit_enable (octave_value (true));

        std::string texi_macros_file = options.texi_macros_file ();
        if (! texi_macros_file.empty ())
          Ftexi_macros_file (*this, octave_value (texi_macros_file));
      }

    // FIXME: we defer creation of the gh_manager object because it
    // creates a root_figure object that requires the display_info
    // object, but that is currently only accessible through the global
    // interpreter object and that is not available until after the
    // interpreter::instance pointer is set (above).  It would be better
    // if m_gh_manager could be an object value instead of a pointer and
    // created as part of the interpreter initialization.  To do that,
    // we should either make the display_info object independent of the
    // interpreter object (does it really need to cache any
    // information?) or defer creation of the root_figure object until
    // it is actually needed.
    m_gh_manager = new gh_manager (*this);

    m_input_system.initialize (line_editing);

    // These can come after command line args since none of them set any
    // defaults that might be changed by command line options.

    initialize_version_info ();

    // This should be done before initializing the load path because
    // some PKG_ADD files might need --traditional behavior.

    if (m_traditional)
      maximum_braindamage ();

    octave_interpreter_ready = true;
  }

  OCTAVE_THREAD_LOCAL interpreter *interpreter::instance = nullptr;

  interpreter::~interpreter (void)
  {
    delete m_gh_manager;
  }

  void interpreter::intern_nargin (octave_idx_type nargs)
  {
    m_evaluator.set_auto_fcn_var (stack_frame::NARGIN, nargs);
  }

  // Read the history file unless a command-line option inhibits that.

  void interpreter::initialize_history (bool read_history_file)
  {
    if (! m_history_initialized)
      {
        // Allow command-line option to override.

        if (m_app_context)
          {
            const cmdline_options& options = m_app_context->options ();

            read_history_file = options.read_history_file ();

            if (! read_history_file)
              command_history::ignore_entries ();
          }

        m_history_system.initialize (read_history_file);

        if (! m_app_context)
          command_history::ignore_entries ();

        m_history_initialized = true;
      }
  }

  // Set the initial path to the system default unless command-line
  // option says to leave it empty.

  void interpreter::initialize_load_path (bool set_initial_path)
  {
    if (! m_load_path_initialized)
      {
        // Allow command-line option to override.

        if (m_app_context)
          {
            const cmdline_options& options = m_app_context->options ();

            set_initial_path = options.set_initial_path ();
          }

        // Temporarily set the execute_pkg_add function to one that
        // catches exceptions.  This is better than wrapping
        // load_path::initialize in a try-catch block because it will
        // not stop executing PKG_ADD files at the first exception.
        // It's also better than changing the default execute_pkg_add
        // function to use safe_source file because that will normally
        // be evaluated from the normal interpreter loop where exceptions
        // are already handled.

        unwind_action restore_add_hook (&load_path::set_add_hook, &m_load_path,
                                        m_load_path.get_add_hook ());

        m_load_path.set_add_hook ([=] (const std::string& dir)
                                  { this->execute_pkg_add (dir); });

        m_load_path.initialize (set_initial_path);

        m_load_path_initialized = true;
      }
  }

  // This may be called separately from execute

  void interpreter::initialize (void)
  {
    if (m_initialized)
      return;

    const cmdline_options& options = m_app_context->options ();

    if (options.experimental_terminal_widget ())
      {
        if (! options.gui ())
          display_startup_message ();
      }
    else
      display_startup_message ();

    // Wait to read the history file until the interpreter reads input
    // files and begins evaluating commands.

    initialize_history ();

    // Initializing the load path may execute PKG_ADD files, so can't be
    // done until the interpreter is ready to execute commands.

    // Deferring it to the execute step also allows the path to be
    // initialized between creating and execute the interpreter, for
    // example, to set a custom path for an embedded interpreter.

    initialize_load_path ();

    octave_save_signal_mask ();

    can_interrupt = true;

    octave_signal_hook = respond_to_pending_signals;
    octave_interrupt_hook = nullptr;

    catch_interrupts ();

    // FIXME: could we eliminate this variable or make it not be global?
    // Global used to communicate with signal handler.
    octave_initialized = true;

    m_initialized = true;
  }

  // Note: this function is currently only used with the new
  // experimental terminal widget.

  void interpreter::get_line_and_eval (void)
  {
    std::mutex mtx;
    std::unique_lock<std::mutex> lock (mtx);
    std::condition_variable cv;
    bool incomplete_parse = false;
    bool evaluation_pending = false;
    bool exiting = false;

    while (true)
      {
        // FIXME: Detect EOF?  Use readline?  If
        // so, then we need to disable idle event loop hook function
        // execution.

        std::string ps
          = incomplete_parse ? m_input_system.PS2 () : m_input_system.PS1 ();

        std::cout << command_editor::decode_prompt_string (ps);

        std::string input;
        std::getline (std::cin, input);

        if (input.empty ())
          continue;

        incomplete_parse = false;
        evaluation_pending = true;
        exiting = false;

        m_event_manager.post_event
          ([&] (interpreter& interp)
           {
             // INTERPRETER THREAD

             std::lock_guard<std::mutex> local_lock (mtx);

             try
               {
                 interp.parse_and_execute (input, incomplete_parse);
               }
             catch (const exit_exception&)
               {
                 evaluation_pending = false;
                 exiting = true;
                 cv.notify_all ();
                 throw;
               }
             catch (const execution_exception& ee)
               {
                 m_error_system.save_exception (ee);
                 m_error_system.display_exception (ee);

                 if (m_interactive)
                   {
                     recover_from_exception ();
                     evaluation_pending = false;
                     cv.notify_all ();
                   }
                 else
                   {
                     evaluation_pending = false;
                     cv.notify_all ();
                     throw exit_exception (1);
                   }
               }
             catch (...)
               {
                 evaluation_pending = false;
                 cv.notify_all ();
                 throw;
               }

             evaluation_pending = false;
             cv.notify_all ();
           });

        // Wait until evaluation is finished before prompting for input
        // again.

        cv.wait (lock, [&] { return ! evaluation_pending; });

        if (exiting)
          break;
      }
  }

  // Note: the following class is currently only used with the new
  // experimental terminal widget.

  class cli_input_reader
  {
  public:

    cli_input_reader (interpreter& interp)
      : m_interpreter (interp), m_thread () { }

    cli_input_reader (const cli_input_reader&) = delete;

    cli_input_reader& operator = (const cli_input_reader&) = delete;

    ~cli_input_reader (void)
    {
      // FIXME: Would it be better to ensure that
      // interpreter::get_line_and_eval exits and then call
      // m_thread.join () here?

      m_thread.detach ();
    }

    void start (void)
    {
      m_thread = std::thread (&interpreter::get_line_and_eval, &m_interpreter);
    }

  private:

    interpreter& m_interpreter;

    std::thread m_thread;
  };

  void interpreter::parse_and_execute (const std::string& input,
                                       bool& incomplete_parse)
  {
    m_evaluator.parse_and_execute (input, incomplete_parse);
  }

  // FIXME: this function is intended to be executed only once.  Should
  // we enforce that restriction?

  int interpreter::execute (void)
  {
    int exit_status = 0;

    try
      {
        initialize ();

        execute_startup_files ();

        if (m_app_context)
          {
            const cmdline_options& options = m_app_context->options ();

            if (m_app_context->have_eval_option_code ())
              {
                int status = execute_eval_option_code ();

                if (status )
                  exit_status = status;

                if (! options.persist ())
                  return exit_status;
              }

            // If there is an extra argument, see if it names a file to
            // read.  Additional arguments are taken as command line options
            // for the script.

            if (m_app_context->have_script_file ())
              {
                int status = execute_command_line_file ();

                if (status)
                  exit_status = status;

                if (! options.persist ())
                  return exit_status;
              }

            if (options.forced_interactive ())
              command_editor::blink_matching_paren (false);

            if (options.server ())
              exit_status = server_loop ();
            else if (options.experimental_terminal_widget ())
              {
                if (options.gui ())
                  {
                    m_event_manager.start_gui (true);

                    exit_status = server_loop ();
                  }
                else
                  {
                    // Use an object so that the thread started for the
                    // reader will be cleaned up no matter how we exit
                    // this function.

                    cli_input_reader reader (*this);

                    reader.start ();

                    exit_status = server_loop ();
                  }
              }
            else
              exit_status = main_loop ();
          }
      }
    catch (const exit_exception& xe)
      {
        exit_status = xe.exit_status ();
      }

    return exit_status;
  }

  // Call a function with exceptions handled to avoid problems with
  // errors while shutting down.

#define OCTAVE_IGNORE_EXCEPTION(E)                                      \
  catch (E)                                                             \
    {                                                                   \
      recover_from_exception ();                                        \
                                                                        \
      std::cerr << "error: ignoring " #E " while preparing to exit"     \
                << std::endl;                                           \
    }

#define OCTAVE_SAFE_CALL(F, ARGS)                                       \
  do                                                                    \
    {                                                                   \
      try                                                               \
        {                                                               \
          unwind_action restore_debug_on_error                          \
            (&error_system::set_debug_on_error, &m_error_system,        \
             m_error_system.debug_on_error ());                        \
                                                                        \
          unwind_action restore_debug_on_warning                        \
            (&error_system::set_debug_on_warning, &m_error_system,      \
             m_error_system.debug_on_warning ());                       \
                                                                        \
          m_error_system.debug_on_error (false);                        \
          m_error_system.debug_on_warning (false);                      \
                                                                        \
          F ARGS;                                                       \
        }                                                               \
      OCTAVE_IGNORE_EXCEPTION (const exit_exception&)                   \
      OCTAVE_IGNORE_EXCEPTION (const interrupt_exception&)              \
      OCTAVE_IGNORE_EXCEPTION (const execution_exception&)              \
      OCTAVE_IGNORE_EXCEPTION (const std::bad_alloc&)                   \
    }                                                                   \
  while (0)

  void interpreter::shutdown (void)
  {
    OCTAVE_SAFE_CALL (feval, ("close", ovl ("all"), 0));

    // If we are attached to a GUI, process pending events and
    // disable the link.

    OCTAVE_SAFE_CALL (m_event_manager.process_events, (true));
    OCTAVE_SAFE_CALL (m_event_manager.disable, ());

    OCTAVE_SAFE_CALL (m_input_system.clear_input_event_hooks, ());

    // Any atexit functions added after this function call won't be
    // executed.  Each atexit function is executed with
    // OCTAVE_SAFE_CALL, so we don't need that here.

    execute_atexit_fcns ();

    // Clear all functions and variables.

    // Note that we don't force symbols to be cleared, so we will
    // respect mlock at this point.  Later, we'll force all variables
    // and functions to be cleared.

    OCTAVE_SAFE_CALL (clear_all, ());

    // We may still have some figures.  Close them.

    OCTAVE_SAFE_CALL (feval, ("close", ovl ("all"), 0));

    // What is supposed to happen if a figure has a closerequestfcn or
    // deletefcn callback registered that creates other figures or
    // variables?  What if those variables are classdef objects with
    // destructors that can create figures?  The possibilities are
    // endless.  At some point, we have to give up and force execution
    // to end.

    // Note that we again don't force symbols to be cleared, so we
    // continue to respect mlock here.  Later, we'll force all variables
    // and functions to be cleared.

    OCTAVE_SAFE_CALL (clear_all, ());

    // Do this explicitly so that destructors for mex file objects
    // are called, so that functions registered with mexAtExit are
    // called.

    OCTAVE_SAFE_CALL (m_symbol_table.clear_mex_functions, ());

    OCTAVE_SAFE_CALL (command_editor::restore_terminal_state, ());

    OCTAVE_SAFE_CALL (m_history_system.write_timestamp, ());

    if (! command_history::ignoring_entries ())
      OCTAVE_SAFE_CALL (command_history::clean_up_and_save, ());

    OCTAVE_SAFE_CALL (m_gtk_manager.unload_all_toolkits, ());

    // Now that the graphics toolkits have been unloaded, force all
    // symbols to be cleared.

    OCTAVE_SAFE_CALL (clear_all, (true));

    // FIXME:  May still need something like this to ensure that
    // destructors for class objects will run properly.  Should that be
    // done earlier?  Before or after atexit functions are executed?
    // What will happen if the destructor for an obect attempts to
    // display a figure?

    OCTAVE_SAFE_CALL (m_symbol_table.cleanup, ());

    OCTAVE_SAFE_CALL (sysdep_cleanup, ());

    OCTAVE_SAFE_CALL (flush_stdout, ());

    // Don't call singleton_cleanup_list::cleanup until we have the
    // problems with registering/unregistering types worked out.  For
    // example, uncomment the following line, then use the make_int
    // function from the examples directory to create an integer
    // object and then exit Octave.  Octave should crash with a
    // segfault when cleaning up the typinfo singleton.  We need some
    // way to force new octave_value_X types that are created in
    // .oct files to be unregistered when the .oct file shared library
    // is unloaded.
    //
    // OCTAVE_SAFE_CALL (singleton_cleanup_list::cleanup, ());
  }

  void interpreter::execute_atexit_fcns (void)
  {
    // Prevent atexit functions from adding new functions to the list.
    m_executing_atexit = true;

    while (! m_atexit_fcns.empty ())
      {
        std::string fcn = m_atexit_fcns.front ();

        m_atexit_fcns.pop_front ();

        OCTAVE_SAFE_CALL (feval, (fcn, octave_value_list (), 0));

        OCTAVE_SAFE_CALL (flush_stdout, ());
      }
  }

  void interpreter::display_startup_message (void) const
  {
    bool inhibit_startup_message = false;

    if (m_app_context)
      {
        const cmdline_options& options = m_app_context->options ();

        inhibit_startup_message = options.inhibit_startup_message ();
      }

    if (m_interactive && ! inhibit_startup_message)
      std::cout << octave_startup_message () << "\n" << std::endl;
  }

  // Initialize by reading startup files.  Return non-zero if an exception
  // occurs when reading any of them, but don't exit early because of an
  // exception.

  int interpreter::execute_startup_files (void)
  {
    bool read_site_files = m_read_site_files;
    bool read_init_files = m_read_init_files;
    bool verbose = m_verbose;
    bool inhibit_startup_message = m_inhibit_startup_message;

    if (m_app_context)
      {
        const cmdline_options& options = m_app_context->options ();

        read_site_files = options.read_site_files ();
        read_init_files = options.read_init_files ();
        verbose = options.verbose_flag ();
        inhibit_startup_message = options.inhibit_startup_message ();
      }

    verbose = (verbose && ! inhibit_startup_message);

    bool require_file = false;

    std::string context;

    int exit_status = 0;

    if (read_site_files)
      {
        // Execute commands from the site-wide configuration file.
        // First from the file $(prefix)/lib/octave/site/m/octaverc
        // (if it exists), then from the file
        // $(prefix)/share/octave/$(version)/m/octaverc (if it exists).

        int status = safe_source_file (config::local_site_defaults_file (),
                                       context, verbose, require_file);

        if (status)
          exit_status = status;

        status = safe_source_file (config::site_defaults_file (),
                                   context, verbose, require_file);

        if (status)
          exit_status = status;
      }

    if (read_init_files)
      {
        // Try to execute commands from the Matlab compatible startup.m file
        // if it exists anywhere in the load path when starting Octave.
        std::string ff_startup_m = file_in_path ("startup.m", "");

        if (! ff_startup_m.empty ())
          {
            int parse_status = 0;

            try
              {
                eval_string (std::string ("startup"), false, parse_status, 0);
              }
            catch (const interrupt_exception&)
              {
                recover_from_exception ();
              }
            catch (const execution_exception& ee)
              {
                handle_exception (ee);
              }
          }

        // Try to execute commands from $CONFIG/octave/octaverc, where
        // $CONFIG is the platform-dependent location for user local
        // configuration files.

        std::string user_config_dir = sys::env::get_user_config_directory ();

        std::string cfg_dir = user_config_dir + sys::file_ops::dir_sep_str ()
                              + "octave";

        std::string cfg_rc = sys::env::make_absolute ("octaverc", cfg_dir);

        if (! cfg_rc.empty ())
          {
            int status = safe_source_file (cfg_rc, context, verbose,
                                           require_file);

            if (status)
              exit_status = status;
          }

        // Try to execute commands from $HOME/$OCTAVE_INITFILE and
        // $OCTAVE_INITFILE.  If $OCTAVE_INITFILE is not set,
        // .octaverc is assumed.

        bool home_rc_already_executed = false;

        std::string initfile = sys::env::getenv ("OCTAVE_INITFILE");

        if (initfile.empty ())
          initfile = ".octaverc";

        std::string home_dir = sys::env::get_home_directory ();

        std::string home_rc = sys::env::make_absolute (initfile, home_dir);

        std::string local_rc;

        if (! home_rc.empty ())
          {
            int status = safe_source_file (home_rc, context, verbose,
                                           require_file);

            if (status)
              exit_status = status;

            // Names alone are not enough.

            sys::file_stat fs_home_rc (home_rc);

            if (fs_home_rc)
              {
                // We want to check for curr_dir after executing home_rc
                // because doing that may change the working directory.

                local_rc = sys::env::make_absolute (initfile);

                home_rc_already_executed = same_file (home_rc, local_rc);
              }
          }

        if (! home_rc_already_executed)
          {
            if (local_rc.empty ())
              local_rc = sys::env::make_absolute (initfile);

            int status = safe_source_file (local_rc, context, verbose,
                                           require_file);

            if (status)
              exit_status = status;
          }
      }

    if (m_interactive && verbose)
      std::cout << std::endl;

    return exit_status;
  }

  // Execute any code specified with --eval 'CODE'

  int interpreter::execute_eval_option_code (void)
  {
    if (! m_app_context)
      return 0;

    const cmdline_options& options = m_app_context->options ();

    std::string code_to_eval = options.code_to_eval ();

    unwind_protect_var<bool> upv (m_interactive, false);

    int parse_status = 0;

    try
      {
        eval_string (code_to_eval, false, parse_status, 0);
      }
    catch (const interrupt_exception&)
      {
        recover_from_exception ();

        return 1;
      }
    catch (const execution_exception& ee)
      {
        handle_exception (ee);

        return 1;
      }

    return parse_status;
  }

  int interpreter::execute_command_line_file (void)
  {
    if (! m_app_context)
      return 0;

    const cmdline_options& options = m_app_context->options ();

    string_vector args = options.all_args ();

    void (interpreter::*interactive_fptr) (bool) = &interpreter::interactive;
    unwind_action restore_interactive (interactive_fptr, this, m_interactive);

    unwind_action restore_argv (&application::intern_argv, m_app_context, args);

    unwind_action restore_nargin (&interpreter::intern_nargin, this,
                                  args.numel () - 1);

    void (application::*program_invocation_name_fptr) (const std::string&)
      = &application::program_invocation_name;
    unwind_action restore_program_invocation_name
      (program_invocation_name_fptr, m_app_context,
       application::program_invocation_name ());

    void (application::*program_name_fptr) (const std::string&)
      = &application::program_name;
    unwind_action restore_program_name
      (program_name_fptr, m_app_context, application::program_name ());

    m_interactive = false;

    // If we are running an executable script (#! /bin/octave) then
    // we should only see the args passed to the script.

    string_vector script_args = options.remaining_args ();

    m_app_context->intern_argv (script_args);
    intern_nargin (script_args.numel () - 1);

    std::string fname = script_args[0];

    m_app_context->set_program_names (fname);

    std::string context;
    bool verbose = false;
    bool require_file = true;

    return safe_source_file (fname, context, verbose, require_file);
  }

  int interpreter::main_loop (void)
  {
    return m_evaluator.repl ();
  }

  int interpreter::server_loop (void)
  {
    return m_evaluator.server_loop ();
  }

  tree_evaluator& interpreter::get_evaluator (void)
  {
    return m_evaluator;
  }

  stream_list& interpreter::get_stream_list (void)
  {
    return m_stream_list;
  }

  url_handle_manager& interpreter::get_url_handle_manager (void)
  {
    return m_url_handle_manager;
  }

  symbol_scope
  interpreter::get_top_scope (void) const
  {
    return m_evaluator.get_top_scope ();
  }

  symbol_scope
  interpreter::get_current_scope (void) const
  {
    return m_evaluator.get_current_scope ();
  }

  symbol_scope
  interpreter::require_current_scope (const std::string& who) const
  {
    symbol_scope scope = get_current_scope ();

    if (! scope)
      error ("%s: symbol table scope missing", who.c_str ());

    return scope;
  }

  profiler& interpreter::get_profiler (void)
  {
    return m_evaluator.get_profiler ();
  }

  int interpreter::chdir (const std::string& dir)
  {
    std::string xdir = sys::file_ops::tilde_expand (dir);

    int cd_ok = sys::env::chdir (xdir);

    if (! cd_ok)
      error ("%s: %s", dir.c_str (), std::strerror (errno));

    Vlast_chdir_time.stamp ();

    // FIXME: should these actions be handled as a list of functions
    // to call so users can add their own chdir handlers?

    m_load_path.read_dir_config (".");
    m_load_path.update ();

    m_event_manager.directory_changed (sys::env::get_current_directory ());

    return cd_ok;
  }

  void interpreter::mlock (bool skip_first) const
  {
    m_evaluator.mlock (skip_first);
  }

  void interpreter::munlock (bool skip_first) const
  {
    m_evaluator.munlock (skip_first);
  }

  bool interpreter::mislocked (bool skip_first) const
  {
    return m_evaluator.mislocked (skip_first);
  }

  void interpreter::munlock (const char *nm)
  {
    if (! nm)
      error ("munlock: invalid value for NAME");

    munlock (std::string (nm));
  }

  void interpreter::munlock (const std::string& nm)
  {
    octave_value val = m_symbol_table.find_function (nm);

    if (val.is_defined ())
      {
        octave_function *fcn = val.function_value ();

        if (fcn)
          fcn->unlock ();
      }
  }

  bool interpreter::mislocked (const char *nm)
  {
    if (! nm)
      error ("mislocked: invalid value for NAME");

    return mislocked (std::string (nm));
  }

  bool interpreter::mislocked (const std::string& nm)
  {
    bool retval = false;

    octave_value val = m_symbol_table.find_function (nm);

    if (val.is_defined ())
      {
        octave_function *fcn = val.function_value ();

        if (fcn)
          retval = fcn->islocked ();
      }

    return retval;
  }

  std::string interpreter::mfilename (const std::string& opt) const
  {
    return m_evaluator.mfilename (opt);
  }

  octave_value_list interpreter::eval_string (const std::string& eval_str,
                                              bool silent, int& parse_status,
                                              int nargout)
  {
    return m_evaluator.eval_string (eval_str, silent, parse_status, nargout);
  }

  octave_value interpreter::eval_string (const std::string& eval_str,
                                         bool silent, int& parse_status)
  {
    return m_evaluator.eval_string (eval_str, silent, parse_status);
  }

  octave_value_list interpreter::eval_string (const octave_value& arg,
                                              bool silent, int& parse_status,
                                              int nargout)
  {
    return m_evaluator.eval_string (arg, silent, parse_status, nargout);
  }

  octave_value_list interpreter::eval (const std::string& try_code,
                                       int nargout)
  {
    return m_evaluator.eval (try_code, nargout);
  }

  octave_value_list interpreter::eval (const std::string& try_code,
                                       const std::string& catch_code,
                                       int nargout)
  {
    return m_evaluator.eval (try_code, catch_code, nargout);
  }

  octave_value_list interpreter::evalin (const std::string& context,
                                         const std::string& try_code,
                                         int nargout)
  {
    return m_evaluator.evalin (context, try_code, nargout);
  }

  octave_value_list interpreter::evalin (const std::string& context,
                                         const std::string& try_code,
                                         const std::string& catch_code,
                                         int nargout)
  {
    return m_evaluator.evalin (context, try_code, catch_code, nargout);
  }

  //! Evaluate an Octave function (built-in or interpreted) and return
  //! the list of result values.
  //!
  //! @param name The name of the function to call.
  //! @param args The arguments to the function.
  //! @param nargout The number of output arguments expected.
  //! @return A list of output values.  The length of the list is not
  //!         necessarily the same as @c nargout.

  octave_value_list interpreter::feval (const char *name,
                                        const octave_value_list& args,
                                        int nargout)
  {
    return feval (std::string (name), args, nargout);
  }

  octave_value_list interpreter::feval (const std::string& name,
                                        const octave_value_list& args,
                                        int nargout)
  {
    octave_value fcn = m_symbol_table.find_function (name, args);

    if (fcn.is_undefined ())
      error ("feval: function '%s' not found", name.c_str ());

    octave_function *of = fcn.function_value ();

    return of->call (m_evaluator, nargout, args);
  }

  octave_value_list interpreter::feval (octave_function *fcn,
                                        const octave_value_list& args,
                                        int nargout)
  {
    if (fcn)
      return fcn->call (m_evaluator, nargout, args);

    return octave_value_list ();
  }

  octave_value_list interpreter::feval (const octave_value& val,
                                        const octave_value_list& args,
                                        int nargout)
  {
    // FIXME: do we really want to silently return an empty ovl if
    // the function object is undefined?  It's essentially what the
    // version above that accepts a pointer to an octave_function
    // object does and some code was apparently written to rely on it
    // (for example, __ode15__).

    if (val.is_undefined ())
      return ovl ();

    if (val.is_function ())
      {
        return feval (val.function_value (), args, nargout);
      }
    else if (val.is_function_handle () || val.is_inline_function ())
      {
        // This covers function handles, inline functions, and anonymous
        //  functions.

        std::list<octave_value_list> arg_list;
        arg_list.push_back (args);

        // FIXME: could we make octave_value::subsref a const method?
        // It would be difficult because there are instances of
        // incrementing the reference count inside subsref methods,
        // which means they can't be const with the current way of
        // handling reference counting.

        octave_value xval = val;
        return xval.subsref ("(", arg_list, nargout);
      }
    else if (val.is_string ())
      {
        return feval (val.string_value (), args, nargout);
      }
    else
      error ("feval: first argument must be a string, inline function, or a function handle");

    return ovl ();
  }

  //! Evaluate an Octave function (built-in or interpreted) and return
  //! the list of result values.
  //!
  //! @param args The first element of @c args is the function to call.
  //!             It may be the name of the function as a string, a function
  //!             handle, or an inline function.  The remaining arguments are
  //!             passed to the function.
  //! @param nargout The number of output arguments expected.
  //! @return A list of output values.  The length of the list is not
  //!         necessarily the same as @c nargout.

  octave_value_list interpreter::feval (const octave_value_list& args,
                                        int nargout)
  {
    if (args.length () == 0)
      error ("feval: first argument must be a string, inline function, or a function handle");

    octave_value f_arg = args(0);

    octave_value_list tmp_args = args.slice (1, args.length () - 1, true);

    return feval (f_arg, tmp_args, nargout);
  }

  octave_value interpreter::make_function_handle (const std::string& name)
  {
    return m_evaluator.make_fcn_handle (name);
  }

  void interpreter::install_variable (const std::string& name,
                                      const octave_value& value, bool global)
  {
    m_evaluator.install_variable (name, value, global);
  }

  octave_value interpreter::global_varval (const std::string& name) const
  {
    return m_evaluator.global_varval (name);
  }

  void interpreter::global_assign (const std::string& name,
                                   const octave_value& val)
  {
    m_evaluator.global_assign (name, val);
  }

  octave_value interpreter::top_level_varval (const std::string& name) const
  {
    return m_evaluator.top_level_varval (name);
  }

  void interpreter::top_level_assign (const std::string& name,
                                      const octave_value& val)
  {
    m_evaluator.top_level_assign (name, val);
  }

  bool interpreter::is_variable (const std::string& name) const
  {
    return m_evaluator.is_variable (name);
  }

  bool interpreter::is_local_variable (const std::string& name) const
  {
    return m_evaluator.is_local_variable (name);
  }

  octave_value interpreter::varval (const std::string& name) const
  {
    return m_evaluator.varval (name);
  }

  void interpreter::assign (const std::string& name,
                            const octave_value& val)
  {
    m_evaluator.assign (name, val);
  }

  void interpreter::assignin (const std::string& context,
                              const std::string& name,
                              const octave_value& val)
  {
    m_evaluator.assignin (context, name, val);
  }

  void interpreter::source_file (const std::string& file_name,
                                 const std::string& context, bool verbose,
                                 bool require_file)
  {
    m_evaluator.source_file (file_name, context, verbose, require_file);
  }

  bool interpreter::at_top_level (void) const
  {
    return m_evaluator.at_top_level ();
  }

  bool interpreter::isglobal (const std::string& name) const
  {
    return m_evaluator.is_global (name);
  }

  octave_value interpreter::find (const std::string& name)
  {
    return m_evaluator.find (name);
  }

  void interpreter::clear_all (bool force)
  {
    m_evaluator.clear_all (force);
  }

  void interpreter::clear_objects (void)
  {
    m_evaluator.clear_objects ();
  }

  void interpreter::clear_variable (const std::string& name)
  {
    m_evaluator.clear_variable (name);
  }

  void interpreter::clear_variable_pattern (const std::string& pattern)
  {
    m_evaluator.clear_variable_pattern (pattern);
  }

  void interpreter::clear_variable_regexp (const std::string& pattern)
  {
    m_evaluator.clear_variable_regexp (pattern);
  }

  void interpreter::clear_variables (void)
  {
    m_evaluator.clear_variables ();
  }

  void interpreter::clear_global_variable (const std::string& name)
  {
    m_evaluator.clear_global_variable (name);
  }

  void interpreter::clear_global_variable_pattern (const std::string& pattern)
  {
    m_evaluator.clear_global_variable_pattern (pattern);
  }

  void interpreter::clear_global_variable_regexp (const std::string& pattern)
  {
    m_evaluator.clear_global_variable_regexp (pattern);
  }

  void interpreter::clear_global_variables (void)
  {
    m_evaluator.clear_global_variables ();
  }

  void interpreter::clear_functions (bool force)
  {
    m_symbol_table.clear_functions (force);
  }

  void interpreter::clear_function (const std::string& name)
  {
    m_symbol_table.clear_function (name);
  }

  void interpreter::clear_symbol (const std::string& name)
  {
    m_evaluator.clear_symbol (name);
  }

  void interpreter::clear_function_pattern (const std::string& pat)
  {
    m_symbol_table.clear_function_pattern (pat);
  }

  void interpreter::clear_function_regexp (const std::string& pat)
  {
    m_symbol_table.clear_function_regexp (pat);
  }

  void interpreter::clear_symbol_pattern (const std::string& pat)
  {
    return m_evaluator.clear_symbol_pattern (pat);
  }

  void interpreter::clear_symbol_regexp (const std::string& pat)
  {
    return m_evaluator.clear_symbol_regexp (pat);
  }

  std::list<std::string> interpreter::global_variable_names (void)
  {
    return m_evaluator.global_variable_names ();
  }

  std::list<std::string> interpreter::top_level_variable_names (void)
  {
    return m_evaluator.top_level_variable_names ();
  }

  std::list<std::string> interpreter::variable_names (void)
  {
    return m_evaluator.variable_names ();
  }

  std::list<std::string> interpreter::user_function_names (void)
  {
    return m_symbol_table.user_function_names ();
  }

  std::list<std::string> interpreter::autoloaded_functions (void) const
  {
    return m_evaluator.autoloaded_functions ();
  }

  // May be used to send an interrupt signal to the the interpreter from
  // another thread (for example, the GUI).

  void interpreter::interrupt (void)
  {
    static int sigint = 0;
    static bool first = true;

    if (first)
      {
        octave_get_sig_number ("SIGINT", &sigint);
        first = false;
      }

    // Send SIGINT to Octave and (optionally) all other processes in its
    // process group.  The signal handler for SIGINT will set a global
    // variable indicating an interrupt has happened.  That variable is
    // checked in many places in the Octave interpreter and eventually
    // results in an interrupt_exception being thrown.  Finally, that
    // exception is caught and returns control to one of the
    // read-eval-print loops or to the server loop.  We use a signal
    // instead of just setting the global variables here so that we will
    // probably send interrupt signals to any subprocesses as well as
    // interrupt execution of the interpreter.

    pid_t pid
      = m_interrupt_all_in_process_group ? 0 : octave_getpid_wrapper ();

    octave_kill_wrapper (pid, sigint);
  }

  void interpreter::pause (void)
  {
    // FIXME: To be reliable, these tree_evaluator functions must be
    // made thread safe.

    m_evaluator.break_on_next_statement (true);
    m_evaluator.reset_debug_state ();
  }

  void interpreter::stop (void)
  {
    // FIXME: To be reliable, these tree_evaluator functions must be
    // made thread safe.

    if (m_evaluator.in_debug_repl ())
      m_evaluator.dbquit (true);
    else
      interrupt ();
  }

  void interpreter::resume (void)
  {
    // FIXME: To be reliable, these tree_evaluator functions must be
    // made thread safe.

    // FIXME: Should there be any feeback about not doing anything if
    // not in debug mode?

    if (m_evaluator.in_debug_repl ())
      m_evaluator.dbcont ();
  }

  // Provided for convenience.  Will be removed once we eliminate the
  // old terminal widget.
  bool interpreter::experimental_terminal_widget (void) const
  {
    if (! m_app_context)
      return false;

    // Embedded interpreters don't execute command line options.
    const cmdline_options& options = m_app_context->options ();

    return options.experimental_terminal_widget ();
  }

  void interpreter::add_debug_watch_expression (const std::string& expr)
  {
    m_evaluator.add_debug_watch_expression (expr);
  }

  void interpreter::remove_debug_watch_expression (const std::string& expr)
  {
    m_evaluator.remove_debug_watch_expression (expr);
  }

  void interpreter::clear_debug_watch_expressions (void)
  {
    m_evaluator.clear_debug_watch_expressions ();
  }

  std::set<std::string> interpreter::debug_watch_expressions (void) const
  {
    return m_evaluator.debug_watch_expressions ();
  }

  void interpreter::handle_exception (const execution_exception& ee)
  {
    m_error_system.save_exception (ee);

    // FIXME: use a separate stream instead of std::cerr directly so that
    // error messages can be redirected more easily?  Pass the message
    // to an event manager function?
    m_error_system.display_exception (ee);

    recover_from_exception ();
  }

  void interpreter::recover_from_exception (void)
  {
    if (octave_interrupt_state)
      m_event_manager.interpreter_interrupted ();

    can_interrupt = true;
    octave_interrupt_state = 0;
    octave_signal_caught = 0;
    octave_restore_signal_mask ();
    catch_interrupts ();
  }

  void interpreter::mark_for_deletion (const std::string& file)
  {
    m_tmp_files.insert (file);
  }

  void interpreter::cleanup_tmp_files (void)
  {
    m_tmp_files.cleanup ();
  }

  void interpreter::quit (int exit_status, bool force, bool confirm)
  {
    if (! force)
      {
        try
          {
            bool cancel = false;

            if (symbol_exist ("finish.m", "file"))
              {
                unwind_protect_var<bool> upv1 (m_executing_finish_script, true);
                unwind_protect_var<bool> upv2 (m_cancel_quit);

                evalin ("base", "finish", 0);

                cancel = m_cancel_quit;
              }

            if (cancel)
              return;

            // Check for confirmation.

            if (confirm && ! m_event_manager.confirm_shutdown ())
              return;
          }
        catch (const execution_exception&)
          {
            // Catch execution_exceptions so we don't throw an
            // exit_exception if there is an in finish.m.  But throw it
            // again so that will be handled as any other
            // execution_exception by the evaluator.  This way, errors
            // will be ignored properly and we won't exit if quit is
            // called recursively from finish.m.

            throw;
          }
      }

    throw exit_exception (exit_status);
  }

  void interpreter::add_atexit_fcn (const std::string& fname)
  {
    if (m_executing_atexit)
      return;

    m_atexit_fcns.push_front (fname);
  }

  bool interpreter::remove_atexit_fcn (const std::string& fname)
  {
    bool found = false;

    for (auto it = m_atexit_fcns.begin ();
         it != m_atexit_fcns.end (); it++)
      {
        if (*it == fname)
          {
            m_atexit_fcns.erase (it);
            found = true;
            break;
          }
      }

    return found;
  }

  void interpreter::add_atexit_function (const std::string& fname)
  {
    interpreter& interp
      = __get_interpreter__ ("interpreter::add_atexit_function");

    interp.add_atexit_fcn (fname);
  }

  bool interpreter::remove_atexit_function (const std::string& fname)
  {
    interpreter& interp
      = __get_interpreter__ ("interpreter::remove_atexit_function");

    return interp.remove_atexit_fcn (fname);
  }

  // What internal options get configured by --traditional.

  void interpreter::maximum_braindamage (void)
  {
    m_input_system.PS1 (">> ");
    m_input_system.PS2 ("");

    m_evaluator.PS4 ("");

    m_load_save_system.crash_dumps_octave_core (false);
    m_load_save_system.save_default_options ("-mat-binary");

    m_history_system.timestamp_format_string ("%%-- %D %I:%M %p --%%");

    m_error_system.beep_on_error (true);
    Fconfirm_recursive_rmdir (octave_value (false));

    Fdisable_diagonal_matrix (octave_value (true));
    Fdisable_permutation_matrix (octave_value (true));
    Fdisable_range (octave_value (true));
    Ffixed_point_format (octave_value (true));
    Fprint_empty_dimensions (octave_value (false));
    Fstruct_levels_to_print (octave_value (0));

    disable_warning ("Octave:abbreviated-property-match");
    disable_warning ("Octave:colon-nonscalar-argument");
    disable_warning ("Octave:data-file-in-path");
    disable_warning ("Octave:function-name-clash");
    disable_warning ("Octave:possible-matlab-short-circuit-operator");
  }

  void interpreter::execute_pkg_add (const std::string& dir)
  {
    try
      {
        m_load_path.execute_pkg_add (dir);
      }
    catch (const interrupt_exception&)
      {
        recover_from_exception ();
      }
    catch (const execution_exception& ee)
      {
        handle_exception (ee);
      }
  }
}