如何将字符串传递到管道并使用流程构建器打印响应

时间:2012-10-23 09:22:50

标签: java stream pipe

这里有一些我正在努力工作的代码。基本上我需要保持进程打开,并在单个会话中提供新命令。一旦发出“exit”字符串命令,该过程就应该完成。感谢帮助......

Process process;
ArrayList<String>command;
ProcessBuilder builder;
Map<String, String> environ;
BufferedWriter bw;
BufferedReader br;
BufferedReader buffErrorStreamReader;

....

 try
    {
    command = new ArrayList<String>();
    command.add("cmd.exe");
    command.add("/c");
    //command.add(currentLine);

    builder = new ProcessBuilder(command);
    environ = builder.environment();

    builder.directory(new File("C://"));
    process = builder.start();

    }catch(Exception e)
          {
          System.out.println(e);
          }

    //Get a System.in Stream
    OutputStream output = process.getOutputStream();
    OutputStreamWriter osw = new OutputStreamWriter(output);
    bw = new BufferedWriter(osw);

    //Get a System.out Stream
    InputStream is = process.getInputStream();
    InputStreamReader isr = new InputStreamReader(is);
    br = new BufferedReader(isr);

    //Get a System.err Stream
    InputStream errorStream = process.getErrorStream();
    InputStreamReader errorStreamReader = new InputStreamReader(errorStream);
    buffErrorStreamReader = new BufferedReader(errorStreamReader);



....

//Read the command from my text component terminal
String currentLine = getCurrentLine();


    ///pass command to pipe input 
    bw.write(currentLine);
    System.out.println("wrote: "+currentLine);

    ///print response 
     String line;
    while ((line = br.readLine()) != null) 
    {
      System.out.println(line);
      //term.println(line);    
    }

    ///print any error response
    String errorLine;
    while ((errorLine = buffErrorStreamReader.readLine()) != null) 
    {
      System.out.println(errorLine);
      //term.println(errorLine);    
    }

4 个答案:

答案 0 :(得分:0)

您正在读取错误流和输入流,直到EOS,直到流程退出时才会发生(或者,很少有我在30多年前从未见过它,关闭stdout和stderr)。通常的做法是(a)合并错误和输出流和/或(b)在单独的线程中读取/它们,这样在这种情况下,您可以继续从其源输入输入,而不必担心产生的输出任何,在外部过程中你不能太了解的事物的本质。

答案 1 :(得分:0)

我的完整代码正在运行。我有一个终端从画布上的当前行获取命令。然后它组装一个进程构建器:“cmd.exe”,“/ c”和“当前行的命令字符串”参数。最后,它获取构建器环境并设置构建器目录。然后它启动该过程并获取该过程的输入和错误流。然后它将这些响应打印到我的画布终端。但是,我的方法的问题是我正在为每个进程打开一个新的cmd.exe(dos命令提示符)。所以基本上,它是一个非交互式终端,因为在命令提交(新进程)之间没有建立会话。该过程只接受命令,打印输出并每次终止。所以我的问题是如何实现交互式终端会话。我已成功实现了通过保存当前路径并使用builder.directory方法将其传递给新进程来更改目录的功能。也可以使用builder.environment对环境变量执行相同的操作(但我无法弄清楚)。这会给人一种真实会话的印象。我也尝试了流的线程,它确实有效。但是我必须做错了,因为在线程打印命令响应后进程终止。

答案 2 :(得分:0)

以下是我的终端的代码,分为两部分。我正在谈论的代码位于VK.ENTER键的switch语句中。

NB。如果要更改目录,它将仅适用于absoulute路径。例如用户目录中的“cd C:// Users / Aubrey /”而不是“cd / Aubrey”!!!

/*
 * Terminal.java - A convienence console.
 * 
 * http://www.splashportal.net
 * 30/09/2012
 */
package terminal;

