From badf56b83e35420fafed0d1a17ca4ed7b523662c Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 22 Nov 2023 12:51:00 +0100 Subject: [PATCH] Improve the message when a provider cannot be used by doing an early check and restore compatibility with Jansi 1.17 --- .../impl/jansi/JansiTerminalProvider.java | 80 ++++++++++--------- .../impl/jansi/win/JansiWinConsoleWriter.java | 4 +- .../impl/jansi/win/JansiWinSysTerminal.java | 17 +--- .../impl/jansi/win/WindowsAnsiWriter.java | 12 +-- .../impl/jansi/win/WindowsSupport.java | 26 ++++++ .../jline/terminal/impl/jna/JnaNativePty.java | 6 ++ .../impl/jna/JnaTerminalProvider.java | 23 +++--- 7 files changed, 96 insertions(+), 72 deletions(-) create mode 100644 terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/WindowsSupport.java diff --git a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiTerminalProvider.java b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiTerminalProvider.java index bd1e56907..a7c72d1a8 100644 --- a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiTerminalProvider.java +++ b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiTerminalProvider.java @@ -17,6 +17,7 @@ import java.util.regex.Pattern; import org.fusesource.jansi.AnsiConsole; +import org.fusesource.jansi.internal.Kernel32; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.Terminal; @@ -79,6 +80,20 @@ public static boolean isAtLeast(int major, int minor) { return JANSI_MAJOR_VERSION > major || JANSI_MAJOR_VERSION == major && JANSI_MINOR_VERSION >= minor; } + public static void verifyAtLeast(int major, int minor) { + if (!isAtLeast(major, minor)) { + throw new UnsupportedOperationException("An old version of Jansi is loaded from " + + Kernel32.class + .getClassLoader() + .getResource(Kernel32.class.getName().replace('.', '/') + ".class")); + } + } + + public JansiTerminalProvider() { + verifyAtLeast(1, 17); + checkIsSystemStream(SystemStream.Output); + } + @Override public String name() { return TerminalBuilder.PROP_PROVIDER_JANSI; @@ -93,29 +108,29 @@ public Pty current(SystemStream systemStream) throws IOException { } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { // Solaris is not supported by jansi // return SolarisNativePty.current(); + throw new UnsupportedOperationException("Unsupported platform " + osName); } else if (osName.startsWith("FreeBSD")) { - if (isAtLeast(1, 16)) { - return FreeBsdNativePty.current(this, systemStream); - } + return FreeBsdNativePty.current(this, systemStream); + } else { + throw new UnsupportedOperationException("Unsupported platform " + osName); } - throw new UnsupportedOperationException(); } public Pty open(Attributes attributes, Size size) throws IOException { - if (isAtLeast(1, 16)) { - String osName = System.getProperty("os.name"); - if (osName.startsWith("Linux")) { - return LinuxNativePty.open(this, attributes, size); - } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { - return OsXNativePty.open(this, attributes, size); - } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { - // Solaris is not supported by jansi - // return SolarisNativePty.current(); - } else if (osName.startsWith("FreeBSD")) { - return FreeBsdNativePty.open(this, attributes, size); - } + String osName = System.getProperty("os.name"); + if (osName.startsWith("Linux")) { + return LinuxNativePty.open(this, attributes, size); + } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { + return OsXNativePty.open(this, attributes, size); + } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { + // Solaris is not supported by jansi + // return SolarisNativePty.current(); + throw new UnsupportedOperationException("Unsupported platform " + osName); + } else if (osName.startsWith("FreeBSD")) { + return FreeBsdNativePty.open(this, attributes, size); + } else { + throw new UnsupportedOperationException("Unsupported platform " + osName); } - throw new UnsupportedOperationException(); } @Override @@ -148,15 +163,10 @@ public Terminal winSysTerminal( boolean paused, SystemStream systemStream) throws IOException { - if (isAtLeast(1, 12)) { - JansiWinSysTerminal terminal = JansiWinSysTerminal.createTerminal( - this, systemStream, name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused); - if (!isAtLeast(1, 16)) { - terminal.disableScrolling(); - } - return terminal; - } - throw new UnsupportedOperationException(); + JansiWinSysTerminal terminal = JansiWinSysTerminal.createTerminal( + this, systemStream, name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused); + terminal.disableScrolling(); + return terminal; } public Terminal posixSysTerminal( @@ -192,22 +202,18 @@ public Terminal newTerminal( @Override public boolean isSystemStream(SystemStream stream) { try { - if (OSUtils.IS_WINDOWS) { - return isWindowsSystemStream(stream); - } else { - return isPosixSystemStream(stream); - } + return checkIsSystemStream(stream); } catch (Throwable t) { return false; } } - public boolean isWindowsSystemStream(SystemStream stream) { - return JansiWinSysTerminal.isWindowsSystemStream(stream); - } - - public boolean isPosixSystemStream(SystemStream stream) { - return JansiNativePty.isPosixSystemStream(stream); + private boolean checkIsSystemStream(SystemStream stream) { + if (OSUtils.IS_WINDOWS) { + return JansiWinSysTerminal.isWindowsSystemStream(stream); + } else { + return JansiNativePty.isPosixSystemStream(stream); + } } @Override diff --git a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinConsoleWriter.java b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinConsoleWriter.java index 6a4811d05..9b2e8eac9 100644 --- a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinConsoleWriter.java +++ b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinConsoleWriter.java @@ -10,10 +10,10 @@ import java.io.IOException; -import org.fusesource.jansi.internal.Kernel32; import org.jline.terminal.impl.AbstractWindowsConsoleWriter; import static org.fusesource.jansi.internal.Kernel32.WriteConsoleW; +import static org.jline.terminal.impl.jansi.win.WindowsSupport.getLastErrorMessage; class JansiWinConsoleWriter extends AbstractWindowsConsoleWriter { @@ -27,7 +27,7 @@ public JansiWinConsoleWriter(long console) { @Override protected void writeConsole(char[] text, int len) throws IOException { if (WriteConsoleW(console, text, len, writtenChars, 0) == 0) { - throw new IOException("Failed to write to console: " + Kernel32.getLastErrorMessage()); + throw new IOException("Failed to write to console: " + getLastErrorMessage()); } } } diff --git a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinSysTerminal.java b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinSysTerminal.java index 42b666eeb..8c0ed497b 100644 --- a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinSysTerminal.java +++ b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinSysTerminal.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.io.Writer; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.function.IntConsumer; import org.fusesource.jansi.internal.Kernel32; @@ -28,10 +27,7 @@ import org.jline.utils.InfoCmp; import org.jline.utils.OSUtils; -import static org.fusesource.jansi.internal.Kernel32.FORMAT_MESSAGE_FROM_SYSTEM; -import static org.fusesource.jansi.internal.Kernel32.FormatMessageW; import static org.fusesource.jansi.internal.Kernel32.GetConsoleScreenBufferInfo; -import static org.fusesource.jansi.internal.Kernel32.GetLastError; import static org.fusesource.jansi.internal.Kernel32.GetStdHandle; import static org.fusesource.jansi.internal.Kernel32.INVALID_HANDLE_VALUE; import static org.fusesource.jansi.internal.Kernel32.STD_ERROR_HANDLE; @@ -39,6 +35,7 @@ import static org.fusesource.jansi.internal.Kernel32.STD_OUTPUT_HANDLE; import static org.fusesource.jansi.internal.Kernel32.WaitForSingleObject; import static org.fusesource.jansi.internal.Kernel32.readConsoleInputHelper; +import static org.jline.terminal.impl.jansi.win.WindowsSupport.getLastErrorMessage; public class JansiWinSysTerminal extends AbstractWindowsTerminal { @@ -294,16 +291,4 @@ public void disableScrolling() { strings.remove(InfoCmp.Capability.delete_line); strings.remove(InfoCmp.Capability.parm_delete_line); } - - static String getLastErrorMessage() { - int errorCode = GetLastError(); - return getErrorMessage(errorCode); - } - - static String getErrorMessage(int errorCode) { - int bufferSize = 160; - byte[] data = new byte[bufferSize]; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, errorCode, 0, data, bufferSize, null); - return new String(data, StandardCharsets.UTF_16LE).trim(); - } } diff --git a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/WindowsAnsiWriter.java b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/WindowsAnsiWriter.java index 3e9155d23..e07cf9331 100644 --- a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/WindowsAnsiWriter.java +++ b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/WindowsAnsiWriter.java @@ -11,11 +11,11 @@ import java.io.IOException; import java.io.Writer; -import org.fusesource.jansi.internal.Kernel32; import org.jline.utils.AnsiWriter; import org.jline.utils.Colors; import static org.fusesource.jansi.internal.Kernel32.*; +import static org.jline.terminal.impl.jansi.win.WindowsSupport.getLastErrorMessage; /** * A Windows ANSI escape processor, that uses JNA to access native platform @@ -81,7 +81,7 @@ public WindowsAnsiWriter(Writer out) throws IOException { private void getConsoleInfo() throws IOException { out.flush(); if (GetConsoleScreenBufferInfo(console, info) == 0) { - throw new IOException("Could not get the screen info: " + Kernel32.getLastErrorMessage()); + throw new IOException("Could not get the screen info: " + getLastErrorMessage()); } if (negative) { info.attributes = invertAttributeColors(info.attributes); @@ -103,7 +103,7 @@ private void applyAttribute() throws IOException { attributes = invertAttributeColors(attributes); } if (SetConsoleTextAttribute(console, attributes) == 0) { - throw new IOException(Kernel32.getLastErrorMessage()); + throw new IOException(getLastErrorMessage()); } } @@ -121,7 +121,7 @@ private void applyCursorPosition() throws IOException { info.cursorPosition.x = (short) Math.max(0, Math.min(info.size.x - 1, info.cursorPosition.x)); info.cursorPosition.y = (short) Math.max(0, Math.min(info.size.y - 1, info.cursorPosition.y)); if (SetConsoleCursorPosition(console, info.cursorPosition.copy()) == 0) { - throw new IOException(Kernel32.getLastErrorMessage()); + throw new IOException(getLastErrorMessage()); } } @@ -359,7 +359,7 @@ protected void processInsertLine(int optionInt) throws IOException { info.attributes = originalColors; info.unicodeChar = ' '; if (ScrollConsoleScreenBuffer(console, scroll, scroll, org, info) == 0) { - throw new IOException(Kernel32.getLastErrorMessage()); + throw new IOException(getLastErrorMessage()); } } @@ -375,7 +375,7 @@ protected void processDeleteLine(int optionInt) throws IOException { info.attributes = originalColors; info.unicodeChar = ' '; if (ScrollConsoleScreenBuffer(console, scroll, scroll, org, info) == 0) { - throw new IOException(Kernel32.getLastErrorMessage()); + throw new IOException(getLastErrorMessage()); } } diff --git a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/WindowsSupport.java b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/WindowsSupport.java new file mode 100644 index 000000000..05c516deb --- /dev/null +++ b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/WindowsSupport.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jansi.win; + +import java.nio.charset.StandardCharsets; + +import static org.fusesource.jansi.internal.Kernel32.FORMAT_MESSAGE_FROM_SYSTEM; +import static org.fusesource.jansi.internal.Kernel32.FormatMessageW; +import static org.fusesource.jansi.internal.Kernel32.GetLastError; + +class WindowsSupport { + + public static String getLastErrorMessage() { + int errorCode = GetLastError(); + int bufferSize = 160; + byte[] data = new byte[bufferSize]; + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, errorCode, 0, data, bufferSize, null); + return new String(data, StandardCharsets.UTF_16LE).trim(); + } +} diff --git a/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaNativePty.java b/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaNativePty.java index c39feb4f9..dae3170a5 100644 --- a/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaNativePty.java +++ b/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaNativePty.java @@ -190,6 +190,9 @@ public static String posixSystemStreamName(SystemStream stream) { private static boolean isatty(int fd) { if (Platform.isMac()) { + if (Platform.is64Bit() && Platform.isARM()) { + throw new UnsupportedOperationException("Unsupported platform mac-aarch64"); + } return OsXNativePty.isatty(fd) == 1; } else if (Platform.isLinux()) { return LinuxNativePty.isatty(fd) == 1; @@ -204,6 +207,9 @@ private static boolean isatty(int fd) { private static String ttyname(int fd) { if (Platform.isMac()) { + if (Platform.is64Bit() && Platform.isARM()) { + throw new UnsupportedOperationException("Unsupported platform mac-aarch64"); + } return OsXNativePty.ttyname(fd); } else if (Platform.isLinux()) { return LinuxNativePty.ttyname(fd); diff --git a/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaTerminalProvider.java b/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaTerminalProvider.java index bd0e9405f..5c4f5cf3f 100644 --- a/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaTerminalProvider.java +++ b/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaTerminalProvider.java @@ -26,6 +26,11 @@ import org.jline.utils.OSUtils; public class JnaTerminalProvider implements TerminalProvider { + + public JnaTerminalProvider() { + checkSystemStream(SystemStream.Output); + } + @Override public String name() { return TerminalBuilder.PROP_PROVIDER_JNA; @@ -106,22 +111,18 @@ public Terminal newTerminal( @Override public boolean isSystemStream(SystemStream stream) { try { - if (OSUtils.IS_WINDOWS) { - return isWindowsSystemStream(stream); - } else { - return isPosixSystemStream(stream); - } + return checkSystemStream(stream); } catch (Throwable t) { return false; } } - public boolean isWindowsSystemStream(SystemStream stream) { - return JnaWinSysTerminal.isWindowsSystemStream(stream); - } - - public boolean isPosixSystemStream(SystemStream stream) { - return JnaNativePty.isPosixSystemStream(stream); + private boolean checkSystemStream(SystemStream stream) { + if (OSUtils.IS_WINDOWS) { + return JnaWinSysTerminal.isWindowsSystemStream(stream); + } else { + return JnaNativePty.isPosixSystemStream(stream); + } } @Override