From: YoonKi Park Date: Mon, 21 Nov 2011 01:53:13 +0000 (+0900) Subject: [Title] add ansicode console X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=957c8b612703b4210edeafd4cc3ea7783c72fa88;p=sdk%2Fide%2Fcommon-eplugin.git [Title] add ansicode console [Redmine#] 2029 --- diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/console/AnsicodeAdapter.java b/com.samsung.slp.common/src/com/samsung/slp/common/console/AnsicodeAdapter.java new file mode 100644 index 0000000..a3a9c2c --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/console/AnsicodeAdapter.java @@ -0,0 +1,193 @@ +package com.samsung.slp.common.console; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.LineStyleEvent; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Display; + +public class AnsicodeAdapter { + public static final char ESCAPE = '\033'; // ANSI Escape Character that starts commands + +/* public static final Color BLACK = Color.BLACK; + public static final Color RED = Color.RED.darker(); + public static final Color GREEN = Color.GREEN.darker(); + public static final Color YELLOW = Color.YELLOW.darker(); + public static final Color BLUE = new Color(66, 66, 255).darker(); + public static final Color MAGENTA = Color.MAGENTA.darker(); + public static final Color CYAN = Color.CYAN.darker(); + public static final Color WHITE = Color.GRAY.brighter(); + + public static final Color INTENSE_BLACK = Color.GRAY.darker(); + public static final Color INTENSE_RED = Color.RED; + public static final Color INTENSE_GREEN = Color.GREEN; + public static final Color INTENSE_YELLOW = Color.YELLOW; + public static final Color INTENSE_BLUE = new Color(66, 66, 255); + public static final Color INTENSE_MAGENTA = Color.MAGENTA; + public static final Color INTENSE_CYAN = Color.CYAN; + public static final Color INTENSE_WHITE = Color.WHITE; + + private static final Color normal[] = {BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE}; + private static final Color bright[] = {INTENSE_BLACK, INTENSE_RED, INTENSE_GREEN, INTENSE_YELLOW, INTENSE_BLUE, INTENSE_MAGENTA, + INTENSE_CYAN, INTENSE_WHITE};*/ + + private Color foreground; + private Color background; + private int fontStyles; + + //"\e[31mHello World\e[m" + private static String ansiString; + private static LineStyleEvent lineEvent; + private static AnsicodeAdapter instance; + private static boolean hasAnsicode = false; + + private static int commandSGR = 'm'; + private static int commandDSR = 'n'; + private static int[] commands = {'m','n'}; + private static List styles = new ArrayList(); + public AnsicodeAdapter() {} + + /** + * Get instance of AnsicodeAdapter + * @param string The String should end with '\n' + * @return Returns the AnsicodeAdapter's incstance + */ + public static AnsicodeAdapter getInstance(LineStyleEvent event) + { + if (instance == null) + instance = new AnsicodeAdapter(); + lineEvent = event; + ansiString = event.lineText; + parseLineStyle(); + return instance; + } + public static AnsicodeAdapter getDefault() + { + if (instance == null) + instance = new AnsicodeAdapter(); + return instance; + } + public static List getRangeStyle() { + return styles; + } + private static void parseLineStyle() { + String str; + + for (int i = 0; i < ansiString.length(); i++) { + if (ansiString.charAt(i) == '\033') { + if (ansiString.indexOf(commandSGR, i) < ansiString.length()) { + + int startCommand = ansiString.indexOf('m', i); + if (startCommand < 0) { + // + return; + } + String colorCodes = ansiString.substring(i+2,startCommand); + String codes[] = colorCodes.split(";"); + + int styleStart = startCommand+1; + int styleEnd = 0; + int endCommand = ansiString.indexOf('\033',styleStart); + if (endCommand < 0 ) { + styleEnd = ansiString.length()-1; + } else { + styleEnd = endCommand; + } + String styleString = ansiString.substring(styleStart, styleEnd); //remove [m + + StyleRange currentStyle = new StyleRange(); + currentStyle.start = lineEvent.lineOffset; + //currentStyle.start = event.lineOffset + styleStart; + currentStyle.length= styleString.length(); + Display display = Display.getCurrent(); + Color blue = display.getSystemColor(SWT.COLOR_BLUE); + + //currentStyle.background = blue; + currentStyle.foreground = blue; + styles.add(currentStyle); + //////// + i = styleEnd; + + } + } + } + } + private void getStyleAttribute(String codes[]) { + boolean brighter = false; + for (int j = 0; j < codes.length; j++) { + + if (codes[j].matches("[\\d]*")) { + int code = Integer.parseInt(codes[j]); + if (code == 0) { + brighter = false; //reset / normal + } else if (code == 1) { + brighter = true; + } else if (code >= 30 && code <= 37) { + + } + } + } + } + public Color getForeground() { + return foreground; + } + public Color getBackground() { + return background; + } + public int getFontStyle() { + return fontStyles; + } + public String getStripAnsiString(String ansiString) { + StringBuilder sb = new StringBuilder(); + + char command = 0; + for (int i=0; i startCommand && startCommand > -1) { + min = startCommand; + } + } + command=ansiString.charAt(min); + startCommand=min; + + if ( startCommand < ansiString.length()) { + + String colorCodes = ansiString.substring(i+2,startCommand); + String codes[] = colorCodes.split(";"); + + int styleStart = startCommand+1; + int styleEnd = 0; + int endEscape = ansiString.indexOf(ESCAPE,styleStart); + if (endEscape < 0 ) { + styleEnd = ansiString.length(); + i = ansiString.length(); + } else { + int endCommand = ansiString.indexOf(command,endEscape); + if (endCommand < 0 ) { + styleEnd = ansiString.length(); + i = ansiString.length(); + } else { + styleEnd = endEscape; + i = endCommand; + } + } + String styleString = null; + styleString = ansiString.substring(styleStart, styleEnd); + sb.append(styleString); + } + }else { + sb.append(ansiString.charAt(i)); + } + } + return sb.toString(); + } + +} diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/process/PTY.java b/com.samsung.slp.common/src/com/samsung/slp/common/process/PTY.java new file mode 100644 index 0000000..5c248eef0 --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/process/PTY.java @@ -0,0 +1,173 @@ +package com.samsung.slp.common.process; + +/******************************************************************************* + * Copyright (c) 2002, 2010 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + * Wind River Systems, Inc. - bug 248071 + *******************************************************************************/ + +import java.io.IOException; +import com.samsung.slp.common.log.Logger; + +/** + * PTY - pseudo terminal support. + */ +public class PTY { + + final boolean console; + String slave; + PTYInputStream in; + PTYOutputStream out; + /** + * NOTE: Field is accessed by the native layer. Do not refactor! + */ + int master; + + private static boolean hasPTY; + private static boolean setTerminalSizeErrorAlreadyLogged; + + /** + * The master fd is used on two streams. We need to wrap the fd + * so that when stream.close() is called the other stream is disabled. + */ + public class MasterFD { + + public int getFD() { + return master; + } + + void setFD(int fd) { + master = fd; + } + } + + /** + * Create PTY for use with Eclipse console. + * Identical to {@link PTY#PTY(boolean) PTY(true)}. + */ + public PTY() throws IOException { + this(true); + } + + /** + * Create pseudo terminal. + * + *

+ * The provided flag indicates whether the pseudo terminal is used with the interactive + * Eclipse console: + *

    + *
  • If true the terminal is configured with no echo and stderr is + * redirected to a pipe instead of the PTY.
  • + *
  • If false the terminal is configured with echo and stderr is + * connected to the PTY. This mode is best suited for use with a proper terminal emulation. + * Note that this mode might not be supported on all platforms. + * Known platforms which support this mode are: + * linux-x86, linux-x86_64, solaris-sparc, macosx. + *
  • + *
+ *

+ * + * @param console whether terminal is used with Eclipse console + * @throws IOException if the PTY could not be created + * @since 5.2 + */ + public PTY(boolean console) throws IOException { + this.console = console; + + if (hasPTY) { + slave= openMaster(console); + } + + if (slave == null) { + //throw new IOException(CCorePlugin.getResourceString("Util.exception.cannotCreatePty")); //$NON-NLS-1$ + throw new IOException("Util.exception.cannotCreatePty"); //$NON-NLS-1$ + } + + in = new PTYInputStream(new MasterFD()); + out = new PTYOutputStream(new MasterFD()); + } + + public String getSlaveName() { + return slave; + } + + public MasterFD getMasterFD() { + return new MasterFD(); + } + + /** + * @return whether this pseudo terminal is for use with the Eclipse console. + * + * @since 5.2 + */ + public final boolean isConsole() { + return console; + } + + public PTYOutputStream getOutputStream() { + return out; + } + + public PTYInputStream getInputStream() { + return in; + } + + /** + * Change terminal window size to given width and height. + *

+ * This should only be used when the pseudo terminal is configured + * for use with a terminal emulation, i.e. when {@link #isConsole()} + * returns false. + *

+ *

+ * Note: This method may not be supported on all platforms. + * Known platforms which support this method are: + * linux-x86, linux-x86_64, solaris-sparc, macosx. + *

+ * + * @since 5.2 + */ + public final void setTerminalSize(int width, int height) { + try { + change_window_size(master, width, height); + } catch (UnsatisfiedLinkError ule) { + if (!setTerminalSizeErrorAlreadyLogged) { + setTerminalSizeErrorAlreadyLogged = true; + //CCorePlugin.log(CCorePlugin.getResourceString("Util.exception.cannotSetTerminalSize"), ule); //$NON-NLS-1$ + Logger.error(ule.getMessage() , ule); + } + } + } + + /** + * @return whether PTY support is available on this platform + */ + public static boolean isSupported() { + return hasPTY; + } + + native String openMaster(boolean console); + + native int change_window_size(int fdm, int width, int height); + + static { + try { + System.loadLibrary("pty"); //$NON-NLS-1$ + hasPTY = true; + } catch (SecurityException e) { + // Comment out it worries the users too much + //CCorePlugin.log(e); + } catch (UnsatisfiedLinkError e) { + // Comment out it worries the users too much + //CCorePlugin.log(e); + } + } + +} + diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/process/PTYInputStream.java b/com.samsung.slp.common/src/com/samsung/slp/common/process/PTYInputStream.java new file mode 100644 index 0000000..59ef2e2 --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/process/PTYInputStream.java @@ -0,0 +1,96 @@ +package com.samsung.slp.common.process; + +/******************************************************************************* + * Copyright (c) 2000, 2011 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - bug 286162 + *******************************************************************************/ +import java.io.IOException; +import java.io.InputStream; + +import com.samsung.slp.common.process.PTY.MasterFD; + +class PTYInputStream extends InputStream { + + MasterFD master; + + /** + * From a Unix valid file descriptor set a Reader. + * @param fd file descriptor. + */ + public PTYInputStream(MasterFD fd) { + master = fd; + } + + /** + * Implementation of read for the InputStream. + * + * @exception IOException on error. + */ + @Override + public int read() throws IOException { + byte b[] = new byte[1]; + if (1 != read(b, 0, 1)) + return -1; + return b[0]; + } + + /** + * @see InputStream#read(byte[], int, int) + */ + @Override + public int read(byte[] buf, int off, int len) throws IOException { + if (buf == null) { + throw new NullPointerException(); + } else if ((off < 0) || (off > buf.length) + || (len < 0) || ((off + len) > buf.length) + || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + byte[] tmpBuf = new byte[len]; + + len = read0(master.getFD(), tmpBuf, len); + if (len <= 0) + return -1; + + System.arraycopy(tmpBuf, 0, buf, off, len); + return len; + } + + /** + * Close the Reader + * @exception IOException on error. + */ + @Override + public void close() throws IOException { + if (master.getFD() == -1) + return; + close0(master.getFD()); + // ignore error on close - see bug 286162 +// if (status == -1) +// throw new IOException(CCorePlugin.getResourceString("Util.exception.closeError")); //$NON-NLS-1$ + master.setFD(-1); + } + + @Override + protected void finalize() throws IOException { + close(); + } + + private native int read0(int fd, byte[] buf, int len) throws IOException; + private native int close0(int fd) throws IOException; + + static { + System.loadLibrary("pty"); //$NON-NLS-1$ + } + +} + diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/process/PTYOutputStream.java b/com.samsung.slp.common/src/com/samsung/slp/common/process/PTYOutputStream.java new file mode 100644 index 0000000..57ceb35 --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/process/PTYOutputStream.java @@ -0,0 +1,89 @@ +package com.samsung.slp.common.process; +/******************************************************************************* + * Copyright (c) 2000, 2011 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +import java.io.IOException; +import java.io.OutputStream; + +import com.samsung.slp.common.process.PTY.MasterFD; + +public class PTYOutputStream extends OutputStream { + + MasterFD master; + + /** + * From a Unix valid file descriptor set a Reader. + * @param fd file descriptor. + */ + public PTYOutputStream(MasterFD fd) { + master = fd; + } + + /** + * @see OutputStream#write(byte[], int, int) + */ + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if ( + (off < 0) + || (off > b.length) + || (len < 0) + || ((off + len) > b.length) + || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + byte[] tmpBuf = new byte[len]; + System.arraycopy(b, off, tmpBuf, off, len); + write0(master.getFD(), tmpBuf, len); + } + /** + * Implementation of read for the InputStream. + * + * @exception IOException on error. + */ + @Override + public void write(int b) throws IOException { + byte[] buf = new byte[1]; + buf[0] = (byte) b; + write(buf, 0, 1); + } + + /** + * Close the Reader + * @exception IOException on error. + */ + @Override + public void close() throws IOException { + if (master.getFD() == -1) + return; + int status = close0(master.getFD()); + if (status == -1) + throw new IOException("close error"); //$NON-NLS-1$ + master.setFD(-1); + } + + @Override + protected void finalize() throws IOException { + close(); + } + + private native int write0(int fd, byte[] b, int len) throws IOException; + private native int close0(int fd) throws IOException; + + static { + System.loadLibrary("pty"); //$NON-NLS-1$ + } + +} diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/process/ProcessClosure.java b/com.samsung.slp.common/src/com/samsung/slp/common/process/ProcessClosure.java new file mode 100644 index 0000000..61cca31 --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/process/ProcessClosure.java @@ -0,0 +1,236 @@ +package com.samsung.slp.common.process; + +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; + +import com.samsung.slp.common.console.AnsicodeAdapter; + +/** + * Bundled state of a launched process including the threads linking the process + * in/output to console documents. + */ +public class ProcessClosure { + + /** + * Thread which continuously reads from a input stream and pushes the read + * data to an output stream which is immediately flushed afterwards. + */ + protected static class ReaderThread extends Thread { + + private InputStream fInputStream; + private OutputStream fOutputStream; + private boolean fFinished = false; + private String lineSeparator; + /* + * outputStream can be null + */ + public ReaderThread(ThreadGroup group, String name, InputStream in, OutputStream out) { + super(group, name); + fOutputStream = out; + fInputStream = in; + setDaemon(true); + lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$ + } + + @Override + public void run() { + try { + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(fInputStream)); + String line; + while ((line = reader.readLine()) != null) { + line = AnsicodeAdapter.getDefault().getStripAnsiString(line); + line += lineSeparator; + fOutputStream.write(line.getBytes()); + } + } catch (IOException x) { + // ignore + } finally { + try { + // writer.flush(); + fOutputStream.flush(); + } catch (IOException e) { + // ignore + } + try { + fInputStream.close(); + } catch (IOException e) { + // ignore + } + } + } finally { + complete(); + } + } + + public synchronized boolean finished() { + return fFinished; + } + + public synchronized void waitFor() { + while (!fFinished) { + try { + wait(); + } catch (InterruptedException e) { + } + } + } + + public synchronized void complete() { + fFinished = true; + notify(); + } + + public void close() { + try { + fOutputStream.close(); + } catch (IOException e) { + // ignore + } + } + } + + protected static int fCounter = 0; + + protected Process fProcess; + + protected OutputStream fOutput; + protected OutputStream fError; + + protected ReaderThread fOutputReader; + protected ReaderThread fErrorReader; + + /** + * Creates a process closure and connects the launched process with a + * console document. + * + * @param outputStream + * prcess stdout is written to this stream. Can be + * null, if not interested in reading the output + * @param errorStream + * prcess stderr is written to this stream. Can be + * null, if not interested in reading the output + */ + public ProcessClosure(Process process, OutputStream outputStream, OutputStream errorStream) { + fProcess = process; + fOutput = outputStream; + fError = errorStream; + } + + /** + * Live links the launched process with the configured in/out streams using + * reader threads. + */ + public void runNonBlocking() { + ThreadGroup group = new ThreadGroup("SRuncher" + fCounter++); //$NON-NLS-1$ + + InputStream stdin = fProcess.getInputStream(); + InputStream stderr = fProcess.getErrorStream(); + + fOutputReader = new ReaderThread(group, "OutputReader", stdin, fOutput); //$NON-NLS-1$ + fErrorReader = new ReaderThread(group, "ErrorReader", stderr, fError); //$NON-NLS-1$ + + fOutputReader.start(); + fErrorReader.start(); + } + + public void runBlocking() { + runNonBlocking(); + + boolean finished = false; + while (!finished) { + try { + fProcess.waitFor(); + } catch (InterruptedException e) { + //System.err.println("Closure exception " +e); + } + try { + fProcess.exitValue(); + finished = true; + } catch (IllegalThreadStateException e) { + //System.err.println("Closure exception " +e); + } + } + + // @@@FIXME: Windows 2000 is screwed; double-check using output threads + if (!fOutputReader.finished()) { + fOutputReader.waitFor(); + } + + if (!fErrorReader.finished()) { + fErrorReader.waitFor(); + } + + fOutputReader.close(); + fErrorReader.close(); + // it seems that thread termination and stream closing is working + // without + // any help + fProcess = null; + fOutputReader = null; + fErrorReader = null; + } + + public boolean isAlive() { + if (fProcess != null) { + if (fOutputReader.isAlive() || fErrorReader.isAlive()) { + return true; + } + fProcess = null; + fOutputReader.close(); + fErrorReader.close(); + fOutputReader = null; + fErrorReader = null; + } + return false; + } + + /** + * The same functionality as "isAlive()" + * but does not affect out streams, + * because they can be shared among processes + */ + public boolean isRunning() { + if (fProcess != null) { + if (fOutputReader.isAlive() || fErrorReader.isAlive()) { + return true; + } + fProcess = null; + } + return false; + } + /** + * Forces the termination the launched process + */ + public void terminate() { + if (fProcess != null) { + fProcess.destroy(); + fProcess = null; + } + if (!fOutputReader.finished()) { + fOutputReader.waitFor(); + } + if (!fErrorReader.finished()) { + fErrorReader.waitFor(); + } + fOutputReader.close(); + fErrorReader.close(); + fOutputReader = null; + fErrorReader = null; + } +} + diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/process/ProcessFactory.java b/com.samsung.slp.common/src/com/samsung/slp/common/process/ProcessFactory.java new file mode 100644 index 0000000..39428ae --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/process/ProcessFactory.java @@ -0,0 +1,99 @@ +package com.samsung.slp.common.process; + +/******************************************************************************* + * Copyright (c) 2000, 2009 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + + +import java.io.File; +import java.io.IOException; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public class ProcessFactory { + + static private ProcessFactory instance; + private boolean hasSpawner; + private Runtime runtime; + + private ProcessFactory() { + hasSpawner = false; + String OS = System.getProperty("os.name").toLowerCase(); //$NON-NLS-1$ + runtime = Runtime.getRuntime(); + try { + // Spawner does not work for Windows 98 fallback + if (OS != null && OS.equals("windows 98")) { //$NON-NLS-1$ + hasSpawner = false; + } else { + System.loadLibrary("spawner"); //$NON-NLS-1$ + hasSpawner = true; + } + } catch (SecurityException e) { + e.printStackTrace(); + } catch (UnsatisfiedLinkError e) { + e.printStackTrace(); + } + } + + public static ProcessFactory getFactory() { + if (instance == null) + instance = new ProcessFactory(); + return instance; + } + + public Process exec(String cmd) throws IOException { + if (hasSpawner) + return new Spawner(cmd); + return runtime.exec(cmd); + } + + public Process exec(String[] cmdarray) throws IOException { + if (hasSpawner) + return new Spawner(cmdarray); + return runtime.exec(cmdarray); + } + + public Process exec(String[] cmdarray, String[] envp) throws IOException { + if (hasSpawner) + return new Spawner(cmdarray, envp); + return runtime.exec(cmdarray, envp); + } + + public Process exec(String cmd, String[] envp) throws IOException { + if (hasSpawner) + return new Spawner(cmd, envp); + return runtime.exec(cmd, envp); + } + + public Process exec(String cmd, String[] envp, File dir) + throws IOException { + if (hasSpawner) + return new Spawner(cmd, envp, dir); + return runtime.exec(cmd, envp, dir); + } + + public Process exec(String cmdarray[], String[] envp, File dir) + throws IOException { + if (hasSpawner) + return new Spawner(cmdarray, envp, dir); + return runtime.exec(cmdarray, envp, dir); + } + + public Process exec(String cmdarray[], String[] envp, File dir, PTY pty) + throws IOException { + if (hasSpawner) + return new Spawner(cmdarray, envp, dir, pty); + //throw new UnsupportedOperationException(CCorePlugin.getResourceString("Util.exception.cannotCreatePty")); //$NON-NLS-1$ + throw new UnsupportedOperationException("Util.exception.cannotCreatePty"); //$NON-NLS-1$ + } +} + diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/process/ProcessLauncher.java b/com.samsung.slp.common/src/com/samsung/slp/common/process/ProcessLauncher.java new file mode 100644 index 0000000..dab4a47 --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/process/ProcessLauncher.java @@ -0,0 +1,190 @@ +package com.samsung.slp.common.process; + +/******************************************************************************* + * Copyright (c) 2006 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Intel Corporation - Initial API and implementation + *******************************************************************************/ + + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * This class implements external process launching for internal builder. + * + * NOTE: This class is subject to change and discuss, + * and is currently available in experimental mode only + */ +public class ProcessLauncher { + public final static int STATE_DONE = 0; + public final static int STATE_RUNNING = 1; + public final static int STATE_CANCELED = 2; + public final static int STATE_ILLEGAL = -1; + + protected String[] cmd; + protected String[] env; + protected File cwd; + protected OutputStream out; + protected OutputStream err; + protected IProgressMonitor monitor; + protected boolean show; + protected String error; + protected String lineSeparator; + protected Process process; + protected ProcessClosure closure = null; + protected int state; + + /** + * Returns command line as a string array + */ + public String[] getCommandArray() { + return cmd; + } + + /** + * Returns command line in a single string + */ + public String getCommandLine() { + StringBuffer buf = new StringBuffer(); + if (cmd != null) { + for (int i = 0; i < cmd.length; i++) { + buf.append(cmd[i]); + buf.append(' '); + } + buf.append(lineSeparator); + } + + return buf.toString(); + } + + /** + * Returns process environment + */ + public String[] getEnvironment() { + return env; + } + + /** + * Returns command working directory + */ + public File getWorkingDir() { + return cwd; + } + + /** + * Returns error message (if any) + */ + public String getErrorMessage() { + return error; + } + + /** + * Returns exit code of a process + */ + public int getExitCode() { + if (process == null || closure.isAlive()) return 0; + try { return process.waitFor(); } + catch (InterruptedException e) { return 0; } + } + + /** + * Initializes launcher + * @param _cmd Command path + * @param args Command arguments + * @param _env Environment + * @param _cwd Working directory + * @param _out Output stream + * @param _err Error output stream + * @param _monitor Progress monitor + * @param _show If true, print command line before launching + */ + public ProcessLauncher(IPath _cmd, String[] args, String[] _env, IPath _cwd, OutputStream _out, OutputStream _err, IProgressMonitor _monitor, boolean _show) { + cmd = createCmdArray(_cmd.toOSString(), args); + env = _env; + cwd = _cwd.toFile(); + out = _out; + err = _err; + monitor = _monitor; + show = _show; + error = ""; //$NON-NLS-1$ + lineSeparator = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Launches a process + */ + public void launch() { + try { + if (show) + printCommandLine(); + state = STATE_RUNNING; + process = ProcessFactory.getFactory().exec(cmd, env, cwd); + closure = new ProcessClosure(process, out, err); + // Close the input of the process since we will never write to it + try { + process.getOutputStream().close(); + } catch (IOException e) { + // do nothing + } + closure.runNonBlocking(); + } catch (IOException e) { + error = e.getMessage(); + closure = null; + } + } + + /** + * Returns process state + */ + public int queryState() { + if (state == STATE_RUNNING) { + if (closure == null) + state = STATE_ILLEGAL; + else if (monitor.isCanceled()) { + closure.terminate(); + error = "CommandLauncher.error.commandCanceled"; //$NON-NLS-1$ + state = STATE_CANCELED; + } else if (!closure.isRunning()) { + state = STATE_DONE; + } + } + + return state; + } + + /** + * Creates a string array representing the command that will be passed + * to the process + */ + protected String[] createCmdArray(String cmdPath, String[] cmdArgs) { + String[] args = new String[1 + cmdArgs.length]; + args[0] = cmdPath; + System.arraycopy(cmdArgs, 0, args, 1, cmdArgs.length); + + return args; + } + + /** + * Prints command line + */ + protected void printCommandLine() { + if (out != null) { + try { + out.write(getCommandLine().getBytes()); + out.flush(); + } catch (IOException e) { + // do nothing + } + } + } +} diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/process/Spawner.java b/com.samsung.slp.common/src/com/samsung/slp/common/process/Spawner.java new file mode 100644 index 0000000..a920154 --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/process/Spawner.java @@ -0,0 +1,513 @@ +package com.samsung.slp.common.process; + +/******************************************************************************* + * Copyright (c) 2000, 2011 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - bug 248071, bug 286162 + *******************************************************************************/ +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.StringTokenizer; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.osgi.util.NLS; + +import com.samsung.slp.common.log.Logger; + +public class Spawner extends Process { + + public int NOOP = 0; + public int HUP = 1; + public int KILL = 9; + public int TERM = 15; + + /** + * On Windows, what this does is far from easy to explain. + * Some of the logic is in the JNI code, some in the spawner.exe code. + * + *
    + *
  • If the process this is being raised against was launched by us (the Spawner) + *
      + *
    • If the process is a cygwin program (has the cygwin1.dll loaded), then issue a 'kill -SIGINT'. If + * the 'kill' utility isn't available, send the process a CTRL-C + *
    • If the process is not a cygwin program, send the process a CTRL-C + *
    + *
  • If the process this is being raised against was not launched by us, use + * DebugBreakProcess to interrupt it (sending a CTRL-C is easy only if we share a console + * with the target process) + *
+ * + * On non-Windows, raising this just raises a POSIX SIGINT + * + */ + public int INT = 2; + + /** + * A fabricated signal number for use on Windows only. Tells the starter program to send a CTRL-C + * regardless of whether the process is a Cygwin one or not. + * + * @since 5.2 + */ + public int CTRLC = 1000; // arbitrary high number to avoid collision + + int pid = 0; + int status; + final int[] fChannels = new int[3]; + boolean isDone; + OutputStream out; + InputStream in; + InputStream err; + private PTY fPty; + + public Spawner(String command, boolean bNoRedirect) throws IOException { + StringTokenizer tokenizer = new StringTokenizer(command); + String[] cmdarray = new String[tokenizer.countTokens()]; + for (int n = 0; tokenizer.hasMoreTokens(); n++) + cmdarray[n] = tokenizer.nextToken(); + if (bNoRedirect) + exec_detached(cmdarray, new String[0], "."); //$NON-NLS-1$ + else + exec(cmdarray, new String[0], "."); //$NON-NLS-1$ + } + /** + * Executes the specified command and arguments in a separate process with the + * specified environment and working directory. + **/ + protected Spawner(String[] cmdarray, String[] envp, File dir) throws IOException { + String dirpath = "."; //$NON-NLS-1$ + if (dir != null) + dirpath = dir.getAbsolutePath(); + exec(cmdarray, envp, dirpath); + } + + protected Spawner(String[] cmdarray, String[] envp, File dir, PTY pty) throws IOException { + String dirpath = "."; //$NON-NLS-1$ + if (dir != null) + dirpath = dir.getAbsolutePath(); + fPty = pty; + exec_pty(cmdarray, envp, dirpath, pty); + } + /** + * Executes the specified string command in a separate process. + **/ + protected Spawner(String command) throws IOException { + this(command, null); + } + + /** + * Executes the specified command and arguments in a separate process. + **/ + protected Spawner(String[] cmdarray) throws IOException { + this(cmdarray, null); + } + + /** + * Executes the specified command and arguments in a separate process with the + * specified environment. + **/ + protected Spawner(String[] cmdarray, String[] envp) throws IOException { + this(cmdarray, envp, null); + } + + /** + * Executes the specified string command in a separate process with the specified + * environment. + **/ + protected Spawner(String cmd, String[] envp) throws IOException { + this(cmd, envp, null); + } + + /** + * Executes the specified string command in a separate process with the specified + * environment and working directory. + **/ + protected Spawner(String command, String[] envp, File dir) throws IOException { + StringTokenizer tokenizer = new StringTokenizer(command); + String[] cmdarray = new String[tokenizer.countTokens()]; + for (int n = 0; tokenizer.hasMoreTokens(); n++) + cmdarray[n] = tokenizer.nextToken(); + String dirpath = "."; //$NON-NLS-1$ + if (dir != null) + dirpath = dir.getAbsolutePath(); + exec(cmdarray, envp, dirpath); + } + + @Override + protected void finalize() throws Throwable { + closeUnusedStreams(); + } + + /** + * See java.lang.Process#getInputStream (); + * The client is responsible for closing the stream explicitly. + **/ + @Override + public synchronized InputStream getInputStream() { + if(null == in) { + if (fPty != null) { + in = fPty.getInputStream(); + } else { + in = new SpawnerInputStream(fChannels[1]); + } + } + return in; + } + + /** + * See java.lang.Process#getOutputStream (); + * The client is responsible for closing the stream explicitly. + **/ + @Override + public synchronized OutputStream getOutputStream() { + if(null == out) { + if (fPty != null) { + out = fPty.getOutputStream(); + } else { + out = new SpawnerOutputStream(fChannels[0]); + } + } + return out; + } + + /** + * See java.lang.Process#getErrorStream (); + * The client is responsible for closing the stream explicitly. + **/ + @Override + public synchronized InputStream getErrorStream() { + if(null == err) { + if (fPty != null && !fPty.isConsole()) { + // If PTY is used and it's not in "Console" mode, then stderr is + // redirected to the PTY's output stream. Therefore, return a + // dummy stream for error stream. + err = new InputStream() { + @Override + public int read() throws IOException { + return -1; + } + }; + } else { + err = new SpawnerInputStream(fChannels[2]); + } + } + return err; + } + + /** + * See java.lang.Process#waitFor (); + **/ + @Override + public synchronized int waitFor() throws InterruptedException { + while (!isDone) { + wait(); + } + + // For situations where the user does not call destroy(), + // we try to kill the streams that were not used here. + // We check for streams that were not created, we create + // them to attach to the pipes, and then we close them + // to release the pipes. + // Streams that were created by the client need to be + // closed by the client itself. + // + // But 345164 + closeUnusedStreams(); + return status; + } + + /** + * See java.lang.Process#exitValue (); + **/ + @Override + public synchronized int exitValue() { + if (!isDone) { + throw new IllegalThreadStateException("Process not Terminated"); //$NON-NLS-1$ + } + return status; + } + + /** + * See java.lang.Process#destroy (); + * + * Clients are responsible for explicitly closing any streams + * that they have requested through + * getErrorStream(), getInputStream() or getOutputStream() + **/ + @Override + public synchronized void destroy() { + // Sends the TERM + terminate(); + + // Close the streams on this side. + // + // We only close the streams that were + // never used by any client. + // So, if the stream was not created yet, + // we create it ourselves and close it + // right away, so as to release the pipe. + // Note that even if the stream was never + // created, the pipe has been allocated in + // native code, so we need to create the + // stream and explicitly close it. + // + // We don't close streams the clients have + // created because we don't know when the + // client will be finished using them. + // It is up to the client to close those + // streams. + // + // But 345164 + closeUnusedStreams(); + + // Grace before using the heavy gone. + if (!isDone) { + try { + wait(1000); + } catch (InterruptedException e) { + } + } + if (!isDone) { + kill(); + } + } + + /** + * On Windows, interrupt the spawned program by using Cygwin's utility 'kill -SIGINT' if it's a Cgywin + * program, otherwise send it a CTRL-C. If Cygwin's 'kill' command is not available, send a CTRL-C. On + * linux, interrupt it by raising a SIGINT. + */ + public int interrupt() { + return raise(pid, INT); + } + + /** + * On Windows, interrupt the spawned program by send it a CTRL-C (even if it's a Cygwin program). On + * linux, interrupt it by raising a SIGINT. + * + * @since 5.2 + */ + public int interruptCTRLC() { + if (Platform.getOS().equals(Platform.OS_WIN32)) { + return raise(pid, CTRLC); + } + else { + return interrupt(); + } + } + + public int hangup() { + return raise(pid, HUP); + } + + public int kill() { + return raise(pid, KILL); + } + + public int terminate() { + return raise(pid, TERM); + } + + public boolean isRunning() { + return (raise(pid, NOOP) == 0); + } + + private void exec(String[] cmdarray, String[] envp, String dirpath) throws IOException { + String command = cmdarray[0]; + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkExec(command); + if (envp == null) + envp = new String[0]; + + Reaper reaper = new Reaper(cmdarray, envp, dirpath); + reaper.setDaemon(true); + reaper.start(); + + // Wait until the subprocess is started or error. + synchronized (this) { + while (pid == 0) { + try { + wait(); + } catch (InterruptedException e) { + } + } + } + + // Check for errors. + if (pid == -1) { + throw new IOException(reaper.getErrorMessage()); + } + } + + private void exec_pty(String[] cmdarray, String[] envp, String dirpath, PTY pty) throws IOException { + String command = cmdarray[0]; + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkExec(command); + if (envp == null) + envp = new String[0]; + + final String slaveName = pty.getSlaveName(); + final int masterFD = pty.getMasterFD().getFD(); + final boolean console = pty.isConsole(); + //int fdm = pty.get + Reaper reaper = new Reaper(cmdarray, envp, dirpath) { + /* (non-Javadoc) + * @see org.eclipse.cdt.utils.spawner.Spawner.Reaper#execute(java.lang.String[], java.lang.String[], java.lang.String, int[]) + */ + @Override + int execute(String[] cmd, String[] env, String dir, int[] channels) throws IOException { + return exec2(cmd, env, dir, channels, slaveName, masterFD, console); + } + }; + reaper.setDaemon(true); + reaper.start(); + + // Wait until the subprocess is started or error. + synchronized (this) { + while (pid == 0) { + try { + wait(); + } catch (InterruptedException e) { + } + } + } + + // Check for errors. + if (pid == -1) { + throw new IOException("Exec_tty error:" + reaper.getErrorMessage()); //$NON-NLS-1$ + } + } + + public void exec_detached(String[] cmdarray, String[] envp, String dirpath) throws IOException { + String command = cmdarray[0]; + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkExec(command); + + if (envp == null) + envp = new String[0]; + pid = exec1(cmdarray, envp, dirpath); + if (pid == -1) { + throw new IOException("Exec error"); //$NON-NLS-1$ + } + fChannels[0] = -1; + fChannels[1] = -1; + fChannels[2] = -1; + } + + /** + * Close any streams not used by clients. + */ + private synchronized void closeUnusedStreams() { + try { + if(null == err) + getErrorStream().close(); + } catch (IOException e) {} + try { + if(null == in) + getInputStream().close(); + } catch (IOException e) {} + try { + if(null == out) + getOutputStream().close(); + } catch (IOException e) {} + } + + /** + * Native method use in normal exec() calls. + */ + native int exec0( String[] cmdarray, String[] envp, String dir, int[] chan) throws IOException; + + /** + * Native method use in no redirect meaning to streams will created. + */ + native int exec1( String[] cmdarray, String[] envp, String dir) throws IOException; + + /** + * Native method when executing with a terminal emulation. + */ + native int exec2( String[] cmdarray, String[] envp, String dir, int[] chan, String slaveName, int masterFD, boolean console) throws IOException; + + /** + * Native method to drop a signal on the process with pid. + */ + public native int raise(int processID, int sig); + + /* + * Native method to wait(3) for process to terminate. + */ + native int waitFor(int processID); + + static { + try { + System.loadLibrary("spawner"); //$NON-NLS-1$ + } catch (SecurityException e) { + Logger.error(e.getMessage() , e); + } catch (UnsatisfiedLinkError e) { + Logger.error(e.getMessage() , e); + } + } + + // Spawn a thread to handle the forking and waiting + // We do it this way because on linux the SIGCHLD is + // send to the one thread. So do the forking and + // the wait in the same thread. + class Reaper extends Thread { + String[] fCmdarray; + String[] fEnvp; + String fDirpath; + volatile Throwable fException; + + public Reaper(String[] array, String[] env, String dir) { + super("Spawner Reaper"); //$NON-NLS-1$ + fCmdarray = array; + fEnvp = env; + fDirpath = dir; + fException = null; + } + + int execute(String[] cmdarray, String[] envp, String dir, int[] channels) throws IOException { + return exec0(cmdarray, envp, dir, channels); + } + + @Override + public void run() { + try { + pid = execute(fCmdarray, fEnvp, fDirpath, fChannels); + } catch (Exception e) { + pid = -1; + fException= e; + } + + // Tell spawner that the process started. + synchronized (Spawner.this) { + Spawner.this.notifyAll(); + } + + if (pid != -1) { + // Sync with spawner and notify when done. + status = waitFor(pid); + synchronized (Spawner.this) { + isDone = true; + Spawner.this.notifyAll(); + } + } + } + + public String getErrorMessage() { + final String reason= fException != null ? fException.getMessage() : "Unknown reason"; //$NON-NLS-1$ + //return NLS.bind(CCorePlugin.getResourceString("Util.error.cannotRun"), fCmdarray[0], reason); //$NON-NLS-1$ + return "Util.error.cannotRun"; + } + } +} + diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/process/SpawnerInputStream.java b/com.samsung.slp.common/src/com/samsung/slp/common/process/SpawnerInputStream.java new file mode 100644 index 0000000..80d89fc --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/process/SpawnerInputStream.java @@ -0,0 +1,114 @@ +package com.samsung.slp.common.process; +/******************************************************************************* + *Copyright (c) 2000, 2011 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + + + +import java.io.IOException; +import java.io.InputStream; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + */ +class SpawnerInputStream extends InputStream { + private int fd; + + /** + * From a Unix valid file descriptor set a Reader. + * @param fd file descriptor. + */ + public SpawnerInputStream(int fd) { + this.fd = fd; + } + + /** + * Implementation of read for the InputStream. + * + * @exception IOException on error. + */ + @Override + public int read() throws IOException { + byte b[] = new byte[1]; + if (1 != read(b, 0, 1)) + return -1; + return b[0]; + } + + /** + * @see InputStream#read(byte[], int, int) + */ + @Override + public int read(byte[] buf, int off, int len) throws IOException { + if (buf == null) { + throw new NullPointerException(); + } else if ( + (off < 0) + || (off > buf.length) + || (len < 0) + || ((off + len) > buf.length) + || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + byte[] tmpBuf = off > 0 ? new byte[len] : buf; + + len = read0(fd, tmpBuf, len); + if (len <= 0) + return -1; + + if (tmpBuf != buf) { + System.arraycopy(tmpBuf, 0, buf, off, len); + } + return len; + } + + /** + * Close the Reader + * @exception IOException on error. + */ + @Override + public void close() throws IOException { + if (fd == -1) + return; + int status = close0(fd); + if (status == -1) + throw new IOException("Util.exception.closeError"); //$NON-NLS-1$ + fd = -1; + } + + @Override + public int available() throws IOException { + try { + return available0(fd); + } + catch (UnsatisfiedLinkError e) { + // for those platforms that do not implement available0 + return super.available(); + } + } + + @Override + protected void finalize() throws IOException { + close(); + } + + private native int read0(int fileDesc, byte[] buf, int len) throws IOException; + private native int close0(int fileDesc) throws IOException; + private native int available0(int fileDesc) throws IOException; + + static { + System.loadLibrary("spawner"); //$NON-NLS-1$ + } + + +} diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/process/SpawnerOutputStream.java b/com.samsung.slp.common/src/com/samsung/slp/common/process/SpawnerOutputStream.java new file mode 100644 index 0000000..8a0bbb0 --- /dev/null +++ b/com.samsung.slp.common/src/com/samsung/slp/common/process/SpawnerOutputStream.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package com.samsung.slp.common.process; + + +import java.io.IOException; +import java.io.OutputStream; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public class SpawnerOutputStream extends OutputStream { + private int fd; + + /** + * From a Unix valid file descriptor set a Reader. + * @param fd file descriptor. + */ + public SpawnerOutputStream(int fd) { + this.fd = fd; + } + + /** + * @see OutputStream#write(byte[], int, int) + */ + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if ( + (off < 0) + || (off > b.length) + || (len < 0) + || ((off + len) > b.length) + || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + byte[] tmpBuf = new byte[len]; + System.arraycopy(b, off, tmpBuf, off, len); + write0(fd, tmpBuf, len); + } + /** + * Implementation of read for the InputStream. + * + * @exception IOException on error. + */ + @Override + public void write(int b) throws IOException { + byte[] buf = new byte[1]; + buf[0] = (byte) b; + write(buf, 0, 1); + } + + /** + * Close the Reader + * @exception IOException on error. + */ + @Override + public void close() throws IOException { + if (fd == -1) + return; + int status = close0(fd); + if (status == -1) + throw new IOException("close error"); //$NON-NLS-1$ + fd = -1; + } + + @Override + protected void finalize() throws IOException { + close(); + } + + private native int write0(int fd, byte[] b, int len) throws IOException; + private native int close0(int fd); + + static { + System.loadLibrary("spawner"); //$NON-NLS-1$ + } + +} diff --git a/com.samsung.slp.common/src/com/samsung/slp/common/util/ConsoleManager.java b/com.samsung.slp.common/src/com/samsung/slp/common/util/ConsoleManager.java index f23cf9a..0e59a30 100644 --- a/com.samsung.slp.common/src/com/samsung/slp/common/util/ConsoleManager.java +++ b/com.samsung.slp.common/src/com/samsung/slp/common/util/ConsoleManager.java @@ -33,12 +33,14 @@ import org.eclipse.ui.console.IConsoleView; import org.eclipse.ui.console.MessageConsole; import org.eclipse.ui.console.MessageConsoleStream; +import com.samsung.slp.common.process.ProcessClosure; + public class ConsoleManager { private static IConsoleManager consoleManager = ConsolePlugin.getDefault().getConsoleManager(); private String consoleName; private boolean consoleFocus; - + private MessageConsole console; /** * Constructs a new console manager. * @@ -50,9 +52,8 @@ public class ConsoleManager this.consoleFocus = focus; } - private MessageConsole getConsole(){ + private MessageConsole getMessageConsole(){ boolean found = false; - MessageConsole console = null; IConsole[] consoles = consoleManager.getConsoles(); for (int i = 0; i < consoles.length; i++) { @@ -73,6 +74,9 @@ public class ConsoleManager return console; } + public IConsole getConsole() { + return console; + } /** * Remove a MessageConsole instance specified key title from list, if it is present * @@ -99,7 +103,7 @@ public class ConsoleManager * @return the MessageConsoleStream connected to MessageConsole */ public MessageConsoleStream getMessageConsoleStream(boolean isError) { - final MessageConsoleStream output = getConsole().newMessageStream(); + final MessageConsoleStream output = getMessageConsole().newMessageStream(); output.setActivateOnWrite(false); final int colorId; @@ -127,12 +131,12 @@ public class ConsoleManager * @return the MessageConsoleStream connected to MessageConsole */ public MessageConsoleStream getMessageConsoleStream() { - return getConsole().newMessageStream(); + return getMessageConsole().newMessageStream(); } public void print(final String line) { Runnable runnable = new Runnable() { public void run() { - final MessageConsoleStream output = getConsole().newMessageStream(); + final MessageConsoleStream output = getMessageConsole().newMessageStream(); output.print(line); try { output.close(); @@ -146,7 +150,7 @@ public class ConsoleManager } public void println(String line) { - final MessageConsoleStream output = getConsole().newMessageStream(); + final MessageConsoleStream output = getMessageConsole().newMessageStream(); output.println(line); try { output.close(); @@ -155,11 +159,29 @@ public class ConsoleManager ConsolePlugin.log(e); } } + public void printlnStreams(Process process) { + + MessageConsoleStream stdoutStream = getMessageConsole().newMessageStream(); + MessageConsoleStream stderrStream = getMessageConsole().newMessageStream(); + + clear(); + // waitAndRead while process is running + ProcessClosure closure = new ProcessClosure(process, stdoutStream, stderrStream); + closure.runBlocking(); // a blocking cal + + try { + stdoutStream.close(); + stderrStream.close(); + } + catch( IOException e ) { + // ignore. + } + } public void clear() { Runnable runnable = new Runnable() { public void run() { - IDocument document = getConsole().getDocument(); + IDocument document = getMessageConsole().getDocument(); if (document != null) { document.set(""); }