GNU bug report logs - #48603
27.2; [PATCH] Quit minibuffers without aborting kmacros

Previous Next

Package: emacs;

Reported by: miha <at> kamnitnik.top

Date: Sun, 23 May 2021 14:32:02 UTC

Severity: normal

Tags: patch

Found in version 27.2

Fixed in version 28.1

Done: Lars Ingebrigtsen <larsi <at> gnus.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 48603 in the body.
You can then email your comments to 48603 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Sun, 23 May 2021 14:32:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to miha <at> kamnitnik.top:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sun, 23 May 2021 14:32:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: miha <at> kamnitnik.top
To: bug-gnu-emacs <at> gnu.org
Subject: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Sun, 23 May 2021 16:36:17 +0200
[Message part 1 (text/plain, inline)]
Rationale: a user wants to copy a command from command history without
executing it. Or user wants to complete a file name in C-x C-f and copy
it without finding the file. I believe this should be possible in
keyboard macros.

[0001-Quit-minibuffers-without-aborting-kmacros.patch (text/x-patch, inline)]
From 5513841b55e47f05f3e049ffcb71a0fc56287fbc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miha=20Rihtar=C5=A1i=C4=8D?= <miha <at> kamnitnik.top>
Date: Sun, 23 May 2021 16:08:43 +0200
Subject: [PATCH] Quit minibuffers without aborting kmacros

* src/data.c (syms_of_data): New error symbol `lite-quit'

* src/keyboard.c (recursive_edit_1): Implement throwing of function
values to `exit`.  In that case, the function will be called without
arguments before returning from the command loop.
(cmd_error):
(Fcommand_error_default_function): Do not abort keyboard macro
execution if lite-quit is signaled.
(command_loop_2): New argument HANDLERS.

* src/macros.c (Fexecute_kbd_macro): Use command_loop_2 instead of
command_loop_1.

* doc/lispref/commands.texi (Quitting): Document `lite-quit'
(Recursive Editing): Document throwing of function values to `exit'.

* doc/lispref/errors.texi (Standard Errors): Document `lite-quit'

* lisp/minibuffer.el (lite-quit-recursive-edit): New function.

* src/minibuf.c (Fabort_minibuffers): Use it.

* lisp/simple.el (minibuffer-error-function): Do not abort keyboard
macro execution if is lite-quit is signaled.
---
 doc/lispref/commands.texi | 14 +++++++++---
 doc/lispref/errors.texi   |  9 ++++++--
 lisp/minibuffer.el        |  9 ++++++++
 lisp/simple.el            |  6 ++++--
 src/data.c                |  2 ++
 src/keyboard.c            | 45 ++++++++++++++++++++++++++++-----------
 src/lisp.h                |  2 +-
 src/macros.c              |  2 +-
 src/minibuf.c             |  2 +-
 9 files changed, 69 insertions(+), 22 deletions(-)

diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 8199ece110..a0b99ed144 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -3378,6 +3378,12 @@ Quitting
 in @ref{Errors}.)
 @end deffn
 
+  To quit without aborting keyboard macro definition or execution, you
+can signal the @code{lite-quit} condition.  This has almost the same
+effect as the @code{quit} condition except that the error handling in
+the command loop handles it without breaking keyboard macro definition
+or execution.
+
   You can specify a character other than @kbd{C-g} to use for quitting.
 See the function @code{set-input-mode} in @ref{Input Modes}.
 
@@ -3562,12 +3568,14 @@ Recursive Editing
 @code{recursive-edit}.  This function contains the command loop; it also
 contains a call to @code{catch} with tag @code{exit}, which makes it
 possible to exit the recursive editing level by throwing to @code{exit}
-(@pxref{Catch and Throw}).  If you throw a value other than @code{t},
-then @code{recursive-edit} returns normally to the function that called
-it.  The command @kbd{C-M-c} (@code{exit-recursive-edit}) does this.
+(@pxref{Catch and Throw}).  If you throw a @code{nil} value, then
+@code{recursive-edit} returns normally to the function that called it.
+The command @kbd{C-M-c} (@code{exit-recursive-edit}) does this.
 Throwing a @code{t} value causes @code{recursive-edit} to quit, so that
 control returns to the command loop one level up.  This is called
 @dfn{aborting}, and is done by @kbd{C-]} (@code{abort-recursive-edit}).
+You can also throw a function value.  In that case,
+@code{recursive-edit} will call it without arguments before returning.
 
   Most applications should not use recursive editing, except as part of
 using the minibuffer.  Usually it is more convenient for the user if you
diff --git a/doc/lispref/errors.texi b/doc/lispref/errors.texi
index fb393b951f..a45489db8e 100644
--- a/doc/lispref/errors.texi
+++ b/doc/lispref/errors.texi
@@ -20,8 +20,9 @@ Standard Errors
 we do not say here that a certain error symbol has additional error
 conditions, that means it has none.
 
-  As a special exception, the error symbol @code{quit} does not have the
-condition @code{error}, because quitting is not considered an error.
+  As a special exception, the error symbols @code{quit} and
+@code{lite-quit} don't have the condition @code{error}, because quitting
+is not considered an error.
 
   Most of these error symbols are defined in C (mainly @file{data.c}),
 but some are defined in Lisp.  For example, the file @file{userlock.el}
@@ -40,6 +41,10 @@ Standard Errors
 @item quit
 The message is @samp{Quit}.  @xref{Quitting}.
 
