changeset 27587:494d6243c188

improve compatibility of quit function * interpreter.h, interpreter.cc (interpreter::execute_startup_files): Don't add __finish__ to the list of atexit functions. (interpreter::m_cancel_quit, interpreter::m_executing_finish_script): New variables. (interpreter::cancel_quit): New function to set m_cancel_quit var. (interpreter::executing_finish_script): New function to access m_executing_finish_script var. (interpreter::quit): New function. Use evalin to execute the "finish" function and catch any execution exceptions that it may generate. If executing finish script, save and restore m_cancel_quit and m_executing_finish_script and set m_executing_finish_script to true. (Fquit): Accept "force" and "cancel" options. Decode arguments and call interpreter::quit. If "cancel" option is used and we are executing the finish script, call interpreter::cancel_quit to set state variable and return. Update docstring. * __finish__.m: Delete. * scripts/startup/module.mk: Delete.
author John W. Eaton <jwe@octave.org>
date Tue, 29 Oct 2019 11:36:14 -0400
parents b0626d075ad7
children f0e3f3e28a8e
files libinterp/corefcn/interpreter.cc libinterp/corefcn/interpreter.h scripts/startup/__finish__.m scripts/startup/module.mk
diffstat 4 files changed, 125 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/interpreter.cc	Tue Oct 29 16:37:02 2019 -0700
+++ b/libinterp/corefcn/interpreter.cc	Tue Oct 29 11:36:14 2019 -0400
@@ -133,11 +133,16 @@
 
 DEFMETHOD (quit, interp, args, ,
            doc: /* -*- texinfo -*-
-@deftypefn  {} {} exit
-@deftypefnx {} {} exit (@var{status})
-@deftypefnx {} {} quit
+@deftypefn  {} {} quit
+@deftypefnx {} {} quit cancel
+@deftypefnx {} {} quit force
+@deftypefnx {} {} quit ("cancel")
+@deftypefnx {} {} quit ("force")
 @deftypefnx {} {} quit (@var{status})
-Exit the current Octave session.
+@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.
@@ -145,33 +150,76 @@
 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}.
+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 */)
 {
-  // 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.
-
-  octave::event_manager& evmgr = interp.get_event_manager ();
+  int numel = args.length ();
 
-  if (! evmgr.confirm_shutdown ())
-    return ovl ();
-
-  if (! quit_allowed)
-    error ("quit: not supported in embedded mode");
+  if (numel > 2)
+    print_usage ();
 
   int exit_status = 0;
 
-  if (args.length () > 0)
-    exit_status = args(0).nint_value ();
+  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);
 
-  // Instead of simply calling exit, we thrown an exception 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.
+          if (opt == "cancel")
+            cancel = true;
+          else if (opt == "force")
+            force = true;
+          else
+            error (msg);
+        }
+      else
+        exit_status = args(0).xnint_value ("quit: STATUS must be an integer");
+    }
 
-  throw octave::exit_exception (exit_status);
+  if (cancel)
+    {
+      if (interp.executing_finish_script ())
+        {
+          interp.cancel_quit (true);
+          return ovl ();
+        }
+      else
+        error (R"(invalid use of "cancel" option)");
+    }
+
+  interp.quit (exit_status, force);
 
   return ovl ();
 }
@@ -415,6 +463,8 @@
       m_load_path_initialized (false),
       m_history_initialized (false),
       m_in_top_level_repl (false),
+      m_cancel_quit (false),
+      m_executing_finish_script (false),
       m_initialized (false)
   {
     // FIXME: When thread_local storage is used by default, this message
@@ -840,10 +890,6 @@
               }
           }
 
-        // Schedule the Matlab compatible finish.m file to run if it exists
-        // anywhere in the load path when exiting Octave.
-        add_atexit_function ("__finish__");
-
         // Try to execute commands from $HOME/$OCTAVE_INITFILE and
         // $OCTAVE_INITFILE.  If $OCTAVE_INITFILE is not set,
         // .octaverc is assumed.
@@ -1694,6 +1740,46 @@
     m_tmp_files.cleanup ();
   }
 
+  void interpreter::quit (int exit_status, bool force)
+  {
+    if (! force)
+      {
+        try
+          {
+            // Attempt to execute finish.m.  If it throws an
+            // exception, cancel quitting.
+
+            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 (! m_event_manager.confirm_shutdown ())
+              return;
+          }
+        catch (const execution_exception& ee)
+          {
+            handle_exception (ee);
+
+            return;
+          }
+      }
+
+    throw exit_exception (exit_status);
+  }
+
   // Functions to call when the interpreter exits.
 
   std::list<std::string> interpreter::atexit_functions;
--- a/libinterp/corefcn/interpreter.h	Tue Oct 29 16:37:02 2019 -0700
+++ b/libinterp/corefcn/interpreter.h	Tue Oct 29 11:36:14 2019 -0400
@@ -437,6 +437,15 @@
 
     void cleanup_tmp_files (void);
 
+    void quit (int exit_status, bool force = false);
+
+    void cancel_quit (bool flag) { m_cancel_quit = flag; }
+
+    bool executing_finish_script (void) const
+    {
+      return m_executing_finish_script;
+    }
+
     static void add_atexit_function (const std::string& fname);
 
     static bool remove_atexit_function (const std::string& fname);
@@ -533,6 +542,10 @@
     // TRUE if we are in the top level interactive read eval print loop.
     bool m_in_top_level_repl;
 
+    bool m_cancel_quit;
+
+    bool m_executing_finish_script;
+
     bool m_initialized;
 
     void maximum_braindamage (void);
--- a/scripts/startup/__finish__.m	Tue Oct 29 16:37:02 2019 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-## Copyright (C) 2008-2019 Ben Abbott
-##
-## 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/>.
-
-## -*- texinfo -*-
-## @deftypefn {} {} __finish__
-## Check for the existence of the function/script, @file{finish}, in the
-## path or current working directory and execute it.
-##
-## This function is intended to be executed upon a clean exit from Octave.
-## This is accomplished in the system script @file{startup/octaverc} by use of
-## the built-in function @code{atexit}.
-## @seealso{atexit}
-## @end deftypefn
-
-function __finish__ ()
-
-  if (exist ("finish.m", "file"))
-    ## Must use evalin for access to base workspace and user variables.
-    ## No argument list for finish because it might be a script, not function.
-    evalin ("base", "finish;");
-  endif
-
-endfunction
-
-
-## No test needed for internal helper m-file.
-%!assert (1)
--- a/scripts/startup/module.mk	Tue Oct 29 16:37:02 2019 -0700
+++ b/scripts/startup/module.mk	Tue Oct 29 11:36:14 2019 -0400
@@ -1,8 +1,5 @@
 FCN_FILE_DIRS += scripts/startup
 
-%canon_reldir%_FCN_FILES = \
-  %reldir%/__finish__.m
-
 SITE_STARTUP_FILE_SRC  = %reldir%/site-rcfile
 
 VERSION_STARTUP_FILE_SRC = %reldir%/version-rcfile
@@ -16,10 +13,6 @@
 
 %canon_reldir%dir = $(fcnfiledir)/startup
 
-%canon_reldir%_DATA = $(%canon_reldir%_FCN_FILES)
-
-FCN_FILES += $(%canon_reldir%_FCN_FILES)
-
 PKG_ADD_FILES += %reldir%/PKG_ADD
 
 DIRSTAMP_FILES += %reldir%/$(octave_dirstamp)