view libinterp/corefcn/toplev.cc @ 21220:d78e45987d6a

rename octave::build_env namespace from octave::config * build-env-features.sh: Rename from ocgt-conf-features.sh. Update for new names. * build-env.h: Rename from oct-conf.h. Also declare features function. * build-env.in.cc: Rename from oct-conf.in.cc. * oct-conf-features.h: Delete. * libinterp/module.mk: Update. * toplev.cc, __init_gnuplot__.cc, octave.cc: Update for new names.
author John W. Eaton <jwe@octave.org>
date Sun, 07 Feb 2016 14:56:17 -0500
parents fcac5dbbf9ed
children a55b8ece1ecd
line wrap: on
line source

/*

Copyright (C) 1995-2015 John W. Eaton

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
<http://www.gnu.org/licenses/>.

*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <cassert>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <new>

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>

#include "cmd-edit.h"
#include "cmd-hist.h"
#include "file-ops.h"
#include "lo-error.h"
#include "lo-mappers.h"
#include "oct-env.h"
#include "oct-locbuf.h"
#include "quit.h"
#include "singleton-cleanup.h"
#include "str-vec.h"

#include "build-env.h"
#include "defaults.h"
#include "defun.h"
#include "error.h"
#include "file-io.h"
#include "graphics.h"
#include "input.h"
#include "lex.h"
#include "load-save.h"
#include "octave-link.h"
#include "oct-hist.h"
#include "oct-map.h"
#include "ovl.h"
#include "ov.h"
#include "pager.h"
#include "parse.h"
#include "pathsearch.h"
#include "procstream.h"
#include "pt-eval.h"
#include "pt-jump.h"
#include "pt-stmt.h"
#include "sighandlers.h"
#include "sysdep.h"
#include "syswait.h"
#include "toplev.h"
#include "unwind-prot.h"
#include "utils.h"
#include "variables.h"
#include "version.h"

#ifndef SHELL_PATH
#  define SHELL_PATH "/bin/sh"
#endif

void (*octave_exit) (int) = ::exit;

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

// TRUE means we are exiting via the builtin exit or quit functions.
bool quitting_gracefully = false;
// This stores the exit status.
int exit_status = 0;

// 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;

octave_call_stack *octave_call_stack::instance = 0;

std::string
octave_call_stack::stack_frame::fcn_file_name (void) const
{
  return m_fcn ? m_fcn->fcn_file_name () : "";
}

std::string
octave_call_stack::stack_frame::fcn_name (bool print_subfn) const
{
  std::string retval;

  if (m_fcn)
    {
      std::string parent_fcn_name = m_fcn->parent_fcn_name ();

      if (print_subfn && ! parent_fcn_name.empty ())
        retval = parent_fcn_name + Vfilemarker;

      retval += m_fcn->name ();
    }
  else
    retval = "<unknown>";

  return retval;
}

bool
octave_call_stack::stack_frame::operator== (const octave_call_stack::stack_frame &rhs) const
{
  if (this->line () != rhs.line ())
    return false;
  else if (this->column () != rhs.column ())
    return false;
  else if (this->fcn_file_name () != rhs.fcn_file_name ())
    return false;
  else if (this->fcn_name () != rhs.fcn_name ())
    return false;
  else
    return true;
}

void
octave_call_stack::create_instance (void)
{
  instance = new octave_call_stack ();

  if (instance)
    {
      instance->do_push (0, symbol_table::top_scope (), 0);

      singleton_cleanup_list::add (cleanup_instance);
    }
}

int
octave_call_stack::do_current_line (void) const
{
  int retval = -1;

  if (! cs.empty ())
    {
      const stack_frame& elt = cs[curr_frame];
      retval = elt.m_line;
    }

  return retval;
}

int
octave_call_stack::do_current_column (void) const
{
  int retval = -1;

  if (! cs.empty ())
    {
      const stack_frame& elt = cs[curr_frame];
      retval = elt.m_column;
    }

  return retval;
}

int
octave_call_stack::do_caller_user_code_line (void) const
{
  int retval = -1;

  const_iterator p = cs.end ();

  while (p != cs.begin ())
    {
      const stack_frame& elt = *(--p);

      octave_function *f = elt.m_fcn;

      if (f && f->is_user_code ())
        {
          if (elt.m_line > 0)
            {
              retval = elt.m_line;
              break;
            }
        }
    }

  return retval;
}

int
octave_call_stack::do_caller_user_code_column (void) const
{
  int retval = -1;

  const_iterator p = cs.end ();

  while (p != cs.begin ())
    {
      const stack_frame& elt = *(--p);

      octave_function *f = elt.m_fcn;

      if (f && f->is_user_code ())
        {
          if (elt.m_column)
            {
              retval = elt.m_column;
              break;
            }
        }
    }

  return retval;
}

size_t
octave_call_stack::do_num_user_code_frames
  (octave_idx_type& curr_user_frame) const
{
  size_t retval = 0;

  curr_user_frame = 0;

  // Look for the caller of dbstack.
  size_t xframe = cs[curr_frame].m_prev;

  bool found = false;

  size_t k = cs.size ();

  for (const_reverse_iterator p = cs.rbegin (); p != cs.rend (); p++)
    {
      octave_function *f = (*p).m_fcn;

      if (--k == xframe)
        found = true;

      if (f && f->is_user_code ())
        {
          if (! found)
            curr_user_frame++;

          retval++;
        }
    }

  // We counted how many user frames were not the one, in reverse.
  // Now set curr_user_frame to be the index in the other direction.
  curr_user_frame = retval - curr_user_frame - 1;

  return retval;
}

octave_user_code *
octave_call_stack::do_caller_user_code (size_t nskip) const
{
  octave_user_code *retval = 0;

  const_iterator p = cs.end ();

  while (p != cs.begin ())
    {
      const stack_frame& elt = *(--p);

      octave_function *f = elt.m_fcn;

      if (f && f->is_user_code ())
        {
          if (nskip > 0)
            nskip--;
          else
            {
              retval = dynamic_cast<octave_user_code *> (f);
              break;
            }
        }
    }

  return retval;
}

bool
octave_call_stack::do_all_scripts (void) const
{
  bool retval = true;

  const_iterator p = cs.end ();

  while (p != cs.begin ())
    {
      const stack_frame& elt = *(--p);

      octave_function *f = elt.m_fcn;

      if (f && ! f->is_user_script ())
        {
          retval = false;
          break;
        }
    }

  return retval;
}

// Use static fields for the best efficiency.
// NOTE: C++0x will allow these two to be merged into one.
static const char *bt_fieldnames[] = { "file", "name", "line",
                                       "column", "scope", "context", 0
                                     };
static const octave_fields bt_fields (bt_fieldnames);

octave_map
octave_call_stack::empty_backtrace (void)
{
  return octave_map (dim_vector (0, 1), bt_fields);
}

std::list<octave_call_stack::stack_frame>
octave_call_stack::do_backtrace_frames (size_t nskip,
                                        octave_idx_type& curr_user_frame) const
{
  std::list<octave_call_stack::stack_frame> retval;

  size_t user_code_frames = do_num_user_code_frames (curr_user_frame);

  size_t nframes = nskip <= user_code_frames ? user_code_frames - nskip : 0;

  // Our list is reversed.
  curr_user_frame = nframes - curr_user_frame - 1;

  if (nframes > 0)
    {
      for (const_reverse_iterator p = cs.rbegin (); p != cs.rend (); p++)
        {
          const stack_frame& elt = *p;

          octave_function *f = elt.m_fcn;

          if (f && f->is_user_code ())
            {
              if (nskip > 0)
                nskip--;
              else
                retval.push_back (elt);
            }
        }
    }

  return retval;
}

octave_map
octave_call_stack::do_backtrace (size_t nskip,
                                 octave_idx_type& curr_user_frame,
                                 bool print_subfn) const
{
  std::list<octave_call_stack::stack_frame> frames
    = do_backtrace_frames (nskip, curr_user_frame);

  size_t nframes = frames.size ();

  octave_map retval (dim_vector (nframes, 1), bt_fields);

  Cell& file = retval.contents (0);
  Cell& name = retval.contents (1);
  Cell& line = retval.contents (2);
  Cell& column = retval.contents (3);
  Cell& scope = retval.contents (4);
  Cell& context = retval.contents (5);

  octave_idx_type k = 0;

  for (std::list<octave_call_stack::stack_frame>::const_iterator p = frames.begin ();
       p != frames.end (); p++)
    {
      const stack_frame& elt = *p;

      scope(k) = elt.m_scope;
      context(k) = elt.m_context;
      file(k) = elt.fcn_file_name ();
      name(k) = elt.fcn_name (print_subfn);
      line(k) = elt.m_line;
      column(k) = elt.m_column;

      k++;
    }

  return retval;
}

bool
octave_call_stack::do_goto_frame (size_t n, bool verbose)
{
  bool retval = false;

  if (n < cs.size ())
    {
      retval = true;

      curr_frame = n;

      const stack_frame& elt = cs[n];

      symbol_table::set_scope_and_context (elt.m_scope, elt.m_context);

      if (verbose)
        octave_stdout << "stopped in " << elt.fcn_name ()
                      << " at line " << elt.m_line
                      << " column " << elt.m_column
                      << " (" << elt.m_scope << "[" << elt.m_context << "])"
                      << std::endl;
    }

  return retval;
}

bool
octave_call_stack::do_goto_frame_relative (int nskip, bool verbose)
{
  bool retval = false;

  int incr = 0;

  if (nskip < 0)
    incr = -1;
  else if (nskip > 0)
    incr = 1;

  // Start looking with the caller of dbup/dbdown/keyboard.
  size_t xframe = cs[curr_frame].m_prev;

  while (true)
    {
      if ((incr < 0 && xframe == 0) || (incr > 0 && xframe == cs.size () - 1))
        break;

      xframe += incr;

      const stack_frame& elt = cs[xframe];

      octave_function *f = elt.m_fcn;

      if (xframe == 0 || (f && f->is_user_code ()))
        {
          if (nskip > 0)
            nskip--;
          else if (nskip < 0)
            nskip++;

          if (nskip == 0)
            {
              curr_frame = xframe;
              cs[cs.size () - 1].m_prev = curr_frame;

              symbol_table::set_scope_and_context (elt.m_scope, elt.m_context);

              if (verbose)
                {
                  std::ostringstream buf;

                  if (f)
                    buf << "stopped in " << f->name ()
                        << " at line " << elt.m_line << std::endl;
                  else
                    buf << "at top level" << std::endl;

                  octave_stdout << buf.str ();
                }

              retval = true;
              break;
            }
        }
      else if (incr == 0)  // Break out of infinite loop by choosing an incr.
        incr = -1;

      // There is no need to set scope and context here.  That will
      // happen when the dbup/dbdown/keyboard frame is popped and we
      // jump to the new "prev" frame set above.
    }

  return retval;
}

void
octave_call_stack::do_goto_caller_frame (void)
{
  size_t xframe = curr_frame;

  bool skipped = false;

  while (xframe != 0)
    {
      xframe = cs[xframe].m_prev;

      const stack_frame& elt = cs[xframe];

      octave_function *f = elt.m_fcn;

      if (elt.m_scope == cs[0].m_scope || (f && f->is_user_code ()))
        {
          if (! skipped)
            // We found the current user code frame, so skip it.
            skipped = true;
          else
            {
              // We found the caller user code frame.
              stack_frame tmp (elt);
              tmp.m_prev = curr_frame;

              curr_frame = cs.size ();

              cs.push_back (tmp);

              symbol_table::set_scope_and_context (tmp.m_scope, tmp.m_context);

              break;
            }
        }
    }
}

void
octave_call_stack::do_goto_base_frame (void)
{
  stack_frame tmp (cs[0]);
  tmp.m_prev = curr_frame;

  curr_frame = cs.size ();

  cs.push_back (tmp);

  symbol_table::set_scope_and_context (tmp.m_scope, tmp.m_context);
}

void
recover_from_exception (void)
{
  can_interrupt = true;
  octave_interrupt_immediately = 0;
  octave_interrupt_state = 0;
  octave_signal_caught = 0;
  octave_exception_state = octave_no_exception;
  octave_restore_signal_mask ();
  octave_catch_interrupts ();
}

int
main_loop (void)
{
  octave_save_signal_mask ();

  can_interrupt = true;

  octave_signal_hook = octave_signal_handler;
  octave_interrupt_hook = 0;
  octave_bad_alloc_hook = 0;

  octave_catch_interrupts ();

  octave_initialized = true;

  // The big loop.

  octave_lexer *lxr = (interactive
                       ? new octave_lexer ()
                       : new octave_lexer (stdin));

  octave_parser parser (*lxr);

  int retval = 0;
  do
    {
      try
        {
          reset_error_handler ();

          parser.reset ();

          if (symbol_table::at_top_level ())
            tree_evaluator::reset_debug_state ();

          retval = parser.run ();

          if (retval == 0)
            {
              if (parser.stmt_list)
                {
                  parser.stmt_list->accept (*current_evaluator);

                  octave_quit ();

                  if (! interactive)
                    {
                      bool quit = (tree_return_command::returning
                                   || tree_break_command::breaking);

                      if (tree_return_command::returning)
                        tree_return_command::returning = 0;

                      if (tree_break_command::breaking)
                        tree_break_command::breaking--;

                      if (quit)
                        break;
                    }

                  if (octave_completion_matches_called)
                    octave_completion_matches_called = false;
                  else
                    command_editor::increment_current_command_number ();
                }
              else if (parser.lexer.end_of_input)
                break;
            }
        }
      catch (const octave_interrupt_exception&)
        {
          recover_from_exception ();

          octave_stdout << "\n";

          if (quitting_gracefully)
            return exit_status;
        }
      catch (const index_exception& e)
        {
          recover_from_exception ();

          std::cerr << "error: unhandled index exception: "
                    << e.message () << " -- trying to return to prompt"
                    << std::endl;
        }
      catch (const octave_execution_exception& e)
        {
          std::string stack_trace = e.info ();

          if (! stack_trace.empty ())
            std::cerr << stack_trace;

          if (interactive)
            recover_from_exception ();
          else
            {
              // We should exit with a nonzero status.
              retval = 1;
              break;
            }
        }
      catch (const std::bad_alloc&)
        {
          recover_from_exception ();

          std::cerr << "error: out of memory -- trying to return to prompt"
                    << std::endl;
        }

#ifdef DBSTOP_NANINF
      if (Vdebug_on_naninf)
        {
          if (setjump (naninf_jump) != 0)
            debug_or_throw_exception (true);  // true = stack trace
        }
#endif
    }
  while (retval == 0);

  octave_stdout << "\n";

  if (retval == EOF)
    retval = 0;

  return retval;
}

// Fix up things before exiting.

static std::list<std::string> octave_atexit_functions;

static void
do_octave_atexit (void)
{
  static bool deja_vu = false;

  OCTAVE_SAFE_CALL (remove_input_event_hook_functions, ());

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

      octave_atexit_functions.pop_front ();

      OCTAVE_SAFE_CALL (reset_error_handler, ());

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

      OCTAVE_SAFE_CALL (flush_octave_stdout, ());
    }

  if (! deja_vu)
    {
      deja_vu = true;

      // Process pending events and disasble octave_link event
      // processing with this call.

      octave_link::process_events (true);

      // Do this explicitly so that destructors for mex file objects
      // are called, so that functions registered with mexAtExit are
      // called.
      OCTAVE_SAFE_CALL (clear_mex_functions, ());

      OCTAVE_SAFE_CALL (command_editor::restore_terminal_state, ());

      // FIXME: is this needed?  Can it cause any trouble?
      OCTAVE_SAFE_CALL (raw_mode, (0));

      OCTAVE_SAFE_CALL (octave_history_write_timestamp, ());

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

      OCTAVE_SAFE_CALL (gh_manager::close_all_figures, ());

      OCTAVE_SAFE_CALL (gtk_manager::unload_all_toolkits, ());

      OCTAVE_SAFE_CALL (close_files, ());

      OCTAVE_SAFE_CALL (cleanup_tmp_files, ());

      OCTAVE_SAFE_CALL (symbol_table::cleanup, ());

      OCTAVE_SAFE_CALL (sysdep_cleanup, ());

      OCTAVE_SAFE_CALL (octave_finalize_hdf5, ());

      OCTAVE_SAFE_CALL (flush_octave_stdout, ());

      if (! quitting_gracefully && interactive)
        {
          octave_stdout << "\n";

          // Yes, we want this to be separate from the call to
          // flush_octave_stdout above.

          OCTAVE_SAFE_CALL (flush_octave_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, ());

      OCTAVE_SAFE_CALL (octave_chunk_buffer::clear, ());
    }
}

void
clean_up_and_exit (int status, bool safe_to_return)
{
  do_octave_atexit ();

  if (octave_link::exit (status))
    {
      if (safe_to_return)
        return;
      else
        {
          // What should we do here?  We might be called from some
          // location other than the end of octave_execute_interpreter,
          // so it might not be safe to return.

          // We have nothing else to do at this point, and the
          // octave_link::exit function is supposed to take care of
          // exiting for us.  Assume that job won't take more than a
          // day...

          octave_sleep (86400); // FIXME: really needed?
        }
    }
  else
    {
      if (octave_exit)
        (*octave_exit) (status);
    }
}

DEFUN (quit, args, ,
       "-*- texinfo -*-\n\
@deftypefn  {} {} exit\n\
@deftypefnx {} {} exit (@var{status})\n\
@deftypefnx {} {} quit\n\
@deftypefnx {} {} quit (@var{status})\n\
Exit the current Octave session.\n\
\n\
If the optional integer value @var{status} is supplied, pass that value to\n\
the operating system as Octave's exit status.  The default value is zero.\n\
\n\
When exiting, Octave will attempt to run the m-file @file{finish.m} if it\n\
exists.  User commands to save the workspace or clean up temporary files\n\
may be placed in that file.  Alternatively, another m-file may be scheduled\n\
to run using @code{atexit}.\n\
@seealso{atexit}\n\
@end deftypefn")
{
  // Confirm OK to shutdown.  Note: A dynamic function installation similar
  // to overriding polymorphism for which the GUI can install its own "quit"
  // yet call this base "quit" could be nice.  No link would be needed here.
  if (! octave_link::confirm_shutdown ())
    return ovl ();

  if (! quit_allowed)
    error ("quit: not supported in embedded mode");

  if (args.length () > 0)
    {
      int tmp = args(0).nint_value ();

      exit_status = tmp;
    }

  // Instead of simply calling exit, we simulate an interrupt
  // with a request to exit cleanly so that no matter where the
  // call to quit occurs, we will run the unwind_protect stack,
  // clear the OCTAVE_LOCAL_BUFFER allocations, etc. before
  // exiting.

  quitting_gracefully = true;

  octave_interrupt_state = -1;

  octave_throw_interrupt_exception ();

  return ovl ();
}

DEFALIAS (exit, quit);

DEFUN (warranty, , ,
       "-*- texinfo -*-\n\
@deftypefn {} {} warranty ()\n\
Describe the conditions for copying and distributing Octave.\n\
@end deftypefn")
{
  octave_stdout << "\n" << octave_name_version_and_copyright () << "\n\
\n\
GNU Octave is free software; you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation; either version 3 of the License, or\n\
(at your option) any later version.\n\
\n\
GNU Octave is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
GNU General Public License for more details.\n\
\n\
You should have received a copy of the GNU General Public License\n\
along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\
\n";

  return ovl ();
}

// Execute a shell command.

static int
wait_for_input (int fid)
{
  int retval = -1;

#if defined (HAVE_SELECT)
  if (fid >= 0)
    {
      fd_set set;

      FD_ZERO (&set);
      FD_SET (fid, &set);

      retval = gnulib::select (FD_SETSIZE, &set, 0, 0, 0);
    }
#else
  retval = 1;
#endif

  return retval;
}

static octave_value_list
run_command_and_return_output (const std::string& cmd_str)
{
  octave_value_list retval;
  unwind_protect frame;

  iprocstream *cmd = new iprocstream (cmd_str.c_str ());

  frame.add_delete (cmd);
  frame.add_fcn (octave_child_list::remove, cmd->pid ());

  if (! *cmd)
    error ("system: unable to start subprocess for '%s'", cmd_str.c_str ());

  int fid = cmd->file_number ();

  std::ostringstream output_buf;

  char ch;

  for (;;)
    {
      if (cmd->get (ch))
        output_buf.put (ch);
      else
        {
          if (! cmd->eof () && errno == EAGAIN)
            {
              cmd->clear ();

              if (wait_for_input (fid) != 1)
                break;
            }
          else
            break;
        }
    }

  int cmd_status = cmd->close ();

  if (octave_wait::ifexited (cmd_status))
    cmd_status = octave_wait::exitstatus (cmd_status);
  else
    cmd_status = 127;

  retval = ovl (cmd_status, output_buf.str ());

  return retval;
}

enum system_exec_type { et_sync, et_async };

DEFUN (system, args, nargout,
       "-*- texinfo -*-\n\
@deftypefn  {} {} system (\"@var{string}\")\n\
@deftypefnx {} {} system (\"@var{string}\", @var{return_output})\n\
@deftypefnx {} {} system (\"@var{string}\", @var{return_output}, @var{type})\n\
@deftypefnx {} {[@var{status}, @var{output}] =} system (@dots{})\n\
Execute a shell command specified by @var{string}.\n\
\n\
If the optional argument @var{type} is @qcode{\"async\"}, the process is\n\
started in the background and the process ID of the child process is\n\
returned immediately.  Otherwise, the child process is started and Octave\n\
waits until it exits.  If the @var{type} argument is omitted, it defaults to\n\
the value @qcode{\"sync\"}.\n\
\n\
If @var{system} is called with one or more output arguments, or if the\n\
optional argument @var{return_output} is true and the subprocess is started\n\
synchronously, then the output from the command is returned as a variable.\n\
Otherwise, if the subprocess is executed synchronously, its output is sent\n\
to the standard output.  To send the output of a command executed with\n\
@code{system} through the pager, use a command like\n\
\n\
@example\n\
@group\n\
[output, text] = system (\"cmd\");\n\
disp (text);\n\
@end group\n\
@end example\n\
\n\
@noindent\n\
or\n\
\n\
@example\n\
printf (\"%s\\n\", nthargout (2, \"system\", \"cmd\"));\n\
@end example\n\
\n\
The @code{system} function can return two values.  The first is the\n\
exit status of the command and the second is any output from the\n\
command that was written to the standard output stream.  For example,\n\
\n\
@example\n\
[status, output] = system (\"echo foo; exit 2\");\n\
@end example\n\
\n\
@noindent\n\
will set the variable @code{output} to the string @samp{foo}, and the\n\
variable @code{status} to the integer @samp{2}.\n\
\n\
For commands run asynchronously, @var{status} is the process id of the\n\
command shell that is started to run the command.\n\
@seealso{unix, dos}\n\
@end deftypefn")
{
  int nargin = args.length ();

  if (nargin == 0 || nargin > 3)
    print_usage ();

  system_exec_type type = et_sync;
  if (nargin == 3)
    {
      std::string type_str = args(2).xstring_value ("system: TYPE must be a string");

      if (type_str == "sync")
        type = et_sync;
      else if (type_str == "async")
        type = et_async;
      else
        error ("system: TYPE must be \"sync\" or \"async\"");
    }

  octave_value_list retval;

  // FIXME: Is this unwind_protect frame needed anymore (12/16/15)?
  unwind_protect frame;

  bool return_output = (nargin == 1 && nargout > 1);

  if (nargin > 1)
    {
      try
        {
          return_output = args(1).is_true ();
        }
      catch (octave_execution_exception& e)
        {
          error (e, "system: RETURN_OUTPUT must be boolean value true or false");
        }
    }

  if (return_output && type == et_async)
    error ("system: can't return output from commands run asynchronously");

  std::string cmd_str = args(0).xstring_value ("system: first argument must be a string");

#if defined (__WIN32__) && ! defined (__CYGWIN__)
  // Work around weird double-quote handling on Windows systems.
  if (type == et_sync)
    cmd_str = "\"" + cmd_str + "\"";
#endif

  if (type == et_async)
    {
      // FIXME: maybe this should go in sysdep.cc?
#ifdef HAVE_FORK
      pid_t pid = fork ();

      if (pid < 0)
        error ("system: fork failed -- can't create child process");
      else if (pid == 0)
        {
          // FIXME: should probably replace this call with something portable.
          execl (SHELL_PATH, "sh", "-c", cmd_str.c_str (),
                 static_cast<void *> (0));

          panic_impossible ();
        }
      else
        retval(0) = pid;
#elif defined (__WIN32__)
      STARTUPINFO si;
      PROCESS_INFORMATION pi;
      ZeroMemory (&si, sizeof (si));
      ZeroMemory (&pi, sizeof (pi));
      OCTAVE_LOCAL_BUFFER (char, xcmd_str, cmd_str.length ()+1);
      strcpy (xcmd_str, cmd_str.c_str ());

      if (! CreateProcess (0, xcmd_str, 0, 0, FALSE, 0, 0, 0, &si, &pi))
        error ("system: CreateProcess failed -- can't create child process");

      retval(0) = pi.dwProcessId;
      CloseHandle (pi.hProcess);
      CloseHandle (pi.hThread);
#else
      err_disabled_feature ("system", "asynchronous system calls");
#endif
    }
  else if (return_output)
    retval = run_command_and_return_output (cmd_str);
  else
    {
      int status = system (cmd_str.c_str ());

      // The value in status is as returned by waitpid.  If
      // the process exited normally, extract the actual exit
      // status of the command.  Otherwise, return 127 as a
      // failure code.

      if (octave_wait::ifexited (status))
        status = octave_wait::exitstatus (status);

      retval(0) = status;
    }

  return retval;
}

/*
%!test
%! cmd = ls_command ();
%! [status, output] = system (cmd);
%! assert (status, 0);
%! assert (ischar (output));
%! assert (! isempty (output));

%!error system ()
%!error system (1, 2, 3)
*/