import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import javax.swing.JScrollPane;
public class Terminal extends Canvas
{
static final long serialVersionUID = 5479170142892558288L;

public static int WIDTH = 80, HEIGHT = 200;
static Process process;

public static final Color
    WHITE       = new Color(0xffffff),
    RED         = new Color(0xff0000),
    GREEN       = new Color(0x00cc00),
    BROWN       = new Color(0x996600),
    BLUE        = new Color(0x0000ff),
    DARK_GREEN  = new Color(0x009900),
    DARK_BLUE   = new Color(0x000099),
    LIGHT_GREY  = new Color(0x999999),
    DARK_GREY   = new Color(0x666666),
    CYAN        = new Color(0x00ffff),
    DARK_CYAN   = new Color(0x009999),
    DARK_YELLOW = new Color(0x999900),
    MAGENTA     = new Color(0xff00ff),
    YELLOW      = new Color(0xffff00),
    PURPLE      = new Color(0x990099),
    BLACK       = new Color(0x000000);

private static final Color[] COLORS = {
        WHITE           ,  // 0 @
        RED             ,  // 1 A
        GREEN           ,  // 2 B
        BROWN           ,  // 3 C
        BLUE            ,  // 4 D
        DARK_GREEN      ,  // 5 E
        DARK_BLUE       ,  // 6 F
        LIGHT_GREY      ,  // 7 G
        DARK_GREY       ,  // 8 H
        CYAN            ,  // 9 I
        DARK_CYAN       ,  // 10 J
        DARK_YELLOW     ,  // 11 K
        MAGENTA         ,  // 12 L
        YELLOW          ,  // 13 M
        PURPLE          ,  // 14 N
        BLACK           ,  // 15 O
    };

public static final int EFFECTS_UNDERLINE = 1;

private static final int
    ESC_NORMAL  = 0,
    ESC_ESCAPE  = 1,
    ESC_POS_Y   = 2,
    ESC_POS_X   = 3,
    ESC_FGCOLOR = 4,
    ESC_BGCOLOR = 5;

