16
16
package org .fusesource .jansi ;
17
17
18
18
import java .io .BufferedOutputStream ;
19
+ import java .io .FileDescriptor ;
20
+ import java .io .FileOutputStream ;
19
21
import java .io .FilterOutputStream ;
20
22
import java .io .IOException ;
21
23
import java .io .OutputStream ;
22
24
import java .io .OutputStreamWriter ;
23
25
import java .io .PrintStream ;
26
+ import java .io .UnsupportedEncodingException ;
24
27
import java .lang .reflect .Field ;
25
28
import java .nio .charset .Charset ;
26
29
import java .util .Locale ;
27
30
28
31
import static org .fusesource .jansi .internal .CLibrary .STDERR_FILENO ;
29
32
import static org .fusesource .jansi .internal .CLibrary .STDOUT_FILENO ;
30
33
import static org .fusesource .jansi .internal .CLibrary .isatty ;
34
+ import static org .fusesource .jansi .internal .Kernel32 .GetConsoleMode ;
35
+ import static org .fusesource .jansi .internal .Kernel32 .GetStdHandle ;
36
+ import static org .fusesource .jansi .internal .Kernel32 .STD_ERROR_HANDLE ;
37
+ import static org .fusesource .jansi .internal .Kernel32 .STD_OUTPUT_HANDLE ;
38
+ import static org .fusesource .jansi .internal .Kernel32 .SetConsoleMode ;
31
39
32
40
/**
33
41
* Provides consistent access to an ANSI aware console PrintStream or an ANSI codes stripping PrintStream
@@ -56,25 +64,36 @@ public class AnsiConsole {
56
64
* <a href="https://conemu.github.io">ConEmu</a> ANSI X3.64 support enabled,
57
65
* used by <a href="https://cmder.net/">cmder</a>
58
66
*/
67
+ @ Deprecated
59
68
static final boolean IS_CON_EMU_ANSI = "ON" .equals (System .getenv ("ConEmuANSI" ));
60
69
61
70
static final boolean IS_CYGWIN = IS_WINDOWS
62
71
&& System .getenv ("PWD" ) != null
63
- && System .getenv ("PWD" ).startsWith ("/" )
64
- && !"cygwin" .equals (System .getenv ("TERM" ));
72
+ && System .getenv ("PWD" ).startsWith ("/" );
65
73
74
+ @ Deprecated
66
75
static final boolean IS_MINGW_XTERM = IS_WINDOWS
67
76
&& System .getenv ("MSYSTEM" ) != null
68
77
&& System .getenv ("MSYSTEM" ).startsWith ("MINGW" )
69
78
&& "xterm" .equals (System .getenv ("TERM" ));
70
79
80
+ static final boolean IS_MSYSTEM = IS_WINDOWS
81
+ && System .getenv ("MSYSTEM" ) != null
82
+ && (System .getenv ("MSYSTEM" ).startsWith ("MINGW" )
83
+ || System .getenv ("MSYSTEM" ).equals ("MSYS" ));
84
+
85
+ static final boolean IS_CONEMU = IS_WINDOWS
86
+ && System .getenv ("ConEmuPID" ) != null ;
87
+
88
+ static final int ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 ;
89
+
71
90
private static JansiOutputType jansiOutputType ;
72
91
static final JansiOutputType JANSI_STDOUT_TYPE ;
73
92
static final JansiOutputType JANSI_STDERR_TYPE ;
74
93
static {
75
- out = wrapSystemOut ( system_out );
94
+ out = ansiSystem ( true );
76
95
JANSI_STDOUT_TYPE = jansiOutputType ;
77
- err = wrapSystemErr ( system_err );
96
+ err = ansiSystem ( false );
78
97
JANSI_STDERR_TYPE = jansiOutputType ;
79
98
}
80
99
@@ -83,6 +102,112 @@ public class AnsiConsole {
83
102
private AnsiConsole () {
84
103
}
85
104
105
+ private static PrintStream ansiSystem (boolean stdout ) {
106
+ OutputStream out = new BufferedNoSyncOutputStream (new FileOutputStream (stdout ? FileDescriptor .out : FileDescriptor .err ));
107
+
108
+ String enc = System .getProperty (stdout ? "sun.stdout.encoding" : "sun.stderr.encoding" );
109
+
110
+ // If the jansi.passthrough property is set, then don't interpret
111
+ // any of the ansi sequences.
112
+ if (Boolean .getBoolean ("jansi.passthrough" )) {
113
+ jansiOutputType = JansiOutputType .PASSTHROUGH ;
114
+ return newPrintStream (out , enc );
115
+ }
116
+
117
+ // If the jansi.strip property is set, then we just strip the
118
+ // the ansi escapes.
119
+ if (Boolean .getBoolean ("jansi.strip" )) {
120
+ jansiOutputType = JansiOutputType .STRIP_ANSI ;
121
+ return new PrintStream (new AnsiNoSyncOutputStream (out , new AnsiProcessor (out )), true );
122
+ }
123
+
124
+ if (IS_WINDOWS ) {
125
+ long console = GetStdHandle (stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE );
126
+ int [] mode = new int [1 ];
127
+ if (GetConsoleMode (console , mode ) != 0
128
+ && SetConsoleMode (console , mode [0 ] | ENABLE_VIRTUAL_TERMINAL_PROCESSING ) != 0 ) {
129
+ jansiOutputType = JansiOutputType .PASSTHROUGH ;
130
+ return newPrintStream (out , enc );
131
+ }
132
+ }
133
+
134
+ if (IS_WINDOWS && !(IS_CON_EMU_ANSI || IS_CYGWIN || IS_MINGW_XTERM )) {
135
+
136
+ // On Windows, when no ANSI-capable terminal is used, we know the console does not natively interpret ANSI
137
+ // codes but we can use jansi-native Kernel32 API for console
138
+ try {
139
+ jansiOutputType = JansiOutputType .WINDOWS ;
140
+ return newPrintStream (new AnsiNoSyncOutputStream (out , new WindowsAnsiProcessor (out , stdout )), enc );
141
+ } catch (Throwable ignore ) {
142
+ // this happens when JNA is not in the path.. or
143
+ // this happens when the stdout is being redirected to a file.
144
+ }
145
+
146
+ // Use the ANSIOutputStream to strip out the ANSI escape sequences.
147
+ jansiOutputType = JansiOutputType .STRIP_ANSI ;
148
+ return newPrintStream (new AnsiNoSyncOutputStream (out , new AnsiProcessor (out )), enc );
149
+ }
150
+
151
+ // We must be on some Unix variant or ANSI-enabled ConEmu, Cygwin or MSYS(2) on Windows...
152
+ try {
153
+ // If the jansi.force property is set, then we force to output
154
+ // the ansi escapes for piping it into ansi color aware commands (e.g. less -r)
155
+ boolean forceColored = Boolean .getBoolean ("jansi.force" );
156
+ // If we can detect that stdout is not a tty.. then setup
157
+ // to strip the ANSI sequences..
158
+ if (!forceColored && isatty (stdout ? 1 : 2 ) == 0 ) {
159
+ jansiOutputType = JansiOutputType .STRIP_ANSI ;
160
+ return newPrintStream (new AnsiNoSyncOutputStream (out , new AnsiProcessor (out )), enc );
161
+ }
162
+ } catch (Throwable ignore ) {
163
+ // These errors happen if the JNI lib is not available for your platform.
164
+ // But since we are on ANSI friendly platform, assume the user is on the console.
165
+ }
166
+
167
+ // By default we assume your Unix tty can handle ANSI codes.
168
+ // Just wrap it up so that when we get closed, we reset the
169
+ // attributes.
170
+ jansiOutputType = JansiOutputType .RESET_ANSI_AT_CLOSE ;
171
+ return newPrintStream (out , enc , AnsiNoSyncOutputStream .RESET_CODE );
172
+ }
173
+
174
+ private static PrintStream newPrintStream (OutputStream out , String enc ) {
175
+ return newPrintStream (out , enc , null );
176
+ }
177
+
178
+ private static PrintStream newPrintStream (OutputStream out , String enc , byte [] reset ) {
179
+ if (enc != null ) {
180
+ try {
181
+ return new ResetAtClosePrintStream (out , enc , reset );
182
+ } catch (UnsupportedEncodingException e ) {
183
+ }
184
+ }
185
+ return new ResetAtClosePrintStream (out , reset );
186
+ }
187
+
188
+ static class ResetAtClosePrintStream extends PrintStream {
189
+
190
+ private final byte [] reset ;
191
+
192
+ public ResetAtClosePrintStream (OutputStream out , byte [] reset ) {
193
+ super (out , true );
194
+ this .reset = reset ;
195
+ }
196
+
197
+ public ResetAtClosePrintStream (OutputStream out , String encoding , byte [] reset ) throws UnsupportedEncodingException {
198
+ super (out , true , encoding );
199
+ this .reset = reset ;
200
+ }
201
+
202
+ @ Override
203
+ public void close () {
204
+ if (reset != null ) {
205
+ write (reset , 0 , reset .length );
206
+ }
207
+ super .close ();
208
+ }
209
+ }
210
+
86
211
@ Deprecated
87
212
public static OutputStream wrapOutputStream (final OutputStream stream ) {
88
213
try {
0 commit comments