Hello,
This patch is submitted for consideration only. It addresses an issue in
sh‑mode where Bash fails to preserve the user-configured echoctl flag.
As described in [Bug
284513](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=284513), the
default FreeBSD /bin/sh (which behaves similarly to Bash in sh‑mode)
does not provide the expected visual feedback when CTRL‑C is pressed,
even when the terminal flag echoctl is enabled. In other words, the
control characters (such as "^C") do not appear as expected, which is
contrary to normal terminal behavior. One of the key ideas in the UNIX
world is that we should not astonish the user.
In the shared terminal-handling code (lib/sh/shtty.c), the function
`tt_setonechar()` clears canonical mode and sets ISIG (and IEXTEN if
defined) without preserving the current state of echoctl. This patch
saves the state of the ECHOCTL flag before modifying the terminal
settings and restores it afterward.
Below is the patch:
```c
--- shtty.c 2008-08-12 12:00:57.000000000 -0400
+++ shtty.c.patched 2025-02-01 17:55:14.515937423 -0500
@@ -109,17 +109,20 @@
TTYSTRUCT *ttp;
{
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
+ int echoctl_was_set = (ttp->c_lflag & ECHOCTL);
- /* XXX - might not want this -- it disables erase and kill
processing. */
+ /* Clear canonical mode */
ttp->c_lflag &= ~ICANON;
+ /* Ensure signals and extended input processing are enabled */
ttp->c_lflag |= ISIG;
# ifdef IEXTEN
ttp->c_lflag |= IEXTEN;
# endif
- ttp->c_iflag |= ICRNL; /* make sure we get CR->NL on input */
- ttp->c_iflag &= ~INLCR; /* but no NL->CR */
+ /* Set input flags: ensure CR is translated to NL, but don't
translate NL to CR */
+ ttp->c_iflag |= ICRNL;
+ ttp->c_iflag &= ~INLCR;
# ifdef OPOST
ttp->c_oflag |= OPOST;
@@
- ttp->c_cc[VMIN] = 1;
- ttp->c_cc[VTIME] = 0;
-
-#else
-
- ttp->sg_flags |= CBREAK;
-#endif
-
- return 0;
-}
+ ttp->c_cc[VMIN] = 1;
+ ttp->c_cc[VTIME] = 0;
+
+ /* Restore the user's echoctl setting */
+ if (echoctl_was_set)
+ ttp->c_lflag |= ECHOCTL;
+ else
+ ttp->c_lflag &= ~ECHOCTL;
+#else
+
+ ttp->sg_flags |= CBREAK;
+#endif
+
+ return 0;
+}
```
Rationale
Preservation of User Settings:
The patch begins by saving the current state of the ECHOCTL flag:
```c
int echoctl_was_set = (ttp->c_lflag & ECHOCTL);
```
This ensures that any terminal configuration set by the user (e.g.,
via `stty -echoctl` or `stty echoctl`) is preserved.
Terminal Setup:
The code clears canonical mode (`ICANON`) and sets ISIG (and IEXTEN,
if defined) to enable one‑character‑at‑a‑time input. It also configures
input flags to translate carriage returns (CR) to newlines (NL) without
converting NL to CR.
Restoration of echoctl:
Finally, the patch restores the echoctl flag to the original user
setting. This guarantees that when the user disables echoing of control
characters, that preference is maintained, avoiding the surprising
behavior observed in the current implementation.
This patch is intended to ensure that Bash in sh‑mode adheres to the
principle that the user should not be astonished by unexpected terminal
behavior.
HTH
Zeffie