// 
// Decompiled by Procyon v0.6.0
// 

package org.jline.terminal;

import java.util.Collections;
import java.util.Collection;
import java.util.HashSet;
import java.util.ServiceLoader;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.stream.Collector;
import java.util.function.Function;
import java.util.Comparator;
import java.util.Arrays;
import java.util.ArrayList;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Iterator;
import java.util.List;
import org.jline.terminal.spi.TerminalExt;
import org.jline.terminal.impl.DumbTerminalProvider;
import org.jline.terminal.impl.AbstractTerminal;
import org.jline.terminal.spi.TerminalProvider;
import org.jline.utils.OSUtils;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jline.terminal.spi.SystemStream;
import java.util.Map;
import org.jline.terminal.impl.AbstractPosixTerminal;
import org.jline.utils.Log;
import java.nio.charset.UnsupportedCharsetException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.io.OutputStream;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Set;

public final class TerminalBuilder
{
    public static final String PROP_ENCODING = "org.jline.terminal.encoding";
    public static final String PROP_STDIN_ENCODING = "org.jline.terminal.stdin.encoding";
    public static final String PROP_STDOUT_ENCODING = "org.jline.terminal.stdout.encoding";
    public static final String PROP_STDERR_ENCODING = "org.jline.terminal.stderr.encoding";
    public static final String PROP_CODEPAGE = "org.jline.terminal.codepage";
    public static final String PROP_TYPE = "org.jline.terminal.type";
    public static final String PROP_PROVIDER = "org.jline.terminal.provider";
    public static final String PROP_PROVIDERS = "org.jline.terminal.providers";
    public static final String PROP_PROVIDER_FFM = "ffm";
    public static final String PROP_PROVIDER_JNI = "jni";
    public static final String PROP_PROVIDER_JANSI = "jansi";
    public static final String PROP_PROVIDER_JNA = "jna";
    public static final String PROP_PROVIDER_EXEC = "exec";
    public static final String PROP_PROVIDER_DUMB = "dumb";
    public static final String PROP_PROVIDERS_DEFAULT;
    public static final String PROP_FFM = "org.jline.terminal.ffm";
    public static final String PROP_JNI = "org.jline.terminal.jni";
    public static final String PROP_JANSI = "org.jline.terminal.jansi";
    public static final String PROP_JNA = "org.jline.terminal.jna";
    public static final String PROP_EXEC = "org.jline.terminal.exec";
    public static final String PROP_DUMB = "org.jline.terminal.dumb";
    public static final String PROP_DUMB_COLOR = "org.jline.terminal.dumb.color";
    public static final String PROP_OUTPUT = "org.jline.terminal.output";
    public static final String PROP_OUTPUT_OUT = "out";
    public static final String PROP_OUTPUT_ERR = "err";
    public static final String PROP_OUTPUT_OUT_ERR = "out-err";
    public static final String PROP_OUTPUT_ERR_OUT = "err-out";
    public static final String PROP_OUTPUT_FORCED_OUT = "forced-out";
    public static final String PROP_OUTPUT_FORCED_ERR = "forced-err";
    public static final String PROP_NON_BLOCKING_READS = "org.jline.terminal.pty.nonBlockingReads";
    public static final String PROP_COLOR_DISTANCE = "org.jline.utils.colorDistance";
    public static final String PROP_DISABLE_ALTERNATE_CHARSET = "org.jline.utils.disableAlternateCharset";
    public static final String PROP_FILE_DESCRIPTOR_CREATION_MODE = "org.jline.terminal.pty.fileDescriptorCreationMode";
    public static final String PROP_FILE_DESCRIPTOR_CREATION_MODE_NATIVE = "native";
    public static final String PROP_FILE_DESCRIPTOR_CREATION_MODE_REFLECTION = "reflection";
    public static final String PROP_FILE_DESCRIPTOR_CREATION_MODE_DEFAULT;
    public static final String PROP_REDIRECT_PIPE_CREATION_MODE = "org.jline.terminal.exec.redirectPipeCreationMode";
    public static final String PROP_REDIRECT_PIPE_CREATION_MODE_NATIVE = "native";
    public static final String PROP_REDIRECT_PIPE_CREATION_MODE_REFLECTION = "reflection";
    public static final String PROP_REDIRECT_PIPE_CREATION_MODE_DEFAULT;
    public static final Set<String> DEPRECATED_PROVIDERS;
    public static final String PROP_DISABLE_DEPRECATED_PROVIDER_WARNING = "org.jline.terminal.disableDeprecatedProviderWarning";
    private static final AtomicReference<Terminal> SYSTEM_TERMINAL;
    private static final AtomicReference<Terminal> TERMINAL_OVERRIDE;
    private String name;
    private InputStream in;
    private OutputStream out;
    private String type;
    private Charset encoding;
    private Charset stdinEncoding;
    private Charset stdoutEncoding;
    private Charset stderrEncoding;
    private int codepage;
    private Boolean system;
    private SystemOutput systemOutput;
    private String provider;
    private String providers;
    private Boolean jna;
    private Boolean jansi;
    private Boolean jni;
    private Boolean exec;
    private Boolean ffm;
    private Boolean dumb;
    private Boolean color;
    private Attributes attributes;
    private Size size;
    private boolean nativeSignals;
    private Terminal.SignalHandler signalHandler;
    private boolean paused;
    private static final int UTF8_CODE_PAGE = 65001;
    
