package xtc.lang.blink; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import xtc.lang.blink.Event.BlinkEventSource; import xtc.lang.blink.Event.DeathEvent; import xtc.lang.blink.Event.RawTextMessageEvent; /** * A process standard input/output handler. This handler is an interface between * the Blink debugger and its sub-process such as debugee, JDB and GDB. This * handler creates two threads to monitor the process stdout and stderr streams. */ abstract class StdIOProcess implements BlinkEventSource { /** For DOS/Window EOL (\r\n), we need to delete '\r' character. */ private static final boolean needDOSEOLProcessing = System.getProperty("os.name") .startsWith("Windows"); /** If the current system uses 2 bytes DOS EOL (\r\n), remove '\r'. * @throws IOException */ private static int read(BufferedReader br, char[] buf) throws IOException { int nRead = br.read(buf); if (nRead < 0) {return nRead;} int nEffectiveRead; if (needDOSEOLProcessing) { nEffectiveRead=0; for(int i=0;i < nRead;i++){ if (buf[i] != '\r') { if (nEffectiveRead == i){ nEffectiveRead++; } else { assert nEffectiveRead <= i; buf[nEffectiveRead++] = buf[i]; } } } } else { nEffectiveRead = nRead; } return nEffectiveRead; } /** The Blink debugger. */ protected final Blink dbg; /** The name of this handler. */ private final String name; /** The command line to create the native process. */ private String[] commandArray; /** The native process. */ private Process process; /** The buffered writer to the process. */ private BufferedWriter out; /** The thread group for the reader threads. */ private final ThreadGroup tg; /** A reader thread for the process output message. */ private final Thread stdOutReader; /** A reader thread for the process error message. */ private final Thread stdErrReader; private boolean died = false; /** * @param name The name of the slave process. * @param commandArray The command array to create the slave process. */ StdIOProcess(Blink dbg, String name) { this.dbg = dbg; this.name = name; this.tg = new ThreadGroup(getEventSourceName()); stdOutReader = new Thread(tg, String.format("StdIOProcessThread(%s, stdout)", getEventSourceName())) { public void run() {monitorStdOut();} }; stdErrReader = new Thread(tg, String.format("StdIOProcessThread(%s, stderr)", getEventSourceName())) { public void run() { monitorStdErr();} }; } /** Getter method for the event source name. */ public String getEventSourceName() { return name; } /** * Start the two monitoring threads. * * @param cmdArray The micro debugger command. */ protected void begin(String[] cmdArray) { this.commandArray = cmdArray; try { process = Runtime.getRuntime().exec(commandArray); OutputStream os = process.getOutputStream(); OutputStreamWriter ow = new OutputStreamWriter(os); out = new BufferedWriter(ow); } catch (IOException e) { dbg.err("could not correctly run " + commandArray + "\n"); e.printStackTrace(); return; } assert process != null; stdOutReader.start(); stdErrReader.start(); } /** * Monitor the micro debugger stdout, and enque the micro debugger message * event. */ private void monitorStdOut() { InputStream is = process.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); char[] buf = new char[1024]; try { int nRead = read(br,buf); while (nRead > 0) { RawTextMessageEvent e = new RawTextMessageEvent(this, buf, 0, nRead); dbg.enqueEvent(e); processMessageEvent(e); nRead = read(br,buf); // possibly blocking } assert nRead == -1; // EOF } catch (IOException e) { } // wait until two reader threads are dead. try {stdErrReader.join();} catch(InterruptedException e) {} // notify my death. died = true; dbg.enqueEvent(new DeathEvent(this)); } /** * Read error messages from the managed process, and show the error message * to the user. */ private void monitorStdErr() { InputStream is = process.getErrorStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); char[] buf = new char[1024]; try { int nRead = read(br,buf); while (nRead > 0) { String m=new String(buf, 0, nRead); dbg.err(m); nRead = read(br,buf); // possibly blocking } assert nRead == -1; // EOF } catch (IOException e) { } } /** * Internally process the raw message to generate macro event. * * @param e The raw text message event. */ abstract void processMessageEvent(RawTextMessageEvent e); /** * Send a message to the output stream. * * @param msg The message. */ public void sendMessage(String msg) { try { out.write(msg); out.flush(); } catch(IOException e) { throw new RuntimeException("A failure to send a message", e); } } public boolean isDead() { return died; } }