added applet mode, terminal window
authoradam <adam@megacz.com>
Sat, 16 Sep 2006 14:35:00 +0000 (15:35 +0100)
committeradam <adam@megacz.com>
Sat, 16 Sep 2006 14:35:00 +0000 (15:35 +0100)
27 files changed:
Makefile
src/de/mud/telnet/CharDisplayTest.java [new file with mode: 0644]
src/de/mud/telnet/IOtest.java [new file with mode: 0644]
src/de/mud/telnet/appWrapper.java [new file with mode: 0644]
src/de/mud/telnet/display/CharDisplay.java [new file with mode: 0644]
src/de/mud/telnet/display/SoftFont.java [new file with mode: 0644]
src/de/mud/telnet/display/Terminal.java [new file with mode: 0644]
src/de/mud/telnet/display/TerminalHost.java [new file with mode: 0644]
src/de/mud/telnet/display/vt320.java [new file with mode: 0644]
src/de/mud/telnet/frame.java [new file with mode: 0644]
src/de/mud/telnet/modules/ButtonBar.java [new file with mode: 0644]
src/de/mud/telnet/modules/Module.java [new file with mode: 0644]
src/de/mud/telnet/modules/Script.java [new file with mode: 0644]
src/de/mud/telnet/modules/TextLabel.java [new file with mode: 0644]
src/de/mud/telnet/socket/StatusPeer.java [new file with mode: 0644]
src/de/mud/telnet/socket/TelnetIO.java [new file with mode: 0644]
src/de/mud/telnet/socket/TelnetWrapper.java [new file with mode: 0644]
src/de/mud/telnet/socket/TimedOutException.java [new file with mode: 0644]
src/de/mud/telnet/telnet.java [new file with mode: 0644]
src/edu/berkeley/fleet/Fleet.java
src/edu/berkeley/fleet/FleetApplet.java [new file with mode: 0644]
src/edu/berkeley/fleet/FleetParser.java
src/edu/berkeley/fleet/Log.java [new file with mode: 0644]
src/edu/berkeley/fleet/Program.java
src/edu/berkeley/fleet/Ship.java
src/edu/berkeley/fleet/Term.java [new file with mode: 0644]
test.fleet

index 3626bcd..5c7aa9e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,12 @@
 run: fleet.jar
        java -cp lib/edu.berkeley.sbp.jar:fleet.jar edu.berkeley.fleet.FleetParser < test.fleet
 
+applet: fleet.jar
+       java -cp lib/edu.berkeley.sbp.jar:fleet.jar edu.berkeley.fleet.FleetApplet < test.fleet
+
 fleet.jar: $(shell find src -name \*.java) fleet.g
        mkdir -p bin
        cp fleet.g bin
+       cp test.fleet bin
        javac -cp lib/edu.berkeley.sbp.jar -d bin $(shell find src -name \*.java)
        cd bin; jar cvf ../$@ .