    public static Terminal terminal() throws IOException {
        return builder().build();
    }
    
    public static TerminalBuilder builder() {
        return new TerminalBuilder();
    }
    
    private TerminalBuilder() {
        this.nativeSignals = true;
        this.signalHandler = Terminal.SignalHandler.SIG_DFL;
        this.paused = false;
    }
    
    public TerminalBuilder name(final String name) {
        this.name = name;
        return this;
    }
    
    public TerminalBuilder streams(final InputStream in, final OutputStream out) {
        this.in = in;
        this.out = out;
        return this;
    }
    
    public TerminalBuilder system(final boolean system) {
        this.system = system;
        return this;
    }
    
    public TerminalBuilder systemOutput(final SystemOutput systemOutput) {
        this.systemOutput = systemOutput;
        return this;
    }
    
    public TerminalBuilder provider(final String provider) {
        this.provider = provider;
        return this;
    }
    
    public TerminalBuilder providers(final String providers) {
        this.providers = providers;
        return this;
    }
    
    public TerminalBuilder jna(final boolean jna) {
        this.jna = jna;
        return this;
    }
    
    public TerminalBuilder jansi(final boolean jansi) {
        this.jansi = jansi;
        return this;
    }
    
    public TerminalBuilder jni(final boolean jni) {
        this.jni = jni;
        return this;
    }
    
    public TerminalBuilder exec(final boolean exec) {
        this.exec = exec;
        return this;
    }
    
    public TerminalBuilder ffm(final boolean ffm) {
        this.ffm = ffm;
        return this;
    }
    
    public TerminalBuilder dumb(final boolean dumb) {
        this.dumb = dumb;
        return this;
    }
    
    public TerminalBuilder type(final String type) {
        this.type = type;
        return this;
    }
    
    public TerminalBuilder color(final boolean color) {
        this.color = color;
        return this;
    }
    
    public TerminalBuilder encoding(final String encoding) throws UnsupportedCharsetException {
        return this.encoding((encoding != null) ? Charset.forName(encoding) : null);
    }
    
    public TerminalBuilder encoding(final Charset encoding) {
        this.encoding = encoding;
        return this;
    }
    
    public TerminalBuilder stdinEncoding(final String encoding) throws UnsupportedCharsetException {
        return this.stdinEncoding((encoding != null) ? Charset.forName(encoding) : null);
    }
    
    public TerminalBuilder stdinEncoding(final Charset encoding) {
        this.stdinEncoding = encoding;
        return this;
    }
    
    public TerminalBuilder stdoutEncoding(final String encoding) throws UnsupportedCharsetException {
        return this.stdoutEncoding((encoding != null) ? Charset.forName(encoding) : null);
    }
    
    public TerminalBuilder stdoutEncoding(final Charset encoding) {
        this.stdoutEncoding = encoding;
        return this;
    }
    
    public TerminalBuilder stderrEncoding(final String encoding) throws UnsupportedCharsetException {
        return this.stderrEncoding((encoding != null) ? Charset.forName(encoding) : null);
    }
    
    public TerminalBuilder stderrEncoding(final Charset encoding) {
        this.stderrEncoding = encoding;
        return this;
    }
    
    @Deprecated
    public TerminalBuilder codepage(final int codepage) {
        this.codepage = codepage;
        return this;
    }
    
    public TerminalBuilder attributes(final Attributes attributes) {
        this.attributes = attributes;
        return this;
    }
    
