All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sshtools.synergy.ssh.TerminalModes Maven / Gradle / Ivy

package com.sshtools.synergy.ssh;

/*-
 * #%L
 * Client API
 * %%
 * Copyright (C) 2002 - 2024 JADAPTIVE Limited
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

import com.sshtools.common.util.ByteArrayReader;
import com.sshtools.common.util.ByteArrayWriter;

/**
 * 

When a client requests a pseudo terminal it informs the server of * any terminal modes that it knows of. This is typically used in * situations where advance terminal configuration is required but * it can also be used to perform simple configuration such as turning * off character echo.

* *

NOTE: the server may ignore some of the modes set if it does not * support them.

* *
 * var session = ssh.openSessionChannel();
 * session.requestPseudoTerminal("vt100", 80, 24, 0, 0, TerminalModesBuilder.create().
 * 			// Turning off echo
 * 			withMode(TerminalModes.Mode.ECHO, false).
 * 			// Setting the Input/Output baud rate
 * 			withMode(TerminalModes.Mode.TTY_OP_ISPEED, 38400).
 * 			withMode(TerminalModes.Mode.TTY_OP_OSPEED, 38400).
 * 			build());
 * 
* * @author Brett Smith * @author Lee David Painter */ public final class TerminalModes { /** * Enumeration of supported modes. */ public enum Mode { /** * Interrupt character; 255 if none. */ VINTR, /** * The quit character (sends SIGQUIT signal on POSIX systems). */ VQUIT, /** * Erase the character to left of the cursor. */ VERASE, /** * Kill the current input line. */ VKILL, /** * End-of-file character (sends EOF from the terminal). */ VEOF, /** * End-of-line character in addition to carriage return and/or linefeed. */ VEOL, /** * Additional end-of-line character. */ VEOL2, /** * Continues paused output (normally control-Q). */ VSTART, /** * Pauses output (normally control-S).. */ VSTOP, /** * Suspends the current program. */ VSUSP, /** * Another suspend character. */ VDSUSP, /** * Reprints the current input line. */ VREPRINT, /** * Erases a word left of cursor. */ VWERASE, /** * Enter the next character typed literally, even if it is a special character */ VLNEXT, /** * Character to flush output. */ VFLUSH, /** * Switch to a different shell layer. */ VSWITCH, /** * Prints system status line (load, command, pid, etc). */ VSTATUS, /** * Toggles the flushing of terminal output. */ VDISCARD, /** * The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE, * and 1 if it is TRUE. */ IGNPAR, /** * Mark parity and framing errors. */ PARMRK, /** * Enable checking of parity errors. */ INPCK, /** * Strip 8th bit off characters. */ ISTRIP, /** * Map NL into CR on input. */ INLCR, /** * Ignore CR on input. */ IGNCR, /** * Map CR to NL on input. */ ICRNL, /** * Translate uppercase characters to lowercase. */ IUCLC, /** * Enable output flow control. */ IXON, /** * Any char will restart after stop. */ IXANY, /** * Enable input flow control. */ IXOFF, /** * Ring bell on input queue full. */ IMAXBEL, /** * Output is assumed to be UTF-8 */ IUTF8, /** * Enable signals INTR, QUIT, [D]SUSP. */ ISIG, /** * Canonicalize input lines. */ ICANON, /** * Enable input and output of uppercase characters by preceding their lowercase * equivalents with "\". */ XCASE, /** * Enable echoing. */ ECHO, /** * Visually erase chars. */ ECHOE, /** * Kill character discards current line. */ ECHOK, /** * Echo NL even if ECHO is off. */ ECHONL, /** * Don't flush after interrupt. */ NOFLSH, /** * Stop background jobs from output.TerminalModes */ TOSTOP, /** * Enable extensions. */ IEXTEN, /** * Echo control characters as ^(Char). */ ECHOCTL, /** * Visual erase for line kill. */ ECHOKE, /** * Retype pending input. */ PENDIN, /** * Enable output processing. */ OPOST, /** * Convert lowercase to uppercase. */ OLCUC, /** * Map NL to CR-NL. */ ONLCR, /** * Translate carriage return to newline (output). */ OCRNL, /** * Translate newline to carriage return-newline (output). */ ONOCR, /** * Newline performs a carriage return (output). */ ONLRET, /** * 7 bit mode. */ CS7, /** * 8 bit mode. */ CS8, /** * Parity enable. */ PARENB, /** * Odd parity, else even. */ PARODD, /** * Specifies the input baud rate in bits per second. */ TTY_OP_ISPEED, /** * Specifies the output baud rate in bits per second. */ TTY_OP_OSPEED; public int toMode() { switch(this) { case VINTR: return 1; case VQUIT: return 2; case VERASE: return 3; case VKILL: return 4; case VEOF: return 5; case VEOL: return 6; case VEOL2: return 7; case VSTART: return 8; case VSTOP: return 9; case VSUSP: return 10; case VDSUSP: return 11; case VREPRINT: return 12; case VWERASE: return 13; case VLNEXT: return 14; case VFLUSH: return 15; case VSWITCH: return 16; case VSTATUS: return 17; case VDISCARD: return 18; case IGNPAR: return 30; case PARMRK: return 31; case INPCK: return 32; case ISTRIP: return 33; case INLCR: return 34; case IGNCR: return 35; case ICRNL: return 36; case IUCLC: return 37; case IXON: return 38; case IXANY: return 39; case IXOFF: return 40; case IMAXBEL: return 41; case IUTF8: return 42; case ISIG: return 50; case ICANON: return 51; case XCASE: return 52; case ECHO: return 53; case ECHOE: return 54; case ECHOK: return 55; case ECHONL: return 56; case NOFLSH: return 57; case TOSTOP: return 58; case IEXTEN: return 59; case ECHOCTL: return 60; case ECHOKE: return 61; case PENDIN: return 62; case OPOST: return 70; case OLCUC: return 71; case ONLCR: return 72; case OCRNL: return 73; case ONOCR: return 74; case ONLRET: return 75; case CS7: return 90; case CS8: return 91; case PARENB: return 92; case PARODD: return 93; case TTY_OP_ISPEED: return 128; case TTY_OP_OSPEED: return 129; } throw new IllegalStateException(); } public static Mode fromMode(int mode) { switch(mode) { case 1: return VINTR; case 2: return VQUIT; case 3: return VERASE; case 4: return VKILL; case 5: return VEOF; case 6: return VEOL; case 7: return VEOL2; case 8: return VSTART; case 9: return VSTOP; case 10: return VSUSP; case 11: return VDSUSP; case 12: return VREPRINT; case 13: return VWERASE; case 14: return VLNEXT; case 15: return VFLUSH; case 16: return VSWITCH; case 17: return VSTATUS; case 18: return VDISCARD; case 30: return IGNPAR; case 31: return PARMRK; case 32: return INPCK; case 33: return ISTRIP; case 34: return INLCR; case 35: return INLCR; case 36: return ICRNL; case 37: return IUCLC; case 38: return IXON; case 39: return IXANY; case 40: return IXOFF; case 41: return IMAXBEL; case 42: return IUTF8; case 50: return ISIG; case 51: return ICANON; case 52: return XCASE; case 53: return ECHO; case 54: return ECHOE; case 55: return ECHOK; case 56: return ECHONL; case 57: return NOFLSH; case 58: return TOSTOP; case 59: return IEXTEN; case 60: return ECHOCTL; case 61: return ECHOKE; case 62: return PENDIN; case 70: return OPOST; case 71: return OLCUC; case 72: return ONLCR; case 73: return OCRNL; case 74: return ONOCR; case 75: return ONLRET; case 90: return CS7; case 91: return CS8; case 92: return PARENB; case 93: return PARODD; case 128: return TTY_OP_ISPEED; case 129: return TTY_OP_OSPEED; } throw new IllegalStateException("" + mode); } } /** * Builds {@link TerminalModes}. * *

* You can reuse an instance of this class providing that you do not want to * change any of the modes. If you do want to change modes you can call the * reset method to clear out old modes. *

*/ public final static class TerminalModesBuilder { private final Map codes = new LinkedHashMap<>(); /** * Clear all modes set in this builder. * * @return this for chaining */ public TerminalModesBuilder reset() { codes.clear(); return this; } /** * Set one or more modes to true, i.e. a value of 1. * * @param modes modes * @return this for chaining */ public TerminalModesBuilder withModes(Mode... modes) { Arrays.asList(modes).forEach(m -> withMode(m, true)); return this; } /** * Set one or more modes to false, i.e. a value of 0. * * @param modes modes * @return this for chaining */ public TerminalModesBuilder withoutModes(Mode... modes) { Arrays.asList(modes).forEach(m -> withMode(m, false)); return this; } /** * Set a boolean mode. * * @param mode mode * @param value value to set * @return this for chaining */ public TerminalModesBuilder withMode(int mode, boolean value) { return withMode(Mode.fromMode(mode), value); } /** * Set a boolean mode. * * @param mode mode * @param value value to set * @return this for chaining */ public TerminalModesBuilder withMode(Mode mode, boolean value) { return withMode(mode, value ? 1 : 0); } /** * Set an integer mode. * * @param mode mode * @param value value to set * @return this for chaining */ public TerminalModesBuilder withMode(int mode, int value) { return withMode(Mode.fromMode(mode), value); } /** * Set an integer mode. * * @param mode mode * @param value value to set * @return this for chaining */ public TerminalModesBuilder withMode(Mode mode, int value) { codes.put(mode, value); return this; } /** * Set a boolean>code> mode to true. * * @param mode mode * @return this for chaining */ public TerminalModesBuilder withMode(int mode) { return withMode(mode, true); } /** * Set a boolean>code> mode to false. * * @param mode mode * @return this for chaining */ public TerminalModesBuilder withoutMode(int mode) { return withMode(mode, false); } /** * Create a new {@link TerminalModesBuilder} * * @return builder */ public static TerminalModesBuilder create() { return new TerminalModesBuilder(); } /** * Build a new {@link TerminalModes}. * * @return modes * @throws IOException */ public TerminalModes build() { return new TerminalModes(this); } public TerminalModesBuilder fromBytes(byte[] modes) { return read(new ByteArrayReader(modes)); } public TerminalModesBuilder read(ByteArrayReader reader) { while(true) { /* Hrm why does ByteArrayWriter throw IOExceptions? */ try { var mode = reader.read(); if(mode < 1) break; withMode(Mode.fromMode(mode), (int)reader.readInt()); } catch (IOException e) { throw new UncheckedIOException(e); } } return this; } } private final Map modes; private TerminalModes(TerminalModesBuilder builder) { this.modes = Collections.unmodifiableMap(new LinkedHashMap<>(builder.codes)); } /** * Get all of the modes and their values as a map. * * @return modes */ public Map modes() { return modes; } /** * Returns the encoded modes for use by the {@link SshSession}. * @return byte[] */ public byte[] toByteArray() { var baw = new ByteArrayWriter(); write(baw); return baw.toByteArray(); } /** * Get the value of a numeric node, or zero if it does not exist * * @param mode * @param default * @return value */ public int get(Mode mode) { return get(mode, 0); } /** * Get the value of a number node, or a default if it does not exist. * * @param mode * @param default * @return value */ public int get(Mode mode, int defaultValue) { return modes.getOrDefault(mode, defaultValue); } /** * Get if a mode is set (i.e. is non-zero). If the mode is not in the set, * false will be returned. * * @param mode mode * @return mode is preent and non-zero */ public boolean is(Mode mode) { return is(mode, false); } /** * Get if a mode is set (i.e. is non-zero) or a default if it does not exist. * * @param mode mode * @param defaultValue default value * @return mode is non-zero */ public boolean is(Mode mode, boolean defaultValue) { var m = modes.get(mode); return m == null ? defaultValue : m > 0; } /** * Get if a mode is present in the set. * * @param mode mode * @return mode is present and non-zero */ public boolean present(Mode mode) { return modes.containsKey(mode); } /** * Write the modes to the writer. * * @param writer */ public void write(ByteArrayWriter writer) { modes.forEach((k, v) -> { writer.write(k.toMode()); try { writer.writeInt(v); } catch (IOException e) { /* Hrm why does ByteArrayWriter throw IOExceptions? */ throw new UncheckedIOException(e); } }); } }