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

  • [PATCH] Preserve ech... Zeffie via Bug reports for the GNU Bourne Again SHell

Reply via email to