Attention is currently required from: Jonas Termansen.
Alexander Aprelev uploaded patch set #9 to the change originally created by Jonas Termansen.
[io] Don't restore terminal state on exit.
This is breaking change #45630.
The Dart VM has until now restored the terminal settings upon exit to
their initial values for stdin, stdout, and stderr. This change removes
that automatic behavior in favor of having the program do the
restoration. Previously the intention was that dart programs can
enable/disable echoing and line buffering and not worry about restoring
the original settings.
However, the VM doing so unconditionally leads to undesirable behavior
e.g. when the program does not care about terminal settings and is
sharing a process group with a program that does care. E.g. if dart's
output is piped into less(1), then there is a race condition where dart
might see the raw terminal settings set by less(1), and if the dart VM
exits after less(1) has exited, then it will restore the raw terminal
settings, leaving the user with a seemingly defective shell with echo
disabled. This race condition can be reproduced using:
cat > yes.dart << EOF
main() {
for (int i = 0; i < 1000000; i++) {
print("yes");
}
}
EOF
stty; (sleep 1 && dart yes.dart) | less; stty; stty sane
The user will end up with a shell with echo behavior disabled. The stty
command shows the current terminal settings, where the difference can be
seen, and 'stty sane' fixes the settings before returning to the shell
prompt. The 'stty sane' call can be omitted to see the defective shell
prompt.
This change removes the terminal restoring behavior (added in Dart
2.0.0) and instead asks applications to do the restoration themselves.
The new design matches how programs in other programming languages
implement interactive input that changes terminal settings.
Furthermore the `echoMode` setting now only controls the `echo` local
mode and no longer sets the `echonl` local mode on POSIX systems (which
controls whether newline are echoed even if the regular echo mode is
disabled). The `echonl` local mode is usually turned off in common shell
environments. Programs that wish to control the `echonl` local mode can
use the new `echoNewlineMode` setting. This change is required to
prevent the reoccurence of #30318 when programs manually restore
`echoMode`.
Windows has further considerations: It also saves the console code pages
and restore them if they were not UTF-8. This behavior is retained as it
is useful and needed for Dart's output to function properly. ANSI output
sequences are also turned on via ENABLE_VIRTUAL_TERMINAL_PROCESSING,
which is slightly changed in this change to only rsetore that setting if
it wasn't already on for consistency.
Closes https://github.com/dart-lang/sdk/issues/36453
Closes https://github.com/dart-lang/sdk/issues/45630
TEST=Reproduced with less as above
Change-Id: I2991f9c7f47b97fe475c1ad6edeb769024f8d0db
---
M CHANGELOG.md
M runtime/bin/console_posix.cc
M runtime/bin/console_win.cc
M runtime/bin/io_natives.cc
M runtime/bin/stdio.cc
M runtime/bin/stdio.h
M runtime/bin/stdio_android.cc
M runtime/bin/stdio_fuchsia.cc
M runtime/bin/stdio_linux.cc
M runtime/bin/stdio_macos.cc
M runtime/bin/stdio_win.cc
M sdk/lib/_internal/js_dev_runtime/patch/io_patch.dart
M sdk/lib/_internal/js_runtime/lib/io_patch.dart
M sdk/lib/_internal/vm/bin/stdio_patch.dart
M sdk/lib/io/embedder_config.dart
M sdk/lib/io/stdio.dart
16 files changed, 337 insertions(+), 69 deletions(-)
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonas Termansen, Alexander Aprelev.
go/dart-cbuild result: FAILURE (NO REGRESSIONS DETECTED)
Details: https://goto.google.com/dart-cbuild/find/461d5d265d48d236a0fc856c61e71fada7374d4d
Attention is currently required from: Jonas Termansen, Alexander Aprelev.
Alexander Aprelev uploaded patch set #10 to the change originally created by Jonas Termansen.
M tests/standalone/io/io_override_test.dart
M tests/standalone_2/io/io_override_test.dart
18 files changed, 339 insertions(+), 69 deletions(-)
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonas Termansen, Alexander Aprelev.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/4304abb63f9375910c0d6c85b33a0505259f2c35
Attention is currently required from: Jonas Termansen, Alexander Aprelev.
1 comment:
File CHANGELOG.md:
This should be added to a new 2.18 section, since we've branched!
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonas Termansen, Alexander Aprelev.
Alexander Aprelev uploaded patch set #11 to the change originally created by Jonas Termansen.
18 files changed, 343 insertions(+), 69 deletions(-)
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonas Termansen, Kevin Moore.
Patch set 11:Code-Review +1
2 comments:
File CHANGELOG.md:
This should be added to a new 2. […]
Done
File sdk/lib/io/stdio.dart:
Patch Set #8, Line 138: @Since("2.14")
If not landed yet, it probably won't get into 2.14 […]
Done
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonas Termansen, Kevin Moore.
Alexander Aprelev uploaded patch set #12 to the change originally created by Jonas Termansen.
18 files changed, 339 insertions(+), 69 deletions(-)
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Kevin Moore, Alexander Aprelev.
Patch set 12:Code-Review +1
2 comments:
Patchset:
Thanks Alex for taking over this change. :) It's basically done, it ended up stuck I had other work to do and I was worried about difficulty rolling this change into Flutter, but let's see how it works out!
File sdk/lib/io/stdio.dart:
2.18
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Kevin Moore, Alexander Aprelev.
Alexander Aprelev uploaded patch set #13 to the change originally created by Jonas Termansen.
18 files changed, 338 insertions(+), 69 deletions(-)
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonas Termansen, Kevin Moore.
Patch set 13:Code-Review +1Commit-Queue +1
1 comment:
File sdk/lib/io/stdio.dart:
2. […]
Done
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Jonas Termansen, Kevin Moore.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/6f63e3bf32e51fe0ab50d74e9cfc0c9b7ef0054b
Attention is currently required from: Alexander Aprelev, Jonas Termansen, Kevin Moore.
Alexander Aprelev uploaded patch set #14 to the change originally created by Jonas Termansen.
The following approvals got outdated and were removed: -Commit-Queue by Alexander Aprelev
18 files changed, 340 insertions(+), 69 deletions(-)
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Alexander Aprelev, Jonas Termansen, Kevin Moore.
Alexander Aprelev uploaded patch set #15 to the change originally created by Jonas Termansen.
[io] Don't restore terminal state on exit.
18 files changed, 338 insertions(+), 69 deletions(-)
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Alexander Aprelev, Jonas Termansen, Kevin Moore.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/b7f8b17f6b57259e304bd32a79ad1d3c728f0cb0
Attention is currently required from: Alexander Aprelev, Jonas Termansen, Kevin Moore.
Patch set 15:Commit-Queue +2
Commit Bot submitted this change.
13 is the latest approved patch-set.
No files were changed between the latest approved patch-set and the submitted one.
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/190484
Reviewed-by: Lasse Nielsen <l...@google.com>
Commit-Queue: Alexander Aprelev <a...@google.com>
Reviewed-by: Jonas Termansen <sor...@google.com>
Reviewed-by: Alexander Aprelev <a...@google.com>
---
M CHANGELOG.md
M runtime/bin/console_posix.cc
M runtime/bin/console_win.cc
M runtime/bin/io_natives.cc
M runtime/bin/stdio.cc
M runtime/bin/stdio.h
M runtime/bin/stdio_android.cc
M runtime/bin/stdio_fuchsia.cc
M runtime/bin/stdio_linux.cc
M runtime/bin/stdio_macos.cc
M runtime/bin/stdio_win.cc
M sdk/lib/_internal/js_dev_runtime/patch/io_patch.dart
M sdk/lib/_internal/js_runtime/lib/io_patch.dart
M sdk/lib/_internal/vm/bin/stdio_patch.dart
M sdk/lib/io/embedder_config.dart
M sdk/lib/io/stdio.dart
M tests/standalone/io/io_override_test.dart
M tests/standalone_2/io/io_override_test.dart
18 files changed, 343 insertions(+), 69 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6195c9e..2eab9df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -54,6 +54,52 @@
- Add `connectionState` attribute and `connectionstatechange` listener to
`RtcPeerConnection`.
+#### `dart:io`
+
+- **Breaking Change** [#45630][]: The Dart VM no longer automatically restores
+ the initial terminal settings upon exit. Programs that change the `Stdin`
+ settings `lineMode` and `echoMode` are now responsible for restoring the
+ settings upon program exit. E.g. a program disabling `echoMode` will now
+ need to restore the setting itself and handle exiting by the appropriate
+ signals if desired:
+
+ ```dart
+ import 'dart:io';
+ import 'dart:async';
+
+ main() {
+ bool echoWasEnabled = stdin.echoMode;
+ try {
+ late StreamSubscription subscription;
+ subscription = ProcessSignal.sigint.watch().listen((ProcessSignal signal) {
+ stdin.echoMode = echoWasEnabled;
+ subscription.cancel();
+ Process.killPid(pid, signal); /* Die by the signal. */
+ });
+ stdin.echoMode = false;
+ } finally {
+ stdin.echoMode = echoWasEnabled;
+ }
+ }
+ ```
+
+ This change is needed to fix [#36453][] where the dart programs not caring
+ about the terminal settings can inadverently corrupt the terminal settings
+ when e.g. piping into less.
+
+ Furthermore the `echoMode` setting now only controls the `echo` local mode
+ and no longer sets the `echonl` local mode on POSIX systems (which controls
+ whether newline are echoed even if the regular echo mode is disabled). The
+ `echonl` local mode is usually turned off in common shell environments.
+ Programs that wish to control the `echonl` local mode can use the new
+ `echoNewlineMode` setting.
+
+ The Windows console code pages (if not UTF-8) and ANSI escape code support
+ (if disabled) remain restored when the VM exits.
+
+[#45630]: https://github.com/dart-lang/sdk/issues/45630
+[#36453]: https://github.com/dart-lang/sdk/issues/36453
+
#### `dart:js_util`
- Added `dartify` and a number of minor helper functions.
diff --git a/runtime/bin/console_posix.cc b/runtime/bin/console_posix.cc
index 17116da..f318ae1 100644
--- a/runtime/bin/console_posix.cc
+++ b/runtime/bin/console_posix.cc
@@ -18,71 +18,10 @@
namespace dart {
namespace bin {
-class PosixConsole {
- public:
- static const tcflag_t kInvalidFlag = -1;
-
- static void Initialize() {
- SaveMode(STDOUT_FILENO, &stdout_initial_c_lflag_);
- SaveMode(STDERR_FILENO, &stderr_initial_c_lflag_);
- SaveMode(STDIN_FILENO, &stdin_initial_c_lflag_);
- }
-
- static void Cleanup() {
- RestoreMode(STDOUT_FILENO, stdout_initial_c_lflag_);
- RestoreMode(STDERR_FILENO, stderr_initial_c_lflag_);
- RestoreMode(STDIN_FILENO, stdin_initial_c_lflag_);
- ClearLFlags();
- }
-
- private:
- static tcflag_t stdout_initial_c_lflag_;
- static tcflag_t stderr_initial_c_lflag_;
- static tcflag_t stdin_initial_c_lflag_;
-
- static void ClearLFlags() {
- stdout_initial_c_lflag_ = kInvalidFlag;
- stderr_initial_c_lflag_ = kInvalidFlag;
- stdin_initial_c_lflag_ = kInvalidFlag;
- }
-
- static void SaveMode(intptr_t fd, tcflag_t* flag) {
- ASSERT(flag != NULL);
- struct termios term;
- int status = TEMP_FAILURE_RETRY(tcgetattr(fd, &term));
- if (status != 0) {
- return;
- }
- *flag = term.c_lflag;
- }
-
- static void RestoreMode(intptr_t fd, tcflag_t flag) {
- if (flag == kInvalidFlag) {
- return;
- }
- struct termios term;
- int status = TEMP_FAILURE_RETRY(tcgetattr(fd, &term));
- if (status != 0) {
- return;
- }
- term.c_lflag = flag;
- VOID_TEMP_FAILURE_RETRY(tcsetattr(fd, TCSANOW, &term));
- }
-
- DISALLOW_ALLOCATION();
- DISALLOW_IMPLICIT_CONSTRUCTORS(PosixConsole);
-};
-
-tcflag_t PosixConsole::stdout_initial_c_lflag_ = PosixConsole::kInvalidFlag;
-tcflag_t PosixConsole::stderr_initial_c_lflag_ = PosixConsole::kInvalidFlag;
-tcflag_t PosixConsole::stdin_initial_c_lflag_ = PosixConsole::kInvalidFlag;
-
void Console::SaveConfig() {
- PosixConsole::Initialize();
}
void Console::RestoreConfig() {
- PosixConsole::Cleanup();
}
} // namespace bin
diff --git a/runtime/bin/console_win.cc b/runtime/bin/console_win.cc
index 59e6603..d714aa5 100644
--- a/runtime/bin/console_win.cc
+++ b/runtime/bin/console_win.cc
@@ -105,6 +105,10 @@
/// to reset the state when we cleanup.
if ((h != INVALID_HANDLE_VALUE) && GetConsoleMode(h, &mode)) {
old_mode = mode;
+ // No reason to restore the mode on exit if it was already desirable.
+ if ((mode & flags) == flags) {
+ return kInvalidFlag;
+ }
if (flags != 0) {
const DWORD request = mode | flags;
SetConsoleMode(h, request);
diff --git a/runtime/bin/io_natives.cc b/runtime/bin/io_natives.cc
index ea51935..002cd2c 100644
--- a/runtime/bin/io_natives.cc
+++ b/runtime/bin/io_natives.cc
@@ -176,6 +176,8 @@
V(Stdin_ReadByte, 1) \
V(Stdin_GetEchoMode, 1) \
V(Stdin_SetEchoMode, 2) \
+ V(Stdin_GetEchoNewlineMode, 1) \
+ V(Stdin_SetEchoNewlineMode, 2) \
V(Stdin_GetLineMode, 1) \
V(Stdin_SetLineMode, 2) \
V(Stdin_AnsiSupported, 1) \
diff --git a/runtime/bin/stdio.cc b/runtime/bin/stdio.cc
index d833ba4..07ea704 100644
--- a/runtime/bin/stdio.cc
+++ b/runtime/bin/stdio.cc
@@ -85,6 +85,39 @@
}
}
+void FUNCTION_NAME(Stdin_GetEchoNewlineMode)(Dart_NativeArguments args) {
+ bool enabled = false;
+ intptr_t fd;
+ if (!GetIntptrArgument(args, 0, &fd)) {
+ return;
+ }
+ if (Stdin::GetEchoNewlineMode(fd, &enabled)) {
+ Dart_SetBooleanReturnValue(args, enabled);
+ } else {
+ Dart_SetReturnValue(args, DartUtils::NewDartOSError());
+ }
+}
+
+void FUNCTION_NAME(Stdin_SetEchoNewlineMode)(Dart_NativeArguments args) {
+ intptr_t fd;
+ if (!GetIntptrArgument(args, 0, &fd)) {
+ return;
+ }
+ bool enabled;
+ Dart_Handle status = Dart_GetNativeBooleanArgument(args, 1, &enabled);
+ if (Dart_IsError(status)) {
+ // The caller is expecting an OSError if something goes wrong.
+ OSError os_error(-1, "Invalid argument", OSError::kUnknown);
+ Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
+ return;
+ }
+ if (Stdin::SetEchoNewlineMode(fd, enabled)) {
+ Dart_SetReturnValue(args, Dart_True());
+ } else {
+ Dart_SetReturnValue(args, DartUtils::NewDartOSError());
+ }
+}
+
void FUNCTION_NAME(Stdin_GetLineMode)(Dart_NativeArguments args) {
bool enabled = false;
intptr_t fd;
diff --git a/runtime/bin/stdio.h b/runtime/bin/stdio.h
index 3bbc019..e5796d5 100644
--- a/runtime/bin/stdio.h
+++ b/runtime/bin/stdio.h
@@ -20,6 +20,9 @@
static bool GetEchoMode(intptr_t fd, bool* enabled);
static bool SetEchoMode(intptr_t fd, bool enabled);
+ static bool GetEchoNewlineMode(intptr_t fd, bool* enabled);
+ static bool SetEchoNewlineMode(intptr_t fd, bool enabled);
+
static bool GetLineMode(intptr_t fd, bool* enabled);
static bool SetLineMode(intptr_t fd, bool enabled);
diff --git a/runtime/bin/stdio_android.cc b/runtime/bin/stdio_android.cc
index 1afb34f..e620e1f 100644
--- a/runtime/bin/stdio_android.cc
+++ b/runtime/bin/stdio_android.cc
@@ -44,9 +44,34 @@
return false;
}
if (enabled) {
- term.c_lflag |= (ECHO | ECHONL);
+ term.c_lflag |= ECHO;
} else {
- term.c_lflag &= ~(ECHO | ECHONL);
+ term.c_lflag &= ~(ECHO);
+ }
+ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
+ return (status == 0);
+}
+
+bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) {
+ struct termios term;
+ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
+ if (status != 0) {
+ return false;
+ }
+ *enabled = ((term.c_lflag & ECHONL) != 0);
+ return true;
+}
+
+bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) {
+ struct termios term;
+ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
+ if (status != 0) {
+ return false;
+ }
+ if (enabled) {
+ term.c_lflag |= ECHONL;
+ } else {
+ term.c_lflag &= ~(ECHONL);
}
status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
return (status == 0);
diff --git a/runtime/bin/stdio_fuchsia.cc b/runtime/bin/stdio_fuchsia.cc
index 2f91a9c..4d84b48 100644
--- a/runtime/bin/stdio_fuchsia.cc
+++ b/runtime/bin/stdio_fuchsia.cc
@@ -34,6 +34,16 @@
return false;
}
+bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) {
+ errno = ENOSYS;
+ return false;
+}
+
+bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) {
+ errno = ENOSYS;
+ return false;
+}
+
bool Stdin::GetLineMode(intptr_t fd, bool* enabled) {
errno = ENOSYS;
return false;
diff --git a/runtime/bin/stdio_linux.cc b/runtime/bin/stdio_linux.cc
index 17db9bd..4acddde 100644
--- a/runtime/bin/stdio_linux.cc
+++ b/runtime/bin/stdio_linux.cc
@@ -44,9 +44,34 @@
return false;
}
if (enabled) {
- term.c_lflag |= (ECHO | ECHONL);
+ term.c_lflag |= ECHO;
} else {
- term.c_lflag &= ~(ECHO | ECHONL);
+ term.c_lflag &= ~(ECHO);
+ }
+ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
+ return (status == 0);
+}
+
+bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) {
+ struct termios term;
+ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
+ if (status != 0) {
+ return false;
+ }
+ *enabled = ((term.c_lflag & ECHONL) != 0);
+ return true;
+}
+
+bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) {
+ struct termios term;
+ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
+ if (status != 0) {
+ return false;
+ }
+ if (enabled) {
+ term.c_lflag |= ECHONL;
+ } else {
+ term.c_lflag &= ~(ECHONL);
}
status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
return (status == 0);
diff --git a/runtime/bin/stdio_macos.cc b/runtime/bin/stdio_macos.cc
index 969b75d..202b169 100644
--- a/runtime/bin/stdio_macos.cc
+++ b/runtime/bin/stdio_macos.cc
@@ -44,9 +44,34 @@
return false;
}
if (enabled) {
- term.c_lflag |= (ECHO | ECHONL);
+ term.c_lflag |= ECHO;
} else {
- term.c_lflag &= ~(ECHO | ECHONL);
+ term.c_lflag &= ~(ECHO);
+ }
+ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
+ return (status == 0);
+}
+
+bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) {
+ struct termios term;
+ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
+ if (status != 0) {
+ return false;
+ }
+ *enabled = ((term.c_lflag & ECHONL) != 0);
+ return true;
+}
+
+bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) {
+ struct termios term;
+ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term));
+ if (status != 0) {
+ return false;
+ }
+ if (enabled) {
+ term.c_lflag |= ECHONL;
+ } else {
+ term.c_lflag &= ~(ECHONL);
}
status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term));
return (status == 0);
diff --git a/runtime/bin/stdio_win.cc b/runtime/bin/stdio_win.cc
index dc65cba..105b611 100644
--- a/runtime/bin/stdio_win.cc
+++ b/runtime/bin/stdio_win.cc
@@ -56,6 +56,19 @@
return SetConsoleMode(h, mode);
}
+bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) {
+ *enabled = false;
+ return true;
+}
+
+bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) {
+ if (enabled) {
+ SetLastError(ERROR_NOT_CAPABLE);
+ return false;
+ }
+ return true;
+}
+
bool Stdin::GetLineMode(intptr_t fd, bool* enabled) {
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/io_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/io_patch.dart
index a70800f..3be8a09 100644
--- a/sdk/lib/_internal/js_dev_runtime/patch/io_patch.dart
+++ b/sdk/lib/_internal/js_dev_runtime/patch/io_patch.dart
@@ -688,6 +688,16 @@
}
@patch
+ bool get echoNewlineMode {
+ throw UnsupportedError("Stdin.echoNewlineMode");
+ }
+
+ @patch
+ void set echoNewlineMode(bool enabled) {
+ throw UnsupportedError("Stdin.echoNewlineMode");
+ }
+
+ @patch
bool get lineMode {
throw UnsupportedError("Stdin.lineMode");
}
diff --git a/sdk/lib/_internal/js_runtime/lib/io_patch.dart b/sdk/lib/_internal/js_runtime/lib/io_patch.dart
index e22aae2..f795f7c 100644
--- a/sdk/lib/_internal/js_runtime/lib/io_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/io_patch.dart
@@ -688,6 +688,16 @@
}
@patch
+ bool get echoNewlineMode {
+ throw UnsupportedError("Stdin.echoNewlineMode");
+ }
+
+ @patch
+ void set echoNewlineMode(bool enabled) {
+ throw UnsupportedError("Stdin.echoNewlineMode");
+ }
+
+ @patch
bool get lineMode {
throw new UnsupportedError("Stdin.lineMode");
}
diff --git a/sdk/lib/_internal/vm/bin/stdio_patch.dart b/sdk/lib/_internal/vm/bin/stdio_patch.dart
index 6a6bd2c..680bf44 100644
--- a/sdk/lib/_internal/vm/bin/stdio_patch.dart
+++ b/sdk/lib/_internal/vm/bin/stdio_patch.dart
@@ -87,6 +87,29 @@
}
@patch
+ bool get echoNewlineMode {
+ var result = _echoNewlineMode(_fd);
+ if (result is OSError) {
+ throw new StdinException(
+ "Error getting terminal echo newline mode", result);
+ }
+ return result;
+ }
+
+ @patch
+ void set echoNewlineMode(bool enabled) {
+ if (!_EmbedderConfig._maySetEchoNewlineMode) {
+ throw new UnsupportedError(
+ "This embedder disallows setting Stdin.echoNewlineMode");
+ }
+ var result = _setEchoNewlineMode(_fd, enabled);
+ if (result is OSError) {
+ throw new StdinException(
+ "Error setting terminal echo newline mode", result);
+ }
+ }
+
+ @patch
bool get lineMode {
var result = _lineMode(_fd);
if (result is OSError) {
@@ -120,6 +143,10 @@
external static _echoMode(int fd);
@pragma("vm:external-name", "Stdin_SetEchoMode")
external static _setEchoMode(int fd, bool enabled);
+ @pragma("vm:external-name", "Stdin_GetEchoNewlineMode")
+ external static _echoNewlineMode(int fd);
+ @pragma("vm:external-name", "Stdin_SetEchoNewlineMode")
+ external static _setEchoNewlineMode(int fd, bool enabled);
@pragma("vm:external-name", "Stdin_GetLineMode")
external static _lineMode(int fd);
@pragma("vm:external-name", "Stdin_SetLineMode")
diff --git a/sdk/lib/io/embedder_config.dart b/sdk/lib/io/embedder_config.dart
index fd5bb24..9b934a6 100644
--- a/sdk/lib/io/embedder_config.dart
+++ b/sdk/lib/io/embedder_config.dart
@@ -24,6 +24,10 @@
@pragma('vm:entry-point')
static bool _maySetEchoMode = true;
+ // Whether the isolate may set [Stdin.echoNewlineMode].
+ @pragma('vm:entry-point')
+ static bool _maySetEchoNewlineMode = true;
+
// Whether the isolate may set [Stdin.lineMode].
@pragma('vm:entry-point')
static bool _maySetLineMode = true;
diff --git a/sdk/lib/io/stdio.dart b/sdk/lib/io/stdio.dart
index dcaf1d8..3c8e355 100644
--- a/sdk/lib/io/stdio.dart
+++ b/sdk/lib/io/stdio.dart
@@ -112,14 +112,34 @@
/// Whether echo mode is enabled on [stdin].
///
- /// If disabled, input from to console will not be echoed.
+ /// If disabled, input from the console will not be echoed.
///
/// Default depends on the parent process, but is usually enabled.
///
+ /// On POSIX systems this mode is the `echo` local terminal mode. Before
+ /// Dart 2.18, it also controlled the `echonl` mode, which is now controlled
+ /// by [echoNewlineMode].
+ ///
/// On Windows this mode can only be enabled if [lineMode] is enabled as well.
external bool get echoMode;
external set echoMode(bool echoMode);
+ /// Whether echo newline mode is enabled on [stdin].
+ ///
+ /// If enabled, newlines from the terminal will be echoed even if the regular
+ /// [echoMode] is disabled. This mode may require `lineMode` to be turned on
+ /// to have an effect.
+ ///
+ /// Default depends on the parent process, but is usually disabled.
+ ///
+ /// On POSIX systems this mode is the `echonl` local terminal mode.
+ ///
+ /// On Windows this mode cannot be set.
+ @Since("2.18")
+ external bool get echoNewlineMode;
+ @Since("2.18")
+ external set echoNewlineMode(bool echoNewlineMode);
+
/// Whether line mode is enabled on [stdin].
///
/// If enabled, characters are delayed until a newline character is entered.
@@ -127,7 +147,10 @@
///
/// Default depends on the parent process, but is usually enabled.
///
- /// On Windows this mode can only be disabled if [echoMode] is disabled as well.
+ /// On POSIX systems this mode is the `icanon` local terminal mode.
+ ///
+ /// On Windows this mode can only be disabled if [echoMode] is disabled as
+ /// well.
external bool get lineMode;
external set lineMode(bool lineMode);
diff --git a/tests/standalone/io/io_override_test.dart b/tests/standalone/io/io_override_test.dart
index 4fe9c66..7acca81 100644
--- a/tests/standalone/io/io_override_test.dart
+++ b/tests/standalone/io/io_override_test.dart
@@ -177,6 +177,7 @@
class StdinMock extends Stream<List<int>> implements Stdin {
bool echoMode = false;
+ bool echoNewlineMode = false;
bool lineMode = false;
bool get hasTerminal => throw "";
bool get supportsAnsiEscapes => throw "";
diff --git a/tests/standalone_2/io/io_override_test.dart b/tests/standalone_2/io/io_override_test.dart
index e7d9a81..e99f611 100644
--- a/tests/standalone_2/io/io_override_test.dart
+++ b/tests/standalone_2/io/io_override_test.dart
@@ -177,6 +177,7 @@
class StdinMock extends Stream<List<int>> implements Stdin {
bool echoMode = false;
+ bool echoNewlineMode = false;
bool lineMode = false;
bool get hasTerminal => throw "";
bool get supportsAnsiEscapes => throw "";
To view, visit change 190484. To unsubscribe, or for help writing mail filters, visit settings.
go/dart-cbuild result: SUCCESS
Details: https://goto.google.com/dart-cbuild/find/83850ac5fa8cfd58a0dcbfe7103b6455a2a1d523