void
octave_add_atexit_function (const std::string& fname)
{
  octave_atexit_functions.push_front (fname);
}

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

  for (std::list<std::string>::iterator p = octave_atexit_functions.begin ();
       p != octave_atexit_functions.end (); p++)
    {
      if (*p == fname)
        {
          octave_atexit_functions.erase (p);
          found = true;
          break;
        }
    }

  return found;
}


DEFUN (atexit, args, nargout,
       "-*- texinfo -*-\n\
@deftypefn  {} {} atexit (@var{fcn})\n\
@deftypefnx {} {} atexit (@var{fcn}, @var{flag})\n\
Register a function to be called when Octave exits.\n\
\n\
For example,\n\
\n\
@example\n\
@group\n\
function last_words ()\n\
  disp (\"Bye bye\");\n\
endfunction\n\
atexit (\"last_words\");\n\
@end group\n\
@end example\n\
\n\
@noindent\n\
will print the message @qcode{\"Bye bye\"} when Octave exits.\n\
\n\
The additional argument @var{flag} will register or unregister @var{fcn}\n\
from the list of functions to be called when Octave exits.  If @var{flag} is\n\
true, the function is registered, and if @var{flag} is false, it is\n\
unregistered.  For example, after registering the function @code{last_words}\n\
above,\n\
\n\
@example\n\
atexit (\"last_words\", false);\n\
@end example\n\
\n\
@noindent\n\
will remove the function from the list and Octave will not call\n\
@code{last_words} when it exits.\n\
\n\
Note that @code{atexit} only removes the first occurrence of a function\n\
from the list, so if a function was placed in the list multiple times with\n\
@code{atexit}, it must also be removed from the list multiple times.\n\
@seealso{quit}\n\
@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)
    octave_add_atexit_function (arg);
  else
    {
      bool found = octave_remove_atexit_function (arg);

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

  return retval;
}

DEFUN (octave_config_info, args, ,
       "-*- texinfo -*-\n\
@deftypefn  {} {} octave_config_info ()\n\
@deftypefnx {} {} octave_config_info (@var{option})\n\
Return a structure containing configuration and installation information for\n\
Octave.\n\
\n\
If @var{option} is a string, return the configuration information for the\n\
specified option.\n\
\n\
@seealso{computer}\n\
@end deftypefn")
{
#if defined (ENABLE_DYNAMIC_LINKING)
  bool octave_supports_dynamic_linking = true;
#else
  bool octave_supports_dynamic_linking = false;
#endif

  static bool initialized = false;
  static octave_scalar_map m;

  struct conf_info_struct
  {
    bool subst_home;
    const char *key;
    const char *val;
  };

  static const conf_info_struct conf_info[] =
  {
    { false, "ALL_CFLAGS", octave::build_env::ALL_CFLAGS },
    { false, "ALL_CXXFLAGS", octave::build_env::ALL_CXXFLAGS },
    { false, "ALL_FFLAGS", octave::build_env::ALL_FFLAGS },
    { false, "ALL_LDFLAGS", octave::build_env::ALL_LDFLAGS },
    { false, "AMD_CPPFLAGS", octave::build_env::AMD_CPPFLAGS },
    { false, "AMD_LDFLAGS", octave::build_env::AMD_LDFLAGS },
    { false, "AMD_LIBS", octave::build_env::AMD_LIBS },
    { false, "AR", octave::build_env::AR },
    { false, "ARFLAGS", octave::build_env::ARFLAGS },
    { false, "ARPACK_CPPFLAGS", octave::build_env::ARPACK_CPPFLAGS },
    { false, "ARPACK_LDFLAGS", octave::build_env::ARPACK_LDFLAGS },
    { false, "ARPACK_LIBS", octave::build_env::ARPACK_LIBS },
    { false, "BLAS_LIBS", octave::build_env::BLAS_LIBS },
    { false, "CAMD_CPPFLAGS", octave::build_env::CAMD_CPPFLAGS },
    { false, "CAMD_LDFLAGS", octave::build_env::CAMD_LDFLAGS },
    { false, "CAMD_LIBS", octave::build_env::CAMD_LIBS },
    { false, "CARBON_LIBS", octave::build_env::CARBON_LIBS },
    { false, "CC", octave::build_env::CC },
    { false, "CCOLAMD_CPPFLAGS", octave::build_env::CCOLAMD_CPPFLAGS },
    { false, "CCOLAMD_LDFLAGS", octave::build_env::CCOLAMD_LDFLAGS },
    { false, "CCOLAMD_LIBS", octave::build_env::CCOLAMD_LIBS },
    { false, "CFLAGS", octave::build_env::CFLAGS },
    { false, "CHOLMOD_CPPFLAGS", octave::build_env::CHOLMOD_CPPFLAGS },
    { false, "CHOLMOD_LDFLAGS", octave::build_env::CHOLMOD_LDFLAGS },
    { false, "CHOLMOD_LIBS", octave::build_env::CHOLMOD_LIBS },
    { false, "COLAMD_CPPFLAGS", octave::build_env::COLAMD_CPPFLAGS },
    { false, "COLAMD_LDFLAGS", octave::build_env::COLAMD_LDFLAGS },
    { false, "COLAMD_LIBS", octave::build_env::COLAMD_LIBS },
    { false, "CPICFLAG", octave::build_env::CPICFLAG },
    { false, "CPPFLAGS", octave::build_env::CPPFLAGS },
    { false, "CURL_CPPFLAGS", octave::build_env::CURL_CPPFLAGS },
    { false, "CURL_LDFLAGS", octave::build_env::CURL_LDFLAGS },
    { false, "CURL_LIBS", octave::build_env::CURL_LIBS },
    { false, "CXSPARSE_CPPFLAGS", octave::build_env::CXSPARSE_CPPFLAGS },
    { false, "CXSPARSE_LDFLAGS", octave::build_env::CXSPARSE_LDFLAGS },
    { false, "CXSPARSE_LIBS", octave::build_env::CXSPARSE_LIBS },
    { false, "CXX", octave::build_env::CXX },
    { false, "CXXCPP", octave::build_env::CXXCPP },
    { false, "CXXFLAGS", octave::build_env::CXXFLAGS },
    { false, "CXXPICFLAG", octave::build_env::CXXPICFLAG },
    { false, "DEFAULT_PAGER", OCTAVE_DEFAULT_PAGER },
    { false, "DEFS", octave::build_env::DEFS },
    { false, "DL_LD", octave::build_env::DL_LD },
    { false, "DL_LDFLAGS", octave::build_env::DL_LDFLAGS },
    { false, "DL_LIBS", octave::build_env::DL_LIBS },
    { false, "GCC_VERSION", octave::build_env::GCC_VERSION },
    { false, "GXX_VERSION", octave::build_env::GXX_VERSION },
    { false, "EXEEXT", octave::build_env::EXEEXT },
    { false, "F77", octave::build_env::F77 },
    { false, "F77_FLOAT_STORE_FLAG", octave::build_env::F77_FLOAT_STORE_FLAG },
    { false, "F77_INTEGER_8_FLAG", octave::build_env::F77_INTEGER_8_FLAG },
    { false, "FFLAGS", octave::build_env::FFLAGS },
    { false, "FFTW3_CPPFLAGS", octave::build_env::FFTW3_CPPFLAGS },
    { false, "FFTW3_LDFLAGS", octave::build_env::FFTW3_LDFLAGS },
    { false, "FFTW3_LIBS", octave::build_env::FFTW3_LIBS },
    { false, "FFTW3F_CPPFLAGS", octave::build_env::FFTW3F_CPPFLAGS },
    { false, "FFTW3F_LDFLAGS", octave::build_env::FFTW3F_LDFLAGS },
    { false, "FFTW3F_LIBS", octave::build_env::FFTW3F_LIBS },
    { false, "FLIBS", octave::build_env::FLIBS },
    { false, "FLTK_CPPFLAGS", octave::build_env::FLTK_CPPFLAGS },
    { false, "FLTK_LDFLAGS", octave::build_env::FLTK_LDFLAGS },
    { false, "FLTK_LIBS", octave::build_env::FLTK_LIBS },
    { false, "FONTCONFIG_CPPFLAGS", octave::build_env::FONTCONFIG_CPPFLAGS },
    { false, "FONTCONFIG_LIBS", octave::build_env::FONTCONFIG_LIBS },
    { false, "FPICFLAG", octave::build_env::FPICFLAG },
    { false, "FT2_CPPFLAGS", octave::build_env::FT2_CPPFLAGS },
    { false, "FT2_LIBS", octave::build_env::FT2_LIBS },
    { false, "GLPK_CPPFLAGS", octave::build_env::GLPK_CPPFLAGS },
    { false, "GLPK_LDFLAGS", octave::build_env::GLPK_LDFLAGS },
    { false, "GLPK_LIBS", octave::build_env::GLPK_LIBS },
    { false, "GNUPLOT", octave::build_env::GNUPLOT },
    { false, "HDF5_CPPFLAGS", octave::build_env::HDF5_CPPFLAGS },
    { false, "HDF5_LDFLAGS", octave::build_env::HDF5_LDFLAGS },
    { false, "HDF5_LIBS", octave::build_env::HDF5_LIBS },
    { false, "LAPACK_LIBS", octave::build_env::LAPACK_LIBS },
    { false, "LDFLAGS", octave::build_env::LDFLAGS },
    { false, "LD_CXX", octave::build_env::LD_CXX },
    { false, "LD_STATIC_FLAG", octave::build_env::LD_STATIC_FLAG },
    { false, "LEX", octave::build_env::LEX },
    { false, "LEXLIB", octave::build_env::LEXLIB },
    { false, "LFLAGS", octave::build_env::LFLAGS },
    { false, "LIBEXT", octave::build_env::LIBEXT },
    { false, "LIBFLAGS", octave::build_env::LIBFLAGS },
    { false, "LIBOCTAVE", octave::build_env::LIBOCTAVE },
    { false, "LIBOCTINTERP", octave::build_env::LIBOCTINTERP },
    { false, "LIBS", octave::build_env::LIBS },
    { false, "LLVM_CPPFLAGS", octave::build_env::LLVM_CPPFLAGS },
    { false, "LLVM_LDFLAGS", octave::build_env::LLVM_LDFLAGS },
    { false, "LLVM_LIBS", octave::build_env::LLVM_LIBS },
    { false, "LN_S", octave::build_env::LN_S },
    { false, "MAGICK_CPPFLAGS", octave::build_env::MAGICK_CPPFLAGS },
    { false, "MAGICK_LDFLAGS", octave::build_env::MAGICK_LDFLAGS },
    { false, "MAGICK_LIBS", octave::build_env::MAGICK_LIBS },
    { false, "MKOCTFILE_DL_LDFLAGS", octave::build_env::MKOCTFILE_DL_LDFLAGS },
    { false, "OCTAVE_LINK_DEPS", octave::build_env::OCTAVE_LINK_DEPS },
    { false, "OCTAVE_LINK_OPTS", octave::build_env::OCTAVE_LINK_OPTS },
    { false, "OCT_LINK_DEPS", octave::build_env::OCT_LINK_DEPS },
    { false, "OCT_LINK_OPTS", octave::build_env::OCT_LINK_OPTS },
    { false, "OPENGL_LIBS", octave::build_env::OPENGL_LIBS },
    { false, "OSMESA_CPPFLAGS", octave::build_env::OSMESA_CPPFLAGS },
    { false, "OSMESA_LDFLAGS", octave::build_env::OSMESA_LDFLAGS },
    { false, "OSMESA_LIBS", octave::build_env::OSMESA_LIBS },
    { false, "PCRE_CPPFLAGS", octave::build_env::PCRE_CPPFLAGS },
    { false, "PCRE_LIBS", octave::build_env::PCRE_LIBS },
    { false, "PTHREAD_CFLAGS", octave::build_env::PTHREAD_CFLAGS },
    { false, "PTHREAD_LIBS", octave::build_env::PTHREAD_LIBS },
    { false, "QHULL_CPPFLAGS", octave::build_env::QHULL_CPPFLAGS },
    { false, "QHULL_LDFLAGS", octave::build_env::QHULL_LDFLAGS },
    { false, "QHULL_LIBS", octave::build_env::QHULL_LIBS },
    { false, "QRUPDATE_CPPFLAGS", octave::build_env::QRUPDATE_CPPFLAGS },
    { false, "QRUPDATE_LDFLAGS", octave::build_env::QRUPDATE_LDFLAGS },
    { false, "QRUPDATE_LIBS", octave::build_env::QRUPDATE_LIBS },
    { false, "QT_CPPFLAGS", octave::build_env::QT_CPPFLAGS },
    { false, "QT_LDFLAGS", octave::build_env::QT_LDFLAGS },
    { false, "QT_LIBS", octave::build_env::QT_LIBS },
    { false, "RANLIB", octave::build_env::RANLIB },
    { false, "RDYNAMIC_FLAG", octave::build_env::RDYNAMIC_FLAG },
    { false, "READLINE_LIBS", octave::build_env::READLINE_LIBS },
    { false, "SED", octave::build_env::SED },
    { false, "SHARED_LIBS", octave::build_env::SHARED_LIBS },
    { false, "SHLEXT", octave::build_env::SHLEXT },
    { false, "SHLEXT_VER", octave::build_env::SHLEXT_VER },
    { false, "SH_LD", octave::build_env::SH_LD },
    { false, "SH_LDFLAGS", octave::build_env::SH_LDFLAGS },
    { false, "SONAME_FLAGS", octave::build_env::SONAME_FLAGS },
    { false, "STATIC_LIBS", octave::build_env::STATIC_LIBS },
    { false, "TERM_LIBS", octave::build_env::TERM_LIBS },
    { false, "UMFPACK_CPPFLAGS", octave::build_env::UMFPACK_CPPFLAGS },
    { false, "UMFPACK_LDFLAGS", octave::build_env::UMFPACK_LDFLAGS },
    { false, "UMFPACK_LIBS", octave::build_env::UMFPACK_LIBS },
    { false, "WARN_CFLAGS", octave::build_env::WARN_CFLAGS },
    { false, "WARN_CXXFLAGS", octave::build_env::WARN_CXXFLAGS },
    { false, "X11_INCFLAGS", octave::build_env::X11_INCFLAGS },
    { false, "X11_LIBS", octave::build_env::X11_LIBS },
    { false, "XTRA_CFLAGS", octave::build_env::XTRA_CFLAGS },
    { false, "XTRA_CXXFLAGS", octave::build_env::XTRA_CXXFLAGS },
    { false, "YACC", octave::build_env::YACC },
    { false, "YFLAGS", octave::build_env::YFLAGS },
    { false, "Z_CPPFLAGS", octave::build_env::Z_CPPFLAGS },
    { false, "Z_LDFLAGS", octave::build_env::Z_LDFLAGS },
    { false, "Z_LIBS", octave::build_env::Z_LIBS },
    { false, "api_version", OCTAVE_API_VERSION },
    { true, "archlibdir", OCTAVE_ARCHLIBDIR },
    { true, "bindir", OCTAVE_BINDIR },
    { false, "canonical_host_type", OCTAVE_CANONICAL_HOST_TYPE },
    { false, "config_opts", octave::build_env::config_opts },
    { true, "datadir", OCTAVE_DATADIR },
    { true, "datarootdir", OCTAVE_DATAROOTDIR },
    { true, "exec_prefix", OCTAVE_EXEC_PREFIX },
    { true, "fcnfiledir", OCTAVE_FCNFILEDIR },
    { true, "imagedir", OCTAVE_IMAGEDIR },
    { true, "includedir", OCTAVE_INCLUDEDIR },
    { true, "infodir", OCTAVE_INFODIR },
    { true, "infofile", OCTAVE_INFOFILE },
    { true, "libdir", OCTAVE_LIBDIR },
    { true, "libexecdir", OCTAVE_LIBEXECDIR },
    { true, "localapiarchlibdir", OCTAVE_LOCALAPIARCHLIBDIR },
    { true, "localapifcnfiledir", OCTAVE_LOCALAPIFCNFILEDIR },
    { true, "localapioctfiledir", OCTAVE_LOCALAPIOCTFILEDIR },
    { true, "localarchlibdir", OCTAVE_LOCALARCHLIBDIR },
    { true, "localfcnfiledir", OCTAVE_LOCALFCNFILEDIR },
    { true, "localoctfiledir", OCTAVE_LOCALOCTFILEDIR },
    { true, "localstartupfiledir", OCTAVE_LOCALSTARTUPFILEDIR },
    { true, "localverarchlibdir", OCTAVE_LOCALVERARCHLIBDIR },
    { true, "localverfcnfiledir", OCTAVE_LOCALVERFCNFILEDIR },
    { true, "localveroctfiledir", OCTAVE_LOCALVEROCTFILEDIR },
    { true, "man1dir", OCTAVE_MAN1DIR },
    { false, "man1ext", OCTAVE_MAN1EXT },
    { true, "mandir", OCTAVE_MANDIR },
    { true, "octdatadir", OCTAVE_OCTDATADIR },
    { true, "octfiledir", OCTAVE_OCTFILEDIR },
    { true, "octetcdir", OCTAVE_OCTETCDIR },
    { true, "octincludedir", OCTAVE_OCTINCLUDEDIR },
    { true, "octlibdir", OCTAVE_OCTLIBDIR },
    { true, "octtestsdir", OCTAVE_OCTTESTSDIR },
    { true, "prefix", OCTAVE_PREFIX },
    { true, "startupfiledir", OCTAVE_STARTUPFILEDIR },
    { false, "version", OCTAVE_VERSION },
    { false, 0, 0 }
  };

  if (! initialized)
    {
      m.assign ("dld", octave_value (octave_supports_dynamic_linking));

      oct_mach_info::float_format ff = oct_mach_info::native_float_format ();
      m.assign ("float_format",
                octave_value (oct_mach_info::float_format_as_string (ff)));

      m.assign ("words_big_endian",
                octave_value (oct_mach_info::words_big_endian ()));

      m.assign ("words_little_endian",
                octave_value (oct_mach_info::words_little_endian ()));

      m.assign ("features", octave_value (octave::build_env::features ()));

      int i = 0;

      while (true)
        {
          const conf_info_struct& elt = conf_info[i++];

          const char *key = elt.key;

          if (key)
            {
              if (elt.subst_home)
                m.assign (key, subst_octave_home (elt.val));
              else
                m.assign (key, elt.val);
            }
          else
            break;
        }

      bool unix_system = true;
      bool mac_system = false;
      bool windows_system = false;

#if defined (WIN32)
      windows_system = true;
#if ! defined (__CYGWIN__)
      unix_system = false;
#endif
#endif

#if defined (OCTAVE_USE_OS_X_API)
      mac_system = true;
#endif

      m.assign ("unix", octave_value (unix_system));
      m.assign ("mac", octave_value (mac_system));
      m.assign ("windows", octave_value (windows_system));

      initialized = true;
    }

  int nargin = args.length ();

  if (nargin > 1)
    print_usage ();

  octave_value_list retval;

  if (nargin == 1)
    {
      std::string arg = args(0).string_value ();

      if (! m.isfield (arg))
        error ("octave_config_info: invalid parameter '%s'", arg.c_str ());

      Cell c = m.contents (arg);

      if (c.is_empty ())
        error ("octave_config_info: no info for '%s'", arg.c_str ());

      retval = ovl (c(0));
    }
  else
    retval = ovl (m);

  return retval;
}

/*
%!assert (ischar (octave_config_info ("version")))
%!test
%! x = octave_config_info ();
%! assert (isstruct (x));
%! assert (! isempty (x));

%!error octave_config_info (1, 2)
*/

#if defined (__GNUG__) && defined (DEBUG_NEW_DELETE)

int debug_new_delete = 0;

typedef void (*vfp)(void);
extern vfp __new_handler;

void *
__builtin_new (size_t sz)
{
  void *p;

  // malloc (0) is unpredictable; avoid it.
  if (sz == 0)
    sz = 1;
  p = gnulib::malloc (sz);
  while (p == 0)
    {
      (*__new_handler) ();
      p = gnulib::malloc (sz);
    }

  if (debug_new_delete)
    std::cerr << "__builtin_new: " << p << std::endl;

  return p;
}

void
__builtin_delete (void *ptr)
{
  if (debug_new_delete)
    std::cerr << "__builtin_delete: " << ptr << std::endl;

  if (ptr)
    free (ptr);
}

#endif