    public TerminalBuilder size(final Size size) {
        this.size = size;
        return this;
    }
    
    public TerminalBuilder nativeSignals(final boolean nativeSignals) {
        this.nativeSignals = nativeSignals;
        return this;
    }
    
    public TerminalBuilder signalHandler(final Terminal.SignalHandler signalHandler) {
        this.signalHandler = signalHandler;
        return this;
    }
    
    public TerminalBuilder paused(final boolean paused) {
        this.paused = paused;
        return this;
    }
    
    public Terminal build() throws IOException {
        final Terminal override = TerminalBuilder.TERMINAL_OVERRIDE.get();
        final Terminal terminal = (override != null) ? override : this.doBuild();
        if (override != null) {
            Log.debug(() -> "Overriding terminal with global value set by TerminalBuilder.setTerminalOverride");
        }
        Log.debug(() -> "Using terminal " + terminal.getClass().getSimpleName());
        if (terminal instanceof AbstractPosixTerminal) {
            Log.debug(() -> "Using pty " + ((AbstractPosixTerminal)terminal).getPty().getClass().getSimpleName());
        }
        return terminal;
    }
    
    private Terminal doBuild() throws IOException {
        String name = this.name;
        if (name == null) {
            name = "JLine terminal";
        }
        final Charset encoding = this.computeEncoding();
        final Charset stdinEncoding = this.computeStdinEncoding();
        final Charset stdoutEncoding = this.computeStdoutEncoding();
        final Charset stderrEncoding = this.computeStderrEncoding();
        String type = this.computeType();
        String provider = this.provider;
        if (provider == null) {
            provider = System.getProperty("org.jline.terminal.provider", null);
        }
        final boolean forceDumb = "dumb".equals(type) || (type != null && type.startsWith("dumb-color")) || (provider != null && provider.equals("dumb"));
        Boolean dumb = this.dumb;
        if (dumb == null) {
            dumb = getBoolean("org.jline.terminal.dumb", null);
        }
        final IllegalStateException exception = new IllegalStateException("Unable to create a terminal");
        final List<TerminalProvider> providers = this.getProviders(provider, exception);
        Terminal terminal = null;
        if ((this.system != null && this.system) || (this.system == null && this.in == null && this.out == null)) {
            if (this.system != null && ((this.in != null && !this.in.equals(System.in)) || (this.out != null && !this.out.equals(System.out) && !this.out.equals(System.err)))) {
                throw new IllegalArgumentException("Cannot create a system terminal using non System streams");
            }
            if (this.attributes != null || this.size != null) {
                Log.warn("Attributes and size fields are ignored when creating a system terminal");
            }
            final SystemOutput systemOutput = this.computeSystemOutput();
            final Map<SystemStream, Boolean> system = Stream.of(SystemStream.values()).collect(Collectors.toMap(stream -> stream, stream -> providers.stream().anyMatch(p -> p.isSystemStream(stream))));
            final SystemStream systemStream = this.select(system, systemOutput);
            if (!forceDumb && system.get(SystemStream.Input) && systemStream != null) {
                if (this.attributes != null || this.size != null) {
                    Log.warn("Attributes and size fields are ignored when creating a system terminal");
                }
                final boolean ansiPassThrough = OSUtils.IS_CONEMU;
                if ((OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) && "xterm".equals(type) && this.type == null && System.getProperty("org.jline.terminal.type") == null) {
                    type = "xterm-256color";
                }
                for (final TerminalProvider prov : providers) {
                    if (terminal == null) {
                        try {
                            final Charset outputEncoding = (systemStream == SystemStream.Error) ? stderrEncoding : stdoutEncoding;
                            terminal = prov.sysTerminal(name, type, ansiPassThrough, encoding, stdinEncoding, outputEncoding, this.nativeSignals, this.signalHandler, this.paused, systemStream);
                        }
                        catch (final Throwable t) {
                            Log.debug("Error creating " + prov.name() + " based terminal: ", t.getMessage(), t);
                            exception.addSuppressed(t);
                        }
                    }
                }
                if (terminal == null && OSUtils.IS_WINDOWS && providers.isEmpty() && (dumb == null || !dumb)) {
                    throw new IllegalStateException("Unable to create a system terminal. On Windows, either JLine's native libraries, JNA or Jansi library is required.  Make sure to add one of those in the classpath.", exception);
                }
            }
            if (terminal instanceof AbstractTerminal) {
                final AbstractTerminal t2 = (AbstractTerminal)terminal;
                if (TerminalBuilder.SYSTEM_TERMINAL.compareAndSet(null, t2)) {
                    final Throwable t;
                    t2.setOnClose(() -> TerminalBuilder.SYSTEM_TERMINAL.compareAndSet(t, null));
                }
                else {
                    exception.addSuppressed(new IllegalStateException("A system terminal is already running. Make sure to use the created system Terminal on the LineReaderBuilder if you're using one or that previously created system Terminals have been correctly closed."));
                    terminal.close();
                    terminal = null;
                }
            }
            if (terminal == null && (forceDumb || dumb == null || dumb)) {
                if (!forceDumb && dumb == null) {
                    if (Log.isDebugEnabled()) {
                        Log.warn("input is tty: " + system.get(SystemStream.Input));
                        Log.warn("output is tty: " + system.get(SystemStream.Output));
                        Log.warn("error is tty: " + system.get(SystemStream.Error));
                        Log.warn("Creating a dumb terminal", exception);
                    }
                    else {
                        Log.warn("Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)");
                    }
                }
                type = this.getDumbTerminalType(dumb, systemStream);
                final Charset outputEncoding2 = (systemStream == SystemStream.Error) ? stderrEncoding : stdoutEncoding;
                terminal = new DumbTerminalProvider().sysTerminal(name, type, false, encoding, stdinEncoding, outputEncoding2, this.nativeSignals, this.signalHandler, this.paused, systemStream);
                if (OSUtils.IS_WINDOWS) {
                    final Attributes attr = terminal.getAttributes();
                    attr.setInputFlag(Attributes.InputFlag.IGNCR, true);
                    terminal.setAttributes(attr);
                }
            }
        }
        else {
            for (final TerminalProvider prov2 : providers) {
                if (terminal == null) {
                    try {
                        terminal = prov2.newTerminal(name, type, this.in, this.out, encoding, stdinEncoding, stdoutEncoding, this.signalHandler, this.paused, this.attributes, this.size);
                    }
                    catch (final Throwable t3) {
                        Log.debug("Error creating " + prov2.name() + " based terminal: ", t3.getMessage(), t3);
                        exception.addSuppressed(t3);
                    }
                }
            }
        }
        if (terminal == null) {
            throw exception;
        }
        if (terminal instanceof TerminalExt) {
            final TerminalExt te = (TerminalExt)terminal;
            if (TerminalBuilder.DEPRECATED_PROVIDERS.contains(te.getProvider().name()) && !getBoolean("org.jline.terminal.disableDeprecatedProviderWarning", false)) {
                Log.warn("The terminal provider " + te.getProvider().name() + " has been deprecated, check your configuration. This warning can be disabled by setting the system property " + "org.jline.terminal.disableDeprecatedProviderWarning" + " to true.");
            }
        }
        return terminal;
    }
    
