Skip to content

Commit 40631bd

Browse files
authored
Merge pull request #95 from hboutemy/printstream
use PrintStream to avoid encoding issues
2 parents 3b2eab6 + d340856 commit 40631bd

File tree

5 files changed

+1503
-22
lines changed

5 files changed

+1503
-22
lines changed

jansi/src/main/java/org/fusesource/jansi/AnsiConsole.java

+103-22
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
*/
1616
package org.fusesource.jansi;
1717

18-
import org.fusesource.jansi.internal.Kernel32;
19-
2018
import static org.fusesource.jansi.internal.CLibrary.STDERR_FILENO;
2119
import static org.fusesource.jansi.internal.CLibrary.STDOUT_FILENO;
2220
import static org.fusesource.jansi.internal.CLibrary.isatty;
@@ -25,15 +23,14 @@
2523
import java.io.IOException;
2624
import java.io.OutputStream;
2725
import java.io.PrintStream;
28-
import java.io.UnsupportedEncodingException;
29-
import java.nio.charset.Charset;
3026
import java.util.Locale;
3127

3228
/**
3329
* Provides consistent access to an ANSI aware console PrintStream.
3430
*
3531
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
3632
* @since 1.0
33+
* @see #wrapPrintStream(PrintStream, int)
3734
*/
3835
public class AnsiConsole {
3936

@@ -58,31 +55,18 @@ public class AnsiConsole {
5855
static final JansiOutputType JANSI_STDOUT_TYPE;
5956
static final JansiOutputType JANSI_STDERR_TYPE;
6057
static {
61-
String charset = Charset.defaultCharset().name();
62-
if (IS_WINDOWS && !IS_CYGWIN && !IS_MINGW) {
63-
int codepage = Kernel32.GetConsoleOutputCP();
64-
//http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
65-
if (Charset.isSupported("ms" + codepage)) {
66-
charset = "ms" + codepage;
67-
} else if (Charset.isSupported("cp" + codepage)) {
68-
charset = "cp" + codepage;
69-
}
70-
}
71-
try {
72-
out = new PrintStream(wrapOutputStream(system_out), false, charset);
73-
JANSI_STDOUT_TYPE = jansiOutputType;
74-
err = new PrintStream(wrapErrorOutputStream(system_err), false, charset);
75-
JANSI_STDERR_TYPE = jansiOutputType;
76-
} catch (UnsupportedEncodingException e) {
77-
throw new RuntimeException(e);
78-
}
58+
out = wrapSystemOut(system_out);
59+
JANSI_STDOUT_TYPE = jansiOutputType;
60+
err = wrapSystemErr(system_err);
61+
JANSI_STDERR_TYPE = jansiOutputType;
7962
}
8063

8164
private static int installed;
8265

8366
private AnsiConsole() {
8467
}
8568

69+
@Deprecated
8670
public static OutputStream wrapOutputStream(final OutputStream stream) {
8771
try {
8872
return wrapOutputStream(stream, STDOUT_FILENO);
@@ -91,6 +75,15 @@ public static OutputStream wrapOutputStream(final OutputStream stream) {
9175
}
9276
}
9377

78+
public static PrintStream wrapSystemOut(final PrintStream ps) {
79+
try {
80+
return wrapPrintStream(ps, STDOUT_FILENO);
81+
} catch (Throwable ignore) {
82+
return wrapPrintStream(ps, 1);
83+
}
84+
}
85+
86+
@Deprecated
9487
public static OutputStream wrapErrorOutputStream(final OutputStream stream) {
9588
try {
9689
return wrapOutputStream(stream, STDERR_FILENO);
@@ -99,6 +92,15 @@ public static OutputStream wrapErrorOutputStream(final OutputStream stream) {
9992
}
10093
}
10194

95+
public static PrintStream wrapSystemErr(final PrintStream ps) {
96+
try {
97+
return wrapPrintStream(ps, STDERR_FILENO);
98+
} catch (Throwable ignore) {
99+
return wrapPrintStream(ps, 2);
100+
}
101+
}
102+
103+
@Deprecated
102104
public static OutputStream wrapOutputStream(final OutputStream stream, int fileno) {
103105

104106
// If the jansi.passthrough property is set, then don't interpret
@@ -161,13 +163,91 @@ public void close() throws IOException {
161163
};
162164
}
163165

166+
/**
167+
* Wrap PrintStream applying rules in following order:<ul>
168+
* <li>if <code>jansi.passthrough</code> is <code>true</code>, don't wrap but just passthrough (console is
169+
* expected to natively support ANSI escape codes),</li>
170+
* <li>if <code>jansi.strip</code> is <code>true</code>, just strip ANSI escape codes inconditionally,</li>
171+
* <li>if OS is Windows and terminal is not Cygwin or Mingw, wrap as WindowsAnsiPrintStream to process ANSI escape codes,</li>
172+
* <li>if file descriptor is a terminal (see <code>isatty(int)</code>) or <code>jansi.force</code> is <code>true</code>,
173+
* just passthrough,</li>
174+
* <li>else strip ANSI escape codes (not a terminal).</li>
175+
* </ul>
176+
*
177+
* @param ps original PrintStream to wrap
178+
* @param fileno file descriptor
179+
* @return wrapped PrintStream depending on OS and system properties
180+
*/
181+
public static PrintStream wrapPrintStream(final PrintStream ps, int fileno) {
182+
183+
// If the jansi.passthrough property is set, then don't interpret
184+
// any of the ansi sequences.
185+
if (Boolean.getBoolean("jansi.passthrough")) {
186+
jansiOutputType = JansiOutputType.PASSTHROUGH;
187+
return ps;
188+
}
189+
190+
// If the jansi.strip property is set, then we just strip the
191+
// the ansi escapes.
192+
if (Boolean.getBoolean("jansi.strip")) {
193+
jansiOutputType = JansiOutputType.STRIP_ANSI;
194+
return new AnsiPrintStream(ps);
195+
}
196+
197+
if (IS_WINDOWS && !IS_CYGWIN && !IS_MINGW) {
198+
199+
// On windows we know the console does not interpret ANSI codes..
200+
try {
201+
jansiOutputType = JansiOutputType.WINDOWS;
202+
return new WindowsAnsiPrintStream(ps);
203+
} catch (Throwable ignore) {
204+
// this happens when JNA is not in the path.. or
205+
// this happens when the stdout is being redirected to a file.
206+
}
207+
208+
// Use the AnsiPrintStream to strip out the ANSI escape sequences.
209+
jansiOutputType = JansiOutputType.STRIP_ANSI;
210+
return new AnsiPrintStream(ps);
211+
}
212+
213+
// We must be on some Unix variant, including Cygwin or MSYS(2) on Windows...
214+
try {
215+
// If the jansi.force property is set, then we force to output
216+
// the ansi escapes for piping it into ansi color aware commands (e.g. less -r)
217+
boolean forceColored = Boolean.getBoolean("jansi.force");
218+
// If we can detect that stdout is not a tty.. then setup
219+
// to strip the ANSI sequences..
220+
if (!forceColored && isatty(fileno) == 0) {
221+
jansiOutputType = JansiOutputType.STRIP_ANSI;
222+
return new AnsiPrintStream(ps);
223+
}
224+
} catch (Throwable ignore) {
225+
// These errors happen if the JNI lib is not available for your platform.
226+
// But since we are on ANSI friendly platform, assume the user is on the console.
227+
}
228+
229+
// By default we assume your Unix tty can handle ANSI codes.
230+
// Just wrap it up so that when we get closed, we reset the
231+
// attributes.
232+
jansiOutputType = JansiOutputType.RESET_ANSI_AT_CLOSE;
233+
return new FilterPrintStream(ps) {
234+
@Override
235+
public void close() {
236+
ps.print(AnsiPrintStream.RESET_CODE);
237+
ps.flush();
238+
super.close();
239+
}
240+
};
241+
}
242+
164243
/**
165244
* If the standard out natively supports ANSI escape codes, then this just
166245
* returns System.out, otherwise it will provide an ANSI aware PrintStream
167246
* which strips out the ANSI escape sequences or which implement the escape
168247
* sequences.
169248
*
170249
* @return a PrintStream which is ANSI aware.
250+
* @see #wrapPrintStream(PrintStream, int)
171251
*/
172252
public static PrintStream out() {
173253
return out;
@@ -180,6 +260,7 @@ public static PrintStream out() {
180260
* sequences.
181261
*
182262
* @return a PrintStream which is ANSI aware.
263+
* @see #wrapPrintStream(PrintStream, int)
183264
*/
184265
public static PrintStream err() {
185266
return err;

0 commit comments

Comments
 (0)