diff libinterp/corefcn/interpreter.cc @ 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 397ba0a8d732
children f0e3f3e28a8e
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;