    private String getDumbTerminalType(final Boolean dumb, final SystemStream systemStream) {
        Boolean color = this.color;
        if (color == null) {
            color = getBoolean("org.jline.terminal.dumb.color", null);
        }
        if (dumb == null) {
            if (color == null) {
                final String emacs = System.getenv("INSIDE_EMACS");
                if (emacs != null && emacs.contains("comint")) {
                    color = true;
                }
            }
            if (color == null) {
                final String ideHome = System.getenv("IDE_HOME");
                if (ideHome != null) {
                    color = true;
                }
                else {
                    final String command = getParentProcessCommand();
                    if (command != null && command.endsWith("/idea")) {
                        color = true;
                    }
                }
            }
            if (color == null) {
                color = (systemStream != null && System.getenv("TERM") != null);
            }
        }
        else if (color == null) {
            color = false;
        }
        return color ? "dumb-color" : "dumb";
    }
    
    public SystemOutput computeSystemOutput() {
        SystemOutput systemOutput = null;
        if (this.out != null) {
            if (this.out.equals(System.out)) {
                systemOutput = SystemOutput.SysOut;
            }
            else if (this.out.equals(System.err)) {
                systemOutput = SystemOutput.SysErr;
            }
        }
        if (systemOutput == null) {
            systemOutput = this.systemOutput;
        }
        if (systemOutput == null) {
            final String str = System.getProperty("org.jline.terminal.output");
            if (str != null) {
                final String lowerCase = str.trim().toLowerCase(Locale.ROOT);
                switch (lowerCase) {
                    case "out": {
                        systemOutput = SystemOutput.SysOut;
                        break;
                    }
                    case "err": {
                        systemOutput = SystemOutput.SysErr;
                        break;
                    }
                    case "out-err": {
                        systemOutput = SystemOutput.SysOutOrSysErr;
                        break;
                    }
                    case "err-out": {
                        systemOutput = SystemOutput.SysErrOrSysOut;
                        break;
                    }
                    case "forced-out": {
                        systemOutput = SystemOutput.ForcedSysOut;
                        break;
                    }
                    case "forced-err": {
                        systemOutput = SystemOutput.ForcedSysErr;
                        break;
                    }
                    default: {
                        Log.debug("Unsupported value for org.jline.terminal.output: " + str + ". Supported values are: " + String.join(", ", "out", "err", "out-err", "err-out") + ".");
                        break;
                    }
                }
            }
        }
        if (systemOutput == null) {
            systemOutput = SystemOutput.SysOutOrSysErr;
        }
        return systemOutput;
    }
    
