/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.felix.shell.remote; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.io.Reader; /** * Class implementing a terminal reader adapter * originally designed for the BeanShell Interpreter. * <p/> * Provides simple line editing (Backspace, Strg-U and a history). */ class TerminalReader extends Reader { protected final InputStream m_in; protected final PrintStream m_out; protected boolean m_echo = false; protected boolean m_eof = false; public TerminalReader(InputStream in, PrintStream out) { m_in = in; m_out = out; }//TerminalReader /** * Tests if this <tt>TerminalReader</tt> will echo * the character input to the terminal. * * @return true if echo, false otherwise. */ public boolean isEcho() { return m_echo; }//isEcho /** * Sets if this <tt>TerminalReader</tt> will echo * the character input to the terminal. * <p/> * This only makes sense in character input mode, * in line mode the terminal will handle editing, * and this is recommended for using a more complex shell. * If you implement your own character based editor, you * might as well change this.<br/> * (Default is false). * * @param echo true if echo, false otherwise. */ public void setEcho(boolean echo) { m_echo = echo; }//setEcho public int read(char[] chars, int off, int len) throws IOException { if (m_eof) { return -1; } for (int i = off; i < off + len; i++) { int ch = m_in.read(); //shortcut for EOT and simple EOF if (ch == EOT || (i == off && ch == -1)) { return -1; } chars[i] = (char) ch; if (ch == -1 || ch == 10 || ch == 13) { m_eof = ch == -1; //store EOF int read = i - off + 1; if (m_echo) { m_out.write(CRLF); } return read; } //naive backspace handling if (ch == BS || ch == DEL) { if (i > off) { i = i - 2; moveLeft(1); eraseToEndOfLine(); } else { i--; bell(); } } else if (ch == CTRL_U) { moveLeft(i - off); eraseToEndOfLine(); i = off - 1; } else { if (m_echo) { m_out.write(chars[i]); } } } return len; }//read /** * Writes the NVT BEL character to the output. */ private void bell() { m_out.write(BEL); m_out.flush(); }//bell /** * Writes the standard vt100/ansi cursor moving code to the output. * * @param i the number of times the cursor should be moved left. * @throws IOException if I/O fails. */ private void moveLeft(int i) throws IOException { CURSOR_LEFT[2] = Byte.decode(Integer.toString(i)).byteValue(); m_out.write(CURSOR_LEFT); m_out.flush(); }//moveLeft /** * Writes the standard vt100/ansi sequence for erasing to the end of the current line. * * @throws IOException if I/O fails. */ private void eraseToEndOfLine() throws IOException { m_out.write(ERASE_TEOL); m_out.flush(); }//eraseToEndOfLine /** * Closes this reader. * Note: will close the input, but not the output. * * @throws IOException */ public void close() throws IOException { m_in.close(); }//close /** * <b>Bell</b><br> * The ANSI defined byte code for the NVT bell. */ public static final byte BEL = 7; /** * <b>BackSpace</b><br> * The ANSI defined byte code of backspace. */ public static final byte BS = 8; /** * <b>Delete</b><br> * The ANSI defined byte code of delete. */ public static final byte DEL = 127; /** * CTRL-u */ public static final byte CTRL_U = 21; /** * Escape character. */ private static byte ESC = 27; /** * vt100/ansi standard sequence for moving the cursor left. */ private static byte[] CURSOR_LEFT = { ESC, '[', '1', 'D' }; /** * vt100/ansi standard sequence for erasing everything from the actual cursor to * the end of the current line. */ private static byte[] ERASE_TEOL = { ESC, '[', 'K' }; /** * Standard NVT line break, which HAS TO BE CR and LF. */ private static byte[] CRLF = { '\r', '\n' }; /** * Standard ASCII end of transmission. */ private static byte EOT = 4; }//class TerminalReader