/*******************************************************************************
* Copyright (c) 2009, 2011 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.ui.launch.setup;
import java.io.BufferedInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.SocketException;
import java.util.ArrayList;
class TelnetInputStream extends FilterInputStream {
public interface TelnetTraceListener {
void command(String s);
}
private final boolean echo;
private final String prompt;
private final InputStream inp;
private final OutputStream out;
private final boolean mode_rem[] = new boolean[256];
private final int mode_cnt[] = new int[256];
private final Reader reader = new Reader();
private final byte buf[] = new byte[512];
private int buf_inp = 0;
private int buf_out = 0;
private boolean eof;
private IOException err;
private boolean closed;
private final ArrayList<TelnetTraceListener> trace_listeners = new ArrayList<TelnetTraceListener>();
private static final int
cm_IAC = 255,
cm_WILL = 251,
cm_WONT = 252,
cm_DO = 253,
cm_DONT = 254,
cm_SB = 250,
cm_SE = 240,
cm_DataMark = 242;
private static final int
co_ECHO = 1,
co_SUPPRESS_GO_AHEAD = 3,
co_STATUS = 5,
co_TERMINAL_TYPE = 24,
co_NAWS = 31, // Negotiate About Window Size
co_TERMINAL_SPEED = 32,
co_TOGGLE_FLOW_CONTROL = 33,
co_X_DISPLAY_LOCATION = 35,
co_ENVIRON = 36,
co_NEW_ENVIRON = 39;
@SuppressWarnings("unused")
private static final int
sp_VAR = 0,
sp_VALUE = 1,
sp_ESC = 2,
sp_USERVAR = 3;
@SuppressWarnings("unused")
private static final int
ac_IS = 0,
ac_SEND = 1,
ac_INFO = 2;
private class Reader extends Thread {
private void logCommand(int cmd, int opt) {
String s = "" + cmd;
switch (cmd) {
case cm_WILL:
s = "WILL";
break;
case cm_WONT:
s = "WONT";
break;
case cm_DO:
s = "DO";
break;
case cm_DONT:
s = "DONT";
break;
case cm_SB:
s = "SB";
break;
}
s += " ";
switch (opt) {
case co_ECHO:
s += "ECHO";
break;
case co_SUPPRESS_GO_AHEAD:
s += "SUPPRESS_GO_AHEAD";
break;
case co_STATUS:
s += "STATUS";
break;
case co_TERMINAL_TYPE:
s += "TERMINAL_TYPE";
break;
case co_NAWS:
s += "NAWS";
break;
case co_TERMINAL_SPEED:
s += "TERMINAL_SPEED";
break;
case co_TOGGLE_FLOW_CONTROL:
s += "TOGGLE_FLOW_CONTROL";
break;
case co_X_DISPLAY_LOCATION:
s += "X_DISPLAY_LOCATION";
break;
case co_ENVIRON:
s += "ENVIRON";
break;
case co_NEW_ENVIRON:
s += "NEW_ENVIRON";
break;
default:
s += opt;
break;
}
for (TelnetTraceListener l : trace_listeners) l.command(s);
}
private int read_ch() throws IOException {
try {
return inp.read();
}
catch (SocketException x) {
String s = x.getMessage();
if (s.startsWith("Socket closed")) return -1;
if (s.startsWith("socket closed")) return -1;
throw x;
}
}
public void run() {
try {
synchronized (out) {
out.write(cm_IAC);
out.write(echo ? cm_DO : cm_DONT);
out.write(co_ECHO);
out.flush();
}
for (;;) {
int rd = read_ch();
if (rd < 0) break;
if (rd == cm_IAC) {
int cm = read_ch();
if (cm < 0) break;
if (cm != cm_IAC) {
int co = read_ch();
if (co < 0) break;
if (co == cm_DataMark) continue;
logCommand(cm, co);
synchronized (out) {
if (mode_cnt[co] >= 5) continue;
switch (cm) {
case cm_WILL:
mode_rem[co] = true;
break;
case cm_WONT:
mode_rem[co] = false;
break;
case cm_DO:
out.write(cm_IAC);
if (co == co_SUPPRESS_GO_AHEAD) {
out.write(cm_WILL);
}
else if (co == co_NEW_ENVIRON && prompt != null) {
out.write(cm_WILL);
}
else {
out.write(cm_WONT);
}
out.write(co);
break;
case cm_DONT:
out.write(cm_IAC);
out.write(cm_WONT);
out.write(co);
break;
case cm_SB:
int ac = read_ch();
if (ac < 0) break;
if (ac == ac_SEND) {
if (co == co_NEW_ENVIRON) {
out.write(cm_IAC);
out.write(cm_SB);
out.write(co_NEW_ENVIRON);
out.write(ac_IS);
if (prompt != null) {
out.write(sp_VAR);
out.write('P');
out.write('S');
out.write('1');
out.write(sp_VALUE);
for (int k = 0; k < prompt.length(); k++) out.write(prompt.charAt(k));
}
out.write(cm_IAC);
out.write(cm_SE);
}
}
int c0 = 0;
for (;;) {
int c1 = read_ch();
if (c0 == cm_IAC && c1 == cm_SE) break;
if (c0 == cm_IAC && c1 == cm_IAC) c1 = 0;
c0 = c1;
}
break;
default:
throw new IOException("Invalid command: " + cm);
}
out.flush();
mode_cnt[co]++;
}
continue;
}
}
synchronized (TelnetInputStream.this) {
int new_inp = (buf_inp + 1) % buf.length;
while (new_inp == buf_out) TelnetInputStream.this.wait();
buf[buf_inp] = (byte)rd;
buf_inp = new_inp;
TelnetInputStream.this.notify();
}
}
}
catch (InterruptedException x) {
err = new InterruptedIOException();
}
catch (IOException x) {
if (!closed) err = x;
}
finally {
synchronized (TelnetInputStream.this) {
eof = true;
TelnetInputStream.this.notify();
}
}
}
}
TelnetInputStream(InputStream inp, OutputStream out, boolean echo, String prompt) {
super(inp);
if (!(inp instanceof BufferedInputStream)) inp = new BufferedInputStream(inp);
this.inp = inp;
this.out = out;
this.echo = echo;
this.prompt = prompt;
reader.start();
}
public synchronized void addTraceListener(TelnetTraceListener l) {
trace_listeners.add(l);
}
public synchronized void removeTraceListener(TelnetTraceListener l) {
trace_listeners.remove(l);
}
public synchronized int read() throws IOException {
try {
while (buf_out == buf_inp) {
if (err != null) throw new IOException(err.getMessage());
if (eof) return -1;
wait();
}
int res = buf[buf_out] & 0xff;
buf_out = (buf_out + 1) % buf.length;
notify();
return res;
}
catch (InterruptedException x) {
throw new InterruptedIOException();
}
}
public synchronized int read(byte b[], int off, int len) throws IOException {
boolean nfy = false;
try {
int cnt = 0;
while (cnt < len) {
while (buf_out == buf_inp) {
if (cnt > 0) return cnt;
if (err != null) throw new IOException(err.getMessage());
if (eof) return -1;
if (nfy) {
notify();
nfy = false;
}
wait();
}
b[off++] = buf[buf_out];
buf_out = (buf_out + 1) % buf.length;
nfy = true;
cnt++;
}
return cnt;
}
catch (InterruptedException x) {
throw new InterruptedIOException();
}
finally {
if (nfy) notify();
}
}
public synchronized int available() throws IOException {
return (buf_inp + buf.length - buf_out) % buf.length;
}
public synchronized void close() throws IOException {
closed = true;
super.close();
}
}