Don’t stuff keyboard input uselessly

Also, document stuffing better.
* src/keyboard.c (stuff_buffered_input):
Give up on stuffing if it fails.
* src/sysdep.c (stuff_char): Return failure indication.
This commit is contained in:
Paul Eggert 2026-02-28 09:03:27 -08:00
parent 936472345d
commit aed1c8b536
6 changed files with 42 additions and 49 deletions

View file

@ -504,6 +504,8 @@ THINGS TO DO
** flow-ctrl.el must be updated. ** flow-ctrl.el must be updated.
** Fix stuff_char for multi-tty. Doesn't seem to be of high priority. ** Fix stuff_char for multi-tty. Doesn't seem to be of high priority.
For security reasons stuff_char does not work by default on many
platforms, which makes such a fix even lower priority.
DIARY OF CHANGES DIARY OF CHANGES
---------------- ----------------

View file

@ -711,12 +711,11 @@ If @var{exit-data} is an integer, that is used as the exit status of
the Emacs process. (This is useful primarily in batch operation; see the Emacs process. (This is useful primarily in batch operation; see
@ref{Batch Mode}.) @ref{Batch Mode}.)
If @var{exit-data} is a string, its contents are stuffed into the If @var{exit-data} is a string, its contents (followed by a newline) are
terminal input buffer so that the shell (or whatever program next reads stuffed into the terminal input buffer so that the shell (or whatever
input) can read them. This is only possible on some systems. Note that program next reads input) can read them. However, stuffing is silently
recent versions of GNU/Linux disable the system API required for skipped if Emacs is not running interactively on a terminal, or if
stuffing the string into the terminal input, so this might not work on stuffing requires special privileges or is not supported on this platform.
your system without special privileges.
If @var{exit-data} is neither an integer nor a string, or is omitted, If @var{exit-data} is neither an integer nor a string, or is omitted,
that means to use the (system-specific) exit status which indicates that means to use the (system-specific) exit status which indicates
@ -816,6 +815,9 @@ before suspending Emacs, or this function signals an error.
If @var{string} is non-@code{nil}, its characters are sent to Emacs's If @var{string} is non-@code{nil}, its characters are sent to Emacs's
superior shell, to be read as terminal input. superior shell, to be read as terminal input.
However, this action is skipped if Emacs is not running interactively on
a terminal, or if stuffing characters into the terminal input requires
special privileges or is not supported on this platform.
Before suspending, @code{suspend-emacs} runs the normal hook Before suspending, @code{suspend-emacs} runs the normal hook
@code{suspend-hook}. After the user resumes Emacs, @code{suspend-hook}. After the user resumes Emacs,
@ -840,7 +842,7 @@ Here is an example of how you could use these hooks:
@c The sit-for prevents the @code{nil} that suspend-emacs returns @c The sit-for prevents the @code{nil} that suspend-emacs returns
@c hiding the message. @c hiding the message.
Here is what you would see upon evaluating @code{(suspend-emacs "pwd")}: Here is what you would see upon evaluating @code{(suspend-emacs)}:
@smallexample @smallexample
@group @group
@ -851,8 +853,6 @@ Really suspend? @kbd{y}
@group @group
---------- Parent Shell ---------- ---------- Parent Shell ----------
bash$ pwd
/home/username
bash$ fg bash$ fg
@end group @end group

View file

