001/* 002 * Copyright (C) 2009-2017 the original author(s). 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fusesource.jansi; 017 018import org.fusesource.jansi.internal.Kernel32; 019 020import static org.fusesource.jansi.internal.CLibrary.STDERR_FILENO; 021import static org.fusesource.jansi.internal.CLibrary.STDOUT_FILENO; 022import static org.fusesource.jansi.internal.CLibrary.isatty; 023 024import java.io.FilterOutputStream; 025import java.io.IOException; 026import java.io.OutputStream; 027import java.io.PrintStream; 028import java.io.UnsupportedEncodingException; 029import java.nio.charset.Charset; 030import java.util.Locale; 031 032/** 033 * Provides consistent access to an ANSI aware console PrintStream. 034 * 035 * @author <a href="http://hiramchirino.com">Hiram Chirino</a> 036 * @since 1.0 037 */ 038public class AnsiConsole { 039 040 public static final PrintStream system_out = System.out; 041 public static final PrintStream out; 042 043 public static final PrintStream system_err = System.err; 044 public static final PrintStream err; 045 046 private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win"); 047 048 private static final boolean IS_CYGWIN = IS_WINDOWS 049 && System.getenv("PWD") != null 050 && System.getenv("PWD").startsWith("/") 051 && !"cygwin".equals(System.getenv("TERM")); 052 053 private static final boolean IS_MINGW = IS_WINDOWS 054 && System.getenv("MSYSTEM") != null 055 && System.getenv("MSYSTEM").startsWith("MINGW"); 056 057 static { 058 String charset = Charset.defaultCharset().name(); 059 if (IS_WINDOWS && !IS_CYGWIN && !IS_MINGW) { 060 int codepage = Kernel32.GetConsoleOutputCP(); 061 //http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html 062 if (Charset.isSupported("ms" + codepage)) { 063 charset = "ms" + codepage; 064 } else if (Charset.isSupported("cp" + codepage)) { 065 charset = "cp" + codepage; 066 } 067 } 068 try { 069 out = new PrintStream(wrapOutputStream(system_out), false, charset); 070 err = new PrintStream(wrapErrorOutputStream(system_err), false, charset); 071 } catch (UnsupportedEncodingException e) { 072 throw new RuntimeException(e); 073 } 074 } 075 076 private static int installed; 077 078 private AnsiConsole() { 079 } 080 081 public static OutputStream wrapOutputStream(final OutputStream stream) { 082 try { 083 return wrapOutputStream(stream, STDOUT_FILENO); 084 } catch (Throwable ignore) { 085 return wrapOutputStream(stream, 1); 086 } 087 } 088 089 public static OutputStream wrapErrorOutputStream(final OutputStream stream) { 090 try { 091 return wrapOutputStream(stream, STDERR_FILENO); 092 } catch (Throwable ignore) { 093 return wrapOutputStream(stream, 2); 094 } 095 } 096 097 public static OutputStream wrapOutputStream(final OutputStream stream, int fileno) { 098 099 // If the jansi.passthrough property is set, then don't interpret 100 // any of the ansi sequences. 101 if (Boolean.getBoolean("jansi.passthrough")) { 102 return stream; 103 } 104 105 // If the jansi.strip property is set, then we just strip the 106 // the ansi escapes. 107 if (Boolean.getBoolean("jansi.strip")) { 108 return new AnsiOutputStream(stream); 109 } 110 111 if (IS_WINDOWS && !IS_CYGWIN && !IS_MINGW) { 112 113 // On windows we know the console does not interpret ANSI codes.. 114 try { 115 return new WindowsAnsiOutputStream(stream); 116 } catch (Throwable ignore) { 117 // this happens when JNA is not in the path.. or 118 // this happens when the stdout is being redirected to a file. 119 } 120 121 // Use the ANSIOutputStream to strip out the ANSI escape sequences. 122 return new AnsiOutputStream(stream); 123 } 124 125 // We must be on some Unix variant, including Cygwin or MSYS(2) on Windows... 126 try { 127 // If the jansi.force property is set, then we force to output 128 // the ansi escapes for piping it into ansi color aware commands (e.g. less -r) 129 boolean forceColored = Boolean.getBoolean("jansi.force"); 130 // If we can detect that stdout is not a tty.. then setup 131 // to strip the ANSI sequences.. 132 if (!forceColored && isatty(fileno) == 0) { 133 return new AnsiOutputStream(stream); 134 } 135 } catch (Throwable ignore) { 136 // These errors happen if the JNI lib is not available for your platform. 137 // But since we are on ANSI friendly platform, assume the user is on the console. 138 } 139 140 // By default we assume your Unix tty can handle ANSI codes. 141 // Just wrap it up so that when we get closed, we reset the 142 // attributes. 143 return new FilterOutputStream(stream) { 144 @Override 145 public void close() throws IOException { 146 write(AnsiOutputStream.RESET_CODE); 147 flush(); 148 super.close(); 149 } 150 }; 151 } 152 153 /** 154 * If the standard out natively supports ANSI escape codes, then this just 155 * returns System.out, otherwise it will provide an ANSI aware PrintStream 156 * which strips out the ANSI escape sequences or which implement the escape 157 * sequences. 158 * 159 * @return a PrintStream which is ANSI aware. 160 */ 161 public static PrintStream out() { 162 return out; 163 } 164 165 /** 166 * If the standard out natively supports ANSI escape codes, then this just 167 * returns System.err, otherwise it will provide an ANSI aware PrintStream 168 * which strips out the ANSI escape sequences or which implement the escape 169 * sequences. 170 * 171 * @return a PrintStream which is ANSI aware. 172 */ 173 public static PrintStream err() { 174 return err; 175 } 176 177 /** 178 * Install Console.out to System.out. 179 */ 180 synchronized static public void systemInstall() { 181 installed++; 182 if (installed == 1) { 183 System.setOut(out); 184 System.setErr(err); 185 } 186 } 187 188 /** 189 * undo a previous {@link #systemInstall()}. If {@link #systemInstall()} was called 190 * multiple times, it {@link #systemUninstall()} must call the same number of times before 191 * it is actually uninstalled. 192 */ 193 synchronized public static void systemUninstall() { 194 installed--; 195 if (installed == 0) { 196 System.setOut(system_out); 197 System.setErr(system_err); 198 } 199 } 200 201}