    static boolean isBreak=false;
    static int heightCounter;
    static ArrayList lines;
    static Terminal term;
    static Frame f;
    static File startPath;

public static Color getColor(int i)
{
    return COLORS[i & 0x0f];
}
static String newPath = System.getenv("temp");


public static void main(String[] args)
{
    f = new Frame("Terminal Test");
    f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                f.dispose();
            }
        } );

    term = new Terminal();

     //add buffer_ image to a scrollpane somehow?
    scrollPane = new ScrollPane();

    scrollPane.add(term, buffer_);

    // disabling forward focus traversal allows TAB to reach the component
    term.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
            Collections.EMPTY_SET);
    term.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent ke) {
                // handle special keys
                switch(ke.getKeyCode()) {
                    case KeyEvent.VK_UP:
                        term.print("\033A");
                        term.repaint();
                        break;
                    case KeyEvent.VK_DOWN:
                        term.print("\033B");
                        term.repaint();
                        break;
                    case KeyEvent.VK_RIGHT:
                        term.print("\033C");
                        term.repaint();
                        break;
                    case KeyEvent.VK_LEFT:
                        term.print("\033D");
                        term.repaint();
                        break;
                    case KeyEvent.VK_ENTER:

    //flag set to block printing of ">>" prompt                 
    isR=true;                    
    try{
    String currentLine = getCurrentLine();
    ArrayList<String>command = new ArrayList<String>();
    command.add("cmd.exe");
    command.add("/c");
    command.add(currentLine);
    System.out.println("Line-IN: "+currentLine);
    ProcessBuilder builder = new ProcessBuilder(command);
    Map<String, String> environ = builder.environment();

    if (currentLine.startsWith("cd")){      
    //startPath = new File(".").getAbsoluteFile();    

    //if an absolute path is specified and exists    
    if(new File(currentLine.substring(3)).exists())
    newPath=currentLine.substring(3);

    System.out.println("Directory : "+new File(newPath));
    builder.directory(new File(newPath));
    process = builder.start();
    InputStream is = process.getInputStream();
    InputStreamReader isr = new InputStreamReader(is);
    BufferedReader br = new BufferedReader(isr);

    InputStream errorStream = process.getErrorStream();
    InputStreamReader errorStreamReader = new InputStreamReader(errorStream);
    BufferedReader buffErrorStreamReader = new BufferedReader(errorStreamReader);

    String line;
    //term.println("");
    while ((line = br.readLine()) != null) {
      System.out.println(line);
      term.println(line);    
    }
    String errorLine;
    term.println("");
    while ((errorLine = buffErrorStreamReader.readLine()) != null) {
      //System.out.println(line);
      term.println(errorLine);    
    }
    buffErrorStreamReader.close();
    br.close();
    //not sure how to use this method to allow for an interactive prompt?
    //for example a "dir /p" command should allow the output to be paged per keypress!
    process.waitFor();
    //flags set to allow printing of ">>" prompt
    isR=false;
    isNew=true;

    System.out.println("Program terminated!");

    }else if(currentLine.startsWith("exit")||currentLine.startsWith("quit")){
                               System.exit(0);
    }else if(currentLine.startsWith("dir /p")){
        //try using dos batch?
    System.out.println("page...");
    command = new ArrayList<String>();
    command.add("cmd.exe");
    command.add("dir"); 
    builder.directory(new File(newPath));
    process = builder.start();
    InputStream is = process.getInputStream();
    InputStreamReader isr = new InputStreamReader(is);
    BufferedReader br = new BufferedReader(isr);

    String line;

    //write out to a FLAT FILE.
    File temp = new File("temp");
    PrintWriter tempOut = new PrintWriter(new FileWriter(temp));
    while ((line = br.readLine()) != null) {
    System.out.println(line);
    tempOut.println(line);    
    }
    //not sure how to use this method to allow for an interactive prompt?
    //for example a "dir /p" command should allow the output to be paged per keypress!
    process.waitFor();

    //flags set to allow printing of ">>" prompt

    br.close();
    tempOut.flush();
    tempOut.close();

    captureLines();  
    int index = 0;

    printPage(index);

    isR=false;
    isNew=true;

    System.out.println("Program terminated!");


    }else
    {    


    builder.directory(new File(newPath));
    process = builder.start();

    Thread t = new Thread() {
    public void run() {
        try{
        System.out.println("blah");
        InputStream is = process.getInputStream();
    InputStreamReader isr = new InputStreamReader(is);
    BufferedReader br = new BufferedReader(isr);

    InputStream errorStream = process.getErrorStream();
    InputStreamReader errorStreamReader = new InputStreamReader(errorStream);
    BufferedReader buffErrorStreamReader = new BufferedReader(errorStreamReader);



    String line;
    //term.println("");
    while ((line = br.readLine()) != null) {
      //System.out.println(line);
      term.println(line);    
    }
    String errorLine;
    //term.println("");
    while ((errorLine = buffErrorStreamReader.readLine()) != null) {
      //System.out.println(line);
      term.println(errorLine);    
    }
    buffErrorStreamReader.close();
    br.close();
    //not sure how to use this method to allow for an interactive prompt?
    //for example a "dir /p" command should allow the output to be paged per keypress!
    process.waitFor();

   //flags set to allow printing of ">>" prompt
    isR=false;
    isNew=true;  

    term.println("");
        }catch(Exception e){System.out.println(e);}
    }
};
t.start();




    System.out.println("Program terminated!");
} 

    }catch(Exception e){System.out.println(e);}
  }
}

            public void keyTyped(KeyEvent ke) {
                char c = ke.getKeyChar();
                if( c == KeyEvent.CHAR_UNDEFINED ) {
                    // handle special keys
                    switch(ke.getKeyCode()) {
                        case KeyEvent.VK_TAB:
                            c = '\t';
                            break;
                        case KeyEvent.VK_ESCAPE:
                            c = (char)27;
                            break;
                    }
                }
                if( c != KeyEvent.CHAR_UNDEFINED ) {
                    term.print(c);
                    term.repaint();
                }
            }
        } );

    f.add(scrollPane, BorderLayout.CENTER);
    term.validate();
    scrollPane.setSize(598, 316);
    //f.setSize(700, 400);
    f.pack();
    f.setVisible(true);

    term.println(DARK_GREY, WHITE, "vTerminal");
    //term.println(DARK_GREY, WHITE, ">>");
    term.requestFocus();
}

private int tileWidth_ = 8;
private int tileHeight_ = 12;
private static TerminalLine[] line_;
private int xCursor_;
private static int yCursor_;
private int xCursorSave_;
private int yCursorSave_;
private boolean wrap_ = true;
private boolean scroll_ = true;
private boolean cursorVisible_ = true;
private int tab_ = 8;
private int xOffset_ = 0;
private int yOffset_ = 0;
private int escapeMode_ = ESC_NORMAL;
/** First character held in an ESC Y Position Cursor command. */
private int escapeHold_ = -1;
private boolean reverseVideo_;
private byte effects_;

private static Image buffer_;
private static ScrollPane scrollPane;
private static Boolean isNew = false;
private static Boolean isR = false;

答案 3 :(得分:0)

//工作代码的第二部分