    public String computeType() {
        String type = this.type;
        if (type == null) {
            type = System.getProperty("org.jline.terminal.type");
        }
        if (type == null) {
            type = System.getenv("TERM");
        }
        return type;
    }
    
    public Charset computeEncoding() {
        Charset encoding = this.encoding;
        if (encoding == null) {
            final String charsetName = System.getProperty("org.jline.terminal.encoding");
            if (charsetName != null && Charset.isSupported(charsetName)) {
                encoding = Charset.forName(charsetName);
            }
        }
        if (encoding == null) {
            int codepage = this.codepage;
            if (codepage <= 0) {
                final String str = System.getProperty("org.jline.terminal.codepage");
                if (str != null) {
                    codepage = Integer.parseInt(str);
                }
            }
            if (codepage >= 0) {
                encoding = getCodepageCharset(codepage);
            }
            else {
                encoding = StandardCharsets.UTF_8;
            }
        }
        return encoding;
    }
    
    public Charset computeStdinEncoding() {
        return this.computeSpecificEncoding(this.stdinEncoding, "org.jline.terminal.stdin.encoding", "stdin.encoding");
    }
    
    public Charset computeStdoutEncoding() {
        return this.computeSpecificEncoding(this.stdoutEncoding, "org.jline.terminal.stdout.encoding", "stdout.encoding");
    }
    
    public Charset computeStderrEncoding() {
        return this.computeSpecificEncoding(this.stderrEncoding, "org.jline.terminal.stderr.encoding", "stderr.encoding");
    }
    
    private Charset computeSpecificEncoding(final Charset specificEncoding, final String jlineProperty, final String standardProperty) {
        Charset encoding = specificEncoding;
        if (encoding == null) {
            String charsetName = System.getProperty(jlineProperty);
            if (charsetName != null && Charset.isSupported(charsetName)) {
                encoding = Charset.forName(charsetName);
            }
            if (encoding == null) {
                charsetName = System.getProperty(standardProperty);
                if (charsetName != null && Charset.isSupported(charsetName)) {
                    encoding = Charset.forName(charsetName);
                }
            }
        }
        if (encoding == null) {
            encoding = this.computeEncoding();
        }
        return encoding;
    }
    
    public List<TerminalProvider> getProviders(final String provider, final IllegalStateException exception) {
        final List<TerminalProvider> providers = new ArrayList<TerminalProvider>();
        this.checkProvider(provider, exception, providers, this.ffm, "org.jline.terminal.ffm", "ffm");
        this.checkProvider(provider, exception, providers, this.jni, "org.jline.terminal.jni", "jni");
        this.checkProvider(provider, exception, providers, this.jansi, "org.jline.terminal.jansi", "jansi");
        this.checkProvider(provider, exception, providers, this.jna, "org.jline.terminal.jna", "jna");
        this.checkProvider(provider, exception, providers, this.exec, "org.jline.terminal.exec", "exec");
        final List<String> order = Arrays.asList(((this.providers != null) ? this.providers : System.getProperty("org.jline.terminal.providers", TerminalBuilder.PROP_PROVIDERS_DEFAULT)).split(","));
        providers.sort(Comparator.comparing(l -> {
            final int idx = order.indexOf(l.name());
            return Integer.valueOf((idx >= 0) ? idx : Integer.MAX_VALUE);
        }));
        final String names = providers.stream().map((Function<? super Object, ?>)TerminalProvider::name).collect((Collector<? super Object, ?, String>)Collectors.joining(", "));
        Log.debug("Available providers: " + names);
        return providers;
    }
    
