diff --git a/jansi/pom.xml b/jansi/pom.xml index 9f93434d..05104c74 100644 --- a/jansi/pom.xml +++ b/jansi/pom.xml @@ -144,6 +144,7 @@ + org.fusesource.jansi.AnsiMain org.fusesource.jansi, org.fusesource.jansi.internal diff --git a/jansi/src/main/java/org/fusesource/jansi/AnsiConsole.java b/jansi/src/main/java/org/fusesource/jansi/AnsiConsole.java index 01d95b02..0b7ae030 100644 --- a/jansi/src/main/java/org/fusesource/jansi/AnsiConsole.java +++ b/jansi/src/main/java/org/fusesource/jansi/AnsiConsole.java @@ -43,17 +43,20 @@ public class AnsiConsole { public static final PrintStream system_err = System.err; public static final PrintStream err; - private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win"); + static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win"); - private static final boolean IS_CYGWIN = IS_WINDOWS + static final boolean IS_CYGWIN = IS_WINDOWS && System.getenv("PWD") != null && System.getenv("PWD").startsWith("/") && !"cygwin".equals(System.getenv("TERM")); - private static final boolean IS_MINGW = IS_WINDOWS + static final boolean IS_MINGW = IS_WINDOWS && System.getenv("MSYSTEM") != null && System.getenv("MSYSTEM").startsWith("MINGW"); + private static JansiOutputType jansiOutputType; + static final JansiOutputType JANSI_STDOUT_TYPE; + static final JansiOutputType JANSI_STDERR_TYPE; static { String charset = Charset.defaultCharset().name(); if (IS_WINDOWS && !IS_CYGWIN && !IS_MINGW) { @@ -67,7 +70,9 @@ public class AnsiConsole { } try { out = new PrintStream(wrapOutputStream(system_out), false, charset); + JANSI_STDOUT_TYPE = jansiOutputType; err = new PrintStream(wrapErrorOutputStream(system_err), false, charset); + JANSI_STDERR_TYPE = jansiOutputType; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } @@ -99,12 +104,14 @@ public static OutputStream wrapOutputStream(final OutputStream stream, int filen // If the jansi.passthrough property is set, then don't interpret // any of the ansi sequences. if (Boolean.getBoolean("jansi.passthrough")) { + jansiOutputType = JansiOutputType.PASSTHROUGH; return stream; } // If the jansi.strip property is set, then we just strip the // the ansi escapes. if (Boolean.getBoolean("jansi.strip")) { + jansiOutputType = JansiOutputType.STRIP_ANSI; return new AnsiOutputStream(stream); } @@ -112,6 +119,7 @@ public static OutputStream wrapOutputStream(final OutputStream stream, int filen // On windows we know the console does not interpret ANSI codes.. try { + jansiOutputType = JansiOutputType.WINDOWS; return new WindowsAnsiOutputStream(stream); } catch (Throwable ignore) { // this happens when JNA is not in the path.. or @@ -119,6 +127,7 @@ public static OutputStream wrapOutputStream(final OutputStream stream, int filen } // Use the ANSIOutputStream to strip out the ANSI escape sequences. + jansiOutputType = JansiOutputType.STRIP_ANSI; return new AnsiOutputStream(stream); } @@ -130,6 +139,7 @@ public static OutputStream wrapOutputStream(final OutputStream stream, int filen // If we can detect that stdout is not a tty.. then setup // to strip the ANSI sequences.. if (!forceColored && isatty(fileno) == 0) { + jansiOutputType = JansiOutputType.STRIP_ANSI; return new AnsiOutputStream(stream); } } catch (Throwable ignore) { @@ -140,6 +150,7 @@ public static OutputStream wrapOutputStream(final OutputStream stream, int filen // By default we assume your Unix tty can handle ANSI codes. // Just wrap it up so that when we get closed, we reset the // attributes. + jansiOutputType = JansiOutputType.RESET_ANSI_AT_CLOSE; return new FilterOutputStream(stream) { @Override public void close() throws IOException { @@ -198,4 +209,14 @@ synchronized public static void systemUninstall() { } } + /** + * Type of output installed by AnsiConsole. + */ + enum JansiOutputType { + PASSTHROUGH, // just pass through, ANSI escape codes are supposed to be supported by terminal + STRIP_ANSI, // strip ANSI escape codes (since not a terminal) + WINDOWS, // detect ANSI escape codes and transform Jansi-supported ones into a Windows API to get desired effect + // (since ANSI escape codes are not natively supported by Windows terminals like cmd.exe or PowerShell) + RESET_ANSI_AT_CLOSE // like pass through but reset ANSI attributes when closing + }; } diff --git a/jansi/src/main/java/org/fusesource/jansi/AnsiMain.java b/jansi/src/main/java/org/fusesource/jansi/AnsiMain.java new file mode 100644 index 00000000..9641a481 --- /dev/null +++ b/jansi/src/main/java/org/fusesource/jansi/AnsiMain.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2009-2017 the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.fusesource.jansi; + +import static org.fusesource.jansi.Ansi.ansi; + +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.Reader; +import java.util.Properties; + +import org.fusesource.hawtjni.runtime.Library; +import org.fusesource.jansi.internal.CLibrary; +import static org.fusesource.jansi.internal.CLibrary.isatty; + +/** + * Main class for the library, providing executable jar to diagnose Jansi setup. + */ +public class AnsiMain { + public static void main(String... args) throws IOException { + System.out.println("Jansi " + getJansiVersion() + + " (Jansi native " + getPomPropertiesVersion("org.fusesource.jansi/jansi-native") + + ", HawtJNI runtime " + getPomPropertiesVersion("org.fusesource.hawtjni/hawtjni-runtime") + ")"); + + System.out.println(); + + // info on native library + System.out.println("library.jansi.path= " + System.getProperty("library.jansi.path", "")); + System.out.println("library.jansi.version= " + System.getProperty("library.jansi.version", "")); + Library lib = new Library("jansi", CLibrary.class); + lib.load(); + /* TODO enable when upgrading hawtjni-runtime to 1.16 with https://github.com/fusesource/hawtjni/pull/36 + System.out.println("path: " + lib.getNativeLibraryPath()); + if (lib.getNativeLibrarySourceUrl() != null) { + System.out.println("source: " + lib.getNativeLibrarySourceUrl()); + }*/ + + System.out.println(); + + System.out.println("os.name= " + System.getProperty("os.name") + ", " + + "os.version= " + System.getProperty("os.version") + ", " + + "os.arch= " + System.getProperty("os.arch")); + System.out.println("file.encoding= " + System.getProperty("file.encoding")); + System.out.println("java.version= " + System.getProperty("java.version") + ", " + + "java.vendor= " + System.getProperty("java.vendor") + "," + + " java.home= " + System.getProperty("java.home")); + + System.out.println(); + + System.out.println("jansi.passthrough= " + Boolean.getBoolean("jansi.passthrough")); + System.out.println("jansi.strip= " + Boolean.getBoolean("jansi.strip")); + System.out.println("jansi.force= " + Boolean.getBoolean("jansi.force")); + System.out.println(Ansi.DISABLE + "= " + Boolean.getBoolean(Ansi.DISABLE)); + + System.out.println(); + + System.out.println("IS_WINDOWS= " + AnsiConsole.IS_WINDOWS); + if (AnsiConsole.IS_WINDOWS) { + System.out.println("IS_CYGWIN= " + AnsiConsole.IS_CYGWIN); + System.out.println("IS_MINGW= " + AnsiConsole.IS_MINGW); + } + + System.out.println(); + + diagnoseTty(false); // System.out + diagnoseTty(true); // System.err + + AnsiConsole.systemInstall(); + + System.out.println(); + + System.out.println("Jansi System.out mode: " + AnsiConsole.JANSI_STDOUT_TYPE); + System.out.println("Jansi System.err mode: " + AnsiConsole.JANSI_STDERR_TYPE); + + try { + System.out.println(); + + testAnsi(false); + testAnsi(true); + + if (args.length == 0) { + printJansiLogoDemo(); + return; + } + + System.out.println(); + + if (args.length == 1) { + File f = new File(args[0]); + if (f.exists()) + { + // write file content + System.out.println(ansi().bold().a("\"" + args[0] + "\" content:").reset()); + writeFileContent(f); + return; + } + } + + // write args without Jansi then with Jansi AnsiConsole + System.out.println(ansi().bold().a("original args:").reset()); + int i = 1; + for (String arg: args) { + AnsiConsole.system_out.print(i++ + ": "); + AnsiConsole.system_out.println(arg); + } + + System.out.println(ansi().bold().a("Jansi filtered args:").reset()); + i = 1; + for (String arg: args) { + System.out.print(i++ + ": "); + System.out.println(arg); + } + } finally { + AnsiConsole.systemUninstall(); + } + } + + private static String getJansiVersion() { + Package p = AnsiMain.class.getPackage(); + return ( p == null ) ? null : p.getImplementationVersion(); + } + + private static void diagnoseTty(boolean stderr) { + int fd = stderr ? CLibrary.STDERR_FILENO : CLibrary.STDOUT_FILENO; + int isatty = isatty(fd); + + System.out.println("isatty(STD" + (stderr ? "ERR" : "OUT") + "_FILENO)= " + isatty + ", System." + + (stderr ? "err" : "out") + " " + ((isatty == 0) ? "is *NOT*" : "is") + " a terminal"); + } + + private static void testAnsi(boolean stderr) { + @SuppressWarnings( "resource" ) + PrintStream s = stderr ? System.err : System.out; + s.print("test on System." + (stderr ? "err" : "out") + ":"); + for(Ansi.Color c: Ansi.Color.values()) { + s.print(" " + ansi().fg(c) + c + ansi().reset()); + } + s.println(); + s.print(" bright:"); + for(Ansi.Color c: Ansi.Color.values()) { + s.print(" " + ansi().fgBright(c) + c + ansi().reset()); + } + s.println(); + s.print(" bold:"); + for(Ansi.Color c: Ansi.Color.values()) { + s.print(" " + ansi().bold().fg(c) + c + ansi().reset()); + } + s.println(); + } + + private static String getPomPropertiesVersion(String path) throws IOException { + InputStream in = AnsiMain.class.getResourceAsStream("/META-INF/maven/" + path + "/pom.properties"); + if (in == null) { + return null; + } + try { + Properties p = new Properties(); + p.load(in); + return p.getProperty("version"); + } finally { + closeQuietly(in); + } + } + + private static void printJansiLogoDemo() throws IOException { + Reader in = new InputStreamReader(AnsiMain.class.getResourceAsStream("jansi.txt"), "UTF-8"); + try { + char[] buf = new char[1024]; + while (in.read(buf) >= 0) { + System.out.print(buf); + } + } finally { + closeQuietly(in); + } + } + + private static void writeFileContent(File f) throws IOException { + InputStream in = new FileInputStream(f); + try { + byte[] buf = new byte[1024]; + int l = 0; + while ((l = in.read(buf)) >= 0) { + System.out.write(buf, 0, l); + } + } finally { + closeQuietly(in); + } + } + + private static void closeQuietly(Closeable c) { + try { + c.close(); + } catch (IOException ioe) { + ioe.printStackTrace(System.err); + } + } +} diff --git a/jansi/src/main/resources/org/fusesource/jansi/jansi.txt b/jansi/src/main/resources/org/fusesource/jansi/jansi.txt new file mode 100644 index 00000000..a2e24faf --- /dev/null +++ b/jansi/src/main/resources/org/fusesource/jansi/jansi.txt @@ -0,0 +1,8 @@ +[?7h +┌──┐┌─────┐ ┌─────┐ ┌──────┬──┐ +│██├┘█████└┬┘█████└┬┘██████│̦│ +┌──┐ │██│██▄▄▄██│██┌─┐██│██▄▄▄▄ │▄▄│ +│▒▒└─┘▒█│▒█┌─┐▒█│▒█│ │▒█│ ▀▀▀▀▒█│▒█│ +└┐▓▓▓▓▓┌┤▓▓│ │▓▓│▓▓│ │▓▓│▀▓▓▓▓▓▀│▓▓│ +└─────┘└──┘ └──┴──┘ └──┴───────┴──┘ + diff --git a/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample3.java b/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample3.java deleted file mode 100644 index 58ff88fc..00000000 --- a/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample3.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2009-2017 the original author(s). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.fusesource.jansi; - -import java.io.IOException; -import java.nio.charset.Charset; - -/** - * Quick test to show issues with non-ascii characters on Windows when using AnsiConsole. - * Not really battle-tested for any Windows encoding, but you can update - */ -public class AnsiConsoleExample3 { - - private final static String NON_ASCII = "éèä"; - private AnsiConsoleExample3() { - } - - public static void main(String[] args) throws IOException { - System.out.println("Platform: " + System.getProperty("os.name")); - System.out.println("Platform encoding: " + Charset.defaultCharset()); - System.out.println("System.out: " + NON_ASCII); - AnsiConsole.out.println("AnsiConsole.out: " + NON_ASCII); - } - -} \ No newline at end of file