diff --git a/src/de/mud/telnet/CharDisplayTest.java b/src/de/mud/telnet/CharDisplayTest.java
new file mode 100644 (file)
index 0000000..b40deb8
--- /dev/null
@@ -0,0 +1,169 @@
+package de.mud.telnet;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+/**
+ * CharDisplayTest
+ * --
+ * $Id: CharDisplayTest.java,v 1.1.1.1 1997/03/05 13:35:16 leo Exp $
+ * $timestamp: Mon Feb 17 20:11:20 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.applet.Applet;
+import java.awt.Button;
+import java.awt.Panel;
+import java.awt.Event;
+import java.awt.FlowLayout;
+import java.awt.BorderLayout;
+import java.awt.Choice;
+import java.awt.TextField;
+import java.awt.Font;
+
+import de.mud.telnet.display.CharDisplay;
+
+/**
+ * CharDisplayTest -- a test applet to show the display/CharDisplay features
+ * --
+ * @version    $Id: CharDisplayTest.java,v 1.1.1.1 1997/03/05 13:35:16 leo Exp $
+ * @author     Matthias L. Jugel, Marcus Meißner
+ */
+public class CharDisplayTest extends Applet
+{
+  CharDisplay display = new CharDisplay(80, 24, "Courier", 14);
+
+  Panel buttons = new Panel();
+  Button info = new Button("Information");
+  Button chars = new Button("Character Table");
+  Button attr = new Button("Attributes");
+  Choice fonts = new Choice();
+  TextField from = new TextField("0", 4);
+
+  public void init()
+  {
+    setLayout(new BorderLayout());
+    fonts.addItem("Helvetica");
+    fonts.addItem("TimesRoman");
+    fonts.addItem("Courier");
+    fonts.addItem("Dialog");
+    fonts.addItem("DialogInput");
+    fonts.addItem("ZapfDingBats");
+    fonts.addItem("default");
+    buttons.add(info);
+    buttons.add(chars);
+    buttons.add(attr);
+    buttons.add(fonts);
+    buttons.add(from);
+    add("North", buttons);
+    display.setResizeStrategy(CharDisplay.RESIZE_FONT);
+    add("Center", display);
+    Info();
+  }
+  
+  public boolean handleEvent(Event evt)
+  {
+    if(evt.target == info) { Info(); return true; }
+    if(evt.target == chars) { CharacterTable(); return true; }
+    if(evt.target == attr) { Attributes(); return true; }
+    if(evt.id == Event.ACTION_EVENT && 
+       (evt.target == fonts || evt.target == from))
+    {
+      remove(display);
+      display = new CharDisplay(80, 24, fonts.getSelectedItem(), 12);
+      add("Center", display);
+      CharacterTable();
+      layout();
+      return true;
+    }
+    return false;
+  }
+
+  private void Clear()
+  {
+    display.deleteArea(0, 0, 80, 24);
+  }
+    
+  private void Info()
+  {
+    Clear();
+    display.putString(4, 1, "CharDisplay.class Information", CharDisplay.INVERT);
+    display.putString(4, 3, "Version: "+display.version, CharDisplay.BOLD);
+    display.putString(4, 5, "This class implements several hardware features needed to implement");
+    display.putString(4, 6, "a video terminal.");
+    display.putString(4, 7, "This includes simple operations, such as putting and inserting single");
+    display.putString(4, 8, "characters or strings on the screen, character attributes and colors.");
+    display.putString(4, 9, "Special features like inserting lines, scrolling text up or down and");
+    display.putString(4,10, "defining scrollareas help implementing terminal emulations.");
+    display.redraw();
+  }
+    
+  private void CharacterTable()
+  {
+    int ch = (new Integer(from.getText())).intValue();
+    
+    Clear();
+    display.putString( 4, 1, "Character Table", CharDisplay.INVERT);
+    for(int c = 1; c < 80; c += 6)
+      for(int l = 3; l < 23; l++)
+      {
+       display.putString(c, l, ""+ch, CharDisplay.INVERT);
+       display.putChar(c+4, l, (char)ch++);
+      }
+    display.markLine(3, 20);
+    display.redraw();
+  }
+
+  private void Attributes()
+  {
+    int c = 4, l = 8;
+    
+    Clear();
+    display.putString( 4, 1, "Character attributes", CharDisplay.INVERT);
+    display.putString( 4, 3, "Normal", CharDisplay.NORMAL);
+    display.putString(22, 3, "Bold", CharDisplay.BOLD);
+    display.putString(40, 3, "Underline", CharDisplay.UNDERLINE);
+    display.putString(58, 3, "Invert", CharDisplay.INVERT);
+
+    display.putString( 4, 5, "Black", 1 << 3 | 8 << 7);
+    display.putString(13, 5, "Red", 2 << 3);
+    display.putString(22, 5, "Green", 3 << 3);
+    display.putString(31, 5, "Yellow", 4 << 3);
+    display.putString(40, 5, "Blue", 5 << 3);
+    display.putString(49, 5, "Magenta", 6 << 3);
+    display.putString(58, 5, "Cyan", 7 << 3);
+    display.putString(67, 5, "LightGray", 8 << 3);
+    
+    for(int bg = 1; bg <= 8; bg++)
+    {
+      for(int fg = 1; fg <= 8; fg++)
+      {
+       for(int a = 0; a <= 7; a++)
+       {
+         display.putChar(c++, l, '@', (fg << 3) | (bg << 7) | a);
+         display.redraw();
+       }
+       c++;
+      }
+      l += 2; c = 4;
+    }
+    
+  }
+}
diff --git a/src/de/mud/telnet/IOtest.java b/src/de/mud/telnet/IOtest.java
new file mode 100644 (file)
index 0000000..9f0eff3
--- /dev/null
@@ -0,0 +1,67 @@
+package de.mud.telnet;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+/* IOtest.java -- An example how to use the TelnetIO class
+ * --
+ * Author: Matthias L. Jugel
+ *
+ * Usage: compile with javac IOtest.java
+ *        run program with java IOtest
+ *
+ * This is not an applet, but the idea might be used in one. 
+ */
+
+import java.util.Vector;
+import java.io.*;
+import de.mud.telnet.socket.*;
+
+/**
+ * IOtest -- a test class for telnet i/o
+ * --
+ * @version    $Id: IOtest.java,v 1.1.1.1 1997/03/05 13:35:16 leo Exp $
+ * @author     Matthias L. Jugel
+ */
+class IOtest {
+
+  // create a new telnet io instance
+  static TelnetIO tio = new TelnetIO();
+
+  // skip any received data until the prompt appears
+  private static void wait(String prompt)
+  {
+    String tmp = "";
+    do {
+      try { tmp = new String(tio.receive(), 0); }
+      catch(IOException e) { e.printStackTrace(); }
+      System.out.println(tmp);
+    } while(tmp.indexOf(prompt) == -1);
+  }
+
+  // send a string to the remote host, since TelnetIO needs a byte buffer
+  // we have to convert the string first
+  private static void send(String str)
+  {
+    byte[] buf = new byte[str.length()];
+    str.getBytes(0, str.length(), buf, 0);
+    try { tio.send(buf); } catch(IOException e) {}
+  }
+
+  // this function is called when running the class with java IOtest
+  // looks very much like a modem login script ;-)
+  public static void main(String args[])
+  {
+    try {
+      tio.connect("localhost");
+      wait("login:");
+      send("<YOUR LOGIN NAME>\r");
+      wait("Password:");
+      send("<YOUR PASSWORD>\r");
+      wait("<YOUR SHELL PROMPT>");
+      send("touch /tmp/THIS_WAS_AN_APPLET\r");
+      tio.disconnect();
+    } catch(IOException e) { e.printStackTrace(); }
+  }
+}
diff --git a/src/de/mud/telnet/appWrapper.java b/src/de/mud/telnet/appWrapper.java
new file mode 100644 (file)
index 0000000..ad38743
--- /dev/null
@@ -0,0 +1,262 @@
+package de.mud.telnet;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+/**
+ * appWrapper -- applet/application wrapper
+ * --
+ * $Id: appWrapper.java,v 1.9 1997/07/24 13:26:24 leo Exp $
+ * $timestamp: Thu Jul 24 13:08:23 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.applet.Applet;
+import java.applet.AppletStub;
+
+import java.awt.Frame;
+import java.awt.Event;
+import java.awt.Panel;
+import java.awt.Button;
+import java.awt.BorderLayout;
+import java.awt.Graphics;
+import java.awt.Color;
+import java.awt.FontMetrics;
+
+/**
+ * The appWrapper is thought to make the applet itself independent from
+ * the original context. This is necessary to be able to detach the applet
+ * from the web browsers window without disconnecting it from events.
+ * Note: This applet should work with any applet without changes.
+ *
+ * <DL>
+ * <DT><B><PRE>&lt;PARAM NAME=&quot;applet&quot; VALUE=&quot;<I>applet</I>&quot;&gt;</PRE></B>
+ * <DD>Defines the applet to be loaded by the appWrapper. State the applet 
+ *     class name without &quot;.class&quot;!<P>
+ * <DT><B><PRE>&lt;PARAM NAME=&quot;startButton&quot; VALUE=&quot;<I>text</I>&quot;&gt;</PRE></B>
+ * <DD>If this parameter is set the applet is not loaded until the user presses
+ *     the button. This decreases first time download delay. The <I>text</I>
+ *     given as value to the parameter is shown on the button. While loading
+ *     the applet the message "Loading ..." is shown on the button.<P>
+ * <DT><B><PRE>&lt;PARAM NAME=&quot;stopButton&quot; VALUE=&quot;<I>text</I>&quot;&gt;</PRE></B>
+ * <DD>This parameter defines the button text when the applet is loaded. When 
+ *     pressing the button while the applet is running this causes the applet
+ *     window to be destroyed and the applet is stopped.<P>
+ * <DT><B><PRE>&lt;PARAM NAME=&quot;frameTitle&quot; VALUE=&quot;<I>text</I>&quot;&gt;</PRE></B>
+ * <DD>The <I>frameTitle</I> is the text that is shown in the title bar of the
+ *     applet window.<P>
+ * </DL>
+ * @version $Id: appWrapper.java,v 1.9 1997/07/24 13:26:24 leo Exp $
+ * @author  Matthias L. Jugel
+ */
+public class appWrapper extends Applet implements AppletStub, Runnable
+{
+  Thread loader = null;
+  
+  String appletName = null;
+  Applet applet = null;
+
+  Button startButton = null;
+  String startLabel, stopLabel, frameTitle;
+
+  frame f;
+  
+  /**
+   * Applet initialization. We load the class giving in parameter "applet"
+   * and set the stub corresponding to ours. Thus we are able to give
+   * it access to the parameters and any applet specific context.
+   */
+  public void init() {
+
+    // get the applet parameter
+    if((appletName = getParameter("applet")) == null) {
+      showStatus("appWrapper: missing applet parameter, nothing loaded");
+      System.err.println("appWrapper: missing applet parameter");
+      return;
+    }
+
+    setLayout(new BorderLayout());
+
+    // get the button and title parameters
+    if((startLabel = getParameter("startButton")) == null)
+      run();
+    else {
+      startButton = new Button(getParameter("startButton"));
+      add("Center", startButton);
+      if((stopLabel = getParameter("stopButton")) == null)
+        stopLabel = "STOP!";
+      if((frameTitle = getParameter("frameTitle")) == null)
+        frameTitle = "The Java Telnet Applet";
+    }
+    
+  }
+  
+  /**
+   * Load the applet finally. When using a button this creates a new frame
+   * to put the applet in.
+   */
+  public void run() {
+    if(applet == null) try {
+      applet = (Applet)Class.forName(getParameter("applet")).newInstance();
+      applet.setStub(this);
+    } catch(Exception e) {
+      System.err.println("appWrapper: could not load "+appletName);
+      e.printStackTrace();
+      return;
+    } else {
+      System.err.println("appWrapper: applet already loaded");
+      return;
+    }
+
+    if(startButton == null) {
+      add("Center", applet);
+      applet.init();
+    } else {
+      f = new frame(frameTitle);
+      f.setLayout(new BorderLayout());
+      f.add("Center", applet);
+      applet.init();
+      f.resize(applet.minimumSize());
+      f.pack();
+      f.show();
+    }
+    applet.start();
+
+    if(startButton != null)
+      startButton.setLabel(stopLabel);
+    
+    // stop loader thread
+    while(loader != null) {
+      if(f == null || !f.isVisible()) {
+        startButton.setLabel(startLabel);
+        loader.stop();
+        loader = null;
+      }
+      try { loader.sleep(5000); }
+      catch(InterruptedException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * This method is called when the applet want's to be resized.
+   * @param width the width of the applet
+   * @param height the height of the applet
+   */
+  public void appletResize(int width, int height) {
+    System.err.println("appWrapper: appletResize()");
+    if(applet != null) applet.resize(width, height);
+  }
+
+  /**
+   * Give information about the applet.
+   */
+  public String getAppletInfo()
+  {
+    String info = "appWrapper: $Id: appWrapper.java,v 1.9 1997/07/24 13:26:24 leo Exp $\n";
+    if(applet != null)
+      info += applet.getAppletInfo();
+    return info;
+  }
+  
+  /**
+   * Give information about the appWrapper and the applet loaded.
+   */
+  public String[][] getParameterInfo()
+  {
+    String info[][];
+    String wrapper[][] = {
+      {"applet",   "String",   "appWrapper: Applet to load"},
+    };
+    if(applet != null) {
+      String tmp[][] = applet.getParameterInfo();
+      info = new String[tmp.length + 1][3];
+      System.arraycopy(tmp, 0, info, 1, tmp.length);
+    }
+    else info = new String[1][3];
+    System.arraycopy(wrapper, 0, info, 0, 1);
+      
+    return info;
+  }
+
+  /**
+   * Write a message to the applet area.
+   */
+  public void paint(Graphics g) 
+  {
+    String message;
+    if(applet != null) 
+      message = "Click to reattach the Applet!";
+    else message = "The was no applet load (maybe an error)!";
+
+    
+    int width = size().width / 2 - 
+      (getFontMetrics(getFont())).stringWidth(message) / 2;
+    int height = size().height / 2;
+    
+    g.setColor(Color.red);
+    g.drawString(message, width, height);
+  }
+  
+  /**
+   * reshape the applet and ourself
+   */
+  public void reshape(int x, int y, int w, int h)
+  {
+    if(applet != null) applet.reshape(x, y, w, h);
+    super.reshape(x, y, w, h);
+  }
+
+  /**
+   * Handle button events. When pressed it either creates the new applet
+   * window or destoys it.
+   */
+  public boolean handleEvent(Event evt) 
+  {
+    if(evt.target == startButton && evt.id == Event.ACTION_EVENT) {
+      if(applet == null) {
+        startButton.setLabel("Loading ...");
+        (loader = new Thread(this)).start();
+      } else {
+        if(applet.getParent() instanceof Frame) {
+          Frame frame = (Frame)applet.getParent();
+          frame.hide();
+          frame.dispose();
+        }
+        applet.stop();
+        applet.destroy();
+        applet = null;
+        startButton.setLabel(startLabel);
+      }
+      return true;
+    }
+    if(evt.id == Event.MOUSE_UP && applet.getParent() instanceof Frame) {
+      Frame frame = (Frame)applet.getParent();
+      frame.hide();
+      frame.dispose();
+      add("Center", applet);
+      validate();
+      layout();
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/src/de/mud/telnet/display/CharDisplay.java b/src/de/mud/telnet/display/CharDisplay.java
new file mode 100644 (file)
index 0000000..89795e1
--- /dev/null
@@ -0,0 +1,1275 @@
+package de.mud.telnet.display;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+/**
+ * CharDisplay -- a simple character display
+ * --
+ * $Id: CharDisplay.java,v 1.27 1998/03/18 12:43:33 leo Exp $
+ * $timestamp: Thu Jul 24 15:19:18 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.awt.Graphics;
+import java.awt.Panel;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+import java.awt.Insets;
+import java.awt.Event;
+import java.awt.TextArea;
+import java.awt.Label;
+import java.awt.Frame;
+import java.awt.Scrollbar;
+import java.awt.Rectangle;
+
+/**
+ * A simple character display.
+ * @version $Id: CharDisplay.java,v 1.27 1998/03/18 12:43:33 leo Exp $
+ * @author  Matthias L. Jugel, Marcus Meißner
+ */
+public class CharDisplay extends Panel
+{
+  /**
+   * If you need the runtime version, just ask this variable.
+   */
+  public String version = "$Revision: 1.27 $ $Date: 1998/03/18 12:43:33 $";
+  /**
+   * Enable debug messages. This is final static to prevent unused
+   * code to be compiled.
+   */
+  public final static int debug = 0;
+  
+  private Dimension size;      /* rows and columns */
+  private Insets insets;      /* size of the border */
+  private boolean raised;      /* indicator if the border is raised */
+
+  private char charArray[][];      /* contains the characters */
+  private int charAttributes[][];    /* contains character attrs */
+  private int bufSize, maxBufSize;    /* buffer sizes */
+
+  private int windowBase;      /* where the start displaying */
+  private int screenBase;      /* the actual screen start */
+  private int topMargin;      /* top scroll margon */
+  private int bottomMargin;      /* bottom scroll margon */
+  private Scrollbar scrollBar;  /* the scroll bar */
+  private String scrBarPos;      /* the scroll bar position */
+
+  private Font normalFont;      /* normal font */
+  private FontMetrics fm;      /* current font metrics */
+  private int charWidth;      /* current width of a char */
+  private int charHeight;      /* current height of a char */
+  private int charDescent;      /* base line descent */
+  private int resizeStrategy;      /* current resizing strategy */
+
+  private int cursorX, cursorY;      /* current cursor position */
+  private Point selectBegin, selectEnd;  /* selection coordinates */
+  private TextArea selection;
+  private Frame selectFrame;
+
+  private de.mud.telnet.display.SoftFont  sf = new de.mud.telnet.display.SoftFont();
+
+  private boolean screenLocked = false;    /* screen needs to be locked */
+                                                /* because of paint requests */
+                                                /* during other operations */
+  private boolean update[];
+  
+  private Color color[] = { Color.black.brighter(),
+                            Color.red.brighter(),
+                            Color.green.brighter(),
+                            Color.yellow.brighter(),
+                            Color.blue.brighter(),
+                            Color.magenta.brighter(),
+                            Color.cyan.brighter(),
+                            Color.white.brighter(),
+  };
+  private final static int COLOR_FG_STD = 7;
+  private final static int COLOR_BG_STD = 0;
+  private final static int COLOR        = 0x7f8;
+  private final static int COLOR_FG     = 0x78;
+  private final static int COLOR_BG     = 0x780;
+
+  /**
+   * Scroll up when inserting a line.
+   */
+  public final static boolean SCROLL_UP   = false;
+  /**
+   * Scroll down when inserting a line.
+   */
+  public final static boolean SCROLL_DOWN = true;
+
+  /**
+   * Do nothing when the container is resized.
+   */
+  public final static int RESIZE_NONE  = 0;
+  /**
+   * Resize the width and height of the characterscreen.
+   */
+  public final static int RESIZE_SCREEN  = 1;
+  /**
+   * Resize the font to the new screensize.
+   */
+  public final static int RESIZE_FONT  = 2;
+  
+  /**
+   * Make character normal.
+   */ 
+  public final static int NORMAL  = 0x00;
+  /**
+   * Make character bold.
+   */ 
+  public final static int BOLD    = 0x01;
+  /**
+   * Underline character.
+   */ 
+  public final static int UNDERLINE  = 0x02;
+  /**
+   * Invert character.
+   */ 
+  public final static int INVERT  = 0x04;
+
+  private void InitializeCharDisplay(int width, int height, 
+                                     String fontname, int fsize)
+  {
+    System.err.println("CharDisplay: screen size: ["+width+","+height+"]");
+    normalFont = new Font(fontname, Font.BOLD, fsize);
+    setFont(normalFont);
+    fm = getFontMetrics(normalFont);
+    if(fm != null)
+    {
+      charWidth = fm.charWidth('@');
+      charHeight = fm.getHeight();
+      charDescent = fm.getDescent();
+    }
+
+    resizeStrategy = RESIZE_FONT;
+    size = new Dimension(width, height);
+    charArray = new char[size.height][size.width];
+    charAttributes = new int[size.height][size.width];
+    bufSize = size.height;
+    maxBufSize = 2 * size.height;
+
+    windowBase = 0;
+    screenBase = 0;
+    topMargin = 0;
+    bottomMargin = size.height - 1;
+
+    update = new boolean[size.height + 1];
+    for(int i = 1; i <= size.height; i++) update[i] = true;
+
+    selectBegin = new Point(0,0);
+    selectEnd = new Point(0,0);
+
+    setLayout(null);
+  }
+  
+  /**
+   * Create a character display with size 80x24 and Font "Courier", size 12.
+   */
+  public CharDisplay()
+  {
+    InitializeCharDisplay(80, 24, "Courier", 12);
+  }
+
+  /**
+   * Create a character display with specific size, Font is "Courier", size 12.
+   */
+  public CharDisplay(int width, int height)
+  {
+    InitializeCharDisplay(width, height, "Courier", 12);
+  }
+
+  /**
+   * Create a character display with 80x24 and specific font and font size.
+   */
+  public CharDisplay(String fname, int fsize)
+  {
+    InitializeCharDisplay(80, 24, fname, fsize);
+  }
+
+  /**
+   * Create a character display with specific size, font and font size.
+   */
+  public CharDisplay(int width, int height, String fname, int fsize)
+  {
+    InitializeCharDisplay(width, height, fname, fsize);
+  }
+  
+  /**
+   * Put a character on the screen with normal font and outline.
+   * The character previously on that position will be overwritten.
+   * You need to call redraw() to update the screen.
+   * @param c x-coordinate (column)
+   * @param l y-coordinate (line)
+   * @param ch the character to show on the screen
+   * @see #insertChar
+   * @see #deleteChar
+   * @see #redraw
+   */
+  public void putChar(int c, int l, char ch)
+  {
+    putChar(c, l, ch, NORMAL);
+  }
+
+  /**
+   * Put a character on the screen with specific font and outline.
+   * The character previously on that position will be overwritten.
+   * You need to call redraw() to update the screen.
+   * @param c x-coordinate (column)
+   * @param l y-coordinate (line)
+   * @param ch the character to show on the screen
+   * @param attributes the character attributes
+   * @see #BOLD
+   * @see #UNDERLINE
+   * @see #INVERT
+   * @see #NORMAL
+   * @see #insertChar
+   * @see #deleteChar
+   * @see #redraw
+   */  
+
+  public void putChar(int c, int l, char ch, int attributes)
+  {
+    c = checkBounds(c, 0, size.width - 1);
+    l = checkBounds(l, 0, size.height - 1);
+    charArray[screenBase + l][c] = ch;
+    charAttributes[screenBase + l][c] = attributes;
+    markLine(l, 1);
+  }
+
+  /**
+   * Get the character at the specified position.
+   * @param c x-coordinate (column)
+   * @param l y-coordinate (line)
+   * @see #putChar
+   */
+  public char getChar(int c, int l)
+  {
+    c = checkBounds(c, 0, size.width - 1);
+    l = checkBounds(l, 0, size.height - 1);
+    return charArray[l][c];
+  }
+
+  /**
+   * Get the attributes for the specified position.
+   * @param c x-coordinate (column)
+   * @param l y-coordinate (line)
+   * @see #putChar
+   */
+  public int getAttributes(int c, int l)
+  {
+    c = checkBounds(c, 0, size.width - 1);
+    l = checkBounds(l, 0, size.height - 1);
+    return charAttributes[l][c];
+  }
+
+  /**
+   * Insert a character at a specific position on the screen.
+   * All character right to from this position will be moved one to the right.
+   * You need to call redraw() to update the screen.
+   * @param c x-coordinate (column)
+   * @param l y-coordinate (line)
+   * @param ch the character to insert
+   * @param attributes the character attributes
+   * @see #BOLD
+   * @see #UNDERLINE
+   * @see #INVERT
+   * @see #NORMAL
+   * @see #putChar
+   * @see #deleteChar
+   * @see #redraw
+   */
+  public void insertChar(int c, int l, char ch, int attributes)
+  {
+    c = checkBounds(c, 0, size.width - 1);
+    l = checkBounds(l, 0, size.height - 1);
+    System.arraycopy(charArray[screenBase + l], c, 
+         charArray[screenBase + l], c + 1, size.width - c - 1);
+    System.arraycopy(charAttributes[screenBase + l], c, 
+         charAttributes[screenBase + l], c + 1, size.width - c - 1);
+    putChar(c, l, ch, attributes);
+  }
+
+  /**
+   * Delete a character at a given position on the screen.
+   * All characters right to the position will be moved one to the left.
+   * You need to call redraw() to update the screen.
+   * @param c x-coordinate (column)
+   * @param l y-coordinate (line)
+   * @see #putChar
+   * @see #insertChar
+   * @see #redraw
+   */
+  public void deleteChar(int c, int l)
+  {
+    c = checkBounds(c, 0, size.width - 1);
+    l = checkBounds(l, 0, size.height - 1);
+    if(c < size.width - 1)
+    {
+      System.arraycopy(charArray[screenBase + l], c + 1,
+           charArray[screenBase + l], c, size.width - c - 1);
+      System.arraycopy(charAttributes[screenBase + l], c + 1,
+           charAttributes[screenBase + l], c, size.width - c - 1);
+    }
+    putChar(size.width - 1, l, (char)0);
+  }
+
+  /**
+   * Put a String at a specific position. Any characters previously on that 
+   * position will be overwritten. You need to call redraw() for screen update.
+   * @param c x-coordinate (column)
+   * @param l y-coordinate (line)
+   * @param s the string to be shown on the screen
+   * @see #BOLD
+   * @see #UNDERLINE
+   * @see #INVERT
+   * @see #NORMAL
+   * @see #putChar
+   * @see #insertLine
+   * @see #deleteLine
+   * @see #redraw
+   */  
+  public void putString(int c, int l, String s)
+  {
+    putString(c, l, s, NORMAL);
+  }
+  
+  /**
+   * Put a String at a specific position giving all characters the same
+   * attributes. Any characters previously on that position will be 
+   * overwritten. You need to call redraw() to update the screen.
+   * @param c x-coordinate (column)
+   * @param l y-coordinate (line)
+   * @param s the string to be shown on the screen
+   * @param attributes character attributes
+   * @see #BOLD
+   * @see #UNDERLINE
+   * @see #INVERT
+   * @see #NORMAL
+   * @see #putChar
+   * @see #insertLine
+   * @see #deleteLine
+   * @see #redraw
+   */
+  public void putString(int c, int l, String s, int attributes)
+  {
+    for(int i = 0; i < s.length() && c + i < size.width; i++)
+      putChar(c + i, l, s.charAt(i), attributes);
+  }
+
+  /**
+   * Insert a blank line at a specific position.
+   * The current line and all previous lines are scrolled one line up. The
+   * top line is lost. You need to call redraw() to update the screen.
+   * @param l the y-coordinate to insert the line
+   * @see #deleteLine
+   * @see #redraw
+   */
+  public void insertLine(int l)
+  {
+    insertLine(l, 1, SCROLL_UP);
+  }
+
+  /**
+   * Insert blank lines at a specific position.
+   * You need to call redraw() to update the screen
+   * @param l the y-coordinate to insert the line
+   * @param n amount of lines to be inserted
+   * @see #deleteLine
+   * @see #redraw
+   */
+  public void insertLine(int l, int n)
+  {
+    insertLine(l, n, SCROLL_UP);
+  }  
+
+  /**
+   * Insert a blank line at a specific position. Scroll text according to
+   * the argument.
+   * You need to call redraw() to update the screen
+   * @param l the y-coordinate to insert the line
+   * @param scrollDown scroll down
+   * @see #deleteLine
+   * @see #SCROLL_UP
+   * @see #SCROLL_DOWN
+   * @see #redraw
+   */
+  public void insertLine(int l, boolean scrollDown)
+  {
+    insertLine(l, 1, scrollDown);
+  }  
+
+  /**
+   * Insert blank lines at a specific position.
+   * The current line and all previous lines are scrolled one line up. The
+   * top line is lost. You need to call redraw() to update the screen.
+   * @param l the y-coordinate to insert the line
+   * @param n number of lines to be inserted
+   * @param scrollDown scroll down
+   * @see #deleteLine
+   * @see #SCROLL_UP
+   * @see #SCROLL_DOWN
+   * @see #redraw
+   */
+  public synchronized void insertLine(int l, int n, boolean scrollDown)
+  {
+    screenLocked = true;
+
+    l = checkBounds(l, 0, size.height - 1);
+
+    char cbuf[][] = null;
+    int abuf[][] = null;
+    int offset = 0;
+    int oldBase = screenBase;
+    int top = (l < topMargin ? 
+               0 : (l > bottomMargin ?
+                    (bottomMargin + 1 < size.height ?
+                     bottomMargin + 1 : size.height - 1) : topMargin));
+    int bottom = (l > bottomMargin ?
+                  size.height - 1 : (l < topMargin ? 
+                                     (topMargin > 0 ?
+                                      topMargin - 1 : 0) : bottomMargin));
+    
+    
+    if(scrollDown) {
+      if(n > (bottom - top)) n = (bottom - top);
+      cbuf = new char[bottom - l - (n - 1)][size.width];
+      abuf = new int[bottom - l - (n - 1)][size.width];
+      
+      System.arraycopy(charArray, oldBase + l, cbuf, 0, bottom - l - (n - 1));
+      System.arraycopy(charAttributes, oldBase + l, 
+                      abuf, 0, bottom - l - (n - 1));
+      System.arraycopy(cbuf, 0, charArray, oldBase + l + n, 
+                      bottom - l - (n - 1));
+      System.arraycopy(abuf, 0, charAttributes, oldBase + l + n, 
+                      bottom - l - (n - 1));
+      cbuf = charArray;
+      abuf = charAttributes;
+    } else try {
+      if(n > (bottom - top) + 1) n = (bottom - top) + 1;
+      if(bufSize < maxBufSize) {
+        if(bufSize + n > maxBufSize) {
+          offset = n - (maxBufSize - bufSize);
+          bufSize = maxBufSize;
+          screenBase = maxBufSize - size.height - 1;
+          windowBase = screenBase;
+        } else {
+          screenBase += n;
+          windowBase += n;
+          bufSize += n;
+        }
+        cbuf = new char[bufSize][size.width];
+        abuf = new int[bufSize][size.width];
+      } else {
+        offset = n;
+        cbuf = charArray;
+        abuf = charAttributes;
+      }
+      /*
+       * copy anything from the top of the buffer (+offset) to the new top
+       * up to the screenBase.
+       */
+      if(oldBase > 0)
+      {
+        System.arraycopy(charArray, offset, 
+                         cbuf, 0, 
+                         oldBase - offset);
+        System.arraycopy(charAttributes, offset, 
+                         abuf, 0, 
+                         oldBase - offset);
+      }
+      /*
+       * copy anything from the top of the screen (screenBase) up to the
+       * topMargin to the new screen
+       */
+      if(top > 0)
+      {
+        System.arraycopy(charArray, oldBase, 
+                         cbuf, screenBase, 
+                         top);
+        System.arraycopy(charAttributes, oldBase, 
+                         abuf, screenBase, 
+                         top);
+      }
+      /* 
+       * copy anything from the topMargin up to the amount of lines inserted
+       * to the gap left over between scrollback buffer and screenBase
+       */
+      if(oldBase > 0) {
+       System.arraycopy(charArray, oldBase + top, 
+                        cbuf, oldBase - offset,
+                        n);
+       System.arraycopy(charAttributes, oldBase + top, 
+                        abuf, oldBase - offset,
+                        n);
+      }
+      /*
+       * copy anything from topMargin + n up to the line linserted to the
+       * topMargin
+       */
+      System.arraycopy(charArray, oldBase + top + n,
+                       cbuf, screenBase + top,
+                       l - top - (n - 1));
+      System.arraycopy(charAttributes, oldBase + top + n,
+                       abuf, screenBase + top,
+                       l - top - (n - 1));
+      /*
+       * copy the all lines next to the inserted to the new buffer
+       */
+      if(l < size.height - 1)
+      {
+        System.arraycopy(charArray, oldBase + l + 1,
+                         cbuf, screenBase + l + 1,
+                         (size.height - 1) - l);
+        System.arraycopy(charAttributes, oldBase + l + 1,
+                         abuf, screenBase + l + 1,
+                         (size.height - 1) - l);
+      }
+    } catch(ArrayIndexOutOfBoundsException e) {
+      System.err.println("*** Error while scrolling up:");
+      System.err.println("--- BEGIN STACKTRACE ---");
+      e.printStackTrace();
+      System.err.println("--- END STACKTRACE ---");
+      System.err.println("bufSize="+bufSize+", maxBufSize="+maxBufSize);
+      System.err.println("top="+top+", bottom="+bottom);
+      System.err.println("n="+n+", l="+l);
+      System.err.println("screenBase="+screenBase+", windowBase="+windowBase);
+      System.err.println("oldBase="+oldBase);
+      System.err.println("size.width="+size.width+", size.height="+size.height);
+      System.err.println("abuf.length="+abuf.length+", cbuf.length="+cbuf.length);
+      System.err.println("*** done dumping debug information");
+    }
+    
+    for(int i = 0; i < n; i++)
+    {
+      cbuf[(screenBase + l) + (scrollDown ? i : -i) ] = new char[size.width];
+      abuf[(screenBase + l) + (scrollDown ? i : -i) ] = new int[size.width];
+    }
+
+    charArray = cbuf;
+    charAttributes = abuf;
+    
+    if(scrollDown)
+      markLine(l, bottom - l + 1);
+    else
+      markLine(top, l - top + 1);
+
+    if(scrollBar != null)
+      scrollBar.setValues(windowBase, size.height, 0, bufSize);
+    
+    screenLocked = false;
+  }
+  
+  /**
+   * Delete a line at a specific position. Subsequent lines will be scrolled 
+   * up to fill the space and a blank line is inserted at the end of the 
+   * screen.
+   * @param l the y-coordinate to insert the line
+   * @see #deleteLine
+   */
+  public void deleteLine(int l)
+  {
+    l = checkBounds(l, 0, size.height - 1);
+
+    int bottom = (l>bottomMargin?size.height-1:
+                 (l<topMargin?topMargin:bottomMargin+1));
+    System.arraycopy(charArray, screenBase + l + 1,
+                     charArray, screenBase + l, bottom - l -1 );
+    System.arraycopy(charAttributes, screenBase + l + 1,
+                     charAttributes, screenBase + l, bottom - l -1);
+    charArray[screenBase + bottom - 1] = new char[size.width];
+    charAttributes[screenBase + bottom - 1] = new int[size.width];
+    markLine(l, bottom - l);
+  }
+
+
+  /**
+   * Delete a rectangular portion of the screen.
+   * You need to call redraw() to update the screen.
+   * @param c x-coordinate (column)
+   * @param l y-coordinate (row)
+   * @param w with of the area in characters
+   * @param h height of the area in characters
+   * @see #deleteChar
+   * @see #deleteLine
+   * @see redraw
+   */
+  public void deleteArea(int c, int l, int w, int h)
+  {
+    c = checkBounds(c, 0, size.width - 1);
+    l = checkBounds(l, 0, size.height - 1);
+
+    char cbuf[] = new char[w];
+    int abuf[] = new int[w];
+    
+    for(int i = 0; i < h && l + i < size.height; i++)
+    {
+      System.arraycopy(cbuf, 0, charArray[screenBase + l + i], c, w);
+      System.arraycopy(abuf, 0, charAttributes[screenBase + l + i], c, w);
+    }
+    markLine(l, h);
+  }
+
+  /**
+   * Puts the cursor at the specified position.
+   * @param c column
+   * @param l line
+   */
+  public void setCursorPos(int c, int l)
+  {
+    c = checkBounds(c, 0, size.width - 1);
+    l = checkBounds(l, 0, size.height - 1);
+    markLine(cursorY, 1);
+    cursorX = (c < size.width ? c : size.width);
+    cursorY = (l < size.height ? l : size.height);
+    markLine(l, 1);
+  }
+
+  /**
+   * Get the current cursor position.
+   * @see java.awt.Dimension
+   */
+  public Dimension getCursorPos()
+  {
+    return new Dimension(cursorX, cursorY);
+  }
+
+  /**
+   * Set the top scroll margin for the screen. If the current bottom margin
+   * is smaller it will become the top margin and the line will become the
+   * bottom margin.
+   * @param l line that is the margin
+   */
+  public void setTopMargin(int l)
+  {
+    if(l > bottomMargin) 
+    {
+      topMargin = bottomMargin;
+      bottomMargin = l;
+    }
+    else
+      topMargin = l;
+    if(topMargin < 0) topMargin = 0;
+    if(bottomMargin > size.height - 1) bottomMargin = size.height - 1;
+  }
+
+  /**
+   * Get the top scroll margin.
+   */
+  public int getTopMargin()
+  {
+    return topMargin;
+  }
+
+  /**
+   * Set the bottom scroll margin for the screen. If the current top margin
+   * is bigger it will become the bottom margin and the line will become the
+   * top margin.
+   * @param l line that is the margin
+   */
+  public void setBottomMargin(int l)
+  {
+    if(l < topMargin) 
+    {
+      bottomMargin = topMargin;
+      topMargin = l;
+    }
+    else
+      bottomMargin = l;
+    if(topMargin < 0) topMargin = 0;
+    if(bottomMargin > size.height - 1) bottomMargin = size.height - 1;
+  }
+
+  /**
+   * Get the bottom scroll margin.
+   */
+  public int getBottomMargin()
+  {
+    return bottomMargin;
+  }
+    
+  /**
+   * Set scrollback buffer size.
+   * @param amount new size of the buffer
+   */
+  public void setBufferSize(int amount)
+  {
+    screenLocked = true;
+
+    if(amount < size.height) amount = size.height;
+    if(amount < maxBufSize)
+    {
+      char cbuf[][] = new char[amount][size.width];
+      int abuf[][] = new int[amount][size.width];
+      System.arraycopy(charArray, bufSize - amount, cbuf, 0, amount);
+      System.arraycopy(charAttributes, bufSize - amount, abuf, 0, amount);
+      charArray = cbuf;
+      charAttributes = abuf;
+    }
+    maxBufSize = amount;
+    screenLocked = false;
+
+    repaint();
+  }
+
+  /**
+   * Retrieve current scrollback buffer size.
+   * @see #setBufferSize
+   */
+  public int getBufferSize()
+  {
+    return bufSize;
+  }
+
+  /**
+   * Retrieve maximum buffer Size.
+   * @see #getBufferSize
+   */
+  public int getMaxBufferSize()
+  {
+    return maxBufSize;
+  }
+
+  /**
+   * Set the current window base. This allows to view the scrollback buffer.
+   * @param line the line where the screen window starts
+   * @see setBufferSize
+   * @see getBufferSize
+   */
+  public void setWindowBase(int line)
+  {
+    if(line > screenBase) line = screenBase;
+    else if(line < 0) line = 0;
+    windowBase = line;
+    repaint();
+  }
+
+  /**
+   * Get the current window base.
+   * @see setWindowBase
+   */
+  public int getWindowBase()
+  {
+    return windowBase;
+  }
+
+  /**
+   * Change the size of the screen. This will include adjustment of the 
+   * scrollback buffer.
+   * @param columns width of the screen
+   * @param columns height of the screen
+   */
+  public void setWindowSize(int width, int height)
+  {
+    char cbuf[][];
+    int abuf[][];
+    int bsize = bufSize;
+
+    if(width < 1 || height < 1) return;
+
+    screenLocked = true;
+    
+    super.update(getGraphics());
+    
+    if(height > maxBufSize) 
+      maxBufSize = height;
+    if(height > bufSize)
+    {
+      bufSize = height;
+      screenBase = 0;
+      windowBase = 0;
+    }
+
+    cbuf = new char[bufSize][width];
+    abuf = new int[bufSize][width];
+    
+    for(int i = 0; i < bsize && i < bufSize; i++)
+    {
+      System.arraycopy(charArray[i], 0, cbuf[i], 0, 
+           width < size.width ? width : size.width);
+      System.arraycopy(charAttributes[i], 0, abuf[i], 0, 
+           width < size.width ? width : size.width);
+    }
+    charArray = cbuf;
+    charAttributes = abuf;
+    size = new Dimension(width, height);
+    topMargin = 0;
+    bottomMargin = height - 1;
+    update = new boolean[height + 1];
+    for(int i = 0; i <= height; i++) update[i] = true;
+    screenLocked = false;
+  }
+
+  /**
+   * Set the strategy when window is resized.
+   * RESIZE_FONT is default.
+   * @param strategy the strategy
+   * @see #RESIZE_NONE
+   * @see #RESIZE_FONT
+   * @see #RESIZE_SCREEN
+   */
+  public void setResizeStrategy(int strategy)
+  {
+    resizeStrategy = strategy;
+  }
+  
+  /**
+   * Get amount of rows on the screen.
+   */
+  public int getRows() { return size.height; }
+
+  /**
+   * Get amount of columns on the screen.
+   */
+  public int getColumns() { return size.width; }
+
+  /**
+   * Set the border thickness and the border type.
+   * @param thickness border thickness in pixels, zero means no border
+   * @param raised a boolean indicating a raised or embossed border
+   */
+  public void setBorder(int thickness, boolean raised)
+  {
+    if(thickness == 0) insets = null;
+    else insets = new Insets(thickness+1, thickness+1, 
+                             thickness+1, thickness+1);
+    this.raised = raised;
+  }
+
+  /**
+   * Set the scrollbar position. valid values are "East" or "West".
+   * @param position the position of the scrollbar
+   */
+  public void setScrollbar(String position)
+  {
+    add(scrollBar = new Scrollbar());
+    scrollBar.setValues(windowBase, size.height, 0, bufSize - size.height);
+    scrBarPos = position;
+  }
+  
+  /**
+   * Mark lines to be updated with redraw().
+   * @param l starting line
+   * @param n amount of lines to be updated
+   * @see #redraw
+   */
+  public void markLine(int l, int n)
+  {
+    l = checkBounds(l, 0, size.height - 1);
+    for(int i = 0; i < n && l + i < size.height; i++) 
+      update[l + i + 1] = true;
+  }
+  
+  /**
+   * Redraw marked lines.
+   * @see #markLine
+   */
+  public void redraw()
+  {
+    update[0] = true;
+    repaint();
+  }
+
+  /**
+   * Update the display. to reduce flashing we have overridden this method.
+   */
+  public void update(Graphics g)
+  {
+    paint(g);
+  }
+  
+  /**
+   * Paint the current screen. All painting is done here. Only lines that have
+   * changed will be redrawn!
+   */
+  public synchronized void paint(Graphics g)
+  {
+    if(screenLocked) return;
+    int xoffset = (super.size().width - size.width * charWidth - 
+                   (scrollBar != null ? 15 : 0)) / 2;
+    int yoffset = (super.size().height - size.height * charHeight) / 2;
+
+    Color fg = color[COLOR_FG_STD];
+    Color bg = color[COLOR_BG_STD];
+
+    if(scrollBar != null && scrBarPos.equals("West")) xoffset += 15;
+    
+    g.setFont(normalFont);
+
+    for(int l = 0; l < size.height; l++)
+    {
+      if(update[0] && !update[l + 1]) continue;
+      update[l + 1] = false;
+      for(int c = 0; c < size.width; c++)
+      {
+        int addr = 0;
+        int currAttr = charAttributes[windowBase + l][c];
+
+        fg = color[COLOR_FG_STD];
+       bg = color[COLOR_BG_STD];
+        if ((currAttr & COLOR_FG) != 0) {
+          fg = color[((currAttr & COLOR_FG) >> 3)-1];
+        }
+        if ((currAttr & COLOR_BG) != 0) {
+          bg = color[((currAttr & COLOR_BG) >> 7)-1];
+        }
+
+        if((currAttr & BOLD) != 0) {
+          if(fg.equals(Color.black))
+            fg = Color.gray;
+          else fg = fg.darker();
+        }
+        if((currAttr & INVERT) != 0) { Color swapc = bg; bg=fg;fg=swapc; }
+
+        if (sf.inSoftFont(charArray[windowBase + l][c])) {
+          g.setColor(bg);      
+          g.fillRect(c * charWidth + xoffset, l * charHeight + yoffset, 
+                    charWidth, charHeight);
+          g.setColor(fg);      
+          sf.drawChar(g,charArray[windowBase + l][c],xoffset+c*charWidth,
+                     l*charHeight+yoffset, charWidth, charHeight);
+          continue;
+        }
+        
+       // determine the maximum of characters we can print in one go
+        while(c + addr < size.width && 
+              charAttributes[windowBase + l][c + addr] == currAttr &&
+              !sf.inSoftFont(charArray[windowBase + l ][c+addr])
+        ) {
+          if(charArray[windowBase + l][c + addr] < ' ')
+            charArray[windowBase + l][c + addr] = ' ';
+          addr++;
+        }
+        
+        // clear the part of the screen we want to change (fill rectangle)
+        g.setColor(bg);
+        g.fillRect(c * charWidth + xoffset, l * charHeight + yoffset,
+                   addr * charWidth, charHeight);
+
+        g.setColor(fg);
+        
+       // draw the characters
+        g.drawChars(charArray[windowBase + l], c, addr, 
+                    c * charWidth + xoffset, 
+                    (l+1) * charHeight - charDescent + yoffset);
+
+        if((currAttr & UNDERLINE) != 0)
+          g.drawLine(c * charWidth + 1 + xoffset,
+                     (l+1) * charHeight - charDescent / 2 + yoffset,
+                     c * charWidth + addr * charWidth + xoffset, 
+                     (l+1) * charHeight - charDescent / 2 + yoffset);
+        
+        c += addr - 1;
+      }
+    }
+
+    // draw cursor
+    if(screenBase + cursorY >= windowBase && 
+       screenBase + cursorY < windowBase + size.height)
+    {
+      g.setColor(color[COLOR_FG_STD]);
+      g.setXORMode(color[COLOR_BG_STD]);
+      g.fillRect( cursorX * charWidth + xoffset, 
+                 (cursorY + screenBase - windowBase) * charHeight + yoffset,
+                 charWidth, charHeight);
+      g.setPaintMode();
+    }
+
+    if(windowBase <= selectBegin.y || windowBase <= selectEnd.y) {
+      int beginLine = selectBegin.y - windowBase;
+      int endLine = selectEnd.y - selectBegin.y;
+      if(beginLine < 0) {
+        endLine += beginLine;
+        beginLine = 0;
+      }
+      if(endLine > size.height) endLine = size.height - beginLine;
+       
+      g.setXORMode(color[COLOR_BG_STD]);
+      g.fillRect(selectBegin.x * charWidth + xoffset,
+                 beginLine * charHeight + yoffset,
+                 (endLine == 0 ? (selectEnd.x - selectBegin.x) : 
+                  (size.width - selectBegin.x)) 
+                 * charWidth,
+                 charHeight);
+      if(endLine > 1)
+        g.fillRect(0 + xoffset, 
+                   (beginLine + 1) * charHeight + yoffset, 
+                   size.width * charWidth, 
+                   (endLine - 1) * charHeight);
+      if(endLine > 0)
+        g.fillRect(0 + xoffset, 
+                   (beginLine + endLine) * charHeight + yoffset,
+                   selectEnd.x * charWidth, 
+                   charHeight);
+      g.setPaintMode();
+    }
+
+    if(insets != null) {
+      g.setColor(getBackground());
+      xoffset--; yoffset--;
+      for(int i = insets.top - 1; i >= 0; i--)
+        g.draw3DRect(xoffset - i, yoffset - i,
+                     charWidth * size.width + 1 + i * 2, 
+                     charHeight * size.height + 1 + i * 2,
+                     raised);
+    }
+
+    update[0] = false;
+  }
+
+  private int checkBounds(int value, int lower, int upper)
+  {
+    if(value < lower) return lower;
+    if(value > upper) return upper;
+    return value;
+  }
+
+  /**
+   * Reshape character display according to resize strategy.
+   * @see #setResizeStrategy
+   */
+  public void reshape(int x, int y, int w, int h)
+  {
+    if(debug > 0)
+      System.err.println("CharDisplay: reshape("+x+","+y+","+w+","+h+")");
+
+    int xborder = 0, yborder = 0;
+    
+    if(insets != null) {
+      w -= (xborder = insets.left + insets.right);
+      h -= (yborder = insets.top + insets.bottom);
+    }
+    if(scrollBar != null) { w -= 15;}
+
+    Font tmpFont = normalFont;
+    String fontName = normalFont.getName();
+    fm = getFontMetrics(normalFont);
+    if(fm != null)
+    {
+      charWidth = fm.charWidth('@');
+      charHeight = fm.getHeight();
+    }
+    
+    switch(resizeStrategy)
+    {
+    case RESIZE_SCREEN:
+      setWindowSize(w / charWidth, size.height = h / charHeight);
+      break;
+    case RESIZE_FONT:
+      int height = h / size.height;
+      int width = w / size.width;
+      
+      fm = getFontMetrics(normalFont = new Font(fontName, Font.PLAIN, 
+                                                charHeight));
+      
+      // adapt current font size (from small up to best fit)
+      if(fm.getHeight() < height || fm.charWidth('@') < width)
+        do {
+          fm = getFontMetrics(normalFont = new Font(fontName, Font.PLAIN, 
+                                                    ++charHeight));
+        } while(fm.getHeight() < height || 
+                fm.charWidth('@') < width); 
+      
+      // now check if we got a font that is too large
+      if(fm.getHeight() > height || fm.charWidth('@') > width)
+        do {
+          fm = getFontMetrics(normalFont = new Font(fontName, Font.PLAIN, 
+                                                    --charHeight));
+        } while(charHeight > 1 && 
+                (fm.getHeight() > height || 
+                 fm.charWidth('@') > width));
+      
+      if(charHeight <= 1) 
+      {
+        System.err.println("CharDisplay: error during resize, resetting");
+        normalFont = tmpFont;
+        System.err.println("CharDisplay: disabling font/screen resize");
+        resizeStrategy = RESIZE_NONE;
+      }
+
+      setFont(normalFont);
+      fm = getFontMetrics(normalFont);
+      charWidth = fm.charWidth('@');
+      charHeight = fm.getHeight();
+      charDescent = fm.getDescent();
+      break;
+    case RESIZE_NONE:
+    default:
+      break;
+    }
+    if(debug > 0)
+    {
+      System.err.println("CharDisplay: charWidth="+charWidth+", "+
+                         "charHeight="+charHeight+", "+
+                         "charDescent="+charDescent);
+      System.err.println("CharDisplay: "+normalFont+", "+fm);
+    }
+    super.reshape(x, y, 
+                  w + xborder + (scrollBar != null ? 15 : 0), 
+                  h + yborder);
+    
+    if(scrollBar != null) {
+      int xoffset = (super.size().width - size.width * charWidth - 15) / 2;
+      int yoffset = (super.size().height - size.height * charHeight) / 2;
+      if(scrBarPos.equals("West"))
+        scrollBar.reshape(xoffset - (xborder / 2), yoffset - yborder / 2,
+                          15, size.height * charHeight + yborder);
+      else
+        scrollBar.reshape(xoffset + (xborder / 2) + size.width * charWidth, 
+                          yoffset - yborder / 2, 15, 
+                          size.height * charHeight + yborder);
+    }
+  }
+
+  /**
+   * Return the real size in points of the character display.
+   * @return Dimension the dimension of the display
+   * @see java.awt.Dimension
+   */
+  public Dimension size()
+  {
+    int xborder = 0, yborder = 0;
+    if(insets != null) {
+      xborder = insets.left + insets.right;
+      yborder = insets.top + insets.bottom;
+    }
+    if(scrollBar != null) xborder += 15;
+    
+    return new Dimension(size.width * charWidth + xborder, 
+                         size.height * charHeight + yborder);
+  }
+
+  /**
+   * Return the preferred Size of the character display.
+   * This turns out to be the actual size.
+   * @return Dimension dimension of the display
+   * @see size
+   */
+  public Dimension preferredSize() 
+  {
+    return size();
+  }
+
+  /**
+   * The insets of the character display define the border.
+   * @return Insets border thickness in pixels
+   */
+  public Insets insets() 
+  {
+    return insets == null ? super.insets() : insets;
+  }
+
+  /**
+   * Handle mouse events for copy & paste
+   * @param evt the event that occured
+   * @return boolean true if action was taken
+   * @see java.awt.Event
+   */
+  public boolean handleEvent(Event evt) 
+  {
+    // handle scrollbar events
+    if(evt != null && evt.target == scrollBar && evt.arg != null) {
+      int val = ((Integer)evt.arg).intValue();
+      setWindowBase(val);
+      return true;
+    }
+
+    if(evt.id == Event.MOUSE_DOWN || evt.id == Event.MOUSE_UP ||
+       evt.id == Event.MOUSE_DRAG) {
+      int xoffset = (super.size().width - size.width * charWidth) / 2;
+      int yoffset = (super.size().height - size.height * charHeight) / 2;
+      switch(evt.id) {
+      case Event.MOUSE_DOWN:
+        selectBegin.x = (evt.x - xoffset) / charWidth;
+        selectBegin.y = (evt.y - yoffset) / charHeight + windowBase;
+        selectEnd.x = selectBegin.x;
+        selectEnd.y = selectBegin.y;
+        if(selectFrame != null) selectFrame.hide();
+        break;
+      case Event.MOUSE_UP:
+      case Event.MOUSE_DRAG:
+        int x = (evt.x - xoffset) / charWidth;
+        int y = (evt.y - yoffset) / charHeight + windowBase;
+        int oldx = selectEnd.x, oldy = selectEnd.y;
+
+        if((x < selectBegin.x && y < selectBegin.y) &&
+           (x < selectEnd.x && y < selectEnd.y)) {
+          selectBegin.x = x;
+          selectBegin.y = y;
+        } else {
+          selectEnd.x = x;
+          selectEnd.y = y;
+        }
+
+        if(evt.id == Event.MOUSE_UP) {
+          if(selectBegin.x == selectEnd.x &&
+             selectBegin.y == selectEnd.y) {
+            repaint();
+            return true;
+          }
+          String tmp = "";
+         // fix end.x and end.y, they can get over the border
+         if (selectEnd.x < 0) selectEnd.x = 0;
+         if (selectEnd.y < 0) selectEnd.y = 0;
+         if (selectEnd.y >= charArray.length) {
+               selectEnd.y = charArray.length-1;
+         }
+         if (selectEnd.x >= charArray[0].length) {
+               selectEnd.x = charArray[0].length-1;
+         }
+          for(int l = selectBegin.y; l <= selectEnd.y; l++)
+            if(l == selectBegin.y) 
+              tmp = (new String(charArray[l])).substring(selectBegin.x) + "\n";
+            else if(l == selectEnd.y) 
+              tmp += (new String(charArray[l])).substring(0, selectEnd.x);
+            else tmp += new String(charArray[l]) + "\n";
+          if(selectFrame == null) {
+           /* for jdk-1.1
+           String s=(String) ((StringSelection)this.getToolkit().
+                              getSystemClipboard().
+                              getContents(this)).
+             getTransferData(DataFlavor.stringFlavor);
+           System.out.println(s);
+           */
+
+            selectFrame = new Frame("Pasteboard");
+            selection = new TextArea();
+            selection.setFont(normalFont);
+            selectFrame.add("Center", selection);
+            selectFrame.add("South", new Label("Click on the terminal window"+
+                                               " to hide!"));
+            selectFrame.pack();
+          }
+          selection.setText(tmp);
+          //selectFrame.show();
+          selection.selectAll();
+          repaint();
+        } else
+          if(oldx != x || oldy != y) repaint();
+        break;
+      }
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/src/de/mud/telnet/display/SoftFont.java b/src/de/mud/telnet/display/SoftFont.java
new file mode 100644 (file)
index 0000000..73dddb5
--- /dev/null
@@ -0,0 +1,1009 @@
+package de.mud.telnet.display;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+/*
+ * SoftFont -- a unicode softfont displayer
+ * --
+ * $Id: SoftFont.java,v 1.8 1997/11/03 17:10:26 marcus Exp $
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.awt.*;
+import java.util.*;
+/**********************************************/
+/*                                            */
+/*       Font file generated by cpi2fnt       */
+/*                                            */
+/**********************************************/
+
+public class SoftFont {
+       final static private char       SF_BITMAP = 0;
+       final static private char       SF_FILLRECT = 1;
+
+
+       final static private char       SF_CHAR = 0;
+       final static private char       SF_WIDTH= 1;
+       final static private char       SF_HEIGHT= 2;
+       final static private char       SF_TYPE  = 3;
+       final static private char       SF_DATA  = 4;
+       java.util.Hashtable font;
+       /** 
+        * softfont characterdata
+        */
+       private static char[][] fontdata = {
+       
+       {0x01,8,8,SF_BITMAP, /* 1 0x01 '^A' */
+       0x7e, /* 01111110 */
+       0x81, /* 10000001 */
+       0xa5, /* 10100101 */
+       0x81, /* 10000001 */
+       0xbd, /* 10111101 */
+       0x99, /* 10011001 */
+       0x81, /* 10000001 */
+       0x7e, /* 01111110 */
+       },{ 0x02,8,8,SF_BITMAP,/* 2 0x02 '^B' */
+       0x7e, /* 01111110 */
+       0xff, /* 11111111 */
+       0xdb, /* 11011011 */
+       0xff, /* 11111111 */
+       0xc3, /* 11000011 */
+       0xe7, /* 11100111 */
+       0xff, /* 11111111 */
+       0x7e, /* 01111110 */
+       },{ 0x03,8,8,SF_BITMAP,/* 3 0x03 '^C' */
+       0x6c, /* 01101100 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0x10, /* 00010000 */
+       0x00, /* 00000000 */
+       },{ 0x04,8,8,SF_BITMAP,/* 4 0x04 '^D' */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x7c, /* 01111100 */
+       0xfe, /* 11111110 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0x10, /* 00010000 */
+       0x00, /* 00000000 */
+       },{ 0x05,8,8,SF_BITMAP,/* 5 0x05 '^E' */
+       0x38, /* 00111000 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xd6, /* 11010110 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       },{ 0x06,8,8,SF_BITMAP,/* 6 0x06 '^F' */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x7c, /* 01111100 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0x7c, /* 01111100 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       },{ 0x2666,8,8,SF_BITMAP,/* 9830 0x2666 BLACK DIAMOND */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       },{ 0x07,8,8,SF_BITMAP,/* 7 0x07 '^G' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       },{ 0x08,8,8,SF_BITMAP,/* 8 0x08 '^H' */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xe7, /* 11100111 */
+       0xc3, /* 11000011 */
+       0xc3, /* 11000011 */
+       0xe7, /* 11100111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       },{ 0x09,8,8,SF_BITMAP,/* 9 0x09 '^I' */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x42, /* 01000010 */
+       0x42, /* 01000010 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       },{ 0x0a,8,8,SF_BITMAP,/* 10 0x0a '^J' */
+       0xff, /* 11111111 */
+       0xc3, /* 11000011 */
+       0x99, /* 10011001 */
+       0xbd, /* 10111101 */
+       0xbd, /* 10111101 */
+       0x99, /* 10011001 */
+       0xc3, /* 11000011 */
+       0xff, /* 11111111 */
+       },{ 0x0b,8,8,SF_BITMAP,/* 11 0x0b '^K' */
+       0x0f, /* 00001111 */
+       0x07, /* 00000111 */
+       0x0f, /* 00001111 */
+       0x7d, /* 01111101 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x78, /* 01111000 */
+       },{ 0x0c,8,8,SF_BITMAP,/* 12 0x0c '^L' */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       },{ 0x0d,8,8,SF_BITMAP,/* 13 0x0d '^M' */
+       0x3f, /* 00111111 */
+       0x33, /* 00110011 */
+       0x3f, /* 00111111 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x70, /* 01110000 */
+       0xf0, /* 11110000 */
+       0xe0, /* 11100000 */
+       },{ 0x0e,8,8,SF_BITMAP,/* 14 0x0e '^N' */
+       0x7f, /* 01111111 */
+       0x63, /* 01100011 */
+       0x7f, /* 01111111 */
+       0x63, /* 01100011 */
+       0x63, /* 01100011 */
+       0x67, /* 01100111 */
+       0xe6, /* 11100110 */
+       0xc0, /* 11000000 */
+       },{ 0x0f,8,8,SF_BITMAP,/* 15 0x0f '^O' */
+       0x18, /* 00011000 */
+       0xdb, /* 11011011 */
+       0x3c, /* 00111100 */
+       0xe7, /* 11100111 */
+       0xe7, /* 11100111 */
+       0x3c, /* 00111100 */
+       0xdb, /* 11011011 */
+       0x18, /* 00011000 */
+       },{ 0x10,8,8,SF_BITMAP,/* 16 0x10 '^P' */
+       0x80, /* 10000000 */
+       0xe0, /* 11100000 */
+       0xf8, /* 11111000 */
+       0xfe, /* 11111110 */
+       0xf8, /* 11111000 */
+       0xe0, /* 11100000 */
+       0x80, /* 10000000 */
+       0x00, /* 00000000 */
+       },{ 0x11,8,8,SF_BITMAP,/* 17 0x11 '^Q' */
+       0x02, /* 00000010 */
+       0x0e, /* 00001110 */
+       0x3e, /* 00111110 */
+       0xfe, /* 11111110 */
+       0x3e, /* 00111110 */
+       0x0e, /* 00001110 */
+       0x02, /* 00000010 */
+       0x00, /* 00000000 */
+       },{ 0x12,8,8,SF_BITMAP,/* 18 0x12 '^R' */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       },{ 0x13,8,8,SF_BITMAP,/* 19 0x13 '^S' */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       },{ 0x14,8,8,SF_BITMAP,/* 20 0x14 '^T' */
+       0x7f, /* 01111111 */
+       0xdb, /* 11011011 */
+       0xdb, /* 11011011 */
+       0x7b, /* 01111011 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x00, /* 00000000 */
+       },{ 0x15,8,8,SF_BITMAP,/* 21 0x15 '^U' */
+       0x3e, /* 00111110 */
+       0x61, /* 01100001 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x86, /* 10000110 */
+       0x7c, /* 01111100 */
+       },{ 0x16,8,8,SF_BITMAP,/* 22 0x16 '^V' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       },{ 0x17,8,8,SF_BITMAP,/* 23 0x17 '^W' */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       },{ 0x18,8,8,SF_BITMAP,/* 24 0x18 '^X' */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       },{ 0x19,8,8,SF_BITMAP,/* 25 0x19 '^Y' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       },{ 0x1a,8,8,SF_BITMAP,/* 26 0x1a '^Z' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0xfe, /* 11111110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       },{ 0x1b,8,8,SF_BITMAP,/* 27 0x1b '^[' */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xfe, /* 11111110 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       },{ 0x1c,8,8,SF_BITMAP,/* 28 0x1c '^\' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       },{ 0x1d,8,8,SF_BITMAP,/* 29 0x1d '^]' */
+       0x00, /* 00000000 */
+       0x24, /* 00100100 */
+       0x66, /* 01100110 */
+       0xff, /* 11111111 */
+       0x66, /* 01100110 */
+       0x24, /* 00100100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       },{ 0x1e,8,8,SF_BITMAP,/* 30 0x1e '^^' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       },{ 0x1f,8,8,SF_BITMAP,/* 31 0x1f '^_' */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       },{ 0x7f,8,8,SF_BITMAP,/* 127 0x7f '\7f' */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       },{ 0x2591,8,8,SF_BITMAP,/* LIGHT SHADE */
+       0x22, /* 00100010 */
+       0x88, /* 10001000 */
+       0x22, /* 00100010 */
+       0x88, /* 10001000 */
+       0x22, /* 00100010 */
+       0x88, /* 10001000 */
+       0x22, /* 00100010 */
+       0x88, /* 10001000 */
+       },{ 0x2592,8,8,SF_BITMAP,/* MEDIUM SHADE */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       },{ 0x2593,8,8,SF_BITMAP,/* DARK SHADE */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       },{ 0x221a,8,8,SF_BITMAP,/* SQUARE ROOT */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       },{ 0x2320,8,8,SF_BITMAP,/* UPPER INTERVAL*/
+        0x0e, /* 00001110 */
+        0x1b, /* 00011011 */
+        0x1b, /* 00011011 */
+        0x18, /* 00011000 */
+        0x18, /* 00011000 */
+        0x18, /* 00011000 */
+        0x18, /* 00011000 */
+        0x18, /* 00011000 */
+       },{ 0x25a0,8,8,SF_FILLRECT,/* BLACK SQUARE */
+               0x2244,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00111100 */
+               /* 00111100 */
+               /* 00111100 */
+               /* 00111100 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x2502,8,8,SF_FILLRECT,/*BOX DRAWINGS LIGHT VERTICAL*/
+               0x3028,
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2524,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT VERTICAL AND LEFT */
+               0x3028,
+               0x0431,
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 11111000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2561,8,8,SF_FILLRECT,/*BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE*/
+               0x3028,
+               0x0231,
+               0x0431,
+               /* 00011000 */
+               /* 00011000 */
+               /* 11111000 */
+               /* 00011000 */
+               /* 11111000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2562,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE */
+               0x2028,
+               0x5028,
+               0x0421,
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 11110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x2556,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE */
+               0x0471,
+               0x2523,
+               0x5523,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x2555,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE */
+               0x3226,
+               0x0231,
+               0x0431,
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111000 */
+               /* 00011000 */
+               /* 11111000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2563,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL AND LEFT*/
+               0x2022,
+               0x0221,
+               0x0421,
+               0x2424,
+               0x5028,
+               /* 00110110 */
+               /* 00110110 */
+               /* 11110110 */
+               /* 00000110 */
+               /* 11110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x2551,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL */
+               0x2028,
+               0x5028,
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x2557,8,8,SF_FILLRECT,/*  BOX DRAWINGS DOUBLE DOWN AND LEFT */
+               0x0271,
+               0x5325,
+               0x0441,
+               0x2523,
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111110 */
+               /* 00000110 */
+               /* 11110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x255d,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE UP AND LEFT */
+               0x2022,
+               0x0241,
+               0x5025,
+               0x0451,
+               /* 00110110 */
+               /* 00110110 */
+               /* 11110110 */
+               /* 00000110 */
+               /* 11111110 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x255c,8,8,SF_FILLRECT,/* BOX DRAWINGS UP DOUBLE AND LEFT SINGLE */
+               0x2024,
+               0x5024,
+               0x0471,
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 11111110 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x255b,8,8,SF_FILLRECT,/* BOX DRAWINGS UP SINGLE AND LEFT DOUBLE */
+               0x3025,
+               0x0231,
+               0x0431,
+               /* 00011000 */
+               /* 00011000 */
+               /* 11111000 */
+               /* 00011000 */
+               /* 11111000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x2510,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT DOWN AND LEFT */
+               0x0451,
+               0x3523,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2514,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT UP AND RIGHT */
+               0x3025,
+               0x5431,
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x2534,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT UP AND HORIZONTAL */
+               0x3024,
+               0x0481,
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x252c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
+               0x0481,
+               0x3523,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x251c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+               0x3028,
+               0x5431,
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011111 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2500,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT HORIZONTAL */
+               0x0481,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x253c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
+               0x3028,
+               0x0481,
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 11111111 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x255e,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE */
+               0x3028,
+               0x5231,
+               0x5431,
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011111 */
+               /* 00011000 */
+               /* 00011111 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x255f,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE */
+               0x2028,
+               0x5028,
+               0x7411,
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110111 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x255a,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE UP AND RIGHT */
+               0x2025,
+               0x5023,
+               0x7211,
+               0x4441,
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110111 */
+               /* 00110000 */
+               /* 00111111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x2554,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE DOWN AND RIGHT */
+               0x2261,
+               0x2325,
+               0x5424,
+               0x7411,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00111111 */
+               /* 00110000 */
+               /* 00110111 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x2569,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE UP AND HORIZONTAL */
+               0x2022,
+               0x0241,
+               0x5022,
+               0x5231,
+               0x0481,
+               /* 00110110 */
+               /* 00110110 */
+               /* 11110111 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x2566,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL */
+               0x0281,
+               0x0441,
+               0x2523,
+               0x5431,
+               0x5523,
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 11110111 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x2560,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL AND RIGHT */
+               0x2028,
+               0x5022,
+               0x5231,
+               0x5431,
+               0x5623,
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110111 */
+               /* 00110000 */
+               /* 00110111 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x2550,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE HORIZONTAL */
+               0x0281,
+               0x0481,
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x256c,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL */
+               0x2022,
+               0x0241,
+               0x5022,
+               0x5231,
+               0x0441,
+               0x2523,
+               0x5431,
+               0x5523,
+               /* 00110110 */
+               /* 00110110 */
+               /* 11110111 */
+               /* 00000000 */
+               /* 11110111 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x2567,8,8,SF_FILLRECT,/* BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE */
+               0x3022,
+               0x0281,
+               0x0481,
+               /* 00011000 */
+               /* 00011000 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x2568,8,8,SF_FILLRECT,/* BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE */
+               0x2024,
+               0x5024,
+               0x0481,
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x2564,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE */
+               0x0281,
+               0x0481,
+               0x3523,
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2565,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE */
+               0x0481,
+               0x2523,
+               0x5523,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x2559,8,8,SF_FILLRECT,/* BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE */
+               0x2024,
+               0x5024,
+               0x2461,
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00111111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x2558,8,8,SF_FILLRECT,/* BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE */
+               0x3025,
+               0x5231,
+               0x5431,
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011111 */
+               /* 00011000 */
+               /* 00011111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x2552,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE */
+               0x3226,
+               0x5231,
+               0x5431,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00011111 */
+               /* 00011000 */
+               /* 00011111 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2553,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE */
+               0x2461,
+               0x2523,
+               0x5523,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00111111 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x256b,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE */
+               0x2028,
+               0x5028,
+               0x0481,
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 11111111 */
+               /* 00110110 */
+               /* 00110110 */
+               /* 00110110 */
+       },{ 0x256a,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE */
+               0x3028,
+               0x0281,
+               0x0481,
+               /* 00011000 */
+               /* 00011000 */
+               /* 11111111 */
+               /* 00011000 */
+               /* 11111111 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2518,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT UP AND LEFT */
+               0x3025,
+               0x0431,
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 11111000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       },{ 0x250c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT DOWN AND RIGHT */
+               0x3451,
+               0x3523,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00011111 */
+               /* 00011000 */
+               /* 00011000 */
+               /* 00011000 */
+       },{ 0x2588,8,8,SF_FILLRECT,/* FULL BLOCK */
+               0x0088,
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+       },{ 0x2584,8,8,SF_FILLRECT,/* LOWER HALF BLOCK */
+               0x0484,
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+       },{ 0x258c,8,8,SF_FILLRECT,/* LEFT HALF BLOCK */
+               0x0048,
+               /* 11110000 */
+               /* 11110000 */
+               /* 11110000 */
+               /* 11110000 */
+               /* 11110000 */
+               /* 11110000 */
+               /* 11110000 */
+               /* 11110000 */
+       },{ 0x2590,8,8,SF_FILLRECT,/* RIGHT HALF BLOCK */
+               0x4048,
+               /* 00001111 */
+               /* 00001111 */
+               /* 00001111 */
+               /* 00001111 */
+               /* 00001111 */
+               /* 00001111 */
+               /* 00001111 */
+               /* 00001111 */
+       },{ 0x2580,8,8,SF_FILLRECT,/* UPPER HALF BLOCK */
+               0x0084,
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+               /* 11111111 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+               /* 00000000 */
+       }};
+
+       public SoftFont() {
+               font = new java.util.Hashtable();
+               for (int i=0;i<fontdata.length;i++)
+                       font.put(new Integer(fontdata[i][0]),new Integer(i));
+
+       }
+
+       public boolean inSoftFont(char c) {
+               boolean insoftfont;
+
+               insoftfont = (null!=font.get(new Integer(c)));
+               if (!insoftfont && (int)c>=0x100) {
+                       System.out.println("Character "+((int)c)+" not in softfont");
+               }
+               return insoftfont;
+       }
+
+       public void drawChar(Graphics g,char c,int x,int y,int cw,int ch) {
+               double  dw,dh;
+               Object  Ientry;
+               int     w,h,entry,i,fontwidth,fontheight;
+               
+               Ientry = font.get(new Integer(c));
+               if (Ientry == null)
+                       return;
+               entry = ((Integer)Ientry).intValue();
+               fontwidth = fontdata[entry][SF_WIDTH];
+               fontheight = fontdata[entry][SF_HEIGHT];
+
+               dw = cw*1.0/fontwidth;
+               dh = ch*1.0/fontheight;
+
+               switch (fontdata[entry][SF_TYPE]) {
+               case SF_BITMAP:
+                       for (h=0;h<fontheight;h++) {
+                               for (w=0;w<fontwidth;w++) {
+                                       //FIXME: 8 bit max currently...
+                                       if (0!=(fontdata[entry][h+SF_DATA] & (1<<(7-w)))) {
+                                               g.fillRect(
+                                                       x+(int)(w*dw),
+                                                       y+(int)(h*dh),
+                                                       ((int)((w+1)*dw))-(int)(w*dw),
+                                                       ((int)((h+1)*dh))-(int)(h*dh)
+                                               );
+                                       }
+                               }
+                       }
+                       break;
+               case SF_FILLRECT:
+                       i=SF_DATA;
+                       while (i<fontdata[entry].length) {
+                               int     xw,xh;
+
+                               w=(fontdata[entry][i]&0xF000)>>12;
+                               h=(fontdata[entry][i]&0x0F00)>>8;
+                               xw = (fontdata[entry][i]&0x00F0)>>4;
+                               xh = (fontdata[entry][i]&0x000F);
+                               g.fillRect(
+                                       x+(int)(w*dw),
+                                       y+(int)(h*dh),
+                                       ((int)((w+xw)*dw))-(int)(w*dw),
+                                       ((int)((h+xh)*dh))-(int)(h*dh)
+                               );
+                               i++;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+}
diff --git a/src/de/mud/telnet/display/Terminal.java b/src/de/mud/telnet/display/Terminal.java
new file mode 100644 (file)
index 0000000..2d14fe4
--- /dev/null
@@ -0,0 +1,73 @@
+package de.mud.telnet.display;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+/*
+ * Terminal -- Terminal emulation (abstract class)
+ * --
+ * $Id: Terminal.java,v 1.1.1.1 1997/03/05 13:35:16 leo Exp $
+ * $timestamp: Wed Mar  5 11:27:13 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.awt.Panel;
+import java.awt.Dimension;
+
+/**
+ * Terminal is an abstract emulation class.
+ * It contains a character display.
+ *
+ * @version $Id: Terminal.java,v 1.1.1.1 1997/03/05 13:35:16 leo Exp $
+ * @author  Matthias L. Jugel, Marcus Meißner
+ */
+public abstract class Terminal extends Panel
+{
+       /**
+        * Get the specific parameter info for the emulation.
+        * @see java.applet.Applet
+        */
+       public abstract String[][] getParameterInfo();
+  
+       /**
+        * Put a character on the screen. The method has to see if it is
+        * a special character that needs to be handles special.
+        * @param c the character
+        * @see #putString
+        */
+       public abstract void putChar(char c);
+       
+       /**
+        * Put a character on the screen. The method has to parse the string
+        * may handle special characters.
+        * @param s the string
+        * @see #putString
+        */
+       public abstract void putString(String s);
+
+       /**
+        * Return the current size of the terminal in characters.
+        */
+       public abstract Dimension getSize();
+  
+       /**
+        * Return actual terminal type identifier.
+        */
+       public abstract String getTerminalType();
+}
diff --git a/src/de/mud/telnet/display/TerminalHost.java b/src/de/mud/telnet/display/TerminalHost.java
new file mode 100644 (file)
index 0000000..341add3
--- /dev/null
@@ -0,0 +1,46 @@
+package de.mud.telnet.display;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+/*
+ * TerminalHost -- this interface defines the remote end of the connection
+ *                 from our Terminal to the Host (virtual).
+ * --
+ * $Id: TerminalHost.java,v 1.1.1.1 1997/03/05 13:35:16 leo Exp $
+ * $timestamp: Wed Mar  5 12:01:31 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * TerminalHost is an interface for the remote (virtual) end of our connection
+ * to the host computer we are connected to.
+ * @version $Id: TerminalHost.java,v 1.1.1.1 1997/03/05 13:35:16 leo Exp $
+ * @author Matthias L Jugel, Marcus Meißner
+ */
+public interface TerminalHost
+{
+       /**
+        * Send a string to the host and return if it was received successfully.
+        * @param s the string to send
+        * @return True for successful receivement.
+        */
+       public boolean send(String s);
+}
+
diff --git a/src/de/mud/telnet/display/vt320.java b/src/de/mud/telnet/display/vt320.java
new file mode 100644 (file)
index 0000000..cc7ff1f
--- /dev/null
@@ -0,0 +1,2018 @@
+package de.mud.telnet.display;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+/*
+ * vt320 -- a DEC VT320 Terminal emulation
+ * --
+ * $Id: vt320.java,v 1.56 1998/03/07 23:47:03 marcus Exp $
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.awt.Scrollbar;
+import java.awt.Event;
+import java.awt.Dimension;
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.util.Vector;
+
+import java.applet.Applet;
+
+/**
+ * A DEC VT320 Terminal Emulation (includes VT100/220 and ANSI).
+ *
+ * The terminal emulation accesses the applet parameters to configure itself.
+ * The following parameters may be set. Default values are written in
+ * <I>italics</I> and other possible values are <B>bold</B>.
+ * <DL>
+ *  <DT><TT>&lt;PARAM NAME="Fx" VALUE="<I>functionkeytext</I>"&gt</TT>
+ *  <DD>Sets the string sent when the function key Fx (x between 1 und 20)
+ *     is pressed.
+ *  <DT><TT>&lt;PARAM NAME="VTcolumns" VALUE="<I>80</I>"&gt</TT>
+ *  <DD>Sets the columns of the terminal initially. If the parameter
+ *      VTresize is set to <B>screen</B> this may change, else it is fixed.
+ *  <DT><TT>&lt;PARAM NAME="VTrows" VALUE="<I>24</I>"&gt</TT>
+ *  <DD>Sets the rows of the terminal initially. If the parameter
+ *      value of VTresize <B>screen</B> this may change!
+ *  <DT><TT>&lt;PARAM NAME="VTfont" VALUE="<I>Courier</I>"&gt</TT>
+ *  <DD>Sets the font to be used for the terminal. It is recommended to
+ *      use <I>Courier</I> or at least a fixed width font.
+ *  <DT><TT>&lt;PARAM NAME="VTfontsize" VALUE="<I>14</I>"&gt</TT>
+ *  <DD>Sets the font size for the terminal. If the parameter
+ *      value of VTresize is set to <B>font</B> this may change!
+ *  <DT><TT>&lt;PARAM NAME="VTresize" VALUE="<I>font</I>"&gt</TT>
+ *  <DD>This parameter determines what the terminal should do if the window
+ *      is resized. The default setting <I><B>font</B></I> will result in
+ *      resizing the font until is matches the window best. Other possible
+ *      values are <B>none</B> or <B>screen</B>. <B>none</B> will let nothing
+ *      happen and <B>screen</B> will let the display try to change the
+ *      amount of rows and columns to match the window best.
+ *  <DT><TT>&lt;PARAM NAME="VTscrollbar" VALUE="<I>false</I>"&gt</TT>
+ *  <DD>Setting this parameter to <B>true</B> will add a scrollbar west to
+ *      the terminal. Other possible values include <B>left</B> to put the
+ *      scrollbar on the left side of the terminal and <B>right</B> to put it
+ *      explicitely to the right side.
+ *  <DT><TT>&lt;PARAM NAME="VTid" VALUE="<I>vt320</I>"&gt</TT>
+ *  <DD>This parameter will override the terminal id <I>vt320</I>. It may
+ *      be used to determine special terminal abilities of VT Terminals.
+ *  <DT><TT>&lt;PARAM NAME="VTbuffer" VALUE="<I>xx</I>"&gt</TT>
+ *  <DD>Initially this parameter is the same as the VTrows parameter. It
+ *      cannot be less than the amount of rows on the display. It determines
+ *      the available scrollback buffer.
+ *  <DT><TT>&lt;PARAM NAME="VTcharset" VALUE="<I>none</I>"&gt</TT>
+ *  <DD>Setting this parameter to <B>ibm</B> will enable mapping of ibm
+ *      characters (as used in PC BBS systems) to UNICODE characters. Note
+ *      that those special characters probably won't show on UNIX systems
+ *      due to lack in X11 UNICODE support.
+ *  <DT><TT>&lt;PARAM NAME="VTvms" VALUE="<I>false</I>"&gt</TT>
+ *  <DD>Setting this parameter to <B>true</B> will change the Backspace key
+ *      into a delete key, cause the numeric keypad keys to emit VT100
+ *      codes when Ctrl is pressed, and make other VMS-important keyboard
+ *      definitions.
+ *  <DT><TT>&lt;PARAM NAME="F<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
+ *  <DD>Function keys from <I>F1</I> to <I>F20</I> are programmable. You can
+ *      install any possible string including special characters, such as
+ *      <TABLE BORDER>
+ *      <TR><TD><TT>\e</TT></TD><TD>Escape</TD>
+ *      <TR><TD><TT>\b</TT></TD><TD>Backspace</TD>
+ *      <TR><TD><TT>\n</TT></TD><TD>Newline</TD>
+ *      <TR><TD><TT>\r</TT></TD><TD>Return</TD>
+ *      </TABLE>
+ *  <DT><TT>&lt;PARAM NAME="CF<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
+ *  <DD>Function keys (with the Control-key pressed) from <I>CF1</I> to <I>CF20</I> are programmable too.
+ *  <DT><TT>&lt;PARAM NAME="SF<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
+ *  <DD>Function keys (with the Shift-key pressed) from <I>SF1</I> to <I>SF20</I> are programmable too.
+ *  <DT><TT>&lt;PARAM NAME="AF<I>nr</I>" VALUE="<I>string</I>"&gt</TT>
+ *  <DD>Function keys (with the Alt-key pressed) from <I>AF1</I> to <I>AF20</I> are programmable too.
+ * </DL>
+ * @version $Id: vt320.java,v 1.56 1998/03/07 23:47:03 marcus Exp $
+ * @author  Matthias L. Jugel, Marcus Mei?ner
+ */
+public class vt320 extends Terminal implements TerminalHost
+{
+  /**
+   * Return the version of the terminal emulation and its display.
+   */
+  public String toString() { return "$Id: vt320.java,v 1.56 1998/03/07 23:47:03 marcus Exp $ "+display.version; }
+
+  // the input handler takes the keyboard input from us.
+  private TerminalHost host = this;
+
+  // due to a bug with Windows we need a keypress cache
+  private int pressedKey = ' ';
+  private long pressedWhen = ' ';
+
+  // The character display
+  private CharDisplay display;
+  private static int debug = 0;
+  private String terminalID = "vt320";
+
+  // X - COLUMNS, Y - ROWS
+  int  R,C;
+  int  Sc,Sr,Sa;
+  int  attributes  = 0;
+  int  insertmode  = 0;
+  int  statusmode = 0;
+  int  vt52mode  = 0;
+  int  normalcursor  = 0;
+  boolean originmode = false;
+  boolean sendcrlf = true;
+
+  private boolean  useibmcharset = false;
+
+  private  static  int  lastwaslf = 0;
+  private static  int i;
+  private final static char ESC = 27;
+  private final static char IND = 132;
+  private final static char NEL = 133;
+  private final static char RI  = 141;
+  private final static char HTS = 136;
+  private final static char DCS = 144;
+  private final static char CSI = 155;
+  private final static char OSC = 157;
+  private final static int TSTATE_DATA  = 0;
+  private final static int TSTATE_ESC  = 1; /* ESC */
+  private final static int TSTATE_CSI  = 2; /* ESC [ */
+  private final static int TSTATE_DCS  = 3; /* ESC P */
+  private final static int TSTATE_DCEQ  = 4; /* ESC [? */
+  private final static int TSTATE_ESCSQUARE= 5; /* ESC # */
+  private final static int TSTATE_OSC= 6;       /* ESC ] */
+  private final static int TSTATE_SETG0= 7;     /* ESC (? */
+  private final static int TSTATE_SETG1= 8;     /* ESC )? */
+  private final static int TSTATE_SETG2= 9;     /* ESC *? */
+  private final static int TSTATE_SETG3= 10;    /* ESC +? */
+  private final static int TSTATE_CSI_DOLLAR  = 11; /* ESC [ Pn $ */
+
+  /* The graphics charsets
+   * B - default ASCII
+   * A - default UK
+   * 0 - DEC SPECIAL
+   * < - User defined
+   * ....
+   */
+  private static char gx[] = {
+  'B',      // g0
+  '0',      // g1
+  'A',      // g2
+  '<',      // g3
+  };
+  private static char gr = 1;  // default GR to G1
+  private static char gl = 0;  // default GL to G0
+
+  // array to store DEC Special -> Unicode mapping
+  //  Unicode   DEC  Unicode name    (DEC name)
+  private static char DECSPECIAL[] = {
+    '\u0040', //5f blank
+    '\u2666', //60 black diamond
+    '\u2592', //61 grey square
+    '\u2409', //62 Horizontal tab  (ht) pict. for control
+    '\u240c', //63 Form Feed       (ff) pict. for control
+    '\u240d', //64 Carriage Return (cr) pict. for control
+    '\u240a', //65 Line Feed       (lf) pict. for control
+    '\u00ba', //66 Masculine ordinal indicator
+    '\u00b1', //67 Plus or minus sign
+    '\u2424', //68 New Line        (nl) pict. for control
+    '\u240b', //69 Vertical Tab    (vt) pict. for control
+    '\u2518', //6a Forms light up   and left
+    '\u2510', //6b Forms light down and left
+    '\u250c', //6c Forms light down and right
+    '\u2514', //6d Forms light up   and right
+    '\u253c', //6e Forms light vertical and horizontal
+    '\u2594', //6f Upper 1/8 block                        (Scan 1)
+    '\u2580', //70 Upper 1/2 block                        (Scan 3)
+    '\u2500', //71 Forms light horizontal or ?em dash?    (Scan 5)
+    '\u25ac', //72 \u25ac black rect. or \u2582 lower 1/4 (Scan 7)
+    '\u005f', //73 \u005f underscore  or \u2581 lower 1/8 (Scan 9)
+    '\u251c', //74 Forms light vertical and right
+    '\u2524', //75 Forms light vertical and left
+    '\u2534', //76 Forms light up   and horizontal
+    '\u252c', //77 Forms light down and horizontal
+    '\u2502', //78 vertical bar
+    '\u2264', //79 less than or equal
+    '\u2265', //7a greater than or equal
+    '\u00b6', //7b paragraph
+    '\u2260', //7c not equal
+    '\u00a3', //7d Pound Sign (british)
+    '\u00b7'  //7e Middle Dot
+  };
+
+  private final static int KEYUP  = Event.UP % 1000;
+  private final static int KEYDOWN  = Event.DOWN % 1000;
+  private final static int KEYLEFT  = Event.LEFT % 1000;
+  private final static int KEYRIGHT  = Event.RIGHT % 1000;
+  private final static int KEYF1  = Event.F1 % 1000;
+  private final static int KEYF2  = Event.F2 % 1000;
+  private final static int KEYF3  = Event.F3 % 1000;
+  private final static int KEYF4  = Event.F4 % 1000;
+  private final static int KEYF5  = Event.F5 % 1000;
+  private final static int KEYF6  = Event.F6 % 1000;
+  private final static int KEYF7  = Event.F7 % 1000;
+  private final static int KEYF8  = Event.F8 % 1000;
+  private final static int KEYF9  = Event.F9 % 1000;
+  private final static int KEYF10  = Event.F10 % 1000;
+  private final static int KEYF11  = Event.F11 % 1000;
+  private final static int KEYF12  = Event.F12 % 1000;
+  private final static int KEYPGDN   = Event.PGDN % 1000;
+  private final static int KEYPGUP   = Event.PGUP % 1000;
+
+  private final static int KEYHOME  = Event.HOME % 1000;
+  private final static int KEYEND  = Event.END % 1000;
+
+  public static final int KEYPRINT_SCREEN  = 20;
+  public static final int KEYSCROLL_LOCK    = 21;
+  public static final int KEYCAPS_LOCK    = 22;
+  public static final int KEYNUM_LOCK    = 23;
+  public static final int KEYPAUSE    = 24;
+  public static final int KEYINSERT    = 25;
+
+    /**
+     * The Insert key.
+     */
+    public static final int INSERT    = 1025;
+
+  /**
+   * Strings to send on function key presseic
+   */
+  private String FunctionKey[];
+  private String FunctionKeyShift[];
+  private String FunctionKeyCtrl[];
+  private String FunctionKeyAlt[];
+  private String KeyUp;
+  private String KeyDown;
+  private String KeyLeft;
+  private String KeyRight;
+  private String KeyBacktab;
+  private String KeyTab;
+
+  private String KP0;
+  private String KP1;
+  private String KP2;
+  private String KP3;
+  private String KP4;
+  private String KP5;
+  private String KP6;
+  private String KP7;
+  private String KP8;
+  private String KP9;
+  private String KPMinus;
+  private String KPComma;
+  private String KPPeriod;
+  private String KPEnter;
+  private String PF1;
+  private String PF2;
+  private String PF3;
+  private String PF4;
+  private String Help;
+  private String Do;
+  private String Find;
+  private String Insert;
+  private String Remove;
+  private String Select;
+  private String PrevScn;
+  private String NextScn;
+
+
+  private String osc,dcs;  /* to memorize OSC & DCS control sequence */
+
+  private int term_state = TSTATE_DATA;
+  private boolean vms = false;
+  private byte[]  Tabs;
+  private int[]  DCEvars = new int [10];
+  private  int  DCEvar;
+
+  /* operation system we run on, Scrollbar hack */
+  private String osn = System.getProperty("os.name");
+
+  public String[][] getParameterInfo() {
+    String pinfo[][] = {
+    {"VTcolumns",  "Integer",   "Columns of the terminal"},
+    {"VTrows",     "Integer",   "Rows of the terminal"},
+    {"VTfont",     "String",    "Terminal font (default is Courier)"},
+    {"VTfontsize", "Integer",   "Font size"},
+    {"VTbuffer",   "Integer",   "Scrollback buffer size"},
+    {"VTscrollbar","Boolean",   "Enable or disable scrollbar"},
+    {"VTresize",   "String",    "One of none, font, screen"},
+    {"VTid",       "String",    "Terminal id, standard is VT320"},
+    {"VTcharset",  "String",    "Charset used"},
+    {"VTvms",      "Boolean",   "Enable or disable VMS key mappings"},
+    {"F1 - F20",   "String",    "Programmable Function Keys"},
+    {"SF1 - SF20",   "String",    "Programmable Shift-ed Function Keys"},
+    {"CF1 - CF20",   "String",    "Programmable Control-ed Function Keys"},
+    {"AF1 - AF20",   "String",    "Programmable Alt-ed Function Keys"},
+    };
+    return pinfo;
+  }
+
+  static String unEscape(String tmp) {
+      int idx = 0, oldidx = 0;
+      String cmd;
+
+      cmd = "";
+      while((idx = tmp.indexOf('\\', oldidx)) >= 0 &&
+            ++idx <= tmp.length()) {
+        cmd += tmp.substring(oldidx, idx-1);
+        if(idx == tmp.length()) return cmd;
+        switch(tmp.charAt(idx)) {
+        case 'b': cmd += "\b"; break;
+        case 'e': cmd += "\u001b"; break;
+        case 'n': cmd += "\n"; break;
+        case 'r': cmd += "\r"; break;
+        case 't': cmd += "\t"; break;
+        case 'v': cmd += "\u000b"; break;
+        case 'a': cmd += "\u0012"; break;
+        default : cmd += tmp.substring(idx, ++idx);break;
+       }
+       oldidx = ++idx;
+      }
+      if(oldidx <= tmp.length()) cmd += tmp.substring(oldidx);
+      return cmd;
+  }
+
+  /**
+   * Initialize terminal.
+   * @param parent the applet parent where to get parameters from
+   * @see display.Terminal
+   */
+  public void addNotify() {
+    if(display == null) {
+      String width = "80", height = "24", resize ="screen";
+      String font = "monaco", fs = "14", bufs = "100";
+      String scrb = "false";
+      String vms = "false";
+      String ibmcset = "false";
+
+      Applet applet = getParent() instanceof Applet ? (Applet)getParent() : null;
+
+      if(applet != null) {
+        try {
+          host = (TerminalHost)applet;
+        } catch(ClassCastException e) {
+          System.err.println("vt320: "+applet+" cannot receive terminal input");
+          host = this;
+        }
+
+        width  = applet.getParameter("VTcolumns");
+        height = applet.getParameter("VTrows");
+        font   = applet.getParameter("VTfont");
+        fs     = applet.getParameter("VTfontsize");
+        bufs   = applet.getParameter("VTbuffer");
+        scrb   = applet.getParameter("VTscrollbar");
+        vms    = applet.getParameter("VTvms");
+        resize = applet.getParameter("VTresize");
+        resize = resize == null ? "font" : resize;
+        ibmcset = applet.getParameter("VTcharset");
+        if ((ibmcset!=null)&&(ibmcset.equals("ibm")))
+          useibmcharset=true;
+
+        if(applet.getParameter("VTid") != null)
+          terminalID = applet.getParameter("VTid");
+      }
+
+      display = new CharDisplay(
+          width==null?80:(new Integer(width)).intValue(),
+          (height==null?24:(new Integer(height)).intValue()),
+          font==null?"Courier":font,
+          fs==null?14:(new Integer(fs)).intValue()
+      );
+      display.setBottomMargin((height==null?
+                              24:
+                              (new Integer(height)).intValue()) - 1);
+      display.setBufferSize(bufs==null?100:(new Integer(bufs)).intValue());
+      if(resize.equals("none"))
+        display.setResizeStrategy(CharDisplay.RESIZE_NONE);
+      else if(resize.equals("font"))
+        display.setResizeStrategy(CharDisplay.RESIZE_FONT);
+      else if(resize.equals("screen"))
+        display.setResizeStrategy(CharDisplay.RESIZE_SCREEN);
+
+      display.setResizeStrategy(CharDisplay.RESIZE_SCREEN);
+
+      display.setBorder(2, false);
+
+      setLayout(new BorderLayout());
+          display.setScrollbar("East");
+          /*
+      if(scrb != null && !scrb.equals("false")) {
+        if(scrb.equals("left") || scrb.equals("true"))
+          display.setScrollbar("West");
+        else if(scrb.equals("right"))
+          display.setScrollbar("East");
+        else
+          System.out.println("vt320: unknown scrollbar location: "+scrb);
+      }
+          */
+      if(vms != null && vms.equals("true"))
+      {
+        this.vms = true;
+      }
+      add("Center", display);
+      InitTerminalVars();
+      if (applet != null) for (int i=1;i<20;i++)
+      {
+       if (applet.getParameter("F"+i)!=null)
+         FunctionKey[i] = unEscape(applet.getParameter("F"+i));
+        if (applet.getParameter("SF"+i)!=null)
+          FunctionKeyShift[i] = unEscape(applet.getParameter("SF"+i));
+        if (applet.getParameter("CF"+i)!=null)
+          FunctionKeyCtrl[i] = unEscape(applet.getParameter("CF"+i));
+        if (applet.getParameter("AF"+i)!=null)
+          FunctionKeyAlt[i] = unEscape(applet.getParameter("AF"+i));
+      }
+    }
+    super.addNotify();
+  }
+
+  private void InitTerminalVars() {
+    int nw = display.getColumns();
+
+    if (nw<132) nw=132; //catch possible later 132/80 resizes
+    Tabs = new byte[nw];
+    for (int i=0;i<nw;i+=8) {
+      Tabs[i]=1;
+    }
+
+    PF1  = "\u001bOP";
+    PF2  = "\u001bOQ";
+    PF3  = "\u001bOR";
+    PF4  = "\u001bOS";
+
+    Find = "\u001b[1~";
+    Insert = "\u001b[2~";
+    Remove = "\u001b[3~";
+    Select = "\u001b[4~";
+    PrevScn = "\u001b[5~";
+    NextScn = "\u001b[6~";
+
+    Help = "\u001b[28~";
+    Do = "\u001b[29~";
+
+    FunctionKey = new String[21];
+    FunctionKey[0]= "";
+    FunctionKey[1]= PF1;
+    FunctionKey[2]= PF2;
+    FunctionKey[3]= PF3;
+    FunctionKey[4]= PF4;
+    /* following are defined differently for vt220 / vt132 ... */
+    FunctionKey[5]= "\u001b[15~";
+    FunctionKey[6]= "\u001b[17~";
+    FunctionKey[7]= "\u001b[18~";
+    FunctionKey[8]= "\u001b[19~";
+    FunctionKey[9]= "\u001b[20~";
+    FunctionKey[10]= "\u001b[21~";
+    FunctionKey[11]= "\u001b[23~";
+    FunctionKey[12]= "\u001b[24~";
+    FunctionKey[13]= "\u001b[25~";
+    FunctionKey[14]= "\u001b[26~";
+    FunctionKey[15]= Help;
+    FunctionKey[16]= Do;
+    FunctionKey[17]= "\u001b[31~";
+    FunctionKey[18]= "\u001b[32~";
+    FunctionKey[19]= "\u001b[33~";
+    FunctionKey[20]= "\u001b[34~";
+
+    FunctionKeyShift = new String[21];
+    FunctionKeyAlt = new String[21];
+    FunctionKeyCtrl = new String[21];
+
+    for (int i=0;i<20;i++)
+    {
+       FunctionKeyShift[i]="";
+       FunctionKeyAlt[i]="";
+       FunctionKeyCtrl[i]="";
+    }
+    FunctionKeyShift[15] = Find;
+    FunctionKeyShift[16] = Select;
+
+    KeyTab   = "\u0009";
+    KeyBacktab = "\u001bOP\u0009";
+    KeyUp    = "\u001b[A";
+    KeyDown  = "\u001b[B";
+    KeyRight  = "\u001b[C";
+    KeyLeft  = "\u001b[D";
+    KP0  = "\u001bOp";
+    KP1  = "\u001bOq";
+    KP2  = "\u001bOr";
+    KP3  = "\u001bOs";
+    KP4  = "\u001bOt";
+    KP5  = "\u001bOu";
+    KP6  = "\u001bOv";
+    KP7  = "\u001bOw";
+    KP8  = "\u001bOx";
+    KP9  = "\u001bOy";
+    KPMinus  = "\u001bOm";
+    KPComma  = "\u001bOl";
+    KPPeriod  = "\u001bOn";
+    KPEnter  = "\u001bOM";
+
+    /* ... */
+  }
+
+  public Dimension getSize() {
+    return new Dimension(display.getColumns(), display.getRows());
+  }
+
+  public String getTerminalType() { return terminalID; }
+
+  /**
+   * Handle events for the terminal. Only accept events for the scroll
+   * bar. Any other events have to be propagated to the parent.
+   * @param evt the event
+   */
+  public boolean handleEvent(Event evt) {
+    // request focus if mouse enters (necessary to avoid mistakes)
+    if(evt.id == Event.MOUSE_ENTER) { display.requestFocus(); return true; }
+    // give focus away if mouse leaves the window
+    if(evt.id == Event.MOUSE_EXIT) { nextFocus(); return true; }
+
+    // handle keyboard events
+
+    /* Netscape for windows does not send keyDown when period
+    * is pressed. This hack catches the keyUp event.
+    */
+    int id = evt.id;
+
+    boolean control = (((evt.CTRL_MASK & evt.modifiers)==0) ? false : true);
+    boolean shift = (((evt.SHIFT_MASK & evt.modifiers)==0) ? false : true );
+    boolean alt = (((evt.ALT_MASK & evt.modifiers)==0) ? false :true);
+
+    pressedKey:
+    {
+        if (pressedKey == 10
+            && (evt.key == 10
+            || evt.key == 13)
+            && evt.when - pressedWhen < 50)    //  Ray: Elliminate stuttering enter/return
+            break pressedKey;
+
+        int priorKey = pressedKey;
+        if(id == Event.KEY_PRESS)
+            pressedKey = evt.key;               //  Keep track of last pressed key
+        else if (evt.key == '.'
+            && evt.id != Event.KEY_RELEASE
+            && evt.key != pressedKey
+        ) {
+            pressedKey = 0;
+        } else
+            break pressedKey;
+        pressedWhen = evt.when;
+        if (evt.key == 10 && !control) {
+            if (sendcrlf)
+              host.send("\r\n"); /* YES, see RFC 854 */
+            else
+              host.send("\r"); /* YES, see RFC 854 */
+            return true;
+        } else
+/* FIXME: on german PC keyboards you have to use Alt-Ctrl-q to get an @,
+ * so we can't just use it here... will probably break some other VMS stuff
+ * -Marcus
+        if (((!vms && evt.key == '2') || evt.key == '@' || evt.key == ' ') && control)
+ */
+        if (((!vms && evt.key == '2') || evt.key == ' ') && control)
+            return host.send("" + (char)0);
+        else if (vms) {
+           if (evt.key == 8) {
+               if (shift && !control)
+                   return host.send("" + (char)10); //  VMS shift deletes word back
+               if (control && !shift)
+                   return host.send("" + (char)24); //  VMS control deletes line back
+               return host.send("" + (char)127);   //  VMS other is delete
+           }
+           if (evt.key == 127 && !control) {
+               if (shift)
+                   return host.send(Insert); //  VMS shift delete = insert
+               else
+                   return host.send(Remove); //  VMS delete = remove
+           }
+            if (control) {
+                switch(evt.key) {
+                    case '0':
+                        return host.send(KP0);
+                    case '1':
+                        return host.send(KP1);
+                    case '2':
+                        return host.send(KP2);
+                    case '3':
+                        return host.send(KP3);
+                    case '4':
+                        return host.send(KP4);
+                    case '5':
+                        return host.send(KP5);
+                    case '6':
+                        return host.send(KP6);
+                    case '7':
+                        return host.send(KP7);
+                    case '8':
+                        return host.send(KP8);
+                    case '9':
+                        return host.send(KP9);
+                    case '.':
+                        return host.send(KPPeriod);
+                    case '-':
+                    case 31:
+                        return host.send(KPMinus);
+                    case '+':
+                        return host.send(KPComma);
+                    case 10:
+                        return host.send(KPEnter);
+                    case '/':
+                        return host.send(PF2);
+                    case '*':
+                        return host.send(PF3);
+                }
+                if (shift && evt.key < 32)
+                    return host.send(PF1+(char)(evt.key + 64));
+            }
+       }
+       // Hmmm. Outside the VMS case?
+        if (shift && (evt.key == '\t'))
+           return host.send(KeyBacktab);
+       else
+           return host.send(""+(char)evt.key);
+    }
+
+    String input = "";
+
+    if (evt.id == evt.KEY_ACTION && !control)
+    {
+      String fmap[];
+      /* bloodsucking little buggers at netscape
+       * don't keep to the standard java values
+       * of <ARROW> or <Fx>
+       */
+      fmap = FunctionKey;
+      if (shift)
+       fmap = FunctionKeyShift;
+      if (control)
+        fmap = FunctionKeyCtrl;
+      if (alt)
+        fmap = FunctionKeyAlt;
+      switch (evt.key % 1000) {
+      case KEYF1:
+        input = fmap[1];
+        break;
+      case KEYF2:
+        input = fmap[2];
+        break;
+      case KEYF3:
+        input = fmap[3];
+        break;
+      case KEYF4:
+        input = fmap[4];
+        break;
+      case KEYF5:
+        input = fmap[5];
+        break;
+      case KEYF6:
+        input = fmap[6];
+        break;
+      case KEYF7:
+        input = fmap[7];
+        break;
+      case KEYF8:
+        input = fmap[8];
+        break;
+      case KEYF9:
+        input = fmap[9];
+        break;
+      case KEYF10:
+        input = fmap[10];
+        break;
+      case KEYF11:
+        input = fmap[11];
+        break;
+      case KEYF12:
+        input = fmap[12];
+        break;
+      case KEYUP:
+        input = KeyUp;
+        break;
+      case KEYDOWN:
+        input = KeyDown;
+        break;
+      case KEYLEFT:
+        input = KeyLeft;
+        break;
+      case KEYRIGHT:
+        input = KeyRight;
+        break;
+      case KEYPGDN:
+        input = NextScn;
+        break;
+      case KEYPGUP:
+        input = PrevScn;
+        break;
+      case KEYINSERT:
+        input = Insert;
+        break;
+//  The following cases fall through if not in VMS mode.
+      case KEYHOME:
+        if (vms)
+        {
+            input = "" + (char)8;
+            break;
+        }
+       //FALLTHROUGH
+      case KEYEND:
+        if (vms)
+        {
+            input = "" + (char)5;
+            break;
+        }
+       //FALLTHROUGH
+      case KEYNUM_LOCK:
+        if (vms && control) {
+            if (pressedKey != evt.key) {
+                pressedKey = evt.key;
+                input = PF1;
+            } else
+                pressedKey = ' ';   //  Here, we eat a second numlock since that returns numlock state
+        }
+       break;
+      default:
+        /*if (debug>0)*/
+          System.out.println("vt320: unknown event:"+(int)evt.key);
+        break;
+      }
+    }
+
+    if(input != null && input.length() > 0)
+      return host.send(input);
+
+    return false;
+  }
+
+  /**
+   * Dummy method to handle input events (String).
+   * This is only needed if our parent is not TerminalHost
+   * @param s String to handle
+   * @return always true
+   * @see display.TerminalHost
+   */
+  public boolean send(String s) {
+    putString(s);
+    return true;
+  }
+
+  private void handle_dcs(String dcs) {
+      if (debug>1)
+          System.out.println("DCS: "+dcs);
+  }
+  private void handle_osc(String osc) {
+      if (debug>1)
+          System.out.println("OSC: "+osc);
+  }
+
+  /**
+  * Put String at current cursor position. Moves cursor
+  * according to the String. Does NOT wrap.
+  * @param s the string
+  */
+  public void putString(String s) {
+    int  i,len=s.length();
+
+    display.markLine(R,1);
+    for (i=0;i<len;i++)
+      putChar(s.charAt(i),false);
+    display.setCursorPos(C, R);
+    display.redraw();
+  }
+
+  public void putChar(char c) {
+    putChar(c,true);
+    display.redraw();
+  }
+
+  public final static char unimap[] = {
+//#
+//#    Name:     cp437_DOSLatinUS to Unicode table
+//#    Unicode version: 1.1
+//#    Table version: 1.1
+//#    Table format:  Format A
+//#    Date:          03/31/95
+//#    Authors:       Michel Suignard <michelsu@microsoft.com>
+//#                   Lori Hoerth <lorih@microsoft.com>
+//#    General notes: none
+//#
+//#    Format: Three tab-separated columns
+//#        Column #1 is the cp1255_WinHebrew code (in hex)
+//#        Column #2 is the Unicode (in hex as 0xXXXX)
+//#        Column #3 is the Unicode name (follows a comment sign, '#')
+//#
+//#    The entries are in cp437_DOSLatinUS order
+//#
+
+  0x0000,// #NULL
+  0x0001,// #START OF HEADING
+  0x0002,// #START OF TEXT
+  0x0003,// #END OF TEXT
+  0x0004,// #END OF TRANSMISSION
+  0x0005,// #ENQUIRY
+  0x0006,// #ACKNOWLEDGE
+  0x0007,// #BELL
+  0x0008,// #BACKSPACE
+  0x0009,// #HORIZONTAL TABULATION
+  0x000a,// #LINE FEED
+  0x000b,// #VERTICAL TABULATION
+  0x000c,// #FORM FEED
+  0x000d,// #CARRIAGE RETURN
+  0x000e,// #SHIFT OUT
+  0x000f,// #SHIFT IN
+  0x0010,// #DATA LINK ESCAPE
+  0x0011,// #DEVICE CONTROL ONE
+  0x0012,// #DEVICE CONTROL TWO
+  0x0013,// #DEVICE CONTROL THREE
+  0x0014,// #DEVICE CONTROL FOUR
+  0x0015,// #NEGATIVE ACKNOWLEDGE
+  0x0016,// #SYNCHRONOUS IDLE
+  0x0017,// #END OF TRANSMISSION BLOCK
+  0x0018,// #CANCEL
+  0x0019,// #END OF MEDIUM
+  0x001a,// #SUBSTITUTE
+  0x001b,// #ESCAPE
+  0x001c,// #FILE SEPARATOR
+  0x001d,// #GROUP SEPARATOR
+  0x001e,// #RECORD SEPARATOR
+  0x001f,// #UNIT SEPARATOR
+  0x0020,// #SPACE
+  0x0021,// #EXCLAMATION MARK
+  0x0022,// #QUOTATION MARK
+  0x0023,// #NUMBER SIGN
+  0x0024,// #DOLLAR SIGN
+  0x0025,// #PERCENT SIGN
+  0x0026,// #AMPERSAND
+  0x0027,// #APOSTROPHE
+  0x0028,// #LEFT PARENTHESIS
+  0x0029,// #RIGHT PARENTHESIS
+  0x002a,// #ASTERISK
+  0x002b,// #PLUS SIGN
+  0x002c,// #COMMA
+  0x002d,// #HYPHEN-MINUS
+  0x002e,// #FULL STOP
+  0x002f,// #SOLIDUS
+  0x0030,// #DIGIT ZERO
+  0x0031,// #DIGIT ONE
+  0x0032,// #DIGIT TWO
+  0x0033,// #DIGIT THREE
+  0x0034,// #DIGIT FOUR
+  0x0035,// #DIGIT FIVE
+  0x0036,// #DIGIT SIX
+  0x0037,// #DIGIT SEVEN
+  0x0038,// #DIGIT EIGHT
+  0x0039,// #DIGIT NINE
+  0x003a,// #COLON
+  0x003b,// #SEMICOLON
+  0x003c,// #LESS-THAN SIGN
+  0x003d,// #EQUALS SIGN
+  0x003e,// #GREATER-THAN SIGN
+  0x003f,// #QUESTION MARK
+  0x0040,// #COMMERCIAL AT
+  0x0041,// #LATIN CAPITAL LETTER A
+  0x0042,// #LATIN CAPITAL LETTER B
+  0x0043,// #LATIN CAPITAL LETTER C
+  0x0044,// #LATIN CAPITAL LETTER D
+  0x0045,// #LATIN CAPITAL LETTER E
+  0x0046,// #LATIN CAPITAL LETTER F
+  0x0047,// #LATIN CAPITAL LETTER G
+  0x0048,// #LATIN CAPITAL LETTER H
+  0x0049,// #LATIN CAPITAL LETTER I
+  0x004a,// #LATIN CAPITAL LETTER J
+  0x004b,// #LATIN CAPITAL LETTER K
+  0x004c,// #LATIN CAPITAL LETTER L
+  0x004d,// #LATIN CAPITAL LETTER M
+  0x004e,// #LATIN CAPITAL LETTER N
+  0x004f,// #LATIN CAPITAL LETTER O
+  0x0050,// #LATIN CAPITAL LETTER P
+  0x0051,// #LATIN CAPITAL LETTER Q
+  0x0052,// #LATIN CAPITAL LETTER R
+  0x0053,// #LATIN CAPITAL LETTER S
+  0x0054,// #LATIN CAPITAL LETTER T
+  0x0055,// #LATIN CAPITAL LETTER U
+  0x0056,// #LATIN CAPITAL LETTER V
+  0x0057,// #LATIN CAPITAL LETTER W
+  0x0058,// #LATIN CAPITAL LETTER X
+  0x0059,// #LATIN CAPITAL LETTER Y
+  0x005a,// #LATIN CAPITAL LETTER Z
+  0x005b,// #LEFT SQUARE BRACKET
+  0x005c,// #REVERSE SOLIDUS
+  0x005d,// #RIGHT SQUARE BRACKET
+  0x005e,// #CIRCUMFLEX ACCENT
+  0x005f,// #LOW LINE
+  0x0060,// #GRAVE ACCENT
+  0x0061,// #LATIN SMALL LETTER A
+  0x0062,// #LATIN SMALL LETTER B
+  0x0063,// #LATIN SMALL LETTER C
+  0x0064,// #LATIN SMALL LETTER D
+  0x0065,// #LATIN SMALL LETTER E
+  0x0066,// #LATIN SMALL LETTER F
+  0x0067,// #LATIN SMALL LETTER G
+  0x0068,// #LATIN SMALL LETTER H
+  0x0069,// #LATIN SMALL LETTER I
+  0x006a,// #LATIN SMALL LETTER J
+  0x006b,// #LATIN SMALL LETTER K
+  0x006c,// #LATIN SMALL LETTER L
+  0x006d,// #LATIN SMALL LETTER M
+  0x006e,// #LATIN SMALL LETTER N
+  0x006f,// #LATIN SMALL LETTER O
+  0x0070,// #LATIN SMALL LETTER P
+  0x0071,// #LATIN SMALL LETTER Q
+  0x0072,// #LATIN SMALL LETTER R
+  0x0073,// #LATIN SMALL LETTER S
+  0x0074,// #LATIN SMALL LETTER T
+  0x0075,// #LATIN SMALL LETTER U
+  0x0076,// #LATIN SMALL LETTER V
+  0x0077,// #LATIN SMALL LETTER W
+  0x0078,// #LATIN SMALL LETTER X
+  0x0079,// #LATIN SMALL LETTER Y
+  0x007a,// #LATIN SMALL LETTER Z
+  0x007b,// #LEFT CURLY BRACKET
+  0x007c,// #VERTICAL LINE
+  0x007d,// #RIGHT CURLY BRACKET
+  0x007e,// #TILDE
+  0x007f,// #DELETE
+  0x00c7,// #LATIN CAPITAL LETTER C WITH CEDILLA
+  0x00fc,// #LATIN SMALL LETTER U WITH DIAERESIS
+  0x00e9,// #LATIN SMALL LETTER E WITH ACUTE
+  0x00e2,// #LATIN SMALL LETTER A WITH CIRCUMFLEX
+  0x00e4,// #LATIN SMALL LETTER A WITH DIAERESIS
+  0x00e0,// #LATIN SMALL LETTER A WITH GRAVE
+  0x00e5,// #LATIN SMALL LETTER A WITH RING ABOVE
+  0x00e7,// #LATIN SMALL LETTER C WITH CEDILLA
+  0x00ea,// #LATIN SMALL LETTER E WITH CIRCUMFLEX
+  0x00eb,// #LATIN SMALL LETTER E WITH DIAERESIS
+  0x00e8,// #LATIN SMALL LETTER E WITH GRAVE
+  0x00ef,// #LATIN SMALL LETTER I WITH DIAERESIS
+  0x00ee,// #LATIN SMALL LETTER I WITH CIRCUMFLEX
+  0x00ec,// #LATIN SMALL LETTER I WITH GRAVE
+  0x00c4,// #LATIN CAPITAL LETTER A WITH DIAERESIS
+  0x00c5,// #LATIN CAPITAL LETTER A WITH RING ABOVE
+  0x00c9,// #LATIN CAPITAL LETTER E WITH ACUTE
+  0x00e6,// #LATIN SMALL LIGATURE AE
+  0x00c6,// #LATIN CAPITAL LIGATURE AE
+  0x00f4,// #LATIN SMALL LETTER O WITH CIRCUMFLEX
+  0x00f6,// #LATIN SMALL LETTER O WITH DIAERESIS
+  0x00f2,// #LATIN SMALL LETTER O WITH GRAVE
+  0x00fb,// #LATIN SMALL LETTER U WITH CIRCUMFLEX
+  0x00f9,// #LATIN SMALL LETTER U WITH GRAVE
+  0x00ff,// #LATIN SMALL LETTER Y WITH DIAERESIS
+  0x00d6,// #LATIN CAPITAL LETTER O WITH DIAERESIS
+  0x00dc,// #LATIN CAPITAL LETTER U WITH DIAERESIS
+  0x00a2,// #CENT SIGN
+  0x00a3,// #POUND SIGN
+  0x00a5,// #YEN SIGN
+  0x20a7,// #PESETA SIGN
+  0x0192,// #LATIN SMALL LETTER F WITH HOOK
+  0x00e1,// #LATIN SMALL LETTER A WITH ACUTE
+  0x00ed,// #LATIN SMALL LETTER I WITH ACUTE
+  0x00f3,// #LATIN SMALL LETTER O WITH ACUTE
+  0x00fa,// #LATIN SMALL LETTER U WITH ACUTE
+  0x00f1,// #LATIN SMALL LETTER N WITH TILDE
+  0x00d1,// #LATIN CAPITAL LETTER N WITH TILDE
+  0x00aa,// #FEMININE ORDINAL INDICATOR
+  0x00ba,// #MASCULINE ORDINAL INDICATOR
+  0x00bf,// #INVERTED QUESTION MARK
+  0x2310,// #REVERSED NOT SIGN
+  0x00ac,// #NOT SIGN
+  0x00bd,// #VULGAR FRACTION ONE HALF
+  0x00bc,// #VULGAR FRACTION ONE QUARTER
+  0x00a1,// #INVERTED EXCLAMATION MARK
+  0x00ab,// #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+  0x00bb,// #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+  0x2591,// #LIGHT SHADE
+  0x2592,// #MEDIUM SHADE
+  0x2593,// #DARK SHADE
+  0x2502,// #BOX DRAWINGS LIGHT VERTICAL
+  0x2524,// #BOX DRAWINGS LIGHT VERTICAL AND LEFT
+  0x2561,// #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+  0x2562,// #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+  0x2556,// #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+  0x2555,// #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+  0x2563,// #BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+  0x2551,// #BOX DRAWINGS DOUBLE VERTICAL
+  0x2557,// #BOX DRAWINGS DOUBLE DOWN AND LEFT
+  0x255d,// #BOX DRAWINGS DOUBLE UP AND LEFT
+  0x255c,// #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+  0x255b,// #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+  0x2510,// #BOX DRAWINGS LIGHT DOWN AND LEFT
+  0x2514,// #BOX DRAWINGS LIGHT UP AND RIGHT
+  0x2534,// #BOX DRAWINGS LIGHT UP AND HORIZONTAL
+  0x252c,// #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+  0x251c,// #BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+  0x2500,// #BOX DRAWINGS LIGHT HORIZONTAL
+  0x253c,// #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+  0x255e,// #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+  0x255f,// #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+  0x255a,// #BOX DRAWINGS DOUBLE UP AND RIGHT
+  0x2554,// #BOX DRAWINGS DOUBLE DOWN AND RIGHT
+  0x2569,// #BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+  0x2566,// #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+  0x2560,// #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+  0x2550,// #BOX DRAWINGS DOUBLE HORIZONTAL
+  0x256c,// #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+  0x2567,// #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+  0x2568,// #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+  0x2564,// #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+  0x2565,// #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+  0x2559,// #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+  0x2558,// #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+  0x2552,// #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+  0x2553,// #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+  0x256b,// #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+  0x256a,// #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+  0x2518,// #BOX DRAWINGS LIGHT UP AND LEFT
+  0x250c,// #BOX DRAWINGS LIGHT DOWN AND RIGHT
+  0x2588,// #FULL BLOCK
+  0x2584,// #LOWER HALF BLOCK
+  0x258c,// #LEFT HALF BLOCK
+  0x2590,// #RIGHT HALF BLOCK
+  0x2580,// #UPPER HALF BLOCK
+  0x03b1,// #GREEK SMALL LETTER ALPHA
+  0x00df,// #LATIN SMALL LETTER SHARP S
+  0x0393,// #GREEK CAPITAL LETTER GAMMA
+  0x03c0,// #GREEK SMALL LETTER PI
+  0x03a3,// #GREEK CAPITAL LETTER SIGMA
+  0x03c3,// #GREEK SMALL LETTER SIGMA
+  0x00b5,// #MICRO SIGN
+  0x03c4,// #GREEK SMALL LETTER TAU
+  0x03a6,// #GREEK CAPITAL LETTER PHI
+  0x0398,// #GREEK CAPITAL LETTER THETA
+  0x03a9,// #GREEK CAPITAL LETTER OMEGA
+  0x03b4,// #GREEK SMALL LETTER DELTA
+  0x221e,// #INFINITY
+  0x03c6,// #GREEK SMALL LETTER PHI
+  0x03b5,// #GREEK SMALL LETTER EPSILON
+  0x2229,// #INTERSECTION
+  0x2261,// #IDENTICAL TO
+  0x00b1,// #PLUS-MINUS SIGN
+  0x2265,// #GREATER-THAN OR EQUAL TO
+  0x2264,// #LESS-THAN OR EQUAL TO
+  0x2320,// #TOP HALF INTEGRAL
+  0x2321,// #BOTTOM HALF INTEGRAL
+  0x00f7,// #DIVISION SIGN
+  0x2248,// #ALMOST EQUAL TO
+  0x00b0,// #DEGREE SIGN
+  0x2219,// #BULLET OPERATOR
+  0x00b7,// #MIDDLE DOT
+  0x221a,// #SQUARE ROOT
+  0x207f,// #SUPERSCRIPT LATIN SMALL LETTER N
+  0x00b2,// #SUPERSCRIPT TWO
+  0x25a0,// #BLACK SQUARE
+  0x00a0,// #NO-BREAK SPACE
+  };
+
+  public char map_cp850_unicode(char x) {
+    if (x>=0x100)
+      return x;
+    return unimap[x];
+  }
+
+  private void _SetCursor(int row,int col) {
+    int maxr = display.getRows();
+    int tm = display.getTopMargin();
+
+    R = (row<0)?0:row;
+    C = (col<0)?0:col;
+
+    if (originmode) {
+       R       += display.getTopMargin();
+       maxr     = display.getBottomMargin();
+    }
+    if (R>maxr) R = maxr;
+  }
+
+  public void putChar(char c,boolean doshowcursor) {
+    Dimension size;
+    int  rows = display.getRows() ; //statusline
+    int  columns = display.getColumns();
+    int  tm = display.getTopMargin();
+    int  bm = display.getBottomMargin();
+    String  tosend;
+    Vector  vec;
+    byte  msg[];
+
+    if (debug>4) System.out.println("putChar("+c+" ["+((int)c)+"]) at R="+R+" , C="+C+", columns="+columns+", rows="+rows);
+    display.markLine(R,1);
+    if (c>255) {
+      if (debug>0)
+        System.out.println("char > 255:"+((int)c));
+      return;
+    }
+    switch (term_state) {
+    case TSTATE_DATA:
+      /* FIXME: we shouldn't use chars with bit 8 set if ibmcharset.
+       * probably... but some BBS do anyway...
+       */
+      if (!useibmcharset) {
+        boolean doneflag = true;
+        switch (c) {
+        case OSC:
+          osc="";
+          term_state = TSTATE_OSC;
+          break;
+        case RI:
+          if (R>tm)
+            R--;
+          else
+            display.insertLine(R,1,display.SCROLL_DOWN);
+          if (debug>1)
+            System.out.println("RI");
+          break;
+        case IND:
+          if (R == tm - 1 || R == bm || R == rows - 1) //  Ray: not bottom margin - 1
+            display.insertLine(R,1,display.SCROLL_UP);
+          else
+            R++;
+          if (debug>1)
+            System.out.println("IND (at "+R+" )");
+          break;
+        case NEL:
+          if (R == tm - 1 || R == bm || R == rows - 1) //  Ray: not bottom margin - 1
+            display.insertLine(R,1,display.SCROLL_UP);
+          else
+            R++;
+          C=0;
+          if (debug>1)
+            System.out.println("NEL (at "+R+" )");
+          break;
+        case HTS:
+          Tabs[C] = 1;
+          if (debug>1)
+            System.out.println("HTS");
+          break;
+        case DCS:
+          dcs="";
+          term_state = TSTATE_DCS;
+          break;
+        default:
+          doneflag = false;
+          break;
+        }
+        if (doneflag) break;
+      }
+      switch (c) {
+      case CSI: // should be in the 8bit section, but some BBS use this
+        term_state = TSTATE_DCEQ;
+        break;
+      case ESC:
+        term_state = TSTATE_ESC;
+        lastwaslf=0;
+        break;
+      case '\b':
+        C--;
+        if (C<0)
+          C=0;
+        lastwaslf = 0;
+        break;
+      case '\t':
+        if (insertmode == 1) {
+          int  nr,newc;
+
+          newc = C;
+          do {
+            display.insertChar(C,R,' ',attributes);
+            newc++;
+          } while (newc<columns && Tabs[newc]==0);
+        } else {
+          do {
+            display.putChar(C++,R,' ',attributes);
+          } while (C<columns && (Tabs[C]==0));
+        }
+        lastwaslf = 0;
+        break;
+      case '\r':
+        C=0;
+        break;
+      case '\n':
+       if (debug>3)
+               System.out.println("R= "+R+", bm "+bm+", tm="+tm+", rows="+rows);
+        if (!vms)
+        {
+            if (lastwaslf!=0 && lastwaslf!=c)   //  Ray: I do not understand this logic.
+              break;
+            lastwaslf=c;
+            C = 0;
+        }
+       if (R == tm - 1 || R == bm || R >= rows - 1)
+           display.insertLine(R,1);
+       else
+           R++;
+        break;
+      case '\016':
+        /* ^N, Shift out - Put G1 into GL */
+        gl = 1;
+        break;
+      case '\017':
+        /* ^O, Shift in - Put G0 into GL */
+        gl = 0;
+        break;
+      default:
+        lastwaslf=0;
+        if (c<32) {
+          if (c!=0)
+            if (debug>0)
+              System.out.println("TSTATE_DATA char: "+((int)c));
+          break;
+        }
+        if(C >= columns) {
+          if(R < rows - 1)
+            R++;
+          else
+            display.insertLine(R,display.SCROLL_UP);
+          C = 0;
+        }
+
+        // Mapping if DEC Special is chosen charset
+        if ( gx[gl] == '0' ) {
+          if ( c >= '\u005f' && c <= '\u007e' ) {
+          if (debug>3)
+            System.out.print("Mapping "+c+" (index "+((short)c-0x5f)+" to ");
+          c = DECSPECIAL[(short)c - 0x5f];
+          if (debug>3)
+            System.out.println(c+" ("+(int)c+")");
+          }
+        }
+/*
+        if ( gx[gr] == '0' ) {
+          if ( c >= '\u00bf' && c <= '\u00fe' ) {
+          if (debug>2)
+            System.out.print("Mapping "+c);
+            c = DECSPECIAL[(short)c - 0xbf];
+            if (debug>2)
+              System.out.println("to "+c);
+          }
+        }
+*/
+        if (useibmcharset)
+          c = map_cp850_unicode(c);
+
+       /*if(true || (statusmode == 0)) { */
+       if (debug>4) System.out.println("output "+c+" at "+C+","+R);
+         if (insertmode==1) {
+           display.insertChar(C, R, c, attributes);
+         } else {
+           display.putChar(C, R, c, attributes);
+         }
+       /*
+       } else {
+         if (insertmode==1) {
+           display.insertChar(C, rows, c, attributes);
+         } else {
+           display.putChar(C, rows, c, attributes);
+         }
+       }
+       */
+        C++;
+        break;
+      } /* switch(c) */
+      break;
+    case TSTATE_OSC:
+      if ((c<0x20) && (c!=ESC)) {// NP - No printing character
+        handle_osc(osc);
+        term_state = TSTATE_DATA;
+        break;
+      }
+      //but check for vt102 ESC \
+      if (c=='\\' && osc.charAt(osc.length()-1)==ESC) {
+        handle_osc(osc);
+        term_state = TSTATE_DATA;
+        break;
+      }
+      osc = osc + c;
+      break;
+    case TSTATE_ESC:
+      switch (c) {
+      case '#':
+        term_state = TSTATE_ESCSQUARE;
+        break;
+      case 'c':
+        /* Hard terminal reset */
+        /*FIXME:*/
+        term_state = TSTATE_DATA;
+        break;
+      case '[':
+        term_state = TSTATE_CSI;
+        DCEvar    = 0;
+        DCEvars[0]  = 0;
+        DCEvars[1]  = 0;
+        DCEvars[2]  = 0;
+        DCEvars[3]  = 0;
+        break;
+        case ']':
+        osc="";
+        term_state = TSTATE_OSC;
+        break;
+      case 'P':
+        dcs="";
+        term_state = TSTATE_DCS;
+        break;
+      case 'E':
+        if (R == tm - 1 || R == bm || R == rows - 1) //  Ray: not bottom margin - 1
+          display.insertLine(R,1,display.SCROLL_UP);
+        else
+          R++;
+        C=0;
+        if (debug>1)
+          System.out.println("ESC E (at "+R+")");
+        term_state = TSTATE_DATA;
+        break;
+      case 'D':
+        if (R == tm - 1 || R == bm || R == rows - 1)
+          display.insertLine(R,1,display.SCROLL_UP);
+        else
+          R++;
+        if (debug>1)
+          System.out.println("ESC D (at "+R+" )");
+        term_state = TSTATE_DATA;
+        break;
+      case 'M': // IL
+        if ((R>=tm) && (R<=bm)) // in scrolregion
+          display.insertLine(R,1,display.SCROLL_DOWN);
+       /* else do nothing ; */
+        if (debug>1)
+          System.out.println("ESC M ");
+        term_state = TSTATE_DATA;
+        break;
+      case 'H':
+        if (debug>1)
+          System.out.println("ESC H at "+C);
+        /* right border probably ...*/
+        if (C>=columns)
+          C=columns-1;
+        Tabs[C] = 1;
+        term_state = TSTATE_DATA;
+        break;
+      case '=':
+        /*application keypad*/
+        if (debug>0)
+          System.out.println("ESC =");
+        term_state = TSTATE_DATA;
+        break;
+      case '>':
+        /*normal keypad*/
+        if (debug>0)
+          System.out.println("ESC >");
+        term_state = TSTATE_DATA;
+        break;
+      case '7':
+        /*save cursor */
+        Sc = C;
+        Sr = R;
+        Sa = attributes;
+        if (debug>1)
+          System.out.println("ESC 7");
+        term_state = TSTATE_DATA;
+        break;
+      case '8':
+        /*restore cursor */
+        C = Sc;
+        R = Sr;
+        attributes = Sa;
+        term_state = TSTATE_DATA;
+        if (debug>1)
+          System.out.println("ESC 7");
+        break;
+      case '(':
+        /* Designate G0 Character set (ISO 2022) */
+        term_state = TSTATE_SETG0;
+        break;
+      case ')':
+        /* Designate G0 character set (ISO 2022) */
+        term_state = TSTATE_SETG1;
+        break;
+      case '*':
+        /* Designate G1 Character set (ISO 2022) */
+        term_state = TSTATE_SETG2;
+        break;
+      case '+':
+        /* Designate G1 Character set (ISO 2022) */
+        term_state = TSTATE_SETG3;
+        break;
+      case '~':
+        /* Locking Shift 1, right */
+        term_state = TSTATE_DATA;
+        gr = 1;
+        break;
+      case 'n':
+        /* Locking Shift 2 */
+        term_state = TSTATE_DATA;
+        gl = 2;
+        break;
+      case '}':
+        /* Locking Shift 2, right */
+        term_state = TSTATE_DATA;
+        gr = 2;
+        break;
+      case 'o':
+        /* Locking Shift 3 */
+        term_state = TSTATE_DATA;
+        gl = 3;
+        break;
+      case '|':
+        /* Locking Shift 3, right */
+        term_state = TSTATE_DATA;
+        gr = 3;
+        break;
+      default:
+        System.out.println("ESC unknown letter: ("+((int)c)+")");
+        term_state = TSTATE_DATA;
+        break;
+      }
+      break;
+    case TSTATE_SETG0:
+      if(c!='0' && c!='A' && c!='B')
+        System.out.println("ESC ( : G0 char set?  ("+((int)c)+")");
+      else {
+        if (debug>2) System.out.println("ESC ( : G0 char set  ("+c+" "+((int)c)+")");
+        gx[0] = c;
+      }
+      term_state = TSTATE_DATA;
+      break;
+    case TSTATE_SETG1:
+      if(c!='0' && c!='A' && c!='B')
+        System.out.println("ESC ) :G1 char set?  ("+((int)c)+")");
+      else {
+        if (debug>2) System.out.println("ESC ) :G1 char set  ("+c+" "+((int)c)+")");
+        gx[1] = c;
+      }
+      term_state = TSTATE_DATA;
+      break;
+    case TSTATE_SETG2:
+      if(c!='0' && c!='A' && c!='B')
+        System.out.println("ESC*:G2 char set?  ("+((int)c)+")");
+      else {
+        if (debug>2) System.out.println("ESC*:G2 char set  ("+c+" "+((int)c)+")");
+       gx[2] = c;
+      }
+      term_state = TSTATE_DATA;
+      break;
+    case TSTATE_SETG3:
+      if(c!='0' && c!='A' && c!='B')
+        System.out.println("ESC+:G3 char set?  ("+((int)c)+")");
+      else {
+        if (debug>2) System.out.println("ESC+:G3 char set  ("+c+" "+((int)c)+")");
+        gx[3] = c;
+      }
+      term_state = TSTATE_DATA;
+      break;
+    case TSTATE_ESCSQUARE:
+      switch (c) {
+      case '8':
+        for (int i=0;i<columns;i++)
+          for (int j=0;j<rows;j++)
+            display.putChar(i,j,'E',0);
+        break;
+      default:
+        System.out.println("ESC # "+c+" not supported.");
+        break;
+      }
+      term_state = TSTATE_DATA;
+      break;
+    case TSTATE_DCS:
+      if (c=='\\' && dcs.charAt(dcs.length()-1)==ESC) {
+        handle_dcs(dcs);
+        term_state = TSTATE_DATA;
+        break;
+      }
+      dcs = dcs + c;
+      break;
+    case TSTATE_DCEQ:
+      switch (c) {
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        DCEvars[DCEvar]=DCEvars[DCEvar]*10+((int)c)-48;
+        break;
+      case 'r': // XTERM_RESTORE
+        if (true || debug>1)
+          System.out.println("ESC [ ? "+DCEvars[0]+" r");
+        /* DEC Mode reset */
+        switch (DCEvars[0]){
+        case 3: /* 80 columns*/
+          size = display.size();
+          display.setWindowSize(80,rows);
+          layout();
+          break;
+        case 4: /* scrolling mode, smooth */
+          break;
+        case 5: /* light background */
+          break;
+        case 6: /* move inside margins ? */
+          originmode = false;
+          break;
+        case 12:/* local echo off */
+          break;
+        }
+        term_state = TSTATE_DATA;
+        break;
+      case 'h': // DECSET
+        if (debug>0)
+          System.out.println("ESC [ ? "+DCEvars[0]+" h");
+        /* DEC Mode set */
+        switch (DCEvars[0]){
+        case 1:  /* Application cursor keys */
+          KeyUp  = "\u001bOA";
+          KeyDown  = "\u001bOB";
+          KeyRight= "\u001bOC";
+          KeyLeft  = "\u001bOD";
+          break;
+        case 3: /* 132 columns*/
+          size = display.size();
+          display.setWindowSize(132,rows);
+          layout();
+          break;
+        case 4: /* scrolling mode, smooth */
+          break;
+        case 5: /* light background */
+          break;
+        case 6: /* move inside margins ? */
+          originmode = true;
+          break;
+        case 12:/* local echo off */
+          break;
+        }
+        term_state = TSTATE_DATA;
+        break;
+      case 'l':        //DECRST
+        /* DEC Mode reset */
+        if (debug>0)
+          System.out.println("ESC [ ? "+DCEvars[0]+" l");
+        switch (DCEvars[0]){
+        case 1:  /* Application cursor keys */
+          KeyUp  = "\u001b[A";
+          KeyDown  = "\u001b[B";
+          KeyRight= "\u001b[C";
+          KeyLeft  = "\u001b[D";
+          break;
+        case 3: /* 80 columns*/
+          size = display.size();
+          display.setWindowSize(80,rows);
+          layout();
+          break;
+        case 4: /* scrolling mode, jump */
+          break;
+        case 5: /* dark background */
+          break;
+        case 6: /* move outside margins ? */
+          originmode = false;
+          break;
+        case 12:/* local echo on */
+          break;
+        }
+        term_state = TSTATE_DATA;
+        break;
+      case ';':
+        DCEvar++;
+        DCEvars[DCEvar] = 0;
+        break;
+      case 'n':
+        if (debug>0)
+          System.out.println("ESC [ ? "+DCEvars[0]+" n");
+        switch (DCEvars[0]) {
+        case 15:
+          /* printer? no printer. */
+          host.send(((char)ESC)+"[?13n");
+          System.out.println("ESC[5n");
+          break;
+        default:break;
+        }
+        term_state = TSTATE_DATA;
+        break;
+      default:
+        if (debug>0)
+          System.out.println("ESC [ ? "+DCEvars[0]+" "+c);
+        term_state = TSTATE_DATA;
+        break;
+      }
+      break;
+    case TSTATE_CSI_DOLLAR:
+      switch (c) {
+      case '}':
+       System.out.println("Active Status Display now "+DCEvars[0]);
+       statusmode = DCEvars[0];
+       break;
+/* bad documentation?
+      case '-':
+       System.out.println("Set Status Display now "+DCEvars[0]);
+       break;
+ */
+      case '~':
+       System.out.println("Status Line mode now "+DCEvars[0]);
+       break;
+      default:
+       System.out.println("UNKNOWN Status Display code "+c+", with Pn="+DCEvars[0]);
+       break;
+      }
+      term_state = TSTATE_DATA;
+      break;
+    case TSTATE_CSI:
+      switch (c) {
+      case '$':
+       term_state=TSTATE_CSI_DOLLAR;
+       break;
+      case '?':
+        DCEvar=0;
+        DCEvars[0]=0;
+        term_state=TSTATE_DCEQ;
+        break;
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        DCEvars[DCEvar]=DCEvars[DCEvar]*10+((int)c)-48;
+        break;
+      case ';':
+        DCEvar++;
+        DCEvars[DCEvar] = 0;
+        break;
+      case 'c':/* send primary device attributes */
+        /* send (ESC[?61c) */
+        host.send(((char)ESC)+"[?1;2c");
+        term_state = TSTATE_DATA;
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" c");
+        break;
+      case 'q':
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" q");
+        term_state = TSTATE_DATA;
+        break;
+      case 'g':
+        /* used for tabsets */
+        switch (DCEvars[0]){
+        case 3:/* clear them */
+          int nw = display.getColumns();
+          Tabs = new byte[nw];
+          break;
+        case 0:
+          Tabs[C] = 0;
+          break;
+        }
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" g");
+        term_state = TSTATE_DATA;
+        break;
+      case 'h':
+        switch (DCEvars[0]) {
+        case 4:
+          insertmode = 1;
+          break;
+        case 20:
+          sendcrlf = true;
+          break;
+        default:
+          System.out.println("unsupported: ESC [ "+DCEvars[0]+" h");
+          break;
+        }
+        term_state = TSTATE_DATA;
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" h");
+        break;
+      case 'l':
+        switch (DCEvars[0]) {
+        case 4:
+          insertmode = 0;
+          break;
+        case 20:
+    sendcrlf = false;
+    break;
+        }
+        term_state = TSTATE_DATA;
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" l");
+        break;
+      case 'A': // CUU
+      {
+        int limit;
+       /* FIXME: xterm only cares about 0 and topmargin */
+        if (R > bm)
+            limit = bm+1;
+        else if (R >= tm) {
+            limit = tm;
+        } else
+            limit = 0;
+        if (DCEvars[0]==0)
+          R--;
+        else
+          R-=DCEvars[0];
+        if (R < limit)
+            R = limit;
+        term_state = TSTATE_DATA;
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" A");
+        break;
+      }
+      case 'B':        // CUD
+        /* cursor down n (1) times */
+      {
+        int limit;
+        if (R < tm)
+            limit = tm-1;
+        else if (R <= bm) {
+            limit = bm;
+        } else
+            limit = rows - 1;
+        if (DCEvars[0]==0)
+          R++;
+        else
+          R+=DCEvars[0];
+        if (R > limit)
+            R = limit;
+        else {
+            if (debug>2) System.out.println("Not limited.");
+        }
+        if (debug>2) System.out.println("to: " + R);
+        term_state = TSTATE_DATA;
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" B (at C="+C+")");
+        break;
+      }
+      case 'C':
+        if (DCEvars[0]==0)
+          C++;
+        else
+          C+=DCEvars[0];
+        if (C>columns-1)
+          C=columns-1;
+        term_state = TSTATE_DATA;
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" C");
+        break;
+      case 'd': // CVA
+       R = DCEvars[0];
+        System.out.println("ESC [ "+DCEvars[0]+" d");
+        term_state = TSTATE_DATA;
+       break;
+      case 'D':
+        if (DCEvars[0]==0)
+          C--;
+        else
+          C-=DCEvars[0];
+        if (C<0) C=0;
+        term_state = TSTATE_DATA;
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" D");
+        break;
+      case 'r': // DECSTBM
+        if (DCEvar>0)   //  Ray:  Any argument is optional
+        {
+          R = DCEvars[1]-1;
+          if (R < 0)
+            R = rows-1;
+          else if (R >= rows) {
+            R = rows - 1;
+         }
+        } else
+          R = rows - 1;
+        display.setBottomMargin(DCEvars[1]-1);
+        if (R >= DCEvars[0])
+        {
+          R = DCEvars[0]-1;
+          if (R < 0)
+            R = 0;
+        }
+        display.setTopMargin(DCEvars[0]-1);
+       _SetCursor(0,0);
+        if (debug>1)
+          System.out.println("ESC ["+DCEvars[0]+" ; "+DCEvars[1]+" r");
+        term_state = TSTATE_DATA;
+        break;
+      case 'G':  /* CUP  / cursor absolute column */
+       C = DCEvars[0];
+       System.out.println("ESC [ "+DCEvars[0]+" G");
+        term_state = TSTATE_DATA;
+       break;
+      case 'H':  /* CUP  / cursor position */
+        /* gets 2 arguments */
+       _SetCursor(DCEvars[0]-1,DCEvars[1]-1);
+        term_state = TSTATE_DATA;
+        if (debug>2) {
+          System.out.println("ESC [ "+DCEvars[0]+";"+DCEvars[1]+" H, originmode "+originmode);
+          System.out.println(" -> R now "+R+", C now "+C);
+       }
+        break;
+      case 'f':  /* move cursor 2 */
+        /* gets 2 arguments */
+        R = DCEvars[0]-1;
+        C = DCEvars[1]-1;
+        if (C<0) C=0;
+        if (R<0) R=0;
+        term_state = TSTATE_DATA;
+        if (debug>2)
+          System.out.println("ESC [ "+DCEvars[0]+";"+DCEvars[1]+" f");
+        break;
+      case 'L':
+        /* insert n lines */
+        if (DCEvars[0]==0)
+          display.insertLine(R,display.SCROLL_DOWN);
+        else
+          display.insertLine(R,DCEvars[0],display.SCROLL_DOWN);
+        term_state = TSTATE_DATA;
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" L");
+        break;
+      case 'M':
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+"M at R="+R);
+        if (DCEvars[0]==0)
+          display.deleteLine(R);
+        else
+          for (int i=0;i<DCEvars[0];i++)
+            display.deleteLine(R);
+        term_state = TSTATE_DATA;
+        break;
+      case 'K':
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" K");
+        /* clear in line */
+        switch (DCEvars[0]) {
+        case 0:/*clear to right*/
+          if (C<columns-1)
+            display.deleteArea(C,R,columns-C,1);
+          break;
+        case 1:/*clear to the left*/
+          if (C>0)
+            display.deleteArea(0,R,C,1);    // Ray: Should at least include character before this one, not C-1
+          break;
+        case 2:/*clear whole line */
+          display.deleteArea(0,R,columns,1);
+          break;
+        }
+        term_state = TSTATE_DATA;
+        break;
+      case 'J':
+        /* clear display.below current line */
+        switch (DCEvars[0]) {
+        case 0:
+          if (R<rows-1)
+            display.deleteArea(0,R + 1,columns,rows-R-1);
+          if (C<columns-1)
+            display.deleteArea(C,R,columns-C,1);
+          break;
+        case 1:
+          if (R>0)
+            display.deleteArea(0,0,columns,R-1);
+          if (C>0)
+            display.deleteArea(0,R,C,1);    // Ray: Should at least include character before this one, not C-1
+          break;
+        case 2:
+          display.deleteArea(0,0,columns,rows);
+          break;
+        }
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" J");
+        term_state = TSTATE_DATA;
+        break;
+      case '@':
+       if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" @");
+       for (int i=0;i<DCEvars[0];i++)
+         display.insertChar(C,R,' ',attributes);
+       term_state = TSTATE_DATA;
+       break;
+      case 'P':
+        if (debug>1)
+          System.out.println("ESC [ "+DCEvars[0]+" P, C="+C+",R="+R);
+       for (int i=0;i<DCEvars[0];i++)
+          display.deleteChar(C,R);
+        term_state = TSTATE_DATA;
+        break;
+      case 'n':
+        switch (DCEvars[0]){
+        case 5: /* malfunction? No malfunction. */
+          host.send(((char)ESC)+"[0n");
+          if(debug > 1)
+            System.out.println("ESC[5n");
+          break;
+        case 6:
+          host.send(((char)ESC)+"["+R+";"+C+"R");
+          if(debug > 1)
+            System.out.println("ESC[6n");
+          break;
+        default:
+          if (debug>0)
+            System.out.println("ESC [ "+DCEvars[0]+" n??");
+          break;
+        }
+        term_state = TSTATE_DATA;
+        break;
+      case 'm':  /* attributes as color, bold , blink,*/
+        if (debug>1)
+          System.out.print("ESC [ ");
+        if (DCEvar == 0 && DCEvars[0] == 0)
+          attributes = 0;
+        for (i=0;i<=DCEvar;i++) {
+          switch (DCEvars[i]) {
+          case 0:
+            if (DCEvar>0)
+              attributes =0;
+            break;
+          case 4:
+            attributes |= CharDisplay.UNDERLINE;
+            break;
+          case 1:
+            attributes |= CharDisplay.BOLD;
+            break;
+          case 7:
+            attributes |= CharDisplay.INVERT;
+            break;
+          case 5: /* blink on */
+            break;
+          case 25: /* blinking off */
+            break;
+          case 27:
+            attributes &= ~CharDisplay.INVERT;
+            break;
+          case 24:
+            attributes &= ~CharDisplay.UNDERLINE;
+            break;
+          case 22:
+            attributes &= ~CharDisplay.BOLD;
+            break;
+          case 30:
+          case 31:
+          case 32:
+          case 33:
+          case 34:
+          case 35:
+          case 36:
+          case 37:
+            attributes &= ~(0xf<<3);
+            attributes |= ((DCEvars[i]-30)+1)<<3;
+            break;
+          case 39:
+            attributes &= ~(0xf<<3);
+            break;
+          case 40:
+          case 41:
+          case 42:
+          case 43:
+          case 44:
+          case 45:
+          case 46:
+          case 47:
+            attributes &= ~(0xf<<7);
+            attributes |= ((DCEvars[i]-40)+1)<<7;
+            break;
+          case 49:
+            attributes &= ~(0xf<<7);
+            break;
+
+          default:
+            System.out.println("ESC [ "+DCEvars[i]+" m unknown...");
+            break;
+          }
+          if (debug>1)
+            System.out.print(""+DCEvars[i]+";");
+        }
+        if (debug>1)
+          System.out.print(" (attributes = "+attributes+")m \n");
+        term_state = TSTATE_DATA;
+        break;
+      default:
+        if (debug>0)
+          System.out.println("ESC [ unknown letter:"+c+" ("+((int)c)+")");
+        term_state = TSTATE_DATA;
+        break;
+      }
+      break;
+    default:
+      term_state = TSTATE_DATA;
+      break;
+    }
+    if (C > columns) C = columns;
+    if (R > rows)  R = rows;
+    if (C < 0)  C = 0;
+    if (R < 0)  R = 0;
+    if (doshowcursor)
+      display.setCursorPos(C, R);
+    display.markLine(R,1);
+  }
+}
diff --git a/src/de/mud/telnet/frame.java b/src/de/mud/telnet/frame.java
new file mode 100644 (file)
index 0000000..74f20fd
--- /dev/null
@@ -0,0 +1,55 @@
+package de.mud.telnet;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+/**
+ * frame -- a frame subclass for handling frame events
+ * --
+ * $Id: frame.java,v 1.1 1997/07/08 09:21:56 leo Exp $
+ * $timestamp: Tue Jul  8 10:02:36 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.awt.Frame;
+import java.awt.Component;
+import java.awt.Event;
+import java.applet.Applet;
+
+public class frame extends Frame 
+{
+  public frame(String title) { super(title); }
+  
+  public boolean handleEvent(Event evt) {
+    if(evt.target == this && evt.id == Event.WINDOW_DESTROY) {
+      Component comp[] = getComponents();
+      for(int i = comp.length - 1; i >= 0; i--)
+        if(comp[i] instanceof Applet) {
+          ((Applet)comp[i]).stop();
+          this.dispose();
+          return true;
+        }
+    }
+    return false;
+  }
+}
+
+    
+    
diff --git a/src/de/mud/telnet/modules/ButtonBar.java b/src/de/mud/telnet/modules/ButtonBar.java
new file mode 100644 (file)
index 0000000..68fc8be
--- /dev/null
@@ -0,0 +1,389 @@
+package de.mud.telnet.modules;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+/**
+ * ButtonBar -- a programmable button bar
+ * --
+ * $Id: ButtonBar.java,v 1.22 1998/02/24 13:09:09 leo Exp $
+ * $timestamp: Mon Aug  4 14:12:21 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+import java.applet.Applet;
+import java.util.Hashtable;
+import java.util.Vector;
+import de.mud.telnet.*;
+
+import java.awt.Panel;
+import java.awt.Button;
+import java.awt.TextField;
+import java.awt.Event;
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.Dimension;
+import java.awt.Container;
+import java.awt.Frame;
+
+/**
+ * This class implements a programmable button bar.
+ * You can add <A HREF="#buttons">Buttons</A> and <A HREF="#fields">Input 
+ * fields</A> to trigger actions in the <A HREF="telnet.html">telnet 
+ * applet</A>. On how to load a module, please refer to the 
+ * <A HREF="telnet.html">telnet</A> documentation.
+ * 
+ * <DL>
+ *  <A NAME="buttons"></A>
+ *  <DT><B>Buttons:</B>
+ *  <DD><TT>&lt;PARAM NAME=<B><I>number</I></B>#Button VALUE=&quot;<B><I>buttontext</I></B>|<B><I>buttonaction</I></B>&quot;&gt;</TT>
+ *  <DD><B><I>number</I></B> is the sequence number and determines the place
+ *      of the button on the row.
+ *      <P>
+ *  <DD><B><I>buttontext</I></B> is a string displayed on the button.
+ *      <P>
+ *  <DD><A NAME="buttonaction"><B><I>buttonaction</I></B></A> may be one
+ *      of the following functions or strings<BR>
+ *      <FONT SIZE=-1>(<I>Note:</I> the backslash character
+ *      in front of the dollar sign is mandatory!)</FONT>
+ *      <UL>
+ *       <LI><TT><I>simple text</I></TT>
+ *           to be sent to the remote host. Newline and/or carriage return
+ *           characters may be added in C syntax <B>\n</B> and <B>\r</B>.
+ *           To support unimplemented function keys the <B>\e</B> escape
+ *           character may be useful. The <B>\b</B> backspace character is
+ *           also supported.
+ *           The text may contain <A HREF="#fieldreference"><B><I>field 
+ *           reference(s)</I></B></A>.
+ *           <P>
+ *       <LI><TT>\$connect(<B><I>host</I></B>[,<B><I>port</I></B>])</TT> 
+ *           tries to initiate a connection to the <B><I>host</I></B>
+ *           at the <B><I>port</I></B>, if given. The standard port is
+ *           23. <B><I>host</I></B> and <B><I>port</I></B> may be hostname
+ *           and number or <A HREF="#fieldreference"><B><I>field
+ *           reference(s)</I></B></A>. If a connection already exists
+ *           nothing will happen.<BR>
+ *           <FONT SIZE=-1>(<I>Note:</I> It is not allowed to have
+ *           spaces anywhere inside the parenthesis!)</FONT>
+ *           <P>
+ *       <LI><TT>\$disconnect()</TT>
+ *           terminates the current connection, but if there was no
+ *           connection nothing will happen.
+ *           <P>
+ *       <LI><TT>\$detach()</TT>
+ *           detaches the applet from the web browser window and
+ *           creates a new frame externally. This may be used to allow
+ *           users to use the applet while browsing the web with the
+ *           same browser window.<BR>
+ *           <FONT SIZE=-1>(<I>Note:</I> You need to load the applet via the
+ *           <A HREF="appWrapper.html">appWrapper class</A> or
+ *           it will not work properly!)</FONT>
+ *      </UL>
+ *  <DD><B>Examples:</B><BR>
+ *      <FONT SIZE=-1>(<I>Note:</I> It makes sense if you look at the
+ *      examples for input fields below.)</FONT>
+ *      <PRE>
+ *        &lt;PARAM NAME=1#Button VALUE="HELP!|help\r\n"&gt;
+ *        &lt;PARAM NAME=2#Button VALUE="HELP:|help \@help@\r\n"&gt;
+ *        &lt;PARAM NAME=4#Button VALUE="simple|\$connect(localhost)"&gt;
+ *        &lt;PARAM NAME=5#Button VALUE="complete|\$connect(www,4711)"&gt;
+ *        &lt;PARAM NAME=6#Button VALUE="connect|\$connect(\@address@)"&gt;
+ *        &lt;PARAM NAME=8#Button VALUE="connect to port|\$connect(\@address@,\@port@)"&gt;
+ *        &lt;PARAM NAME=10#Button VALUE="window|\$detach()"&gt;
+ *     </PRE>
+ *     <P>
+ *  <A NAME="fields"></A>
+ *  <DT><B>Input fields</B>
+ *  <DD><TT>&lt;PARAM NAME=<B><I>number</I></B>#Input VALUE=&quot;<B><I>fieldname</I></B>[#<I><B>length</B></I>]|<B><I>initial text</I></B>[|<B><I>action</I></B>]&quot;&gt;</TT>
+ *  <DD><B><I>number</I></B> is the sequence number and determines the place
+ *      of the field on the row.
+ *      <P>
+ *  <DD><A NAME="fieldreference"><B><I>fieldname</I></B></A> is a
+ *      symbolic name to reference the input field. A reference may be used in 
+ *      <A HREF="#buttonaction"><B><I>button actions</I></B></A> and
+ *      is constructed as follows:
+ *      <TT>\@<B><I>fieldname</I></B>@</TT>
+ *      The <B>\@fieldname@</B> macro will be replaced by the string entered in
+ *      the text field.
+ *      <P>
+ *  <DD><B><I>length</I></B> is the length of the input field in numbers of
+ *      characters.
+ *      <P>
+ *  <DD><B><I>initial text</I></B> is the text to be placed into the input
+ *      field on startup
+ *  <DD><B><I>action</I></B> may be used similar to a 
+ *      <A HREF="#buttonaction"><B><I>button action</I></B></A>. This action 
+ *      will be used if the users presses Return in the inputfield. Leave
+ *      empty if you only want to use a button to send the text!
+ *  <DD><B>Examples:</B><BR>
+ *      <FONT SIZE=-1>(<I>Note:</I> It makes sense if you look at the
+ *      examples for buttons before.)</FONT>
+ *      <PRE>
+ *        &lt;PARAM NAME=3#Input VALUE="help#10|"&gt;
+ *        &lt;PARAM NAME=7#Input VALUE="address|www.first.gmd.de"&gt;
+ *        &lt;PARAM NAME=8#Input VALUE="send#5|who|\@send@\r\n"&gt;
+ *        &lt;PARAM NAME=9#Input VALUE="port#5|4711"&gt;
+ *      </PRE>
+ *      <P>
+ * </DL>
+ * @version $Id: ButtonBar.java,v 1.22 1998/02/24 13:09:09 leo Exp $
+ * @author  Matthias L. Jugel, Marcus Meißner
+ * @see modules.Module
+ */
+public class ButtonBar extends Panel implements Module
+{
+  // our parent is the telnet app
+  private telnet parent;
+  
+  // these tables contain our buttons and fields.
+  private Hashtable buttons = null;
+  private Hashtable fields = null;
+
+  // the top level (for detaching)
+  private Container toplevel;
+
+  /**
+   * This method is called by our loader to notify us of it. 
+   * @param o The object that has loaded this object.
+   * @see display.Module
+   */
+  public void setLoader(Object o) { parent = (telnet)o; }
+
+  /**
+   * If the applet connects this method is called.
+   * @param host remote hostaddress - not used
+   * @param port remote port - not used
+   */
+  public void connect(String host, int port) {
+    // do nothing yet.
+  }
+
+  /**
+   * Get notified of disconnection. Do nothing.
+   */
+  public void disconnect() {
+    // do nothing yet
+  }
+
+  /**
+   * This module does not take any input. It works passive.
+   * @return null to remove from the list of receiver modules.
+   * @see display.Module
+   */
+  public String receive(String s) { return null; }
+       
+  /**
+   * create the buttonbar from the parameter list. We will know our parent,
+   * when we have been added.
+   */
+  public void addNotify() {
+    if(buttons == null && fields == null) {
+      String tmp; 
+
+      int nr = 1;
+      String button = null, input = null;
+      while((button = parent.getParameter(nr+"#Button")) != null ||
+           (input = parent.getParameter(nr+"#Input")) != null) {
+       nr++;
+       if(button != null) {
+         if(buttons == null) buttons = new Hashtable();
+         int idx = button.indexOf('|');
+         if(button.length() == 0)
+           System.out.println("ButtonBar: Button: no definition");
+         if(idx < 0 || idx == 0) {
+           System.out.println("ButtonBar: Button: empty name \""+button+"\"");
+           continue;
+         }
+         if(idx == button.length() - 1) {
+           System.out.println("ButtonBar: Button: empty command \""+button+"\"");
+           continue;
+         }
+         Button b = new Button(button.substring(0, idx));
+         buttons.put(b, button.substring(idx+1, button.length()));
+         add(b);
+       } else
+         if(input != null) {
+           if(fields == null) fields = new Hashtable();
+           int idx = input.indexOf('|');
+           if(input.length() == 0)
+             System.out.println("ButtonBar: Input field: no definition");
+           if(idx < 0 || idx == 0) {
+             System.out.println("ButtonBar: Input field: empty name \""+input+"\"");
+             continue;
+           }
+           int si, size;
+           if((si = input.indexOf('#', 0)) == 0) {
+             System.out.println("ButtonBar: Input field: empty name");
+             continue;
+           }
+           if(si < 0 || si == idx-1) size = 10;
+           else size = Integer.parseInt(input.substring(si+1, idx));
+           TextField t = 
+             new TextField(input.substring(idx + 1, 
+                                           ((input.lastIndexOf('|') == idx) ?
+                                             input.length() : 
+                                             (idx = input.lastIndexOf('|')))),
+                           size);
+           buttons.put(t, input.substring(idx + 1, input.length()));
+           fields.put(input.substring(0, (si < 0 ? idx : si)), t);
+           add(t);
+         }
+       button = input = null;
+      }
+    }
+    super.addNotify();
+  }
+
+  public boolean handleEvent(Event evt) {
+    String tmp;
+    if(evt.id == Event.ACTION_EVENT &&
+       (tmp = (String)buttons.get(evt.target)) != null) {
+      System.out.println("ButtonBar: "+tmp);
+      String cmd = "", function = null;
+      int idx = 0, oldidx = 0;
+      while((idx = tmp.indexOf('\\', oldidx)) >= 0 && 
+           ++idx <= tmp.length()) {
+       cmd += tmp.substring(oldidx, idx-1);
+       switch(tmp.charAt(idx)) {
+       case 'b': cmd += "\b"; break;
+       case 'e': cmd += "\e"; break;
+       case 'n': cmd += "\n"; break;
+       case 'r': cmd += "\r"; break;
+       case '$': {
+         int ni = tmp.indexOf('(', idx+1);
+         if(ni < idx) {
+           System.out.println("ERROR: Function: missing '('");
+           break;
+         }
+         if(ni == ++idx) {
+           System.out.println("ERROR: Function: missing name");
+           break;
+         }
+         function = tmp.substring(idx, ni);
+         idx = ni+1;
+         ni = tmp.indexOf(')', idx);
+         if(ni < idx) {
+           System.out.println("ERROR: Function: missing ')'");
+           break;
+         }
+         tmp = tmp.substring(idx, ni);
+         idx = oldidx = 0;
+         continue;
+       }
+       case '@': {
+         int ni = tmp.indexOf('@', idx+1);
+         if(ni < idx) {
+           System.out.println("ERROR: Input Field: '@'-End Marker not found");
+           break;
+         }
+         if(ni == ++idx) {
+           System.out.println("ERROR: Input Field: no name specified");
+           break;
+         }
+         String name = tmp.substring(idx, ni);
+         idx = ni;
+         TextField t;
+         if(fields == null || (t = (TextField)fields.get(name)) == null) {
+           System.out.println("ERROR: Input Field: requested input \""+
+                              name+"\" does not exist");
+           break;
+         }
+         cmd += t.getText();
+         t.setText("");
+         break;
+       }
+       default : cmd += tmp.substring(idx, ++idx);
+       }
+       oldidx = ++idx;
+      }
+
+      if(oldidx <= tmp.length()) cmd += tmp.substring(oldidx, tmp.length());
+      
+      if(function != null) {
+       if(function.equals("exit")) { 
+         try {
+           System.exit(0);
+         } catch(Exception e) { e.printStackTrace(); }
+       }
+       if(function.equals("connect")) {
+         String address = null;
+         int port = -1;
+         try {
+           if((idx = cmd.indexOf(",")) >= 0) {
+             try {
+               port = Integer.parseInt(cmd.substring(idx+1, cmd.length()));
+             } catch(Exception e) {
+               port = -1;
+             }
+             cmd = cmd.substring(0, idx);
+           }
+           if(cmd.length() > 0) address = cmd;
+           if(address != null) 
+             if(port != -1) parent.connect(address, port);
+             else parent.connect(address);
+           else parent.connect();
+         } catch(Exception e) {
+           System.err.println("ButtonBar: connect(): failed");
+           e.printStackTrace();
+         }
+       } else
+         if(function.equals("disconnect") && parent.disconnect())
+           parent.send("\r\nClosed connection.\r\n");
+         else
+           if(function.equals("detach")) {
+             if(parent.getParent() instanceof Frame) {
+               Frame top = (Frame)parent.getParent();
+               if(toplevel != null) {
+                 System.out.println("ButtonBar: reattaching applet...");
+                 toplevel.setLayout(new BorderLayout());
+                 toplevel.add("Center", parent);
+                 toplevel.validate();
+                 toplevel.layout();
+                 toplevel = null;
+               } else {
+                 System.out.println("ButtonBar: destroying window...");
+                 parent.disconnect();
+               }
+               top.dispose();
+             } else {
+               System.out.println("ButtonBar: detaching applet...");
+               toplevel = parent.getParent();
+               frame top = new frame("The Java Telnet Applet");
+               Dimension s = parent.size();
+               top.reshape(0, 0, s.width, s.height);
+               top.setLayout(new BorderLayout());
+               top.add("Center", parent);
+               top.pack();
+               top.show();
+             }
+           }
+           else
+             System.out.println("ERROR: function not implemented: \""+
+                                function+"\"");
+       return true;
+      }
+      // cmd += tmp.substring(oldidx, tmp.length());
+      if(cmd.length() > 0) parent.send(cmd);
+      return true;
+    }
+    return false;
+  }
+}
+
diff --git a/src/de/mud/telnet/modules/Module.java b/src/de/mud/telnet/modules/Module.java
new file mode 100644 (file)
index 0000000..61e15c4
--- /dev/null
@@ -0,0 +1,64 @@
+package de.mud.telnet.modules;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+/**
+ * Module -- Module interface
+ * --
+ * $Id: Module.java,v 1.3 1997/03/24 14:55:18 leo Exp $
+ * $timestamp: Mon Mar 24 15:35:13 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * Modules must implement this interface to be detected as valid modules
+ * @version $Id: Module.java,v 1.3 1997/03/24 14:55:18 leo Exp $
+ * @author  Matthias L. Jugel, Marcus Meißner
+ */
+public interface Module 
+{
+       /**
+        * Set the loader of the module. This is necessary to know if you want to
+        * contact the modules parent.
+        * @param loader The object that has loaded this module.
+        */
+       public void setLoader(Object loader);
+
+       /**
+        * Connected to the remote host. This method notifies upon new connection.
+        * @param host remote hostname
+        * @param port remote port
+        */
+       public void connect(String host, int port);
+       
+       /**
+        * Disconnect from the host. This method notifies of lost connection.
+        */
+       public void disconnect();
+       
+       /**
+        * Receive data from somewhere. If a modules does not want to receive data
+        * it should return null to remove itself from the list of receiver modules.
+        * @param s The string we receive.
+   * @return the modified string or null (to remove from receiver list)
+        */
+       public String receive(String s);
+}
+
diff --git a/src/de/mud/telnet/modules/Script.java b/src/de/mud/telnet/modules/Script.java
new file mode 100644 (file)
index 0000000..a255f14
--- /dev/null
@@ -0,0 +1,181 @@
+package de.mud.telnet.modules;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+/**
+ * Script -- A module for scripting (very simple).
+ * --
+ * $Id: Script.java,v 1.6 1997/11/03 10:25:57 leo Exp $
+ * $timestamp: Mon Mar 24 15:52:12 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import de.mud.telnet.*;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.awt.Dialog;
+import java.awt.Frame;
+import java.awt.TextField;
+import java.awt.Label;
+import java.awt.Event;
+
+/**
+ * A very simple scripting module. It takes pairs of pattern and text and
+ * sends the corresponding text when the pattern matches. Each pattern is
+ * only matched once per connected session.
+ *
+ * <DL>
+ *  <DT><B>Scripts:</B>
+ *   <DD><PRE>&lt;PARAM NAME=script VALUE=&quot;<B><I>pattern</I></B>|<B><I>text</I></B>|<B><I>...</I></B>&quot;&gt;</PRE>
+ *   <DD>A script contains of pairs of <I>pattern</I> and <I>text</I> strings.
+ *       If the pattern is matched against the output from the remote host,
+ *       the corresponding text will be sent. Each pattern will match only
+ *       <B>once</B> per session. A session is defined by connect and 
+ *       disconnect.<P>
+ *       Thus it is possible to program an autologin as follows:<BR>
+ *       <PRE><B>"login:|leo|Password:|mypassword|leo@www|ls"</B></PRE>
+ *       Newlines will be added automatically to the string sent! At the
+ *       moment the order of the pattern and text pairs is <I>not</I> relevant.
+ *       <P>
+ *       It is possible to prompt the user for input if a match occurs. If the
+ *       corresponding <I>text</I> is a string enclosed in braces ([] or {}) a
+ *       dialog window is opened with <I>text</I> as prompt. A special case 
+ *       is an empty prompt in which case the <I>pattern</I> will be shown as 
+ *       prompt. &quot;[Your name:]&quot; would open a dialog window with the
+ *       text &quot;Your name&quot; as prompt. Curly braces have a special
+ *       meaning; any user input will be shown as &quot;*&quot; which makes
+ *       it possible to program password prompts. Example: 
+ *       &quot;{Your password:}&quot;.<P>
+ *       A special match like: &quot;login:|[]&quot; can be used to open a
+ *       dialog and display &quot;login:&quot; as prompt. This works for
+ *       &quot;{}&quot; as well.
+ *       
+ * </DL>
+ * @version $Id: Script.java,v 1.6 1997/11/03 10:25:57 leo Exp $
+ * @author  Matthias L. Jugel, Marcus Meißner
+ * @see modules.Module
+ */
+public class Script extends Hashtable implements Module
+{
+  // This is the target for any text we want to send
+  private telnet applet = null;
+
+  /**
+   * Set the applet as module loader
+   * @param o the object that is the applet (must be an Applet)
+   * @see module.Module
+   * @see java.applet.Applet
+   */
+  public void setLoader(Object o) { applet = (telnet)o; }
+       
+  /**
+   * Configure the script module by reading the script PARAMeter.
+   * @param host remote hostaddress - not used
+   * @param port remote port - not used
+   */
+  public void connect(String host, int port) {
+    String tmp = applet.getParameter("script");
+    
+    // delete all entries
+    clear();
+    
+    if(tmp != null) {
+      int idx = tmp.indexOf('|');
+      int oldidx = 0;
+      while(idx >= 0) {
+       String match = tmp.substring(oldidx, idx);
+       oldidx = idx;
+       idx = tmp.indexOf('|', idx+1);
+       idx = idx < 0 ? idx = tmp.length() : idx;
+       String send = tmp.substring(oldidx+1, idx);
+       put(match, send);
+       oldidx = idx+1;
+       idx = tmp.indexOf('|', idx+1);
+      }
+    }
+  }
+
+  /**
+   * Get notified of disconnection. Do nothing.
+   */
+  public void disconnect() {}
+       
+  /**
+   * This method is called when data is received. It tries to match the
+   * input to the list of patterns and sends corresponding text on success.
+   * If the response is [] or {} the user will be prompted with the matching
+   * text. You can modify the prompt string by entering it inside of the
+   * brackets or curly braces (e.g. [Enter your id:]). In case of curly
+   * braces the input area will not show the typed in text (for passwords)!
+   *
+   * @param s The string to test.
+   * @see peer.InputPeer
+   */
+  public String receive(String s) {
+    if(isEmpty()) return s;
+    Enumeration match = keys();
+    while(match.hasMoreElements()) {
+      String key = (String)match.nextElement();
+      if(s.indexOf(key) != -1) {
+       String value = (String)get(key);
+       if(value.indexOf("[") == 0 || value.indexOf("{") == 0) {
+         TextField input = new TextField(20);
+         if(value.startsWith("{")) input.setEchoCharacter('*');
+         if("[]".equals(value) || "{}".equals(value)) value = key;
+         else value = value.substring(1, value.length() - 1);
+         Thread current = Thread.currentThread();
+         new UserDialog(new Frame(), value, false, current, input);
+         current.suspend();
+         value = input.getText();
+       }
+       applet.send(value + "\r");
+       remove(key);
+      }
+    }
+    return s;
+  }
+static class UserDialog extends Dialog {
+  TextField input;
+  Thread thread;
+  String value;
+
+  public UserDialog(Frame parent, String value, boolean modal, 
+                   Thread t, TextField reply) {
+    super(parent, value, modal);
+    thread = t; input = reply;
+    add("West", new Label(value));
+    add("Center", input);
+    pack();
+    show();
+  }
+
+  public boolean handleEvent(Event evt) {
+    if(evt.target == input && evt.key == 10) {
+      thread.resume();
+      hide(); dispose();
+      return true;
+    }
+    return false;
+  }
+}
+
+}
+
diff --git a/src/de/mud/telnet/modules/TextLabel.java b/src/de/mud/telnet/modules/TextLabel.java
new file mode 100644 (file)
index 0000000..2fb57f4
--- /dev/null
@@ -0,0 +1,113 @@
+package de.mud.telnet.modules;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+/**
+ * TextLabel -- A module to display a Label on the applet.
+ * --
+ * $Id: TextLabel.java,v 1.1 1997/07/09 20:12:05 leo Exp $
+ * $timestamp: Wed Jul  9 17:37:28 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import de.mud.telnet.*;
+import java.awt.Panel;
+import java.awt.Label;
+import java.awt.GridLayout;
+import java.awt.Font;
+
+/**
+ * This small module lets you display text somewhere in the applets area.
+ *
+ * <DL>
+ *  <DT><B>Label:</B>
+ *   <DD><PRE>&lt;PARAM NAME=labelRows    VALUE=&quot;<B><I>rows</B></I>&quot;&gt;</PRE>
+ *   <DD>Defines the how many rows the label will have.
+ *   <DD><PRE>&lt;PARAM NAME=labelCols    VALUE=&quot;<B><I>cols</B></I>&quot;&gt;</PRE>
+ *   <DD>Defines the how many columns the label will have.
+ *   <DD><PRE>&lt;PARAM NAME=labelFont    VALUE=&quot;<B><I>font[,size]</B></I>&quot;&gt;</PRE>
+ *   <DD>The font for displaying the label text. If the <I>size</I> is left out
+ *       a standard size of 14 points is assumed.
+ *   <DD><PRE>&lt;PARAM NAME=label#<I>number</I> VALUE=&quot;<B><I>text</I></B>&quot;&gt;</PRE>
+ *   <DT>The labels are enumerated and displayed in rows and columns.
+ * </DL>
+ * @version $Id: TextLabel.java,v 1.1 1997/07/09 20:12:05 leo Exp $
+ * @author  Matthias L. Jugel, Marcus Meißner
+ * @see modules.Module
+ */
+public class TextLabel extends Panel implements Module
+{
+  telnet applet;
+  
+       /**
+        * Set the applet as module loader and configure.
+        * @param o the object that is the applet (must be an Applet)
+        * @see module.Module
+        * @see java.applet.Applet
+        */
+       public void setLoader(Object o) { 
+    applet = (telnet)o;
+
+    int rows = 1, cols = 1;
+    
+    String tmp = applet.getParameter("labelRows");
+    if(tmp != null) rows = Integer.parseInt(tmp);
+    if((tmp = applet.getParameter("labelCols")) != null)
+      cols = Integer.parseInt(tmp);
+
+    setLayout(new GridLayout(rows, cols));
+
+    Font labelFont = null;
+    if((tmp = applet.getParameter("labelFont")) != null) {
+      int idx = tmp.indexOf(",");
+      int size = 14;
+      if(idx != -1) size = Integer.parseInt(tmp.substring(idx+1));
+      labelFont = new Font(tmp, Font.PLAIN, size);
+    }
+    
+    int no = 1;
+    while((tmp = applet.getParameter("label#"+no++)) != null) {
+      Label text = new Label(tmp);
+      if(labelFont != null) text.setFont(labelFont);
+      add(text);
+    }
+  }
+       
+       /**
+        * Do nothing upon connect.
+        * @param host remote hostaddress - not used
+        * @param port remote port - not used
+        */
+       public void connect(String host, int port) {}
+  
+       /**
+        * Do nothing upon disconnecton.
+        */
+       public void disconnect() {}
+       
+       /**
+   * Do nothing when receiving text. Be removed upon first call.
+        * @param s The string received.
+        * @see peer.InputPeer
+        */
+       public String receive(String s) { return null; }
+}
+
diff --git a/src/de/mud/telnet/socket/StatusPeer.java b/src/de/mud/telnet/socket/StatusPeer.java
new file mode 100644 (file)
index 0000000..782de56
--- /dev/null
@@ -0,0 +1,51 @@
+package de.mud.telnet.socket;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+/**
+ * Status peer interface.
+ * --
+ * $Id: StatusPeer.java,v 1.1.1.1 1997/03/05 13:35:16 leo Exp $
+ * $timestamp: Wed Mar  5 13:40:54 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.util.Vector;
+
+/**
+ * StatusPeer -- interface for status messages
+ * --
+ * @version    $Id: StatusPeer.java,v 1.1.1.1 1997/03/05 13:35:16 leo Exp $
+ * @author     Matthias L. Jugel, Marcus Meißner
+ */
+
+public interface StatusPeer
+{
+       /**
+        * This method is called for the peer of the TelnetIO class if there is
+        * a statuschange.
+        * @param status A Vector containing the key as element 0 and any arguments
+        *               from element 1 on.
+        * @return an object that matches the requested information or null
+        * @see socket.TelnetIO
+        */
+  public Object notifyStatus(Vector status);
+}
diff --git a/src/de/mud/telnet/socket/TelnetIO.java b/src/de/mud/telnet/socket/TelnetIO.java
new file mode 100644 (file)
index 0000000..7ad09e8
--- /dev/null
@@ -0,0 +1,618 @@
+package de.mud.telnet.socket;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+/**
+ * socket.TelnetIO - a telnet implementation
+ * --
+ * $Id: TelnetIO.java,v 1.10 1998/02/09 10:22:18 leo Exp $
+ * $timestamp: Tue May 27 13:27:05 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.net.Socket;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.awt.Dimension;
+import java.util.Vector;
+
+/**
+ * Implements simple telnet io
+ *
+ * @version $Id: TelnetIO.java,v 1.10 1998/02/09 10:22:18 leo Exp $
+ * @author  Matthias L. Jugel, Marcus Meißner
+ * @version 1.2 3/7/97 George Ruban added available() because it was needed.
+ */
+public class TelnetIO implements StatusPeer
+{
+  /**
+   * Return the version of TelnetIO.
+   */
+  public String toString() { return "$Id: TelnetIO.java,v 1.10 1998/02/09 10:22:18 leo Exp $"; }
+  
+       /**
+        * Debug level. This results in additional diagnostic messages on the
+        * java console.
+        */
+       private static int debug = 0;
+
+       /**
+        * State variable for telnetnegotiation reader
+        */
+       private byte neg_state = 0;
+
+       /**
+        * constants for the negotiation state
+        */
+       private final static byte STATE_DATA    = 0;
+       private final static byte STATE_IAC     = 1;
+       private final static byte STATE_IACSB   = 2;
+       private final static byte STATE_IACWILL = 3;
+       private final static byte STATE_IACDO   = 4;
+       private final static byte STATE_IACWONT = 5;
+       private final static byte STATE_IACDONT = 6;
+       private final static byte STATE_IACSBIAC        = 7;
+       private final static byte STATE_IACSBDATA       = 8;
+       private final static byte STATE_IACSBDATAIAC    = 9;
+
+       /**
+        * What IAC SB <xx> we are handling right now
+        */
+       private byte current_sb;
+
+       /**
+        * IAC - init sequence for telnet negotiation.
+        */
+       private final static byte IAC  = (byte)255;
+       /**
+        * [IAC] End Of Record
+        */
+       private final static byte EOR  = (byte)239;
+       /**
+        * [IAC] WILL
+        */
+       private final static byte WILL  = (byte)251;
+       /**
+        * [IAC] WONT
+        */
+       private final static byte WONT  = (byte)252;
+       /**
+        * [IAC] DO
+        */
+       private final static byte DO    = (byte)253;
+       /**
+        * [IAC] DONT
+        */
+       private final static byte DONT  = (byte)254;
+       /**
+        * [IAC] Sub Begin 
+        */
+       private final static byte SB  = (byte)250;
+       /**
+        * [IAC] Sub End
+        */
+       private final static byte SE  = (byte)240;
+       /**
+        * Telnet option: echo text
+        */
+       private final static byte TELOPT_ECHO  = (byte)1;  /* echo on/off */
+       /**
+        * Telnet option: End Of Record
+        */
+       private final static byte TELOPT_EOR   = (byte)25;  /* end of record */
+       /**
+        * Telnet option: Negotiate About Window Size
+        */
+       private final static byte TELOPT_NAWS  = (byte)31;  /* NA-WindowSize*/
+       /**
+        * Telnet option: Terminal Type
+        */
+       private final static byte TELOPT_TTYPE  = (byte)24;  /* terminal type */
+
+       private final static byte[] IACWILL  = { IAC, WILL };
+       private final static byte[] IACWONT  = { IAC, WONT };
+       private final static byte[] IACDO    = { IAC, DO        };
+       private final static byte[] IACDONT  = { IAC, DONT };
+       private final static byte[] IACSB  = { IAC, SB };
+       private final static byte[] IACSE  = { IAC, SE };
+
+       /** 
+        * Telnet option qualifier 'IS' 
+        */
+       private final static byte TELQUAL_IS = (byte)0;
+
+       /** 
+        * Telnet option qualifier 'SEND' 
+        */
+       private final static byte TELQUAL_SEND = (byte)1;
+
+       /**
+        * What IAC DO(NT) request do we have received already ?
+        */
+        private byte[] receivedDX;
+  
+       /**
+        * What IAC WILL/WONT request do we have received already ?
+        */
+       private byte[] receivedWX;
+       /**
+        * What IAC DO/DONT request do we have sent already ?
+        */
+       private byte[] sentDX;
+       /**
+        * What IAC WILL/WONT request do we have sent already ?
+        */
+       private byte[] sentWX;
+
+       private Socket socket;
+       private BufferedInputStream is;
+       private BufferedOutputStream os;
+
+       private StatusPeer peer = this;         /* peer, notified on status */
+
+       /**
+        * Connect to the remote host at the specified port.
+        * @param address the symbolic host address
+        * @param port the numeric port
+        * @see #disconnect
+        */
+       public void connect(String address, int port) throws IOException {
+               if(debug > 0) System.out.println("Telnet.connect("+address+","+port+")");
+               socket = new Socket(address, port);
+               is = new BufferedInputStream(socket.getInputStream());
+               os = new BufferedOutputStream(socket.getOutputStream());
+               neg_state = 0;
+               receivedDX = new byte[256]; 
+               sentDX = new byte[256];
+               receivedWX = new byte[256]; 
+               sentWX = new byte[256];
+       }
+
+       /**
+        * Disconnect from remote host.
+        * @see #connect
+        */
+       public void disconnect() throws IOException {
+         if(debug > 0) System.out.println("TelnetIO.disconnect()");
+         if(socket !=null) socket.close();
+       }
+  
+       /**
+        * Connect to the remote host at the default telnet port (23).
+        * @param address the symbolic host address
+        */
+       public void connect(String address) throws IOException {
+               connect(address, 23);
+       }
+
+       /**
+        * Set the object to be notified about current status.
+        * @param obj object to be notified.
+        */
+       public void setPeer(StatusPeer obj) { peer = obj; }
+
+       /** Returns bytes available to be read.  Since they haven't been
+        * negotiated over, this could be misleading.
+        * Most useful as a boolean value - "are any bytes available" -
+        * rather than as an exact count of "how many ara available."
+        *
+        * @exception IOException on problems with the socket connection
+        */
+       public int available() throws IOException
+       {
+         return is.available();
+       }
+       
+
+       /**
+        * Read data from the remote host. Blocks until data is available. 
+        * Returns an array of bytes.
+        * @see #send
+        */
+       public byte[] receive() throws IOException {
+               int count = is.available();
+               byte buf[] = new byte[count];
+               count = is.read(buf);
+               if(count < 0) throw new IOException("Connection closed.");
+               if(debug > 1) System.out.println("TelnetIO.receive(): read bytes: "+count);
+               buf = negotiate(buf, count);
+               return buf;
+       }
+
+       /**
+        * Send data to the remote host.
+        * @param buf array of bytes to send
+        * @see #receive
+        */
+       public void send(byte[] buf) throws IOException {
+               if(debug > 1) System.out.println("TelnetIO.send("+buf+")");
+               os.write(buf);
+               os.flush();
+       }
+
+       public void send(byte b) throws IOException {
+               if(debug > 1) System.out.println("TelnetIO.send("+b+")");
+               os.write(b);
+               os.flush();
+       }
+
+       /**
+        * Handle an incoming IAC SB <type> <bytes> IAC SE
+        * @param type type of SB
+        * @param sbata byte array as <bytes>
+        * @param sbcount nr of bytes. may be 0 too.
+        */
+       private void handle_sb(byte type, byte[] sbdata, int sbcount) 
+               throws IOException 
+       {
+               if(debug > 1) 
+                       System.out.println("TelnetIO.handle_sb("+type+")");
+               switch (type) {
+               case TELOPT_TTYPE:
+                       if (sbcount>0 && sbdata[0]==TELQUAL_SEND) {
+                               String ttype;
+                               send(IACSB);send(TELOPT_TTYPE);send(TELQUAL_IS);
+                               /* FIXME: need more logic here if we use 
+                                * more than one terminal type
+                                */
+                               Vector vec = new Vector(2);
+                               vec.addElement("TTYPE");
+                               ttype = (String)peer.notifyStatus(vec);
+                               if(ttype == null) ttype = "dumb";
+                               byte[] bttype = new byte[ttype.length()];
+
+                               ttype.getBytes(0,ttype.length(), bttype, 0);
+                               send(bttype);
+                               send(IACSE);
+                       }
+
+               }
+       }
+
+       /**
+        * Notify about current telnet status. This method is called top-down.
+        * @param status contains status information
+        */
+       public Object notifyStatus(Vector status) {
+               if(debug > 0) 
+                 System.out.println("TelnetIO.notifyStatus("+status+")");
+               return null;
+       }
+
+       /* wo faengt buf an bei buf[0] oder bei buf[1] */
+       private byte[] negotiate(byte buf[], int count) throws IOException {
+               if(debug > 1) 
+                       System.out.println("TelnetIO.negotiate("+buf+","+count+")");
+               byte nbuf[] = new byte[count];
+               byte sbbuf[] = new byte[count];
+               byte sendbuf[] = new byte[3];
+               byte b,reply;
+               int  sbcount = 0;
+               int boffset = 0, noffset = 0;
+               Vector  vec = new Vector(2);
+
+               while(boffset < count) {
+                       b=buf[boffset++];
+                       /* of course, byte is a signed entity (-128 -> 127)
+                        * but apparently the SGI Netscape 3.0 doesn't seem
+                        * to care and provides happily values up to 255
+                        */
+                       if (b>=128)
+                               b=(byte)((int)b-256);
+                       switch (neg_state) {
+                       case STATE_DATA:
+                               if (b==IAC) {
+                                       neg_state = STATE_IAC;
+                               } else {
+                                       nbuf[noffset++]=b;
+                               }
+                               break;
+                       case STATE_IAC:
+                               switch (b) {
+                               case IAC:
+                                       if(debug > 2) 
+                                               System.out.print("IAC ");
+                                       neg_state = STATE_DATA;
+                                       nbuf[noffset++]=IAC;
+                                       break;
+                               case WILL:
+                                       if(debug > 2)
+                                               System.out.print("WILL ");
+                                       neg_state = STATE_IACWILL;
+                                       break;
+                               case WONT:
+                                       if(debug > 2)
+                                               System.out.print("WONT ");
+                                       neg_state = STATE_IACWONT;
+                                       break;
+                               case DONT:
+                                       if(debug > 2)
+                                               System.out.print("DONT ");
+                                       neg_state = STATE_IACDONT;
+                                       break;
+                               case DO:
+                                       if(debug > 2)
+                                               System.out.print("DO ");
+                                       neg_state = STATE_IACDO;
+                                       break;
+                               case EOR:
+                                       if(debug > 2)
+                                               System.out.print("EOR ");
+                                       neg_state = STATE_DATA;
+                                       break;
+                               case SB:
+                                       if(debug > 2)
+                                               System.out.print("SB ");
+                                       neg_state = STATE_IACSB;
+                                       sbcount = 0;
+                                       break;
+                               default:
+                                       if(debug > 2)
+                                               System.out.print(
+                                                       "<UNKNOWN "+b+" > "
+                                               );
+                                       neg_state = STATE_DATA;
+                                       break;
+                               }
+                               break;
+                       case STATE_IACWILL:
+                               switch(b) {
+                               case TELOPT_ECHO:
+                                       if(debug > 2) 
+                                               System.out.println("ECHO");
+                                       reply = DO;
+                                       vec = new Vector(2);
+                                       vec.addElement("NOLOCALECHO");
+                                       peer.notifyStatus(vec);
+                                       break;
+                               case TELOPT_EOR:
+                                       if(debug > 2) 
+                                               System.out.println("EOR");
+                                       reply = DO;
+                                       break;
+                               default:
+                                       if(debug > 2)
+                                               System.out.println(
+                                                       "<UNKNOWN,"+b+">"
+                                               );
+                                       reply = DONT;
+                                       break;
+                               }
+                               if(debug > 1)
+                                 System.out.println("<"+b+", WILL ="+WILL+">");
+                               if (    reply != sentDX[b+128] ||
+                                       WILL != receivedWX[b+128]
+                               ) {
+                                       sendbuf[0]=IAC;
+                                       sendbuf[1]=reply;
+                                       sendbuf[2]=b;
+                                       send(sendbuf);
+                                       sentDX[b+128] = reply;
+                                       receivedWX[b+128] = WILL;
+                               }
+                               neg_state = STATE_DATA;
+                               break;
+                       case STATE_IACWONT:
+                               switch(b) {
+                               case TELOPT_ECHO:
+                                       if(debug > 2) 
+                                               System.out.println("ECHO");
+
+                                       vec = new Vector(2);
+                                       vec.addElement("LOCALECHO");
+                                       peer.notifyStatus(vec);
+                                       reply = DONT;
+                                       break;
+                               case TELOPT_EOR:
+                                       if(debug > 2) 
+                                               System.out.println("EOR");
+                                       reply = DONT;
+                                       break;
+                               default:
+                                       if(debug > 2) 
+                                               System.out.println(
+                                                       "<UNKNOWN,"+b+">"
+                                               );
+                                       reply = DONT;
+                                       break;
+                               }
+                               if (    reply != sentDX[b+128] ||
+                                       WONT != receivedWX[b+128]
+                               ) {
+                                       sendbuf[0]=IAC;
+                                       sendbuf[1]=reply;
+                                       sendbuf[2]=b;
+                                       send(sendbuf);
+                                       sentDX[b+128] = reply;
+                                       receivedWX[b+128] = WILL;
+                               }
+                               neg_state = STATE_DATA;
+                               break;
+                       case STATE_IACDO:
+                               switch (b) {
+                               case TELOPT_ECHO:
+                                       if(debug > 2) 
+                                               System.out.println("ECHO");
+                                       reply = WILL;
+                                       vec = new Vector(2);
+                                       vec.addElement("LOCALECHO");
+                                       peer.notifyStatus(vec);
+                                       break;
+                               case TELOPT_TTYPE:
+                                       if(debug > 2) 
+                                               System.out.println("TTYPE");
+                                       reply = WILL;
+                                       break;
+                               case TELOPT_NAWS:
+                                       if(debug > 2) 
+                                               System.out.println("NAWS");
+                                       vec = new Vector(2);
+                                       vec.addElement("NAWS");
+                                       Dimension size = (Dimension)
+                                               peer.notifyStatus(vec);
+                                       receivedDX[b] = DO;
+                                       if(size == null)
+                                       {
+                                               /* this shouldn't happen */
+                                               send(IAC);
+                                               send(WONT);
+                                               send(TELOPT_NAWS);
+                                               reply = WONT;
+                                               sentWX[b] = WONT;
+                                               break;
+                                       }
+                                       reply = WILL;
+                                       sentWX[b] = WILL;
+                                       sendbuf[0]=IAC;
+                                       sendbuf[1]=WILL;
+                                       sendbuf[2]=TELOPT_NAWS;
+                                       send(sendbuf);
+                                       send(IAC);send(SB);send(TELOPT_NAWS);
+                                       send((byte) (size.width >> 8));
+                                       send((byte) (size.width & 0xff));
+                                       send((byte) (size.height >> 8));
+                                       send((byte) (size.height & 0xff));
+                                       send(IAC);send(SE);
+                                       break;
+                               default:
+                                       if(debug > 2) 
+                                               System.out.println(
+                                                       "<UNKNOWN,"+b+">"
+                                               );
+                                       reply = WONT;
+                                       break;
+                               }
+                               if (    reply != sentWX[128+b] ||
+                                       DO != receivedDX[128+b]
+                               ) {
+                                       sendbuf[0]=IAC;
+                                       sendbuf[1]=reply;
+                                       sendbuf[2]=b;
+                                       send(sendbuf);
+                                       sentWX[b+128] = reply;
+                                       receivedDX[b+128] = DO;
+                               }
+                               neg_state = STATE_DATA;
+                               break;
+                       case STATE_IACDONT:
+                               switch (b) {
+                               case TELOPT_ECHO:
+                                       if(debug > 2) 
+                                               System.out.println("ECHO");
+                                       reply   = WONT;
+                                       vec = new Vector(2);
+                                       vec.addElement("NOLOCALECHO");
+                                       peer.notifyStatus(vec);
+                                       break;
+                               case TELOPT_NAWS:
+                                       if(debug > 2) 
+                                               System.out.println("NAWS");
+                                       reply   = WONT;
+                                       break;
+                               default:
+                                       if(debug > 2) 
+                                               System.out.println(
+                                                       "<UNKNOWN,"+b+">"
+                                               );
+                                       reply   = WONT;
+                                       break;
+                               }
+                               if (    reply   != sentWX[b+128] ||
+                                       DONT    != receivedDX[b+128]
+                               ) {
+                                       send(IAC);send(reply);send(b);
+                                       sentWX[b+128]           = reply;
+                                       receivedDX[b+128]       = DONT;
+                               }
+                               neg_state = STATE_DATA;
+                               break;
+                       case STATE_IACSBIAC:
+                               if(debug > 2) System.out.println(""+b+" ");
+                               if (b == IAC) {
+                                       sbcount         = 0;
+                                       current_sb      = b;
+                                       neg_state       = STATE_IACSBDATA;
+                               } else {
+                                       System.out.println("(bad) "+b+" ");
+                                       neg_state       = STATE_DATA;
+                               }
+                               break;
+                       case STATE_IACSB:
+                               if(debug > 2) System.out.println(""+b+" ");
+                               switch (b) {
+                               case IAC:
+                                       neg_state = STATE_IACSBIAC;
+                                       break;
+                               default:
+                                       current_sb      = b;
+                                       sbcount         = 0;
+                                       neg_state       = STATE_IACSBDATA;
+                                       break;
+                               }
+                               break;
+                       case STATE_IACSBDATA:
+                               if (debug > 2) System.out.println(""+b+" ");
+                               switch (b) {
+                               case IAC:
+                                       neg_state = STATE_IACSBDATAIAC;
+                                       break;
+                               default:
+                                       sbbuf[sbcount++] = b;
+                                       break;
+                               }
+                               break;
+                       case STATE_IACSBDATAIAC:
+                               if (debug > 2) System.out.println(""+b+" ");
+                               switch (b) {
+                               case IAC:
+                                       neg_state = STATE_IACSBDATA;
+                                       sbbuf[sbcount++] = IAC;
+                                       break;
+                               case SE:
+                                       handle_sb(current_sb,sbbuf,sbcount);
+                                       current_sb      = 0;
+                                       neg_state       = STATE_DATA;
+                                       break;
+                               case SB:
+                                       handle_sb(current_sb,sbbuf,sbcount);
+                                       neg_state       = STATE_IACSB;
+                                       break;
+                               default:
+                                       neg_state       = STATE_DATA;
+                                       break;
+                               }
+                               break;
+                       default:
+                               if (debug > 2) 
+                                       System.out.println(
+                                               "This should not happen: "+
+                                               neg_state+" "
+                                       );
+                               neg_state = STATE_DATA;
+                               break;
+                       }
+               }
+               buf     = new byte[noffset];
+               System.arraycopy(nbuf, 0, buf, 0, noffset);
+               return buf;
+       }
+}
diff --git a/src/de/mud/telnet/socket/TelnetWrapper.java b/src/de/mud/telnet/socket/TelnetWrapper.java
new file mode 100644 (file)
index 0000000..adec69c
--- /dev/null
@@ -0,0 +1,400 @@
+package de.mud.telnet.socket;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+import java.io.IOException;
+import java.util.Date;
+
+/** Wrapper for a Java Telnet call. 
+ * To use, make a new TelnetWrapper() with the name or IP address of a host.
+ * Then, for most uses, the easiest way is to call setPrompt() with the
+ * expected prompt, then call login(), and a sequence of sendLine()'s
+ * until you get what you want done.
+ * <P>
+ * If you don't know the prompt ahead of time, you have to do a sequence of
+ * send() and wait() or receiveUntil() calls.  send() sends a string across
+ * the telnet connection. Add a '\r' to the end if you want to
+ * complete a command. wait() waits for an exact string from the other side
+ * of the telnet connection, and returns nothing,
+ * receiveUntil() also waits for a string, but returns all the data
+ * that it received while waiting, including the string itself. 
+ * Use this if you want the output from a command. Please note that
+ * the telnet connection will usually echo the sent command. 
+ * <P>
+ * sendLine() is generally better, since it adds the '\r'
+ * automatically, waits for the prompt before returning, and returns all
+ * data received before the prompt, with the prompt itself cut off the
+ * end, and the sent command cut off the beginning. login() and
+ * sendLine() are implemented using send(), wait() and receiveUntil().
+ * They can be freely mixed and matched.
+ * <P>
+ * Here is a simple example of the use of TelnetWrapper:
+ * <PRE>
+ * // creates a new file in /tmp, lists the directory to prove it done
+ * {
+ *   TelnetWrapper telnet = new TelnetWrapper("123.45.78.90");
+ *
+ *   // setting the correct prompt ahead of time is very important 
+ *   // if you want to use login and sendLine
+ *   telnet.setPrompt("$ ");
+ *   telnet.login("loginname", "password");
+ *
+ *   // this is how you have to do it otherwise
+ *   telnet.send("touch /tmp/TELNET_WRAPPER" + "\r");
+ *   telnet.wait("$ ");
+ *
+ *   // sendLine 1: adds the \r automatically, 2: waits for the prompt
+ *   // before returning 3: returns what was printed from the command
+ *   String ls = telnet.sendLine("ls /tmp");
+ *   System.out.println(ls);
+ *
+ *   // clean up
+ *   telnet.disconnect();
+ * } 
+ * </PRE>
+ * @author George Ruban 3/4/97
+ * @version 0.2 5/15/97 - added comments, replaced String += with
+ *    StringBuffer.append() in receiveUntil(), added port constructor
+ * @version 0.3 7/30/97 - added optional timeout to receiveUntil() and wait()
+ * @see TelnetIO
+ */
+public class TelnetWrapper
+{
+  /** The telnet connection. That which is wrapped. */
+  TelnetIO tio;
+  /** Set to true for System.out.println debugging. */
+  public boolean debug = false;
+  /** The current prompt on the remote system. */
+  private String prompt;
+
+  /** The default prompt used by all TelnetWrappers unless specifically
+   * overridden.
+   * @see #setPrompt
+   */
+  private static String defaultPrompt = "$ ";
+
+  /** The default login name used by TelnetWrappers.
+   * If defaultLogin and defaultPassword are both non-null
+   * when a TelnetWrapper is created, the TelnetWrapper will attempt
+   * to login.
+   */
+  private static String defaultLogin = null;
+
+  /** The default password used by TelnetWrappers.
+   * If defaultLogin and defaultPassword are both non-null
+   * when a TelnetWrapper is created, the TelnetWrapper will attempt
+   * to login.
+   */
+  private static String defaultPassword = null;
+  
+  /** Skip any received data until the token appears. 
+   * More efficient than receiveUntil, but liable to fail on large
+   * tokens that can be spread over several "send"s. In that case,
+   * consider using receiveUntil and ignoring the return value.
+   * @param token String to wait for
+   * @exception IOException on problems with the socket connection
+   * @see #receiveUntil
+   */
+  public void wait(String token) throws IOException
+  {
+    wait(token, -1);
+  }
+
+  /** Wait for a String or a timeout. 
+   * If time runs out, throws a TimedOutException.
+   * Sleeps in intervals of 100 milliseconds until either receiving the
+   * token or timeout.
+   * <P>
+   * More efficient than receiveUntil, but liable to fail on large
+   * tokens that can be spread over several "send"s. In that case,
+   * consider using receiveUntil and ignoring the return value.
+   * @param token String to wait for
+   * @param timeout time in milliseconds to wait (negative means wait forever)
+   * @exception IOException on problems with the socket connection
+   * @exception TimedOutException if time runs out before token received
+   * @see #receiveUntil(String, long)
+   */
+  public void wait(String token, long timeout) 
+    throws IOException, TimedOutException
+  {
+    if(debug) System.out.println("wait(" + token + ", " + timeout + ")...");
+    String tmp = "";
+    long deadline = 0;
+    if(timeout >= 0) 
+      deadline = new Date().getTime() + timeout;
+    
+    do {
+      if(timeout >= 0)
+      {
+       while(available() <= 0)
+       {
+         if(new Date().getTime() > deadline) 
+           throw new TimedOutException();
+         try{
+           Thread.currentThread().sleep(100);
+         }
+         catch(InterruptedException ignored)
+         {}
+       }
+      }
+      tmp = receive();
+    } while(tmp.indexOf(token) == -1);
+    if(debug) System.out.println("wait(" + token  + ", " + timeout + 
+                                ") successful.");
+  }
+
+  /** Returns bytes available to be read.  Since they haven't been
+   * negotiated over, this could be misleading...
+   */
+  public int available() throws IOException
+  {
+    return tio.available();
+  }
+       
+  /** Returns a String from the telnet connection. Blocks
+   * until one is available. No guarantees that the string is in
+   * any way complete.
+   * NOTE: uses Java 1.0.2 style String-bytes conversion.*/
+  public String receive() throws IOException
+  {
+    String s = new String(receiveBytes(), 0);
+    if(debug) System.out.println(s);
+    return s;
+  }
+
+  /** Returns a byte array. Blocks until data is available. */
+  public byte[] receiveBytes() throws IOException
+  {
+    return tio.receive();
+  }
+
+  /** Returns all data received up until a certain token. 
+   * @param token String to wait for
+   * @exception IOException on problems with the socket connection
+   * @see #wait
+   */
+  public String receiveUntil(String token) throws IOException
+  {
+    return receiveUntil(token, -1);
+  }
+  
+
+  /** Returns all data received up until a certain token. 
+   * @param token String to wait for
+   * @param timeout time in milliseconds to wait (negative means wait forever)
+   * @exception IOException on problems with the socket connection
+   * @exception TimedOutException if time runs out before token received
+   * @see #wait(String, long)
+   */
+  public String receiveUntil(String token, long timeout) 
+    throws IOException, TimedOutException
+  {
+    StringBuffer buf = new StringBuffer();
+    long deadline = 0;
+    if(timeout >= 0) 
+      deadline = new Date().getTime() + timeout;
+    do
+    {
+      if(timeout >= 0)
+      {
+       while(available() <= 0)
+       {
+         if(new Date().getTime() > deadline) 
+           throw new TimedOutException();
+         try{
+           Thread.currentThread().sleep(100);
+         }
+         catch(InterruptedException ignored)
+         {}
+       }
+      }
+      buf.append(receive());
+    } while(buf.toString().indexOf(token) == -1);
+    return buf.toString();
+  }
+  
+  /** Sends a String to the remote host.
+   * NOTE: uses Java 1.0.2 style String-bytes conversion.
+   * @exception IOException on problems with the socket connection
+   */
+  public void send(String s) throws IOException
+  {
+    if(debug) System.out.println(s);
+    byte[] buf = new byte[s.length()];
+    s.getBytes(0, buf.length, buf, 0);
+    tio.send(buf);
+  }
+
+  /** Sends a line to the remote host, returns all data before the prompt.
+   * Since telnet seems to rely on carriage returns ('\r'), 
+   * one will be appended to the sent string, if necessary.
+   * @param command command line to send
+   * @return whatever data the command produced before the prompt.
+   * @see #setPrompt
+   */
+  public String sendLine(String command) throws IOException
+  {
+    if(command.charAt(command.length() -1) != '\r') 
+      command += "\r";
+    send(command);
+    String s = receiveUntil(prompt);
+
+    // telnet typically echoes the command with a \r\n ending...
+    return s.substring(command.length() + 1, s.indexOf(prompt));
+  }
+  
+  /** Sends bytes over the telnet connection. */
+  public void send(byte[] buf) throws IOException
+  {
+    tio.send(buf);
+  }
+  
+  /** Logs in as a particular user and password. 
+    * Returns after receiving prompt. */
+  public void login(String loginName, String password) throws IOException
+  {
+    wait("login:");
+    send(loginName + "\r");
+    wait("Password:");
+    sendLine(password + "\r");
+  }
+    
+  /** Connects to the default telnet port on the given host. 
+   * If the defaultLogin and defaultPassword are non-null, attempts login. */
+  public TelnetWrapper(String host) throws IOException
+  {
+    tio = new TelnetIO();
+    setPrompt(defaultPrompt);
+    tio.connect(host);
+    if(defaultLogin != null && defaultPassword != null)
+    {
+      login(defaultLogin, defaultPassword);
+    }
+  }
+
+  /** Connects to a specific telnet port on the given host. 
+   * If the defaultLogin and defaultPassword are non-null, attempts login. */
+  public TelnetWrapper(String host, int port) throws IOException
+  {
+    tio = new TelnetIO();
+    setPrompt(defaultPrompt);
+    tio.connect(host, port);
+    if(defaultLogin != null && defaultPassword != null)
+    {
+      login(defaultLogin, defaultPassword);
+    }
+  }
+  
+  /** Sets the expected prompt. 
+   * If this function is not explicitly called, the default prompt is used.
+   * @see #setDefaultPrompt
+   */
+  public void setPrompt(String prompt)
+  {
+    if(prompt == null) throw new IllegalArgumentException("null prompt.");
+    this.prompt = prompt;
+  }
+
+  /** Sets the default prompt used by all TelnetWrappers.
+   * This can be specifically overridden for a specific instance.
+   * The default prompt starts out as "$ " until this function is called.
+   * @see #setPrompt
+   */
+  public static void setDefaultPrompt(String prompt)
+  {
+    if(prompt == null) throw new IllegalArgumentException("null prompt.");
+    defaultPrompt = prompt;
+  }
+
+  /** Sets the default login used by TelnetWrappers.
+   * If this method is called with non-null login and password,
+   * all TelnetWrappers will attempt to login when first created.
+   * @param login login name to use
+   * @param password password to use
+   * @see #login
+   * @see #unsetLogin
+   */
+  public static void setLogin(String login, String password)
+  {
+    if(login == null || password == null)
+      throw new IllegalArgumentException("null login or password.");
+    defaultLogin = login;
+    defaultPassword = password;
+  }
+
+
+  /** Turns off the default login of TelnetWrappers.
+   * After this method is called, TelnetWrappers will not
+   * login until that method is explicitly called.
+   * @see #setLogin
+   * @see #login
+   */
+  public static void unsetLogin()
+  {
+    defaultLogin = defaultPassword = null;
+  }
+  
+  /** Ends the telnet connection. */
+  public void disconnect() throws IOException
+  {
+    if(tio != null) tio.disconnect();
+    tio = null;
+  }
+  
+  /** Ends the telnet connection. */
+  public void finalize()
+  {
+    try
+    {
+      disconnect();
+    }
+    catch(IOException e)
+    {} // after all, what can be done at this point?
+  }  
+
+  /** Telnet test driver.
+   * Modeled after the IOtest.java example in the Telnet Applet.
+   * Logs in to "host", creates a timestamped file in /tmp, lists the
+   * /tmp directory to System.out, disconnects.  Shows off several
+   * TelnetWrapper methods.
+   * @param args host login password prompt
+   */
+  public static void main(String args[]) throws IOException
+  {
+    if(args.length != 4) throw new 
+      IllegalArgumentException("Usage: TelnetWrapper host login password prompt");
+    
+    String host = args[0];
+    String login = args[1];
+    String password = args[2];
+    String prompt = args[3];
+
+    Date now = new Date();
+    String timestamp = now.getYear() + "-" +
+               (now.getMonth()+1) + "-" + now.getDate() + "-" +
+                 now.getHours() + ":" + now.getMinutes() + ":" +
+                   now.getSeconds();
+    TelnetWrapper telnet = new TelnetWrapper(host);
+    telnet.debug = true;
+
+    // setting the correct prompt ahead of time is very important 
+    // if you want to use login and sendLine
+    telnet.setPrompt(prompt);
+    telnet.login(login, password);
+
+    // this is how you have to do it otherwise
+    telnet.send("touch /tmp/TELNET_WRAPPER-" + timestamp + "\r");
+    telnet.wait(prompt);
+
+    // sendLine 1: adds the \r automatically, 2: waits for the prompt
+    // before returning 3: returns what was printed from the command
+    String ls = telnet.sendLine("ls /tmp");
+    System.out.println(ls);
+
+    // clean up
+    telnet.disconnect();
+  }
+}
+
diff --git a/src/de/mud/telnet/socket/TimedOutException.java b/src/de/mud/telnet/socket/TimedOutException.java
new file mode 100644 (file)
index 0000000..dc12333
--- /dev/null
@@ -0,0 +1,23 @@
+package de.mud.telnet.socket;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+import java.io.IOException;
+
+/** Exception thrown when a Telnet connection takes too long
+ * before receiving a specified String token.
+ * @author George Ruban
+ * @version 0.1 7/30/97 */
+public class TimedOutException extends IOException
+{
+  public TimedOutException()
+  {
+  }
+
+  public TimedOutException(String message)
+  {
+    super(message);
+  }
+}
+
diff --git a/src/de/mud/telnet/telnet.java b/src/de/mud/telnet/telnet.java
new file mode 100644 (file)
index 0000000..0d86c4e
--- /dev/null
@@ -0,0 +1,562 @@
+package de.mud.telnet;
+/* "In case you would like to use the packages as libraries please
+ *  apply the GNU Library General Public License as documented in the
+ *  file COPYING.LIB." (from Telnet/Documentation/index.html)
+ */
+
+/**
+ * telnet -- implements a simple telnet
+ * --
+ * $Id: telnet.java,v 1.19 1998/02/09 10:22:15 leo Exp $
+ * $timestamp: Mon Aug  4 13:11:14 1997 by Matthias L. Jugel :$
+ *
+ * This file is part of "The Java Telnet Applet".
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * "The Java Telnet Applet" 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 Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+import java.applet.Applet;
+import java.awt.Frame;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Panel;
+import java.awt.Event;
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.io.IOException;
+
+import de.mud.telnet.socket.*;
+import de.mud.telnet.display.*;
+import de.mud.telnet.modules.*;
+
+/**
+ * A telnet implementation that supports different terminal emulations.
+ * @version $Id: telnet.java,v 1.19 1998/02/09 10:22:15 leo Exp $
+ * @author  Matthias L. Jugel, Marcus Meißner
+ */
+public class telnet extends Applet implements Runnable, TerminalHost, StatusPeer
+{
+  /**
+   * The telnet io methods.
+   * @see socket.TelnetIO
+   */
+  protected TelnetIO tio;
+
+  /**
+   * The terminal emulation (dynamically loaded).
+   * @see emulation
+   * @see display.Terminal
+   * @see display.TerminalHost
+   */
+  protected Terminal term;
+
+  /**
+   * The host address to connect to. This is retrieved from the PARAM tag
+   * "address".
+   */
+  protected String address;
+
+  /**
+   * The port number (default ist 23). This can be specified as the PARAM tag
+   * "port".
+   */
+  protected int port = 23;
+
+  /**
+   * The proxy ip address. If this variable is set telnet will try to connect
+   * to this address and then send a string to tell the relay where the
+   * target host is.
+   * @see address
+   */
+  protected String proxy = null;
+  /**
+   * The proxy port number. This is the port where the relay is expected to
+   * listen for incoming connections.
+   * @see proxy
+   * @see port
+   */
+  protected int proxyport;
+  
+  /**
+   * Emulation type (default is vt320). This can be specified as the PARAM
+   * tag "emulation".
+   * @see term
+   * @see display.Terminal
+   * @see display.TerminalHost
+   */
+  protected String emulation = "vt320";
+
+  /**
+   * Dynamically loaded modules are stored here.
+   */
+  protected Vector modules = null;
+
+  // some state variables;
+  private boolean localecho = true;
+  private boolean connected = false;
+
+  private Thread t;
+  private Container parent;
+
+  /**
+   * This Hashtable contains information retrievable by getParameter() in case
+   * the program is run as an application and the AppletStub is missing.
+   */
+  public Hashtable params;
+
+  /** 
+   * Retrieve the current version of the applet.
+   * @return String a string with the version information.
+   */
+  public String getAppletInfo()
+  {
+    String info = "The Java(tm) Telnet Applet\n$Id: telnet.java,v 1.19 1998/02/09 10:22:15 leo Exp $\n";
+    info += "Terminal emulation: "+term.getTerminalType()+
+      " ["+term.toString()+"]\n";
+    info += "Terminal IO version: "+tio.toString()+"\n";
+    if(modules != null && modules.size() > 0) {
+      info += "Resident modules loaded: ("+modules.size()+")";
+      for(int i = 0; i < modules.size(); i++)
+        info += "   + "+(modules.elementAt(i)).toString()+"\n";
+    }
+    
+    return info;
+  }
+    
+  /**
+   * Retrieve parameter tag information. This includes the tag information from
+   * terminal and loaded modules.
+   * @return String an array of array of string with tag information
+   * @see java.applet.Applet#getParameterInfo
+   */
+  public String[][] getParameterInfo()
+  {
+    String pinfo[][];
+    String info[][] = {
+      {"address",  "String",   "IP address"},
+      {"port",     "Integer",  "Port number"},
+      {"proxy",    "String",   "IP address of relay"},
+      {"proxyport","Integer",  "Port number of relay"},
+      {"emulation","String",   "Emulation to be used (standard is vt320)"},
+    };
+    String tinfo[][] = (term != null ? term.getParameterInfo() : null);
+    if(tinfo != null) pinfo = new String[tinfo.length + 3][3];
+    else pinfo = new String[3][3];
+    System.arraycopy(info, 0, pinfo, 0, 3);
+    System.arraycopy(tinfo, 0, pinfo, 3, tinfo.length);
+    return pinfo;
+  }
+
+  /**
+   * We override the Applet method getParameter() to be able to handle 
+   * parameters even as application.
+   * @param name The name of the queried parameter.
+   * @return the value of the parameter
+   * @see java.applet.Applet#getParameter
+   */ 
+  public String getParameter(String name)
+  {
+    if(params == null) return super.getParameter(name);
+    return (String)params.get(name);
+  }
+
+  /**
+   * The main function is called on startup of the application.
+   */
+  public static void main(String args[]) 
+  {
+    // an application has to create a new instance of itself.
+    telnet applet = new telnet();
+
+    // create params from command line arguments
+    applet.params = new Hashtable();
+    switch(args.length) 
+    {
+    case 2: applet.params.put("port", args[1]);
+    case 1: applet.params.put("address", args[0]); 
+      break;
+    default: 
+      System.out.println("Usage: java telnet host [port]");
+      System.exit(0);
+    } 
+    applet.params.put("VTscrollbar", "true");
+    applet.params.put("module#1", "ButtonBar");
+    applet.params.put("1#Button", "Exit|\\$exit()");
+    applet.params.put("2#Button", "Connect|\\$connect(\\@address@,\\@port@)");
+    applet.params.put("3#Input", "address#30|"
+          +(args.length > 0 ? args[0] : "localhost"));
+    applet.params.put("4#Input", "port#4|23");
+    applet.params.put("5#Button", "Disconnect|\\$disconnect()");
+
+    // we put the applet in its own frame
+    Frame frame = new Frame("The Java Telnet Application ["+args[0]+"]");
+    frame.setLayout(new BorderLayout());
+    frame.add("Center", applet);
+    frame.resize(380, 590);
+
+    applet.init();
+
+    frame.pack();
+    frame.show();
+
+    applet.start();
+  }
+  
+  /**
+   * Initialize applet. This method reads the PARAM tags "address",
+   * "port" and "emulation". The emulation class is loaded dynamically.
+   * It also loads modules given as parameter "module#<nr>".
+   */
+  public void init()
+  {
+    String tmp; 
+
+    // save the current parent for future use
+    parent = getParent();
+    
+    // get the address we want to connect to
+    address = getParameter("address");
+    
+    if((tmp = getParameter("port")) == null) 
+      port = 23;
+    else
+      port = Integer.parseInt(tmp);
+
+    if((proxy = getParameter("proxy")) != null)
+      if((tmp = getParameter("proxyport")) == null)
+        proxyport = 31415;
+      else
+        proxyport = Integer.parseInt(tmp);
+    
+    if((emulation = getParameter("emulation")) == null)
+      emulation = "vt320";
+
+    // load the terminal emulation
+    try {
+      term = (Terminal)Class.forName("display."+emulation).newInstance();
+      System.out.println("telnet: load terminal emulation: "+emulation);
+    } catch(Exception e) {
+      System.err.println("telnet: cannot load terminal emulation "+emulation);
+      e.printStackTrace();
+    }
+    setLayout(new BorderLayout());
+    
+    // load modules, position is determined by the @<position> modifier
+    modules = new Vector();
+    int nr = 1;
+    while((tmp = getParameter("module#"+nr++)) != null) try {
+      Panel north = null, south = null, west = null, east = null;
+      String position = "North", initFile = null;
+
+      // try to get the initialization file name
+      if(tmp.indexOf(',') != -1) {
+        initFile = tmp.substring(tmp.indexOf(','+1));
+        tmp = tmp.substring(0, tmp.indexOf(','));
+        initFile = tmp.substring(tmp.indexOf(','+1));
+      }
+           
+      // find the desired location
+      if(tmp.indexOf('@') != -1) {
+        position = tmp.substring(tmp.indexOf('@')+1);
+        tmp = tmp.substring(0, tmp.indexOf('@'));
+      } 
+      Object obj = (Object)Class.forName("modules."+tmp).newInstance();
+
+      // probe for module (implementing modules.Module)
+      try {
+        ((Module)obj).setLoader(this);
+        modules.addElement((Module)obj);
+        System.out.println("telnet: module "+tmp+" detected");
+      } catch(ClassCastException e) {
+        System.out.println("telnet: warning: "+tmp+" may not be a "+
+                           "valid module");
+      }
+
+      // probe for visible component (java.awt.Component and descendants)
+      try {
+       Component component = (Component)obj;
+       if(position.equals("North")) {
+          if(north == null) { north = new Panel(); add("North", north); }
+          north.add(component);
+        } else if(position.equals("South")) {
+          if(south == null) { south = new Panel(); add("South", south); }
+          south.add(component);
+        } else if(position.equals("East")) {
+          if(east == null) { east = new Panel(); add("East", east); }
+          east.add(component);
+        } else if(position.equals("West")) {
+          if(west == null) { west = new Panel(); add("West", west); } 
+          west.add(component);
+        }
+        System.err.println("telnet: module "+tmp+" is a visible component");
+      } catch(ClassCastException e) {}
+
+    } catch(Exception e) {
+      System.err.println("telnet: cannot load module "+tmp);
+      e.printStackTrace();
+    }
+    if(modules.isEmpty()) modules = null;
+    add("Center", term);
+  }
+
+  /**
+   * Upon start of the applet try to create a new connection.
+   */
+  public void start()
+  {
+    if(!connect(address, port) && params == null)
+      showStatus("telnet: connection to "+address+" "+port+" failed");
+  }
+
+  /**
+   * Disconnect when the applet is stopped.
+   */
+  public final void stop()
+  {
+    disconnect();
+  }
+
+  /**
+   * Try to read data from the sockets and put it on the terminal.
+   * This is done until the thread dies or an error occurs.
+   */
+  public void run()
+  {
+    while(t != null)
+      try {
+        String tmp = new String(tio.receive(), 0);
+
+        // cycle through the list of modules
+        if(modules != null) {
+          Enumeration modlist = modules.elements();
+          while(modlist.hasMoreElements()) {
+            Module m = (Module)modlist.nextElement();
+            String modified = m.receive(tmp);
+            // call the receive() method and if it returns null
+            // remove the module from the list
+            if(modified == null) modules.removeElement(m);
+            else tmp = modified;
+          }
+        }
+        // put the modified string to the terminal
+        term.putString(tmp);
+    } catch(IOException e) {
+      disconnect();
+    }
+  }
+
+  /**
+   * Connect to the specified host and port but don't break existing 
+   * connections. Connects to the host and port specified in the tags. 
+   * @return false if connection was unsuccessful
+   */
+  public boolean connect()
+  {
+    return connect(address, port);
+  }
+
+  /**
+   * Connect to the specified host and port but don't break existing 
+   * connections. Uses the port specified in the tags or 23.
+   * @param host destination host address
+   */
+  public boolean connect(String host)
+  {
+    return connect(host, port);
+  }
+  
+  /**
+   * Connect to the specified host and port but don't break existing 
+   * connections.
+   * @param host destination host address
+   * @param prt destination hosts port
+   */
+  public boolean connect(String host, int prt)
+  {
+    address = host; port = prt;
+
+    if(address == null || address.length() == 0) return false;
+    
+    // There should be no thread when we try to connect
+    if(t != null && connected) {
+      System.err.println("telnet: connect: existing connection preserved");
+      return false;
+    } else t = null;
+    
+    try {
+      // In any case try to disconnect if tio is still active
+      // if there was no tio create a new one.
+      if(tio != null) try { tio.disconnect(); } catch(IOException e) {}
+      else (tio = new TelnetIO()).setPeer(this);
+
+      term.putString("Trying "+address+(port==23?"":" "+port)+" ...\n\r");
+      try {
+        // connect to to our destination at the given port
+        if(proxy != null) {
+          tio.connect(proxy, proxyport);
+          String str = "relay "+address+" "+port+"\n";
+          byte[] bytes = new byte[str.length()];
+          str.getBytes(0, str.length(), bytes, 0);
+          tio.send(bytes);
+        } else 
+          tio.connect(address, port);
+        term.putString("Connected to "+address+".\n\r");
+        // initial conditions are connected and localecho
+        connected = true;
+        localecho = true;
+
+        // cycle through the list of modules and notify connection
+        if(modules != null) {
+          Enumeration modlist = modules.elements();
+          while(modlist.hasMoreElements())
+            // call the connect() method
+            ((Module)modlist.nextElement()).connect(address, port);
+        }
+      } catch(IOException e) {
+        term.putString("Failed to connect.\n\r");
+        // to be sure, we remove the TelnetIO instance
+        tio = null;
+        System.err.println("telnet: failed to connect to "+address+" "+port);
+        e.printStackTrace();
+        return false;
+      }
+      // if our connection was successful, create a new thread and start it
+      t = new Thread(this);
+      t.setPriority(Thread.MIN_PRIORITY);
+      t.start();
+    } catch(Exception e) {
+      // hmm, what happened?
+      System.err.println("telnet: an error occured:");
+      e.printStackTrace();
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Disconnect from the remote host.
+   * @return false if there was a problem disconnecting.
+   */
+  public boolean disconnect()
+  {
+    if(tio == null) {
+      System.err.println("telnet: no connection");
+      return false;
+    }
+    try {
+      connected = false; t = null;
+      // cycle through the list of modules and notify connection
+      if(modules != null) {
+        Enumeration modlist = modules.elements();
+        while(modlist.hasMoreElements())
+          // call the disconnect() method
+          ((Module)modlist.nextElement()).disconnect();
+      }
+      term.putString("\n\rConnection closed.\n\r");
+      tio.disconnect();
+    } catch(Exception e) {
+      System.err.println("telnet: disconnection problem");
+      e.printStackTrace();
+      tio = null; t = null;
+      return false;
+    }
+    return true;
+  }
+  
+  /**
+   * Send a String to the remote host. Implements display.TerminalHost
+   * @param s String to be sent
+   * @return true if we are connected
+   * @see display.TerminalHost
+   */
+  public boolean send(String str)
+  {
+    if(connected) try {
+      byte[] bytes = new byte[str.length()];
+      str.getBytes(0, str.length(), bytes, 0);
+      tio.send(bytes);
+      if(localecho) {
+        if ((str.length()==2) && (str.charAt(0)=='\r') && (str.charAt(1)==0))
+          term.putString("\r\n");
+        else
+          term.putString(str);
+      }
+    } catch(Exception e) {
+      System.err.println("telnet.send(): disconnected");
+      disconnect();
+      return false;
+    }
+    else return false;
+    return true;
+  }
+
+  /**
+   * Send a String to the remote Host.
+   * @param str String to be sent
+   * @return true if we are connected
+   * @see modules.BSXModule
+   */
+  public boolean writeToSocket(String str)
+    {
+      if (connected) try {
+       byte[] bytes = new byte[str.length()];
+       str.getBytes(0, str.length(), bytes, 0);
+       tio.send(bytes);
+      } catch(Exception e) {
+       System.err.println("telnet.send(): disconnected");
+       disconnect();
+       return false;
+      }
+      else return false;
+      return true;
+    }
+  /**
+   * Send a String to the users terminal
+   * @param str String to be displayed
+   * @return void
+   * @see modules.BSXModule
+   */
+  public void writeToUser(String str)
+  {
+    if (term!=null)
+      term.putString(str);
+  }
+
+  /**
+   * This method is called when telnet needs to be notified of status changes.
+   * @param status Vector of status information.
+   * @return an object of the information requested.
+   * @see socket.StatusPeer
+   */
+  public Object notifyStatus(Vector status)
+  {
+    String what = (String)status.elementAt(0);
+    if(what.equals("NAWS"))
+      return term.getSize();
+    if(what.equals("TTYPE"))
+      if(term.getTerminalType() == null)
+        return emulation;
+      else return term.getTerminalType();
+    if(what.equals("LOCALECHO"))
+      localecho = true;
+    if(what.equals("NOLOCALECHO")) 
+      localecho = false;
+    return null;
+  }
+}
index 8701191..227db43 100644 (file)
@@ -42,7 +42,7 @@ public class Fleet {
     }
 
     public void dispatchCodeBag(int descriptor) {
-        System.out.println("instr: dispatching codebag #"+descriptor);
+        Log.println("instr: dispatching codebag #"+descriptor);
         Program.allCodeBags.get(descriptor).dispatch(this);
     }
 
diff --git a/src/edu/berkeley/fleet/FleetApplet.java b/src/edu/berkeley/fleet/FleetApplet.java
new file mode 100644 (file)
index 0000000..93f9d1a
--- /dev/null
@@ -0,0 +1,81 @@
+package edu.berkeley.fleet;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.applet.*;
+import java.io.*;
+import java.util.*;
+
+public class FleetApplet extends Applet {
+
+    public static void main(String[] s) throws Exception {
+        Frame f = new Frame();
+        f.show();
+
+        PipedOutputStream po1 = new PipedOutputStream();
+        PipedInputStream pi1 = new PipedInputStream(po1);
+
+        PipedOutputStream po2 = new PipedOutputStream();
+        final PipedInputStream pi2 = new PipedInputStream(po2);
+
+        JPanel top = new JPanel();
+        top.setLayout(new BorderLayout());
+        final JTextArea text = new JTextArea(100, 10);
+        top.add(new JScrollPane(text), BorderLayout.CENTER);
+
+        JButton button = new JButton("interpret");
+        top.add(button, BorderLayout.SOUTH);
+
+        Term term = new Term(pi1, po2);
+        term.setMinimumSize(new Dimension(Integer.MAX_VALUE, 10));
+
+        JSplitPane jsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
+                                        top,
+                                        term
+                                        );
+        f.add(jsp);
+
+        Log.log = new PrintWriter(new OutputStreamWriter(po1));
+
+        new Thread() {
+            public void run() {
+                try {
+                    while(true)
+                        System.err.println(pi2.read());
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }.start();
+        f.pack();
+
+        text.setFont(new Font("monospaced", 0, 20));
+        StringBuffer in = new StringBuffer();
+        BufferedReader br =
+            new BufferedReader(new InputStreamReader(FleetApplet.class.getClassLoader().getResourceAsStream("test.fleet")));
+        while(true) {
+            String str = br.readLine();
+            if (str==null) break;
+            in.append(str+"\n");
+        }
+        text.setText(in.toString());
+        button.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    try {
+                        System.err.println("launching");
+                        FleetParser.go(new StringReader(text.getText()));
+                        System.err.println("launched");
+                    } catch (Exception ex) {
+                        ex.printStackTrace();
+                    }
+                }
+            });
+
+        jsp.setDividerLocation(0.5);
+        f.pack();
+        f.show();
+        f.setSize(800, 600);
+        f.doLayout();
+    }
+
+}
index 7cd3dee..b05e75c 100644 (file)
@@ -12,7 +12,10 @@ import java.io.*;
 public class FleetParser {
 
     public static void main(String[] s) throws Exception {
+        go(new InputStreamReader(System.in));
+    }
 
+    public static void go(Reader r) throws Exception {
         InputStream grammarStream =
             FleetParser.class.getClassLoader().getResourceAsStream("fleet.g");
 
@@ -22,38 +25,38 @@ public class FleetParser {
         Union   mathGrammar        = Grammar.create(parsedGrammar, "s", gbr);
         Parser  mathParser         = new CharParser(mathGrammar);
 
-        System.out.println("about to parse: tests/test.fleet");
-        Tree tree = mathParser.parse(new CharInput(System.in)).expand1();
+        Log.println("about to parse: tests/test.fleet");
+        Tree tree = mathParser.parse(new CharInput(r)).expand1();
 
         // below is ugly voodoo which will go away very soon.  ignore it.
         TreeFunctor tf = (TreeFunctor)tree.head();
         Program program = (Program)tf.invoke(tree);
         // above is ugly voodoo which will go away very soon.  ignore it.
 
-        System.out.println();
-        System.out.println("dispatching root codebag:");
-        System.out.println(program.root);
+        Log.println();
+        Log.println("dispatching root codebag:");
+        Log.println(program.root);
 
         Fleet fleet = new Fleet();
         program.configure(fleet);
 
-        System.out.println("memory before execution:");
-        System.out.print("  ");
+        Log.println("memory before execution:");
+        Log.print("  ");
         for(int i=0; i<fleet.mem.length; i++)
-            System.out.print(fleet.mem[i] + " ");
-        System.out.println();
+            Log.print(fleet.mem[i] + " ");
+        Log.println();
 
-        System.out.println();
-        System.out.println("enabling execution...");
+        Log.println();
+        Log.println("enabling execution...");
         fleet.go();
-        System.out.println("execution halted.");
+        Log.println("execution halted.");
 
-        System.out.println();
-        System.out.println("memory after execution:");
-        System.out.print("  ");
+        Log.println();
+        Log.println("memory after execution:");
+        Log.print("  ");
         for(int i=0; i<fleet.mem.length; i++)
-            System.out.print(fleet.mem[i] + " ");
-        System.out.println();
+            Log.print(fleet.mem[i] + " ");
+        Log.println();
     }
 
 }
diff --git a/src/edu/berkeley/fleet/Log.java b/src/edu/berkeley/fleet/Log.java
new file mode 100644 (file)
index 0000000..04dc4fd
--- /dev/null
@@ -0,0 +1,25 @@
+package edu.berkeley.fleet;
+import java.io.*;
+
+public class Log {
+
+    public static PrintWriter log = new PrintWriter(new OutputStreamWriter(System.out));
+
+    public static void print(Object o) {
+        try {
+            log.print(o);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    public static void println() { println(""); }
+    public static void println(Object o) {
+        try {
+            log.println(o);
+            log.flush();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}
index 4b54576..9515a1a 100644 (file)
@@ -156,7 +156,7 @@ public class Program {
             for(Port d : dest) {
                 Ship.Inbox ib = fleet.getInbox(d.ship, d.port);
                 ob.addDestination(ib);
-                System.out.println("instr: " + ob + " -> " + ib);
+                Log.println("instr: " + ob + " -> " + ib);
             }
         }
         public String toString() {
@@ -178,7 +178,7 @@ public class Program {
             for(Port d : dest) {
                 Ship.Inbox ib = fleet.getInbox(d.ship, d.port);
                 ib.add(val);
-                System.out.println("instr: " + val + " -> " + ib);
+                Log.println("instr: " + val + " -> " + ib);
             }
         }
         public String toString() {
@@ -200,7 +200,7 @@ public class Program {
             for(Port d : dest) {
                 Ship.Inbox ib = fleet.getInbox(d.ship, d.port);
                 ib.add(cb.getIdentifier());
-                System.out.println("instr: codebag #" + cb.getIdentifier() + " -> " + ib);
+                Log.println("instr: codebag #" + cb.getIdentifier() + " -> " + ib);
             }
         }
         public String toString() {
index 074b04e..9e9fc4d 100644 (file)
@@ -38,7 +38,7 @@ public abstract class Ship {
                 int data = ob.data.remove();
                 Inbox destination = ob.destination.remove();
                 destination.add(data);
-                System.out.println("data:  " + ob + " ----("+data+")----> " + destination);
+                Log.println("data:  " + ob + " ----("+data+")----> " + destination);
             }
         }
         service();
diff --git a/src/edu/berkeley/fleet/Term.java b/src/edu/berkeley/fleet/Term.java
new file mode 100644 (file)
index 0000000..4a30a67
--- /dev/null
@@ -0,0 +1,57 @@
+package edu.berkeley.fleet;
+
+import de.mud.telnet.*;
+import de.mud.telnet.modules.*;
+import de.mud.telnet.display.*;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+import javax.swing.border.*;
+import java.net.*;
+import java.io.*;
+
+public class Term extends vt320 implements ComponentListener {
+    private final InputStream is;
+    private final OutputStream os;
+
+    public Term(InputStream is, OutputStream os) { this.is = is; this.os = os; addComponentListener(this); }
+
+    public void componentResized(ComponentEvent e) { repaint(); }
+    public void componentMoved(ComponentEvent e) { }
+    public void componentHidden(ComponentEvent e) { }
+    public void componentShown(ComponentEvent e) { }
+
+    private class ListenerThread extends Thread {
+        public void run() {
+            try {
+                byte[] buf = new byte[1024];
+                while(true) {
+                    int numread = is.read(buf, 0, buf.length);
+                    if (numread==-1) break;
+                    if (numread==0) continue;
+                    Term.this.putString(new String(buf, 0, numread, "US-ASCII"));
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+    boolean listenerStarted = false;
+    public void paint(Graphics g) {
+        if (!listenerStarted) { listenerStarted = true; new ListenerThread().start(); }
+        super.paint(g);
+    }
+    public synchronized void putString(String s) { super.putString(s); }
+    public synchronized boolean send(String s) {
+        try {
+            os.write(s.getBytes("US-ASCII"));
+            os.flush();
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+}
index 7bf925a..8081937 100644 (file)
 
 
 // define the initial contents of memory
-
 #memory { 000, 100, 200, 300, 400, 500 }
 
+// preload the counter register
+4            -> i.write
+i.writedone  -> gate.release
+gate.codebag <- top
+
 top: { i.read  -> less.in1
        0       -> less.in2
                   less.out -> ifthen.if
@@ -40,31 +44,14 @@ continue: {
        1000          -> adder.in2
        adder.out     -> memwrite.data
        memwrite.done -> gate.release
-       gate.codebag  <- write
+       gate.codebag  <- {
+                          i.read        -> adder2.in1
+                          -1            -> adder2.in2
+                          adder2.out    -> i.write
+                          i.writedone   -> gate2.release
+                          gate2.codebag <- top
+                        }
      }
-write: {
-  i.read        -> adder2.in1
-  -1            -> adder2.in2
-  adder2.out    -> i.write
-  i.writedone   -> gate2.release
-  gate2.codebag <- top
-}
-
-
-4   -> i.write
-i.writedone -> gate.release
-gate.codebag <- top
 
 
 
-//1             -> adder.in1
-//1             -> adder.in2
-//adder.out     -> memread.addr
-//memread.data  -> adder.in1
-//13            -> adder.in2
-//
-//adder.out        -> less.in1
-//214              -> less.in2
-//less.out         -> ifthen.if
-//{ 7 -> halt.in } -> ifthen.then
-//{ 9 -> halt.in } -> ifthen.else