@ -2947,9 +2947,9 @@ sort_args (int argc, char **argv)
DEFUN ("kill-emacs", Fkill_emacs, Skill_emacs, 0, 2, "P", DEFUN ("kill-emacs", Fkill_emacs, Skill_emacs, 0, 2, "P",
doc: /* Exit the Emacs job and kill it. doc: /* Exit the Emacs job and kill it.
If ARG is an integer, return ARG as the exit program code. If ARG is an integer, return ARG as the exit program code.
If ARG is a string, stuff it as keyboard input. (This might If ARG is a string, stuff it and then a newline as keyboard input,
not work on modern systems due to security considerations, or if Emacs is running interactively on a terminal and the platform
not at all.) supports and allows stuffing; this may need special privileges.
Any other value of ARG, or ARG omitted, means return an Any other value of ARG, or ARG omitted, means return an
exit code that indicates successful program termination. exit code that indicates successful program termination.

View file

@ -12421,19 +12421,15 @@ DEFUN ("suspend-emacs", Fsuspend_emacs, Ssuspend_emacs, 0, 1, "",
If `cannot-suspend' is non-nil, or if the system doesn't support job If `cannot-suspend' is non-nil, or if the system doesn't support job
control, run a subshell instead. control, run a subshell instead.
If optional arg STUFFSTRING is non-nil, its characters are stuffed If optional arg STUFFSTRING is non-nil, stuff it and then a newline as
to be read as terminal input by Emacs's parent, after suspension. keyboard input, if Emacs is running interactively on a terminal and the
platform supports and allows stuffing; this may need special privileges.
Before suspending, run the normal hook `suspend-hook'. Before suspending, run the normal hook `suspend-hook'.
After resumption run the normal hook `suspend-resume-hook'. After resumption run the normal hook `suspend-resume-hook'.
Some operating systems cannot stop the Emacs process and resume it later. Some operating systems cannot stop the Emacs process and resume it later.
On such systems, Emacs starts a subshell instead of suspending. On such systems, Emacs starts a subshell instead of suspending. */)
On some operating systems, stuffing characters into terminal input
buffer requires special privileges or is not supported at all.
On such systems, calling this function with non-nil STUFFSTRING might
either signal an error or silently fail to stuff the characters. */)
(Lisp_Object stuffstring) (Lisp_Object stuffstring)
{ {
specpdl_ref count = SPECPDL_INDEX (); specpdl_ref count = SPECPDL_INDEX ();
@ -12472,38 +12468,34 @@ either signal an error or silently fail to stuff the characters. */)
return Qnil; return Qnil;
} }
/* If STUFFSTRING is a string, stuff its contents as pending terminal input. /* If STUFFSTRING is a string, stuff its contents and then a newline as
Then in any case stuff anything Emacs has read ahead and not used. */ pending terminal input. Then stuff anything Emacs has read ahead and
not used. However, do nothing if stuffing does not work. */
void void
stuff_buffered_input (Lisp_Object stuffstring) stuff_buffered_input (Lisp_Object stuffstring)
{ {
#ifdef SIGTSTP /* stuff_char is defined if SIGTSTP. */ #ifdef SIGTSTP /* stuff_char is defined if SIGTSTP. */
register unsigned char *p; int bad_stuff = 0;
if (STRINGP (stuffstring)) if (STRINGP (stuffstring))
{ {
register ptrdiff_t count; char *p = SSDATA (stuffstring);
for (ptrdiff_t i = SBYTES (stuffstring); !bad_stuff && 0 < i; i--)
p = SDATA (stuffstring); bad_stuff = stuff_char (*p++);
count = SBYTES (stuffstring); if (!bad_stuff)
while (count-- > 0) bad_stuff = stuff_char ('\n');
stuff_char (*p++);
stuff_char ('\n');
} }
/* Anything we have read ahead, put back for the shell to read. */ /* Anything we have read ahead, put back for the shell to read.
/* ?? What should this do when we have multiple keyboards?? When we have multiple keyboards, we should stuff everything back
Should we ignore anything that was typed in at the "wrong" kboard? into the keyboard it came from, but fixing this is low priority as
many systems prohibit stuffing for security reasons. */
rms: we should stuff everything back into the kboard
it came from. */
for (; kbd_fetch_ptr != kbd_store_ptr; for (; kbd_fetch_ptr != kbd_store_ptr;
kbd_fetch_ptr = next_kbd_event (kbd_fetch_ptr)) kbd_fetch_ptr = next_kbd_event (kbd_fetch_ptr))
{ {
if (kbd_fetch_ptr->kind == ASCII_KEYSTROKE_EVENT && !bad_stuff)
if (kbd_fetch_ptr->kind == ASCII_KEYSTROKE_EVENT) bad_stuff = stuff_char (kbd_fetch_ptr->ie.code);
stuff_char (kbd_fetch_ptr->ie.code);
clear_event (&kbd_fetch_ptr->ie); clear_event (&kbd_fetch_ptr->ie);
} }

View file

@ -5280,7 +5280,7 @@ maybe_disable_address_randomization (int argc, char **argv)
extern int emacs_exec_file (char const *, char *const *, char *const *); extern int emacs_exec_file (char const *, char *const *, char *const *);
extern void init_standard_fds (void); extern void init_standard_fds (void);
extern char *emacs_get_current_dir_name (void); extern char *emacs_get_current_dir_name (void);
extern void stuff_char (char c); extern int stuff_char (char c);
extern void init_foreground_group (void); extern void init_foreground_group (void);
extern void sys_subshell (void); extern void sys_subshell (void);
extern void sys_suspend (void); extern void sys_suspend (void);

View file

@ -390,23 +390,22 @@ discard_tty_input (void)
/* Arrange for character C to be read as the next input from /* Arrange for character C to be read as the next input from
the terminal. the terminal.
Return 0 on success, -1 otherwise.
XXX What if we have multiple ttys? XXX What if we have multiple ttys?
*/ */
void int
stuff_char (char c) stuff_char (char c)
{ {
if (! (FRAMEP (selected_frame)
&& FRAME_LIVE_P (XFRAME (selected_frame))
&& FRAME_TERMCAP_P (XFRAME (selected_frame))))
return;
/* Should perhaps error if in batch mode */ /* Should perhaps error if in batch mode */
#ifdef TIOCSTI #ifdef TIOCSTI
ioctl (fileno (CURTTY()->input), TIOCSTI, &c); if (FRAMEP (selected_frame)
#else /* no TIOCSTI */ && FRAME_LIVE_P (XFRAME (selected_frame))
error ("Cannot stuff terminal input characters in this version of Unix"); && FRAME_TERMCAP_P (XFRAME (selected_frame)))
return ioctl (fileno (CURTTY()->input), TIOCSTI, &c);
#endif /* no TIOCSTI */ #endif /* no TIOCSTI */
return -1;
} }
#endif /* SIGTSTP */ #endif /* SIGTSTP */