    private void checkProvider(final String provider, final IllegalStateException exception, final List<TerminalProvider> providers, final Boolean load, final String property, final String name) {
        Boolean doLoad = (provider != null) ? Boolean.valueOf(name.equals(provider)) : load;
        if (doLoad == null) {
            doLoad = getBoolean(property, true);
        }
        if (doLoad) {
            try {
                final TerminalProvider prov = TerminalProvider.load(name);
                prov.isSystemStream(SystemStream.Output);
                providers.add(prov);
            }
            catch (final Throwable t) {
                Log.debug("Unable to load " + name + " provider: ", t);
                exception.addSuppressed(t);
            }
        }
    }
    
    private SystemStream select(final Map<SystemStream, Boolean> system, final SystemOutput systemOutput) {
        switch (systemOutput.ordinal()) {
            case 0: {
                return select(system, SystemStream.Output);
            }
            case 1: {
                return select(system, SystemStream.Error);
            }
            case 2: {
                return select(system, SystemStream.Output, SystemStream.Error);
            }
            case 3: {
                return select(system, SystemStream.Error, SystemStream.Output);
            }
            case 4: {
                return SystemStream.Output;
            }
            case 5: {
                return SystemStream.Error;
            }
            default: {
                return null;
            }
        }
    }
    
    private static SystemStream select(final Map<SystemStream, Boolean> system, final SystemStream... streams) {
        for (final SystemStream s : streams) {
            if (system.get(s)) {
                return s;
            }
        }
        return null;
    }
    
    private static String getParentProcessCommand() {
        try {
            final Class<?> phClass = Class.forName("java.lang.ProcessHandle");
            final Object current = phClass.getMethod("current", (Class<?>[])new Class[0]).invoke(null, new Object[0]);
            final Object parent = ((Optional)phClass.getMethod("parent", (Class<?>[])new Class[0]).invoke(current, new Object[0])).orElse(null);
            final Method infoMethod = phClass.getMethod("info", (Class<?>[])new Class[0]);
            final Object info = infoMethod.invoke(parent, new Object[0]);
            final Object command = ((Optional)infoMethod.getReturnType().getMethod("command", (Class<?>[])new Class[0]).invoke(info, new Object[0])).orElse(null);
            return (String)command;
        }
        catch (final Throwable t) {
            return null;
        }
    }
    
    private static Boolean getBoolean(final String name, final Boolean def) {
        try {
            final String str = System.getProperty(name);
            if (str != null) {
                return Boolean.parseBoolean(str);
            }
        }
        catch (final IllegalArgumentException | NullPointerException ex) {}
        return def;
    }
    
    private static <S> S load(final Class<S> clazz) {
        return ServiceLoader.load(clazz, clazz.getClassLoader()).iterator().next();
    }
    
    private static Charset getCodepageCharset(final int codepage) {
        if (codepage == 65001) {
            return StandardCharsets.UTF_8;
        }
        final String charsetMS = "ms" + codepage;
        if (Charset.isSupported(charsetMS)) {
            return Charset.forName(charsetMS);
        }
        final String charsetCP = "cp" + codepage;
        if (Charset.isSupported(charsetCP)) {
            return Charset.forName(charsetCP);
        }
        return Charset.defaultCharset();
    }
    
    @Deprecated
    public static void setTerminalOverride(final Terminal terminal) {
        TerminalBuilder.TERMINAL_OVERRIDE.set(terminal);
    }
    
    static {
        PROP_PROVIDERS_DEFAULT = String.join(",", "ffm", "jni", "jansi", "jna", "exec");
        PROP_FILE_DESCRIPTOR_CREATION_MODE_DEFAULT = String.join(",", "reflection", "native");
        PROP_REDIRECT_PIPE_CREATION_MODE_DEFAULT = String.join(",", "reflection", "native");
        DEPRECATED_PROVIDERS = Collections.unmodifiableSet((Set<? extends String>)new HashSet<String>(Arrays.asList("jna", "jansi")));
        SYSTEM_TERMINAL = new AtomicReference<Terminal>();
        TERMINAL_OVERRIDE = new AtomicReference<Terminal>();
    }
    
    public enum SystemOutput
    {
        SysOut, 
        SysErr, 
        SysOutOrSysErr, 
        SysErrOrSysOut, 
        ForcedSysOut, 
        ForcedSysErr;
    }
}