/** Creates a new terminal.  */
    public Terminal()
    {



        line_ = new TerminalLine[HEIGHT];
        for(int y = 0; y < HEIGHT; ++y) {
            line_[y] = new TerminalLine(WIDTH);
        }

        setBackground(DARK_GREY);
        setForeground(LIGHT_GREY);
        setFont( new Font("Monospaced", Font.PLAIN, 12) );
        clearScreen();
    }
    public static void captureLines(){

        String line;

        try{

        heightCounter=Terminal.HEIGHT;
        BufferedReader tempIn = new BufferedReader(new FileReader("temp"));
        term.println("");
        lines = new ArrayList();
        while((line = tempIn.readLine())!=null)
            {
            lines.add(line);
            }
        tempIn.close();

        }catch(Exception e){System.out.println(e);}
        return;
    }
    public static void printPage(int index){
        try{
        if(index<lines.size()){
        for (int i=0; i<25; i++){

            if(index==lines.size())
              return; 

           //System.out.println(index+" "+lines.get(index));
           term.println(lines.get(index).toString());
           index++;  
        }Thread.sleep(500);

        printPage(index);
        }else{return;}

    }catch(Exception e){System.out.println(e);}
    }

    public static String getCurrentLine(){
    TerminalLine currentLine = line_[getYCursor()];
        String command="";

                for(char c: currentLine.data){
                command+= c;
                        }
        String outString = command.substring(2);
        return (outString.trim());
    }

    public synchronized void clearScreen()
    {
        for(int y = 0; y < HEIGHT; ++y) {
            clearLine(y);
        }
        repaint();
    }

    public synchronized void clearLine(int line)
    {
        clearLine(line, 0, WIDTH-1);
    }

    public synchronized void clearLine(int y, int x0, int x1)
    {
        Color bg = getBackground();
        Color fg = getForeground();
        TerminalLine line = line_[y];


        for(int x = x0; x <= x1; ++x) {
            line.data[x] = ' ';
            line.bgColor[x] = bg;
            line.fgColor[x] = fg;
            line.effects[x] = 0;
        }
    }

    public synchronized boolean getCursorVisible()
    { return cursorVisible_; }

    public synchronized char getData(int x, int y)
    {
        return line_[y].data[x];
    }

    public synchronized Color getBgColor(int x, int y)
    {
        return line_[y].bgColor[x];
    }

    public synchronized byte getEffects(int x, int y)
    {
        return line_[y].effects[x];
    }

    public synchronized Color getFgColor(int x, int y)
    {
        return line_[y].fgColor[x];
    }

    public synchronized Dimension getMaximumSize()
    {
        return getPreferredSize();
    }

    public synchronized Dimension getMinimumSize()
    {
        return getPreferredSize();
    }

    public synchronized Dimension getPreferredSize()
    {
        return new Dimension(WIDTH * tileWidth_, HEIGHT * tileHeight_);
    }

    public boolean getReverseVideo()
    { return reverseVideo_; }

    public synchronized boolean getScroll()
    { return scroll_; }

    public synchronized int getTab()
    { return tab_; }

    public int getTileWidth()
    { return tileWidth_; }

    public int getTileHeight()
    { return tileHeight_; }

    public synchronized int getXOffset()
    { return xOffset_; }

    public synchronized int getYOffset()
    { return yOffset_; }

    public synchronized int getXCursor()
    { return xCursor_; }

    public static synchronized int getYCursor()
    { return yCursor_; }

    public synchronized void gotoxy(int x, int y)
    {
        xCursor_ = x;
        yCursor_ = y;
    }

    public synchronized void print(String s)
    {
        print(null, null, s);
    }

    public synchronized void print(Color bg, Color fg, String s)
    {
        for(int i = 0, len = s.length(); i < len; ++i) {
            print(bg, fg, s.charAt(i) );
        }
        repaint();
    }

    public synchronized void print(char c)
    {
        print(null, null, c);
    }

    public synchronized void print(Color bg, Color fg, char c)
    {
        if( escapeMode_ == ESC_NORMAL ) {
            switch(c) {
                case 7:     //  7 = BEL, bell
                    bell();
                    break;
                case '\b':  //  8 = BS, backspace
                    --xCursor_;
                    break;
                case '\t':  //  9 = HT, tab
                    xCursor_ = ((xCursor_ + tab_) / tab_) * tab_;
                    break;
                case '\n':  // 10 = LF, newline
                    xCursor_ = 0;
                    ++yCursor_;
                    //flag set to allow printing of ">>" prompt 
                    isNew=true;
                    break;
                case 11:    // 11 = VT, vertical tab
                case '\f':  // 12 = FF, form feed
                    ++yCursor_;
                    break;
                case '\r':  // 13 = CR, ignored because Java's newline is \n
                    break;
                case 27:    // 27 = ESC, enters escape mode
                    escapeMode_ = ESC_ESCAPE;
                    break;
                case 127: { // 127 = DEL, right-delete
                    int yc = yCursor_;
                    TerminalLine line = line_[yc];
                    for(int x = xCursor_; x < WIDTH-1; ++x) {
                        line.setData(x, line.bgColor[x+1], line.fgColor[x+1],
                            line.data[x+1], line.effects[x+1] );
                    }
                    setData(WIDTH-1, yc, null, null, ' ');
                    break;
                }
                default:
                    if( c < 32 ) {
                        // unknown control character
                        bell();
                    } else {
                        setData(xCursor_, yCursor_, bg, fg, c);
                        ++xCursor_;
                    }
            }
        } else {
            escapeMode_ = doEscape(c);
        } 
        // wrap cursor
        if( xCursor_ < 0 ) {
            if( wrap_ ) {
                xCursor_ = WIDTH-1;
                --yCursor_;
            } else {
                xCursor_ = 0;
            }
        } else if( xCursor_ >= WIDTH ) {
            if( wrap_ ) {
                xCursor_ = 0;
                ++yCursor_;
            } else {
                xCursor_ = WIDTH-1;
            }
        }
        if( yCursor_ < 0 ) {
            if( scroll_ ) {
                scrollDown(0);
            }
            yCursor_ = 0;
        } else if( yCursor_ >= HEIGHT ) {
            if( scroll_ ) {
                scrollUp(0);
            }
            yCursor_ = HEIGHT-1;
        }
        //cool flags here to print ">>" prompt: 1. when there is new line and 2. when the isR flag has detected the end of printing  in the return event.
        if(isR){
            isNew=false;

        }
        if(isNew){
            isNew=false;
         print(DARK_GREY, YELLOW, ">>");

        }
    }

    public synchronized void scrollDown(int topline)
    {
        // scroll down, exposing a blank line at top
        // don't waste the existing array, recycle it.
        TerminalLine line0 = line_[HEIGHT-1];
        // move all lines down
        for(int y = HEIGHT-1; y > topline; --y) {
            line_[y] = line_[y-1];
        }
        // put it at the top
        line_[topline] = line0;
        clearLine(topline);
    }

    public synchronized void scrollUp(int topline)
    {
        // scroll up, exposing a blank line at bottom
        // don't waste the existing array, recycle it.
        TerminalLine line0 = line_[topline];
        // move all lines up
        for(int y = topline; y < HEIGHT-1; ++y) {
            line_[y] = line_[y+1];
        }
        // put it at the end
        line_[HEIGHT-1] = line0;
        clearLine(HEIGHT-1);
    }

    private int doEscape(char c)
    {
        switch(escapeMode_) {
            case ESC_NORMAL:
                throw new IllegalStateException();
            case ESC_ESCAPE:
                // handled below
                break;
            case ESC_POS_Y:
                escapeHold_ = Math.max(0, Math.min(HEIGHT-1, (int)(c - 32)));
                return ESC_POS_X;
            case ESC_POS_X:
                yCursor_ = escapeHold_;
                xCursor_ = Math.max(0, Math.min(WIDTH-1, (int)(c - 32)));
                return ESC_NORMAL;
            case ESC_FGCOLOR:
                setForeground( getColor( (int)c ) );
                return ESC_NORMAL;
            case ESC_BGCOLOR:
                setBackground( getColor( (int)c ) );
                return ESC_NORMAL;
            default:
                throw new IllegalStateException("Unknown escape mode "+escapeMode_);
        }

        switch(c) {
            case 24:        // 24 = CAN, cancel escape
            case 26:        // 26 = SUB, cancel escape
            case 27:        // 27 = ESC, cancel escape
                return ESC_NORMAL;
            case 'A':       // Cursor Up
                if( yCursor_ > 0 ) {
                    --yCursor_;
                }
                return ESC_NORMAL;
            case 'B':       // Cursor Down
                if( yCursor_ < HEIGHT-1 ) {
                    ++yCursor_;
                }
                return ESC_NORMAL;
            case 'C':       // Cursor Forward
                if( xCursor_ < WIDTH-1 ) {
                    ++xCursor_;
                }
                return ESC_NORMAL;
            case 'D':       // Cursor Backward
                if( xCursor_ > 0 ) {
                    --xCursor_;
                }
                return ESC_NORMAL;
            case 'E':       // Clear Screen, Home Cursor
                clearScreen();
                xCursor_ = 0;
                yCursor_ = 0;
                return ESC_NORMAL;
            case 'H':       // Home Cursor
                xCursor_ = 0;
                yCursor_ = 0;
                return ESC_NORMAL;
            case 'I':       // Reverse Index
                --yCursor_;
                return ESC_NORMAL;
            case 'J':       // Erase to End of Page
                clearLine(yCursor_, xCursor_, WIDTH-1);
                for(int y = yCursor_+1; y < HEIGHT; ++y) {
                    clearLine(y);
                }
                return ESC_NORMAL;
            case 'K':       // Erase to End of Line
                clearLine(yCursor_, xCursor_, WIDTH-1);
                return ESC_NORMAL;
            case 'L':       // Insert Line
                scrollDown(yCursor_);
                xCursor_ = 0;
                return ESC_NORMAL;
            case 'M':       // Delete Line
                scrollUp(yCursor_);
                xCursor_ = 0;
                return ESC_NORMAL;
            case 'N': {     // Delete Character
                int yc = yCursor_;
                TerminalLine line = line_[yc];
                for(int x = xCursor_; x < WIDTH-1; ++x) {
                    line.setData(x, line.bgColor[x+1], line.fgColor[x+1],
                        line.data[x+1], line.effects[x+1] );
                }
                setData(WIDTH-1, yc, null, null, ' ');
                break;
            }
            case 'Y':       // Position Cursor
                return ESC_POS_Y;
            case 'b':       // Set Foreground Color
                return ESC_FGCOLOR;
            case 'c':       // Set Background Color
                return ESC_BGCOLOR;
            case 'd': {     // Erase from Beginning of Display
                int xc = xCursor_, yc = yCursor_;
                clearLine(yc, 0, xc);
                for(int y = 0; y < yc; ++y) {
                    clearLine(y);
                }
                return ESC_NORMAL;
            }
            case 'j':       // Save Cursor Position
                xCursorSave_ = xCursor_;
                yCursorSave_ = yCursor_;
                return ESC_NORMAL;
            case 'k':       // Restore Cursor Position
                xCursor_ = xCursorSave_;
                yCursor_ = yCursorSave_;
                return ESC_NORMAL;
            case 'l':       // Erase Entire Line
                clearLine(yCursor_);
                xCursor_ = 0;
                return ESC_NORMAL;
            case 'm':       // Enable Cursor
                cursorVisible_ = true;
                return ESC_NORMAL;
            case 'n':       // Disable Cursor
                cursorVisible_ = false;
                return ESC_NORMAL;
            case 'o':       // Erase from Beginning of Line
                clearLine(yCursor_, 0, xCursor_);
                return ESC_NORMAL;
            case 'p':       // Enable Reverse Video
                reverseVideo_ = true;
                return ESC_NORMAL;
            case 'q':       // Disable Reverse Video
                reverseVideo_ = false;
                return ESC_NORMAL;
            case 'r':       // Enable Underlining
                effects_ |= EFFECTS_UNDERLINE;
                return ESC_NORMAL;
            case 'u':       // Disable Underlining
                effects_ &= ~EFFECTS_UNDERLINE;
                return ESC_NORMAL;
            case 'v':       // Enable Wrapping
                wrap_ = true;
                return ESC_NORMAL;
            case 'w':       // Disable Wrapping
                wrap_ = false;
                return ESC_NORMAL;
        }
        // unknown escape code
        bell();
        return ESC_NORMAL;
    }

    public synchronized void println(String s)
    {
        println(null, null, s);
    }

    public synchronized void println(Color bg, Color fg, String s)
    {
        print(bg, fg, s);
        print(bg, fg, '\n');
        repaint();
    }

    public synchronized void setCursorVisible(boolean c)
    { cursorVisible_ = c; }

    public synchronized void setData(int x, int y, char c)
    {
        setData(x, y, null, null, c);
    }

    public synchronized void setData(int x, int y, Color bg, Color fg, char c)
    {
        setData(x, y, bg, fg, c, effects_);
    }

    public synchronized void setData(int x, int y, Color bg, Color fg, char c, byte effects)
    {
        if( bg == null ) {
            bg = getBackground();
        }
        if( fg == null ) {
            fg = getForeground();
        }
        if( reverseVideo_ ) {
            Color tmp = bg;
            bg = fg;
            fg = tmp;
        }
        TerminalLine line = line_[y];
        line.data[x] = c;
        line.bgColor[x] = bg;
        line.fgColor[x] = fg;
        line.effects[x] = effects;
    }

    public synchronized void setBgColor(int x, int y, Color b)
    {
        line_[y].bgColor[x] = b;
    }

    public synchronized void setEffects(int x, int y, byte e)
    {
        line_[y].effects[x] = e;
    }

    public synchronized void setFgColor(int x, int y, Color f)
    {
        line_[y].fgColor[x] = f;
    }

    public void setFont(Font font)
    {
        super.setFont(font);
        FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
        tileWidth_ = fm.charWidth('W');
        tileHeight_ = fm.getHeight() - fm.getDescent();
        xOffset_ = 0;
        yOffset_ = -fm.getDescent();
        //System.out.println("tile "+tileWidth_+"x"+tileHeight_+", offset="+xOffset_+","+yOffset_);
    }

    public void setReverseVideo(boolean r)
    { reverseVideo_ = r; }

    public synchronized void setScroll(boolean s)
    { scroll_ = s; }

    public synchronized void setTab(int t)
    { tab_ = t; }

    public void setTileSize(int w, int h)
    {
        tileWidth_ = w;
        tileHeight_ = h;
        buffer_ = null;
    }

    public synchronized void setXOffset(int x)
    { xOffset_ = x; }

    public synchronized void setYOffset(int y)
    { yOffset_ = y; }

    public synchronized void setXCursor(int x)
    { xCursor_ = x; }

    public synchronized void setYCursor(int y)
    { yCursor_ = y; }

    public synchronized void paint(Graphics g)
    {
        if( buffer_ != null ) {
            g.drawImage(buffer_, 0, 0, null);
        }
    }

    public synchronized void update(Graphics gg)
    {
        if( buffer_ == null ) {
            Dimension d = getPreferredSize();
            buffer_ = createImage(d.width, d.height);
            if( buffer_ == null ) {
                repaint(100L); //wtf?
                return;
            }
        }

        Graphics g = buffer_.getGraphics();
        g.setFont( getFont() );
        FontMetrics fm = g.getFontMetrics();
        char[] cbuf = new char[1];  // for drawChars
        for(int y = 0; y < HEIGHT; ++y) {
            int sy = y * tileHeight_;
            TerminalLine line = line_[y];
            for(int x = 0; x < WIDTH; ++x) {
                int sx = x * tileWidth_;
                // background
                g.setColor( line.bgColor[x] );
                g.fillRect(sx, sy, tileWidth_, tileHeight_);
                // character
                g.setColor( line.fgColor[x] );
                cbuf[0] = line.data[x];
                g.drawChars(cbuf, 0, 1, sx + xOffset_,
                        sy + fm.getAscent() + yOffset_);
                // effects
                if( (line.effects[x] & EFFECTS_UNDERLINE) != 0 ) {
                    g.drawLine(sx, sy+tileHeight_-1,
                            sx+tileWidth_-1, sy+tileHeight_-1);
                }
                // borders for testing
                //g.drawLine(sx, sy, sx+tileWidth_-1, sy);
                //g.drawLine(sx, sy, sx, sy+tileHeight_-1);
            }
        }
        if( cursorVisible_ ) {
            Color bg = getBgColor(xCursor_, yCursor_);
            if( bg == BLACK ) {
                bg = WHITE;
            }
            g.setXORMode(bg);
            g.fillRect(xCursor_ * tileWidth_, yCursor_ * tileHeight_,
                    tileWidth_, tileHeight_);
            g.setPaintMode();
        }

        paint(gg);
    }

    }
    class TerminalLine
    {
    public Color[] bgColor;
    public Color[] fgColor;
    public char[] data;
    public byte[] effects;

    public TerminalLine(int width)
    {
        bgColor = new Color[width];
        fgColor = new Color[width];
        data = new char[width];
        effects = new byte[width];
    }
    public void setData(int x, Color bg, Color fg, char c, byte e)
    {
        data[x] = c;
        bgColor[x] = bg;
        fgColor[x] = fg;
        effects[x] = e;
    }   
 }