+@item lite-quit
+The message is @samp{Quit}.  This is a subcategory of @code{quit}.
+@xref{Quitting}.
+
 @item args-out-of-range
 The message is @samp{Args out of range}.  This happens when trying to
 access an element beyond the range of a sequence, buffer, or other
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index e04f1040b3..d1b88b0355 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -2318,6 +2318,15 @@ exit-minibuffer
   (setq deactivate-mark nil)
   (throw 'exit nil))
 
+(defun lite-quit-recursive-edit ()
+  "Quit the command that requested this recursive edit wihtout error.
+Like `abort-recursive-edit' wihtout aborting keyboard macro
+execution."
+  ;; See Info node `(elisp)Recursive Editing' for an explanation of
+  ;; throwing a function to `exit'.
+  (throw 'exit (lambda ()
+                 (signal 'lite-quit nil))))
+
 (defun self-insert-and-exit ()
   "Terminate minibuffer input."
   (interactive)
diff --git a/lisp/simple.el b/lisp/simple.el
index 2a90a07631..191c9d482d 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2854,8 +2854,10 @@ minibuffer-error-function
 The same as `command-error-default-function' but display error messages
 at the end of the minibuffer using `minibuffer-message' to not obscure
 the minibuffer contents."
-  (discard-input)
-  (ding)
+  (if (memq 'lite-quit (get (car data) 'error-conditions))
+      (ding t)
+    (discard-input)
+    (ding))
   (let ((string (error-message-string data)))
     ;; If we know from where the error was signaled, show it in
     ;; *Messages*.
diff --git a/src/data.c b/src/data.c
index d547f5da5e..315d43942c 100644
--- a/src/data.c
+++ b/src/data.c
@@ -3899,6 +3899,7 @@ syms_of_data (void)
   DEFSYM (Qerror, "error");
   DEFSYM (Quser_error, "user-error");
   DEFSYM (Qquit, "quit");
+  DEFSYM (Qlite_quit, "lite-quit");
   DEFSYM (Qwrong_length_argument, "wrong-length-argument");
   DEFSYM (Qwrong_type_argument, "wrong-type-argument");
   DEFSYM (Qargs_out_of_range, "args-out-of-range");
@@ -3971,6 +3972,7 @@ #define PUT_ERROR(sym, tail, msg)			\
   Fput (sym, Qerror_message, build_pure_c_string (msg))
 
   PUT_ERROR (Qquit, Qnil, "Quit");
+  PUT_ERROR (Qlite_quit, pure_cons (Qquit, Qnil), "Quit");
 
   PUT_ERROR (Quser_error, error_tail, "");
   PUT_ERROR (Qwrong_length_argument, error_tail, "Wrong length argument");
diff --git a/src/keyboard.c b/src/keyboard.c
index 47b5e59024..10e1386023 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -725,6 +725,9 @@ recursive_edit_1 (void)
   if (STRINGP (val))
     xsignal1 (Qerror, val);
 
+  if (FUNCTIONP (val))
+    call0 (val);
+
   return unbind_to (count, Qnil);
 }
 
@@ -921,6 +924,7 @@ restore_kboard_configuration (int was_locked)
 cmd_error (Lisp_Object data)
 {
   Lisp_Object old_level, old_length;
+  Lisp_Object conditions;
   char macroerror[sizeof "After..kbd macro iterations: "
 		  + INT_STRLEN_BOUND (EMACS_INT)];
 
@@ -940,10 +944,15 @@ cmd_error (Lisp_Object data)
   else
     *macroerror = 0;
 
+  conditions = Fget (XCAR (data), Qerror_conditions);
+  if (NILP (Fmemq (Qlite_quit, conditions)))
+    {
+      Vexecuting_kbd_macro = Qnil;
+      executing_kbd_macro = Qnil;
+    }
+
   Vstandard_output = Qt;
   Vstandard_input = Qt;
-  Vexecuting_kbd_macro = Qnil;
-  executing_kbd_macro = Qnil;
   kset_prefix_arg (current_kboard, Qnil);
   kset_last_prefix_arg (current_kboard, Qnil);
   cancel_echoing ();
@@ -998,6 +1007,7 @@ DEFUN ("command-error-default-function", Fcommand_error_default_function,
   (Lisp_Object data, Lisp_Object context, Lisp_Object signal)
 {
   struct frame *sf = SELECTED_FRAME ();
+  Lisp_Object conditions;
 
   CHECK_STRING (context);
 
@@ -1024,17 +1034,27 @@ DEFUN ("command-error-default-function", Fcommand_error_default_function,
     }
   else
     {
+      conditions = Fget (XCAR (data), Qerror_conditions);
+
       clear_message (1, 0);
-      Fdiscard_input ();
       message_log_maybe_newline ();
-      bitch_at_user ();
+
+      if (!NILP (Fmemq (Qlite_quit, conditions)))
+	{
+	  Fding (Qt);
+	}
+      else
+	{
+	  Fdiscard_input ();
+	  bitch_at_user ();
+	}
 
       print_error_message (data, Qt, SSDATA (context), signal);
     }
   return Qnil;
 }
 
-static Lisp_Object command_loop_2 (Lisp_Object);
+static Lisp_Object command_loop_1 (void);
 static Lisp_Object top_level_1 (Lisp_Object);
 
 /* Entry to editor-command-loop.
@@ -1062,7 +1082,7 @@ command_loop (void)
   if (command_loop_level > 0 || minibuf_level > 0)
     {
       Lisp_Object val;
-      val = internal_catch (Qexit, command_loop_2, Qnil);
+      val = internal_catch (Qexit, command_loop_2, Qerror);
       executing_kbd_macro = Qnil;
       return val;
     }
@@ -1070,7 +1090,7 @@ command_loop (void)
     while (1)
       {
 	internal_catch (Qtop_level, top_level_1, Qnil);
-	internal_catch (Qtop_level, command_loop_2, Qnil);
+	internal_catch (Qtop_level, command_loop_2, Qerror);
 	executing_kbd_macro = Qnil;
 
 	/* End of file in -batch run causes exit here.  */
@@ -1083,15 +1103,16 @@ command_loop (void)
    editing loop, and reenter the editing loop.
    When there is an error, cmd_error runs and returns a non-nil
    value to us.  A value of nil means that command_loop_1 itself
-   returned due to end of file (or end of kbd macro).  */
+   returned due to end of file (or end of kbd macro).  HANDLERS is a
+   list of condition names, passed to internal_condition_case.  */
 
-static Lisp_Object
-command_loop_2 (Lisp_Object ignore)
+Lisp_Object
+command_loop_2 (Lisp_Object handlers)
 {
   register Lisp_Object val;
 
   do
-    val = internal_condition_case (command_loop_1, Qerror, cmd_error);
+    val = internal_condition_case (command_loop_1, handlers, cmd_error);
   while (!NILP (val));
 
   return Qnil;
@@ -1234,7 +1255,7 @@ some_mouse_moved (void)
                               bool, bool, bool, bool);
 static void adjust_point_for_property (ptrdiff_t, bool);
 
-Lisp_Object
+static Lisp_Object
 command_loop_1 (void)
 {
   modiff_count prev_modiff = 0;
diff --git a/src/lisp.h b/src/lisp.h
index 91b7a89d0f..a30d7b861d 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4417,7 +4417,7 @@ fast_string_match_ignore_case (Lisp_Object regexp, Lisp_Object string)
 extern bool detect_input_pending_run_timers (bool);
 extern void safe_run_hooks (Lisp_Object);
 extern void cmd_error_internal (Lisp_Object, const char *);
-extern Lisp_Object command_loop_1 (void);
+extern Lisp_Object command_loop_2 (Lisp_Object);
 extern Lisp_Object read_menu_command (void);
 extern Lisp_Object recursive_edit_1 (void);
 extern void record_auto_save (void);
diff --git a/src/macros.c b/src/macros.c
index 60d0766a75..53ad727a5b 100644
--- a/src/macros.c
+++ b/src/macros.c
@@ -324,7 +324,7 @@ DEFUN ("execute-kbd-macro", Fexecute_kbd_macro, Sexecute_kbd_macro, 1, 3, 0,
 	    break;
 	}
 
-      command_loop_1 ();
+      command_loop_2 (list1 (Qlite_quit));
 
       executing_kbd_macro_iterations = ++success_count;
 
diff --git a/src/minibuf.c b/src/minibuf.c
index cffb7fe787..9237cbe2b6 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -496,7 +496,7 @@ DEFUN ("abort-minibuffers", Fabort_minibuffers, Sabort_minibuffers, 0, 0, "",
 	}
     }
   else
-    Fthrow (Qexit, Qt);
+    CALLN (Ffuncall, intern ("lite-quit-recursive-edit"));
   return Qnil;
 }
 
-- 
2.31.1


Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Tue, 25 May 2021 19:56:01 GMT) Full text and rfc822 format available.

Message #8 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: miha <at> kamnitnik.top
Cc: 48603 <at> debbugs.gnu.org
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Tue, 25 May 2021 21:54:59 +0200
miha <at> kamnitnik.top writes:

> Rationale: a user wants to copy a command from command history without
> executing it. Or user wants to complete a file name in C-x C-f and copy
> it without finding the file. I believe this should be possible in
> keyboard macros.

`C-g' is part of how one normally operates `M-x' (etc.) in some
circumstances, so it might make sense to have a special quit symbol for
that; yes.

Does anybody else have an opinion here?

> * src/data.c (syms_of_data): New error symbol `lite-quit'

But it should be called something more descriptive, like
`minibuffer-quit'.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Tue, 25 May 2021 21:29:01 GMT) Full text and rfc822 format available.

Message #11 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: <miha <at> kamnitnik.top>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: 48603 <at> debbugs.gnu.org
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Tue, 25 May 2021 23:34:11 +0200
[Message part 1 (text/plain, inline)]
Lars Ingebrigtsen <larsi <at> gnus.org> writes:

> miha <at> kamnitnik.top writes:
>
>> Rationale: a user wants to copy a command from command history without
>> executing it. Or user wants to complete a file name in C-x C-f and copy
>> it without finding the file. I believe this should be possible in
>> keyboard macros.
>
> `C-g' is part of how one normally operates `M-x' (etc.) in some
> circumstances, so it might make sense to have a special quit symbol for
> that; yes.
>
> Does anybody else have an opinion here?

One alternative to a new quit error symbol would be to introduce a new
variable `kmacro-dont-end-on-error'. One would then set it to non-nil
before signaling a quit (or any error) and command loop would set it
back to nil after handling the error without aborting kmacro definition.
What do you think?

>> * src/data.c (syms_of_data): New error symbol `lite-quit'
>
> But it should be called something more descriptive, like
> `minibuffer-quit'.

Good idea, attaching a revised patch (which also adds two NEWS entries.)

[0001-Quit-minibuffers-without-aborting-kmacros.patch (text/x-patch, inline)]
From 289f4d6cae45c6846d9ef4c245b1635056a2f288 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miha=20Rihtar=C5=A1i=C4=8D?= <miha <at> kamnitnik.top>
Date: Sun, 23 May 2021 16:08:43 +0200
Subject: [PATCH] Quit minibuffers without aborting kmacros

* src/data.c (syms_of_data): New error symbol `minibuffer-quit'

* src/keyboard.c (recursive_edit_1): Implement throwing of function
values to `exit`.  In that case, the function will be called without
arguments before returning from the command loop.
(cmd_error):
(Fcommand_error_default_function): Do not abort keyboard macro
execution if minibuffer-quit is signaled.
(command_loop_2): New argument HANDLERS.

* src/macros.c (Fexecute_kbd_macro): Use command_loop_2 instead of
command_loop_1.

* doc/lispref/commands.texi (Quitting): Document `minibuffer-quit'
(Recursive Editing): Document throwing of function values to `exit'.

* doc/lispref/errors.texi (Standard Errors): Document
`minibuffer-quit'

* lisp/minibuffer.el (minibuffer-quit-recursive-edit): New function.

* src/minibuf.c (Fabort_minibuffers): Use it.

* lisp/simple.el (minibuffer-error-function): Do not abort keyboard
macro execution if is minibuffer-quit is signaled.
---
 doc/lispref/commands.texi | 14 +++++++++---
 doc/lispref/errors.texi   |  9 ++++++--
 etc/NEWS                  |  9 ++++++++
 lisp/minibuffer.el        |  9 ++++++++
 lisp/simple.el            |  6 ++++--
 src/data.c                |  2 ++
 src/keyboard.c            | 45 ++++++++++++++++++++++++++++-----------
 src/lisp.h                |  2 +-
 src/macros.c              |  2 +-
 src/minibuf.c             |  2 +-
 10 files changed, 78 insertions(+), 22 deletions(-)

diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 8199ece110..164e1ad630 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -3378,6 +3378,12 @@ Quitting
 in @ref{Errors}.)
 @end deffn
 
+  To quit without aborting keyboard macro definition or execution, you
+can signal the @code{minibuffer-quit} condition.  This has almost the
+same effect as the @code{quit} condition except that the error handling
+in the command loop handles it without breaking keyboard macro
+definition or execution.
+
   You can specify a character other than @kbd{C-g} to use for quitting.
 See the function @code{set-input-mode} in @ref{Input Modes}.
 
@@ -3562,12 +3568,14 @@ Recursive Editing
 @code{recursive-edit}.  This function contains the command loop; it also
 contains a call to @code{catch} with tag @code{exit}, which makes it
 possible to exit the recursive editing level by throwing to @code{exit}
-(@pxref{Catch and Throw}).  If you throw a value other than @code{t},
-then @code{recursive-edit} returns normally to the function that called
-it.  The command @kbd{C-M-c} (@code{exit-recursive-edit}) does this.
+(@pxref{Catch and Throw}).  If you throw a @code{nil} value, then
+@code{recursive-edit} returns normally to the function that called it.
+The command @kbd{C-M-c} (@code{exit-recursive-edit}) does this.
 Throwing a @code{t} value causes @code{recursive-edit} to quit, so that
 control returns to the command loop one level up.  This is called
 @dfn{aborting}, and is done by @kbd{C-]} (@code{abort-recursive-edit}).
+You can also throw a function value.  In that case,
+@code{recursive-edit} will call it without arguments before returning.
 
   Most applications should not use recursive editing, except as part of
 using the minibuffer.  Usually it is more convenient for the user if you
diff --git a/doc/lispref/errors.texi b/doc/lispref/errors.texi
index fb393b951f..f848218e26 100644
--- a/doc/lispref/errors.texi
+++ b/doc/lispref/errors.texi
@@ -20,8 +20,9 @@ Standard Errors
 we do not say here that a certain error symbol has additional error
 conditions, that means it has none.
 
-  As a special exception, the error symbol @code{quit} does not have the
-condition @code{error}, because quitting is not considered an error.
+  As a special exception, the error symbols @code{quit} and
+@code{minibuffer-quit} don't have the condition @code{error}, because
+quitting is not considered an error.
 
   Most of these error symbols are defined in C (mainly @file{data.c}),
 but some are defined in Lisp.  For example, the file @file{userlock.el}
@@ -40,6 +41,10 @@ Standard Errors
 @item quit
 The message is @samp{Quit}.  @xref{Quitting}.
 
+@item minibuffer-quit
+The message is @samp{Quit}.  This is a subcategory of @code{quit}.
+@xref{Quitting}.
+
 @item args-out-of-range
 The message is @samp{Args out of range}.  This happens when trying to
 access an element beyond the range of a sequence, buffer, or other
diff --git a/etc/NEWS b/etc/NEWS
index ea74dfe217..1bfd87dcb7 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2705,6 +2705,15 @@ the Emacs Lisp reference manual for background.
 
 * Lisp Changes in Emacs 28.1
 
++++
+** A function can now be thrown to the 'exit' label in addition to t or nil.
+The command loop will call it with zero arguments before returning.
+
++++
+** New error symbol 'minibuffer-quit'.
+Signaling it has almost the same effect as 'quit' except that it
+doesn't cause keyboard macro termination.
+
 ---
 ** Emacs now attempts to test for high-rate subprocess output more fairly.
 When several subprocesses produce output simultaneously at high rate,
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index ec21b7b93b..400dff058c 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -2318,6 +2318,15 @@ exit-minibuffer
   (setq deactivate-mark nil)
   (throw 'exit nil))
 
+(defun minibuffer-quit-recursive-edit ()
+  "Quit the command that requested this recursive edit wihtout error.
+Like `abort-recursive-edit' wihtout aborting keyboard macro
+execution."
+  ;; See Info node `(elisp)Recursive Editing' for an explanation of
+  ;; throwing a function to `exit'.
+  (throw 'exit (lambda ()
+                 (signal 'minibuffer-quit nil))))
+
 (defun self-insert-and-exit ()
   "Terminate minibuffer input."
   (interactive)
diff --git a/lisp/simple.el b/lisp/simple.el
index 8849919360..4546bbde2a 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2855,8 +2855,10 @@ minibuffer-error-function
 The same as `command-error-default-function' but display error messages
 at the end of the minibuffer using `minibuffer-message' to not obscure
 the minibuffer contents."
-  (discard-input)
-  (ding)
+  (if (memq 'minibuffer-quit (get (car data) 'error-conditions))
+      (ding t)
+    (discard-input)
+    (ding))
   (let ((string (error-message-string data)))
     ;; If we know from where the error was signaled, show it in
     ;; *Messages*.
diff --git a/src/data.c b/src/data.c
index d547f5da5e..903d171d8b 100644
--- a/src/data.c
+++ b/src/data.c
@@ -3899,6 +3899,7 @@ syms_of_data (void)
   DEFSYM (Qerror, "error");
   DEFSYM (Quser_error, "user-error");
   DEFSYM (Qquit, "quit");
+  DEFSYM (Qminibuffer_quit, "minibuffer-quit");
   DEFSYM (Qwrong_length_argument, "wrong-length-argument");
   DEFSYM (Qwrong_type_argument, "wrong-type-argument");
   DEFSYM (Qargs_out_of_range, "args-out-of-range");
@@ -3971,6 +3972,7 @@ #define PUT_ERROR(sym, tail, msg)			\
   Fput (sym, Qerror_message, build_pure_c_string (msg))
 
   PUT_ERROR (Qquit, Qnil, "Quit");
+  PUT_ERROR (Qminibuffer_quit, pure_cons (Qquit, Qnil), "Quit");
 
   PUT_ERROR (Quser_error, error_tail, "");
   PUT_ERROR (Qwrong_length_argument, error_tail, "Wrong length argument");
diff --git a/src/keyboard.c b/src/keyboard.c
index 47b5e59024..0ac6d24b0a 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -725,6 +725,9 @@ recursive_edit_1 (void)
   if (STRINGP (val))
     xsignal1 (Qerror, val);
 
+  if (FUNCTIONP (val))
+    call0 (val);
+
   return unbind_to (count, Qnil);
 }
 
@@ -921,6 +924,7 @@ restore_kboard_configuration (int was_locked)
 cmd_error (Lisp_Object data)
 {
   Lisp_Object old_level, old_length;
+  Lisp_Object conditions;
   char macroerror[sizeof "After..kbd macro iterations: "
 		  + INT_STRLEN_BOUND (EMACS_INT)];
 
@@ -940,10 +944,15 @@ cmd_error (Lisp_Object data)
   else
     *macroerror = 0;
 
+  conditions = Fget (XCAR (data), Qerror_conditions);
+  if (NILP (Fmemq (Qminibuffer_quit, conditions)))
+    {
+      Vexecuting_kbd_macro = Qnil;
+      executing_kbd_macro = Qnil;
+    }
+
   Vstandard_output = Qt;
   Vstandard_input = Qt;
-  Vexecuting_kbd_macro = Qnil;
-  executing_kbd_macro = Qnil;
   kset_prefix_arg (current_kboard, Qnil);
   kset_last_prefix_arg (current_kboard, Qnil);
   cancel_echoing ();
@@ -998,6 +1007,7 @@ DEFUN ("command-error-default-function", Fcommand_error_default_function,
   (Lisp_Object data, Lisp_Object context, Lisp_Object signal)
 {
   struct frame *sf = SELECTED_FRAME ();
+  Lisp_Object conditions;
 
   CHECK_STRING (context);
 
@@ -1024,17 +1034,27 @@ DEFUN ("command-error-default-function", Fcommand_error_default_function,
     }
   else
     {
+      conditions = Fget (XCAR (data), Qerror_conditions);
+
       clear_message (1, 0);
-      Fdiscard_input ();
       message_log_maybe_newline ();
-      bitch_at_user ();
+
+      if (!NILP (Fmemq (Qminibuffer_quit, conditions)))
+	{
+	  Fding (Qt);
+	}
+      else
+	{
+	  Fdiscard_input ();
+	  bitch_at_user ();
+	}
 
       print_error_message (data, Qt, SSDATA (context), signal);
     }
   return Qnil;
 }
 
-static Lisp_Object command_loop_2 (Lisp_Object);
+static Lisp_Object command_loop_1 (void);
 static Lisp_Object top_level_1 (Lisp_Object);
 
 /* Entry to editor-command-loop.
@@ -1062,7 +1082,7 @@ command_loop (void)
   if (command_loop_level > 0 || minibuf_level > 0)
     {
       Lisp_Object val;
-      val = internal_catch (Qexit, command_loop_2, Qnil);
+      val = internal_catch (Qexit, command_loop_2, Qerror);
       executing_kbd_macro = Qnil;
       return val;
     }
@@ -1070,7 +1090,7 @@ command_loop (void)
     while (1)
       {
 	internal_catch (Qtop_level, top_level_1, Qnil);
-	internal_catch (Qtop_level, command_loop_2, Qnil);
+	internal_catch (Qtop_level, command_loop_2, Qerror);
 	executing_kbd_macro = Qnil;
 
 	/* End of file in -batch run causes exit here.  */
@@ -1083,15 +1103,16 @@ command_loop (void)
    editing loop, and reenter the editing loop.
    When there is an error, cmd_error runs and returns a non-nil
    value to us.  A value of nil means that command_loop_1 itself
-   returned due to end of file (or end of kbd macro).  */
+   returned due to end of file (or end of kbd macro).  HANDLERS is a
+   list of condition names, passed to internal_condition_case.  */
 
-static Lisp_Object
-command_loop_2 (Lisp_Object ignore)
+Lisp_Object
+command_loop_2 (Lisp_Object handlers)
 {
   register Lisp_Object val;
 
   do
-    val = internal_condition_case (command_loop_1, Qerror, cmd_error);
+    val = internal_condition_case (command_loop_1, handlers, cmd_error);
   while (!NILP (val));
 
   return Qnil;
@@ -1234,7 +1255,7 @@ some_mouse_moved (void)
                               bool, bool, bool, bool);
 static void adjust_point_for_property (ptrdiff_t, bool);
 
-Lisp_Object
+static Lisp_Object
 command_loop_1 (void)
 {
   modiff_count prev_modiff = 0;
diff --git a/src/lisp.h b/src/lisp.h
index 91b7a89d0f..a30d7b861d 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4417,7 +4417,7 @@ fast_string_match_ignore_case (Lisp_Object regexp, Lisp_Object string)
 extern bool detect_input_pending_run_timers (bool);
 extern void safe_run_hooks (Lisp_Object);
 extern void cmd_error_internal (Lisp_Object, const char *);
-extern Lisp_Object command_loop_1 (void);
+extern Lisp_Object command_loop_2 (Lisp_Object);
 extern Lisp_Object read_menu_command (void);
 extern Lisp_Object recursive_edit_1 (void);
 extern void record_auto_save (void);
diff --git a/src/macros.c b/src/macros.c
index 60d0766a75..0752a5bb6f 100644
--- a/src/macros.c
+++ b/src/macros.c
@@ -324,7 +324,7 @@ DEFUN ("execute-kbd-macro", Fexecute_kbd_macro, Sexecute_kbd_macro, 1, 3, 0,
 	    break;
 	}
 
-      command_loop_1 ();
+      command_loop_2 (list1 (Qminibuffer_quit));
 
       executing_kbd_macro_iterations = ++success_count;
 
diff --git a/src/minibuf.c b/src/minibuf.c
index cffb7fe787..9f4970320e 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -496,7 +496,7 @@ DEFUN ("abort-minibuffers", Fabort_minibuffers, Sabort_minibuffers, 0, 0, "",
 	}
     }
   else
-    Fthrow (Qexit, Qt);
+    CALLN (Ffuncall, intern ("minibuffer-quit-recursive-edit"));
   return Qnil;
 }
 
-- 
2.31.1

[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Tue, 20 Jul 2021 12:38:02 GMT) Full text and rfc822 format available.

Message #14 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: <miha <at> kamnitnik.top>
Cc: 48603 <at> debbugs.gnu.org
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Tue, 20 Jul 2021 14:37:42 +0200
<miha <at> kamnitnik.top> writes:

> Good idea, attaching a revised patch (which also adds two NEWS entries.)

Sorry; I forgot all about this.  I've now re-read and tested the patch,
and it seems to work fine for me, so I've pushed it to Emacs 28.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




bug marked as fixed in version 28.1, send any further explanations to 48603 <at> debbugs.gnu.org and miha <at> kamnitnik.top Request was from Lars Ingebrigtsen <larsi <at> gnus.org> to control <at> debbugs.gnu.org. (Tue, 20 Jul 2021 12:38:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Tue, 17 Aug 2021 22:46:01 GMT) Full text and rfc822 format available.

Message #19 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: "Basil L. Contovounesios" <contovob <at> tcd.ie>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: 48603 <at> debbugs.gnu.org, miha <at> kamnitnik.top
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Tue, 17 Aug 2021 23:45:15 +0100
[Message part 1 (text/plain, inline)]
Lars Ingebrigtsen [2021-07-20 14:37 +0200] wrote:

> <miha <at> kamnitnik.top> writes:
>
>> Good idea, attaching a revised patch (which also adds two NEWS entries.)
>
> Sorry; I forgot all about this.  I've now re-read and tested the patch,
> and it seems to work fine for me, so I've pushed it to Emacs 28.

Thanks, but given the following file quit.el, which is distilled from
the Ivy package's batch-run test suite:

[quit.el (application/emacs-lisp, inline)]
[Message part 3 (text/plain, inline)]
the patch gives rise to the following change in behaviour.

Before:

  $ emacs -Q -batch -l quit.el

  (quit)
  $ echo $?
  0

After:

  $ emacs -Q -batch -l quit.el
  Quit
  $ echo $?
  255

IOW, it's no longer possible to catch the quit around execute-kbd-macro.
I tried wrapping it in (catch 'exit ...) as well, but to no avail.
Surely it should always be possible to catch a quit condition from Lisp?

Thanks,

-- 
Basil

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Wed, 18 Aug 2021 14:56:02 GMT) Full text and rfc822 format available.

Message #22 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: "Basil L. Contovounesios" <contovob <at> tcd.ie>
Cc: 48603 <at> debbugs.gnu.org, miha <at> kamnitnik.top
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Wed, 18 Aug 2021 16:55:40 +0200
"Basil L. Contovounesios" <contovob <at> tcd.ie> writes:

> Thanks, but given the following file quit.el, which is distilled from
> the Ivy package's batch-run test suite:
>
> ;; -*- lexical-binding: t -*-
> (defconst my-read "\C-ce")
> (local-set-key my-read (lambda () (interactive) (read-from-minibuffer "")))
> (condition-case err
>     (execute-kbd-macro (concat my-read "\C-g"))
>   (t (print err)))

[...]

> IOW, it's no longer possible to catch the quit around execute-kbd-macro.
> I tried wrapping it in (catch 'exit ...) as well, but to no avail.
> Surely it should always be possible to catch a quit condition from Lisp?

Yup.  Miha?

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Wed, 01 Sep 2021 06:56:01 GMT) Full text and rfc822 format available.

Message #25 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: <miha <at> kamnitnik.top>
To: "Basil L. Contovounesios" <contovob <at> tcd.ie>, Lars Ingebrigtsen
 <larsi <at> gnus.org>
Cc: 48603 <at> debbugs.gnu.org
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Wed, 01 Sep 2021 08:58:04 +0200
[Message part 1 (text/plain, inline)]
"Basil L. Contovounesios" via "Bug reports for GNU Emacs, the Swiss army
knife of text editors" <bug-gnu-emacs <at> gnu.org> writes:

> Lars Ingebrigtsen [2021-07-20 14:37 +0200] wrote:
>
>> <miha <at> kamnitnik.top> writes:
>>
>>> Good idea, attaching a revised patch (which also adds two NEWS entries.)
>>
>> Sorry; I forgot all about this.  I've now re-read and tested the patch,
>> and it seems to work fine for me, so I've pushed it to Emacs 28.
>
> Thanks, but given the following file quit.el, which is distilled from
> the Ivy package's batch-run test suite:
> [...]
> IOW, it's no longer possible to catch the quit around execute-kbd-macro.
> I tried wrapping it in (catch 'exit ...) as well, but to no avail.
> Surely it should always be possible to catch a quit condition from Lisp?
>
In order to make C-g from a minibuffer not abort kmacro execution,
execute-kbd-macro was made to catch the minibuffer-quit condition,
handle it with the default error handler (command-error-function) and
continue with kmacro execution.

In batch mode, this error handler kills Emacs with status 255. Please
consider the attached patch, which avoids killing for the
minibuffer-quit condition.

However, even with this patch, slight change in behaviour still remains:
- In Emacs 27, C-g in the minibuffer is bound to abort-recursive-edit
  which makes execute-kbd-macro signal an error (a quit).
- In Emacs 28, C-g is bound to abort-minibuffers. This command will not
  make execute-kbd-macro signal any errors.
I hope this change in default behaviour is acceptable, otherwise it
wouldn't be possible to have C-g continue with kmacro execution. For
test suites, you can get back the old behaviour with

(define-key minibuffer-local-map "\C-g" #'abort-recursive-edit)

Sorry for late reply,
best regards.

[0001-In-batch-mode-avoid-killing-Emacs-with-C-g-in-the-mi.patch (text/x-patch, inline)]
From dac1a0ccca5678dda4b331d1a788a8432c9a7a03 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miha=20Rihtar=C5=A1i=C4=8D?= <miha <at> kamnitnik.top>
Date: Wed, 1 Sep 2021 08:32:25 +0200
Subject: [PATCH] In batch mode, avoid killing Emacs with C-g in the minibuffer

* src/keyboard.c (Fcommand_error_default_function): Don't kill emacs
when handling the minibuffer-quit condition (bug#48603).
---
 src/keyboard.c | 37 +++++++++++++++++++------------------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/src/keyboard.c b/src/keyboard.c
index 81ff9df153..6a8c33ae3b 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -1009,25 +1009,28 @@ DEFUN ("command-error-default-function", Fcommand_error_default_function,
   (Lisp_Object data, Lisp_Object context, Lisp_Object signal)
 {
   struct frame *sf = SELECTED_FRAME ();
-  Lisp_Object conditions;
+  Lisp_Object conditions = Fget (XCAR (data), Qerror_conditions);
+  int is_minibuffer_quit = !NILP (Fmemq (Qminibuffer_quit, conditions));
 
   CHECK_STRING (context);
 
   /* If the window system or terminal frame hasn't been initialized
-     yet, or we're not interactive, write the message to stderr and exit.  */
-  if (!sf->glyphs_initialized_p
-	   /* The initial frame is a special non-displaying frame. It
-	      will be current in daemon mode when there are no frames
-	      to display, and in non-daemon mode before the real frame
-	      has finished initializing.  If an error is thrown in the
-	      latter case while creating the frame, then the frame
-	      will never be displayed, so the safest thing to do is
-	      write to stderr and quit.  In daemon mode, there are
-	      many other potential errors that do not prevent frames
-	      from being created, so continuing as normal is better in
-	      that case.  */
-	   || (!IS_DAEMON && FRAME_INITIAL_P (sf))
-	   || noninteractive)
+     yet, or we're not interactive, write the message to stderr and exit.
+     Don't do this for the minibuffer-quit condition.  */
+  if (!is_minibuffer_quit
+      && (!sf->glyphs_initialized_p
+	  /* The initial frame is a special non-displaying frame. It
+	     will be current in daemon mode when there are no frames
+	     to display, and in non-daemon mode before the real frame
+	     has finished initializing.  If an error is thrown in the
+	     latter case while creating the frame, then the frame
+	     will never be displayed, so the safest thing to do is
+	     write to stderr and quit.  In daemon mode, there are
+	     many other potential errors that do not prevent frames
+	     from being created, so continuing as normal is better in
+	     that case.  */
+	  || (!IS_DAEMON && FRAME_INITIAL_P (sf))
+	  || noninteractive))
     {
       print_error_message (data, Qexternal_debugging_output,
 			   SSDATA (context), signal);
@@ -1036,12 +1039,10 @@ DEFUN ("command-error-default-function", Fcommand_error_default_function,
     }
   else
     {
-      conditions = Fget (XCAR (data), Qerror_conditions);
-
       clear_message (1, 0);
       message_log_maybe_newline ();
 
-      if (!NILP (Fmemq (Qminibuffer_quit, conditions)))
+      if (is_minibuffer_quit)
 	{
 	  Fding (Qt);
 	}
-- 
2.32.0

[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Wed, 01 Sep 2021 08:12:02 GMT) Full text and rfc822 format available.

Message #28 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: <miha <at> kamnitnik.top>
Cc: "Basil L. Contovounesios" <contovob <at> tcd.ie>, 48603 <at> debbugs.gnu.org
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Wed, 01 Sep 2021 10:11:08 +0200
<miha <at> kamnitnik.top> writes:

> * src/keyboard.c (Fcommand_error_default_function): Don't kill emacs
> when handling the minibuffer-quit condition (bug#48603).

Thanks; looks good to me, so I've now pushed it to Emacs 28.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Fri, 03 Sep 2021 17:54:01 GMT) Full text and rfc822 format available.

Message #31 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: "Basil L. Contovounesios" <contovob <at> tcd.ie>
To: <miha <at> kamnitnik.top>
Cc: Lars Ingebrigtsen <larsi <at> gnus.org>, 48603 <at> debbugs.gnu.org
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Fri, 03 Sep 2021 18:53:18 +0100
miha <at> kamnitnik.top [2021-09-01 08:58 +0200] wrote:

> However, even with this patch, slight change in behaviour still remains:
> - In Emacs 27, C-g in the minibuffer is bound to abort-recursive-edit
>   which makes execute-kbd-macro signal an error (a quit).
> - In Emacs 28, C-g is bound to abort-minibuffers. This command will not
>   make execute-kbd-macro signal any errors.
> I hope this change in default behaviour is acceptable, otherwise it
> wouldn't be possible to have C-g continue with kmacro execution. For
> test suites, you can get back the old behaviour with
>
> (define-key minibuffer-local-map "\C-g" #'abort-recursive-edit)

Thanks, that WFM.

> Sorry for late reply,

No worries, and thanks again,

-- 
Basil




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Tue, 07 Sep 2021 15:09:02 GMT) Full text and rfc822 format available.

Message #34 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: <miha <at> kamnitnik.top>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: 48603 <at> debbugs.gnu.org
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Tue, 07 Sep 2021 17:11:11 +0200
[Message part 1 (text/plain, inline)]
Lars Ingebrigtsen <larsi <at> gnus.org> writes:

> <miha <at> kamnitnik.top> writes:
>
>> Good idea, attaching a revised patch (which also adds two NEWS entries.)
>
> Sorry; I forgot all about this.  I've now re-read and tested the patch,
> and it seems to work fine for me, so I've pushed it to Emacs 28.

After some testing, I found out that we can't record kmacros that end
with C-g in the minibuffer. For example, trying to record a kmacro like
"C-n M-x C-g" will actually record only "C-n".

Please consider the attached patch to fix this.

[0001-Allow-kmacros-to-end-with-C-g-in-minibuffer.patch (text/x-patch, inline)]
From 1fc801cde2cd0a7c557fdb3d4e1593eaa85518f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miha=20Rihtar=C5=A1i=C4=8D?= <miha <at> kamnitnik.top>
Date: Tue, 7 Sep 2021 16:55:22 +0200
Subject: [PATCH] Allow kmacros to end with C-g in minibuffer

* src/keyboard.c (cmd_error): If a command causes a minibuffer-quit
condition, record its key in a keyboard macro.
---
 src/keyboard.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/keyboard.c b/src/keyboard.c
index f6139b30e7..a32c3e7b2b 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -951,6 +951,10 @@ cmd_error (Lisp_Object data)
       Vexecuting_kbd_macro = Qnil;
       executing_kbd_macro = Qnil;
     }
+  else if (!NILP (KVAR (current_kboard, defining_kbd_macro)))
+    /* A command that signals a minibuffer-quit condition should be
+       installed in kbd macro.  */
+    finalize_kbd_macro_chars ();
 
   specbind (Qstandard_output, Qt);
   specbind (Qstandard_input, Qt);
-- 
2.33.0

[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#48603; Package emacs. (Wed, 08 Sep 2021 07:42:02 GMT) Full text and rfc822 format available.

Message #37 received at 48603 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: <miha <at> kamnitnik.top>
Cc: 48603 <at> debbugs.gnu.org
Subject: Re: bug#48603: 27.2; [PATCH] Quit minibuffers without aborting kmacros
Date: Wed, 08 Sep 2021 09:40:57 +0200
<miha <at> kamnitnik.top> writes:

> After some testing, I found out that we can't record kmacros that end
> with C-g in the minibuffer. For example, trying to record a kmacro like
> "C-n M-x C-g" will actually record only "C-n".
>
> Please consider the attached patch to fix this.

Thanks; seems to work fine when testing, and doesn't seem to lead to any
obvious regressions, so I've pushed it to Emacs 28.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Wed, 06 Oct 2021 11:24:04 GMT) Full text and rfc822 format available.

This bug report was last modified 2 years and 201 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.