Cleanup: use consistent names for projects and packages.
diff --git a/com.google.eclipse.elt.emulator/.classpath b/com.google.eclipse.elt.emulator/.classpath
new file mode 100644
index 0000000..deb6736
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.emulator/.gitignore b/com.google.eclipse.elt.emulator/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/com.google.eclipse.elt.emulator/.project b/com.google.eclipse.elt.emulator/.project
new file mode 100644
index 0000000..f17f0b6
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.eclipse.elt.emulator</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.emulator/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.emulator/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..468cbfa
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,14 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
diff --git a/com.google.eclipse.elt.emulator/META-INF/MANIFEST.MF b/com.google.eclipse.elt.emulator/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..b27807c
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.google.eclipse.elt.emulator;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Activator: com.google.eclipse.elt.emulator.impl.TerminalPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.ui,
+ org.eclipse.jface.text
+Bundle-ActivationPolicy: lazy
+Eclipse-LazyStart: true
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ClassPath: .
+Export-Package: com.google.eclipse.elt.emulator.actions;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.connector;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.control;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.core;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.provisional.api;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.provisional.api.provider;x-friends:="com.google.eclipse.elt.view"
diff --git a/com.google.eclipse.elt.emulator/build.properties b/com.google.eclipse.elt.emulator/build.properties
new file mode 100644
index 0000000..c46bda7
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/build.properties
@@ -0,0 +1,26 @@
+###############################################################################
+# Copyright (c) 2003, 2011 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Initial Contributors:
+# The following Wind River employees contributed to the Terminal component
+# that contains this file: Chris Thew, Fran Litterio, Stephen Lamb,
+# Helmut Haigermoser and Ted Williams.
+#
+# Contributors:
+# Michael Scharf (Wind River) - split into core, view and connector plugins 
+# Martin Oberhuber (Wind River) - fixed copyright headers and beautified
+# Anna Dushistova (MontaVista) - added icons
+###############################################################################
+bin.includes = .,\
+               META-INF/,\
+               plugin.xml,\
+               plugin.properties,\
+               icons/
+source.. = src/
+output.. = bin/
+javacSource=1.6
+javacTarget=1.6
diff --git a/com.google.eclipse.elt.emulator/icons/clcl16/clear_co.gif b/com.google.eclipse.elt.emulator/icons/clcl16/clear_co.gif
new file mode 100644
index 0000000..af30a42
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/icons/clcl16/clear_co.gif
Binary files differ
diff --git a/com.google.eclipse.elt.emulator/icons/dlcl16/clear_co.gif b/com.google.eclipse.elt.emulator/icons/dlcl16/clear_co.gif
new file mode 100644
index 0000000..6775edf
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/icons/dlcl16/clear_co.gif
Binary files differ
diff --git a/com.google.eclipse.elt.emulator/icons/elcl16/clear_co.gif b/com.google.eclipse.elt.emulator/icons/elcl16/clear_co.gif
new file mode 100644
index 0000000..af30a42
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/icons/elcl16/clear_co.gif
Binary files differ
diff --git a/com.google.eclipse.elt.emulator/plugin.properties b/com.google.eclipse.elt.emulator/plugin.properties
new file mode 100644
index 0000000..26ccbac
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/plugin.properties
@@ -0,0 +1,28 @@
+###############################################################################
+# Copyright (c) 2003, 2011 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Initial Contributors:
+# The following Wind River employees contributed to the Terminal component
+# that contains this file: Chris Thew, Fran Litterio, Stephen Lamb,
+# Helmut Haigermoser and Ted Williams.
+#
+# Contributors:
+# Michael Scharf (Wind River) - split into core, view and connector plugins 
+# Martin Oberhuber (Wind River) - fixed copyright headers and beautified
+###############################################################################
+
+# NLS_MESSAGEFORMAT_NONE
+
+pluginName = Target Management Terminal Widget
+providerName = Eclipse TM Project
+
+terminal.context.name=Terminal Widget
+terminal.context.description=Override ALT+x menu access keys
+
+terminal.insertion.description=Terminal view insertion
+terminal.insertion.name=Terminal view insert
+terminal.insertion.category.name=Terminal Widget
diff --git a/com.google.eclipse.elt.emulator/plugin.xml b/com.google.eclipse.elt.emulator/plugin.xml
new file mode 100644
index 0000000..c7125f8
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/plugin.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<!--
+# Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse 
+# Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<plugin>
+  <extension point="org.eclipse.ui.contexts">
+    <context
+      name="%terminal.context.name"
+      description="%terminal.context.description"
+      id="com.google.eclipse.elt.emulator.TerminalContext" />
+  </extension>
+  <extension point="org.eclipse.ui.commands">
+    <!-- Dummy commands for bindings, see below -->
+    <command
+      categoryId="com.google.eclipse.elt.emulator.category1"
+      id="com.google.eclipse.elt.emulator.command1"
+      name="%terminal.insertion.name" />
+    <category
+      description="%terminal.view.insertion.description"
+      id="com.google.eclipse.elt.emulator.category1"
+      name="%terminal.insertion.category.name" />
+  </extension>
+  <extension point="org.eclipse.ui.bindings">
+    <!-- 
+    These keybindings are needed to disable the menu-activation keys (e.g., Alt-F for the File menu, etc.). The code 
+    in method TerminalControl.TerminalFocusListener.focusGained() disables the Eclipse key binding service, but it 
+    doesn't disable the global menu-activation keys. 
+    -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+A" /> <!-- Search -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+E" /> <!-- Edit -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+F" /> <!-- File -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+G" />
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+H" /> <!-- Help -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+N" /> <!-- Navigate -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+P" /> <!-- Project -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+R" /> <!-- Run -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+S" /> <!-- Source -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+T" /> <!-- Refactor -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+V" /> <!-- CVS -->
+    <key
+      commandId="com.google.eclipse.elt.emulator.command1"
+      contextId="com.google.eclipse.elt.emulator.TerminalContext"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="Alt+W" /> <!-- Window -->
+  </extension>
+</plugin>
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/AbstractTerminalAction.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/AbstractTerminalAction.java
new file mode 100644
index 0000000..65fb0be
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/AbstractTerminalAction.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.resource.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+import com.google.eclipse.elt.emulator.impl.TerminalPlugin;
+
+public abstract class AbstractTerminalAction extends Action {
+  private final ITerminalViewControl target;
+
+  public AbstractTerminalAction(String id) {
+    this(null, id, 0);
+  }
+
+  public AbstractTerminalAction(ITerminalViewControl target, String id) {
+    this(target, id, 0);
+  }
+
+  public AbstractTerminalAction(ITerminalViewControl target, String id, int style) {
+    super("", style);
+    this.target = target;
+    setId(id);
+  }
+
+  @Override public abstract void run();
+
+  protected void setUpAction(
+      String text, String toolTip, String image, String enabledImage, String disabledImage, boolean enabled) {
+    setUpAction(
+        text, toolTip, image, enabledImage, disabledImage, enabled, TerminalPlugin.getDefault().getImageRegistry());
+  }
+
+  protected void setUpAction(
+      String text, String toolTip, String hoverImage, String enabledImage, String disabledImage, boolean enabled,
+      ImageRegistry imageRegistry) {
+    setUpAction(
+        text, toolTip, imageRegistry.getDescriptor(hoverImage), imageRegistry.getDescriptor(enabledImage),
+        imageRegistry.getDescriptor(disabledImage), enabled);
+  }
+
+  protected void setUpAction(
+      String text, String toolTip, ImageDescriptor hoverImage, ImageDescriptor enabledImage,
+      ImageDescriptor disabledImage, boolean enabled) {
+    setText(text);
+    setToolTipText(toolTip);
+    setEnabled(enabled);
+    if (enabledImage != null) {
+      setImageDescriptor(enabledImage);
+    }
+    if (disabledImage != null) {
+      setDisabledImageDescriptor(disabledImage);
+    }
+    if (hoverImage != null) {
+      setHoverImageDescriptor(hoverImage);
+    }
+  }
+
+  /**
+   * Returns the terminal instance on which the action should operate.
+   *
+   * @return the terminal instance on which the action should operate.
+   */
+  protected ITerminalViewControl getTarget() {
+    return target;
+  }
+
+  /**
+   * Subclasses can update their action.
+   *
+   * @param aboutToShow {@code true} before the menu is shown, {@code false} when the menu gets hidden.
+   */
+  public void updateAction(boolean aboutToShow) {}
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.java
new file mode 100644
index 0000000..5db6675
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import org.eclipse.osgi.util.NLS;
+
+public class ActionMessages extends NLS {
+  public static String copy;
+  public static String cut;
+  public static String paste;
+  public static String selectAll;
+  public static String clearAll;
+
+  static {
+    Class<?> clazz = ActionMessages.class;
+    NLS.initializeMessages(clazz.getName(), clazz);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.properties b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.properties
new file mode 100644
index 0000000..881315b
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2003, 2009 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+# NLS_MESSAGEFORMAT_NONE
+
+copy = Copy
+cut = Cut
+paste = Paste
+selectAll = Select All
+clearAll = Clear Terminal
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/Images.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/Images.java
new file mode 100644
index 0000000..f5e1961
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/Images.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+public interface Images {
+  String IMAGE_DIR_ROOT = "icons/";
+  String IMAGE_DIR_LOCALTOOL = IMAGE_DIR_ROOT + "clcl16/";
+  String IMAGE_DIR_DLCL = IMAGE_DIR_ROOT + "dlcl16/";
+  String IMAGE_DIR_ELCL = IMAGE_DIR_ROOT + "elcl16/";
+  String IMAGE_CLCL_CLEAR_ALL = "ImageClclClearAll";
+  String IMAGE_DLCL_CLEAR_ALL = "ImageDlclClearAll";
+  String IMAGE_ELCL_CLEAR_ALL = "ImageElclClearAll";
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionClearAll.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionClearAll.java
new file mode 100644
index 0000000..983ec85
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionClearAll.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ ********************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.clearAll;
+import static com.google.eclipse.elt.emulator.actions.Images.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionClearAll extends AbstractTerminalAction {
+  public TerminalActionClearAll() {
+    super(TerminalActionClearAll.class.getName());
+    setupAction();
+  }
+
+  public TerminalActionClearAll(ITerminalViewControl target) {
+    super(target, TerminalActionClearAll.class.getName());
+    setupAction();
+  }
+
+  private void setupAction() {
+    setUpAction(clearAll, clearAll, IMAGE_CLCL_CLEAR_ALL, IMAGE_ELCL_CLEAR_ALL, IMAGE_DLCL_CLEAR_ALL, false);
+  }
+
+  @Override public void run() {
+    ITerminalViewControl target = getTarget();
+    if (target != null) {
+      target.clearTerminal();
+    }
+  }
+
+  @Override public void updateAction(boolean aboutToShow) {
+    ITerminalViewControl target = getTarget();
+    setEnabled(target != null && !target.isEmpty());
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCopy.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCopy.java
new file mode 100644
index 0000000..f4664bc
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCopy.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static org.eclipse.ui.ISharedImages.*;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.copy;
+
+import org.eclipse.ui.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionCopy extends AbstractTerminalAction {
+  public TerminalActionCopy() {
+    super(TerminalActionCopy.class.getName());
+    setUpAction();
+  }
+
+  public TerminalActionCopy(ITerminalViewControl target) {
+    super(target, TerminalActionCopy.class.getName());
+    setUpAction();
+  }
+
+  private void setUpAction() {
+    ISharedImages shared = PlatformUI.getWorkbench().getSharedImages();
+    setUpAction(
+        copy, copy, shared.getImageDescriptor(IMG_TOOL_COPY), shared.getImageDescriptor(IMG_TOOL_COPY),
+        shared.getImageDescriptor(IMG_TOOL_COPY_DISABLED), true);
+  }
+
+  @Override public void run() {
+    ITerminalViewControl target = getTarget();
+    if (target != null) {
+      String selection = target.getSelection();
+      if (!selection.isEmpty()) {
+        target.copy();
+      } else {
+        target.sendKey('\u0003');
+      }
+    }
+  }
+
+  @Override public void updateAction(boolean aboutToShow) {
+    ITerminalViewControl target = getTarget();
+    boolean enabled = target != null;
+    if (aboutToShow && enabled) {
+      enabled = target.getSelection().length() > 0;
+    }
+    setEnabled(enabled);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCut.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCut.java
new file mode 100644
index 0000000..f1559c8
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCut.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static org.eclipse.ui.ISharedImages.*;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.cut;
+
+import org.eclipse.ui.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionCut extends AbstractTerminalAction {
+  public TerminalActionCut() {
+    super(TerminalActionCut.class.getName());
+    setUpAction();
+  }
+
+  public TerminalActionCut(ITerminalViewControl target) {
+    super(target, TerminalActionCut.class.getName());
+    setUpAction();
+  }
+
+  private void setUpAction() {
+    ISharedImages shared = PlatformUI.getWorkbench().getSharedImages();
+    setUpAction(
+        cut, cut, shared.getImageDescriptor(IMG_TOOL_CUT), shared.getImageDescriptor(IMG_TOOL_CUT),
+        shared.getImageDescriptor(IMG_TOOL_CUT_DISABLED), true);
+  }
+
+  @Override public void run() {
+    ITerminalViewControl target = getTarget();
+    if (target != null) {
+      target.sendKey('\u0018');
+    }
+  }
+
+  @Override public void updateAction(boolean aboutToShow) {
+    // Cut is always disabled
+    setEnabled(false);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionPaste.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionPaste.java
new file mode 100644
index 0000000..0303f5c
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionPaste.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Initial Contributors:
+ * The following Wind River employees contributed to the Terminal component
+ * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb,
+ * Helmut Haigermoser and Ted Williams.
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - split into core, view and connector plugins
+ * Martin Oberhuber (Wind River) - fixed copyright headers and beautified
+ * Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin
+ * Uwe Stieber (Wind River) - [260372] [terminal] Certain terminal actions are enabled if no target terminal control is available
+ * Uwe Stieber (Wind River) - [294719] [terminal] SWT Widget disposed in TerminalActionPaste
+ * Martin Oberhuber (Wind River) - [296212] Cannot paste text into terminal on some Linux hosts
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static org.eclipse.ui.ISharedImages.*;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.paste;
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.CONNECTED;
+
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.ui.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionPaste extends AbstractTerminalAction {
+  public TerminalActionPaste() {
+    super(TerminalActionPaste.class.getName());
+    setUpAction();
+  }
+
+  public TerminalActionPaste(ITerminalViewControl target) {
+    super(target, TerminalActionPaste.class.getName());
+    setUpAction();
+  }
+
+  private void setUpAction() {
+    ISharedImages shared = PlatformUI.getWorkbench().getSharedImages();
+    setUpAction(
+        paste, paste, shared.getImageDescriptor(IMG_TOOL_PASTE), shared.getImageDescriptor(IMG_TOOL_PASTE_DISABLED),
+        shared.getImageDescriptor(IMG_TOOL_PASTE), false);
+  }
+
+  @Override public void run() {
+    ITerminalViewControl target = getTarget();
+    if (target != null) {
+      target.paste();
+    }
+  }
+
+  @Override public void updateAction(boolean aboutToShow) {
+    ITerminalViewControl target = getTarget();
+    boolean enabled = target != null && target.getClipboard() != null && !target.getClipboard().isDisposed();
+    if (enabled) {
+      String text = (String) target.getClipboard().getContents(TextTransfer.getInstance());
+      enabled = text != null && !text.isEmpty() && target.getState() == CONNECTED;
+    }
+    setEnabled(enabled);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionSelectAll.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionSelectAll.java
new file mode 100644
index 0000000..f52d742
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionSelectAll.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.selectAll;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionSelectAll extends AbstractTerminalAction {
+  public TerminalActionSelectAll() {
+    super(TerminalActionSelectAll.class.getName());
+    setUpAction();
+  }
+
+  public TerminalActionSelectAll(ITerminalViewControl target) {
+    super(target, TerminalActionSelectAll.class.getName());
+    setUpAction();
+  }
+
+  private void setUpAction() {
+    setUpAction(selectAll, selectAll, (ImageDescriptor) null, null, null, false);
+  }
+
+  @Override public void run() {
+    ITerminalViewControl target = getTarget();
+    if (target != null) {
+      target.selectAll();
+    }
+  }
+
+  @Override public void updateAction(boolean aboutToShow) {
+    ITerminalViewControl target = getTarget();
+    setEnabled(target != null && !target.isEmpty());
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalConnector.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalConnector.java
new file mode 100644
index 0000000..ae5b7f1
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalConnector.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ * Michael Scharf (Wind River) - [200541] Extract from TerminalConnectorExtension.TerminalConnectorProxy
+ * Martin Oberhuber (Wind River) - [225853][api] Provide more default functionality in TerminalConnectorImpl
+ * Uwe Stieber (Wind River) - [282996] [terminal][api] Add "hidden" attribute to terminal connector extension point
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.connector;
+
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.CLOSED;
+
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.*;
+
+import com.google.eclipse.elt.emulator.impl.TerminalMessages;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+import com.google.eclipse.elt.emulator.provisional.api.provider.TerminalConnectorDelegate;
+
+/**
+ * An {@link ITerminalConnector} instance, also known as terminal connection type, for maintaining a single terminal
+ * connection.
+ *
+ * It provides all terminal connector functions that can be provided by static markup without loading the actual
+ * implementation class. The actual {@link TerminalConnectorDelegate} implementation class is lazily loaded by the provided
+ * {@link TerminalConnector.Factory} interface when needed. class, and delegates to the actual implementation when
+ * needed. The following methods can be called without initializing the contributed implementation class:
+ * {@link #getId()}, {@link #getName()}, {@link #getSettingsSummary()}, {@link #setTerminalSize(int, int)},
+ * {@link #getAdapter(Class)}.
+ */
+public class TerminalConnector implements ITerminalConnector {
+  /**
+   * Creates an instance of TerminalConnectorImpl. This is used to lazily load classed defined in extensions.
+   */
+  public interface Factory {
+    TerminalConnectorDelegate makeConnector() throws Exception;
+  }
+
+  private final TerminalConnector.Factory terminalConnectorFactory;
+  private final String name;
+  private final String id;
+
+  private TerminalConnectorDelegate connector;
+
+  //If the initialization of the class specified in the extension fails, this variable contains the error.
+  private Exception initializationException;
+
+  /**
+   * Constructor for the terminal connector.
+   *
+   * @param terminalConnectorFactory Factory for lazily instantiating the TerminalConnectorImpl when needed.
+   * @param id terminal connector ID. The connector is publicly known under this ID.
+   * @param name translatable name to display the connector in the UI.
+   */
+  public TerminalConnector(TerminalConnector.Factory terminalConnectorFactory, String id, String name) {
+    this.terminalConnectorFactory = terminalConnectorFactory;
+    this.id = id;
+    this.name = name;
+  }
+
+  @Override public String getInitializationErrorMessage() {
+    getConnectorDelegate();
+    if (initializationException != null) {
+      return initializationException.getLocalizedMessage();
+    }
+    return null;
+  }
+
+  @Override public String getId() {
+    return id;
+  }
+
+  @Override public String getName() {
+    return name;
+  }
+
+  private TerminalConnectorDelegate getConnectorDelegate() {
+    if (!isInitialized()) {
+      initializeConnector();
+    }
+    return connector;
+  }
+
+  private void initializeConnector() {
+    try {
+      connector = terminalConnectorFactory.makeConnector();
+      connector.initialize();
+    } catch (Exception e) {
+      initializationException = e;
+      connector = new TerminalConnectorDelegate() {
+        @Override protected void connect() {
+          terminalControl.setState(CLOSED);
+          terminalControl.setErrorMessage(getInitializationErrorMessage());
+        }
+
+        @Override public OutputStream getTerminalToRemoteStream() {
+          return null;
+        }
+
+        @Override public String getSettingsSummary() {
+          return null;
+        }
+      };
+      Logger.logException(e);
+    }
+  }
+
+  @Override public boolean isInitialized() {
+    return connector != null || initializationException != null;
+  }
+
+  @Override public void connect(ITerminalControl control) {
+    getConnectorDelegate().connect(control);
+  }
+
+  @Override public void disconnect() {
+    getConnectorDelegate().disconnect();
+  }
+
+  @Override public OutputStream getTerminalToRemoteStream() {
+    return getConnectorDelegate().getTerminalToRemoteStream();
+  }
+
+  @Override public String getSettingsSummary() {
+    if (connector != null) {
+      return getConnectorDelegate().getSettingsSummary();
+    }
+    return TerminalMessages.notInitialized;
+  }
+
+  @Override public boolean isLocalEcho() {
+    return getConnectorDelegate().isLocalEcho();
+  }
+
+  @Override public void setTerminalSize(int newWidth, int newHeight) {
+    // We assume that setTerminalSize is called also after the terminal has been initialized, otherwise we would have to
+    // cache the values.
+    if (connector != null) {
+      connector.setTerminalSize(newWidth, newHeight);
+    }
+  }
+
+  @Override public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+    TerminalConnectorDelegate connector = null;
+    if (isInitialized()) {
+      connector = getConnectorDelegate();
+    }
+    // If we cannot create the connector then we cannot adapt.
+    if (connector != null) {
+      // Maybe the connector is adaptable.
+      if (connector instanceof IAdaptable) {
+        Object result = ((IAdaptable) connector).getAdapter(adapter);
+        // Not sure if the next block is needed.
+        if (result == null) {
+          // Defer to the platform.
+          result = Platform.getAdapterManager().getAdapter(connector, adapter);
+        }
+        if (result != null) {
+          return result;
+        }
+      }
+      // Maybe the real adapter is what we need.
+      if (adapter.isInstance(connector)) {
+        return connector;
+      }
+    }
+    // Maybe we have to be adapted.
+    return Platform.getAdapterManager().getAdapter(this, adapter);
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalToRemoteInjectionOutputStream.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalToRemoteInjectionOutputStream.java
new file mode 100644
index 0000000..514c5b0
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalToRemoteInjectionOutputStream.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.connector;
+
+import java.io.*;
+
+class TerminalToRemoteInjectionOutputStream extends FilterOutputStream {
+  /**
+   * This class handles bytes written to the {@link TerminalToRemoteInjectionOutputStream}.
+   */
+  static public abstract class Interceptor {
+    protected OutputStream original;
+
+    public void begin(OutputStream original) throws IOException {
+      this.original = original;
+    }
+
+    public void write(int b) throws IOException {}
+
+    public void write(byte[] b, int off, int len) throws IOException {}
+
+    public void close() throws IOException {}
+
+    public void flush() {}
+  }
+
+  public static class BufferInterceptor extends Interceptor {
+    private final ByteArrayOutputStream fBuffer = new ByteArrayOutputStream();
+
+    @Override public void close() throws IOException {
+      original.write(fBuffer.toByteArray());
+    }
+
+    @Override public void write(byte[] b, int off, int len) throws IOException {
+      fBuffer.write(b, off, len);
+    }
+
+    @Override public void write(int b) throws IOException {
+      fBuffer.write(b);
+    }
+  }
+
+  private class TerminalFilterOutputStream extends OutputStream {
+    final private Object lock = TerminalToRemoteInjectionOutputStream.this;
+
+    @Override public void close() throws IOException {
+      synchronized (lock) {
+        if (injection == this) {
+          flush();
+          ungrabOutput();
+        }
+      }
+    }
+
+    @Override public void write(byte[] b, int off, int len) throws IOException {
+      synchronized (lock) {
+        checkStream();
+        out.write(b, off, len);
+      }
+    }
+
+    @Override public void write(byte[] b) throws IOException {
+      synchronized (lock) {
+        checkStream();
+        out.write(b);
+      }
+    }
+
+    @Override public void flush() throws IOException {
+      synchronized (lock) {
+        checkStream();
+        out.flush();
+      }
+    }
+
+    @Override public void write(int b) throws IOException {
+      synchronized (lock) {
+        checkStream();
+        out.write(b);
+      }
+    }
+
+    private void checkStream() throws IOException {
+      if (injection != this)
+       {
+        throw new IOException("Stream is closed");
+      }
+    }
+  }
+
+  private Interceptor interceptor;
+  private TerminalFilterOutputStream injection;
+
+  public TerminalToRemoteInjectionOutputStream(OutputStream out) {
+    super(out);
+  }
+
+  synchronized protected void ungrabOutput() throws IOException {
+    if (interceptor != null) {
+      interceptor.close();
+      interceptor = null;
+    }
+    injection = null;
+  }
+
+  /**
+   * There can only be one injection stream active at a time. You must call close on the returned output stream to end
+   * the injection.
+   * @param interceptor This is used handle bytes sent while the injection stream is active.
+   * @return a output stream that can be used to write to the decorated stream.
+   * @throws IOException if something goes wrong.
+   */
+  public synchronized OutputStream grabOutput(Interceptor interceptor) throws IOException {
+    if (injection != null) {
+      throw new IOException("Buffer in use");
+    }
+    this.interceptor = interceptor;
+    this.interceptor.begin(out);
+    injection = new TerminalFilterOutputStream();
+    return injection;
+  }
+
+  public synchronized OutputStream grabOutput() throws IOException {
+    return grabOutput(new BufferInterceptor());
+  }
+
+  @Override synchronized public void close() throws IOException {
+    if (injection != null) {
+      injection.close();
+    }
+    super.close();
+  }
+
+  @Override synchronized public void flush() throws IOException {
+    if (interceptor != null) {
+      interceptor.flush();
+    }
+    out.flush();
+  }
+
+  @Override synchronized public void write(byte[] b, int off, int len) throws IOException {
+    if (interceptor != null) {
+      interceptor.write(b, off, len);
+    } else {
+      out.write(b, off, len);
+    }
+  }
+
+  @Override synchronized public void write(int b) throws IOException {
+    if (interceptor != null) {
+      interceptor.write(b);
+    } else {
+      out.write(b);
+    }
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/CommandInputFieldWithHistory.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/CommandInputFieldWithHistory.java
new file mode 100644
index 0000000..5eeda0e
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/CommandInputFieldWithHistory.java
@@ -0,0 +1,287 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.control;
+
+import static java.util.Arrays.asList;
+
+import java.util.*;
+import java.util.List;
+
+import org.eclipse.jface.fieldassist.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
+
+/**
+ * Manages the Command History for the command line input of the terminal control.
+ *
+ * <p>
+ * <ul>
+ * <li>Navigate with ARROW_UP,ARROW_DOWN,PAGE_UP,PAGE_DOWN</li>
+ * <li>ESC to cancel history editing</li>
+ * <li>History can be edited (by moving up and edit) but changes are not persistent (like in bash)</li>
+ * <li>If the same command is entered multiple times in a row, only one entry is kept in the history</li>
+ * </ul>
+ * </p>
+ */
+public class CommandInputFieldWithHistory implements ICommandInputField {
+  final List<String> allHistory = new ArrayList<String>();
+
+  // Keeps a modifiable history while in history editing mode.
+  List<String> editedHistory;
+
+  // The current position in the edit history.
+  private int editHistoryPosition = 0;
+
+  // The limit of the history.
+  private final int maxHistorySize;
+
+  private Text inputField;
+  private Sash sash;
+
+  public CommandInputFieldWithHistory(int maxHistorySize) {
+    this.maxHistorySize = maxHistorySize;
+  }
+
+  /**
+   * Add a line to the history.
+   * @param line The line to be added to the history.
+   */
+  protected void pushLine(String line) {
+    endHistoryMode();
+    // Anything to remember?
+    if (line == null || line.trim().length() == 0) {
+      return;
+    }
+    allHistory.add(0, line);
+    // Ignore if the same as last/
+    if (allHistory.size() > 1 && line.equals(allHistory.get(1))) {
+      allHistory.remove(0);
+    }
+    // Limit the history size.
+    if (allHistory.size() >= maxHistorySize) {
+      allHistory.remove(allHistory.size() - 1);
+    }
+  }
+
+  public void setHistory(String history) {
+    endHistoryMode();
+    allHistory.clear();
+    if (history == null) {
+      return;
+    }
+    allHistory.addAll(asList(history.split("\n")));
+  }
+
+  public String getHistory() {
+    StringBuilder buffer = new StringBuilder();
+    boolean separate = false;
+    for (String line : allHistory) {
+      if (line.length() > 0) {
+        if (separate) {
+          buffer.append("\n");
+        } else {
+          separate = true;
+        }
+        buffer.append(line);
+      }
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * Moves a line of text in history.
+   * @param lineToBeMoved the line of text to be moved.
+   * @param count (+1 or -1) for forward and backward movement (-1 goes back.)
+   * @return the new string to be displayed in the command line or {@code null}, if the limit is reached.
+   */
+  public String move(String lineToBeMoved, int count) {
+    if (!inHistoryMode()) {
+      editedHistory = new ArrayList<String>(allHistory.size() + 1);
+      editedHistory.add(lineToBeMoved);
+      editedHistory.addAll(allHistory);
+      editHistoryPosition = 0;
+    }
+    editedHistory.set(editHistoryPosition, lineToBeMoved);
+    if (editHistoryPosition + count >= editedHistory.size()) {
+      return null;
+    }
+    if (editHistoryPosition + count < 0) {
+      return null;
+    }
+    editHistoryPosition += count;
+    return editedHistory.get(editHistoryPosition);
+  }
+
+  private boolean inHistoryMode() {
+    return editedHistory != null;
+  }
+
+  /**
+   * Exit the history movements and go to position 0;
+   * @return the string to be shown in the command line
+   */
+  protected String escape() {
+    if (!inHistoryMode()) {
+      return null;
+    }
+    String line = editedHistory.get(0);
+    endHistoryMode();
+    return line;
+  }
+
+  private void endHistoryMode() {
+    editedHistory = null;
+    editHistoryPosition = 0;
+  }
+
+  @Override public void createControl(final Composite parent, final ITerminalViewControl terminal) {
+    sash = new Sash(parent, SWT.HORIZONTAL);
+    final GridData sashLayoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+    sashLayoutData.heightHint = 5;
+    sash.setLayoutData(sashLayoutData);
+    sash.addListener(SWT.Selection, new Listener() {
+      @Override public void handleEvent(Event e) {
+        if (e.detail == SWT.DRAG) {
+          // Don't redraw during drag, it causes paint errors - bug 220971.
+          return;
+        }
+        // No idea why this is needed.
+        GridData inputFieldLayoutData = (GridData) inputField.getLayoutData();
+        Rectangle sashRect = sash.getBounds();
+        Rectangle containerRect = parent.getClientArea();
+
+        int h = inputField.getLineHeight();
+        // Make sure the input filed height is a multiple of the line height.
+        inputFieldLayoutData.heightHint = Math.max(((containerRect.height - e.y - sashRect.height) / h) * h, h);
+        // Do not show less then one line.
+        e.y = Math.min(e.y, containerRect.height - h);
+        inputField.setLayoutData(inputFieldLayoutData);
+        parent.layout();
+        // else the content assist icon will be replicated
+        parent.redraw();
+      }
+    });
+    inputField = new Text(parent, SWT.MULTI | SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);
+    GridData inputFieldLayoutData = new GridData(SWT.FILL, SWT.FILL, true, false);
+    boolean installDecoration = true;
+    if (installDecoration) {
+      // The ContentAssistCommandAdapter says: "The client is responsible for ensuring that adequate space is reserved
+      // for the decoration."
+      // TODO: What is the "adequate space"?
+      inputFieldLayoutData.horizontalIndent = 6;
+    }
+    inputField.setLayoutData(inputFieldLayoutData);
+    inputField.setFont(terminal.getFont());
+    // Register field assist *before* the key listener.
+    // Otherwise the ENTER key is sent *first* to the input field and then to the field assist pop-up.
+    // (https://bugs.eclipse.org/bugs/show_bug.cgi?id=211659)
+    new ContentAssistCommandAdapter(
+        inputField, new TextContentAdapter(), new FieldAssist(), null, null, installDecoration);
+    inputField.addKeyListener(new KeyListener() {
+      @Override public void keyPressed(KeyEvent e) {
+        // If the field assist has handled the key already then ignore it.
+        // (https://bugs.eclipse.org/bugs/show_bug.cgi?id=211659)
+        if (!e.doit) {
+          return;
+        }
+        if (e.keyCode == '\n' || e.keyCode == '\r') {
+          e.doit = false;
+          String line = inputField.getText();
+          if (!terminal.pasteString(line + "\n")) {
+            return;
+          }
+          pushLine(line);
+          setCommand("");
+        }
+        if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.PAGE_UP) {
+          e.doit = false;
+          setCommand(move(inputField.getText(), 1));
+        }
+        if (e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.PAGE_DOWN) {
+          e.doit = false;
+          setCommand(move(inputField.getText(), -1));
+        }
+        if (e.keyCode == SWT.ESC) {
+          e.doit = false;
+          setCommand(escape());
+        }
+      }
+
+      private void setCommand(String line) {
+        if (line == null) {
+          return;
+        }
+        inputField.setText(line);
+        inputField.setSelection(inputField.getCharCount());
+      }
+
+      @Override public void keyReleased(KeyEvent e) {
+      }
+    });
+  }
+
+  @Override public void setFont(Font font) {
+    inputField.setFont(font);
+    inputField.getParent().layout(true);
+  }
+
+  @Override public void dispose() {
+    sash.dispose();
+    sash = null;
+    inputField.dispose();
+    inputField = null;
+  }
+
+  private class FieldAssist implements IContentProposalProvider {
+    @Override public IContentProposal[] getProposals(String contents, int position) {
+      String prefix = contents.substring(0, position);
+      List<IContentProposal> result = new ArrayList<IContentProposal>();
+      Collection<String> seen = new HashSet<String>();
+      for (String history : allHistory) {
+        if (history.startsWith(prefix) && !seen.contains(history)) {
+          // The content is the rest of the history item.
+          String content = history.substring(prefix.length());
+          result.add(new Proposal(content, history));
+          // Don't add this proposal again.
+          seen.add(history);
+        }
+      }
+      return result.toArray(new IContentProposal[result.size()]);
+    }
+  }
+
+  private static class Proposal implements IContentProposal {
+    private final String content;
+    private final String label;
+
+    Proposal(String content, String label) {
+      this.content = content;
+      this.label = label;
+    }
+
+    @Override public String getContent() {
+      return content;
+    }
+
+    @Override public int getCursorPosition() {
+      return content.length();
+    }
+
+    @Override public String getDescription() {
+      return null;
+    }
+
+    @Override public String getLabel() {
+      return label;
+    }
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ICommandInputField.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ICommandInputField.java
new file mode 100644
index 0000000..d70450d
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ICommandInputField.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.control;
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * Interface to create a command input control.
+ */
+public interface ICommandInputField {
+  void createControl(Composite parent, ITerminalViewControl terminal);
+
+  void dispose();
+
+  /**
+   * Sets the font of a control created with {@link #createControl(Composite, ITerminalViewControl)}.
+   * @param font the new text font.
+   */
+  void setFont(Font font);
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalListener.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalListener.java
new file mode 100644
index 0000000..df8c6c5
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalListener.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.control;
+
+import com.google.eclipse.elt.emulator.provisional.api.TerminalState;
+
+/**
+ * Provided by a view implementation.
+ */
+public interface ITerminalListener {
+  /**
+   * Called when the state of the connection has changed.
+   * @param state the state of the connection.
+   */
+  void setState(TerminalState state);
+
+  /**
+   * Set the title of the terminal.
+   * @param title the new title.
+   */
+  void setTerminalTitle(String title);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalViewControl.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalViewControl.java
new file mode 100644
index 0000000..65ddab2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalViewControl.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ ******************************************************************************/
+package com.google.eclipse.elt.emulator.control;
+
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Control;
+
+import com.google.eclipse.elt.emulator.provisional.api.*;
+
+public interface ITerminalViewControl {
+  void setEncoding(String encoding) throws UnsupportedEncodingException;
+
+  String getEncoding();
+
+  boolean isEmpty();
+
+  void setFont(Font font);
+
+  void setInvertedColors(boolean invert);
+
+  Font getFont();
+
+  Control getControl();
+
+  Control getRootControl();
+
+  boolean isDisposed();
+
+  void selectAll();
+
+  void clearTerminal();
+
+  void copy();
+
+  void paste();
+
+  String getSelection();
+
+  TerminalState getState();
+
+  Clipboard getClipboard();
+
+  void connectTerminal();
+
+  void disconnectTerminal();
+
+  void disposeTerminal();
+
+  boolean isConnected();
+
+  String getSettingsSummary();
+
+  boolean setFocus();
+
+  ITerminalConnector getTerminalConnector();
+
+  void setConnector(ITerminalConnector connector);
+
+  ITerminalConnector[] getConnectors();
+
+  void sendKey(char arg0);
+
+  public boolean pasteString(String string);
+
+  void setCommandInputField(ICommandInputField inputField);
+
+  ICommandInputField getCommandInputField();
+
+  public int getBufferLineLimit();
+
+  public void setBufferLineLimit(int bufferLineLimit);
+
+  boolean isScrollLockOn();
+
+  void setScrollLockOn(boolean on);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/EditActionAccelerators.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/EditActionAccelerators.java
new file mode 100644
index 0000000..f39c7e2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/EditActionAccelerators.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.emulator.core;
+
+import static org.eclipse.jface.bindings.keys.SWTKeySupport.convertKeyStrokeToAccelerator;
+
+import java.util.*;
+
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.bindings.keys.*;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.keys.IBindingService;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class EditActionAccelerators {
+  private static final String COPY_COMMAND_ID = "com.google.eclipse.terminal.local.copy";
+  private static final String PASTE_COMMAND_ID = "com.google.eclipse.terminal.local.paste";
+
+  private final Map<Integer, String> commandIdsByAccelerator = new HashMap<Integer, String>();
+
+  private void load() {
+    addAccelerator(COPY_COMMAND_ID);
+    addAccelerator(PASTE_COMMAND_ID);
+  }
+
+  private void addAccelerator(String commandId) {
+    KeySequence keySequence = bindingFor(commandId);
+    if (keySequence == null) {
+      return;
+    }
+    KeyStroke[] keyStrokes = keySequence.getKeyStrokes();
+    if (keyStrokes.length != 0) {
+      int accelerator = convertKeyStrokeToAccelerator(keyStrokes[0]);
+      commandIdsByAccelerator.put(new Integer(accelerator), commandId);
+    }
+  }
+
+  private static KeySequence bindingFor(String commandId) {
+    IBindingService bindingService = bindingService();
+    TriggerSequence binding = bindingService.getBestActiveBindingFor(commandId);
+    if (binding instanceof KeySequence) {
+      KeySequence keySequence = (KeySequence) binding;
+      return keySequence;
+    }
+    return null;
+  }
+
+  private static IBindingService bindingService() {
+    return (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
+  }
+
+  boolean isCopyAction(int accelerator) {
+    return isMatchingAction(accelerator, COPY_COMMAND_ID);
+  }
+
+  boolean isPasteAction(int accelerator) {
+    return isMatchingAction(accelerator, PASTE_COMMAND_ID);
+  }
+
+  private boolean isMatchingAction(int accelerator, String commandId) {
+    if (commandIdsByAccelerator.isEmpty()) {
+      load();
+    }
+    return commandId.equals(commandIdsByAccelerator.get(new Integer(accelerator)));
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/IVT100EmulatorBackend.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/IVT100EmulatorBackend.java
new file mode 100644
index 0000000..8519a39
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/IVT100EmulatorBackend.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import java.util.List;
+
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+import com.google.eclipse.elt.emulator.model.Style;
+
+public interface IVT100EmulatorBackend {
+  /**
+   * Erases all text from the terminal view. Including the history.
+   */
+  void clearAll();
+
+  /**
+   * Sets the dimensions of the addressable scroll space of the screen. Keeps the cursor position relative to the bottom
+   * of the screen.
+   *
+   * @param lines the number of lines.
+   * @param columns the number of columns.
+   */
+  void setDimensions(int lines, int columns);
+
+  /**
+   * Makes room for N characters on the current line at the cursor position. Text under the cursor moves right
+   * without wrapping at the end of the line.
+   *
+   * @param charactersToInsert the number of characters to insert.
+   */
+  void insertCharacters(int charactersToInsert);
+
+  /**
+   * Erases from the cursor position (inclusive) to the end of screen. The cursor does not move.
+   */
+  void eraseToEndOfScreen();
+
+  /**
+   * Erases from the beginning of screen to the cursor position (inclusive). The cursor does not move.
+   */
+  void eraseToCursor();
+
+  /**
+   * Erases the complete display. All lines are erased and changed to single-width. The cursor does not move.
+   */
+  void eraseAll();
+
+  /**
+   * Erases the complete line.
+   */
+  void eraseLine();
+
+  /**
+   * Erases from the cursor position (inclusive) to the end of line.
+   */
+  void eraseLineToEnd();
+
+  /**
+   * Erases from beginning of line to the cursor position (inclusive).
+   */
+  void eraseLineToCursor();
+
+  /**
+   * Inserts n lines at line with cursor. Lines displayed below cursor are moved down. Lines moved past the bottom
+   * margin are lost. This sequence is ignored when cursor is outside scrolling region.
+   *
+   * @param lineCount the number of lines to insert.
+   */
+  void insertLines(int lineCount);
+
+  /**
+   * Deletes n characters, starting with the character at the cursor position. When a character is deleted, all
+   * characters to the right of cursor move left. This creates a space character at right margin. This character has
+   * same character attribute as the last character moved left.
+   *
+   * @param charCount the number of characters to delete.
+   */
+  void deleteCharacters(int charCount);
+
+  /**
+   * Deletes n lines, starting at line with cursor. As lines are deleted, the lines displayed below cursor move up.
+   * The lines added to bottom of screen have spaces with the same character attributes as the last line moved up.
+   * This sequence is ignored when cursor is outside scrolling region.
+   *
+   * @param lineCount the number of lines to delete.
+   */
+  void deleteLines(int lineCount);
+
+  Style getDefaultStyle();
+
+  void setDefaultStyle(Style defaultStyle);
+
+  Style getStyle();
+
+  /**
+   * Sets the style to use.
+   *
+   * @param style the new style.
+   */
+  void setStyle(Style style);
+
+  /**
+   * Displays a subset of the newly-received text in the terminal view, wrapping text at the right edge of the screen
+   * and overwriting text when the cursor is not at the very end of the screen's text.
+   *
+   * <p>
+   * There are never any ANSI control characters or escape sequences in the text being displayed by this method (this
+   * includes newlines, carriage returns, and tabs.)
+   * </p>
+   *
+   * @param buffer the text to append.
+   */
+  void appendString(String buffer);
+
+  /**
+   * Process a newline (Control-J) character. A newline (NL) character just moves the cursor to the same column on the
+   * next line, creating new lines when the cursor reaches the bottom edge of the terminal. This is counter-intuitive,
+   * especially to UNIX programmers who are taught that writing a single NL to a terminal is sufficient to move the
+   * cursor to the first column of the next line, as if a carriage return (CR) and a NL were written.
+   *
+   * <p>
+   * UNIX terminals typically display a NL character as a CR followed by a NL because the terminal device typically has
+   * the ONLCR attribute bit set (see the termios(4) man page for details), which causes the terminal device driver to
+   * translate NL to CR + NL on output. The terminal itself (i.e., a hardware terminal or a terminal emulator, like
+   * xterm or this code) _always_ interprets a CR to mean "move the cursor to the beginning of the current
+   * line" and a NL to mean "move the cursor to the same column on the next line".
+   * </p>
+   */
+  void processNewline();
+
+  /**
+   * Returns the relative line number of the line containing the cursor. The returned line number is relative to the
+   * top-most visible line, which has relative line number 0.
+   *
+   * @return the relative line number of the line containing the cursor.
+   */
+  int getCursorLine();
+
+  int getCursorColumn();
+
+  /**
+   * Moves the cursor to the specified line and column.
+   *
+   * @param targetLine is the line number of a screen line, so it has a minimum value of 0 (the topmost screen line) and
+   * a maximum value of heightInLines - 1 (the bottom-most screen line). A line does not have to contain any text to
+   * move the cursor to any column in that line.
+   * @param targetColumn the given column.
+   */
+  void setCursor(int targetLine, int targetColumn);
+
+  void setCursorColumn(int targetColumn);
+
+  void setCursorLine(int targetLine);
+
+  int getLines();
+
+  int getColumns();
+
+  List<IHyperlink> hyperlinksAt(int line);
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/LoggingOutputStream.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/LoggingOutputStream.java
new file mode 100644
index 0000000..1b4c9e4
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/LoggingOutputStream.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import java.io.*;
+
+import com.google.eclipse.elt.emulator.provisional.api.Logger;
+
+public class LoggingOutputStream extends FilterOutputStream {
+  public LoggingOutputStream(OutputStream out) {
+    super(out);
+  }
+
+  @Override public void write(byte[] b, int off, int len) throws IOException {
+    if (Logger.isLogEnabled()) {
+      Logger.log("Received " + len + " bytes: '" + Logger.encode(new String(b, 0, len)) + "'");
+    }
+    if ((off | len | (b.length - (len + off)) | (off + len)) < 0) {
+      throw new IndexOutOfBoundsException();
+    }
+    for (int i = 0; i < len; i++) {
+      super.write(b[off + i]);
+    }
+  }
+
+  @Override public void write(int b) throws IOException {
+    if (Logger.isLogEnabled()) {
+      Logger.log("Received " + 1 + " bytes: '" + Logger.encode(new String(new byte[] { (byte) b }, 0, 1)) + "'");
+    }
+    super.write(b);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100BackendTraceDecorator.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100BackendTraceDecorator.java
new file mode 100644
index 0000000..3e1d689
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100BackendTraceDecorator.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import java.io.PrintStream;
+import java.util.List;
+
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+import com.google.eclipse.elt.emulator.model.Style;
+
+public class VT100BackendTraceDecorator implements IVT100EmulatorBackend {
+  final IVT100EmulatorBackend backend;
+  final PrintStream out;
+
+  public VT100BackendTraceDecorator(IVT100EmulatorBackend backend, PrintStream out) {
+    this.backend = backend;
+    this.out = out;
+  }
+
+  @Override public void appendString(String buffer) {
+    out.println("appendString(\"" + buffer + "\")");
+    backend.appendString(buffer);
+  }
+
+  @Override public void clearAll() {
+    out.println("clearAll()");
+    backend.clearAll();
+  }
+
+  @Override public void deleteCharacters(int n) {
+    out.println("deleteCharacters(" + n + ")");
+    backend.deleteCharacters(n);
+  }
+
+  @Override public void deleteLines(int n) {
+    out.println("deleteLines(" + n + ")");
+    backend.deleteLines(n);
+  }
+
+  @Override public void eraseAll() {
+    out.println("eraseAll()");
+    backend.eraseAll();
+  }
+
+  @Override public void eraseLine() {
+    out.println("eraseLine()");
+    backend.eraseLine();
+  }
+
+  @Override public void eraseLineToCursor() {
+    out.println("eraseLineToCursor()");
+    backend.eraseLineToCursor();
+  }
+
+  @Override public void eraseLineToEnd() {
+    out.println("eraseLineToEnd()");
+    backend.eraseLineToEnd();
+  }
+
+  @Override public void eraseToCursor() {
+    out.println("eraseToCursor()");
+    backend.eraseToCursor();
+  }
+
+  @Override public void eraseToEndOfScreen() {
+    out.println("eraseToEndOfScreen()");
+    backend.eraseToEndOfScreen();
+  }
+
+  @Override public int getColumns() {
+    return backend.getColumns();
+  }
+
+  @Override public int getCursorColumn() {
+    return backend.getCursorColumn();
+  }
+
+  @Override public int getCursorLine() {
+    return backend.getCursorLine();
+  }
+
+  @Override public Style getDefaultStyle() {
+    return backend.getDefaultStyle();
+  }
+
+  @Override public int getLines() {
+    return backend.getLines();
+  }
+
+  @Override public Style getStyle() {
+    return backend.getStyle();
+  }
+
+  @Override public void insertCharacters(int charactersToInsert) {
+    out.println("insertCharacters(" + charactersToInsert + ")");
+    backend.insertCharacters(charactersToInsert);
+  }
+
+  @Override public void insertLines(int n) {
+    out.println("insertLines(" + n + ")");
+    backend.insertLines(n);
+  }
+
+  @Override public void processNewline() {
+    out.println("processNewline()");
+    backend.processNewline();
+  }
+
+  @Override public void setCursor(int targetLine, int targetColumn) {
+    out.println("setCursor(" + targetLine + ", " + targetColumn + ")");
+    backend.setCursor(targetLine, targetColumn);
+  }
+
+  @Override public void setCursorColumn(int targetColumn) {
+    out.println("setCursorColumn(" + targetColumn + ")");
+    backend.setCursorColumn(targetColumn);
+  }
+
+  @Override public void setCursorLine(int targetLine) {
+    out.println("setCursorLine(" + targetLine + ")");
+    backend.setCursorLine(targetLine);
+  }
+
+  @Override public void setDefaultStyle(Style defaultStyle) {
+    out.println("setDefaultStyle(" + defaultStyle + ")");
+    backend.setDefaultStyle(defaultStyle);
+  }
+
+  @Override public void setDimensions(int lines, int cols) {
+    out.println("setDimensions(" + lines + "," + cols + ")");
+    backend.setDimensions(lines, cols);
+  }
+
+  @Override public void setStyle(Style style) {
+    out.println("setStyle(" + style + ")");
+    backend.setStyle(style);
+  }
+
+  @Override public List<IHyperlink> hyperlinksAt(int line) {
+    return backend.hyperlinksAt(line);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100Emulator.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100Emulator.java
new file mode 100644
index 0000000..f6625f8
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100Emulator.java
@@ -0,0 +1,834 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import java.io.*;
+import java.util.List;
+
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.swt.events.*;
+
+import com.google.eclipse.elt.emulator.impl.*;
+import com.google.eclipse.elt.emulator.model.*;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+
+/**
+ * This class processes character data received from the remote host and displays it to the user using the Terminal
+ * view's StyledText widget. This class processes ANSI control characters, including NUL, backspace, carriage return,
+ * line-feed, and a subset of ANSI escape sequences sufficient to allow use of screen-oriented applications, such as vi,
+ * Emacs, and any GNU readline-enabled application (bash, bc, ncftp, etc.).
+ * <p>
+ */
+public class VT100Emulator implements ControlListener {
+  // This is a character processing state: Initial state.
+  private static final int ANSISTATE_INITIAL = 0;
+
+  // This is a character processing state: We've seen an escape character.
+  private static final int ANSISTATE_ESCAPE = 1;
+
+  // Character processing state: We've seen a '[' after an escape character. Expecting a parameter character or a
+  // command character next.
+  private static final int ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND = 2;
+
+  // Character processing state: We've seen a ']' after an escape character. We are now expecting an operating system
+  // command that reprograms an intelligent terminal.
+  private static final int ANSISTATE_EXPECTING_OS_COMMAND = 3;
+
+  /**
+   * This field holds the current state of the Finite Terminal State Automaton (FSA) that recognizes ANSI escape
+   * sequences.
+   *
+   * @see #processNewText()
+   */
+  private int ansiState = ANSISTATE_INITIAL;
+
+  // Holds a reference to the {@link TerminalControl} object that instantiates this class.
+  private final ITerminalControlForText terminal;
+
+  // Holds a reference to the StyledText widget that is used to display text to the user.
+  private final IVT100EmulatorBackend text;
+
+  // Holds the saved absolute line number of the cursor when processing the "ESC 7" and "ESC 8" command sequences.
+  private int savedCursorLine = 0;
+
+  // Holds the saved column number of the cursor when processing the "ESC 7" and "ESC 8" command sequences.
+  private int savedCursorColumn = 0;
+
+  // Holds an array of StringBuffer objects, each of which is one parameter from the current ANSI escape
+  // sequence. For example, when parsing the escape sequence "\e[20;10H", this array holds the strings "20" and "10".
+  private final StringBuffer[] parameters = new StringBuffer[16];
+
+  // Holds the OS-specific command found in an escape sequence of the form "\e]...\u0007".
+  private final StringBuffer ansiOsCommand = new StringBuffer(128);
+
+  // Holds the index of the next unused element of the array stored in field 'parameters'.
+  private int nextAnsiParameter = 0;
+
+  private int nextChar = -1;
+
+  private Reader reader;
+
+  private boolean crAfterNewLine;
+
+  /**
+   * The constructor.
+   */
+  public VT100Emulator(ITerminalTextData data, ITerminalControlForText terminal, Reader reader) {
+    super();
+    this.terminal = terminal;
+    for (int i = 0; i < parameters.length; ++i) {
+      parameters[i] = new StringBuffer();
+    }
+    setInputStreamReader(reader);
+    if (TerminalPlugin.isOptionEnabled("com.google.eclipse.tm.terminal/debug/log/VT100Backend")) {
+      text = new VT100BackendTraceDecorator(new VT100EmulatorBackend(data), System.out);
+    } else {
+      text = new VT100EmulatorBackend(data);
+    }
+  }
+
+  /**
+   * Set the reader that this terminal gets its input from.
+   *
+   * The reader can be changed while the Terminal is running, but a change of the reader likely loses some characters
+   * which have not yet been fully read. Changing the reader can be done in order to change the selected Encoding,
+   * though. This is typically done when the Terminal is constructed, i.e. before it really starts operation; or, when
+   * the user manually selects a different encoding and thus doesn't care about losing old characters.
+   *
+   * @param reader the new reader.
+   */
+  public void setInputStreamReader(Reader reader) {
+    this.reader = reader;
+  }
+
+  public void setDimensions(int lines, int cols) {
+    text.setDimensions(lines, cols);
+    ITerminalConnector telnetConnection = getConnector();
+    if (telnetConnection != null) {
+      telnetConnection.setTerminalSize(text.getColumns(), text.getLines());
+    }
+  }
+
+  public void dispose() {
+  }
+
+  @Override public void controlMoved(ControlEvent event) {
+  }
+
+  @Override public void controlResized(ControlEvent event) {
+    adjustTerminalDimensions();
+  }
+
+  public void clearTerminal() {
+    text.clearAll();
+  }
+
+  public void fontChanged() {
+    if (text != null) {
+      adjustTerminalDimensions();
+    }
+  }
+
+  public void processText() {
+    try {
+      // Find the width and height of the terminal, and resize it to display an integral number of lines and columns.
+      adjustTerminalDimensions();
+      // Restore the caret offset, process and display the new text, then save
+      // the caret offset. See the documentation for field caretOffset for
+      // details.
+      // ISSUE: Is this causing the scroll-to-bottom-on-output behavior?
+      try {
+        processNewText();
+      } catch (IOException e) {
+        Logger.logException(e);
+      }
+    } catch (RuntimeException e) {
+      Logger.logException(e);
+    }
+  }
+
+  /**
+   * This method scans the newly received text, processing ANSI control characters and escape sequences and displaying
+   * normal text.
+   *
+   * @throws IOException if something goes wrong.
+   */
+  private void processNewText() throws IOException {
+    // Scan the newly received text.
+    while (hasNextChar()) {
+      char character = getNextChar();
+      switch (ansiState) {
+      case ANSISTATE_INITIAL:
+        switch (character) {
+        case '\u0000':
+          break; // NUL character. Ignore it.
+        case '\u0007':
+          processBEL(); // BEL (Control-G)
+          break;
+        case '\b':
+          processBackspace(); // Backspace
+          break;
+        case '\t':
+          processTab(); // Tab.
+          break;
+        case '\n':
+          processNewline(); // Newline (Control-J)
+          if (crAfterNewLine) {
+            processCarriageReturn(); // Carriage Return (Control-M)
+          }
+          break;
+        case '\r':
+          processCarriageReturn(); // Carriage Return (Control-M)
+          break;
+        case '\u001b':
+          ansiState = ANSISTATE_ESCAPE; // Escape.
+          break;
+        default:
+          processNonControlCharacters(character);
+          break;
+        }
+        break;
+      case ANSISTATE_ESCAPE:
+        // We've seen an escape character. Here, we process the character immediately following the escape.
+        switch (character) {
+        case '[':
+          ansiState = ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND;
+          nextAnsiParameter = 0;
+          // Erase the parameter strings in preparation for optional parameter characters.
+          for (int i = 0; i < parameters.length; ++i) {
+            parameters[i].delete(0, parameters[i].length());
+          }
+          break;
+        case ']':
+          ansiState = ANSISTATE_EXPECTING_OS_COMMAND;
+          ansiOsCommand.delete(0, ansiOsCommand.length());
+          break;
+        case '7':
+          // Save cursor position and character attributes.
+          ansiState = ANSISTATE_INITIAL;
+          savedCursorLine = relativeCursorLine();
+          savedCursorColumn = getCursorColumn();
+          break;
+        case '8':
+          // Restore cursor and attributes to previously saved position.
+          ansiState = ANSISTATE_INITIAL;
+          moveCursor(savedCursorLine, savedCursorColumn);
+          break;
+        case 'c':
+          // Reset the terminal.
+          ansiState = ANSISTATE_INITIAL;
+          resetTerminal();
+          break;
+        default:
+          Logger.log("Unsupported escape sequence: escape '" + character + "'");
+          ansiState = ANSISTATE_INITIAL;
+          break;
+        }
+        break;
+      case ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND:
+        // Parameters can appear after the '[' in an escape sequence, but they are optional.
+        if (character == '@' || (character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z')) {
+          ansiState = ANSISTATE_INITIAL;
+          processAnsiCommandCharacter(character);
+        } else {
+          processAnsiParameterCharacter(character);
+        }
+        break;
+      case ANSISTATE_EXPECTING_OS_COMMAND:
+        // A BEL (\u0007) character marks the end of the OSC sequence.
+        if (character == '\u0007') {
+          ansiState = ANSISTATE_INITIAL;
+          processAnsiOsCommand();
+        } else {
+          ansiOsCommand.append(character);
+        }
+        break;
+      default:
+        // This should never happen! If it does happen, it means there is a bug in the FSA. For robustness, we return to
+        // the initial state.
+        Logger.log("INVALID ANSI FSA STATE: " + ansiState);
+        ansiState = ANSISTATE_INITIAL;
+        break;
+      }
+    }
+  }
+
+  private void resetTerminal() {
+    text.eraseAll();
+    text.setCursor(0, 0);
+    text.setStyle(text.getDefaultStyle());
+  }
+
+  // This method is called when we have parsed an OS Command escape sequence. The only one we support is
+  // "\e]0;...\u0007", which sets the terminal title.
+  private void processAnsiOsCommand() {
+    if (ansiOsCommand.charAt(0) != '0' || ansiOsCommand.charAt(1) != ';') {
+      Logger.log("Ignoring unsupported ANSI OSC sequence: '" + ansiOsCommand + "'");
+      return;
+    }
+    terminal.setTerminalTitle(ansiOsCommand.substring(2));
+  }
+
+  // Dispatches control to various processing methods based on the command character found in the most recently received
+  // ANSI escape sequence. This method only handles command characters that follow the ANSI standard Control Sequence
+  // Introducer (CSI), which is "\e[...", where "..." is an optional ';'-separated sequence of numeric parameters.
+  private void processAnsiCommandCharacter(char ansiCommandCharacter) {
+    // If the width or height of the terminal is ridiculously small (one line or column or less), don't even try to
+    // process the escape sequence. This avoids throwing an exception (SPR 107450). The display will be messed up, but
+    // what did you user expect by making the terminal so small?
+    switch (ansiCommandCharacter) {
+    case '@':
+      // Insert character(s).
+      processAnsiCommand_atsign();
+      break;
+    case 'A':
+      // Move cursor up N lines (default 1).
+      processAnsiCommand_A();
+      break;
+    case 'B':
+      // Move cursor down N lines (default 1).
+      processAnsiCommand_B();
+      break;
+    case 'C':
+      // Move cursor forward N columns (default 1).
+      processAnsiCommand_C();
+      break;
+    case 'D':
+      // Move cursor backward N columns (default 1).
+      processAnsiCommand_D();
+      break;
+    case 'E':
+      // Move cursor to first column of Nth next line (default 1).
+      processAnsiCommand_E();
+      break;
+    case 'F':
+      // Move cursor to first column of Nth previous line (default 1).
+      processAnsiCommand_F();
+      break;
+    case 'G':
+      // Move to column N of current line (default 1).
+      processAnsiCommand_G();
+      break;
+    case 'H':
+      // Set cursor Position.
+      processAnsiCommand_H();
+      break;
+    case 'J':
+      // Erase part or all of display. Cursor does not move.
+      processAnsiCommand_J();
+      break;
+    case 'K':
+      // Erase in line (cursor does not move).
+      processAnsiCommand_K();
+      break;
+    case 'L':
+      // Insert line(s) (current line moves down).
+      processAnsiCommand_L();
+      break;
+    case 'M':
+      // Delete line(s).
+      processAnsiCommand_M();
+      break;
+    case 'm':
+      // Set Graphics Rendition (SGR).
+      processAnsiCommand_m();
+      break;
+    case 'n':
+      // Device Status Report (DSR).
+      processAnsiCommand_n();
+      break;
+    case 'P':
+      // Delete character(s).
+      processAnsiCommand_P();
+      break;
+    case 'S':
+      // Scroll up.
+      // Emacs, vi, and GNU readline don't seem to use this command, so we ignore it for now.
+      break;
+    case 'T':
+      // Scroll down.
+      // Emacs, vi, and GNU readline don't seem to use this command, so we ignore it for now.
+      break;
+    case 'X':
+      // Erase character.
+      // Emacs, vi, and GNU readline don't seem to use this command, so we ignore it for now.
+      break;
+    case 'Z':
+      // Cursor back tab.
+      // Emacs, vi, and GNU readline don't seem to use this command, so we ignore it for now.
+      break;
+
+    default:
+      Logger.log("Ignoring unsupported ANSI command character: '" + ansiCommandCharacter + "'");
+      break;
+    }
+  }
+
+  // Makes room for N characters on the current line at the cursor position. Text under the cursor moves right without
+  // wrapping at the end of the line.
+  private void processAnsiCommand_atsign() {
+    int charactersToInsert = getAnsiParameter(0);
+    text.insertCharacters(charactersToInsert);
+  }
+
+  // Moves the cursor up by the number of lines specified by the escape sequence parameter (default 1).
+  private void processAnsiCommand_A() {
+    moveCursorUp(getAnsiParameter(0));
+  }
+
+  // Moves the cursor down by the number of lines specified by the escape sequence parameter (default 1).
+  private void processAnsiCommand_B() {
+    moveCursorDown(getAnsiParameter(0));
+  }
+
+  // Moves the cursor forward by the number of columns specified by the escape sequence parameter (default 1).
+  private void processAnsiCommand_C() {
+    moveCursorForward(getAnsiParameter(0));
+  }
+
+  // Moves the cursor backward by the number of columns specified by the escape sequence parameter (default 1).
+  private void processAnsiCommand_D() {
+    moveCursorBackward(getAnsiParameter(0));
+  }
+
+  // Moves the cursor to the first column of the Nth next line, where N is specified by the ANSI parameter (default 1).
+  private void processAnsiCommand_E() {
+    int linesToMove = getAnsiParameter(0);
+    moveCursor(relativeCursorLine() + linesToMove, 0);
+  }
+
+  // Moves the cursor to the first column of the Nth previous line, where N is specified by the ANSI parameter
+  // (default 1).
+  private void processAnsiCommand_F() {
+    int linesToMove = getAnsiParameter(0);
+    moveCursor(relativeCursorLine() - linesToMove, 0);
+  }
+
+  // Moves the cursor within the current line to the column specified by the ANSI parameter (default is column 1).
+  private void processAnsiCommand_G() {
+    int targetColumn = 1;
+    if (parameters[0].length() > 0) {
+      targetColumn = getAnsiParameter(0) - 1;
+    }
+    moveCursor(relativeCursorLine(), targetColumn);
+  }
+
+  // Sets the cursor to a position specified by the escape sequence parameters (default is the upper left corner of the
+  // screen).
+  private void processAnsiCommand_H() {
+    moveCursor(getAnsiParameter(0) - 1, getAnsiParameter(1) - 1);
+  }
+
+  // Deletes some (or all) of the text on the screen without moving the cursor.
+  private void processAnsiCommand_J() {
+    int parameter;
+    if (parameters[0].length() == 0) {
+      parameter = 0;
+    } else {
+      parameter = getAnsiParameter(0);
+    }
+    switch (parameter) {
+    case 0:
+      text.eraseToEndOfScreen();
+      break;
+    case 1:
+      // Erase from beginning to current position (inclusive).
+      text.eraseToCursor();
+      break;
+    case 2:
+      // Erase entire display.
+      text.eraseAll();
+      break;
+    default:
+      Logger.log("Unexpected J-command parameter: " + parameter);
+      break;
+    }
+  }
+
+  // Deletes some (or all) of the text in the current line without moving the cursor.
+  private void processAnsiCommand_K() {
+    int parameter = getAnsiParameter(0);
+    switch (parameter) {
+    case 0:
+      // Erase from beginning to current position (inclusive).
+      text.eraseLineToCursor();
+      break;
+    case 1:
+      // Erase from current position to end (inclusive).
+      text.eraseLineToEnd();
+      break;
+    case 2:
+      // Erase entire line.
+      text.eraseLine();
+      break;
+    default:
+      Logger.log("Unexpected K-command parameter: " + parameter);
+      break;
+    }
+  }
+
+  // Inserts one or more blank lines. The current line of text moves down. Text that falls off the bottom of the screen
+  // is deleted.
+  private void processAnsiCommand_L() {
+    text.insertLines(getAnsiParameter(0));
+  }
+
+  // Deletes one or more lines of text. Any lines below the deleted lines move up, which we implement by appending
+  // new lines to the end of the text.
+  private void processAnsiCommand_M() {
+    text.deleteLines(getAnsiParameter(0));
+  }
+
+  // Sets a new graphics rendition mode, such as foreground/background color, bold/normal text, and reverse video.
+  private void processAnsiCommand_m() {
+    if (parameters[0].length() == 0) {
+      // This a special case: when no ANSI parameter is specified, act like a single parameter equal to 0 was specified.
+      parameters[0].append('0');
+    }
+    Style style = text.getStyle();
+    // There are a non-zero number of ANSI parameters. Process each one in order.
+    int parameterCount = parameters.length;
+    int parameterIndex = 0;
+    while (parameterIndex < parameterCount && parameters[parameterIndex].length() > 0) {
+      int parameter = getAnsiParameter(parameterIndex);
+      switch (parameter) {
+      case 0:
+        // Reset all graphics modes.
+        style = text.getDefaultStyle();
+        break;
+      case 1:
+        style = style.setBold(true);
+        break;
+      case 4:
+        style = style.setUnderline(true);
+        break;
+      case 5:
+        style = style.setBlink(true);
+        break;
+      case 7:
+        style = style.setReverse(true);
+        break;
+      case 10: // Set primary font. Ignored.
+        break;
+      case 21:
+      case 22:
+        style = style.setBold(false);
+        break;
+      case 24:
+        style = style.setUnderline(false);
+        break;
+      case 25:
+        style = style.setBlink(false);
+        break;
+      case 27:
+        style = style.setReverse(false);
+        break;
+      case 30:
+        style = style.setForground("BLACK");
+        break;
+      case 31:
+        style = style.setForground("RED");
+        break;
+      case 32:
+        style = style.setForground("GREEN");
+        break;
+      case 33:
+        style = style.setForground("YELLOW");
+        break;
+      case 34:
+        style = style.setForground("BLUE");
+        break;
+      case 35:
+        style = style.setForground("MAGENTA");
+        break;
+      case 36:
+        style = style.setForground("CYAN");
+        break;
+      case 37:
+        style = style.setForground("WHITE_FOREGROUND");
+        break;
+      case 40:
+        style = style.setBackground("BLACK");
+        break;
+      case 41:
+        style = style.setBackground("RED");
+        break;
+      case 42:
+        style = style.setBackground("GREEN");
+        break;
+      case 43:
+        style = style.setBackground("YELLOW");
+        break;
+      case 44:
+        style = style.setBackground("BLUE");
+        break;
+      case 45:
+        style = style.setBackground("MAGENTA");
+        break;
+      case 46:
+        style = style.setBackground("CYAN");
+        break;
+      case 47:
+        style = style.setBackground("WHITE");
+        break;
+      default:
+        Logger.log("Unsupported graphics rendition parameter: " + parameter);
+        break;
+      }
+      ++parameterIndex;
+    }
+    text.setStyle(style);
+  }
+
+  // Responds to an ANSI Device Status Report (DSR) command from the remote endpoint requesting the cursor position.
+  // Requests for other kinds of status are ignored.
+  private void processAnsiCommand_n() {
+    // Do nothing if the numeric parameter was not 6 (which means report cursor position).
+    if (getAnsiParameter(0) != 6) {
+      return;
+    }
+    // Send the ANSI cursor position (which is 1-based) to the remote endpoint.
+    String positionReport = "\u001b[" + (relativeCursorLine() + 1) + ";" + (getCursorColumn() + 1) + "R";
+    try {
+      // TODO(alruiz): use same encoding as terminal.
+      OutputStreamWriter streamWriter = new OutputStreamWriter(terminal.getOutputStream(), "ISO-8859-1");
+      streamWriter.write(positionReport, 0, positionReport.length());
+      streamWriter.flush();
+    } catch (IOException ex) {
+      Logger.log("Caught IOException!");
+    }
+  }
+
+  // Deletes one or more characters starting at the current cursor position. Characters on the same line and to the
+  // right of the deleted characters move left. If there are no characters on the current line at or to the right of the
+  // cursor column, no text is deleted.
+  private void processAnsiCommand_P() {
+    text.deleteCharacters(getAnsiParameter(0));
+  }
+
+  // Returns one of the numeric ANSI parameters received in the most recent escape sequence.
+  private int getAnsiParameter(int parameterIndex) {
+    if (parameterIndex < 0 || parameterIndex >= parameters.length) {
+      // This should never happen.
+      return -1;
+    }
+    String parameter = parameters[parameterIndex].toString();
+    if (parameter.length() == 0) {
+      return 1;
+    }
+    int parameterValue = 1;
+    // Don't trust the remote endpoint to send well formed numeric parameters.
+    try {
+      parameterValue = Integer.parseInt(parameter);
+    } catch (NumberFormatException ex) {
+      parameterValue = 1;
+    }
+    return parameterValue;
+  }
+
+  // Processes a single parameter character in an ANSI escape sequence. Parameters are the (optional) characters
+  // between the leading "\e[" and the command character in an escape sequence (e.g., in the escape sequence
+  // "\e[20;10H", the parameter characters are "20;10"). Parameters are integers separated by one or more ';'s.
+  private void processAnsiParameterCharacter(char ch) {
+    if (ch == ';') {
+      ++nextAnsiParameter;
+    } else {
+      if (nextAnsiParameter < parameters.length) {
+        parameters[nextAnsiParameter].append(ch);
+      }
+    }
+  }
+
+  // Processes a contiguous sequence of non-control characters. This is a performance optimization, so that we don't
+  // have to insert or append each non-control character individually to the StyledText widget. A non-control character
+  // is any character that passes the condition in the below while loop.
+  private void processNonControlCharacters(char character) throws IOException {
+    StringBuilder buffer = new StringBuilder();
+    buffer.append(character);
+    // Identify a contiguous sequence of non-control characters, starting at firstNonControlCharacterIndex in newText.
+    while (hasNextChar()) {
+      character = getNextChar();
+      if (character == '\u0000' || character == '\b' || character == '\t' || character == '\u0007' || character == '\n'
+          || character == '\r' || character == '\u001b') {
+        pushBackChar(character);
+        break;
+      }
+      buffer.append(character);
+    }
+    // Now insert the sequence of non-control characters in the StyledText widget
+    // at the location of the cursor.
+    displayNewText(buffer.toString());
+  }
+
+  // Displays a subset of the newly-received text in the Terminal view, wrapping text at the right edge of the screen
+  // and overwriting text when the cursor is not at the very end of the screen's text.
+  // There are never any ANSI control characters or escape sequences in the text being displayed by this method (this
+  // includes newlines, carriage returns, and tabs).
+  private void displayNewText(String buffer) {
+    text.appendString(buffer);
+  }
+
+  // Processes a BEL (Control-G) character.
+  private void processBEL() {
+    // TODO
+    // Display.getDefault().beep();
+  }
+
+  // Processes a backspace (Control-H) character.
+  private void processBackspace() {
+    moveCursorBackward(1);
+  }
+
+  // Processes a tab (Control-I) character. We don't insert a tab character into the StyledText widget. Instead, we move
+  // the cursor forward to the next tab stop, without altering any of the text. Tab stops are every 8 columns. The
+  // cursor will never move past the rightmost column.
+  private void processTab() {
+    moveCursorForward(8 - (getCursorColumn() % 8));
+  }
+
+  // Processes a newline (Control-J) character. A newline (NL) character just moves the cursor to the same column on the
+  // next line, creating new lines when the cursor reaches the bottom edge of the terminal. This is counter-intuitive,
+  // especially to UNIX programmers who are taught that writing a single NL to a terminal is sufficient to move the
+  // cursor to the first column of the next line, as if a carriage return (CR) and a NL were written.
+  //
+  // UNIX terminals typically display a NL character as a CR followed by a NL because the terminal device typically has
+  // the ONLCR attribute bit set (see the termios(4) man page for details), which causes the terminal device driver to
+  // translate NL to CR + NL on output. The terminal itself (i.e., a hardware terminal or a terminal emulator, like
+  // xterm or this code) _always_ interprets a CR to mean "move the cursor to the beginning of the current
+  // line" and a NL to mean "move the cursor to the same column on the next line".
+  private void processNewline() {
+    text.processNewline();
+  }
+
+  // Processes a Carriage Return (Control-M).
+  private void processCarriageReturn() {
+    text.setCursorColumn(0);
+  }
+
+  // Computes the width of the terminal in columns and its height in lines, then adjusts the width and height of the
+  // view's StyledText widget so that it displays an integral number of lines and columns of text. The adjustment is
+  // always to shrink the widget vertically or horizontally, because if the control were to grow, it would be clipped by
+  // the edges of the view window (i.e., the view window does not become larger to accommodate its contents becoming
+  // larger).
+  //
+  // This method must be called immediately before each time text is written to the terminal so that we can properly
+  // line wrap text. Because it is called so frequently, it must be fast when there is no resizing to be done.
+  private void adjustTerminalDimensions() {
+    // Compute how many pixels we need to shrink the StyledText control vertically to make it display an integral number
+    // of lines of text.
+    //
+    // TODO
+    // if(text.getColumns()!=80 && text.getLines()!=80) text.setDimensions(24, 80);
+    ITerminalConnector connector = getConnector();
+    // TODO MSA: send only if dimensions have really changed!
+    if (connector != null) {
+      connector.setTerminalSize(text.getColumns(), text.getLines());
+    }
+  }
+
+  private ITerminalConnector getConnector() {
+    if (terminal.getTerminalConnector() != null) {
+      return terminal.getTerminalConnector();
+    }
+    return null;
+  }
+
+  // Returns the relative line number of the line containing the cursor. The returned line number is relative to the
+  // top-most visible line, which has relative line number 0.
+  private int relativeCursorLine() {
+    return text.getCursorLine();
+  }
+
+  // Moves the cursor to the specified line and column. Parameter <i>targetLine</i> is the line number of a screen line,
+  // so it has a minimum value of 0 (the topmost screen line) and a maximum value of heightInLines - 1 (the
+  // bottom-most screen line). A line does not have to contain any text to move the cursor to any column in that line.
+  private void moveCursor(int targetLine, int targetColumn) {
+    text.setCursor(targetLine, targetColumn);
+  }
+
+  // Moves the cursor down n lines, but won't move the cursor past the bottom of the screen. This method does not cause
+  // any scrolling.
+  private void moveCursorDown(int lines) {
+    moveCursor(relativeCursorLine() + lines, getCursorColumn());
+  }
+
+  // Moves the cursor up n lines, but won't move the cursor past the top of the screen. This method does not cause any
+  // scrolling.
+  private void moveCursorUp(int lines) {
+    moveCursor(relativeCursorLine() - lines, getCursorColumn());
+  }
+
+  // Method moves the cursor forward n columns, but won't move the cursor past the right edge of the screen, nor will
+  // it move the cursor onto the next line. This method does not cause any scrolling.
+  private void moveCursorForward(int columnsToMove) {
+    moveCursor(relativeCursorLine(), getCursorColumn() + columnsToMove);
+  }
+
+  // Moves the cursor backward n columns, but won't move the cursor past the left edge of the screen, nor will it move
+  // the cursor onto the previous line. This method does not cause any scrolling.
+  private void moveCursorBackward(int columnsToMove) {
+    moveCursor(relativeCursorLine(), getCursorColumn() - columnsToMove);
+  }
+
+  /**
+   * Resets the state of the terminal text (foreground color, background color, font style and other internal state). It
+   * essentially makes it ready for new input.
+   */
+  public void resetState() {
+    ansiState = ANSISTATE_INITIAL;
+    text.setStyle(text.getDefaultStyle());
+  }
+
+  private char getNextChar() throws IOException {
+    int c = -1;
+    if (nextChar != -1) {
+      c = nextChar;
+      nextChar = -1;
+    } else {
+      c = reader.read();
+    }
+    // TODO: better end of file handling
+    if (c == -1) {
+      c = 0;
+    }
+    return (char) c;
+  }
+
+  private boolean hasNextChar() throws IOException {
+    if (nextChar >= 0) {
+      return true;
+    }
+    return reader.ready();
+  }
+
+  /**
+   * Put back one character to the stream. This method can push back exactly one character. The character is the next
+   * character returned by {@link #getNextChar}.
+   *
+   * @param c the character to be pushed back.
+   */
+  private void pushBackChar(char c) {
+    nextChar = c;
+  }
+
+  private int getCursorColumn() {
+    return text.getCursorColumn();
+  }
+
+  public boolean isCrAfterNewLine() {
+    return crAfterNewLine;
+  }
+
+  public void setCrAfterNewLine(boolean crAfterNewLine) {
+    this.crAfterNewLine = crAfterNewLine;
+  }
+
+  public List<IHyperlink> hyperlinksAt(int line) {
+    return text.hyperlinksAt(line);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100EmulatorBackend.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100EmulatorBackend.java
new file mode 100644
index 0000000..4d56c59
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100EmulatorBackend.java
@@ -0,0 +1,368 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import static java.util.Collections.emptyList;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+import com.google.eclipse.elt.emulator.hyperlink.*;
+import com.google.eclipse.elt.emulator.model.*;
+
+public class VT100EmulatorBackend implements IVT100EmulatorBackend {
+  // This field holds the number of the column in which the cursor is logically positioned. The left-most column on the
+  // screen is column 0, and column numbers increase to the right. The maximum value of this field is
+  // widthInColumns - 1. We track the cursor column using this field to avoid having to recompute it repeatly using
+  // StyledText method calls.
+  //
+  // The StyledText widget that displays text has a vertical bar (called the "caret") that appears _between_ character
+  // cells, but ANSI terminals have the concept of a cursor that appears _in_ a character cell, so we need a convention
+  // for which character cell the cursor logically occupies when the caret is physically between two cells. The
+  // convention used in this class is that the cursor is logically in column N when the caret is physically positioned
+  // immediately to the _left_ of column N.
+  //
+  // When cursorColumn is N, the next character output to the terminal appears in column N. When a character is output
+  // to the rightmost column on a given line (column widthInColumns - 1), the cursor moves to column 0 on the next line
+  // after the character is drawn (this is how line wrapping is implemented). If the cursor is in the bottom-most line
+  // when line wrapping occurs, the topmost visible line is scrolled off the top edge of the screen.
+  private int cursorColumn;
+
+  private int cursorLine;
+  private Style defaultStyle;
+  private Style style;
+  private int lines;
+  private int columns;
+
+  private final ITerminalTextData terminal;
+
+  private final IHyperlinkFactory httpHyperlinkFactory = new HttpHyperlinkFactory();
+  private final Map<Integer, List<IHyperlink>> hyperlinks = new HashMap<Integer, List<IHyperlink>>();
+
+  public VT100EmulatorBackend(ITerminalTextData terminal) {
+    this.terminal = terminal;
+  }
+
+  @Override public void clearAll() {
+    synchronized (terminal) {
+      // clear the history
+      int n = terminal.getHeight();
+      for (int line = 0; line < n; line++) {
+        terminal.cleanLine(line);
+      }
+      terminal.setDimensions(lines, terminal.getWidth());
+      setStyle(getDefaultStyle());
+      setCursor(0, 0);
+    }
+  }
+
+  @Override public void setDimensions(int lines, int columns) {
+    synchronized (terminal) {
+      if (lines == this.lines && columns == this.columns) {
+        return; // nothing to do
+      }
+      // relative cursor line
+      int cursorLine = getCursorLine();
+      int cursorColumn = getCursorColumn();
+      int height = terminal.getHeight();
+      // absolute cursor line
+      int absoluteCursorLine = cursorLine + height - this.lines;
+      int newLines = Math.max(lines, height);
+      if (lines < this.lines) {
+        if (height == this.lines) {
+          // if the terminal has no history, then resize by setting the size to the new size.
+          // TODO We are assuming that cursor line points at end of text.
+          newLines = Math.max(lines, cursorLine + 1);
+        }
+      }
+      this.lines = lines;
+      this.columns = columns;
+      // Make the terminal at least as high as we need lines.
+      terminal.setDimensions(newLines, this.columns);
+      // Compute relative cursor line.
+      cursorLine = absoluteCursorLine - (newLines - this.lines);
+      setCursor(cursorLine, cursorColumn);
+    }
+  }
+
+  int toAbsoluteLine(int line) {
+    synchronized (terminal) {
+      return terminal.getHeight() - this.lines + line;
+    }
+  }
+
+  @Override public void insertCharacters(int charactersToInsert) {
+    synchronized (terminal) {
+      int line = toAbsoluteLine(cursorLine);
+      int n = charactersToInsert;
+      for (int column = columns - 1; column >= cursorColumn + n; column--) {
+        char c = terminal.getChar(line, column - n);
+        Style style = terminal.getStyle(line, column - n);
+        terminal.setChar(line, column, c, style);
+      }
+      int last = Math.min(cursorColumn + n, columns);
+      for (int col = cursorColumn; col < last; col++) {
+        terminal.setChar(line, col, '\000', null);
+      }
+    }
+  }
+
+  @Override public void eraseToEndOfScreen() {
+    synchronized (terminal) {
+      eraseLineToEnd();
+      for (int line = toAbsoluteLine(cursorLine + 1); line < toAbsoluteLine(lines); line++) {
+        terminal.cleanLine(line);
+      }
+    }
+  }
+
+  @Override public void eraseToCursor() {
+    synchronized (terminal) {
+      for (int line = toAbsoluteLine(0); line < toAbsoluteLine(cursorLine); line++) {
+        terminal.cleanLine(line);
+      }
+      eraseLineToCursor();
+    }
+  }
+
+  @Override public void eraseAll() {
+    synchronized (terminal) {
+      for (int line = toAbsoluteLine(0); line < toAbsoluteLine(lines); line++) {
+        terminal.cleanLine(line);
+      }
+    }
+  }
+
+  @Override public void eraseLine() {
+    synchronized (terminal) {
+      terminal.cleanLine(toAbsoluteLine(cursorLine));
+    }
+  }
+
+  @Override public void eraseLineToEnd() {
+    synchronized (terminal) {
+      int line = toAbsoluteLine(cursorLine);
+      for (int col = cursorColumn; col < columns; col++) {
+        terminal.setChar(line, col, '\000', null);
+      }
+    }
+  }
+
+  @Override public void eraseLineToCursor() {
+    synchronized (terminal) {
+      int line = toAbsoluteLine(cursorLine);
+      for (int col = 0; col <= cursorColumn; col++) {
+        terminal.setChar(line, col, '\000', null);
+      }
+    }
+  }
+
+  @Override public void insertLines(int lineCount) {
+    synchronized (terminal) {
+      if (!isCursorInScrollingRegion()) {
+        return;
+      }
+      Assert.isTrue(lineCount > 0);
+      int line = toAbsoluteLine(cursorLine);
+      int nLines = terminal.getHeight() - line;
+      terminal.scroll(line, nLines, lineCount);
+    }
+  }
+
+  @Override public void deleteCharacters(int charCount) {
+    synchronized (terminal) {
+      int line = toAbsoluteLine(cursorLine);
+      for (int col = cursorColumn + charCount; col < columns; col++) {
+        char c = terminal.getChar(line, col);
+        Style style = terminal.getStyle(line, col);
+        terminal.setChar(line, col - charCount, c, style);
+      }
+      int first = Math.max(cursorColumn, columns - charCount);
+      for (int col = first; col < columns; col++) {
+        terminal.setChar(line, col, '\000', null);
+      }
+    }
+  }
+
+  @Override public void deleteLines(int lineCount) {
+    synchronized (terminal) {
+      if (!isCursorInScrollingRegion()) {
+        return;
+      }
+      Assert.isTrue(lineCount > 0);
+      int line = toAbsoluteLine(cursorLine);
+      int currentLineCount = terminal.getHeight() - line;
+      terminal.scroll(line, currentLineCount, -lineCount);
+    }
+  }
+
+  private boolean isCursorInScrollingRegion() {
+    return true;
+  }
+
+  @Override public Style getDefaultStyle() {
+    synchronized (terminal) {
+      return defaultStyle;
+    }
+  }
+
+  @Override public void setDefaultStyle(Style defaultStyle) {
+    synchronized (terminal) {
+      this.defaultStyle = defaultStyle;
+    }
+  }
+
+  @Override public Style getStyle() {
+    synchronized (terminal) {
+      if (style == null) {
+        return defaultStyle;
+      }
+      return style;
+    }
+  }
+
+  @Override public void setStyle(Style style) {
+    synchronized (terminal) {
+      this.style = style;
+    }
+  }
+
+  @Override public void appendString(String buffer) {
+    synchronized (terminal) {
+      char[] chars = buffer.toCharArray();
+      int line = toAbsoluteLine(cursorLine);
+      int originalLine = line;
+      List<IHyperlink> found = emptyList();
+      if (buffer != null) {
+        found = httpHyperlinkFactory.hyperlinksIn(cursorColumn, buffer);
+        hyperlinks.put(new Integer(line), found);
+      }
+      int i = 0;
+      while (i < chars.length) {
+        int n = Math.min(columns - cursorColumn, chars.length - i);
+        terminal.setChars(line, cursorColumn, chars, i, n, style);
+        int col = cursorColumn + n;
+        i += n;
+        // wrap needed?
+        if (col >= columns) {
+          doNewline();
+          line = toAbsoluteLine(cursorLine);
+          setCursorColumn(0);
+        } else {
+          setCursorColumn(col);
+        }
+      }
+      drawHyperlinks(found, originalLine);
+    }
+  }
+
+  private void drawHyperlinks(List<IHyperlink> hyperlinks, int line) {
+    for (IHyperlink hyperlink : hyperlinks) {
+      IRegion region = hyperlink.getHyperlinkRegion();
+      int start = region.getOffset();
+      int end = start + region.getLength();
+      for (int column = start; column < end; column++) {
+        Style style = terminal.getStyle(line, column);
+        if (style != null) {
+          style = style.setUnderline(true);
+          terminal.setChar(line, column, terminal.getChar(line, column), style);
+        }
+      }
+    }
+  }
+
+  // MUST be called from a synchronized block!
+  private void doNewline() {
+    if (cursorLine + 1 >= lines) {
+      int h = terminal.getHeight();
+      terminal.addLine();
+      if (h != terminal.getHeight()) {
+        setCursorLine(cursorLine + 1);
+      }
+    } else {
+      setCursorLine(cursorLine + 1);
+    }
+  }
+
+  @Override public void processNewline() {
+    synchronized (terminal) {
+      doNewline();
+    }
+  }
+
+  @Override public int getCursorLine() {
+    synchronized (terminal) {
+      return cursorLine;
+    }
+  }
+
+  @Override public int getCursorColumn() {
+    synchronized (terminal) {
+      return cursorColumn;
+    }
+  }
+
+  @Override public void setCursor(int targetLine, int targetColumn) {
+    synchronized (terminal) {
+      setCursorLine(targetLine);
+      setCursorColumn(targetColumn);
+    }
+  }
+
+  @Override public void setCursorColumn(int targetColumn) {
+    synchronized (terminal) {
+      if (targetColumn < 0) {
+        targetColumn = 0;
+      } else if (targetColumn >= columns) {
+        targetColumn = columns - 1;
+      }
+      cursorColumn = targetColumn;
+      // We make the assumption that nobody is changing the
+      // terminal cursor except this class!
+      // This assumption gives a huge performance improvement
+      terminal.setCursorColumn(targetColumn);
+    }
+  }
+
+  @Override public void setCursorLine(int targetLine) {
+    synchronized (terminal) {
+      if (targetLine < 0) {
+        targetLine = 0;
+      } else if (targetLine >= lines) {
+        targetLine = lines - 1;
+      }
+      cursorLine = targetLine;
+      // We make the assumption that nobody is changing the terminal cursor except this class!
+      // This assumption gives a huge performance improvement.
+      terminal.setCursorLine(toAbsoluteLine(targetLine));
+    }
+  }
+
+  @Override public int getLines() {
+    synchronized (terminal) {
+      return lines;
+    }
+  }
+
+  @Override public int getColumns() {
+    synchronized (terminal) {
+      return columns;
+    }
+  }
+
+  @Override public List<IHyperlink> hyperlinksAt(int line) {
+    List<IHyperlink> found = hyperlinks.get(new Integer(line));
+    if (found == null) {
+      return emptyList();
+    }
+    return found;
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100TerminalControl.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100TerminalControl.java
new file mode 100644
index 0000000..5fa669f
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100TerminalControl.java
@@ -0,0 +1,843 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import static org.eclipse.core.runtime.Status.OK_STATUS;
+import static org.eclipse.jface.bindings.keys.SWTKeySupport.convertEventToUnmodifiedAccelerator;
+
+import static com.google.eclipse.elt.emulator.impl.TerminalPlugin.isOptionEnabled;
+
+import java.io.*;
+import java.net.SocketException;
+import java.util.List;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.contexts.*;
+import org.eclipse.ui.keys.IBindingService;
+
+import com.google.eclipse.elt.emulator.control.*;
+import com.google.eclipse.elt.emulator.impl.*;
+import com.google.eclipse.elt.emulator.model.*;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+import com.google.eclipse.elt.emulator.textcanvas.*;
+import com.google.eclipse.elt.emulator.textcanvas.PipedInputStream;
+
+public class VT100TerminalControl implements ITerminalControlForText, ITerminalControl, ITerminalViewControl {
+  protected static final String[] LINE_DELIMITERS = { "\n" };
+
+  private static final String DEFAULT_ENCODING =
+      new InputStreamReader(new ByteArrayInputStream(new byte[0])).getEncoding();
+
+  // This field holds a reference to a TerminalText object that performs all ANSI text processing on data received from
+  // the remote host and controls how text is displayed using the view's StyledText widget.
+  private final VT100Emulator terminalText;
+
+  private Display display;
+  private TextCanvas textControl;
+  private Composite rootControl;
+  private Clipboard clipboard;
+  private KeyListener keyHandler;
+  private final ITerminalListener terminalListener;
+  private String message = "";
+  private FocusListener fFocusListener;
+  private ITerminalConnector connector;
+  private final ITerminalConnector[] connectors;
+  private final PipedInputStream inputStream;
+  private String encoding = DEFAULT_ENCODING;
+  private InputStreamReader inputStreamReader;
+  private ICommandInputField commandInputField;
+  private volatile TerminalState state;
+  private final ITerminalTextData terminalModel;
+
+  volatile private Job job;
+
+  private final EditActionAccelerators editActionAccelerators = new EditActionAccelerators();
+
+  public VT100TerminalControl(ITerminalListener target, Composite wndParent, ITerminalConnector[] connectors) {
+    this.connectors = connectors;
+    terminalListener = target;
+    terminalModel = TerminalTextDataFactory.makeTerminalTextData();
+    terminalModel.setMaxHeight(1000);
+    inputStream = new PipedInputStream(8 * 1024);
+    terminalText = new VT100Emulator(terminalModel, this, null);
+    try {
+      // Use default Encoding as start, until setEncoding() is called.
+      setEncoding(null);
+    } catch (UnsupportedEncodingException e) {
+      e.printStackTrace();
+      // Fall back to local platform default encoding
+      encoding = DEFAULT_ENCODING;
+      inputStreamReader = new InputStreamReader(inputStream);
+      terminalText.setInputStreamReader(inputStreamReader);
+    }
+    setUpTerminal(wndParent);
+  }
+
+  @Override public void setEncoding(String encoding) throws UnsupportedEncodingException {
+    if (encoding == null) {
+      encoding = "ISO-8859-1";
+    }
+    inputStreamReader = new InputStreamReader(inputStream, encoding);
+    // remember encoding if above didn't throw an exception
+    this.encoding = encoding;
+    terminalText.setInputStreamReader(inputStreamReader);
+  }
+
+  @Override public String getEncoding() {
+    return encoding;
+  }
+
+  @Override public ITerminalConnector[] getConnectors() {
+    return connectors;
+  }
+
+  @Override public void copy() {
+    copy(DND.CLIPBOARD);
+  }
+
+  private void copy(int clipboardType) {
+    String selection = getSelection();
+    if (selection == null || selection.isEmpty()) {
+      return;
+    }
+    Object[] data = new Object[] { selection };
+    Transfer[] types = new Transfer[] { TextTransfer.getInstance() };
+    clipboard.setContents(data, types, clipboardType);
+  }
+
+  @Override public void paste() {
+    paste(DND.CLIPBOARD);
+  }
+
+  private void paste(int clipboardType) {
+    TextTransfer textTransfer = TextTransfer.getInstance();
+    String strText = (String) clipboard.getContents(textTransfer, clipboardType);
+    pasteString(strText);
+  }
+
+  @Override public boolean pasteString(String text) {
+    if (!isConnected()) {
+      return false;
+    }
+    if (text == null) {
+      return false;
+    }
+    if (!encoding.equals(DEFAULT_ENCODING)) {
+      sendString(text);
+    } else {
+      // TODO I do not understand why pasteString would do this here...
+      for (int i = 0; i < text.length(); i++) {
+        sendChar(text.charAt(i), false);
+      }
+    }
+    return true;
+  }
+
+  @Override public void selectAll() {
+    getTextControl().selectAll();
+  }
+
+  @Override public void sendKey(char character) {
+    Event event;
+    KeyEvent keyEvent;
+    event = new Event();
+    event.widget = getTextControl();
+    event.character = character;
+    event.keyCode = 0;
+    event.stateMask = 0;
+    event.doit = true;
+    keyEvent = new KeyEvent(event);
+    keyHandler.keyPressed(keyEvent);
+  }
+
+  @Override public void clearTerminal() {
+    getTerminalText().clearTerminal();
+  }
+
+  @Override public Clipboard getClipboard() {
+    return clipboard;
+  }
+
+  @Override public String getSelection() {
+    String text = textControl.getSelectionText();
+    return text == null ? "" : text;
+  }
+
+  @Override public boolean setFocus() {
+    return getTextControl().setFocus();
+  }
+
+  @Override public boolean isEmpty() {
+    return getTextControl().isEmpty();
+  }
+
+  @Override public boolean isDisposed() {
+    return getTextControl().isDisposed();
+  }
+
+  @Override public boolean isConnected() {
+    return state == TerminalState.CONNECTED;
+  }
+
+  @Override public void disposeTerminal() {
+    disconnectTerminal();
+    clipboard.dispose();
+    getTerminalText().dispose();
+  }
+
+  @Override public void connectTerminal() {
+    if (getTerminalConnector() == null) {
+      return;
+    }
+    terminalText.resetState();
+    if (connector.getInitializationErrorMessage() != null) {
+      showErrorMessage(NLS.bind(TerminalMessages.cannotConnectTo, connector.getName(),
+          connector.getInitializationErrorMessage()));
+      return;
+    }
+    getTerminalConnector().connect(this);
+    // clean the error message
+    setErrorMessage("");
+    waitForConnect();
+  }
+
+  @Override public ITerminalConnector getTerminalConnector() {
+    return connector;
+  }
+
+  @Override public void disconnectTerminal() {
+    Logger.log("entered.");
+    // Disconnect the remote side first.
+    if (getState() != TerminalState.CLOSED && getTerminalConnector() != null) {
+      getTerminalConnector().disconnect();
+    }
+    // Ensure that a new Job can be started; then clean up old Job.
+    Job newJob;
+    synchronized (this) {
+      newJob = job;
+      job = null;
+    }
+    if (newJob != null) {
+      newJob.cancel();
+      // Join job to avoid leaving job running after workbench shutdown (333613).
+      // Interrupt to be fast enough; cannot close fInputStream since it is re-used (bug 348700).
+      Thread t = newJob.getThread();
+      if (t != null) {
+        t.interrupt();
+      }
+      try {
+        newJob.join();
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+      }
+    }
+  }
+
+  private void waitForConnect() {
+    // TODO
+    // Eliminate this code
+    while (getState() == TerminalState.CONNECTING) {
+      if (display.readAndDispatch()) {
+        continue;
+      }
+      display.sleep();
+    }
+    if (getTextControl().isDisposed()) {
+      disconnectTerminal();
+      return;
+    }
+    if (!getMsg().isEmpty()) {
+      showErrorMessage(getMsg());
+      disconnectTerminal();
+      return;
+    }
+    getTextControl().setFocus();
+    startReaderJob();
+  }
+
+  private synchronized void startReaderJob() {
+    if (job == null) {
+      initializeJob();
+      job.setSystem(true);
+      job.schedule();
+    }
+  }
+
+  private void initializeJob() {
+    job = new Job("Terminal data reader") {
+      @Override protected IStatus run(IProgressMonitor monitor) {
+        IStatus status = OK_STATUS;
+        try {
+          while (true) {
+            while (inputStream.available() == 0 && !monitor.isCanceled()) {
+              try {
+                inputStream.waitForAvailable(500);
+              } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+              }
+            }
+            if (monitor.isCanceled()) {
+              // Do not disconnect terminal here because another reader job may already be running.
+              status = Status.CANCEL_STATUS;
+              break;
+            }
+            try {
+              // TODO: should block when no text is available!
+              terminalText.processText();
+            } catch (Exception e) {
+              disconnectTerminal();
+              status = new Status(IStatus.ERROR, TerminalPlugin.PLUGIN_ID, e.getLocalizedMessage(), e);
+              break;
+            }
+          }
+        } finally {
+          // Clean the job: start a new one when the connection gets restarted.
+          // Bug 208145: make sure we do not clean an other job that's already started (since it would become a zombie)
+          synchronized (VT100TerminalControl.this) {
+            if (job == this) {
+              job = null;
+            }
+          }
+        }
+        return status;
+      }
+    };
+  }
+
+  private void showErrorMessage(String message) {
+    String title = TerminalMessages.terminalError;
+    MessageBox mb = new MessageBox(getShell(), SWT.ICON_ERROR | SWT.OK);
+    mb.setText(title);
+    mb.setMessage(message);
+    mb.open();
+  }
+
+  protected void sendString(String string) {
+    try {
+      // Send the string after converting it to an array of bytes using the platform's default character encoding.
+      //
+      // TODO: Find a way to force this to use the ISO Latin-1 encoding.
+      // TODO: handle Encoding Errors in a better way
+      getOutputStream().write(string.getBytes(encoding));
+      getOutputStream().flush();
+    } catch (SocketException socketException) {
+      displayTextInTerminal(socketException.getMessage());
+      String strMsg = TerminalMessages.socketError + "!\n" + socketException.getMessage();
+      showErrorMessage(strMsg);
+      Logger.logException(socketException);
+      disconnectTerminal();
+    } catch (IOException ioException) {
+      showErrorMessage(TerminalMessages.ioError + "!\n" + ioException.getMessage());
+      Logger.logException(ioException);
+      disconnectTerminal();
+    }
+  }
+
+  @Override public Shell getShell() {
+    return getTextControl().getShell();
+  }
+
+  protected void sendChar(char chKey, boolean altKeyPressed) {
+    try {
+      String text = Character.toString(chKey);
+      byte[] bytes = text.getBytes(getEncoding());
+      OutputStream os = getOutputStream();
+      if (os == null) {
+        // Bug 207785: NPE when trying to send char while no longer connected
+        Logger.log("NOT sending '" + text + "' because no longer connected");
+      } else {
+        if (altKeyPressed) {
+          // When the ALT key is pressed at the same time that a character is typed, translate it into an ESCAPE
+          // followed by the character. The alternative in this case is to set the high bit of the character being
+          // transmitted, but that will cause input such as ALT-f to be seen as the ISO Latin-1 character '�', which
+          // can be confusing to European users running Emacs, for whom Alt-f should move forward a word instead of
+          // inserting the '�' character.
+          //
+          // TODO: Make the ESCAPE-vs-highbit behavior user configurable.
+          Logger.log("sending ESC + '" + text + "'");
+          getOutputStream().write('\u001b');
+          getOutputStream().write(bytes);
+        } else {
+          Logger.log("sending '" + text + "'");
+          getOutputStream().write(bytes);
+        }
+        getOutputStream().flush();
+      }
+    } catch (SocketException socketException) {
+      Logger.logException(socketException);
+      displayTextInTerminal(socketException.getMessage());
+      String message = TerminalMessages.socketError + "!\n" + socketException.getMessage();
+      showErrorMessage(message);
+      Logger.logException(socketException);
+      disconnectTerminal();
+    } catch (IOException ioException) {
+      Logger.logException(ioException);
+      displayTextInTerminal(ioException.getMessage());
+      String message = TerminalMessages.ioError + "!\n" + ioException.getMessage();
+      showErrorMessage(message);
+      Logger.logException(ioException);
+      disconnectTerminal();
+    }
+  }
+
+  @Override public void setUpTerminal(Composite parent) {
+    Assert.isNotNull(parent);
+    state = TerminalState.CLOSED;
+    setUpControls(parent);
+    setupListeners();
+  }
+
+  @Override public void setFont(Font font) {
+    getTextControl().setFont(font);
+    if (commandInputField != null) {
+      commandInputField.setFont(font);
+    }
+    // Tell the TerminalControl singleton that the font has changed.
+    textControl.onFontChange();
+    getTerminalText().fontChanged();
+  }
+
+  @Override public Font getFont() {
+    return getTextControl().getFont();
+  }
+
+  @Override public Control getControl() {
+    return textControl;
+  }
+
+  @Override public Control getRootControl() {
+    return rootControl;
+  }
+
+  protected void setUpControls(Composite parent) {
+    // The Terminal view now aims to be an ANSI-conforming terminal emulator, so it can't have a horizontal scroll bar
+    // (but a vertical one is ok). Also, do _not_ make the TextViewer read-only, because that prevents it from seeing a
+    // TAB character when the user presses TAB (instead, the TAB causes focus to switch to another Workbench control).
+    // We prevent local keyboard input from modifying the text in method TerminalVerifyKeyListener.verifyKey().
+    rootControl = new Composite(parent, SWT.NONE);
+    GridLayout layout = new GridLayout();
+    layout.marginWidth = 0;
+    layout.marginHeight = 0;
+    layout.verticalSpacing = 0;
+    rootControl.setLayout(layout);
+    ITerminalTextDataSnapshot snapshot = terminalModel.makeSnapshot();
+    // TODO how to get the initial size correctly!
+    snapshot.updateSnapshot(false);
+    ITextCanvasModel canvasModel = new PollingTextCanvasModel(snapshot);
+    textControl = new TextCanvas(rootControl, canvasModel, SWT.NONE, new TextLineRenderer(textControl, canvasModel));
+    textControl.addMouseListener(new MouseAdapter() {
+      @Override public void mouseUp(MouseEvent e) {
+        IHyperlink hyperlink = hyperlinkAt(e);
+        if (hyperlink != null) {
+          hyperlink.open();
+        }
+      }
+    });
+    textControl.addMouseMoveListener(new MouseMoveListener() {
+      @Override public void mouseMove(MouseEvent e) {
+        IHyperlink hyperlink = hyperlinkAt(e);
+        int cursorId = (hyperlink == null) ? SWT.CURSOR_IBEAM : SWT.CURSOR_HAND;
+        Cursor newCursor = textControl.getDisplay().getSystemCursor(cursorId);
+        if (!newCursor.equals(textControl.getCursor())) {
+          textControl.setCursor(newCursor);
+        }
+      }
+    });
+    textControl.setLayoutData(new GridData(GridData.FILL_BOTH));
+    textControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+    textControl.addResizeHandler(new TextCanvas.ResizeListener() {
+      @Override public void sizeChanged(int lines, int columns) {
+        terminalText.setDimensions(lines, columns);
+      }
+    });
+    textControl.addMouseListener(new MouseAdapter() {
+      @Override public void mouseUp(MouseEvent e) {
+        // update selection used by middle mouse button paste
+        if (e.button == 1 && getSelection().length() > 0) {
+          copy(DND.SELECTION_CLIPBOARD);
+        }
+      }
+    });
+    display = getTextControl().getDisplay();
+    clipboard = new Clipboard(display);
+    setFont(JFaceResources.getTextFont());
+  }
+
+  private IHyperlink hyperlinkAt(MouseEvent e) {
+    Point p = textControl.screenPointToCell(e.x, e.y);
+    if (p != null) {
+      List<IHyperlink> hyperlinks = terminalText.hyperlinksAt(p.y);
+      for (IHyperlink hyperlink : hyperlinks) {
+        IRegion region = hyperlink.getHyperlinkRegion();
+        int start = region.getOffset();
+        int end = start + region.getLength() - 1;
+        if (p.x >= start && p.x <= end) {
+          return hyperlink;
+        }
+      }
+    }
+    return null;
+  }
+
+  protected void setupListeners() {
+    keyHandler = new TerminalKeyHandler();
+    fFocusListener = new TerminalFocusListener();
+    getTextControl().addKeyListener(keyHandler);
+    getTextControl().addFocusListener(fFocusListener);
+  }
+
+  @Override public void displayTextInTerminal(String text) {
+    writeToTerminal("\r\n" + text + "\r\n");
+  }
+
+  private void writeToTerminal(String text) {
+    try {
+      getRemoteToTerminalOutputStream().write(text.getBytes(encoding));
+    } catch (UnsupportedEncodingException e) {
+      // should never happen!
+      e.printStackTrace();
+    } catch (IOException e) {
+      // should never happen!
+      e.printStackTrace();
+    }
+  }
+
+  @Override public OutputStream getRemoteToTerminalOutputStream() {
+    if (Logger.isLogEnabled()) {
+      return new LoggingOutputStream(inputStream.getOutputStream());
+    }
+    return inputStream.getOutputStream();
+  }
+
+  protected boolean isLogCharEnabled() {
+    return isOptionEnabled(Logger.TRACE_DEBUG_LOG_CHAR);
+  }
+
+  protected boolean isLogBufferSizeEnabled() {
+    return isOptionEnabled(Logger.TRACE_DEBUG_LOG_BUFFER_SIZE);
+  }
+
+  @Override public OutputStream getOutputStream() {
+    if (getTerminalConnector() != null) {
+      return getTerminalConnector().getTerminalToRemoteStream();
+    }
+    return null;
+  }
+
+  @Override public void setErrorMessage(String message) {
+    this.message = message;
+  }
+
+  public String getMsg() {
+    return message;
+  }
+
+  protected TextCanvas getTextControl() {
+    return textControl;
+  }
+
+  public VT100Emulator getTerminalText() {
+    return terminalText;
+  }
+
+  protected class TerminalFocusListener implements FocusListener {
+    private IContextActivation contextActivation = null;
+
+    @Override public void focusGained(FocusEvent event) {
+      // Disable all keyboard accelerators (e.g., Control-B) so the Terminal view can see every key stroke. Without
+      // this, Emacs, vi, and Bash are unusable in the terminal view.
+      IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
+      bindingService.setKeyFilterEnabled(false);
+      // The above code fails to cause Eclipse to disable menu-activation accelerators (e.g., Alt-F for the File menu),
+      // so we set the command context to be the Terminal view's command context. This enables us to override
+      // menu-activation accelerators with no-op commands in our plugin.xml file, which enables the terminal view to see
+      // absolutly _all_ key-presses.
+      IContextService contextService = (IContextService) PlatformUI.getWorkbench().getAdapter(IContextService.class);
+      contextActivation = contextService.activateContext("com.google.eclipse.elt.emulator.TerminalContext");
+    }
+
+    @Override public void focusLost(FocusEvent event) {
+      // Enable all key bindings.
+      IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
+      bindingService.setKeyFilterEnabled(true);
+      // Restore the command context to its previous value.
+      IContextService contextService = (IContextService) PlatformUI.getWorkbench().getAdapter(IContextService.class);
+      contextService.deactivateContext(contextActivation);
+    }
+  }
+
+  protected class TerminalKeyHandler extends KeyAdapter {
+    @Override public void keyPressed(KeyEvent event) {
+      if (getState() == TerminalState.CONNECTING) {
+        return;
+      }
+      int accelerator = convertEventToUnmodifiedAccelerator(event);
+      if (editActionAccelerators.isCopyAction(accelerator)) {
+        copy();
+        return;
+      }
+      if (editActionAccelerators.isPasteAction(accelerator)) {
+        paste();
+        return;
+      }
+      // We set the event.doit to false to prevent any further processing of this key event. The only reason this is
+      // here is because I was seeing the F10 key both send an escape sequence (due to this method) and switch focus to
+      // the Workbench File menu (forcing the user to click in the terminal view again to continue entering text). This
+      // fixes that.
+      event.doit = false;
+      char character = event.character;
+      if (state == TerminalState.CLOSED) {
+        // Pressing ENTER while not connected causes us to connect.
+        if (character == '\r') {
+          connectTerminal();
+          return;
+        }
+        // Ignore all other keyboard input when not connected.
+        // Allow other key handlers (such as Ctrl+F1) do their work.
+        event.doit = true;
+        return;
+      }
+      // Manage the Del key
+      if (event.keyCode == 0x000007f) {
+        sendString("\u001b[3~");
+        return;
+      }
+      // If the event character is NUL ('\u0000'), then a special key was pressed (e.g., PageUp, PageDown, an arrow key,
+      // a function key, Shift, Alt, Control, etc.). The one exception is when the user presses Control-@, which sends a
+      // NUL character, in which case we must send the NUL to the remote endpoint. This is necessary so that Emacs will
+      // work correctly, because Control-@ (i.e., NUL) invokes Emacs' set-mark-command when Emacs is running on a
+      // terminal. When the user presses Control-@, the keyCode is 50.
+      if (character == '\u0000' && event.keyCode != 50) {
+        // A special key was pressed. Figure out which one it was and send the appropriate ANSI escape sequence.
+        //
+        // IMPORTANT: Control will not enter this method for these special keys unless certain <keybinding> tags are
+        // present in the plugin.xml file for the Terminal view. Do not delete those tags.
+        switch (event.keyCode) {
+        case 0x1000001: // Up arrow.
+          sendString("\u001b[A");
+          break;
+        case 0x1000002: // Down arrow.
+          sendString("\u001b[B");
+          break;
+        case 0x1000003: // Left arrow.
+          sendString("\u001b[D");
+          break;
+        case 0x1000004: // Right arrow.
+          sendString("\u001b[C");
+          break;
+        case 0x1000005: // PgUp key.
+          sendString("\u001b[5~");
+          break;
+        case 0x1000006: // PgDn key.
+          sendString("\u001b[6~");
+          break;
+        case 0x1000007: // Home key.
+          sendString("\u001b[H");
+          break;
+        case 0x1000008: // End key.
+          sendString("\u001b[F");
+          break;
+        case 0x1000009: // Insert.
+          sendString("\u001b[2~");
+          break;
+        case 0x100000a: // F1 key.
+          if ((event.stateMask & SWT.CTRL) != 0) {
+            // Allow Ctrl+F1 to act locally as well as on the remote, because it is typically non-intrusive
+            event.doit = true;
+          }
+          sendString("\u001b[M");
+          break;
+        case 0x100000b: // F2 key.
+          sendString("\u001b[N");
+          break;
+        case 0x100000c: // F3 key.
+          sendString("\u001b[O");
+          break;
+        case 0x100000d: // F4 key.
+          sendString("\u001b[P");
+          break;
+        case 0x100000e: // F5 key.
+          sendString("\u001b[Q");
+          break;
+        case 0x100000f: // F6 key.
+          sendString("\u001b[R");
+          break;
+        case 0x1000010: // F7 key.
+          sendString("\u001b[S");
+          break;
+        case 0x1000011: // F8 key.
+          sendString("\u001b[T");
+          break;
+        case 0x1000012: // F9 key.
+          sendString("\u001b[U");
+          break;
+        case 0x1000013: // F10 key.
+          sendString("\u001b[V");
+          break;
+        case 0x1000014: // F11 key.
+          sendString("\u001b[W");
+          break;
+        case 0x1000015: // F12 key.
+          sendString("\u001b[X");
+          break;
+        default:
+          // Ignore other special keys. Control flows through this case when the user presses SHIFT, CONTROL, ALT, and
+          // any other key not handled by the above cases.
+          break;
+        }
+        // It's ok to return here, because we never locally echo special keys.
+        return;
+      }
+      // To fix SPR 110341, we consider the Alt key to be pressed only when the Control key is _not_ also pressed. This
+      // works around a bug in SWT where, on European keyboards, the AltGr key being pressed appears to us as Control
+      // + Alt being pressed simultaneously.
+      Logger.log("stateMask = " + event.stateMask);
+      boolean altKeyPressed = (((event.stateMask & SWT.ALT) != 0) && ((event.stateMask & SWT.CTRL) == 0));
+      if (!altKeyPressed && (event.stateMask & SWT.CTRL) != 0 && character == ' ') {
+        // Send a NUL character -- many terminal emulators send NUL when Control-Space is pressed. This is used to set
+        // the mark in Emacs.
+        character = '\u0000';
+      }
+      sendChar(character, altKeyPressed);
+      // Special case: When we are in a TCP connection and echoing characters locally, send a LF after sending a CR.
+      // ISSUE: Is this absolutely required?
+      if (character == '\r' && getTerminalConnector() != null && isConnected() && getTerminalConnector().isLocalEcho()) {
+        sendChar('\n', false);
+      }
+      // Now decide if we should locally echo the character we just sent. We do _not_ locally echo the character if any
+      // of these conditions are true:
+      //
+      // * This is a serial connection.
+      // * This is a TCP connection (i.e., m_telnetConnection is not null) and the remote endpoint is not a TELNET
+      //   server.
+      // * The ALT (or META) key is pressed.
+      // * The character is any of the first 32 ISO Latin-1 characters except Control-I or Control-M.
+      // * The character is the DELETE character.
+      if (getTerminalConnector() == null || getTerminalConnector().isLocalEcho() == false || altKeyPressed
+          || (character >= '\u0001' && character < '\t') || (character > '\t' && character < '\r')
+          || (character > '\r' && character <= '\u001f') || character == '\u007f') {
+        // No local echoing.
+        return;
+      }
+      // Locally echo the character.
+      StringBuilder charBuffer = new StringBuilder();
+      charBuffer.append(character);
+      // If the character is a carriage return, we locally echo it as a CR + LF combination.
+      if (character == '\r') {
+        charBuffer.append('\n');
+      }
+      writeToTerminal(charBuffer.toString());
+    }
+  }
+
+  @Override public void setTerminalTitle(String title) {
+    terminalListener.setTerminalTitle(title);
+  }
+
+  @Override public TerminalState getState() {
+    return state;
+  }
+
+  @Override public void setState(TerminalState state) {
+    this.state = state;
+    terminalListener.setState(state);
+    // enable the (blinking) cursor if the terminal is connected
+    runAsyncInDisplayThread(new Runnable() {
+      @Override public void run() {
+        if (textControl != null && !textControl.isDisposed()) {
+          textControl.setCursorEnabled(isConnected());
+        }
+      }
+    });
+  }
+
+  private void runAsyncInDisplayThread(Runnable runnable) {
+    if (Display.findDisplay(Thread.currentThread()) != null) {
+      runnable.run();
+    } else if (PlatformUI.isWorkbenchRunning()) {
+      PlatformUI.getWorkbench().getDisplay().asyncExec(runnable);
+      // else should not happen and we ignore it...
+    }
+  }
+
+  @Override public String getSettingsSummary() {
+    if (getTerminalConnector() != null) {
+      return getTerminalConnector().getSettingsSummary();
+    }
+    return "";
+  }
+
+  @Override public void setConnector(ITerminalConnector connector) {
+    this.connector = connector;
+  }
+
+  @Override public ICommandInputField getCommandInputField() {
+    return commandInputField;
+  }
+
+  @Override public void setCommandInputField(ICommandInputField inputField) {
+    if (commandInputField != null) {
+      commandInputField.dispose();
+    }
+    commandInputField = inputField;
+    if (commandInputField != null) {
+      commandInputField.createControl(rootControl, this);
+    }
+    if (rootControl.isVisible()) {
+      rootControl.layout(true);
+    }
+  }
+
+  @Override public int getBufferLineLimit() {
+    return terminalModel.getMaxHeight();
+  }
+
+  @Override public void setBufferLineLimit(int bufferLineLimit) {
+    if (bufferLineLimit <= 0) {
+      return;
+    }
+    synchronized (terminalModel) {
+      if (terminalModel.getHeight() > bufferLineLimit) {
+        terminalModel.setDimensions(bufferLineLimit, terminalModel.getWidth());
+      }
+      terminalModel.setMaxHeight(bufferLineLimit);
+    }
+  }
+
+  @Override public boolean isScrollLockOn() {
+    return textControl.isScrollLockOn();
+  }
+
+  @Override public void setScrollLockOn(boolean on) {
+    textControl.setScrollLockOn(on);
+  }
+
+  @Override public void setInvertedColors(boolean invert) {
+    textControl.setInvertedColors(invert);
+  }
+
+  public void setColors(RGB background, RGB foreground) {
+    textControl.setColors(background, foreground);
+  }
+
+  public void setBlinkingCursor(boolean useBlinkingCursor) {
+    textControl.setBlinkingCursor(useBlinkingCursor);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/HttpHyperlinkFactory.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/HttpHyperlinkFactory.java
new file mode 100644
index 0000000..8b2e4d0
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/HttpHyperlinkFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.emulator.hyperlink;
+
+import java.util.*;
+import java.util.regex.*;
+
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class HttpHyperlinkFactory implements IHyperlinkFactory {
+  private static Pattern URL_PATTERN =
+      Pattern.compile("http(s)?:\\/{2}[\\d\\w-]+(\\.[\\d\\w-]+)*(?:(?:\\/[^\\s/]*))*(\\:[\\d]+)?");
+
+  @Override public List<IHyperlink> hyperlinksIn(int column, String text) {
+    if (text == null) {
+      return NO_HYPERLINKS;
+    }
+    List<IHyperlink> hyperlinks = new ArrayList<IHyperlink>();
+    Matcher matcher = URL_PATTERN.matcher(text);
+    int start = 0;
+    while (matcher.find(start)) {
+      String url = matcher.group().trim();
+      if (url.isEmpty()) {
+        continue;
+      }
+      IRegion region = new Region(column + matcher.start(), url.length());
+      hyperlinks.add(new UrlHyperlink(region, url));
+      start = matcher.end();
+    }
+    return hyperlinks.isEmpty() ? NO_HYPERLINKS : hyperlinks;
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/IHyperlinkFactory.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/IHyperlinkFactory.java
new file mode 100644
index 0000000..f58919a
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/IHyperlinkFactory.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.emulator.hyperlink;
+
+import static java.util.Collections.emptyList;
+
+import java.util.List;
+
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public interface IHyperlinkFactory {
+  List<IHyperlink> NO_HYPERLINKS = emptyList();
+
+  List<IHyperlink> hyperlinksIn(int column, String text);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/UrlHyperlink.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/UrlHyperlink.java
new file mode 100644
index 0000000..bec695b
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/UrlHyperlink.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.emulator.hyperlink;
+
+import java.net.*;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.URLHyperlink;
+import org.eclipse.ui.*;
+import org.eclipse.ui.browser.*;
+
+import com.google.eclipse.elt.emulator.impl.TerminalPlugin;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class UrlHyperlink extends URLHyperlink {
+  public UrlHyperlink(IRegion region, String urlString) {
+    super(region, urlString);
+  }
+
+  @Override public void open() {
+    // Create the browser
+    IWorkbenchBrowserSupport support= PlatformUI.getWorkbench().getBrowserSupport();
+    IWebBrowser browser;
+    try {
+      browser= support.createBrowser(null);
+    } catch (PartInitException e) {
+      logErrorOpeningUrl(e);
+      return;
+    }
+    URL url = null;
+    try {
+      url = new URL(getURLString());
+      browser.openURL(url);
+    } catch (PartInitException e) {
+      openInExternalBrowser(url);
+    } catch (MalformedURLException e) {
+      logErrorOpeningUrl(e);
+    }
+  }
+
+  private void openInExternalBrowser(URL url) {
+    try {
+      PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser().openURL(url);
+    } catch (Exception e) {
+      logErrorOpeningUrl(e);
+    }
+  }
+
+  private void logErrorOpeningUrl(Exception e) {
+    e.printStackTrace();
+    String format = "Unable to open URL '%s'";
+    TerminalPlugin.log(String.format(format, getURLString()), e);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/ITerminalControlForText.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/ITerminalControlForText.java
new file mode 100644
index 0000000..37d7bd1
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/ITerminalControlForText.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.impl;
+
+import java.io.OutputStream;
+
+import com.google.eclipse.elt.emulator.provisional.api.*;
+
+public interface ITerminalControlForText {
+
+  TerminalState getState();
+
+  void setState(TerminalState state);
+
+  void setTerminalTitle(String title);
+
+  ITerminalConnector getTerminalConnector();
+
+  OutputStream getOutputStream();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalInputStream.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalInputStream.java
new file mode 100644
index 0000000..a41a0e0
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalInputStream.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 1996, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River)
+ * Douglas Lea (Addison Wesley) - [cq:1552] BoundedBufferWithStateTracking adapted to BoundedByteBuffer
+ *******************************************************************************/
+
+package com.google.eclipse.elt.emulator.impl;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.swt.widgets.Display;
+
+import com.google.eclipse.elt.emulator.util.BoundedByteBuffer;
+
+/**
+ * The main purpose of this class is to start a {@code Runnable} in the display thread when data is available and to
+ * pretend no data is available after a given amount of time the {@code Runnable} is running.
+ */
+public class TerminalInputStream extends InputStream {
+  // The maximum time in milliseconds the 'notifyChange' runs until 'ready()' returns false.
+  private final int uiTimeout;
+
+  // The output stream used by the terminal backend to write to the terminal.
+  protected final OutputStream outputStream;
+
+  // This runnable is called every time some characters are available from.
+  private final Runnable notifyChange;
+
+  // A shared timer for all terminals. This times is used to limit the time used in the display thread.
+  static Timer timer = new Timer(false);
+
+  // A blocking byte queue.
+  private final BoundedByteBuffer queue;
+
+  // The maximum amount of data read and written in one shot. The timer cannot interrupt reading this amount of data.
+  // This is used as optimization, because reading single characters can be very inefficient, because each call is
+  // synchronized.
+  // Block size must be smaller than the Queue capacity!
+  final int BLOCK_SIZE = 64;
+
+  // The runnable that is scheduled in the display tread. Takes care of the timeout management. It calls the
+  // 'notifyChange'.
+  // Synchronized with 'queue'.
+  private Runnable runnable;
+
+  // Used as flag to indicate that the current runnable has used enough time in the display thread. This variable is set
+  // by a timer thread after the Runnable starts to run in the Display thread after 'uiTimeout'.
+  // Synchronized with 'queue'.
+  private boolean enoughDisplayTime;
+
+  /**
+   * Constructor.
+   *
+   * @param bufferSize the size of the buffer of the output stream
+   * @param uiTimeout the maximum time the {@code notifyChange} {@code Runnable} runs. It will be rescheduled after
+   *         {@code uiTimeout} if input data is still available.
+   * @param notifyChange a {@code Runnable} that is posted to the display thread via {@link Display#asyncExec}. The
+   *         {@code Runnable} is posted several times!
+   */
+  public TerminalInputStream(int bufferSize, int uiTimeout, Runnable notifyChange) {
+    outputStream = new TerminalOutputStream();
+    this.notifyChange = notifyChange;
+    int capacity = bufferSize;
+    if (capacity < BLOCK_SIZE) {
+      capacity = 2 * BLOCK_SIZE;
+    }
+    queue = new BoundedByteBuffer(capacity);
+    this.uiTimeout = uiTimeout;
+  }
+
+  /**
+   * Posts the {@code Runnable} {@link #notifyChange} to the display thread, unless the {@code Runnable} is already
+   * scheduled. It will make {@link #ready} return {@code false} after {@link #uiTimeout} milliseconds.
+   */
+  void bytesAreAvailable() {
+    // synchronize on the queue to reduce the locks
+    synchronized (queue) {
+      if (runnable == null) {
+        runnable = new Runnable() {
+          @Override public void run() {
+            synchronized (queue) {
+              runnable = null;
+            }
+            startTimer(uiTimeout);
+            notifyChange.run();
+          }
+        };
+        // TODO: Make sure we don't create a display if the display is disposed.
+        Display.getDefault().asyncExec(runnable);
+      }
+    }
+  }
+
+  /**
+   * Starts a timer that sets {@link #enoughDisplayTime} to {@code true} after the given milliseconds.
+   *
+   * @param milliseconds the time after which {@code enoughDisplayTime} is set to {@code true}.
+   */
+  void startTimer(int milliseconds) {
+    synchronized (queue) {
+      enoughDisplayTime = false;
+    }
+    timer.schedule(new TimerTask() {
+      @Override public void run() {
+        synchronized (queue) {
+          enoughDisplayTime = true;
+          // there is some data available
+          if (queue.size() > 0) {
+            // schedule a new runnable to do the work
+            bytesAreAvailable();
+          }
+        }
+      }
+    }, milliseconds);
+  }
+
+  /**
+   * Returns the output stream used by the back-end to write to the terminal.
+   *
+   * @return the output stream used by the back-end to write to the terminal.
+   */
+  public OutputStream getOutputStream() {
+    return outputStream;
+  }
+
+  /**
+   * Indicate whether a character is available for the terminal to show. Must be called in the display thread.
+   * @return {@code true} if a character is available for the terminal to show, {@code false} otherwise.
+   */
+  @Override public int available() {
+    int available;
+    synchronized (queue) {
+      if (enoughDisplayTime) {
+        return 0;
+      }
+      available = queue.size();
+    }
+    // Limit the available amount of data. Otherwise our trick of limiting the time spend reading might not work.
+    if (available > BLOCK_SIZE) {
+      available = BLOCK_SIZE;
+    }
+    return available;
+  }
+
+  /**
+   * Returns the next available byte. Checks with {@link #available} if characters are available.
+   *
+   * @return the next available byte.
+   */
+  @Override public int read() throws IOException {
+    try {
+      return queue.read();
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+      return -1;
+    }
+  }
+
+  /**
+   * Closing a {@code ByteArrayInputStream} has no effect. The methods in this class can be called after the stream has
+   * been closed without generating an {@code IOException}.
+   */
+  @Override public void close() {}
+
+  @Override public int read(byte[] b, int off, int len) throws IOException {
+    int n = 0;
+    // Read as much as we can using a single synchronized statement.
+    synchronized (queue) {
+      try {
+        // Make sure that not more than BLOCK_SIZE is read in one call.
+        while (queue.size() > 0 && n < len && n < BLOCK_SIZE) {
+          b[off + n] = queue.read();
+          n++;
+        }
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+      }
+    }
+    return n;
+  }
+
+  /**
+   * An output stream that calls {@link TerminalInputStream#textAvailable} every time data is written to the stream. The
+   * data is written to {@link TerminalInputStream#queue}.
+   *
+   */
+  class TerminalOutputStream extends OutputStream {
+    @Override public void write(byte[] b, int off, int len) throws IOException {
+      try {
+        // optimization to avoid many synchronized sections: put the data in junks into the queue.
+        int newOff = off;
+        int end = off + len;
+        while (newOff < end) {
+          int n = newOff + BLOCK_SIZE;
+          if (n > end) {
+            n = end;
+          }
+          // now block the queue for the time we need to
+          // add some characters
+          synchronized (queue) {
+            for (int i = newOff; i < n; i++) {
+              queue.write(b[i]);
+            }
+            bytesAreAvailable();
+          }
+          newOff += BLOCK_SIZE;
+        }
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+      }
+    }
+
+    @Override public void write(int b) throws IOException {
+      try {
+        // A kind of optimization, because both calls use the queue lock.
+        synchronized (queue) {
+          queue.write((byte) b);
+          bytesAreAvailable();
+        }
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+      }
+    }
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.java
new file mode 100644
index 0000000..fa1755e
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.impl;
+
+import org.eclipse.osgi.util.NLS;
+
+public class TerminalMessages extends NLS {
+  public static String terminalError;
+  public static String socketError;
+  public static String ioError;
+  public static String cannotConnectTo;
+  public static String notInitialized;
+
+  static {
+    Class<?> clazz = TerminalMessages.class;
+    NLS.initializeMessages(clazz.getName(), clazz);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.properties b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.properties
new file mode 100644
index 0000000..1fecdb3
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2003, 2009 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+# NLS_MESSAGEFORMAT_VAR
+
+terminalError = Terminal Error
+socketError = Socket Error
+ioError = IO Error
+cannotConnectTo = Cannot initialize {0}:\n{1}
+notInitialized = Not Initialized
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalPlugin.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalPlugin.java
new file mode 100644
index 0000000..1a9fe5b
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalPlugin.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.impl;
+
+import static org.eclipse.core.runtime.IStatus.ERROR;
+
+import static com.google.eclipse.elt.emulator.actions.Images.*;
+
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.resource.*;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+public class TerminalPlugin extends AbstractUIPlugin {
+  public static final String PLUGIN_ID = "com.google.eclipse.elt.emulator";
+
+  protected static TerminalPlugin plugin;
+
+  public TerminalPlugin() {
+    plugin = this;
+  }
+
+  public static TerminalPlugin getDefault() {
+    return plugin;
+  }
+
+  public static boolean isOptionEnabled(String option) {
+    String enabled = Platform.getDebugOption(option);
+    if (enabled == null) {
+      return false;
+    }
+    return Boolean.parseBoolean(enabled);
+  }
+
+  @Override protected void initializeImageRegistry(ImageRegistry imageRegistry) {
+    try {
+      // Local tool-bars
+      putImageInRegistry(imageRegistry, IMAGE_CLCL_CLEAR_ALL, IMAGE_DIR_LOCALTOOL + "clear_co.gif");
+      // Enabled local tool-bars
+      putImageInRegistry(imageRegistry, IMAGE_ELCL_CLEAR_ALL, IMAGE_DIR_ELCL + "clear_co.gif");
+      // Disabled local tool-bars
+      putImageInRegistry(imageRegistry, IMAGE_DLCL_CLEAR_ALL, IMAGE_DIR_DLCL + "clear_co.gif");
+    } catch (MalformedURLException malformedURLException) {
+      malformedURLException.printStackTrace();
+    }
+  }
+
+  protected void putImageInRegistry(
+      ImageRegistry imageRegistry, String strKey, String relativePath) throws MalformedURLException {
+    URL url = TerminalPlugin.getDefault().getBundle().getEntry(relativePath);
+    ImageDescriptor imageDescriptor = ImageDescriptor.createFromURL(url);
+    imageRegistry.put(strKey, imageDescriptor);
+  }
+
+  public static void log(String message, Exception error) {
+    getDefault().getLog().log(new Status(ERROR, PLUGIN_ID, message, error));
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/ISnapshotChanges.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/ISnapshotChanges.java
new file mode 100644
index 0000000..b4f0fe0
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/ISnapshotChanges.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import com.google.eclipse.elt.emulator.model.ITerminalTextData;
+
+public interface ISnapshotChanges {
+  void markLineChanged(int line);
+
+  void markLinesChanged(int line, int size);
+
+  /**
+   * Marks all lines within the scrolling region changed and resets the scrolling information.
+   */
+  void convertScrollingIntoChanges();
+
+  boolean hasChanged();
+
+  void scroll(int startLine, int size, int shift);
+
+  void setAllChanged(int height);
+
+  int getFirstChangedLine();
+
+  int getLastChangedLine();
+
+  int getScrollWindowStartLine();
+
+  int getScrollWindowSize();
+
+  int getScrollWindowShift();
+
+  boolean hasLineChanged(int line);
+
+  void markDimensionsChanged();
+
+  boolean hasDimensionsChanged();
+
+  void markCursorChanged();
+
+  boolean hasTerminalChanged();
+
+  void setTerminalChanged();
+
+  void copyChangedLines(ITerminalTextData destination, ITerminalTextData source);
+
+  void setInterestWindow(int startLine, int size);
+
+  int getInterestWindowStartLine();
+
+  int getInterestWindowSize();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SnapshotChanges.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SnapshotChanges.java
new file mode 100644
index 0000000..fc43d0d
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SnapshotChanges.java
@@ -0,0 +1,333 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.ITerminalTextData;
+
+/**
+ * Collects the changes of the {@link ITerminalTextData}.
+ */
+public class SnapshotChanges implements ISnapshotChanges {
+  private int firstChangedLine;
+  private int lastChangedLine;
+  private int scrollWindowStartLine;
+  private int scrollWindowSize;
+  private int scrollWindowShift;
+  private boolean dontTrackScrolling;
+  private boolean[] changedLines;
+  private int interestWindowSize;
+  private int interestWindowStartLine;
+  private boolean fDimensionsChanged;
+  private boolean fTerminalHasChanged;
+  private boolean cursorHasChanged;
+
+  public SnapshotChanges(int windowStart, int windowSize) {
+    this(windowStart + windowSize);
+    interestWindowStartLine = windowStart;
+    interestWindowSize = windowSize;
+  }
+
+  public SnapshotChanges(int changedLineCount) {
+    setChangedLineCount(changedLineCount);
+    firstChangedLine = Integer.MAX_VALUE;
+    lastChangedLine = -1;
+  }
+
+  // Indicates whether the range overlaps with the interest window.
+  private boolean isInInterestWindow(int line, int size) {
+    if (interestWindowSize <= 0) {
+      return true;
+    }
+    if (line + size <= interestWindowStartLine || line >= interestWindowStartLine + interestWindowSize) {
+      return false;
+    }
+    return true;
+  }
+
+  private boolean isInInterestWindow(int line) {
+    if (interestWindowSize <= 0) {
+      return true;
+    }
+    if (line < interestWindowStartLine || line >= interestWindowStartLine + interestWindowSize) {
+      return false;
+    }
+    return true;
+  }
+
+  private int fitLineToWindow(int line) {
+    if (interestWindowSize <= 0) {
+      return line;
+    }
+    if (line < interestWindowStartLine) {
+      return interestWindowStartLine;
+    }
+    return line;
+  }
+
+  /**
+   * The result is only defined if {@link #isInInterestWindow(int, int)} returns {@code true}.
+   * <p>
+   * Note:{@link #fitLineToWindow(int)} has to be called on the line to move the window correctly.
+   * </p>
+   * @param line the line <b>before</b> {@link #fitLineToWindow(int)} has been called.
+   * @param size
+   * @return the adjusted size.
+   */
+  private int fitSizeToWindow(int line, int size) {
+    if (interestWindowSize <= 0) {
+      return size;
+    }
+    if (line < interestWindowStartLine) {
+      size -= interestWindowStartLine - line;
+      line = interestWindowStartLine;
+    }
+    if (line + size > interestWindowStartLine + interestWindowSize) {
+      size = interestWindowStartLine + interestWindowSize - line;
+    }
+    return size;
+  }
+
+  @Override public void markLineChanged(int line) {
+    if (!isInInterestWindow(line)) {
+      return;
+    }
+    line = fitLineToWindow(line);
+    if (line < firstChangedLine) {
+      firstChangedLine = line;
+    }
+    if (line > lastChangedLine) {
+      lastChangedLine = line;
+    }
+    // In case the terminal got resized we expand don't remember the changed line because there is nothing to copy.
+    if (line < getChangedLineCount()) {
+      setChangedLine(line, true);
+    }
+  }
+
+  @Override public void markLinesChanged(int line, int size) {
+    if (size <= 0 || !isInInterestWindow(line, size)) {
+      return;
+    }
+    // Do not exceed the bounds of changedLines. The terminal might have been resized and we can only keep changes for
+    // the size of the previous terminal.
+    size = fitSizeToWindow(line, size);
+    line = fitLineToWindow(line);
+    int m = Math.min(line + size, getChangedLineCount());
+    for (int i = line; i < m; i++) {
+      setChangedLine(i, true);
+    }
+    // this sets firstChangedLine as well
+    markLineChanged(line);
+    // this sets lastChangedLine as well
+    markLineChanged(line + size - 1);
+  }
+
+  @Override public void markCursorChanged() {
+    cursorHasChanged = true;
+  }
+
+  @Override public void convertScrollingIntoChanges() {
+    markLinesChanged(scrollWindowStartLine, scrollWindowSize);
+    scrollWindowStartLine = 0;
+    scrollWindowSize = 0;
+    scrollWindowShift = 0;
+  }
+
+  @Override public boolean hasChanged() {
+    if (firstChangedLine != Integer.MAX_VALUE || lastChangedLine > 0 || scrollWindowShift != 0 || fDimensionsChanged
+        || cursorHasChanged) {
+      return true;
+    }
+    return false;
+  }
+
+  @Override public void markDimensionsChanged() {
+    fDimensionsChanged = true;
+  }
+
+  @Override public boolean hasDimensionsChanged() {
+    return fDimensionsChanged;
+  }
+
+  @Override public boolean hasTerminalChanged() {
+    return fTerminalHasChanged;
+  }
+
+  @Override public void setTerminalChanged() {
+    fTerminalHasChanged = true;
+  }
+
+  @Override public void scroll(int startLine, int size, int shift) {
+    size = fitSizeToWindow(startLine, size);
+    startLine = fitLineToWindow(startLine);
+    // Let's track only negative shifts.
+    if (dontTrackScrolling) {
+      // We are in a state where we cannot track scrolling so let's simply mark the scrolled lines as changed.
+      markLinesChanged(startLine, size);
+    } else if (shift >= 0) {
+      // We cannot handle positive scroll forget about clever caching of scroll events.
+      doNotTrackScrollingAnymore();
+      // Mark all lines inside the scroll region as changed.
+      markLinesChanged(startLine, size);
+    } else {
+      // We have already scrolled.
+      if (scrollWindowShift < 0) {
+        // We have already scrolled.
+        if (scrollWindowStartLine == startLine && scrollWindowSize == size) {
+          // We are scrolling the same region again?
+          scrollWindowShift += shift;
+          scrollChangesLinesWithNegativeShift(startLine, size, shift);
+        } else {
+          // Mark all lines in the old scroll region as changed.
+          doNotTrackScrollingAnymore();
+          markLinesChanged(startLine, size);
+        }
+      } else {
+        // First scroll in this change -- we just notify it
+        scrollWindowStartLine = startLine;
+        scrollWindowSize = size;
+        scrollWindowShift = shift;
+        scrollChangesLinesWithNegativeShift(startLine, size, shift);
+      }
+    }
+  }
+
+  // Some incompatible scrolling occurred. We cannot do the scroll optimization anymore.
+  private void doNotTrackScrollingAnymore() {
+    if (scrollWindowSize > 0) {
+      // Convert the current scrolling into changes.
+      markLinesChanged(scrollWindowStartLine, scrollWindowSize);
+      scrollWindowStartLine = 0;
+      scrollWindowSize = 0;
+      scrollWindowShift = 0;
+    }
+    // Don't be clever on scrolling anymore.
+    dontTrackScrolling = true;
+  }
+
+  private void scrollChangesLinesWithNegativeShift(int line, int n, int shift) {
+    Assert.isTrue(shift < 0);
+    // Scroll the region. Don't run out of bounds!
+    int m = Math.min(line + n + shift, getChangedLineCount() + shift);
+    for (int i = line; i < m; i++) {
+      setChangedLine(i, hasLineChanged(i - shift));
+      // Move the first changed line up. We don't have to move the maximum down, because with a shift scroll, the max is
+      // moved by the next loop in this method
+      if (i < firstChangedLine && hasLineChanged(i)) {
+        firstChangedLine = i;
+      }
+    }
+    // Mark the "opened" lines as changed.
+    for (int i = Math.max(0, line + n + shift); i < line + n; i++) {
+      markLineChanged(i);
+    }
+  }
+
+  @Override public void setAllChanged(int height) {
+    scrollWindowStartLine = 0;
+    scrollWindowSize = 0;
+    scrollWindowShift = 0;
+    firstChangedLine = fitLineToWindow(0);
+    lastChangedLine = firstChangedLine + fitSizeToWindow(0, height) - 1;
+    // No need to keep an array of changes anymore.
+    setChangedLineCount(0);
+  }
+
+  @Override public int getFirstChangedLine() {
+    return firstChangedLine;
+  }
+
+  @Override public int getLastChangedLine() {
+    return lastChangedLine;
+  }
+
+  @Override public int getScrollWindowStartLine() {
+    return scrollWindowStartLine;
+  }
+
+  @Override public int getScrollWindowSize() {
+    return scrollWindowSize;
+  }
+
+  @Override public int getScrollWindowShift() {
+    return scrollWindowShift;
+  }
+
+  @Override public void copyChangedLines(ITerminalTextData destination, ITerminalTextData source) {
+    int n = Math.min(lastChangedLine + 1, source.getHeight());
+    for (int i = firstChangedLine; i < n; i++) {
+      if (hasLineChanged(i)) {
+        destination.copyLine(source, i, i);
+      }
+    }
+  }
+
+  @Override public int getInterestWindowSize() {
+    return interestWindowSize;
+  }
+
+  @Override public int getInterestWindowStartLine() {
+    return interestWindowStartLine;
+  }
+
+  @Override public void setInterestWindow(int startLine, int size) {
+    int oldStartLine = interestWindowStartLine;
+    int oldSize = interestWindowSize;
+    interestWindowStartLine = startLine;
+    interestWindowSize = size;
+    if (oldSize > 0) {
+      int shift = oldStartLine - startLine;
+      if (shift == 0) {
+        if (size > oldSize) {
+          // add lines to the end
+          markLinesChanged(oldStartLine + oldSize, size - oldSize);
+        }
+        // else no lines within the window have changed
+
+      } else if (Math.abs(shift) < size) {
+        if (shift < 0) {
+          // we can scroll
+          scroll(startLine, oldSize, shift);
+          // mark the lines at the end as new
+          for (int i = oldStartLine + oldSize; i < startLine + size; i++) {
+            markLineChanged(i);
+          }
+        } else {
+          // we cannot shift positive -- mark all changed
+          markLinesChanged(startLine, size);
+        }
+      } else {
+        // no scrolling possible
+        markLinesChanged(startLine, size);
+      }
+    }
+  }
+
+  @Override public boolean hasLineChanged(int line) {
+    if (line < getChangedLineCount()) {
+      return changedLines[line];
+    }
+    // since the height of the terminal could have changed but we have tracked only changes of the previous terminal
+    // height, any line outside the the range of the previous height has changed
+    return isInInterestWindow(line);
+  }
+
+  private int getChangedLineCount() {
+    return changedLines.length;
+  }
+
+  private void setChangedLine(int line, boolean changed) {
+    changedLines[line] = changed;
+  }
+
+  void setChangedLineCount(int length) {
+    changedLines = new boolean[length];
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SynchronizedTerminalTextData.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SynchronizedTerminalTextData.java
new file mode 100644
index 0000000..74f7fc6
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SynchronizedTerminalTextData.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse public synchronized License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+/**
+ * This is a decorator to make all access to an {@link ITerminalTextData} synchronized.
+ */
+public class SynchronizedTerminalTextData implements ITerminalTextData {
+  private final ITerminalTextData data;
+
+  public SynchronizedTerminalTextData(ITerminalTextData data) {
+    this.data = data;
+  }
+
+  @Override public synchronized void addLine() {
+    data.addLine();
+  }
+
+  @Override public synchronized void cleanLine(int line) {
+    data.cleanLine(line);
+  }
+
+  @Override public synchronized void copy(ITerminalTextData source) {
+    data.copy(source);
+  }
+
+  @Override public synchronized void copyLine(ITerminalTextData source, int sourceLine, int destinationLine) {
+    data.copyLine(source, sourceLine, destinationLine);
+  }
+
+  @Override public synchronized void copyRange(
+      ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length) {
+    data.copyRange(source, sourceStartLine, destinationStartLine, length);
+  }
+
+  @Override public synchronized char getChar(int line, int column) {
+    return data.getChar(line, column);
+  }
+
+  @Override public synchronized char[] getChars(int line) {
+    return data.getChars(line);
+  }
+
+  @Override public synchronized int getCursorColumn() {
+    return data.getCursorColumn();
+  }
+
+  @Override public synchronized int getCursorLine() {
+    return data.getCursorLine();
+  }
+
+  @Override public synchronized int getHeight() {
+    return data.getHeight();
+  }
+
+  @Override public synchronized LineSegment[] getLineSegments(int line, int startColumn, int columnCount) {
+    return data.getLineSegments(line, startColumn, columnCount);
+  }
+
+  @Override public synchronized int getMaxHeight() {
+    return data.getMaxHeight();
+  }
+
+  @Override public synchronized Style getStyle(int line, int column) {
+    return data.getStyle(line, column);
+  }
+
+  @Override public synchronized Style[] getStyles(int line) {
+    return data.getStyles(line);
+  }
+
+  @Override public synchronized int getWidth() {
+    return data.getWidth();
+  }
+
+  @Override public synchronized ITerminalTextDataSnapshot makeSnapshot() {
+    return data.makeSnapshot();
+  }
+
+  @Override public synchronized void scroll(int startLine, int size, int shift) {
+    data.scroll(startLine, size, shift);
+  }
+
+  @Override public synchronized void setChar(int line, int column, char c, Style style) {
+    data.setChar(line, column, c, style);
+  }
+
+  @Override public synchronized void setChars(int line, int column, char[] chars, int start, int length, Style style) {
+    data.setChars(line, column, chars, start, length, style);
+  }
+
+  @Override public synchronized void setChars(int line, int column, char[] chars, Style style) {
+    data.setChars(line, column, chars, style);
+  }
+
+  @Override public synchronized void setCursorColumn(int column) {
+    data.setCursorColumn(column);
+  }
+
+  @Override public synchronized void setCursorLine(int line) {
+    data.setCursorLine(line);
+  }
+
+  @Override public synchronized void setDimensions(int height, int width) {
+    data.setDimensions(height, width);
+  }
+
+  @Override public synchronized void setMaxHeight(int height) {
+    data.setMaxHeight(height);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextData.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextData.java
new file mode 100644
index 0000000..2805e46
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextData.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import java.util.*;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public class TerminalTextData implements ITerminalTextData {
+  private final ITerminalTextData data;
+  private final List<TerminalTextDataSnapshot> snapshots = new ArrayList<TerminalTextDataSnapshot>();
+  private int cursorLine;
+  private int cursorColumn;
+
+  public TerminalTextData() {
+    this(new TerminalTextDataFastScroll());
+  }
+
+  public TerminalTextData(ITerminalTextData data) {
+    this.data = data;
+  }
+
+  @Override public int getWidth() {
+    return data.getWidth();
+  }
+
+  @Override public int getHeight() {
+    return data.getHeight();
+  }
+
+  @Override public void setDimensions(int height, int width) {
+    int currentHeight = getHeight();
+    int currentWidth = getWidth();
+    if (currentWidth == width && currentHeight == height) {
+      return;
+    }
+    data.setDimensions(height, width);
+    sendDimensionsChanged(currentHeight, currentWidth, height, width);
+  }
+
+  private void sendDimensionsChanged(int oldHeight, int oldWidth, int newHeight, int newWidth) {
+    // determine what has changed
+    if (oldWidth == newWidth) {
+      if (oldHeight < newHeight) {
+        sendLinesChangedToSnapshot(oldHeight, newHeight - oldHeight);
+      } else {
+        sendLinesChangedToSnapshot(newHeight, oldHeight - newHeight);
+      }
+    } else {
+      sendLinesChangedToSnapshot(0, oldHeight);
+    }
+    sendDimensionsChanged();
+  }
+
+  @Override public LineSegment[] getLineSegments(int line, int column, int len) {
+    return data.getLineSegments(line, column, len);
+  }
+
+  @Override public char getChar(int line, int column) {
+    return data.getChar(line, column);
+  }
+
+  @Override public Style getStyle(int line, int column) {
+    return data.getStyle(line, column);
+  }
+
+  @Override public void setChar(int line, int column, char c, Style style) {
+    data.setChar(line, column, c, style);
+    sendLineChangedToSnapshots(line);
+  }
+
+  @Override public void setChars(int line, int column, char[] chars, Style style) {
+    data.setChars(line, column, chars, style);
+    sendLineChangedToSnapshots(line);
+  }
+
+  @Override public void setChars(int line, int column, char[] chars, int start, int length, Style style) {
+    data.setChars(line, column, chars, start, length, style);
+    sendLineChangedToSnapshots(line);
+  }
+
+  @Override public void scroll(int startLine, int size, int shift) {
+    data.scroll(startLine, size, shift);
+    sendScrolledToSnapshots(startLine, size, shift);
+  }
+
+  @Override public String toString() {
+    return data.toString();
+  }
+
+  private void sendDimensionsChanged() {
+    for (TerminalTextDataSnapshot snapshot : snapshots) {
+      snapshot.markDimensionsChanged();
+    }
+  }
+
+  protected void sendLineChangedToSnapshots(int line) {
+    for (TerminalTextDataSnapshot snapshot : snapshots) {
+      snapshot.markLineChanged(line);
+    }
+  }
+
+  protected void sendLinesChangedToSnapshot(int startingLine, int lineCount) {
+    for (TerminalTextDataSnapshot snapshot : snapshots) {
+      snapshot.markLinesChanged(startingLine, lineCount);
+    }
+  }
+
+  /**
+   * Notifies snapshot that a region was scrolled.
+   * @param startingLine first line of scrolled region.
+   * @param lineCount size of scrolled region (number of lines.)
+   * @param shift delta by which the region is scrolled.
+   */
+  protected void sendScrolledToSnapshots(int startingLine, int lineCount, int shift) {
+    for (TerminalTextDataSnapshot snapshot : snapshots) {
+      snapshot.scroll(startingLine, lineCount, shift);
+    }
+  }
+
+  protected void sendCursorChanged() {
+    for (TerminalTextDataSnapshot snapshot : snapshots) {
+      snapshot.markCursorChanged();
+    }
+  }
+
+  /**
+   * Removes the snapshot from the observer list.
+   * @param snapshot a snapshot of a terminal model.
+   */
+  protected void removeSnapshot(TerminalTextDataSnapshot snapshot) {
+    snapshots.remove(snapshot);
+  }
+
+  @Override public ITerminalTextDataSnapshot makeSnapshot() {
+    TerminalTextDataSnapshot snapshot = new TerminalTextDataSnapshot(this);
+    snapshot.markDimensionsChanged();
+    snapshots.add(snapshot);
+    return snapshot;
+  }
+
+  @Override public void addLine() {
+    int oldHeight = getHeight();
+    data.addLine();
+    // was is an append or a scroll?
+    int newHeight = getHeight();
+    if (newHeight > oldHeight) {
+      // the line was appended
+      sendLinesChangedToSnapshot(oldHeight, 1);
+      int width = getWidth();
+      sendDimensionsChanged(oldHeight, width, newHeight, width);
+    } else {
+      // the line was scrolled
+      sendScrolledToSnapshots(0, oldHeight, -1);
+    }
+  }
+
+  @Override public void copy(ITerminalTextData source) {
+    data.copy(source);
+    cursorLine = source.getCursorLine();
+    cursorColumn = source.getCursorColumn();
+  }
+
+  @Override public void copyLine(ITerminalTextData source, int sourceLine, int destinationLine) {
+    data.copyLine(source, sourceLine, destinationLine);
+  }
+
+  @Override public void copyRange(ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length) {
+    data.copyRange(source, sourceStartLine, destinationStartLine, length);
+  }
+
+  @Override public char[] getChars(int line) {
+    return data.getChars(line);
+  }
+
+  @Override public Style[] getStyles(int line) {
+    return data.getStyles(line);
+  }
+
+  @Override public int getMaxHeight() {
+    return data.getMaxHeight();
+  }
+
+  @Override public void setMaxHeight(int height) {
+    data.setMaxHeight(height);
+  }
+
+  @Override public void cleanLine(int line) {
+    data.cleanLine(line);
+    sendLineChangedToSnapshots(line);
+  }
+
+  @Override public int getCursorColumn() {
+    return cursorColumn;
+  }
+
+  @Override public int getCursorLine() {
+    return cursorLine;
+  }
+
+  @Override public void setCursorColumn(int column) {
+    cursorColumn = column;
+    sendCursorChanged();
+  }
+
+  @Override public void setCursorLine(int line) {
+    cursorLine = line;
+    sendCursorChanged();
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataFastScroll.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataFastScroll.java
new file mode 100644
index 0000000..66c4b21
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataFastScroll.java
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+/**
+ * This class is optimized for scrolling the entire {@link #getHeight()}. The scrolling is done by moving an offset into
+ * the data and using the modulo operator.
+ */
+public class TerminalTextDataFastScroll implements ITerminalTextData {
+  private final ITerminalTextData data;
+  private int height;
+  private int maxHeight;
+  private int offset;
+
+  public TerminalTextDataFastScroll(ITerminalTextData data, int maxHeight) {
+    this.maxHeight = maxHeight;
+    this.data = data;
+    this.data.setDimensions(this.maxHeight, this.data.getWidth());
+    if (this.maxHeight > 2) {
+      moveOffset(-2);
+    }
+  }
+
+  public TerminalTextDataFastScroll(int maxHeight) {
+    this(new TerminalTextDataStore(), maxHeight);
+  }
+
+  public TerminalTextDataFastScroll() {
+    this(new TerminalTextDataStore(), 1);
+  }
+
+  private int getPositionOfLine(int line) {
+    return (line + offset) % maxHeight;
+  }
+
+  private void moveOffset(int delta) {
+    Assert.isTrue(Math.abs(delta) < maxHeight);
+    offset = (maxHeight + offset + delta) % maxHeight;
+  }
+
+  @Override public void addLine() {
+    if (getHeight() < maxHeight) {
+      setDimensions(getHeight() + 1, getWidth());
+    } else {
+      scroll(0, getHeight(), -1);
+    }
+  }
+
+  @Override public void cleanLine(int line) {
+    data.cleanLine(getPositionOfLine(line));
+  }
+
+  @Override public void copy(ITerminalTextData source) {
+    int height = source.getHeight();
+    setDimensions(source.getHeight(), source.getWidth());
+    for (int i = 0; i < height; i++) {
+      data.copyLine(source, i, getPositionOfLine(i));
+    }
+  }
+
+  @Override public void copyLine(ITerminalTextData source, int sourceLine, int destinationLine) {
+    data.copyLine(source, sourceLine, getPositionOfLine(destinationLine));
+  }
+
+  @Override public void copyRange(ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length) {
+    Assert.isTrue(destinationStartLine >= 0 && destinationStartLine + length <= height);
+    for (int i = 0; i < length; i++) {
+      data.copyLine(source, i + sourceStartLine, getPositionOfLine(i + destinationStartLine));
+    }
+  }
+
+  @Override public char getChar(int line, int column) {
+    Assert.isTrue(line >= 0 && line < height);
+    return data.getChar(getPositionOfLine(line), column);
+  }
+
+  @Override public char[] getChars(int line) {
+    Assert.isTrue(line >= 0 && line < height);
+    return data.getChars(getPositionOfLine(line));
+  }
+
+  @Override public int getHeight() {
+    return height;
+  }
+
+  @Override public LineSegment[] getLineSegments(int line, int startCol, int numberOfCols) {
+    Assert.isTrue(line >= 0 && line < height);
+    return data.getLineSegments(getPositionOfLine(line), startCol, numberOfCols);
+  }
+
+  @Override public int getMaxHeight() {
+    return maxHeight;
+  }
+
+  @Override public Style getStyle(int line, int column) {
+    Assert.isTrue(line >= 0 && line < height);
+    return data.getStyle(getPositionOfLine(line), column);
+  }
+
+  @Override public Style[] getStyles(int line) {
+    Assert.isTrue(line >= 0 && line < height);
+    return data.getStyles(getPositionOfLine(line));
+  }
+
+  @Override public int getWidth() {
+    return data.getWidth();
+  }
+
+  @Override public ITerminalTextDataSnapshot makeSnapshot() {
+    return data.makeSnapshot();
+  }
+
+  private void cleanLines(int line, int len) {
+    for (int i = line; i < line + len; i++) {
+      data.cleanLine(getPositionOfLine(i));
+    }
+  }
+
+  @Override public void scroll(int startLine, int size, int shift) {
+    Assert.isTrue(startLine >= 0 && startLine + size <= height);
+    if (shift >= maxHeight || -shift >= maxHeight) {
+      cleanLines(startLine, maxHeight - startLine);
+      return;
+    }
+    if (size == height) {
+      // This is the case this class is optimized for!
+      moveOffset(-shift);
+      // We only have to clean the lines that appear by the move.
+      if (shift < 0) {
+        cleanLines(Math.max(startLine, startLine + size + shift), Math.min(-shift, getHeight() - startLine));
+      } else {
+        cleanLines(startLine, Math.min(shift, getHeight() - startLine));
+      }
+    } else {
+      // we have to copy the lines.
+      if (shift < 0) {
+        // move the region up
+        for (int i = startLine; i < startLine + size + shift; i++) {
+          data.copyLine(data, getPositionOfLine(i - shift), getPositionOfLine(i));
+        }
+        // then clean the opened lines
+        cleanLines(Math.max(0, startLine + size + shift), Math.min(-shift, getHeight() - startLine));
+      } else {
+        for (int i = startLine + size - 1; i >= startLine && i - shift >= 0; i--) {
+          data.copyLine(data, getPositionOfLine(i - shift), getPositionOfLine(i));
+        }
+        cleanLines(startLine, Math.min(shift, getHeight() - startLine));
+      }
+    }
+  }
+
+  @Override public void setChar(int line, int column, char c, Style style) {
+    Assert.isTrue(line >= 0 && line < height);
+    data.setChar(getPositionOfLine(line), column, c, style);
+  }
+
+  @Override public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
+    Assert.isTrue(line >= 0 && line < height);
+    data.setChars(getPositionOfLine(line), column, chars, start, len, style);
+  }
+
+  @Override public void setChars(int line, int column, char[] chars, Style style) {
+    Assert.isTrue(line >= 0 && line < height);
+    data.setChars(getPositionOfLine(line), column, chars, style);
+  }
+
+  @Override public void setDimensions(int height, int width) {
+    Assert.isTrue(height >= 0 && width >= 0);
+    if (height > maxHeight) {
+      setMaxHeight(height);
+    }
+    this.height = height;
+    if (width != data.getWidth()) {
+      data.setDimensions(maxHeight, width);
+    }
+  }
+
+  @Override public void setMaxHeight(int maxHeight) {
+    Assert.isTrue(maxHeight >= height);
+    // move everything to offset0
+    int start = getPositionOfLine(0);
+    if (start != 0) {
+      // invent a more efficient algorithm....
+      ITerminalTextData buffer = new TerminalTextDataStore();
+      // create a buffer with the expected height
+      buffer.setDimensions(maxHeight, getWidth());
+      int n = Math.min(maxHeight - start, maxHeight);
+      // copy the first part
+      buffer.copyRange(data, start, 0, n);
+      // copy the second part
+      if (n < maxHeight) {
+        buffer.copyRange(data, 0, n, Math.min(maxHeight - n, maxHeight - n));
+      }
+      // copy the buffer back to our data
+      data.copy(buffer);
+      moveOffset(-start);
+    } else {
+      data.setDimensions(maxHeight, data.getWidth());
+    }
+    this.maxHeight = maxHeight;
+  }
+
+  @Override public int getCursorColumn() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override public int getCursorLine() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override public void setCursorColumn(int column) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override public void setCursorLine(int line) {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataSnapshot.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataSnapshot.java
new file mode 100644
index 0000000..f4ae9ba
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataSnapshot.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+class TerminalTextDataSnapshot implements ITerminalTextDataSnapshot {
+  // The changes of the current snapshot relative to the previous snapshot.
+  private volatile ISnapshotChanges currentChanges;
+
+  // Keeps track of changes that happened since the current snapshot has been made.
+  private ISnapshotChanges futureChanges;
+
+  // Is used as lock and is the reference to the terminal we take snapshots from.
+  private final TerminalTextData terminal;
+
+  // A snapshot copy of terminal. It does not need internal synchronization.
+  private final TerminalTextDataWindow snapshot;
+
+  private final List<SnapshotOutOfDateListener> listeners = new ArrayList<SnapshotOutOfDateListener>();
+
+  private boolean listenersNeedNotify;
+  private int interestWindowSize;
+  private int interestWindowStartLine;
+
+  TerminalTextDataSnapshot(TerminalTextData terminal) {
+    snapshot = new TerminalTextDataWindow();
+    this.terminal = terminal;
+    currentChanges = new SnapshotChanges(this.terminal.getHeight());
+    currentChanges.setTerminalChanged();
+    futureChanges = new SnapshotChanges(this.terminal.getHeight());
+    futureChanges.markLinesChanged(0, this.terminal.getHeight());
+    listenersNeedNotify = true;
+    interestWindowSize = -1;
+  }
+
+  @Override public void detach() {
+    terminal.removeSnapshot(this);
+  }
+
+  @Override public boolean isOutOfDate() {
+    // This is called from terminal, therefore we lock on terminal
+    synchronized (terminal) {
+      return futureChanges.hasChanged();
+    }
+  }
+
+  @Override public void updateSnapshot(boolean detectScrolling) {
+    // Make sure terminal does not change while we make the snapshot.
+    synchronized (terminal) {
+      // let's make the future changes current
+      currentChanges = futureChanges;
+      futureChanges = new SnapshotChanges(terminal.getHeight());
+      futureChanges.setInterestWindow(interestWindowStartLine, interestWindowSize);
+      // and update the snapshot
+      if (snapshot.getHeight() != terminal.getHeight() || snapshot.getWidth() != terminal.getWidth()) {
+        if (interestWindowSize == -1) {
+          snapshot.setWindow(0, terminal.getHeight());
+        }
+        // if the dimensions have changed, we need a full copy.
+        snapshot.copy(terminal);
+        // and we mark all lines as changed.
+        currentChanges.setAllChanged(terminal.getHeight());
+      } else {
+        // first we do the scroll on the copy
+        int start = currentChanges.getScrollWindowStartLine();
+        int lines = Math.min(currentChanges.getScrollWindowSize(), snapshot.getHeight() - start);
+        snapshot.scroll(start, lines, currentChanges.getScrollWindowShift());
+        // and then create the snapshot of the changed lines.
+        currentChanges.copyChangedLines(snapshot, terminal);
+      }
+      listenersNeedNotify = true;
+      snapshot.setCursorLine(terminal.getCursorLine());
+      snapshot.setCursorColumn(terminal.getCursorColumn());
+    }
+    if (!detectScrolling) {
+      // let's pretend there was no scrolling and convert the scrolling into line changes
+      currentChanges.convertScrollingIntoChanges();
+    }
+  }
+
+  @Override public char getChar(int line, int column) {
+    return snapshot.getChar(line, column);
+  }
+
+  @Override public int getHeight() {
+    return snapshot.getHeight();
+  }
+
+  @Override public LineSegment[] getLineSegments(int line, int column, int len) {
+    return snapshot.getLineSegments(line, column, len);
+  }
+
+  @Override public Style getStyle(int line, int column) {
+    return snapshot.getStyle(line, column);
+  }
+
+  @Override public int getWidth() {
+    return snapshot.getWidth();
+  }
+
+  @Override public int getFirstChangedLine() {
+    return currentChanges.getFirstChangedLine();
+  }
+
+  @Override public int getLastChangedLine() {
+    return currentChanges.getLastChangedLine();
+  }
+
+  @Override public boolean hasLineChanged(int line) {
+    return currentChanges.hasLineChanged(line);
+  }
+
+  @Override public boolean hasDimensionsChanged() {
+    return currentChanges.hasDimensionsChanged();
+  }
+
+  @Override public boolean hasTerminalChanged() {
+    return currentChanges.hasTerminalChanged();
+  }
+
+  @Override public int getScrollWindowStartLine() {
+    return currentChanges.getScrollWindowStartLine();
+  }
+
+  @Override public int getScrollWindowSize() {
+    return currentChanges.getScrollWindowSize();
+  }
+
+  @Override public int getScrollWindowShift() {
+    return currentChanges.getScrollWindowShift();
+  }
+
+  void markLineChanged(int line) {
+    futureChanges.markLineChanged(line);
+    futureChanges.setTerminalChanged();
+    notifyListers();
+  }
+
+  void markLinesChanged(int line, int size) {
+    futureChanges.markLinesChanged(line, size);
+    futureChanges.setTerminalChanged();
+    notifyListers();
+  }
+
+  void markDimensionsChanged() {
+    futureChanges.markDimensionsChanged();
+    futureChanges.setTerminalChanged();
+    notifyListers();
+  }
+
+  void markCursorChanged() {
+    futureChanges.markCursorChanged();
+    futureChanges.setTerminalChanged();
+    notifyListers();
+  }
+
+  void scroll(int startLine, int size, int shift) {
+    futureChanges.scroll(startLine, size, shift);
+    futureChanges.setTerminalChanged();
+    notifyListers();
+  }
+
+  private void notifyListers() {
+    synchronized (terminal) {
+      if (listenersNeedNotify) {
+        for (SnapshotOutOfDateListener listener : listeners) {
+          listener.snapshotOutOfDate(this);
+        }
+        listenersNeedNotify = false;
+      }
+    }
+  }
+
+  @Override public ITerminalTextDataSnapshot makeSnapshot() {
+    return snapshot.makeSnapshot();
+  }
+
+  @Override synchronized public void addListener(SnapshotOutOfDateListener listener) {
+    listeners.add(listener);
+  }
+
+  @Override synchronized public void removeListener(SnapshotOutOfDateListener listener) {
+    listeners.remove(listener);
+  }
+
+  @Override public String toString() {
+    return snapshot.toString();
+  }
+
+  @Override public int getInterestWindowSize() {
+    return interestWindowSize;
+  }
+
+  @Override public int getInterestWindowStartLine() {
+    return interestWindowStartLine;
+  }
+
+  @Override public void setInterestWindow(int startLine, int size) {
+    Assert.isTrue(startLine >= 0 && size >= 0);
+    interestWindowStartLine = startLine;
+    interestWindowSize = size;
+    snapshot.setWindow(startLine, size);
+    futureChanges.setInterestWindow(startLine, size);
+    notifyListers();
+  }
+
+  @Override public char[] getChars(int line) {
+    return snapshot.getChars(line);
+  }
+
+  @Override public Style[] getStyles(int line) {
+    return snapshot.getStyles(line);
+  }
+
+  @Override public int getCursorColumn() {
+    return snapshot.getCursorColumn();
+  }
+
+  @Override public int getCursorLine() {
+    return snapshot.getCursorLine();
+  }
+
+  @Override public ITerminalTextData getTerminalTextData() {
+    return terminal;
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataStore.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataStore.java
new file mode 100644
index 0000000..dc7855f
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataStore.java
@@ -0,0 +1,295 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import static java.util.Arrays.copyOf;
+
+import java.lang.reflect.Array;
+import java.util.*;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public class TerminalTextDataStore implements ITerminalTextData {
+  private char[][] chars;
+  private Style[][] styles;
+  private int width;
+  private int height;
+  private int maxHeight;
+  private int cursorColumn;
+  private int cursorLine;
+
+  public TerminalTextDataStore() {
+    chars = new char[0][];
+    styles = new Style[0][];
+    width = 0;
+  }
+
+  @Override public int getWidth() {
+    return width;
+  }
+
+  @Override public int getHeight() {
+    return height;
+  }
+
+  @Override public void setDimensions(int height, int width) {
+    Assert.isTrue(height >= 0 && width >= 0);
+    // Just extend the region.
+    if (height > chars.length) {
+      int h = 4 * height / 3;
+      if (maxHeight > 0 && h > maxHeight) {
+        h = maxHeight;
+      }
+      styles = (Style[][]) resizeArray(styles, height);
+      chars = (char[][]) resizeArray(chars, height);
+    }
+    // Clean the new lines
+    if (height > this.height) {
+      for (int i = this.height; i < height; i++) {
+        styles[i] = null;
+        chars[i] = null;
+      }
+    }
+    // Set dimensions after successful resize.
+    this.width = width;
+    this.height = height;
+  }
+
+  /**
+   * Reallocates an array with a new size, and copies the contents of the old array to the new array.
+   *
+   * @param originalArray the old array, to be reallocated.
+   * @param newSize the new array size.
+   * @return A new array with the same contents (chopped off if needed or filled with 0 or null).
+   */
+  private Object resizeArray(Object originalArray, int newSize) {
+    int oldSize = Array.getLength(originalArray);
+    if (oldSize == newSize) {
+      return originalArray;
+    }
+    Class<?> elementType = originalArray.getClass().getComponentType();
+    Object newArray = Array.newInstance(elementType, newSize);
+    int preserveLength = Math.min(oldSize, newSize);
+    if (preserveLength > 0) {
+      System.arraycopy(originalArray, 0, newArray, 0, preserveLength);
+    }
+    return newArray;
+  }
+
+  @Override public LineSegment[] getLineSegments(int line, int startingColumn, int columnCount) {
+    // get the styles and chars for this line
+    Style[] styles = this.styles[line];
+    char[] chars = this.chars[line];
+    int column = startingColumn;
+    int size = startingColumn + columnCount;
+    // Expand the line if needed
+    if (styles == null) {
+      styles = new Style[size];
+    } else if (styles.length < size) {
+      styles = (Style[]) resizeArray(styles, size);
+    }
+    if (chars == null) {
+      chars = new char[size];
+    } else if (chars.length < size) {
+      chars = (char[]) resizeArray(chars, size);
+    }
+    // Create the line segments
+    Style style = styles[startingColumn];
+    List<LineSegment> segments = new ArrayList<LineSegment>();
+    for (int i = startingColumn; i < size; i++) {
+      if (styles[i] != style) {
+        segments.add(new LineSegment(column, new String(chars, column, i - column), style));
+        style = styles[i];
+        column = i;
+      }
+    }
+    if (column < size) {
+      segments.add(new LineSegment(column, new String(chars, column, size - column), style));
+    }
+    return segments.toArray(new LineSegment[segments.size()]);
+  }
+
+  @Override public char getChar(int line, int column) {
+    Assert.isTrue(column < width);
+    if (chars[line] == null || column >= chars[line].length) {
+      return 0;
+    }
+    return chars[line][column];
+  }
+
+  @Override public Style getStyle(int line, int column) {
+    Assert.isTrue(column < width);
+    if (styles[line] == null || column >= styles[line].length) {
+      return null;
+    }
+    return styles[line][column];
+  }
+
+  void ensureLineLength(int iLine, int length) {
+    if (length > width) {
+      throw new RuntimeException();
+    }
+    if (chars[iLine] == null) {
+      chars[iLine] = new char[length];
+    } else if (chars[iLine].length < length) {
+      chars[iLine] = (char[]) resizeArray(chars[iLine], length);
+    }
+    if (styles[iLine] == null) {
+      styles[iLine] = new Style[length];
+    } else if (styles[iLine].length < length) {
+      styles[iLine] = (Style[]) resizeArray(styles[iLine], length);
+    }
+  }
+
+  @Override public void setChar(int line, int column, char c, Style style) {
+    ensureLineLength(line, column + 1);
+    chars[line][column] = c;
+    styles[line][column] = style;
+  }
+
+  @Override public void setChars(int line, int column, char[] chars, Style style) {
+    setChars(line, column, chars, 0, chars.length, style);
+  }
+
+  @Override public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
+    ensureLineLength(line, column + len);
+    for (int i = 0; i < len; i++) {
+      this.chars[line][column + i] = chars[i + start];
+      this.styles[line][column + i] = style;
+    }
+  }
+
+  @Override public void scroll(int startLine, int size, int shift) {
+    Assert.isTrue(startLine + size <= getHeight());
+    if (shift < 0) {
+      // move the region up
+      for (int i = startLine; i < startLine + size + shift; i++) {
+        chars[i] = chars[i - shift];
+        styles[i] = styles[i - shift];
+      }
+      // then clean the opened lines
+      cleanLines(Math.max(startLine, startLine + size + shift), Math.min(-shift, getHeight() - startLine));
+    } else {
+      for (int i = startLine + size - 1; i >= startLine && i - shift >= 0; i--) {
+        chars[i] = chars[i - shift];
+        styles[i] = styles[i - shift];
+      }
+      cleanLines(startLine, Math.min(shift, getHeight() - startLine));
+    }
+  }
+
+  private void cleanLines(int line, int len) {
+    for (int i = line; i < line + len; i++) {
+      chars[i] = null;
+      styles[i] = null;
+    }
+  }
+
+  @Override public String toString() {
+    StringBuffer buff = new StringBuffer();
+    for (int line = 0; line < getHeight(); line++) {
+      if (line > 0) {
+        buff.append("\n");
+      }
+      for (int column = 0; column < width; column++) {
+        buff.append(getChar(line, column));
+      }
+    }
+    return buff.toString();
+  }
+
+  @Override public ITerminalTextDataSnapshot makeSnapshot() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override public void addLine() {
+    if (maxHeight > 0 && getHeight() < maxHeight) {
+      setDimensions(getHeight() + 1, getWidth());
+    } else {
+      scroll(0, getHeight(), -1);
+    }
+  }
+
+  @Override public void copy(ITerminalTextData source) {
+    width = source.getWidth();
+    int newHeight = source.getHeight();
+    if (getHeight() != newHeight) {
+      chars = new char[newHeight][];
+      styles = new Style[newHeight][];
+    }
+    for (int i = 0; i < newHeight; i++) {
+      chars[i] = source.getChars(i);
+      styles[i] = source.getStyles(i);
+    }
+    height = newHeight;
+    cursorLine = source.getCursorLine();
+    cursorColumn = source.getCursorColumn();
+  }
+
+  @Override public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) {
+    for (int i = 0; i < length; i++) {
+      chars[i + destStartLine] = source.getChars(i + sourceStartLine);
+      styles[i + destStartLine] = source.getStyles(i + sourceStartLine);
+    }
+  }
+
+  @Override public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
+    chars[destLine] = source.getChars(sourceLine);
+    styles[destLine] = source.getStyles(sourceLine);
+  }
+
+  @Override public char[] getChars(int line) {
+    if (chars[line] == null) {
+      return null;
+    }
+    return chars[line].clone();
+  }
+
+  @Override public Style[] getStyles(int line) {
+    if (styles[line] == null) {
+      return null;
+    }
+    return styles[line].clone();
+  }
+
+  public void setLine(int line, char[] chars, Style[] styles) {
+    this.chars[line] = copyOf(chars, chars.length);
+    this.styles[line] = copyOf(styles, styles.length);
+  }
+
+  @Override public void setMaxHeight(int height) {
+    maxHeight = height;
+  }
+
+  @Override public int getMaxHeight() {
+    return maxHeight;
+  }
+
+  @Override public void cleanLine(int line) {
+    chars[line] = null;
+    styles[line] = null;
+  }
+
+  @Override public int getCursorColumn() {
+    return cursorColumn;
+  }
+
+  @Override public int getCursorLine() {
+    return cursorLine;
+  }
+
+  @Override public void setCursorColumn(int column) {
+    cursorColumn = column;
+  }
+
+  @Override public void setCursorLine(int line) {
+    cursorLine = line;
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataWindow.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataWindow.java
new file mode 100644
index 0000000..54c6285
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataWindow.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+/**
+ * This class stores the data only within a window {@link #setWindow(int, int)} and {@link #getWindowStartLine()} and
+ * {@link #getWindowSize()}. Everything outside the is {@code char=='\000'} and {@code style=null}.
+ */
+public class TerminalTextDataWindow implements ITerminalTextData {
+  private final ITerminalTextData data;
+
+  private int windowStartLine;
+  private int windowSize;
+  private int height;
+  private int maxHeight;
+
+  public TerminalTextDataWindow(ITerminalTextData data) {
+    this.data = data;
+  }
+
+  public TerminalTextDataWindow() {
+    this(new TerminalTextDataStore());
+  }
+
+  private boolean isInWindow(int line) {
+    return line >= windowStartLine && line < windowStartLine + windowSize;
+  }
+
+  @Override public char getChar(int line, int column) {
+    if (!isInWindow(line)) {
+      return 0;
+    }
+    return data.getChar(line - windowStartLine, column);
+  }
+
+  @Override public char[] getChars(int line) {
+    if (!isInWindow(line)) {
+      return null;
+    }
+    return data.getChars(line - windowStartLine);
+  }
+
+  @Override public int getHeight() {
+    return height;
+  }
+
+  @Override public LineSegment[] getLineSegments(int line, int startingColumn, int columnCount) {
+    if (!isInWindow(line)) {
+      return new LineSegment[] { new LineSegment(startingColumn, new String(new char[columnCount]), null) };
+    }
+    return data.getLineSegments(line - windowStartLine, startingColumn, columnCount);
+  }
+
+  @Override public int getMaxHeight() {
+    return maxHeight;
+  }
+
+  @Override public Style getStyle(int line, int column) {
+    if (!isInWindow(line)) {
+      return null;
+    }
+    return data.getStyle(line - windowStartLine, column);
+  }
+
+  @Override public Style[] getStyles(int line) {
+    if (!isInWindow(line)) {
+      return null;
+    }
+    return data.getStyles(line - windowStartLine);
+  }
+
+  @Override public int getWidth() {
+    return data.getWidth();
+  }
+
+  @Override public ITerminalTextDataSnapshot makeSnapshot() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override public void addLine() {
+    if (maxHeight > 0 && getHeight() < maxHeight) {
+      setDimensions(getHeight() + 1, getWidth());
+    } else {
+      scroll(0, getHeight(), -1);
+    }
+  }
+
+  @Override public void copy(ITerminalTextData source) {
+    // We inherit the dimensions of the source.
+    setDimensions(source.getHeight(), source.getWidth());
+    int n = Math.min(windowSize, source.getHeight() - windowStartLine);
+    if (n > 0) {
+      data.copyRange(source, windowStartLine, 0, n);
+    }
+  }
+
+  @Override public void copyRange(ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length) {
+    int newLength = length;
+    int destinationStart = destinationStartLine - windowStartLine;
+    int sourceStart = sourceStartLine;
+    // If start outside our range, cut the length to copy.
+    if (destinationStart < 0) {
+      newLength += destinationStart;
+      sourceStart -= destinationStart;
+      destinationStart = 0;
+    }
+    // Do not exceed the window size.
+    newLength = Math.min(newLength, windowSize);
+    if (newLength > 0) {
+      data.copyRange(source, sourceStart, destinationStart, newLength);
+    }
+  }
+
+  @Override public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
+    if (isInWindow(destLine)) {
+      data.copyLine(source, sourceLine, destLine - windowStartLine);
+    }
+  }
+
+  @Override public void scroll(int startLine, int lineCount, int shift) {
+    Assert.isTrue(startLine >= 0 && startLine + lineCount <= height);
+    int length = lineCount;
+    int start = startLine - windowStartLine;
+    // If start outside our range, cut the length to copy.
+    if (start < 0) {
+      length += start;
+      start = 0;
+    }
+    length = Math.min(length, windowSize - start);
+    // do not exceed the window size
+    if (length > 0) {
+      data.scroll(start, length, shift);
+    }
+  }
+
+  @Override public void setChar(int line, int column, char c, Style style) {
+    if (!isInWindow(line)) {
+      return;
+    }
+    data.setChar(line - windowStartLine, column, c, style);
+  }
+
+  @Override public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
+    if (!isInWindow(line)) {
+      return;
+    }
+    data.setChars(line - windowStartLine, column, chars, start, len, style);
+  }
+
+  @Override public void setChars(int line, int column, char[] chars, Style style) {
+    if (!isInWindow(line)) {
+      return;
+    }
+    data.setChars(line - windowStartLine, column, chars, style);
+  }
+
+  @Override public void setDimensions(int height, int width) {
+    Assert.isTrue(height >= 0);
+    data.setDimensions(windowSize, width);
+    setHeight(height);
+  }
+
+  @Override public void setMaxHeight(int maxHeight) {
+    this.maxHeight = maxHeight;
+  }
+
+  public void setWindow(int startLine, int size) {
+    windowStartLine = startLine;
+    windowSize = size;
+    data.setDimensions(windowSize, getWidth());
+  }
+
+  public int getWindowStartLine() {
+    return windowStartLine;
+  }
+
+  public int getWindowSize() {
+    return windowSize;
+  }
+
+  public void setHeight(int height) {
+    this.height = height;
+  }
+
+  @Override public void cleanLine(int line) {
+    if (isInWindow(line)) {
+      data.cleanLine(line - windowStartLine);
+    }
+  }
+
+  @Override public int getCursorColumn() {
+    return data.getCursorColumn();
+  }
+
+  @Override public int getCursorLine() {
+    return data.getCursorLine();
+  }
+
+  @Override public void setCursorColumn(int column) {
+    data.setCursorColumn(column);
+  }
+
+  @Override public void setCursorLine(int line) {
+    data.setCursorLine(line);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextData.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextData.java
new file mode 100644
index 0000000..4c3b351
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextData.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+/**
+ * A writable matrix of characters and {@link Style}. This is intended to be the low level representation of the text of
+ * a Terminal. Higher layers are responsible to fill the text and styles into this representation.
+ */
+public interface ITerminalTextData extends ITerminalTextDataReadOnly {
+
+  void setDimensions(int height, int width);
+
+  void setMaxHeight(int height);
+
+  int getMaxHeight();
+
+  void setChar(int line, int column, char c, Style style);
+
+  void setChars(int line, int column, char[] chars, Style style);
+
+  void setChars(int line, int column, char[] chars, int start, int len, Style style);
+
+  void cleanLine(int line);
+
+  /**
+   * Shifts some lines up or down. The "empty" space is filled with {@code '\000'} chars and {@code null} {@link Style}.
+   * <p>
+   * To illustrate shift, here is some sample data:
+   * <pre>
+   * 0 aaaa
+   * 1 bbbb
+   * 2 cccc
+   * 3 dddd
+   * 4 eeee
+   * </pre>
+   *
+   * Shift a region of 3 lines <b>up</b> by one line {@code shift(1,3,-1)}
+   * <pre>
+   * 0 aaaa
+   * 1 cccc
+   * 2 dddd
+   * 3
+   * 4 eeee
+   * </pre>
+   *
+   * Shift a region of 3 lines <b>down</b> by one line {@code shift(1,3,1)}
+   * <pre>
+   * 0 aaaa
+   * 1
+   * 2 bbbb
+   * 3 cccc
+   * 4 eeee
+   * </pre>
+   *
+   * @param startLine the start line of the shift.
+   * @param size the number of lines to shift.
+   * @param shift how much scrolling is done. New scrolled area is filled with {@code '\000'}. A negative number means
+   *        scroll down, positive scroll up (see example above).
+   */
+  void scroll(int startLine, int size, int shift);
+
+  /**
+   * Adds a new line to the terminal. If maxHeigth is reached, the entire terminal will be scrolled. Else a line will be
+   * added.
+   */
+  void addLine();
+
+  void copy(ITerminalTextData source);
+
+  void copyLine(ITerminalTextData source, int sourceLine, int destinationLine);
+
+  void copyRange(ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length);
+
+  void setCursorLine(int line);
+
+  void setCursorColumn(int column);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataReadOnly.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataReadOnly.java
new file mode 100644
index 0000000..aa98f42
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataReadOnly.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+public interface ITerminalTextDataReadOnly {
+  int getWidth();
+
+  int getHeight();
+
+  LineSegment[] getLineSegments(int line, int startCol, int numberOfCols);
+
+  char getChar(int line, int column);
+
+  Style getStyle(int line, int column);
+
+  /**
+   * Creates a new instance of {@link ITerminalTextDataSnapshot} that can be used to track changes. Make sure to call
+   * {@link ITerminalTextDataSnapshot#detach()} if you don't need the snapshots anymore.
+   * <p>
+   * <b>Note: </b>A new snapshot is empty and needs a call to {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}
+   * to get its initial values. You might want to setup the snapshot to your needs by calling
+   * {@link ITerminalTextDataSnapshot#setInterestWindow(int, int)}.
+   * </p>
+   *
+   * @return a new instance of {@link ITerminalTextDataSnapshot} that "listens" to changes of this one.
+   */
+  public ITerminalTextDataSnapshot makeSnapshot();
+
+  char[] getChars(int line);
+
+  Style[] getStyles(int line);
+
+  int getCursorLine();
+
+  int getCursorColumn();
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataSnapshot.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataSnapshot.java
new file mode 100644
index 0000000..746e638
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataSnapshot.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+/**
+ * Maintains a snapshot of an instance of {@link ITerminalTextData}. While the {@link ITerminalTextData} continues
+ * changing, the snapshot remains unchanged until the next snapshot is taken by calling
+ * {@link #updateSnapshot(boolean)}. This is important, because the {@link ITerminalTextData} might get modified by
+ * another thread.
+ */
+public interface ITerminalTextDataSnapshot extends ITerminalTextDataReadOnly {
+  /**
+   * This listener gets called when the current snapshot is out of date. Calling
+   * {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} will have an effect. Once the
+   * {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} method is called, it will not be called until
+   * {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} is called and a new snapshot needs to be updated again.
+   */
+  interface SnapshotOutOfDateListener {
+    /**
+     * Gets called when the snapshot is out of date. To get the snapshot up to date, call
+     * {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}.
+     *
+     * @param snapshot The snapshot that is out of date.
+     */
+    void snapshotOutOfDate(ITerminalTextDataSnapshot snapshot);
+  }
+
+  void addListener(SnapshotOutOfDateListener listener);
+
+  void removeListener(SnapshotOutOfDateListener listener);
+
+  /**
+   * Ends the listening to the {@link ITerminalTextData}. After this has been called no new snapshot data is collected.
+   */
+  void detach();
+
+  boolean isOutOfDate();
+
+  /**
+   * The window of interest is the region the snapshot should track. Changes outside this region are ignored. The change
+   * takes effect after an update.
+   * @param startLine -1 means track the end of the data.
+   * @param size number of lines to track. A size of -1 means track all.
+   */
+  void setInterestWindow(int startLine, int size);
+
+  int getInterestWindowStartLine();
+
+  int getInterestWindowSize();
+
+  /**
+   * Create a new snapshot of the {@link ITerminalTextData}. It will efficiently copy the data of the
+   * {@link ITerminalTextData} into an internal representation. The snapshot also keeps track of the changes since the
+   * previous snapshot.
+   * <p>
+   * With the methods {@link #getFirstChangedLine()}, {@link #getLastChangedLine()} and {@link #hasLineChanged(int)} you
+   * can find out what has changed in the current snapshot since the previous snapshot.
+   * </p>
+   * <p>
+   * If {@code detectScrolling} is {@code true} the information about scrolling can be retrieved using the following methods:
+   * {@link #getScrollWindowStartLine()}, {@link #getScrollWindowSize()} and {@link #getScrollWindowShift()}.
+   * </p>
+   * <p>
+   * <b>Note:</b> The method {@link #hasLineChanged(int)} returns changes <b>after</b> the scrolling has been applied.
+   * </p>
+   *
+   * @param detectScrolling indicates whether the snapshot should try to identify scroll changes since the last
+   *        snapshot.
+   */
+  void updateSnapshot(boolean detectScrolling);
+
+  /**
+   * Returns the first line changed in this snapshot compared to the previous snapshot.
+   * <p>
+   * <b>Note:</b> If no line has changed, this returns {@link Integer#MAX_VALUE}.
+   * <p>
+   * <b>Note:</b> if {@link #updateSnapshot(boolean)} has been called with <code>true</code>, then this does not include
+   * lines that only have been scrolled. This is the first line that has changed <b>after</b> the scroll has been
+   * applied.
+   *
+   * @return the first line changed in this snapshot compared to the previous snapshot.
+   */
+  int getFirstChangedLine();
+
+  /**
+   * Returns the last line changed in this snapshot compared to the previous snapshot. If the height has changed since
+   * the last update of the snapshot, then the returned value is within the new dimensions.
+   * <p>
+   * <b>Note:</b> If no line has changed, this returns <code>-1</code>
+   * <p>
+   * <b>Note:</b> if {@link #updateSnapshot(boolean)} has been called with <code>true</code>, then this does not include
+   * lines that only have been scrolled. This is the last line that has changed <b>after</b> the scroll has been
+   * applied.
+   * <p>
+   * A typical for loop using this method would look like this (note the <code>&lt;=</code> in the for loop):
+   * <pre>
+   * for (int line = snap.{@link #getFirstChangedLine()}; line <b>&lt;=</b> snap.getLastChangedLine(); line++)
+   *    if(snap.{@link #hasLineChanged(int) hasLineChanged(line)})
+   *       doSomething(line);
+   * </pre>
+   * @return the last line changed in this snapshot compared to the previous snapshot.
+   */
+  int getLastChangedLine();
+
+  boolean hasLineChanged(int line);
+
+  boolean hasDimensionsChanged();
+
+  boolean hasTerminalChanged();
+
+  /**
+   * If {@link #updateSnapshot(boolean)} was called with {@code true}, then this method returns the top of the scroll
+   * region.
+   *
+   * @return The first line scrolled in this snapshot compared to the previous snapshot.
+   *
+   * @see ITerminalTextData#scroll(int, int, int)
+   */
+  int getScrollWindowStartLine();
+
+  /**
+   * If {@link #updateSnapshot(boolean)} was called with {@code true}, then this method returns the size of the scroll
+   * region. If nothing has changed, 0 is returned.
+   *
+   * @return The number of lines scrolled in this snapshot compared to the previous snapshot.
+   *
+   * @see ITerminalTextData#scroll(int, int, int)
+   */
+  int getScrollWindowSize();
+
+  /**
+   * If {@link #updateSnapshot(boolean)} was called with {@code true}, then this method returns number of lines moved by
+   * the scroll region.
+   *
+   * @return The the scroll shift of this snapshot compared to the previous snapshot.
+   *
+   * @see ITerminalTextData#scroll(int, int, int)
+   */
+  int getScrollWindowShift();
+
+  ITerminalTextData getTerminalTextData();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/LineSegment.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/LineSegment.java
new file mode 100644
index 0000000..4c61f7f
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/LineSegment.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+public class LineSegment {
+  private final int column;
+  private final String text;
+  private final Style style;
+
+  public LineSegment(int column, String text, Style style) {
+    this.column = column;
+    this.text = text;
+    this.style = style;
+  }
+
+  public int getColumn() {
+    return column;
+  }
+
+  public Style getStyle() {
+    return style;
+  }
+
+  public String getText() {
+    return text;
+  }
+
+  @Override public String toString() {
+    return "LineSegment(" + column + ", \"" + text + "\"," + style + ")";
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/Style.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/Style.java
new file mode 100644
index 0000000..3c0bb95
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/Style.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+import java.util.*;
+
+// TODO add an Object for user data, use weak map to keep track of styles with associated user data.
+public class Style {
+  private final StyleColor foreground;
+  private final StyleColor background;
+  private final boolean bold;
+  private final boolean blink;
+  private final boolean underline;
+  private final boolean reverse;
+
+  private static final Map<Style, Style> STYLES = new HashMap<Style, Style>();
+
+  private Style(StyleColor forground, StyleColor background, boolean bold, boolean blink, boolean underline,
+      boolean reverse) {
+    this.foreground = forground;
+    this.background = background;
+    this.bold = bold;
+    this.blink = blink;
+    this.underline = underline;
+    this.reverse = reverse;
+  }
+
+  public static Style getStyle(StyleColor forground, StyleColor background, boolean bold, boolean blink,
+      boolean underline, boolean reverse) {
+    Style style = new Style(forground, background, bold, blink, underline, reverse);
+    Style cached;
+    synchronized (STYLES) {
+      cached = STYLES.get(style);
+      if (cached == null) {
+        cached = style;
+        STYLES.put(cached, cached);
+      }
+    }
+    return cached;
+  }
+
+  public static Style getStyle(String forground, String background) {
+    return getStyle(
+        StyleColor.getStyleColor(forground), StyleColor.getStyleColor(background), false, false, false, false);
+  }
+
+  public static Style getStyle(StyleColor forground, StyleColor background) {
+    return getStyle(forground, background, false, false, false, false);
+  }
+
+  public Style setForground(StyleColor forground) {
+    return getStyle(forground, background, bold, blink, underline, reverse);
+  }
+
+  public Style setBackground(StyleColor background) {
+    return getStyle(foreground, background, bold, blink, underline, reverse);
+  }
+
+  public Style setForground(String colorName) {
+    return getStyle(StyleColor.getStyleColor(colorName), background, bold, blink, underline, reverse);
+  }
+
+  public Style setBackground(String colorName) {
+    return getStyle(foreground, StyleColor.getStyleColor(colorName), bold, blink, underline, reverse);
+  }
+
+  public Style setBold(boolean bold) {
+    return getStyle(foreground, background, bold, blink, underline, reverse);
+  }
+
+  public Style setBlink(boolean blink) {
+    return getStyle(foreground, background, bold, blink, underline, reverse);
+  }
+
+  public Style setUnderline(boolean underline) {
+    return getStyle(foreground, background, bold, blink, underline, reverse);
+  }
+
+  public Style setReverse(boolean reverse) {
+    return getStyle(foreground, background, bold, blink, underline, reverse);
+  }
+
+  public StyleColor getBackground() {
+    return background;
+  }
+
+  public boolean isBlink() {
+    return blink;
+  }
+
+  public boolean isBold() {
+    return bold;
+  }
+
+  public StyleColor getForeground() {
+    return foreground;
+  }
+
+  public boolean isReverse() {
+    return reverse;
+  }
+
+  public boolean isUnderline() {
+    return underline;
+  }
+
+  @Override public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((background == null) ? 0 : background.hashCode());
+    result = prime * result + (blink ? 1231 : 1237);
+    result = prime * result + (bold ? 1231 : 1237);
+    result = prime * result + ((foreground == null) ? 0 : foreground.hashCode());
+    result = prime * result + (reverse ? 1231 : 1237);
+    result = prime * result + (underline ? 1231 : 1237);
+    return result;
+  }
+
+  @Override public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    final Style other = (Style) obj;
+    if (background != other.background) {
+      return false;
+    }
+    if (blink != other.blink) {
+      return false;
+    }
+    if (bold != other.bold) {
+      return false;
+    }
+    if (foreground != other.foreground) {
+      return false;
+    }
+    if (reverse != other.reverse) {
+      return false;
+    }
+    return underline == other.underline;
+  }
+
+  @Override public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("Style [foreground=");
+    builder.append(foreground);
+    builder.append(", background=");
+    builder.append(background);
+    builder.append(", bold=");
+    builder.append(bold);
+    builder.append(", blink=");
+    builder.append(blink);
+    builder.append(", underline=");
+    builder.append(underline);
+    builder.append(", reverse=");
+    builder.append(reverse);
+    builder.append("]");
+    return builder.toString();
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/StyleColor.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/StyleColor.java
new file mode 100644
index 0000000..5748b28
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/StyleColor.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+import java.util.concurrent.*;
+
+public class StyleColor {
+  private static final ConcurrentMap<String, StyleColor> STYLE_COLORS = new ConcurrentHashMap<String, StyleColor>();
+
+  private final String name;
+
+  public static StyleColor getStyleColor(String name) {
+    StyleColor result = new StyleColor(name);
+    return STYLE_COLORS.putIfAbsent(name, result);
+  }
+
+  private StyleColor(String name) {
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  @Override public String toString() {
+    return name;
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/TerminalTextDataFactory.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/TerminalTextDataFactory.java
new file mode 100644
index 0000000..a6f8b2c
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/TerminalTextDataFactory.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+import com.google.eclipse.elt.emulator.internal.model.*;
+
+public class TerminalTextDataFactory {
+  public static ITerminalTextData makeTerminalTextData() {
+    return new SynchronizedTerminalTextData(new TerminalTextData());
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalConnector.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalConnector.java
new file mode 100644
index 0000000..1668177
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalConnector.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api;
+
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * A connection type.
+ *
+ * @author Michael Scharf
+ */
+public interface ITerminalConnector extends IAdaptable {
+  String getId();
+
+  String getName();
+
+  boolean isInitialized();
+
+  String getInitializationErrorMessage();
+
+  void connect(ITerminalControl control);
+
+  void disconnect();
+
+  boolean isLocalEcho();
+
+  void setTerminalSize(int newWidth, int newHeight);
+
+  OutputStream getTerminalToRemoteStream();
+
+  String getSettingsSummary();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalControl.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalControl.java
new file mode 100644
index 0000000..a9b00ce
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalControl.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api;
+
+import java.io.*;
+
+import org.eclipse.swt.widgets.*;
+
+/**
+ * Represents the terminal view as seen by a terminal connection.
+ *
+ * @author Michael Scharf
+ */
+public interface ITerminalControl {
+  TerminalState getState();
+
+  void setState(TerminalState state);
+
+  void setUpTerminal(Composite parent);
+
+  Shell getShell();
+
+  /**
+   * Set the encoding that the terminal uses to decode bytes from the Terminal-to-remote-Stream into Unicode characters
+   * used in Java; or, to encode characters typed by the user into bytes sent over the wire to the remote.
+   * <p>
+   * By default, the local platform default encoding is used. Also note that the encoding must not be applied in case
+   * the terminal stream is processed by some data transfer protocol which requires binary data.
+   * </p>
+   * <p>
+   * Validity of the encoding set here is not checked. Since some encodings do not cover the entire range of Unicode
+   * characters, it can happen that a particular Unicode text typed in by the user can not be encoded into a byte stream
+   * with the encoding specified. and {@link UnsupportedEncodingException} will be thrown in this case at the time the
+   * text is about to be processed.
+   * </p>
+   * <p>
+   * The concrete encoding to use can either be specified manually by a user, by means of a dialog, or a connector can
+   * try to obtain it automatically from the remote side (e.g. by evaluating an environment variable such as LANG on
+   * UNIX systems.)
+   * </p>
+   *
+   * @param encoding the new encoding.
+   * @throws UnsupportedEncodingException if the given encoding is not supported.
+   */
+  void setEncoding(String encoding) throws UnsupportedEncodingException;
+
+  String getEncoding();
+
+  /**
+   * Show a text in the terminal. If puts newlines at the beginning and the end.
+   *
+   * @param text the text to display.
+   */
+  void displayTextInTerminal(String text);
+
+  /**
+   * Returns the stream used to write to the terminal. Any bytes written to this stream appear in the terminal or are
+   * interpreted by the emulator as control sequences. The stream in the opposite direction, terminal to remote is in
+   * {@link ITerminalConnector#getTerminalToRemoteStream()}.
+   *
+   * @return the stream used to write to the terminal.
+   */
+  OutputStream getRemoteToTerminalOutputStream();
+
+  void setTerminalTitle(String title);
+
+  /**
+   * Show an error message during connect.
+   * @param errorMessage the new error message.
+   */
+  // TODO(Michael Scharf): Should be replaced by a better error notification mechanism!
+  void setErrorMessage(String errorMessage);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/Logger.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/Logger.java
new file mode 100644
index 0000000..c4d8976
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/Logger.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api;
+
+import static org.eclipse.core.runtime.IStatus.*;
+
+import java.io.*;
+
+import org.eclipse.core.runtime.Status;
+
+import com.google.eclipse.elt.emulator.impl.TerminalPlugin;
+
+/**
+ * A simple logger class. Every method in this class is static, so they can be called from both class and instance
+ * methods. To use this class, write code like this:
+ * <p>
+ *
+ * <pre>
+ * Logger.log(&quot;something has happened&quot;);
+ * Logger.log(&quot;counter is &quot; + counter);
+ * </pre>
+ *
+ * @author Fran Litterio <francis.litterio@windriver.com>
+ */
+public final class Logger {
+  public static final String TRACE_DEBUG_LOG = "org.eclipse.tm.terminal/debug/log";
+  public static final String TRACE_DEBUG_LOG_ERROR = "org.eclipse.tm.terminal/debug/log/error";
+  public static final String TRACE_DEBUG_LOG_INFO = "org.eclipse.tm.terminal/debug/log/info";
+  public static final String TRACE_DEBUG_LOG_CHAR = "org.eclipse.tm.terminal/debug/log/char";
+  public static final String TRACE_DEBUG_LOG_BUFFER_SIZE = "org.eclipse.tm.terminal/debug/log/buffer/size";
+
+  private static PrintStream logStream;
+
+  static {
+    String logFile = logFile();
+    if (logFile != null) {
+      try {
+        logStream = new PrintStream(new FileOutputStream(logFile, true));
+      } catch (Exception ex) {
+        logStream = System.err;
+        logStream.println("Exception when opening log file -- logging to stderr!");
+        ex.printStackTrace(logStream);
+      }
+    }
+  }
+
+  private static String logFile() {
+    File directory = new File("C:\\eclipselogs");
+    if (directory.isDirectory()) {
+      return directory + "\\tmterminal.log";
+    }
+    directory = new File("/tmp/eclipselogs");
+    if (directory.isDirectory()) {
+      return directory + "/tmterminal.log";
+    }
+    return null;
+  }
+
+  /**
+   * Encodes text such that non-printable control characters are converted into user-readable escape sequences for
+   * logging.
+   * @param message the text to encode
+   * @return encoded the encoded text;
+   */
+  public static final String encode(String message) {
+    boolean encoded = false;
+    StringBuilder buffer = new StringBuilder(message.length() + 32);
+    for (int i = 0; i < message.length(); i++) {
+      char c = message.charAt(i);
+      switch (c) {
+      case '\\':
+      case '\'':
+        buffer.append('\\').append(c);
+        encoded = true;
+        break;
+      case '\r':
+        buffer.append('\\').append('r');
+        encoded = true;
+        break;
+      case '\n':
+        buffer.append('\\').append('n');
+        encoded = true;
+        break;
+      case '\t':
+        buffer.append('\\').append('t');
+        encoded = true;
+        break;
+      case '\f':
+        buffer.append('\\').append('f');
+        encoded = true;
+        break;
+      case '\b':
+        buffer.append('\\').append('b');
+        encoded = true;
+        break;
+      default:
+        if (c <= '\u000f') {
+          buffer.append('\\').append('x').append('0').append(Integer.toHexString(c));
+          encoded = true;
+        } else if (c >= ' ' && c < '\u007f') {
+          buffer.append(c);
+        } else if (c <= '\u00ff') {
+          buffer.append('\\').append('x').append(Integer.toHexString(c));
+          encoded = true;
+        } else {
+          buffer.append('\\').append('u');
+          if (c <= '\u0fff') {
+            buffer.append('0');
+          }
+          buffer.append(Integer.toHexString(c));
+          encoded = true;
+        }
+      }
+    }
+    if (encoded) {
+      return buffer.toString();
+    }
+    return message;
+  }
+
+  public static final boolean isLogEnabled() {
+    return (logStream != null);
+  }
+
+  public static final void log(String message) {
+    if (logStream != null) {
+      StackTraceElement caller = Thread.currentThread().getStackTrace()[1];
+      String className = caller.getClassName();
+      className = className.substring(className.lastIndexOf('.') + 1);
+      logStream.println(className + "." + caller.getMethodName() + ":" + caller.getLineNumber() + ": " + message);
+      logStream.flush();
+    }
+  }
+
+  public static final void logException(Exception e) {
+    if (TerminalPlugin.getDefault() != null) {
+      TerminalPlugin.getDefault().getLog().log(new Status(ERROR, TerminalPlugin.PLUGIN_ID, OK, e.getMessage(), e));
+    } else {
+      e.printStackTrace();
+    }
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/TerminalState.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/TerminalState.java
new file mode 100644
index 0000000..7a347e2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/TerminalState.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api;
+
+/**
+ * Represent the sate of a terminal connection.
+ *
+ * @author Michael Scharf
+ */
+public enum TerminalState {
+  /**
+   * The terminal is not connected.
+   */
+  CLOSED("CLOSED"), CONNECTING("CONNECTING..."), CONNECTED("CONNECTED");
+
+  private final String state;
+
+  private TerminalState(String state) {
+    this.state = state;
+  }
+
+  @Override public String toString() {
+    return state;
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/provider/TerminalConnectorDelegate.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/provider/TerminalConnectorDelegate.java
new file mode 100644
index 0000000..d47b593
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/provider/TerminalConnectorDelegate.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ * Martin Oberhuber (Wind River) - [225853][api] Provide more default functionality in TerminalConnectorImpl
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api.provider;
+
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.CLOSED;
+
+import java.io.OutputStream;
+
+import com.google.eclipse.elt.emulator.provisional.api.ITerminalControl;
+
+/**
+ * Abstract base class for all terminal connector implementations.
+ *
+ * @since org.eclipse.tm.terminal 2.0
+ */
+public abstract class TerminalConnectorDelegate {
+  // The TerminalControl associated with this connector. Required for advertising state changes when needed.
+  protected ITerminalControl terminalControl;
+
+  /**
+   * Initialize this connector. This is called once after the constructor, in order to perform any required
+   * initializations such as loading required native libraries. Any work that may lead to runtime exceptions should be
+   * done in this method rather than in the constructor.
+   *
+   * @throws Exception when the connector fails to initialize (due to missing required libraries, for instance).
+   */
+  public void initialize() throws Exception {}
+
+  public final void connect(ITerminalControl control) {
+    this.terminalControl = control;
+    connect();
+  }
+
+  protected abstract void connect();
+
+  public final void disconnect() {
+    onDisconnect();
+    terminalControl.setState(CLOSED);
+  }
+
+  protected void onDisconnect() {}
+
+  /**
+   * Returns the terminal-to-remote stream (bytes written to this stream will be sent to the remote site). For the
+   * stream in the other direction (remote to terminal see {@link ITerminalControl#getRemoteToTerminalOutputStream()}.
+   *
+   * @return the terminal-to-remote stream.
+   */
+  public abstract OutputStream getTerminalToRemoteStream();
+
+  /**
+   * @return A string that represents the settings of the connection. This representation may be shown in the status
+   * line of the terminal view.
+   */
+  public abstract String getSettingsSummary();
+
+  /**
+   * Tests if local echo is needed. The default implementation returns {@code false}.
+   *
+   * @return {@code false} by default.
+   */
+  public boolean isLocalEcho() {
+    return false;
+  }
+
+  /**
+   * Notifies the remote site that the size of the terminal has changed.
+   *
+   * @param width the new width in characters.
+   * @param height the new height in characters.
+   */
+  public void setTerminalSize(int width, int height) {}
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/AbstractTextCanvasModel.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/AbstractTextCanvasModel.java
new file mode 100644
index 0000000..ad7697c
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/AbstractTextCanvasModel.java
@@ -0,0 +1,353 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.swt.graphics.Point;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public abstract class AbstractTextCanvasModel implements ITextCanvasModel {
+  private final List<ITextCanvasModelListener> listeners = new ArrayList<ITextCanvasModelListener>();
+  private final Point selectionAnchor = new Point(0, 0);
+
+  private final ITerminalTextDataSnapshot snapshot;
+
+  private int cursorLine;
+  private int cursorColumn;
+  private boolean showCursor;
+  private long cursorTime;
+  private boolean cursorIsEnabled;
+  private int lines;
+
+  private int selectionStartLine = -1;
+  private int seletionEndLine;
+  private int selectionStartCoumn;
+  private int selectionEndColumn;
+  private ITerminalTextDataSnapshot selectionSnapshot;
+  private String currentSelection = "";
+
+  // do not update while update is running
+  boolean inUpdate;
+
+  private int columns;
+  private boolean useBlinkingCursor;
+
+  public AbstractTextCanvasModel(ITerminalTextDataSnapshot snapshot) {
+    this.snapshot = snapshot;
+    lines = this.snapshot.getHeight();
+  }
+
+  @Override public void addCellCanvasModelListener(ITextCanvasModelListener listener) {
+    listeners.add(listener);
+  }
+
+  @Override public void removeCellCanvasModelListener(ITextCanvasModelListener listener) {
+    listeners.remove(listener);
+  }
+
+  private void fireCellRangeChanged(int x, int y, int width, int height) {
+    for (ITextCanvasModelListener listener : listeners) {
+      listener.rangeChanged(x, y, width, height);
+    }
+  }
+
+  private void fireDimensionsChanged(int width, int height) {
+    for (ITextCanvasModelListener listener : listeners) {
+      listener.dimensionsChanged(width, height);
+    }
+  }
+
+  private void fireTerminalDataChanged() {
+    for (ITextCanvasModelListener listener : listeners) {
+      listener.terminalDataChanged();
+    }
+  }
+
+  @Override public ITerminalTextDataReadOnly getTerminalText() {
+    return snapshot;
+  }
+
+  protected ITerminalTextDataSnapshot getSnapshot() {
+    return snapshot;
+  }
+
+  private void updateSnapshot() {
+    if (!inUpdate && snapshot.isOutOfDate()) {
+      inUpdate = true;
+      try {
+        snapshot.updateSnapshot(false);
+        if (snapshot.hasTerminalChanged()) {
+          fireTerminalDataChanged();
+        }
+        // TODO why does hasDimensionsChanged not work?
+        // if (snapshot.hasDimensionsChanged()) fireDimensionsChanged();
+        if (lines != snapshot.getHeight() || columns != snapshot.getWidth()) {
+          fireDimensionsChanged(snapshot.getWidth(), snapshot.getHeight());
+          lines = snapshot.getHeight();
+          columns = snapshot.getWidth();
+        }
+        int y = snapshot.getFirstChangedLine();
+        // has any line changed?
+        if (y < Integer.MAX_VALUE) {
+          int height = snapshot.getLastChangedLine() - y + 1;
+          fireCellRangeChanged(0, y, snapshot.getWidth(), height);
+        }
+      } finally {
+        inUpdate = false;
+      }
+    }
+  }
+
+  /**
+   * This method must be called from the UI thread.
+   */
+  public void update() {
+    updateSnapshot();
+    updateSelection();
+    updateCursor();
+  }
+
+  @Override public int getCursorColumn() {
+    return cursorColumn;
+  }
+
+  @Override public int getCursorLine() {
+    return cursorLine;
+  }
+
+  @Override public boolean isCursorOn() {
+    return showCursor && cursorIsEnabled;
+  }
+
+  @Override public void setBlinkingCursor(boolean useBlinkingCursor) {
+    this.useBlinkingCursor = useBlinkingCursor;
+    updateCursor();
+  }
+
+  // TODO: should be called regularly to draw an update of the blinking cursor?
+  private void updateCursor() {
+    if (!cursorIsEnabled) {
+      return;
+    }
+    int cursorLine = getSnapshot().getCursorLine();
+    int cursorColumn = getSnapshot().getCursorColumn();
+    // If cursor at the end put it to the end of the last line.
+    if (cursorLine >= getSnapshot().getHeight()) {
+      cursorLine = getSnapshot().getHeight() - 1;
+      cursorColumn = getSnapshot().getWidth() - 1;
+    }
+    // Has the cursor moved?
+    if (this.cursorLine != cursorLine || this.cursorColumn != cursorColumn) {
+      // Hide the old cursor!
+      showCursor = false;
+      // Clean the previous cursor. Bug 206363: paint also the char to the left and right of the cursor
+      int col = this.cursorColumn;
+      int width = 2;
+      if (col > 0) {
+        col--;
+        width++;
+      }
+      fireCellRangeChanged(col, this.cursorLine, width, 1);
+      // The cursor is shown when it moves.
+      showCursor = true;
+      cursorTime = System.currentTimeMillis();
+      this.cursorLine = cursorLine;
+      this.cursorColumn = cursorColumn;
+      // Draw the new cursor
+      fireCellRangeChanged(this.cursorColumn, this.cursorLine, 1, 1);
+    } else {
+      if (useBlinkingCursor) {
+        long time = System.currentTimeMillis();
+        // TODO Make the cursor blink time customizable.
+        if (time - cursorTime > 500) {
+          showCursor = !showCursor;
+          cursorTime = time;
+          // On some windows machines, there is some leftover when updating the cursor.
+          // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206363
+          int col = this.cursorColumn;
+          int width = 2;
+          if (col > 0) {
+            col--;
+            width++;
+          }
+          fireCellRangeChanged(col, this.cursorLine, width, 1);
+        }
+      }
+    }
+  }
+
+  @Override public void setVisibleRectangle(int startLine, int startCol, int height, int width) {
+    snapshot.setInterestWindow(Math.max(0, startLine), Math.max(1, height));
+    update();
+  }
+
+  protected void showCursor(boolean show) {
+    showCursor = true;
+  }
+
+  @Override public void setCursorEnabled(boolean visible) {
+    cursorTime = System.currentTimeMillis();
+    showCursor = visible;
+    cursorIsEnabled = visible;
+    fireCellRangeChanged(cursorColumn, cursorLine, 1, 1);
+  }
+
+  @Override public boolean isCursorEnabled() {
+    return cursorIsEnabled;
+  }
+
+  @Override public Point getSelectionEnd() {
+    if (selectionStartLine < 0) {
+      return null;
+    }
+    return new Point(selectionEndColumn, seletionEndLine);
+  }
+
+  @Override public Point getSelectionStart() {
+    if (selectionStartLine < 0) {
+      return null;
+    }
+    return new Point(selectionStartCoumn, selectionStartLine);
+  }
+
+  @Override public Point getSelectionAnchor() {
+    if (selectionStartLine < 0) {
+      return null;
+    }
+    return new Point(selectionAnchor.x, selectionAnchor.y);
+  }
+
+  @Override public void setSelectionAnchor(Point anchor) {
+    selectionAnchor.x = anchor.x;
+    selectionAnchor.y = anchor.y;
+  }
+
+  @Override public void setSelection(int startLine, int endLine, int startColumn, int endColumn) {
+    doSetSelection(startLine, endLine, startColumn, endColumn);
+    currentSelection = extractSelectedText();
+  }
+
+  private void doSetSelection(int startLine, int endLine, int startColumn, int endColumn) {
+    Assert.isTrue(startLine < 0 || startLine <= endLine);
+    if (startLine >= 0) {
+      if (selectionSnapshot == null) {
+        selectionSnapshot = snapshot.getTerminalTextData().makeSnapshot();
+        selectionSnapshot.updateSnapshot(true);
+      }
+    } else if (selectionSnapshot != null) {
+      selectionSnapshot.detach();
+      selectionSnapshot = null;
+    }
+    int oldStart = selectionStartLine;
+    int oldEnd = seletionEndLine;
+    selectionStartLine = startLine;
+    seletionEndLine = endLine;
+    selectionStartCoumn = startColumn;
+    selectionEndColumn = endColumn;
+    if (selectionSnapshot != null) {
+      selectionSnapshot.setInterestWindow(0, selectionSnapshot.getHeight());
+    }
+    int changedStart;
+    int changedEnd;
+    if (oldStart < 0) {
+      changedStart = selectionStartLine;
+      changedEnd = seletionEndLine;
+    } else if (selectionStartLine < 0) {
+      changedStart = oldStart;
+      changedEnd = oldEnd;
+    } else {
+      changedStart = Math.min(oldStart, selectionStartLine);
+      changedEnd = Math.max(oldEnd, seletionEndLine);
+    }
+    if (changedStart >= 0) {
+      fireCellRangeChanged(0, changedStart, snapshot.getWidth(), changedEnd - changedStart + 1);
+    }
+  }
+
+  @Override public boolean hasLineSelection(int line) {
+    if (selectionStartLine < 0) {
+      return false;
+    }
+    return line >= selectionStartLine && line <= seletionEndLine;
+  }
+
+  @Override public String getSelectedText() {
+    return currentSelection;
+  }
+
+  /**
+   * Calculates the currently selected text
+   * @return the currently selected text
+   */
+  private String extractSelectedText() {
+    if (selectionStartLine < 0 || selectionStartCoumn < 0 || selectionSnapshot == null) {
+      return "";
+    }
+    StringBuilder buffer = new StringBuilder();
+    for (int line = selectionStartLine; line <= seletionEndLine; line++) {
+      String text;
+      char[] chars = selectionSnapshot.getChars(line);
+      if (chars != null) {
+        text = new String(chars);
+        if (line == seletionEndLine && selectionEndColumn >= 0) {
+          text = text.substring(0, Math.min(selectionEndColumn + 1, text.length()));
+        }
+        if (line == selectionStartLine) {
+          text = text.substring(Math.min(selectionStartCoumn, text.length()));
+        }
+        // get rid of the empty space at the end of the lines
+        text = text.replaceAll("\000+$","");
+        // null means space
+        text = text.replace('\000', ' ');
+      } else {
+        text = "";
+      }
+      buffer.append(text);
+      if (line < seletionEndLine) {
+        buffer.append('\n');
+      }
+    }
+    return buffer.toString();
+  }
+
+  private void updateSelection() {
+    if (selectionSnapshot != null && selectionSnapshot.isOutOfDate()) {
+      selectionSnapshot.updateSnapshot(true);
+      // Has the selection moved?
+      if (selectionSnapshot != null && selectionStartLine >= 0 && selectionSnapshot.getScrollWindowSize() > 0) {
+        int start = selectionStartLine + selectionSnapshot.getScrollWindowShift();
+        int end = seletionEndLine + selectionSnapshot.getScrollWindowShift();
+        if (start < 0) {
+          if (end >= 0) {
+            start = 0;
+          } else {
+            start = -1;
+          }
+        }
+        doSetSelection(start, end, selectionStartCoumn, selectionEndColumn);
+      }
+      // Check if the content of the selection has changed. If the content has changed, clear the selection.
+      if (currentSelection.length() > 0 && selectionSnapshot != null
+          && selectionSnapshot.getFirstChangedLine() <= seletionEndLine
+          && selectionSnapshot.getLastChangedLine() >= selectionStartLine) {
+        // Has the selected text changed?
+        if (!currentSelection.equals(extractSelectedText())) {
+          setSelection(-1, -1, -1, -1);
+        }
+      }
+      // Update the observed window...
+      if (selectionSnapshot != null) {
+        // TODO make -1 to work!
+        selectionSnapshot.setInterestWindow(0, selectionSnapshot.getHeight());
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/GridCanvas.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/GridCanvas.java
new file mode 100644
index 0000000..b964b8a
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/GridCanvas.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A Grid based Canvas. The canvas has rows and columns. CellPainting is done with the abstract method drawCell
+ */
+public abstract class GridCanvas extends VirtualCanvas {
+  private int cellWidth;
+  private int cellHeight;
+
+  public GridCanvas(Composite parent, int style) {
+    super(parent, style);
+    addListener(SWT.MouseWheel, new Listener() {
+      @Override public void handleEvent(Event event) {
+        if (getVerticalBar().isVisible()) {
+          int delta = -cellHeight;
+          if (event.count < 0) {
+            delta = -delta;
+          }
+          scrollYDelta(delta);
+        }
+        event.doit = false;
+      }
+    });
+  }
+
+  @Override protected void paint(GC gc) {
+    Rectangle clipping = gc.getClipping();
+    if (clipping.width == 0 || clipping.height == 0) {
+      return;
+    }
+    Rectangle clientArea = getScreenRectInVirtualSpace();
+    // Beginning coordinates.
+    int xOffset = clientArea.x;
+    int yOffset = clientArea.y;
+    int colFirst = virtualXToCell(xOffset + clipping.x);
+    if (colFirst > getCols()) {
+      colFirst = getCols();
+    } else if (colFirst < 0) {
+      colFirst = 0;
+    }
+    int rowFirst = virtualYToCell(yOffset + clipping.y);
+    // End coordinates.
+    int colLast = virtualXToCell(xOffset + clipping.x + clipping.width + cellWidth);
+    if (colLast > getCols()) {
+      colLast = getCols();
+    }
+    int rowLast = virtualYToCell(yOffset + clipping.y + clipping.height + cellHeight);
+    if (rowLast > getRows()) {
+      rowLast = getRows();
+    }
+    // Draw the cells.
+    for (int row = rowFirst; row <= rowLast; row++) {
+      int cx = colFirst * cellWidth - xOffset;
+      int cy = row * cellHeight - yOffset;
+      drawLine(gc, row, cx, cy, colFirst, colLast);
+    }
+    paintUnoccupiedSpace(gc, clipping);
+  }
+
+  abstract void drawLine(GC gc, int row, int x, int y, int colFirst, int colLast);
+
+  abstract protected int getRows();
+
+  abstract protected int getCols();
+
+  protected void setCellWidth(int cellWidth) {
+    this.cellWidth = cellWidth;
+    getHorizontalBar().setIncrement(this.cellWidth);
+  }
+
+  public int getCellWidth() {
+    return cellWidth;
+  }
+
+  protected void setCellHeight(int cellHeight) {
+    this.cellHeight = cellHeight;
+    getVerticalBar().setIncrement(this.cellHeight);
+  }
+
+  public int getCellHeight() {
+    return cellHeight;
+  }
+
+  int virtualXToCell(int x) {
+    return x / cellWidth;
+  }
+
+  int virtualYToCell(int y) {
+    return y / cellHeight;
+  }
+
+  protected Point screenPointToCell(int x, int y) {
+    x = screenXtoVirtual(x) / cellWidth;
+    y = screenYtoVirtual(y) / cellHeight;
+    return new Point(x, y);
+  }
+
+  Point screenPointToCell(Point point) {
+    return screenPointToCell(point.x, point.y);
+  }
+
+  protected Point cellToOriginOnScreen(int x, int y) {
+    x = virtualXtoScreen(cellWidth * x);
+    y = virtualYtoScreen(cellHeight * y);
+    return new Point(x, y);
+  }
+
+  Point cellToOriginOnScreen(Point cell) {
+    return cellToOriginOnScreen(cell.x, cell.y);
+  }
+
+  Rectangle getCellScreenRect(Point cell) {
+    return getCellScreenRect(cell.x, cell.y);
+  }
+
+  Rectangle getCellScreenRect(int x, int y) {
+    x = cellWidth * virtualXtoScreen(x);
+    y = cellHeight * virtualYtoScreen(y);
+    return new Rectangle(x, y, cellWidth, cellHeight);
+  }
+
+  protected Rectangle getCellVirtualRect(Point cell) {
+    return getCellVirtualRect(cell.x, cell.y);
+  }
+
+  Rectangle getCellVirtualRect(int x, int y) {
+    x = cellWidth * x;
+    y = cellHeight * y;
+    return new Rectangle(x, y, cellWidth, cellHeight);
+  }
+
+  @Override protected void viewRectangleChanged(int x, int y, int width, int height) {
+    int cellX = virtualXToCell(x);
+    int cellY = virtualYToCell(y);
+    // End coordinates
+    int xE = virtualXToCell(x + width);
+    // if(xE>getCols())
+    // xE=getCols();
+    int yE = virtualYToCell(y + height);
+    // if(yE>getRows())
+    // yE=getRows();
+    visibleCellRectangleChanged(cellX, cellY, xE - cellX, yE - cellY);
+  }
+
+  protected void visibleCellRectangleChanged(int x, int y, int width, int height) {}
+
+  @Override protected void setVirtualExtend(int width, int height) {
+    int cellHeight = getCellHeight();
+    if (cellHeight > 0) {
+      height -= height % cellHeight;
+    }
+    super.setVirtualExtend(width, height);
+  }
+
+  @Override protected void setVirtualOrigin(int x, int y) {
+    int cellHeight = getCellHeight();
+    if (cellHeight > 0) {
+      int remainder = y % cellHeight;
+      if (remainder < 0) {
+        y -= (cellHeight + remainder);
+      } else {
+        y -= remainder;
+      }
+    }
+    super.setVirtualOrigin(x, y);
+  }
+
+  @Override protected void scrollY(ScrollBar vBar) {
+    int vSelection = vBar.getSelection();
+    Rectangle bounds = getVirtualBounds();
+    int y = -vSelection;
+    int cellHeight = getCellHeight();
+    if (cellHeight > 0) {
+      int remainder = y % cellHeight;
+      if (remainder < 0) {
+        y -= (cellHeight + remainder);
+      } else {
+        y -= remainder;
+      }
+    }
+    int deltaY = y - bounds.y;
+    if (deltaY != 0) {
+      scrollSmart(0, deltaY);
+      setVirtualOrigin(bounds.x, bounds.y += deltaY);
+    }
+    if (-bounds.y + getRows() * getCellHeight() >= bounds.height) {
+      // Scrolled to bottom - need to redraw bottom area
+      Rectangle clientRect = getClientArea();
+      redraw(0, clientRect.height - this.cellHeight, clientRect.width, this.cellHeight, false);
+    }
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ILinelRenderer.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ILinelRenderer.java
new file mode 100644
index 0000000..d6f856a
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ILinelRenderer.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.graphics.*;
+
+public interface ILinelRenderer {
+  int getCellWidth();
+
+  int getCellHeight();
+
+  void drawLine(ITextCanvasModel model, GC gc, int line, int x, int y, int firstColumn, int lastColumn);
+
+  void onFontChange();
+
+  void setInvertedColors(boolean invert);
+
+  Color getDefaultBackgroundColor();
+
+  void setColors(RGB background, RGB foreground);
+
+  void setFont(Font font);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModel.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModel.java
new file mode 100644
index 0000000..e2aa233
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModel.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.graphics.Point;
+
+import com.google.eclipse.elt.emulator.model.ITerminalTextDataReadOnly;
+
+public interface ITextCanvasModel {
+  void addCellCanvasModelListener(ITextCanvasModelListener listener);
+
+  void removeCellCanvasModelListener(ITextCanvasModelListener listener);
+
+  ITerminalTextDataReadOnly getTerminalText();
+
+  void setVisibleRectangle(int startLine, int startCol, int height, int width);
+
+  /**
+   * Indicates whether the cursor is shown (used for blinking cursors.)
+   *
+   * @return {@code true} if the cursor is shown, {@code false} otherwise.
+   */
+  boolean isCursorOn();
+
+  boolean isCursorEnabled();
+
+  /**
+   * Show/Hide the cursor.
+   *
+   * @param visible indicates whether the cursor should be visible.
+   */
+  void setCursorEnabled(boolean visible);
+
+  int getCursorLine();
+
+  int getCursorColumn();
+
+  Point getSelectionStart();
+
+  Point getSelectionEnd();
+
+  Point getSelectionAnchor();
+
+  void setSelectionAnchor(Point anchor);
+
+  // Negative 'startLine' clears the selection.
+  void setSelection(int startLine, int endLine, int startColumn, int endColumn);
+
+  boolean hasLineSelection(int line);
+
+  String getSelectedText();
+
+  void setBlinkingCursor(boolean useBlinkingCursor);
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModelListener.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModelListener.java
new file mode 100644
index 0000000..0038a9a
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModelListener.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+public interface ITextCanvasModelListener {
+  void rangeChanged(int col, int line, int width, int height);
+
+  void dimensionsChanged(int cols, int rows);
+
+  /**
+   * Called when any text change happened. Used to scroll to the end of text in auto scroll mode. This does not get
+   * fired when the window of interest has changed.
+   */
+  void terminalDataChanged();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PipedInputStream.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PipedInputStream.java
new file mode 100644
index 0000000..f0dbbd2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PipedInputStream.java
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright (c) 1996, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import java.io.*;
+
+import com.google.eclipse.elt.emulator.util.BoundedByteBuffer;
+
+/**
+ * Starts a {@code Runnable} in the display thread when data is available and to pretend no data is available after a
+ * given amount of time the {@code Runnable} is running.
+ */
+public class PipedInputStream extends InputStream {
+  // The output stream used by the terminal back-end to write to the terminal
+  protected final OutputStream outputStream;
+
+  // A blocking byte queue.
+  private final BoundedByteBuffer queue;
+
+  /**
+   * Constructor.
+   *
+   * @param bufferSize the size of the buffer of the output stream.
+   */
+  public PipedInputStream(int bufferSize) {
+    outputStream = new PipedOutputStream();
+    queue = new BoundedByteBuffer(bufferSize);
+  }
+
+  /**
+   * Returns the output stream used by the back-end to write to the terminal.
+   *
+   * @return the output stream used by the back-end to write to the terminal.
+   */
+  public OutputStream getOutputStream() {
+    return outputStream;
+  }
+
+  /**
+   * Waits until data is available for reading.
+   *
+   * @param time the time wait, in milliseconds.
+   * @throws InterruptedException when the thread is interrupted while waiting for the buffer to become ready.
+   */
+  public void waitForAvailable(long time) throws InterruptedException {
+    synchronized (queue) {
+      if (queue.size() == 0 && !queue.isClosed()) {
+        queue.wait(time);
+      }
+    }
+  }
+
+  @Override public int available() {
+    synchronized (queue) {
+      return queue.size();
+    }
+  }
+
+  @Override public int read() throws IOException {
+    try {
+      synchronized (queue) {
+        return queue.read();
+      }
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+      return -1;
+    }
+  }
+
+  @Override public void close() throws IOException {
+    synchronized (queue) {
+      queue.close();
+    }
+  }
+
+  @Override public int read(byte[] b, int off, int len) throws IOException {
+    if (len == 0) {
+      return 0;
+    }
+    int n = 0;
+    // Read as much as we can using a single synchronized statement.
+    try {
+      synchronized (queue) {
+        // If nothing available, block and read one byte.
+        if (queue.size() == 0) {
+          // Block now until at least one byte is available.
+          int c = queue.read();
+          // Are we at the end of stream?
+          if (c == -1) {
+            return -1;
+          }
+          b[off] = (byte) c;
+          n++;
+        }
+        // Is there more data available?
+        if (n < len && queue.size() > 0) {
+          // Read at most available.
+          int available = Math.min(queue.size(), len - n);
+          // Are we at the end of the stream?
+          if (available == 0 && queue.isClosed()) {
+            // If no byte was read, return -1 to indicate end of stream; otherwise return the bytes we read up to now.
+            if (n == 0) {
+              n = -1;
+            }
+            return n;
+          }
+          queue.read(b, off + n, available);
+          n += available;
+        }
+      }
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+    }
+    return n;
+  }
+
+  /**
+   * An output stream that calls {@link PipedInputStream#textAvailable} every time data is written to the stream. The
+   * data is written to {@link PipedInputStream#queue}.
+   */
+  private class PipedOutputStream extends OutputStream {
+    @Override public void write(byte[] b, int off, int len) throws IOException {
+      try {
+        synchronized (queue) {
+          if (queue.isClosed()) {
+            throw new IOException("Stream is closed!");
+          }
+          int written = 0;
+          while (written < len) {
+            if (queue.getFreeSlots() == 0) {
+              // If no slots available, write one byte and block until free slots are available.
+              queue.write(b[off + written]);
+              written++;
+            } else {
+              // If slots are available, write as much as we can in one junk
+              int n = Math.min(queue.getFreeSlots(), len - written);
+              queue.write(b, off + written, n);
+              written += n;
+            }
+          }
+        }
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+      }
+    }
+
+    @Override public void write(int b) throws IOException {
+      try {
+        synchronized (queue) {
+          if (queue.isClosed()) {
+            throw new IOException("Stream is closed!");
+          }
+          queue.write((byte) b);
+        }
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+      }
+    }
+
+    @Override public void close() throws IOException {
+      synchronized (queue) {
+        queue.close();
+      }
+    }
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PollingTextCanvasModel.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PollingTextCanvasModel.java
new file mode 100644
index 0000000..37c7059
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PollingTextCanvasModel.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.widgets.Display;
+
+import com.google.eclipse.elt.emulator.model.ITerminalTextDataSnapshot;
+
+public class PollingTextCanvasModel extends AbstractTextCanvasModel {
+  private int pollInterval = 50;
+
+  public PollingTextCanvasModel(ITerminalTextDataSnapshot snapshot) {
+    super(snapshot);
+    Display.getDefault().timerExec(pollInterval, new Runnable() {
+      @Override public void run() {
+        update();
+        Display.getDefault().timerExec(pollInterval, this);
+      }
+    });
+  }
+
+  public void setUpdateInterval(int interval) {
+    pollInterval = interval;
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/StyleMap.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/StyleMap.java
new file mode 100644
index 0000000..c277bac
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/StyleMap.java
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import java.util.*;
+
+import org.eclipse.jface.resource.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.Display;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public class StyleMap {
+  private static final String BLACK = "black";
+  private static final String WHITE = "white";
+  private static final String WHITE_FOREGROUND = "white_foreground";
+  private static final String GRAY = "gray";
+  private static final String MAGENTA = "magenta";
+  private static final String CYAN = "cyan";
+  private static final String YELLOW = "yellow";
+  private static final String BLUE = "blue";
+  private static final String GREEN = "green";
+  private static final String RED = "red";
+
+  private static final String PREFIX = "org.eclipse.tm.internal.";
+
+  private final Map<StyleColor, Color> colorMapForeground = new HashMap<StyleColor, Color>();
+  private final Map<StyleColor, Color> colorMapBackground = new HashMap<StyleColor, Color>();
+  private final Map<StyleColor, Color> colorMapIntense = new HashMap<StyleColor, Color>();
+
+  private Point charSize;
+
+  private boolean invertColors;
+  private boolean proportional;
+
+  private final int[] offsets = new int[256];
+
+  private Color background = getColor(new RGB(0, 0, 0));
+  private Color foreground = getColor(new RGB(229, 229, 229));
+
+  private Font font = JFaceResources.getFontRegistry().get("org.eclipse.jface.textfont");
+
+  StyleMap() {
+    initColors();
+    updateFont();
+  }
+
+  private void initColors() {
+    initForegroundColors();
+    initBackgroundColors();
+    initIntenseColors();
+  }
+
+  private void initForegroundColors() {
+    if (invertColors) {
+      setColor(colorMapForeground, WHITE, 0, 0, 0);
+      setColor(colorMapForeground, WHITE_FOREGROUND, 50, 50, 50);
+      setColor(colorMapForeground, BLACK, 229, 229, 229);
+    } else {
+      setColor(colorMapForeground, WHITE, 255, 255, 255);
+      setColor(colorMapForeground, WHITE_FOREGROUND, 229, 229, 229);
+      setColor(colorMapForeground, BLACK, 50, 50, 50);
+    }
+    setColor(colorMapForeground, RED, 205, 0, 0);
+    setColor(colorMapForeground, GREEN, 0, 205, 0);
+    setColor(colorMapForeground, BLUE, 0, 0, 238);
+    setColor(colorMapForeground, YELLOW, 205, 205, 0);
+    setColor(colorMapForeground, CYAN, 0, 205, 205);
+    setColor(colorMapForeground, MAGENTA, 205, 0, 205);
+    setColor(colorMapForeground, GRAY, 229, 229, 229);
+  }
+
+  private void initBackgroundColors() {
+    if (invertColors) {
+      setColor(colorMapBackground, WHITE, 0, 0, 0);
+      setColor(colorMapBackground, WHITE_FOREGROUND, 50, 50, 50); // only used when colors are inverse
+      setColor(colorMapBackground, BLACK, 255, 255, 255); // cursor color
+    } else {
+      setColor(colorMapBackground, WHITE, 255, 255, 255);
+      setColor(colorMapBackground, WHITE_FOREGROUND, 229, 229, 229);
+      setColor(colorMapBackground, BLACK, 0, 0, 0);
+    }
+    setColor(colorMapBackground, RED, 205, 0, 0);
+    setColor(colorMapBackground, GREEN, 0, 205, 0);
+    setColor(colorMapBackground, BLUE, 0, 0, 238);
+    setColor(colorMapBackground, YELLOW, 205, 205, 0);
+    setColor(colorMapBackground, CYAN, 0, 205, 205);
+    setColor(colorMapBackground, MAGENTA, 205, 0, 205);
+    setColor(colorMapBackground, GRAY, 229, 229, 229);
+  }
+
+  private void initIntenseColors() {
+    if (invertColors) {
+      setColor(colorMapIntense, WHITE, 127, 127, 127);
+      setColor(colorMapIntense, WHITE_FOREGROUND, 0, 0, 0); // only used when colors are inverse
+      setColor(colorMapIntense, BLACK, 255, 255, 255);
+    } else {
+      setColor(colorMapIntense, WHITE, 255, 255, 255);
+      setColor(colorMapIntense, WHITE_FOREGROUND, 255, 255, 255);
+      setColor(colorMapIntense, BLACK, 0, 0, 0);
+    }
+    setColor(colorMapIntense, RED, 255, 0, 0);
+    setColor(colorMapIntense, GREEN, 0, 255, 0);
+    setColor(colorMapIntense, BLUE, 92, 92, 255);
+    setColor(colorMapIntense, YELLOW, 255, 255, 0);
+    setColor(colorMapIntense, CYAN, 0, 255, 255);
+    setColor(colorMapIntense, MAGENTA, 255, 0, 255);
+    setColor(colorMapIntense, GRAY, 255, 255, 255);
+  }
+
+  private void setColor(Map<StyleColor, Color> colorMap, String name, int r, int g, int b) {
+    Color color = getColor(new RGB(r, g, b));
+    setColor(colorMap, color, StyleColor.getStyleColor(name));
+    setColor(colorMap, color, StyleColor.getStyleColor(name.toUpperCase()));
+  }
+
+  private void setColor(Map<StyleColor, Color> colorMap, Color color, StyleColor styleColor) {
+    if (styleColor != null) {
+      colorMap.put(styleColor, color);
+    }
+  }
+
+  public Color getForegroundColor(Style style) {
+    if (style == null) {
+      return foreground;
+    }
+    StyleColor color = style.isReverse() ? style.getBackground() : style.getForeground();
+    Map<StyleColor, Color> map = style.isBold() ? colorMapIntense : colorMapForeground;
+    Color actualColor = map.get(color);
+    if (actualColor == null) {
+      actualColor = foreground;
+    }
+    return actualColor;
+  }
+
+  public Color getBackgroundColor(Style style) {
+    if (style == null) {
+      return background;
+    }
+    StyleColor color = style.isReverse() ? style.getForeground() : style.getBackground();
+    Color actualColor = colorMapBackground.get(color);
+    if (actualColor == null) {
+      actualColor = background;
+    }
+    return actualColor;
+  }
+
+  public void setInvertedColors(boolean invert) {
+    if (invert == invertColors) {
+      return;
+    }
+    invertColors = invert;
+    initColors();
+  }
+
+  public Font getFont(Style style) {
+    if (style == null) {
+      return font;
+    }
+    FontData fontDatas[] = font.getFontData();
+    FontData data = fontDatas[0];
+    if (style.isBold()) {
+      return new Font(font.getDevice(), data.getName(), data.getHeight(), data.getStyle() | SWT.BOLD);
+    }
+    if (style.isUnderline()) {
+      return new Font(font.getDevice(), data.getName(), data.getHeight(), data.getStyle() | SWT.ITALIC);
+    }
+    return font;
+  }
+
+  public Font getFont() {
+    return font;
+  }
+
+  public int getFontWidth() {
+    return charSize.x;
+  }
+
+  public int getFontHeight() {
+    return charSize.y;
+  }
+
+  public void updateFont() {
+    Display display = Display.getCurrent();
+    GC gc = new GC(display);
+    gc.setFont(font);
+    charSize = gc.textExtent("W");
+    proportional = false;
+    for (char c = ' '; c <= '~'; c++) {
+      // Consider only the first 128 chars for deciding if a font is proportional.
+      if (measureChar(gc, c, true)) {
+        proportional = true;
+      }
+    }
+    // TODO should we also consider the upper 128 chars?
+    for (char c = ' ' + 128; c <= '~' + 128; c++) {
+      measureChar(gc, c, false);
+    }
+    if (proportional) {
+      charSize.x -= 2; // Works better on small fonts.
+    }
+    for (int i = 0; i < offsets.length; i++) {
+      offsets[i] = (charSize.x - offsets[i]) / 2;
+    }
+    gc.dispose();
+  }
+
+  private boolean measureChar(GC gc, char c, boolean updateMax) {
+    boolean proportional = false;
+    Point extent = gc.textExtent(String.valueOf(c));
+    if (extent.x > 0 && extent.y > 0 && (charSize.x != extent.x || charSize.y != extent.y)) {
+      proportional = true;
+      if (updateMax) {
+        charSize.x = Math.max(charSize.x, extent.x);
+        charSize.y = Math.max(charSize.y, extent.y);
+      }
+    }
+    offsets[c] = extent.x;
+    return proportional;
+  }
+
+  public boolean isFontProportional() {
+    return proportional;
+  }
+
+  /**
+   * Return the offset in pixels required to center a given character.
+   *
+   * @param c the character to measure.
+   * @return the offset in x direction to center this character.
+   */
+  public int getCharOffset(char c) {
+    if (c >= offsets.length) {
+      return 0;
+    }
+    return offsets[c];
+  }
+
+  public void setColors(RGB background, RGB foreground) {
+    this.background = getColor(background);
+    this.foreground = getColor(foreground);
+  }
+
+  private Color getColor(RGB colorData) {
+    String name = PREFIX + colorData.red + "-" + colorData.green + "-" + colorData.blue;
+    ColorRegistry colorRegistry = JFaceResources.getColorRegistry();
+    Color color = colorRegistry.get(name);
+    if (color == null) {
+      colorRegistry.put(name, colorData);
+      color = colorRegistry.get(name);
+    }
+    return color;
+  }
+
+  public void setFont(Font font) {
+    this.font = font;
+    updateFont();
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextCanvas.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextCanvas.java
new file mode 100644
index 0000000..02b19b2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextCanvas.java
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * A cell oriented Canvas. Maintains a list of "cells". It can either be vertically or horizontally scrolled. The
+ * CellRenderer is responsible for painting the cell.
+ */
+public class TextCanvas extends GridCanvas {
+  private final ITextCanvasModel cellCanvasModel;
+  private final ILinelRenderer cellRenderer;
+  private boolean scrollLockOn;
+  private Point draggingStart;
+  private Point draggingEnd;
+  private boolean hasSelection;
+  private ResizeListener resizeListener;
+
+  // The minSize is meant to determine the minimum size of the backing store (grid) into which remote data is rendered.
+  // If the viewport is smaller than that minimum size, the backing store size remains at the minSize,and a scrollbar is
+  // shown instead. In reality, this has the following issues or effects today:
+  //
+  // (a) Bug 281328: For very early data coming in before the widget is realized, the minSize determines into what
+  // initial grid that is rendered. See also {@link #addResizeHandler(ResizeListener)}.
+  //
+  // (b) Bug 294468: Since we have redraw and size computation problems with horizontal scrollers, for now the
+  // minColumns must be small enough to avoid a horizontal scroller appearing in most cases.
+  //
+  // (c) Bug 294327: Since we have problems with the vertical scroller showing the correct location, minLines must be
+  // small enough to avoid a vertical scroller or new data may be rendered off-screen.
+  //
+  // As a compromise, we have been working with a 20x4 since the terminal inception, though many users would want a
+  // 80x24 minSize and backing store.
+
+  // Pros and cons of the small minsize:
+  // + consistent "remote size==viewport size", vi works as expected
+  // - dumb terminals which expect 80x24 render garbled on small viewport
+  //
+  // If bug 294468 were resolved, an 80 wide minSize would be preferrable since it allows switching the terminal
+  // viewport small/large as needed, without destroying the backing store. For a complete solution, bug 196462 tracks
+  // the request for a user-defined fixed-widow-size-mode.
+  private int minColumns = 80;
+
+  private int minLines = 4;
+  private boolean cursorEnabled;
+  private boolean resizing;
+
+  /**
+   * Create a new CellCanvas with the given SWT style bits. (SWT.H_SCROLL and SWT.V_SCROLL are automatically added).
+   */
+  public TextCanvas(Composite parent, ITextCanvasModel model, int style, ILinelRenderer cellRenderer) {
+    super(parent, style | SWT.H_SCROLL | SWT.V_SCROLL);
+    this.cellRenderer = cellRenderer;
+    setCellWidth(cellRenderer.getCellWidth());
+    setCellHeight(cellRenderer.getCellHeight());
+    cellCanvasModel = model;
+    cellCanvasModel.addCellCanvasModelListener(new ITextCanvasModelListener() {
+      @Override public void rangeChanged(int col, int line, int width, int height) {
+        repaintRange(col, line, width, height);
+      }
+
+      @Override public void dimensionsChanged(int cols, int rows) {
+        calculateGrid();
+      }
+
+      @Override public void terminalDataChanged() {
+        if (!isDisposed() && !resizing) {
+          // scroll to end (unless scroll lock is active)
+          calculateGrid();
+          scrollToEnd();
+        }
+      }
+    });
+    // let the cursor blink if the text canvas gets the focus...
+    addFocusListener(new FocusListener() {
+      @Override public void focusGained(FocusEvent e) {
+        cellCanvasModel.setCursorEnabled(cursorEnabled);
+      }
+
+      @Override public void focusLost(FocusEvent e) {
+        cellCanvasModel.setCursorEnabled(false);
+      }
+    });
+    addMouseListener(new MouseAdapter() {
+      @Override public void mouseDown(MouseEvent e) {
+        if (e.button == 1) { // left button
+          draggingStart = screenPointToCell(e.x, e.y);
+          hasSelection = false;
+          if ((e.stateMask & SWT.SHIFT) != 0) {
+            Point anchor = cellCanvasModel.getSelectionAnchor();
+            if (anchor != null) {
+              draggingStart = anchor;
+            }
+          } else {
+            cellCanvasModel.setSelectionAnchor(draggingStart);
+          }
+          draggingEnd = null;
+        }
+      }
+
+      @Override public void mouseUp(MouseEvent e) {
+        if (e.button == 1) { // left button
+          updateHasSelection(e);
+          if (hasSelection) {
+            setSelection(screenPointToCell(e.x, e.y));
+          } else {
+            cellCanvasModel.setSelection(-1, -1, -1, -1);
+          }
+          draggingStart = null;
+        }
+      }
+    });
+    addMouseMoveListener(new MouseMoveListener() {
+      @Override public void mouseMove(MouseEvent e) {
+        if (draggingStart != null) {
+          updateHasSelection(e);
+          setSelection(screenPointToCell(e.x, e.y));
+        }
+      }
+    });
+    serVerticalBarVisible(true);
+    setHorizontalBarVisible(false);
+  }
+
+  // The user has to drag the mouse to at least one character to make a selection. Once this is done, even a one char
+  // selection is OK.
+  private void updateHasSelection(MouseEvent e) {
+    if (draggingStart != null) {
+      Point p = screenPointToCell(e.x, e.y);
+      if (draggingStart.x != p.x || draggingStart.y != p.y) {
+        hasSelection = true;
+      }
+    }
+  }
+
+  void setSelection(Point p) {
+    if (draggingStart != null && !p.equals(draggingEnd)) {
+      draggingEnd = p;
+      if (compare(p, draggingStart) < 0) {
+        // bug 219589 - make sure selection start coordinates are non-negative
+        int startColumn = Math.max(0, p.x);
+        int startRow = Math.max(p.y, 0);
+        cellCanvasModel.setSelection(startRow, draggingStart.y, startColumn, draggingStart.x);
+      } else {
+        cellCanvasModel.setSelection(draggingStart.y, p.y, draggingStart.x, p.x);
+      }
+    }
+  }
+
+  int compare(Point p1, Point p2) {
+    if (p1.equals(p2)) {
+      return 0;
+    }
+    if (p1.y == p2.y) {
+      return p1.x > p2.x ? 1 : -1;
+    }
+    return p1.y > p2.y ? 1 : -1;
+  }
+
+  public ILinelRenderer getCellRenderer() {
+    return cellRenderer;
+  }
+
+  public int getMinColumns() {
+    return minColumns;
+  }
+
+  public void setMinColumns(int minColumns) {
+    this.minColumns = minColumns;
+  }
+
+  public int getMinLines() {
+    return minLines;
+  }
+
+  public void setMinLines(int minLines) {
+    this.minLines = minLines;
+  }
+
+  protected void onResize(boolean init) {
+    if (resizeListener != null) {
+      Rectangle bonds = getClientArea();
+      int cellHeight = getCellHeight();
+      int cellWidth = getCellWidth();
+      int lines = bonds.height / cellHeight;
+      int columns = bonds.width / cellWidth;
+      // When the view is minimized, its size is set to 0 we don't sent this to the terminal!
+      if ((lines > 0 && columns > 0) || init) {
+        if (columns < minColumns) {
+          if (!isHorizontalBarVisble()) {
+            setHorizontalBarVisible(true);
+            bonds = getClientArea();
+            lines = bonds.height / cellHeight;
+          }
+          columns = minColumns;
+        } else if (columns >= minColumns && isHorizontalBarVisble()) {
+          setHorizontalBarVisible(false);
+          bonds = getClientArea();
+          lines = bonds.height / cellHeight;
+          columns = bonds.width / cellWidth;
+        }
+        if (lines < minLines) {
+          lines = minLines;
+        }
+        resizeListener.sizeChanged(lines, columns);
+      }
+    }
+    super.onResize();
+    calculateGrid();
+  }
+
+  @Override protected void onResize() {
+    resizing = true;
+    try {
+      onResize(false);
+    } finally {
+      resizing = false;
+    }
+  }
+
+  private void calculateGrid() {
+    Rectangle virtualBounds = getVirtualBounds();
+    setRedraw(false);
+    try {
+      setVirtualExtend(getCols() * getCellWidth(), getRows() * getCellHeight());
+      getParent().layout();
+      if (resizing) {
+        // scroll to end if view port was near last line
+        Rectangle viewRect = getViewRectangle();
+        if (virtualBounds.height - (viewRect.y + viewRect.height) < getCellHeight() * 2) {
+          scrollToEnd();
+        }
+      }
+    } finally {
+      setRedraw(true);
+    }
+  }
+
+  void scrollToEnd() {
+    if (!scrollLockOn) {
+      int y = -(getRows() * getCellHeight() - getClientArea().height);
+      if (y > 0) {
+        y = 0;
+      }
+      Rectangle v = getViewRectangle();
+      if (v.y != -y) {
+        setVirtualOrigin(v.x, y);
+      }
+      // make sure the scroll area is correct.
+      scrollY(getVerticalBar());
+      scrollX(getHorizontalBar());
+    }
+  }
+
+  public boolean isScrollLockOn() {
+    return scrollLockOn;
+  }
+
+  public void setScrollLockOn(boolean on) {
+    scrollLockOn = on;
+  }
+
+  protected void repaintRange(int col, int line, int width, int height) {
+    Point origin = cellToOriginOnScreen(col, line);
+    Rectangle r = new Rectangle(origin.x, origin.y, width * getCellWidth(), height * getCellHeight());
+    repaint(r);
+  }
+
+  @Override protected void drawLine(GC gc, int line, int x, int y, int colFirst, int colLast) {
+    cellRenderer.drawLine(cellCanvasModel, gc, line, x, y, colFirst, colLast);
+  }
+
+  @Override protected Color getTerminalBackgroundColor() {
+    return cellRenderer.getDefaultBackgroundColor();
+  }
+
+  @Override protected void visibleCellRectangleChanged(int x, int y, int width, int height) {
+    cellCanvasModel.setVisibleRectangle(y, x, height, width);
+    update();
+  }
+
+  @Override protected int getCols() {
+    return cellCanvasModel.getTerminalText().getWidth();
+  }
+
+  @Override protected int getRows() {
+    return cellCanvasModel.getTerminalText().getHeight();
+  }
+
+  public String getSelectionText() {
+    // TODO -- create a hasSelectionMethod!
+    return cellCanvasModel.getSelectedText();
+  }
+
+  public void copy() {
+    Clipboard clipboard = new Clipboard(getDisplay());
+    clipboard.setContents(new Object[] { getSelectionText() }, new Transfer[] { TextTransfer.getInstance() });
+    clipboard.dispose();
+  }
+
+  public void selectAll() {
+    cellCanvasModel.setSelection(
+        0, cellCanvasModel.getTerminalText().getHeight(), 0, cellCanvasModel.getTerminalText().getWidth());
+    cellCanvasModel.setSelectionAnchor(new Point(0, 0));
+  }
+
+  public boolean isEmpty() {
+    return false;
+  }
+
+  /**
+   * Gets notified when the visible size of the terminal changes. This should update the model!
+   */
+  public static interface ResizeListener {
+    void sizeChanged(int lines, int columns);
+  }
+
+  public void addResizeHandler(ResizeListener listener) {
+    if (resizeListener != null) {
+      throw new IllegalArgumentException("There can be at most one listener at the moment!");
+    }
+    resizeListener = listener;
+    // Bug 281328: The very first few characters might be missing in the terminal control if opened and connected
+    // programmatically.
+    //
+    // In case the terminal had not been visible yet or is too small (less than one line visible), the terminal should
+    // have a minimum size to avoid RuntimeExceptions.
+    Rectangle bonds = getClientArea();
+    if (bonds.height < getCellHeight() || bonds.width < getCellWidth()) {
+      // Widget not realized yet, or minimized to < 1 item. Just tell the listener our min size.
+      resizeListener.sizeChanged(getMinLines(), getMinColumns());
+    } else {
+      // Widget realized: compute actual size and force telling the listener
+      onResize(true);
+    }
+  }
+
+  public void onFontChange() {
+    cellRenderer.onFontChange();
+    setCellWidth(cellRenderer.getCellWidth());
+    setCellHeight(cellRenderer.getCellHeight());
+    calculateGrid();
+  }
+
+  public void setInvertedColors(boolean invert) {
+    cellRenderer.setInvertedColors(invert);
+    redraw();
+  }
+
+  /**
+   * Indicates whether the cursor is enabled (blinking.) By default the cursor is not enabled.
+   *
+   * @return {@code true} if the cursor is enabled, {@code false} otherwise.
+   */
+  public boolean isCursorEnabled() {
+    return cursorEnabled;
+  }
+
+  /**
+   * Enables or disables the cursor (enabling means that the cursor blinks.)
+   *
+   * @param enabled indicates whether the cursor should be enabled.
+   */
+  public void setCursorEnabled(boolean enabled) {
+    if (enabled != cursorEnabled) {
+      cursorEnabled = enabled;
+      cellCanvasModel.setCursorEnabled(cursorEnabled);
+    }
+  }
+
+  public void setColors(RGB background, RGB foreground) {
+    cellRenderer.setColors(background, foreground);
+    redraw();
+  }
+
+  @Override public void setFont(Font font) {
+    super.setFont(font);
+    cellRenderer.setFont(font);
+    redraw();
+  }
+
+  @Override public Point screenPointToCell(int x, int y) {
+    return super.screenPointToCell(x, y);
+  }
+
+  public void setBlinkingCursor(boolean useBlinkingCursor) {
+    cellCanvasModel.setBlinkingCursor(useBlinkingCursor);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextLineRenderer.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextLineRenderer.java
new file mode 100644
index 0000000..409b562
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextLineRenderer.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import static org.eclipse.swt.SWT.*;
+
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.Display;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public class TextLineRenderer implements ILinelRenderer {
+  private final ITextCanvasModel model;
+  private final StyleMap styleMap = new StyleMap();
+
+  public TextLineRenderer(TextCanvas c, ITextCanvasModel model) {
+    this.model = model;
+  }
+
+  @Override public int getCellWidth() {
+    return styleMap.getFontWidth();
+  }
+
+  @Override public int getCellHeight() {
+    return styleMap.getFontHeight();
+  }
+
+  @Override public void drawLine(
+      ITextCanvasModel model, GC gc, int line, int x, int y, int firstColumn, int lastColumn) {
+    if (line < 0 || line >= getTerminalText().getHeight() || firstColumn >= getTerminalText().getWidth()
+        || firstColumn - lastColumn == 0) {
+      fillBackground(gc, x, y, getCellWidth() * (lastColumn - firstColumn), getCellHeight());
+      return;
+    }
+    lastColumn = Math.min(lastColumn, getTerminalText().getWidth());
+    LineSegment[] segments = getTerminalText().getLineSegments(line, firstColumn, lastColumn - firstColumn);
+    for (int i = 0; i < segments.length; i++) {
+      LineSegment segment = segments[i];
+      Style style = segment.getStyle();
+      setupGC(gc, style);
+      String text = segment.getText();
+      drawText(gc, x, y, firstColumn, segment.getColumn(), text);
+      drawCursor(model, gc, line, x, y, firstColumn);
+    }
+    if (this.model.hasLineSelection(line)) {
+      Display display = Display.getCurrent();
+      gc.setForeground(display.getSystemColor(COLOR_LIST_SELECTION_TEXT));
+      gc.setBackground(display.getSystemColor(COLOR_LIST_SELECTION));
+      Point start = model.getSelectionStart();
+      Point end = model.getSelectionEnd();
+      char[] chars = model.getTerminalText().getChars(line);
+      if (chars == null) {
+        return;
+      }
+      int offset = 0;
+      if (start.y == line) {
+        offset = start.x;
+      }
+      offset = Math.max(offset, firstColumn);
+      int len;
+      if (end.y == line) {
+        len = end.x - offset + 1;
+      } else {
+        len = chars.length - offset + 1;
+      }
+      len = Math.min(len, chars.length - offset);
+      if (len > 0) {
+        String text = new String(chars, offset, len);
+        drawText(gc, x, y, firstColumn, offset, text);
+      }
+    }
+  }
+
+  private void fillBackground(GC gc, int x, int y, int width, int height) {
+    Color bg = gc.getBackground();
+    gc.setBackground(getDefaultBackgroundColor());
+    gc.fillRectangle(x, y, width, height);
+    gc.setBackground(bg);
+  }
+
+  @Override public Color getDefaultBackgroundColor() {
+    // null == default style
+    return styleMap.getBackgroundColor(null);
+  }
+
+  private void drawCursor(ITextCanvasModel model, GC gc, int row, int x, int y, int colFirst) {
+    if (!model.isCursorOn()) {
+      return;
+    }
+    int cursorLine = model.getCursorLine();
+    if (row == cursorLine) {
+      int cursorColumn = model.getCursorColumn();
+      if (cursorColumn < getTerminalText().getWidth()) {
+        Style style = getTerminalText().getStyle(row, cursorColumn);
+        if (style != null) {
+          style = style.setReverse(!style.isReverse());
+          setupGC(gc, style);
+        } else {
+          setBackground(gc, styleMap.getForegroundColor(null));
+          setForeground(gc, styleMap.getBackgroundColor(null));
+        }
+        String text = String.valueOf(getTerminalText().getChar(row, cursorColumn));
+        drawText(gc, x, y, colFirst, cursorColumn, text);
+      }
+    }
+  }
+
+  private void drawText(GC gc, int x, int y, int colFirst, int col, String text) {
+    int offset = (col - colFirst) * getCellWidth();
+    if (styleMap.isFontProportional()) {
+      // Draw the background.
+      // TODO why does this not work?
+      // gc.fillRectangle(x, y, styleMap.getFontWidth() * text.length(), styleMap.getFontHeight());
+      for (int i = 0; i < text.length(); i++) {
+        char c = text.charAt(i);
+        int fontWidth = styleMap.getFontWidth();
+        int newX = x + offset + i * fontWidth;
+        // TODO why do I have to draw the background character by character?
+        gc.fillRectangle(newX, y, fontWidth, styleMap.getFontHeight());
+        if (c != ' ' && c != '\000') {
+          gc.drawString(String.valueOf(c), styleMap.getCharOffset(c) + newX, y, true);
+        }
+      }
+    } else {
+      text = text.replace('\000', ' ');
+      gc.drawString(text, x + offset, y, false);
+    }
+  }
+
+  private void setupGC(GC gc, Style style) {
+    setForeground(gc, styleMap.getForegroundColor(style));
+    setBackground(gc, styleMap.getBackgroundColor(style));
+    Font font = styleMap.getFont(style);
+    if (font != gc.getFont()) {
+      gc.setFont(font);
+    }
+  }
+
+  private void setForeground(GC gc, Color color) {
+    if (color != gc.getForeground()) {
+      gc.setForeground(color);
+    }
+  }
+
+  private void setBackground(GC gc, Color color) {
+    if (color != gc.getBackground()) {
+      gc.setBackground(color);
+    }
+  }
+
+  ITerminalTextDataReadOnly getTerminalText() {
+    return model.getTerminalText();
+  }
+
+  @Override public void onFontChange() {
+    styleMap.updateFont();
+  }
+
+  @Override public void setInvertedColors(boolean invert) {
+    styleMap.setInvertedColors(invert);
+
+  }
+
+  @Override public void setColors(RGB background, RGB foreground) {
+    styleMap.setColors(background, foreground);
+  }
+
+  @Override public void setFont(Font font) {
+    styleMap.setFont(font);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/VirtualCanvas.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/VirtualCanvas.java
new file mode 100644
index 0000000..901ddc7
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/VirtualCanvas.java
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import static org.eclipse.core.runtime.IStatus.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+import com.google.eclipse.elt.emulator.impl.TerminalPlugin;
+
+/**
+ * A {@code Canvas} showing a virtual object. Virtual: the extent of the total canvas. Screen: the visible client area
+ * in the screen.
+ */
+public abstract class VirtualCanvas extends Canvas {
+  private final Rectangle virtualBounds = new Rectangle(0, 0, 0, 0);
+
+  /** Called when the viewed part is changing. */
+  private final Rectangle viewRectangle = new Rectangle(0, 0, 0, 0);
+
+  private Rectangle clientArea;
+
+   /** Prevent infinite loop in {@link #updateScrollbars()} */
+  private boolean inUpdateScrollbars;
+
+  private static boolean inUpdateScrollbarsLogged;
+
+  public VirtualCanvas(Composite parent, int style) {
+    super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE);
+    clientArea = getClientArea();
+    addListener(SWT.Paint, new Listener() {
+      @Override public void handleEvent(Event event) {
+        paint(event.gc);
+      }
+    });
+    addListener(SWT.Resize, new Listener() {
+      @Override public void handleEvent(Event event) {
+        clientArea = getClientArea();
+        onResize();
+      }
+    });
+    getVerticalBar().addListener(SWT.Selection, new Listener() {
+      @Override public void handleEvent(Event e) {
+        scrollY((ScrollBar) e.widget);
+      }
+    });
+    getHorizontalBar().addListener(SWT.Selection, new Listener() {
+      @Override public void handleEvent(Event e) {
+        scrollX((ScrollBar) e.widget);
+      }
+    });
+  }
+
+  protected void onResize() {
+    updateViewRectangle();
+  }
+
+  protected void scrollX(ScrollBar horizontalBar) {
+    int selection = horizontalBar.getSelection();
+    int destinationX = -selection - virtualBounds.x;
+    virtualBounds.x = -selection;
+    scrollSmart(destinationX, 0);
+    updateViewRectangle();
+  }
+
+  protected void scrollXDelta(int delta) {
+    getHorizontalBar().setSelection(-virtualBounds.x + delta);
+    scrollX(getHorizontalBar());
+  }
+
+  protected void scrollY(ScrollBar vBar) {
+    int vSelection = vBar.getSelection();
+    int destY = -vSelection - virtualBounds.y;
+    if (destY != 0) {
+      virtualBounds.y = -vSelection;
+      scrollSmart(0, destY);
+      updateViewRectangle();
+    }
+  }
+
+  protected void scrollYDelta(int delta) {
+    getVerticalBar().setSelection(-virtualBounds.y + delta);
+    scrollY(getVerticalBar());
+  }
+
+  protected void scrollSmart(int deltaX, int deltaY) {
+    if (deltaX != 0 || deltaY != 0) {
+      Rectangle rect = getBounds();
+      scroll(deltaX, deltaY, 0, 0, rect.width, rect.height, false);
+    }
+  }
+
+  protected void revealRect(Rectangle rect) {
+    Rectangle visibleRect = getScreenRectInVirtualSpace();
+    // scroll the X part
+    int deltaX = 0;
+    if (rect.x < visibleRect.x) {
+      deltaX = rect.x - visibleRect.x;
+    } else if (visibleRect.x + visibleRect.width < rect.x + rect.width) {
+      deltaX = (rect.x + rect.width) - (visibleRect.x + visibleRect.width);
+    }
+    if (deltaX != 0) {
+      getHorizontalBar().setSelection(-virtualBounds.x + deltaX);
+      scrollX(getHorizontalBar());
+    }
+    // scroll the Y part
+    int deltaY = 0;
+    if (rect.y < visibleRect.y) {
+      deltaY = rect.y - visibleRect.y;
+    } else if (visibleRect.y + visibleRect.height < rect.y + rect.height) {
+      deltaY = (rect.y + rect.height) - (visibleRect.y + visibleRect.height);
+    }
+    if (deltaY != 0) {
+      getVerticalBar().setSelection(-virtualBounds.y + deltaY);
+      scrollY(getVerticalBar());
+    }
+  }
+
+  protected void repaint(Rectangle r) {
+    if (isDisposed()) {
+      return;
+    }
+    if (inClipping(r, clientArea)) {
+      redraw(r.x, r.y, r.width, r.height, true);
+      update();
+    }
+  }
+
+  abstract protected void paint(GC gc);
+
+  protected Color getTerminalBackgroundColor() {
+    return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+  }
+
+  protected void paintUnoccupiedSpace(GC gc, Rectangle clipping) {
+    int width = virtualBounds.width + virtualBounds.x;
+    int height = virtualBounds.height + virtualBounds.y;
+    int marginWidth = (clipping.x + clipping.width) - width;
+    int marginHeight = (clipping.y + clipping.height) - height;
+    if (marginWidth > 0 || marginHeight > 0) {
+      Color background = getBackground();
+      gc.setBackground(getTerminalBackgroundColor());
+      if (marginWidth > 0) {
+        gc.fillRectangle(width, clipping.y, marginWidth, clipping.height);
+      }
+      if (marginHeight > 0) {
+        gc.fillRectangle(clipping.x, height, clipping.width, marginHeight);
+      }
+      gc.setBackground(background);
+    }
+  }
+
+  protected boolean inClipping(Rectangle clipping, Rectangle r) {
+    // TODO check if this is OK in all cases (the <=!)
+    if (r.x + r.width <= clipping.x) {
+      return false;
+    }
+    if (clipping.x + clipping.width <= r.x) {
+      return false;
+    }
+    if (r.y + r.height <= clipping.y) {
+      return false;
+    }
+    if (clipping.y + clipping.height <= r.y) {
+      return false;
+    }
+    return true;
+  }
+
+  protected Rectangle getScreenRectInVirtualSpace() {
+    return new Rectangle(
+        clientArea.x - virtualBounds.x, clientArea.y - virtualBounds.y, clientArea.width, clientArea.height);
+  }
+
+  protected Rectangle getRectInVirtualSpace(Rectangle r) {
+    return new Rectangle(r.x - virtualBounds.x, r.y - virtualBounds.y, r.width, r.height);
+  }
+
+  protected void setVirtualExtend(int width, int height) {
+    virtualBounds.width = width;
+    virtualBounds.height = height;
+    updateScrollbars();
+    updateViewRectangle();
+  }
+
+  protected void setVirtualOrigin(int x, int y) {
+    if (virtualBounds.x != x || virtualBounds.y != y) {
+      virtualBounds.x = x;
+      virtualBounds.y = y;
+      getHorizontalBar().setSelection(-x);
+      getVerticalBar().setSelection(-y);
+      updateViewRectangle();
+    }
+  }
+
+  protected Rectangle getVirtualBounds() {
+    return cloneRectangle(virtualBounds);
+  }
+
+  protected int virtualXtoScreen(int x) {
+    return x + virtualBounds.x;
+  }
+
+  protected int virtualYtoScreen(int y) {
+    return y + virtualBounds.y;
+  }
+
+  protected int screenXtoVirtual(int x) {
+    return x - virtualBounds.x;
+  }
+
+  protected int screenYtoVirtual(int y) {
+    return y - virtualBounds.y;
+  }
+
+  protected void updateViewRectangle() {
+    if (viewRectangle.x == -virtualBounds.x && viewRectangle.y == -virtualBounds.y
+        && viewRectangle.width == clientArea.width && viewRectangle.height == clientArea.height) {
+      return;
+    }
+    viewRectangle.x = -virtualBounds.x;
+    viewRectangle.y = -virtualBounds.y;
+    viewRectangle.width = clientArea.width;
+    viewRectangle.height = clientArea.height;
+    viewRectangleChanged(viewRectangle.x, viewRectangle.y, viewRectangle.width, viewRectangle.height);
+  }
+
+  protected Rectangle getViewRectangle() {
+    return cloneRectangle(viewRectangle);
+  }
+
+  private Rectangle cloneRectangle(Rectangle r) {
+    return new Rectangle(r.x, r.y, r.width, r.height);
+  }
+
+  protected void viewRectangleChanged(int x, int y, int width, int height) {}
+
+  private void updateScrollbars() {
+    // don't get into infinite loops....
+    if (!inUpdateScrollbars) {
+      inUpdateScrollbars = true;
+      try {
+        doUpdateScrollbar();
+      } finally {
+        inUpdateScrollbars = false;
+      }
+    } else {
+      if (!inUpdateScrollbarsLogged) {
+        inUpdateScrollbarsLogged = true;
+        ILog logger = TerminalPlugin.getDefault().getLog();
+        logger.log(new Status(WARNING, TerminalPlugin.PLUGIN_ID, OK, "Unexpected Recursion in terminal", null));
+      }
+    }
+  }
+
+  private void doUpdateScrollbar() {
+    Rectangle clientArea = getClientArea();
+    ScrollBar horizontal = getHorizontalBar();
+    // Even if setVisible was called on the scrollbar, isVisible returns false if its parent is not visible.
+    if (!isVisible() || horizontal.isVisible()) {
+      horizontal.setPageIncrement(clientArea.width - horizontal.getIncrement());
+      int max = virtualBounds.width;
+      horizontal.setMaximum(max);
+      horizontal.setThumb(clientArea.width);
+    }
+    ScrollBar vertical = getVerticalBar();
+    // even if setVisible was called on the scrollbar, isVisible returns false if its parent is not visible.
+    if (!isVisible() || vertical.isVisible()) {
+      vertical.setPageIncrement(clientArea.height - vertical.getIncrement());
+      int max = virtualBounds.height;
+      vertical.setMaximum(max);
+      vertical.setThumb(clientArea.height);
+    }
+  }
+
+  protected boolean isVertialBarVisible() {
+    return getVerticalBar().isVisible();
+  }
+
+  protected void serVerticalBarVisible(boolean showVScrollBar) {
+    ScrollBar vertical = getVerticalBar();
+    vertical.setVisible(showVScrollBar);
+    vertical.setSelection(0);
+  }
+
+  protected boolean isHorizontalBarVisble() {
+    return getHorizontalBar().isVisible();
+  }
+
+  protected void setHorizontalBarVisible(boolean showHScrollBar) {
+    ScrollBar horizontal = getHorizontalBar();
+    horizontal.setVisible(showHScrollBar);
+    horizontal.setSelection(0);
+  }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/util/BoundedByteBuffer.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/util/BoundedByteBuffer.java
new file mode 100644
index 0000000..6440bd2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/util/BoundedByteBuffer.java
@@ -0,0 +1,138 @@
+package com.google.eclipse.elt.emulator.util;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * A byte bounded buffer used to synchronize the input and the output stream.
+ * <p>
+ * Adapted from {@code BoundedBufferWithStateTracking} http://gee.cs.oswego.edu/dl/cpj/allcode.java
+ * http://gee.cs.oswego.edu/dl/cpj/
+ * <p>
+ * BoundedBufferWithStateTracking is part of the examples for the book Concurrent Programming in Java: Design
+ * Principles and Patterns by Doug Lea (ISBN 0-201-31009-0). Second edition published by Addison-Wesley, November
+ * 1999. The code is Copyright(c) Douglas Lea 1996, 1999 and released to the public domain and may be used for any
+ * purposes whatsoever.
+ * <p>
+ * For some reasons a solution based on PipedOutputStream/PipedIntputStream does work *very* slowly:
+ * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4404700
+ * <p>
+ */
+public class BoundedByteBuffer {
+  private final byte[] buffer; // the elements
+  private int putPosition; // circular indices
+  private int takePosition;
+  private int usedSlots; // the count
+  private boolean closed;
+
+  public BoundedByteBuffer(int capacity) throws IllegalArgumentException {
+    // Make sure we don't deadlock on too small capacity.
+    if (capacity <= 0) {
+      throw new IllegalArgumentException("Capacity should be greater than zero");
+    }
+    buffer = new byte[capacity];
+  }
+
+  /**
+   * Returns the bytes available for {@link #read()}.
+   *
+   * @return the bytes available for reading.
+   */
+  public int size() {
+    return usedSlots;
+  }
+
+  /**
+   * Writes a single byte to the buffer. Blocks if the buffer is full.
+   *
+   * @param b byte to write to the buffer.
+   * @throws InterruptedException when the thread is interrupted while waiting for the buffer to become ready.
+   */
+  public void write(byte b) throws InterruptedException {
+    while (usedSlots == buffer.length) {
+      // Wait until not full.
+      wait();
+    }
+    buffer[putPosition] = b;
+    putPosition = (putPosition + 1) % buffer.length; // cyclically increment
+    if (usedSlots++ == 0) {
+      notifyAll();
+    }
+  }
+
+  public int getFreeSlots() {
+    return buffer.length - usedSlots;
+  }
+
+  public void write(byte[] b, int off, int len) throws InterruptedException {
+    Assert.isTrue(len <= getFreeSlots());
+    while (usedSlots == buffer.length) {
+      // Wait until not full.
+      wait();
+    }
+    int n = Math.min(len, buffer.length - putPosition);
+    System.arraycopy(b, off, buffer, putPosition, n);
+    if (putPosition + len > buffer.length) {
+      System.arraycopy(b, off + n, buffer, 0, len - n);
+    }
+    putPosition = (putPosition + len) % buffer.length; // cyclically increment
+    boolean wasEmpty = usedSlots == 0;
+    usedSlots += len;
+    if (wasEmpty) {
+      notifyAll();
+    }
+  }
+
+  /**
+   * Read a single byte. Blocks until a byte is available.
+   *
+   * @return a byte from the buffer.
+   * @throws InterruptedException when the thread is interrupted while waiting for the buffer to become ready.
+   */
+  public byte read() throws InterruptedException {
+    while (usedSlots == 0) {
+      if (closed) {
+        return -1;
+      }
+      // Wait until not empty.
+      wait();
+    }
+    byte b = buffer[takePosition];
+    takePosition = (takePosition + 1) % buffer.length;
+    if (usedSlots-- == buffer.length) {
+      notifyAll();
+    }
+    return b;
+  }
+
+  public int read(byte[] b, int off, int len) throws InterruptedException {
+    Assert.isTrue(len <= size());
+    while (usedSlots == 0) {
+      if (closed) {
+        return 0;
+      }
+      // Wait until not empty.
+      wait();
+    }
+    int n = Math.min(len, buffer.length - takePosition);
+    System.arraycopy(buffer, takePosition, b, off, n);
+    if (takePosition + len > n) {
+      System.arraycopy(buffer, 0, b, off + n, len - n);
+    }
+    takePosition = (takePosition + len) % buffer.length;
+    boolean wasFull = usedSlots == buffer.length;
+    usedSlots -= len;
+    if (wasFull) {
+      notifyAll();
+    }
+    return len;
+  }
+
+  public void close() {
+    closed = true;
+    notifyAll();
+  }
+
+  public boolean isClosed() {
+    return closed;
+  }
+}
diff --git a/com.google.eclipse.elt.feature/.project b/com.google.eclipse.elt.feature/.project
new file mode 100644
index 0000000..d0fc7d6
--- /dev/null
+++ b/com.google.eclipse.elt.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.eclipse.elt.feature</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.FeatureBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.FeatureNature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.feature/build.properties b/com.google.eclipse.elt.feature/build.properties
new file mode 100644
index 0000000..b73855a
--- /dev/null
+++ b/com.google.eclipse.elt.feature/build.properties
@@ -0,0 +1,7 @@
+bin.includes = feature.xml,\
+               epl-v10.html,\
+               feature.properties
+src.includes = feature.xml,\
+               feature.properties,\
+               build.properties,\
+               epl-v10.html
diff --git a/com.google.eclipse.elt.feature/epl-v10.html b/com.google.eclipse.elt.feature/epl-v10.html
new file mode 100644
index 0000000..5443e16
--- /dev/null
+++ b/com.google.eclipse.elt.feature/epl-v10.html
@@ -0,0 +1,324 @@
+
+<!-- saved from url=(0088)https://workspacemechanic.eclipselabs.org.codespot.com/hg/features/mechanic/epl-v10.html -->
+<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<meta name="ProgId" content="Word.Document">
+<meta name="Generator" content="Microsoft Word 9">
+<meta name="Originator" content="Microsoft Word 9">
+<link rel="File-List" href="https://workspacemechanic.eclipselabs.org.codespot.com/hg/features/mechanic/Eclipse%20EPL%202003_11_10%20Final_files/filelist.xml">
+<title>Eclipse Public License - Version 1.0</title>
+<!--[if gte mso 9]><xml>
+ <o:DocumentProperties>
+  <o:Revision>2</o:Revision>
+  <o:TotalTime>3</o:TotalTime>
+  <o:Created>2004-03-05T23:03:00Z</o:Created>
+  <o:LastSaved>2004-03-05T23:03:00Z</o:LastSaved>
+  <o:Pages>4</o:Pages>
+  <o:Words>1626</o:Words>
+  <o:Characters>9270</o:Characters>
+   <o:Lines>77</o:Lines>
+  <o:Paragraphs>18</o:Paragraphs>
+  <o:CharactersWithSpaces>11384</o:CharactersWithSpaces>
+  <o:Version>9.4402</o:Version>
+ </o:DocumentProperties>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:WordDocument>
+  <w:TrackRevisions/>
+ </w:WordDocument>
+</xml><![endif]-->
+<style>
+<!--
+ /* Font Definitions */
+@font-face
+	{font-family:Tahoma;
+	panose-1:2 11 6 4 3 5 4 4 2 4;
+	mso-font-charset:0;
+	mso-generic-font-family:swiss;
+	mso-font-pitch:variable;
+	mso-font-signature:553679495 -2147483648 8 0 66047 0;}
+ /* Style Definitions */
+p.MsoNormal, li.MsoNormal, div.MsoNormal
+	{mso-style-parent:"";
+	margin:0in;
+	margin-bottom:.0001pt;
+	mso-pagination:widow-orphan;
+	font-size:12.0pt;
+	font-family:"Times New Roman";
+	mso-fareast-font-family:"Times New Roman";}
+p
+	{margin-right:0in;
+	mso-margin-top-alt:auto;
+	mso-margin-bottom-alt:auto;
+	margin-left:0in;
+	mso-pagination:widow-orphan;
+	font-size:12.0pt;
+	font-family:"Times New Roman";
+	mso-fareast-font-family:"Times New Roman";}
+p.BalloonText, li.BalloonText, div.BalloonText
+	{mso-style-name:"Balloon Text";
+	margin:0in;
+	margin-bottom:.0001pt;
+	mso-pagination:widow-orphan;
+	font-size:8.0pt;
+	font-family:Tahoma;
+	mso-fareast-font-family:"Times New Roman";}
+@page Section1
+	{size:8.5in 11.0in;
+	margin:1.0in 1.25in 1.0in 1.25in;
+	mso-header-margin:.5in;
+	mso-footer-margin:.5in;
+	mso-paper-source:0;}
+div.Section1
+	{page:Section1;}
+-->
+</style>
+</head>
+
+<body lang="EN-US" style="tab-interval:.5in">
+
+<div class="Section1">
+
+<p align="center" style="text-align:center"><b>Eclipse Public License - v 1.0</b>
+</p>
+
+<p><span style="font-size:10.0pt">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER
+THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE,
+REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE
+OF THIS AGREEMENT.</span> </p>
+
+<p><b><span style="font-size:10.0pt">1. DEFINITIONS</span></b> </p>
+
+<p><span style="font-size:10.0pt">"Contribution" means:</span> </p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">a)
+in the case of the initial Contributor, the initial code and documentation
+distributed under this Agreement, and<br clear="left">
+b) in the case of each subsequent Contributor:</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">i)
+changes to the Program, and</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">ii)
+additions to the Program;</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">where
+such changes and/or additions to the Program originate from and are distributed
+by that particular Contributor. A Contribution 'originates' from a Contributor
+if it was added to the Program by such Contributor itself or anyone acting on
+such Contributor's behalf. Contributions do not include additions to the
+Program which: (i) are separate modules of software distributed in conjunction
+with the Program under their own license agreement, and (ii) are not derivative
+works of the Program. </span></p>
+
+<p><span style="font-size:10.0pt">"Contributor" means any person or
+entity that distributes the Program.</span> </p>
+
+<p><span style="font-size:10.0pt">"Licensed Patents " mean patent
+claims licensable by a Contributor which are necessarily infringed by the use
+or sale of its Contribution alone or when combined with the Program. </span></p>
+
+<p><span style="font-size:10.0pt">"Program" means the Contributions
+distributed in accordance with this Agreement.</span> </p>
+
+<p><span style="font-size:10.0pt">"Recipient" means anyone who
+receives the Program under this Agreement, including all Contributors.</span> </p>
+
+<p><b><span style="font-size:10.0pt">2. GRANT OF RIGHTS</span></b> </p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">a)
+Subject to the terms of this Agreement, each Contributor hereby grants Recipient
+a non-exclusive, worldwide, royalty-free copyright license to<span style="color:red"> </span>reproduce, prepare derivative works of, publicly
+display, publicly perform, distribute and sublicense the Contribution of such
+Contributor, if any, and such derivative works, in source code and object code
+form.</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">b)
+Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide,<span style="color:green"> </span>royalty-free
+patent license under Licensed Patents to make, use, sell, offer to sell, import
+and otherwise transfer the Contribution of such Contributor, if any, in source
+code and object code form. This patent license shall apply to the combination
+of the Contribution and the Program if, at the time the Contribution is added
+by the Contributor, such addition of the Contribution causes such combination
+to be covered by the Licensed Patents. The patent license shall not apply to
+any other combinations which include the Contribution. No hardware per se is
+licensed hereunder. </span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">c)
+Recipient understands that although each Contributor grants the licenses to its
+Contributions set forth herein, no assurances are provided by any Contributor
+that the Program does not infringe the patent or other intellectual property
+rights of any other entity. Each Contributor disclaims any liability to Recipient
+for claims brought by any other entity based on infringement of intellectual
+property rights or otherwise. As a condition to exercising the rights and
+licenses granted hereunder, each Recipient hereby assumes sole responsibility
+to secure any other intellectual property rights needed, if any. For example,
+if a third party patent license is required to allow Recipient to distribute
+the Program, it is Recipient's responsibility to acquire that license before
+distributing the Program.</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">d)
+Each Contributor represents that to its knowledge it has sufficient copyright
+rights in its Contribution, if any, to grant the copyright license set forth in
+this Agreement. </span></p>
+
+<p><b><span style="font-size:10.0pt">3. REQUIREMENTS</span></b> </p>
+
+<p><span style="font-size:10.0pt">A Contributor may choose to distribute the
+Program in object code form under its own license agreement, provided that:</span>
+</p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">a)
+it complies with the terms and conditions of this Agreement; and</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">b)
+its license agreement:</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">i)
+effectively disclaims on behalf of all Contributors all warranties and
+conditions, express and implied, including warranties or conditions of title
+and non-infringement, and implied warranties or conditions of merchantability
+and fitness for a particular purpose; </span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">ii)
+effectively excludes on behalf of all Contributors all liability for damages,
+including direct, indirect, special, incidental and consequential damages, such
+as lost profits; </span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">iii)
+states that any provisions which differ from this Agreement are offered by that
+Contributor alone and not by any other party; and</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">iv)
+states that source code for the Program is available from such Contributor, and
+informs licensees how to obtain it in a reasonable manner on or through a
+medium customarily used for software exchange.<span style="color:blue"> </span></span></p>
+
+<p><span style="font-size:10.0pt">When the Program is made available in source
+code form:</span> </p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">a)
+it must be made available under this Agreement; and </span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">b) a
+copy of this Agreement must be included with each copy of the Program. </span></p>
+
+<p><span style="font-size:10.0pt">Contributors may not remove or alter any
+copyright notices contained within the Program. </span></p>
+
+<p><span style="font-size:10.0pt">Each Contributor must identify itself as the
+originator of its Contribution, if any, in a manner that reasonably allows
+subsequent Recipients to identify the originator of the Contribution. </span></p>
+
+<p><b><span style="font-size:10.0pt">4. COMMERCIAL DISTRIBUTION</span></b> </p>
+
+<p><span style="font-size:10.0pt">Commercial distributors of software may
+accept certain responsibilities with respect to end users, business partners
+and the like. While this license is intended to facilitate the commercial use
+of the Program, the Contributor who includes the Program in a commercial
+product offering should do so in a manner which does not create potential
+liability for other Contributors. Therefore, if a Contributor includes the
+Program in a commercial product offering, such Contributor ("Commercial
+Contributor") hereby agrees to defend and indemnify every other
+Contributor ("Indemnified Contributor") against any losses, damages and
+costs (collectively "Losses") arising from claims, lawsuits and other
+legal actions brought by a third party against the Indemnified Contributor to
+the extent caused by the acts or omissions of such Commercial Contributor in
+connection with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In order
+to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial Contributor
+to control, and cooperate with the Commercial Contributor in, the defense and
+any related settlement negotiations. The Indemnified Contributor may participate
+in any such claim at its own expense.</span> </p>
+
+<p><span style="font-size:10.0pt">For example, a Contributor might include the
+Program in a commercial product offering, Product X. That Contributor is then a
+Commercial Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance claims and
+warranties are such Commercial Contributor's responsibility alone. Under this
+section, the Commercial Contributor would have to defend claims against the
+other Contributors related to those performance claims and warranties, and if a
+court requires any other Contributor to pay any damages as a result, the
+Commercial Contributor must pay those damages.</span> </p>
+
+<p><b><span style="font-size:10.0pt">5. NO WARRANTY</span></b> </p>
+
+<p><span style="font-size:10.0pt">EXCEPT AS EXPRESSLY SET FORTH IN THIS
+AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING,
+WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and distributing the
+Program and assumes all risks associated with its exercise of rights under this
+Agreement , including but not limited to the risks and costs of program errors,
+compliance with applicable laws, damage to or loss of data, programs or
+equipment, and unavailability or interruption of operations. </span></p>
+
+<p><b><span style="font-size:10.0pt">6. DISCLAIMER OF LIABILITY</span></b> </p>
+
+<p><span style="font-size:10.0pt">EXCEPT AS EXPRESSLY SET FORTH IN THIS
+AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF
+THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGES.</span> </p>
+
+<p><b><span style="font-size:10.0pt">7. GENERAL</span></b> </p>
+
+<p><span style="font-size:10.0pt">If any provision of this Agreement is invalid
+or unenforceable under applicable law, it shall not affect the validity or
+enforceability of the remainder of the terms of this Agreement, and without
+further action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.</span> </p>
+
+<p><span style="font-size:10.0pt">If Recipient institutes patent litigation
+against any entity (including a cross-claim or counterclaim in a lawsuit)
+alleging that the Program itself (excluding combinations of the Program with
+other software or hardware) infringes such Recipient's patent(s), then such
+Recipient's rights granted under Section 2(b) shall terminate as of the date
+such litigation is filed. </span></p>
+
+<p><span style="font-size:10.0pt">All Recipient's rights under this Agreement
+shall terminate if it fails to comply with any of the material terms or
+conditions of this Agreement and does not cure such failure in a reasonable
+period of time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use and
+distribution of the Program as soon as reasonably practicable. However,
+Recipient's obligations under this Agreement and any licenses granted by
+Recipient relating to the Program shall continue and survive. </span></p>
+
+<p><span style="font-size:10.0pt">Everyone is permitted to copy and distribute
+copies of this Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The Agreement
+Steward reserves the right to publish new versions (including revisions) of
+this Agreement from time to time. No one other than the Agreement Steward has
+the right to modify this Agreement. The Eclipse Foundation is the initial
+Agreement Steward. The Eclipse Foundation may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each new version
+of the Agreement will be given a distinguishing version number. The Program
+(including Contributions) may always be distributed subject to the version of
+the Agreement under which it was received. In addition, after a new version of
+the Agreement is published, Contributor may elect to distribute the Program
+(including its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to
+the intellectual property of any Contributor under this Agreement, whether
+expressly, by implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.</span> </p>
+
+<p><span style="font-size:10.0pt">This Agreement is governed by the laws of the
+State of New York and the intellectual property laws of the United States of
+America. No party to this Agreement will bring a legal action under this
+Agreement more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.</span> </p>
+
+<p class="MsoNormal"><!--[if !supportEmptyParas]-->&nbsp;<!--[endif]--><o:p></o:p></p>
+
+</div>
+
+
+
+</body></html>
\ No newline at end of file
diff --git a/com.google.eclipse.elt.feature/feature.properties b/com.google.eclipse.elt.feature/feature.properties
new file mode 100644
index 0000000..7c9a8e4
--- /dev/null
+++ b/com.google.eclipse.elt.feature/feature.properties
@@ -0,0 +1,169 @@
+###############################################################################
+# Copyright (c) 2012 Google Inc
+#
+# 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:
+#     Alex Ruiz - initial API and implementation
+###############################################################################
+# features.properties
+# contains externalized strings for feature.xml
+# "%foo" in feature.xml corresponds to the key "foo" in this file
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# This file should be translated.
+
+# "featureName" property - name of the feature
+featureName=Terminal
+
+# "providerName" property - name of the company that provides the feature
+providerName=Google, Inc.
+
+# "updateSiteName" property - label for the update site
+updateSiteName=elt
+
+# "description" property - description of the feature
+description=Command-line terminal for Eclipse
+
+# copyright
+copyright=\
+Copyright (c) 2012 Google, Inc.\n\   
+All rights reserved. This program and the accompanying materials\n\
+are made available under the terms of the Eclipse Public License v1.0\n\
+which accompanies this distribution, and is available at\n\
+http://www.eclipse.org/legal/epl-v10.html
+
+# "licenseURL" property - URL of the "Feature License"
+# do not translate value - just change to point to a locale-specific HTML page
+licenseURL=epl-v10.html
+
+# "license" property - text of the "Feature Update License"
+# should be plain text version of license agreement pointed to be "licenseURL"
+license=\
+Eclipse Foundation Software User Agreement\n\
+February 1, 2011\n\
+\n\
+Usage Of Content\n\
+\n\
+THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR\n\
+OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT").\n\
+USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS\n\
+AGREEMENT AND/OR THE TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR\n\
+NOTICES INDICATED OR REFERENCED BELOW.  BY USING THE CONTENT, YOU\n\
+AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED BY THIS AGREEMENT\n\
+AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS\n\
+OR NOTICES INDICATED OR REFERENCED BELOW.  IF YOU DO NOT AGREE TO THE\n\
+TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS\n\
+OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
+BELOW, THEN YOU MAY NOT USE THE CONTENT.\n\
+\n\
+Applicable Licenses\n\
+\n\
+Unless otherwise indicated, all Content made available by the\n\
+Eclipse Foundation is provided to you under the terms and conditions of\n\
+the Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is\n\
+provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html.\n\
+For purposes of the EPL, "Program" will mean the Content.\n\
+\n\
+Content includes, but is not limited to, source code, object code,\n\
+documentation and other files maintained in the Eclipse Foundation source code\n\
+repository ("Repository") in software modules ("Modules") and made available\n\
+as downloadable archives ("Downloads").\n\
+\n\
+       - Content may be structured and packaged into modules to facilitate delivering,\n\
+         extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"),\n\
+         plug-in fragments ("Fragments"), and features ("Features").\n\
+       - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java(TM) ARchive)\n\
+         in a directory named "plugins".\n\
+       - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.\n\
+         Each Feature may be packaged as a sub-directory in a directory named "features".\n\
+         Within a Feature, files named "feature.xml" may contain a list of the names and version\n\
+         numbers of the Plug-ins and/or Fragments associated with that Feature.\n\
+       - Features may also include other Features ("Included Features"). Within a Feature, files\n\
+         named "feature.xml" may contain a list of the names and version numbers of Included Features.\n\
+\n\
+The terms and conditions governing Plug-ins and Fragments should be\n\
+contained in files named "about.html" ("Abouts"). The terms and\n\
+conditions governing Features and Included Features should be contained\n\
+in files named "license.html" ("Feature Licenses"). Abouts and Feature\n\
+Licenses may be located in any directory of a Download or Module\n\
+including, but not limited to the following locations:\n\
+\n\
+       - The top-level (root) directory\n\
+       - Plug-in and Fragment directories\n\
+       - Inside Plug-ins and Fragments packaged as JARs\n\
+       - Sub-directories of the directory named "src" of certain Plug-ins\n\
+       - Feature directories\n\
+\n\
+Note: if a Feature made available by the Eclipse Foundation is installed using the\n\
+Provisioning Technology (as defined below), you must agree to a license ("Feature \n\
+Update License") during the installation process. If the Feature contains\n\
+Included Features, the Feature Update License should either provide you\n\
+with the terms and conditions governing the Included Features or inform\n\
+you where you can locate them. Feature Update Licenses may be found in\n\
+the "license" property of files named "feature.properties" found within a Feature.\n\
+Such Abouts, Feature Licenses, and Feature Update Licenses contain the\n\
+terms and conditions (or references to such terms and conditions) that\n\
+govern your use of the associated Content in that directory.\n\
+\n\
+THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER\n\
+TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.\n\
+SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
+\n\
+       - Eclipse Distribution License Version 1.0 (available at http://www.eclipse.org/licenses/edl-v1.0.html)\n\
+       - Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)\n\
+       - Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)\n\
+       - Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)\n\
+       - Metro Link Public License 1.00 (available at http://www.opengroup.org/openmotif/supporters/metrolink/license.html)\n\
+       - Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)\n\
+\n\
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR\n\
+TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License\n\
+is provided, please contact the Eclipse Foundation to determine what terms and conditions\n\
+govern that particular Content.\n\
+\n\
+\n\Use of Provisioning Technology\n\
+\n\
+The Eclipse Foundation makes available provisioning software, examples of which include,\n\
+but are not limited to, p2 and the Eclipse Update Manager ("Provisioning Technology") for\n\
+the purpose of allowing users to install software, documentation, information and/or\n\
+other materials (collectively "Installable Software"). This capability is provided with\n\
+the intent of allowing such users to install, extend and update Eclipse-based products.\n\
+Information about packaging Installable Software is available at\n\
+http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\
+\n\
+You may use Provisioning Technology to allow other parties to install Installable Software.\n\
+You shall be responsible for enabling the applicable license agreements relating to the\n\
+Installable Software to be presented to, and accepted by, the users of the Provisioning Technology\n\
+in accordance with the Specification. By using Provisioning Technology in such a manner and\n\
+making it available in accordance with the Specification, you further acknowledge your\n\
+agreement to, and the acquisition of all necessary rights to permit the following:\n\
+\n\
+       1. A series of actions may occur ("Provisioning Process") in which a user may execute\n\
+          the Provisioning Technology on a machine ("Target Machine") with the intent of installing,\n\
+          extending or updating the functionality of an Eclipse-based product.\n\
+       2. During the Provisioning Process, the Provisioning Technology may cause third party\n\
+          Installable Software or a portion thereof to be accessed and copied to the Target Machine.\n\
+       3. Pursuant to the Specification, you will provide to the user the terms and conditions that\n\
+          govern the use of the Installable Software ("Installable Software Agreement") and such\n\
+          Installable Software Agreement shall be accessed from the Target Machine in accordance\n\
+          with the Specification. Such Installable Software Agreement must inform the user of the\n\
+          terms and conditions that govern the Installable Software and must solicit acceptance by\n\
+          the end user in the manner prescribed in such Installable Software Agreement. Upon such\n\
+          indication of agreement by the user, the provisioning Technology will complete installation\n\
+          of the Installable Software.\n\
+\n\
+Cryptography\n\
+\n\
+Content may contain encryption software. The country in which you are\n\
+currently may have restrictions on the import, possession, and use,\n\
+and/or re-export to another country, of encryption software. BEFORE\n\
+using any encryption software, please check the country's laws,\n\
+regulations and policies concerning the import, possession, or use, and\n\
+re-export of encryption software, to see if this is permitted.\n\
+\n\
+Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.\n
+########### end of license property ##########################################
diff --git a/com.google.eclipse.elt.feature/feature.xml b/com.google.eclipse.elt.feature/feature.xml
new file mode 100644
index 0000000..969eac7
--- /dev/null
+++ b/com.google.eclipse.elt.feature/feature.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="com.google.eclipse.elt"
+      label="%featureName"
+      version="1.1.0.qualifier"
+      provider-name="%providerName">
+
+   <description url="https://code.google.com/p/elt/">
+      %description
+   </description>
+
+   <copyright>
+      %description
+   </copyright>
+
+   <license url="%licenseURL">
+      %license
+   </license>
+
+   <url>
+      <update label="%updateSiteName" url="http://elt.googlecode.com/git/update-site"/>
+   </url>
+
+   <requires>
+      <import plugin="org.eclipse.core.runtime" version="3.7.0" match="compatible"/>
+      <import plugin="org.eclipse.ui"/>
+      <import plugin="org.eclipse.jface.text"/>
+      <import plugin="org.eclipse.core.resources"/>
+      <import plugin="com.ibm.icu" version="4.4.2" match="greaterOrEqual"/>
+      <import plugin="org.eclipse.debug.core"/>
+      <import plugin="com.google.eclipse.elt.pty" version="1.1.0" match="greaterOrEqual"/>
+      <import plugin="com.google.eclipse.elt.emulator" version="1.1.0" match="greaterOrEqual"/>
+   </requires>
+
+   <plugin
+         id="com.google.eclipse.elt.emulator"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="com.google.eclipse.elt.macosx"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         fragment="true"/>
+
+   <plugin
+         id="com.google.eclipse.elt.pty"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="com.google.eclipse.elt.pty.linux"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         fragment="true"/>
+
+   <plugin
+         id="com.google.eclipse.elt.pty.linux.x86"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         fragment="true"/>
+
+   <plugin
+         id="com.google.eclipse.elt.pty.linux.x86_64"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         fragment="true"/>
+
+   <plugin
+         id="com.google.eclipse.elt.view"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>
diff --git a/com.google.eclipse.elt.pty.linux.x86/.project b/com.google.eclipse.elt.pty.linux.x86/.project
new file mode 100644
index 0000000..6b9e649
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/.project
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.eclipse.elt.pty.linux.x86</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty.linux.x86/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty.linux.x86/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..79238ec
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %fragmentName.linux.x86
+Bundle-SymbolicName: com.google.eclipse.elt.pty.linux.x86;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Vendor: %providerName
+Fragment-Host: com.google.eclipse.elt.pty;bundle-version="1.1.0"
+Bundle-Localization: plugin
+Eclipse-PlatformFilter: (&(osgi.os=linux)(osgi.arch=x86))
diff --git a/com.google.eclipse.elt.pty.linux.x86/build.properties b/com.google.eclipse.elt.pty.linux.x86/build.properties
new file mode 100644
index 0000000..e16c8a6
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/build.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2005, 2009 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
+###############################################################################
+bin.includes = os/,\
+               META-INF/
diff --git a/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgpty.so b/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgpty.so
new file mode 100644
index 0000000..60088bc
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgpty.so
Binary files differ
diff --git a/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgspawner.so b/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgspawner.so
new file mode 100644
index 0000000..b6fb632
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgspawner.so
Binary files differ
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/.project b/com.google.eclipse.elt.pty.linux.x86_64/.project
new file mode 100644
index 0000000..12c9497
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/.project
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.eclipse.elt.pty.linux.x86_64</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty.linux.x86_64/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5d6b87b
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %fragmentName.linux.x86_64
+Bundle-SymbolicName: com.google.eclipse.elt.pty.linux.x86_64;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Vendor: %providerName
+Fragment-Host: com.google.eclipse.elt.pty;bundle-version="1.1.0"
+Bundle-Localization: plugin
+Eclipse-PlatformFilter: (&(osgi.os=linux)(osgi.arch=x86_64))
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/build.properties b/com.google.eclipse.elt.pty.linux.x86_64/build.properties
new file mode 100644
index 0000000..9faf19a
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/build.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2005, 2009 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
+###############################################################################
+bin.includes = os/,\
+               about.html,\
+               META-INF/
+src.includes = about.html
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgpty.so b/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgpty.so
new file mode 100644
index 0000000..587fa69
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgpty.so
Binary files differ
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgspawner.so b/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgspawner.so
new file mode 100644
index 0000000..18736e1
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgspawner.so
Binary files differ
diff --git a/com.google.eclipse.elt.pty.linux/.classpath b/com.google.eclipse.elt.pty.linux/.classpath
new file mode 100644
index 0000000..7d9b38e
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.pty.linux/.gitignore b/com.google.eclipse.elt.pty.linux/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/com.google.eclipse.elt.pty.linux/.project b/com.google.eclipse.elt.pty.linux/.project
new file mode 100644
index 0000000..2ca6207
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.eclipse.elt.pty.linux</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty.linux/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.pty.linux/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c537b63
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/com.google.eclipse.elt.pty.linux/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty.linux/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..add2061
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %fragmentName.linux
+Bundle-SymbolicName: com.google.eclipse.elt.pty.linux;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-ClassPath: cdt_linux.jar
+Bundle-Vendor: %providerName
+Fragment-Host: com.google.eclipse.elt.pty;bundle-version="1.1.0"
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Eclipse-PlatformFilter: (osgi.os=linux)
diff --git a/com.google.eclipse.elt.pty.linux/build.properties b/com.google.eclipse.elt.pty.linux/build.properties
new file mode 100644
index 0000000..6542e91
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2005, 2006 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
+###############################################################################
+bin.includes = fragment.xml,\
+               about.html,\
+               .,\
+               META-INF/
+src.includes = about.html,\
+               library/
+source.. = src/
diff --git a/com.google.eclipse.elt.pty.linux/fragment.xml b/com.google.eclipse.elt.pty.linux/fragment.xml
new file mode 100644
index 0000000..aedc72a
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/fragment.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<fragment>
+
+</fragment>
diff --git a/com.google.eclipse.elt.pty.linux/library/Makefile b/com.google.eclipse.elt.pty.linux/library/Makefile
new file mode 100644
index 0000000..6e67977
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/Makefile
@@ -0,0 +1,44 @@
+# makefile for libspawner.so
+
+ifeq ($(JAVA_HOME),)
+$(warning JAVA_HOME not set in environment)
+endif
+
+# Defaults which can be overridden.
+OS = linux
+ARCH = x86
+
+JDK_INCLUDES= $(JAVA_HOME)/include
+JDK_OS_INCLUDES= $(JAVA_HOME)/include/$(OS)
+
+CC=gcc
+CPPFLAGS = -I. -I$(JDK_INCLUDES) -I$(JDK_OS_INCLUDES)
+CFLAGS +=-fpic -D_REENTRANT -D_GNU_SOURCE
+
+INSTALL_DIR = ../../org.eclipse.cdt.core.linux.$(ARCH)/os/$(OS)/$(ARCH)
+
+LIB_NAME_SPAWNER = libspawner.so
+LIB_NAME_FULL_SPAWNER = $(INSTALL_DIR)/libspawner.so
+OBJS_SPAWNER=spawner.o io.o exec_unix.o exec_pty.o pfind.o openpty.o
+
+LIB_NAME_PTY = libpty.so
+LIB_NAME_FULL_PTY = $(INSTALL_DIR)/libpty.so
+OBJS_PTY= openpty.o pty.o ptyio.o
+
+OBJS = $(OBJS_SPAWNER) $(OBJS_PTY)
+
+all: $(LIB_NAME_FULL_SPAWNER) $(LIB_NAME_FULL_PTY)
+
+rebuild: clean all
+
+$(LIB_NAME_FULL_SPAWNER) : $(OBJS_SPAWNER)
+	mkdir -p $(INSTALL_DIR)
+	$(CC) -g -shared -Wl,-soname,$(LIB_NAME_SPAWNER) $(LDFLAGS) -o $(LIB_NAME_FULL_SPAWNER) $(OBJS_SPAWNER) -lc
+
+$(LIB_NAME_FULL_PTY): $(OBJS_PTY)
+	mkdir -p $(INSTALL_DIR)
+	$(CC) -g -shared -Wl,-soname,$(LIB_NAME_PTY) $(LDFLAGS) -o $(LIB_NAME_FULL_PTY) $(OBJS_PTY)
+
+clean :
+	$(RM) $(OBJS_SPAWNER) $(LIB_NAME_FULL_SPAWNER)
+	$(RM) $(OBJS_PTY) $(LIB_NAME_FULL_PTY)
diff --git a/com.google.eclipse.elt.pty.linux/library/PTY.h b/com.google.eclipse.elt.pty.linux/library/PTY.h
new file mode 100644
index 0000000..f717942
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/PTY.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTY */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTY
+#define _Included_org_eclipse_cdt_utils_pty_PTY
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTY
+ * Method:    openMaster
+ * Signature: (Z)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_eclipse_cdt_utils_pty_PTY_openMaster
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTY
+ * Method:    change_window_size
+ * Signature: (III)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size
+  (JNIEnv *, jobject, jint, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/PTYInputStream.h b/com.google.eclipse.elt.pty.linux/library/PTYInputStream.h
new file mode 100644
index 0000000..e734927
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/PTYInputStream.h
@@ -0,0 +1,32 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTYInputStream */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTYInputStream
+#define _Included_org_eclipse_cdt_utils_pty_PTYInputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_eclipse_cdt_utils_pty_PTYInputStream_SKIP_BUFFER_SIZE
+#define org_eclipse_cdt_utils_pty_PTYInputStream_SKIP_BUFFER_SIZE 2048L
+/* Inaccessible static: skipBuffer */
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method:    read0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0
+  (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0
+  (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/PTYOutputStream.h b/com.google.eclipse.elt.pty.linux/library/PTYOutputStream.h
new file mode 100644
index 0000000..fb28491
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/PTYOutputStream.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTYOutputStream */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTYOutputStream
+#define _Included_org_eclipse_cdt_utils_pty_PTYOutputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method:    write0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0
+  (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0
+  (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/Spawner.h b/com.google.eclipse.elt.pty.linux/library/Spawner.h
new file mode 100644
index 0000000..02727f9
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/Spawner.h
@@ -0,0 +1,53 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_spawner_Spawner */
+
+#ifndef _Included_org_eclipse_cdt_utils_spawner_Spawner
+#define _Included_org_eclipse_cdt_utils_spawner_Spawner
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    exec0
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0
+  (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    exec1
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1
+  (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    exec2
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILjava/lang/String;IZ)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2
+  (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray, jstring, jint, jboolean);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    raise
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise
+  (JNIEnv *, jobject, jint, jint);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    waitFor
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor
+  (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/SpawnerInputStream.h b/com.google.eclipse.elt.pty.linux/library/SpawnerInputStream.h
new file mode 100644
index 0000000..3b32d2b
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/SpawnerInputStream.h
@@ -0,0 +1,32 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */

+#include <jni.h>

+/* Header for class com_qnx_tools_utils_spawner_SpawnerInputStream */

+

+#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerInputStream

+#define _Included_com_qnx_tools_utils_spawner_SpawnerInputStream

+#ifdef __cplusplus

+extern "C" {

+#endif

+#undef com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE

+#define com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE 2048L

+/* Inaccessible static: skipBuffer */

+/*

+ * Class:     org_elipse_cdt_utils_spawner_SpawnerInputStream

+ * Method:    read0

+ * Signature: (I[BI)I

+ */

+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0

+  (JNIEnv *, jobject, jint, jbyteArray, jint);

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerInputStream

+ * Method:    close0

+ * Signature: (I)I

+ */

+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0

+  (JNIEnv *, jobject, jint);

+

+#ifdef __cplusplus

+}

+#endif

+#endif

diff --git a/com.google.eclipse.elt.pty.linux/library/SpawnerOutputStream.h b/com.google.eclipse.elt.pty.linux/library/SpawnerOutputStream.h
new file mode 100644
index 0000000..f835947
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/SpawnerOutputStream.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */

+#include <jni.h>

+/* Header for class com_qnx_tools_utils_spawner_SpawnerOutputStream */

+

+#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream

+#define _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream

+#ifdef __cplusplus

+extern "C" {

+#endif

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerOutputStream

+ * Method:    write0

+ * Signature: (I[BI)I

+ */

+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0

+  (JNIEnv *, jobject, jint, jbyteArray, jint);

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerOutputStream

+ * Method:    close0

+ * Signature: (I)I

+ */

+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0

+  (JNIEnv *, jobject, jint);

+

+#ifdef __cplusplus

+}

+#endif

+#endif

diff --git a/com.google.eclipse.elt.pty.linux/library/exec0.h b/com.google.eclipse.elt.pty.linux/library/exec0.h
new file mode 100644
index 0000000..3033ec1
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/exec0.h
@@ -0,0 +1,29 @@
+/*******************************************************************************

+ * 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.

+ *     Mikhail Zabaluev (Nokia) - bug 82744

+ *******************************************************************************/

+#include <unistd.h>

+#include <sys/wait.h>

+#include <sys/types.h>

+#include <signal.h>

+#include <errno.h>

+

+extern pid_t exec0(const char *path, char *const argv[],

+                   char *const envp[], const char *dirpath,

+                   int channels[3]);

+

+

+extern pid_t exec_pty(const char *path, char *const argv[],

+                      char *const envp[], const char *dirpath,

+                      int channels[3], const char *pts_name, int fdm,

+                      int console);

+

+extern int wait0(pid_t pid);

diff --git a/com.google.eclipse.elt.pty.linux/library/exec_pty.c b/com.google.eclipse.elt.pty.linux/library/exec_pty.c
new file mode 100644
index 0000000..dd68615
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/exec_pty.c
@@ -0,0 +1,186 @@
+/*******************************************************************************

+ * Copyright (c) 2004, 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.  

+ *     Mikhail Zabaluev (Nokia) - bug 82744

+ *     Mikhail Sennikovsky - bug 145737

+ *******************************************************************************/

+#include "exec0.h"

+#include "openpty.h"

+#include <unistd.h>

+#include <stdlib.h>

+#include <stdio.h>

+#include <errno.h>

+#include <string.h>

+#include <libgen.h>

+#include <stdlib.h>

+#include <termios.h>

+

+/* from pfind.c */

+extern char *pfind(const char *name, char * const envp[]);

+

+pid_t

+exec_pty(const char *path, char *const argv[], char *const envp[],

+      const char *dirpath, int channels[3], const char *pts_name, int fdm, int console)

+{

+	int pipe2[2];

+	pid_t childpid;

+	char *full_path;

+

+	/*

+	 * We use pfind() to check that the program exists and is an executable.

+	 * If not pass the error up.  Also execve() wants a full path.

+	 */ 

+	full_path = pfind(path, envp);

+	if (full_path == NULL) {

+		fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : "");

+		return -1;

+	}

+

+	/*

+	 *  Make sure we can create our pipes before forking.

+	 */ 

+	if (channels != NULL && console) {

+		if (pipe(pipe2) < 0) { 

+			fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));

+			free(full_path);

+			return -1;

+		}

+	}

+

+	childpid = fork();

+

+	if (childpid < 0) {

+		fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));

+		free(full_path);

+		return -1;

+	} else if (childpid == 0) { /* child */

+

+		chdir(dirpath);

+

+		if (channels != NULL) {

+			int fds;

+

+			if (!console && setsid() < 0) {

+				perror("setsid()");

+				return -1;

+			}

+

+			fds = ptys_open(fdm, pts_name);

+			if (fds < 0) {

+				fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));

+				return -1;

+			}

+

+			/* Close the read end of pipe2 */

+			if (console && close(pipe2[0]) == -1) {

+				perror("close(pipe2[0]))");

+			}

+

+			/* close the master, no need in the child */

+			close(fdm);

+

+			if (console) {

+				set_noecho(fds);

+				if (setpgid(getpid(), getpid()) < 0) {

+					perror("setpgid()");

+					return -1;

+				}

+			}

+

+			/* redirections */

+			dup2(fds, STDIN_FILENO);   /* dup stdin */

+			dup2(fds, STDOUT_FILENO);  /* dup stdout */

+			if (console) {

+				dup2(pipe2[1], STDERR_FILENO);  /* dup stderr */

+			} else {

+				dup2(fds, STDERR_FILENO);  /* dup stderr */

+			}

+			close(fds);  /* done with fds. */

+		}

+

+		/* Close all the fd's in the child */

+		{

+			int fdlimit = sysconf(_SC_OPEN_MAX);

+			int fd = 3;

+

+			while (fd < fdlimit)

+				close(fd++);

+		}

+

+		if (envp[0] == NULL) {

+			execv(full_path, argv);

+		} else {

+			execve(full_path, argv, envp);

+		}

+

+		_exit(127);

+

+	} else if (childpid != 0) { /* parent */

+		if (console) {

+			set_noecho(fdm);

+		}

+		if (channels != NULL) {

+			channels[0] = fdm; /* Input Stream. */

+			channels[1] = fdm; /* Output Stream.  */

+			if (console) {

+				/* close the write end of pipe1 */

+				if (close(pipe2[1]) == -1)

+					perror("close(pipe2[1])");

+				channels[2] = pipe2[0]; /* stderr Stream.  */

+			} else {

+				channels[2] = fdm; /* Error Stream.  */

+			}

+		}

+

+		free(full_path);

+		return childpid;

+	}

+

+	free(full_path);

+	return -1;                  /*NOT REACHED */

+}

+#ifdef __STAND_ALONE__

+int main(int argc, char **argv, char **envp) {

+	const char *path = "./bufferring_test";

+	int channels[3] = { -1, -1, -1};

+	int status;	

+	FILE *app_stdin;

+	FILE *app_stdout;

+	FILE *app_stderr;

+	char pts_name[32];

+	int fdm;	

+	char buffer[32];

+

+	fdm = ptym_open(pts_name);

+	status =  exec_pty(path, argv, envp, ".", channels, pts_name, fdm);

+	if (status >= 0) {

+		app_stdin = fdopen(channels[0], "w");	

+		app_stdout = fdopen(channels[1], "r");	

+		app_stderr = fdopen(channels[2], "r");	

+		if (app_stdout == NULL || app_stderr == NULL || app_stdin == NULL) {

+			fprintf(stderr, "PROBLEMS\n");

+		} else {

+			fputs("foo\n", app_stdin);

+			fputs("bar\n", app_stdin);

+			while(fgets(buffer, sizeof buffer, app_stdout) != NULL) {

+				fprintf(stdout, "STDOUT: %s\n", buffer);

+			}

+			while(fgets(buffer, sizeof buffer, app_stderr) != NULL) {

+				fprintf(stdout, "STDERR: %s\n", buffer);

+			}

+		}

+	}

+	fputs("bye\n", stdout);

+	close(channels[0]);

+	close(channels[1]);

+	close(channels[2]);

+	return 0;	

+}

+#endif

diff --git a/com.google.eclipse.elt.pty.linux/library/exec_unix.c b/com.google.eclipse.elt.pty.linux/library/exec_unix.c
new file mode 100644
index 0000000..ced7b74
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/exec_unix.c
@@ -0,0 +1,158 @@
+/*******************************************************************************

+ * 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.

+ *     Mikhail Sennikovsky - bug 145737

+ *******************************************************************************/

+#include "exec0.h"

+#include <unistd.h>

+#include <stdlib.h>

+#include <stdio.h>

+#include <errno.h>

+#include <string.h>

+#include <libgen.h>

+#include <stdlib.h>

+

+/* from pfind.c */

+extern char *pfind(const char *name, char * const envp[]);

+

+pid_t

+exec0(const char *path, char *const argv[], char *const envp[],

+      const char *dirpath, int channels[3])

+{

+	int pipe0[2], pipe1[2], pipe2[2];

+	pid_t childpid;

+	char *full_path;

+

+	/*

+	 * We use pfind() to check that the program exists and is an executable.

+	 * If not pass the error up.  Also execve() wants a full path.

+	 */ 

+	full_path = pfind(path, envp);

+	if (full_path == NULL) {

+		fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : "");

+		return -1;

+	}

+

+	/*

+	 *  Make sure we can create our pipes before forking.

+	 */ 

+	if (channels != NULL) {

+		if (pipe(pipe0) < 0 || pipe(pipe1) < 0 || pipe(pipe2) < 0) {

+			fprintf(stderr, "%s(%d): returning due to error.\n",

+				__FUNCTION__, __LINE__);

+			free(full_path);

+			return -1;

+		}

+	}

+

+	childpid = fork();

+

+	if (childpid < 0) {

+		fprintf(stderr, "%s(%d): returning due to error: %s\n",

+			__FUNCTION__, __LINE__, strerror(errno));

+		free(full_path);

+		return -1;

+	} else if (childpid == 0) { /* child */

+		char *ptr;

+

+		chdir(dirpath);

+

+		if (channels != NULL) {

+			/* Close the write end of pipe0 */

+			if (close(pipe0[1]) == -1)

+				perror("close(pipe0[1])");

+

+			/* Close the read end of pipe1 */

+			if (close(pipe1[0]) == -1)

+				perror("close(pipe1[0])");

+

+			/* Close the read end of pipe2 */

+			if (close(pipe2[0]) == -1)

+				perror("close(pipe2[0]))");

+

+			/* redirections */

+			dup2(pipe0[0], STDIN_FILENO);   /* dup stdin */

+			dup2(pipe1[1], STDOUT_FILENO);  /* dup stdout */

+			dup2(pipe2[1], STDERR_FILENO);  /* dup stderr */

+		}

+

+		/* Close all the fd's in the child */

+		{

+			int fdlimit = sysconf(_SC_OPEN_MAX);

+			int fd = 3;

+

+			while (fd < fdlimit)

+				close(fd++);

+		}

+

+		setpgid(getpid(), getpid());

+

+		if (envp[0] == NULL) {

+			execv(full_path, argv);

+		} else {

+			execve(full_path, argv, envp);

+		}

+

+		_exit(127);

+

+	} else if (childpid != 0) { /* parent */

+

+		char b;

+

+		if (channels != NULL) {

+			/* close the read end of pipe1 */

+			if (close(pipe0[0]) == -1)

+				perror("close(pipe0[0])");

+ 

+			/* close the write end of pipe2 */

+			if (close(pipe1[1]) == -1) 

+				perror("close(pipe1[1])");

+

+			/* close the write end of pipe2 */

+			if (close(pipe2[1]) == -1) 

+				perror("close(pipe2[1])");

+

+			channels[0] = pipe0[1]; /* Output Stream. */

+			channels[1] = pipe1[0]; /* Input Stream.  */

+			channels[2] = pipe2[0]; /* Input Stream.  */

+		}

+

+		free(full_path);

+		return childpid;

+	}

+

+	free(full_path);

+	return -1;                  /*NOT REACHED */

+}

+

+

+int wait0(pid_t pid)

+{

+	int status;

+	int val = -1;

+

+	if (pid < 0)

+		return -1;

+	

+	for (;;) {

+		if (waitpid(pid, &status, 0) < 0) {

+			if (errno == EINTR) {

+				// interrupted system call - retry

+				continue;

+			}

+		}

+		break;

+	}

+	if (WIFEXITED(status)) {

+		val = WEXITSTATUS(status);

+	}

+

+	return val;

+}

diff --git a/com.google.eclipse.elt.pty.linux/library/io.c b/com.google.eclipse.elt.pty.linux/library/io.c
new file mode 100644
index 0000000..8540b82
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/io.c
@@ -0,0 +1,114 @@
+/*******************************************************************************

+ * Copyright (c) 2002, 2006 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.

+ *******************************************************************************/

+#include <jni.h>

+#include <stdio.h>

+#include <SpawnerInputStream.h>

+#include <SpawnerOutputStream.h>

+#include <unistd.h>

+

+/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerInputStream */

+/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerOutputStream */

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerInputStream

+ * Method:    read0

+ * Signature: (I)I

+ */

+JNIEXPORT jint JNICALL

+Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0(JNIEnv * env,

+                                                          jobject jobj,

+                                                          jint jfd,

+                                                          jbyteArray buf,

+                                                          jint buf_len)

+{

+    int fd;

+    int status;

+    jbyte *data;

+    int data_len;

+

+    data = (*env)->GetByteArrayElements(env, buf, 0);

+    data_len = buf_len;

+    fd = jfd;

+

+    status = read( fd, data, data_len );

+    (*env)->ReleaseByteArrayElements(env, buf, data, 0);

+

+    if (status == 0) {

+        /* EOF. */

+        status = -1;

+    } else if (status == -1) {

+        /* Error, toss an exception */

+        jclass exception = (*env)->FindClass(env, "java/io/IOException");

+        if (exception == NULL) {

+            /* Give up.  */

+            return -1;

+        }

+        (*env)->ThrowNew(env, exception, "read error");

+    }

+

+    return status;

+}

+

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerInputStream

+ * Method:    close0

+ * Signature: (I)I

+ */

+JNIEXPORT jint JNICALL

+Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0(JNIEnv * env,

+                                                           jobject jobj,

+                                                           jint fd)

+{

+    return close(fd);

+}

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerOutputStream

+ * Method:    write0

+ * Signature: (II)I

+ */

+JNIEXPORT jint JNICALL

+Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0(JNIEnv * env,

+                                                            jobject jobj,

+                                                            jint jfd,

+                                                            jbyteArray buf,

+                                                            jint buf_len)

+{

+    int status;

+    int fd;

+    jbyte *data;

+    int data_len;

+

+    data = (*env)->GetByteArrayElements(env, buf, 0);

+    data_len = buf_len;

+    fd = jfd;

+

+    status = write(fd, data, data_len);

+    (*env)->ReleaseByteArrayElements(env, buf, data, 0);

+

+    return status;

+}

+

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerOutputStream

+ * Method:    close0

+ * Signature: (I)I

+ */

+JNIEXPORT jint JNICALL

+Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0(JNIEnv * env,

+                                                            jobject jobj,

+                                                            jint fd)

+{

+    return close(fd);

+}

diff --git a/com.google.eclipse.elt.pty.linux/library/openpty.c b/com.google.eclipse.elt.pty.linux/library/openpty.c
new file mode 100644
index 0000000..818ca8e
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/openpty.c
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * 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.
+ *     Mikhail Zabaluev (Nokia) - bug 82744
+ *     Corey Ashford (IBM) - bug 272370, bug 272372
+ *******************************************************************************/
+
+/* _XOPEN_SOURCE is needed to bring in the header for ptsname */
+#define _XOPEN_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <grp.h>
+
+#include <stdlib.h>
+
+/**
+ * This is taken from R. W. Stevens book.
+ * Alain Magloire.
+ */
+
+int ptym_open (char *pts_name);
+int ptys_open (int fdm, const char * pts_name);
+void set_noecho(int fd);
+
+int
+openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp)
+{
+	char line[20];
+	line[0]=0;
+	*amaster = ptym_open(line);
+	if (*amaster < 0)
+		return -1;
+	*aslave = ptys_open(*amaster, line);
+	if (*aslave < 0) {
+		close(*amaster);
+		return -1;
+	}
+
+	if (name)
+		strcpy(name, line);
+#ifndef TCSAFLUSH
+#define TCSAFLUSH TCSETAF
+#endif
+	if (termp)
+		(void) tcsetattr(*aslave, TCSAFLUSH, termp);
+#ifdef TIOCSWINSZ
+	if (winp)
+		(void) ioctl(*aslave, TIOCSWINSZ, (char *)winp);
+#endif
+	return 0;
+}
+
+void
+set_noecho(int fd)
+{
+	struct termios stermios;
+	if (tcgetattr(fd, &stermios) < 0) {
+		return ;
+	}
+
+	/* turn off echo */
+	stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+	/* Turn off the NL to CR/NL mapping ou output.  */
+	/*stermios.c_oflag &= ~(ONLCR);*/
+
+	stermios.c_iflag |= (IGNCR);
+
+	tcsetattr(fd, TCSANOW, &stermios);
+}
+
+int
+ptym_open(char * pts_name)
+{
+	int fdm;
+	char *ptr;
+
+	strcpy(pts_name, "/dev/ptmx");
+	fdm = getpt();
+	if (fdm < 0)
+		return -1;
+	if (grantpt(fdm) < 0) { /* grant access to slave */
+		close(fdm);
+		return -2;
+	}
+	if (unlockpt(fdm) < 0) { /* clear slave's lock flag */
+		close(fdm);
+		return -3;
+	}
+	ptr = ptsname(fdm);
+	if (ptr == NULL) { /* get slave's name */
+		close (fdm);
+		return -4;
+	}
+	strcpy(pts_name, ptr); /* return name of slave */
+	return fdm;            /* return fd of master */
+}
+
+int
+ptys_open(int fdm, const char * pts_name)
+{
+	int fds;
+	/* following should allocate controlling terminal */
+	fds = open(pts_name, O_RDWR);
+	if (fds < 0) {
+		close(fdm);
+		return -5;
+	}
+
+#if	defined(TIOCSCTTY)
+	/*  TIOCSCTTY is the BSD way to acquire a controlling terminal. */
+	if (ioctl(fds, TIOCSCTTY, (char *)0) < 0) {
+		// ignore error: this is expected in console-mode
+	}
+#endif
+	return fds;
+}
diff --git a/com.google.eclipse.elt.pty.linux/library/openpty.h b/com.google.eclipse.elt.pty.linux/library/openpty.h
new file mode 100644
index 0000000..c6475fa
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/openpty.h
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * 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.
+ *     Mikhail Zabaluev (Nokia) - bug 82744
+ *******************************************************************************/
+#ifndef _OPENPTY_H
+#define _OPENPTY_H
+int ptym_open (char *pts_name);
+int ptys_open (int fdm, const char * pts_name);
+void set_noecho(int fd);
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/pfind.c b/com.google.eclipse.elt.pty.linux/library/pfind.c
new file mode 100644
index 0000000..519c6c3
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/pfind.c
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * 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.
+ *     Mikhail Sennikovsky - bug 145737
+ *     Everton Rufino Constantino (IBM) - bug 237611
+ *******************************************************************************/
+/*
+ * pfind.c - Search for a binary in $PATH.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define PATH_DEF "PATH="
+const int path_def_len = 5; /* strlen(PATH_DEF); */
+
+char * path_val(char * const envp[])
+{
+	int i;
+	if (envp == NULL || envp[0] == NULL)
+		return getenv("PATH" );
+	
+	for(i = 0; envp[i] != NULL; i++){
+		char* p = envp[i];
+		if(!strncmp(PATH_DEF, p, path_def_len)){
+			return p + path_def_len;
+		}
+	}
+	
+	return NULL;
+}
+
+char * pfind(const char *name, char * const envp[])
+{
+	char *tok;
+	char *sp;
+	char *path;
+	char fullpath[PATH_MAX+1];
+
+	/* Sanity check.  */
+	if (name == NULL) {
+		fprintf(stderr, "pfind(): Null argument.\n");
+		return NULL;
+	}
+
+	/* For absolute name or name with a path, check if it is an executable.  */
+	if (name[0] == '/' || name[0] == '.') {
+		if (access(name, X_OK) == 0) {
+			return strdup(name);
+		}
+		return NULL;
+	}
+
+	/* Search in the PATH environment.  */
+	path = path_val( envp );
+
+	if (path == NULL || strlen(path) <= 0) {
+		fprintf(stderr, "Unable to get $PATH.\n");
+		return NULL;
+	}
+
+	/* The value return by getenv() is readonly */
+	path = strdup(path);
+
+	tok = strtok_r(path, ":", &sp);
+	while (tok != NULL) {
+		snprintf(fullpath, sizeof(fullpath) - 1, "%s/%s", tok, name);
+
+		if (access(fullpath, X_OK) == 0) {
+			free(path);
+			return strdup(fullpath);
+		}
+
+		tok = strtok_r( NULL, ":", &sp );
+	}
+
+	free(path);
+	return NULL;
+}
+
+#ifdef BUILD_WITH_MAIN
+int main(int argc, char **argv)
+{
+   int i;
+   char *fullpath;
+
+   for (i=1; i<argc; i++) {
+      fullpath = pfind(argv[i], NULL);
+      if (fullpath == NULL)
+        printf("Unable to find %s in $PATH.\n", argv[i]);
+      else 
+        printf("Found %s @ %s.\n", argv[i], fullpath);
+   }
+}
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/pty.c b/com.google.eclipse.elt.pty.linux/library/pty.c
new file mode 100644
index 0000000..5c1e864
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/pty.c
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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.
+ *******************************************************************************/
+#include <sys/ioctl.h>
+#include "PTY.h"
+#include "openpty.h"
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTY
+ * Method:    forkpty
+ * Signature: ()I
+ */
+JNIEXPORT jstring JNICALL
+Java_org_eclipse_cdt_utils_pty_PTY_openMaster (JNIEnv *env, jobject jobj, jboolean console) {
+	jfieldID fid; /* Store the field ID */
+	jstring jstr = NULL;
+	int master = -1;
+	char line[1024];	/* FIXME: Should be enough */
+	jclass cls;
+
+	line[0] = '\0';
+
+	master = ptym_open(line);
+	if (master >= 0) {
+		// turn off echo
+		if (console) {
+			set_noecho(master);
+		}
+
+		/* Get a reference to the obj's class */
+		cls = (*env)->GetObjectClass(env, jobj);
+
+		/* Set the master fd.  */
+		fid = (*env)->GetFieldID(env, cls, "master", "I");
+		if (fid == NULL) {
+			return NULL;
+		}
+		(*env)->SetIntField(env, jobj, fid, (jint)master);
+
+		/* Create a new String for the slave.  */
+		jstr = (*env)->NewStringUTF(env, line);
+	}
+	return jstr;
+}
+
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size
+  (JNIEnv *env, jobject jobj, jint fdm, jint width, jint height)
+{
+#ifdef	TIOCGWINSZ
+	struct winsize win;
+
+	win.ws_col = width;
+	win.ws_row = height;
+	win.ws_xpixel = 0;
+	win.ws_ypixel = 0;
+
+	return ioctl(fdm, TIOCSWINSZ, &win);
+#else
+	return 0;
+#endif
+}
+
diff --git a/com.google.eclipse.elt.pty.linux/library/ptyio.c b/com.google.eclipse.elt.pty.linux/library/ptyio.c
new file mode 100644
index 0000000..a999787
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/ptyio.c
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2002 - 2005 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
+ *******************************************************************************/
+#include <jni.h>
+#include <stdio.h>
+#include <PTYInputStream.h>
+#include <PTYOutputStream.h>
+#include <unistd.h>
+
+/* Header for class _org_eclipse_cdt_utils_pty_PTYInputStream */
+/* Header for class _org_eclipse_cdt_utils_pty_PTYOutputStream */
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method:    read0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0(JNIEnv * env,
+                                                          jobject jobj,
+                                                          jint jfd,
+                                                          jbyteArray buf,
+                                                          jint buf_len)
+{
+    int fd;
+    int status;
+    jbyte *data;
+    int data_len;
+
+    data = (*env)->GetByteArrayElements(env, buf, 0);
+    data_len = buf_len;
+    fd = jfd;
+
+    status = read( fd, data, data_len );
+    (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+    if (status == 0) {
+        /* EOF. */
+        status = -1;
+    } else if (status == -1) {
+        /* Error, toss an exception */
+	/* Ignore the error for now, the debugger will attempt
+	 * to close this multiple time.  */
+#if 0
+        jclass exception = (*env)->FindClass(env, "java/io/IOException");
+        if (exception == NULL) {
+            /* Give up.  */
+            return -1;
+        }
+        (*env)->ThrowNew(env, exception, "read error");
+#endif
+    }
+
+    return status;
+}
+
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0(JNIEnv * env,
+                                                           jobject jobj,
+                                                           jint fd)
+{
+    return close(fd);
+}
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method:    write0
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0(JNIEnv * env,
+                                                            jobject jobj,
+                                                            jint jfd,
+                                                            jbyteArray buf,
+                                                            jint buf_len)
+{
+    int status;
+    int fd;
+    jbyte *data;
+    int data_len;
+
+    data = (*env)->GetByteArrayElements(env, buf, 0);
+    data_len = buf_len;
+    fd = jfd;
+
+    status = write(fd, data, data_len);
+    (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+    return status;
+}
+
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0(JNIEnv * env,
+                                                            jobject jobj,
+                                                            jint fd)
+{
+    return close(fd);
+}
diff --git a/com.google.eclipse.elt.pty.linux/library/spawner.c b/com.google.eclipse.elt.pty.linux/library/spawner.c
new file mode 100644
index 0000000..67b12b0
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/spawner.c
@@ -0,0 +1,302 @@
+/*******************************************************************************

+ * 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.  

+ *     Mikhail Zabaluev (Nokia) - bug 82744

+ *******************************************************************************/

+#include <unistd.h>

+#include <stdlib.h>

+#include <stdio.h>

+#include <signal.h>

+#include <string.h>

+#include <jni.h>

+

+#include "exec0.h"

+#include <Spawner.h>

+

+

+#define DEBUGIT 0

+

+

+/*

+ * Header for class org_eclipse_cdt_utils_spawner_Spawner 

+ */

+

+

+#if DEBUGIT

+static void print_array(char **c_array)

+{

+    if (c_array) {

+        char **p = c_array;

+        for (; *p; p++) {

+            if (*p) {

+                fprintf(stderr, " %s", *p);

+            }

+        }

+    } else {

+        fprintf(stderr, "null");

+    }

+    fprintf(stderr, "\n");

+}

+#endif

+

+

+static char **alloc_c_array(JNIEnv * env, jobjectArray j_array)

+{

+    int i;

+    jint c_array_size = (*env)->GetArrayLength(env, j_array);

+    char **c_array = calloc(c_array_size + 1, sizeof(*c_array));

+

+    if (c_array == NULL)

+        return NULL;

+

+    for (i = 0; i < c_array_size; i++) {

+        jstring j_str =

+            (jstring) (*env)->GetObjectArrayElement(env, j_array, i);

+        const char *c_str = (*env)->GetStringUTFChars(env, j_str, NULL);

+        c_array[i] = (char *) strdup(c_str);

+        (*env)->ReleaseStringUTFChars(env, j_str, c_str);

+        (*env)->DeleteLocalRef(env, j_str);

+    }

+

+    return c_array;

+}

+

+

+static void free_c_array(char **c_array)

+{

+    if (c_array) {

+        char **p = c_array;

+        for (; *p; p++) {

+            if (*p) {

+                free(*p);

+            }

+        }

+        free(c_array);

+    }

+}

+

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_Spawner

+ * Method:    exec2

+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILorg/eclipse/cdt/utils/pty/PTY;)I

+ */

+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2

+  (JNIEnv *env, jobject jobj, jobjectArray jcmd, jobjectArray jenv, jstring jdir, jintArray jchannels,

+   jstring jslaveName, jint masterFD, jboolean console)

+{

+    jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0);

+    const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);

+    const char *pts_name = (*env)->GetStringUTFChars(env, jslaveName, NULL);

+    char **cmd = NULL;

+    char **envp = NULL;

+    int fd[3];

+    pid_t pid = -1;

+

+    if (channels == NULL)

+        goto bail_out;

+

+    cmd = alloc_c_array(env, jcmd);

+    if (cmd == NULL)

+        goto bail_out;

+

+    envp = alloc_c_array(env, jenv);

+    if (envp == NULL)

+        goto bail_out;

+

+#if DEBUGIT

+    fprintf(stderr, "command:");

+    print_array(cmd);

+    fprintf(stderr, "Envp:");

+    print_array(envp);

+    fprintf(stderr, "dirpath: %s\n", dirpath);

+    fprintf(stderr, "pts_name: %s\n", pts_name);

+#endif

+

+    pid = exec_pty(cmd[0], cmd, envp, dirpath, fd, pts_name, masterFD, console);

+    if (pid < 0)

+        goto bail_out;

+

+    channels[0] = fd[0];

+    channels[1] = fd[1];

+    channels[2] = fd[2];

+

+  bail_out:

+    (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0);

+    (*env)->ReleaseStringUTFChars(env, jdir, dirpath);

+    (*env)->ReleaseStringUTFChars(env, jslaveName, pts_name);

+    if (cmd)

+        free_c_array(cmd);

+    if (envp)

+        free_c_array(envp);

+    return pid;

+}

+ 

+

+JNIEXPORT jint JNICALL

+Java_org_eclipse_cdt_utils_spawner_Spawner_exec1(JNIEnv * env, jobject jobj,

+                                               jobjectArray jcmd,

+                                               jobjectArray jenv,

+                                               jstring jdir)

+{

+    const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);

+    char **cmd = NULL;

+    char **envp = NULL;

+    pid_t pid = -1;

+

+    cmd = alloc_c_array(env, jcmd);

+    if (cmd == NULL)

+        goto bail_out;

+

+    envp = alloc_c_array(env, jenv);

+    if (envp == NULL)

+        goto bail_out;

+

+#if DEBUGIT

+    fprintf(stderr, "command:");

+    print_array(cmd);

+    fprintf(stderr, "Envp:");

+    print_array(envp);

+    fprintf(stderr, "dirpath: %s\n", dirpath);

+#endif

+

+    pid = exec0(cmd[0], cmd, envp, dirpath, NULL);

+    if (pid < 0)

+        goto bail_out;

+

+  bail_out:

+    (*env)->ReleaseStringUTFChars(env, jdir, dirpath);

+    if (cmd)

+        free_c_array(cmd);

+    if (envp)

+        free_c_array(envp);

+    return pid;

+}

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_Spawner

+ * Method:    exec0

+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I

+ */

+JNIEXPORT jint JNICALL

+Java_org_eclipse_cdt_utils_spawner_Spawner_exec0(JNIEnv * env, jobject jobj,

+                                               jobjectArray jcmd,

+                                               jobjectArray jenv,

+                                               jstring jdir,

+                                               jintArray jchannels)

+{

+    jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0);

+    const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);

+    char **cmd = NULL;

+    char **envp = NULL;

+    int fd[3];

+    pid_t pid = -1;

+

+    if (channels == NULL)

+        goto bail_out;

+

+    cmd = alloc_c_array(env, jcmd);

+    if (cmd == NULL)

+        goto bail_out;

+

+    envp = alloc_c_array(env, jenv);

+    if (envp == NULL)

+        goto bail_out;

+

+#if DEBUGIT

+    fprintf(stderr, "command:");

+    print_array(cmd);

+    fprintf(stderr, "Envp:");

+    print_array(envp);

+    fprintf(stderr, "dirpath: %s\n", dirpath);

+#endif

+

+    pid = exec0(cmd[0], cmd, envp, dirpath, fd);

+    if (pid < 0)

+        goto bail_out;

+

+    channels[0] = fd[0];

+    channels[1] = fd[1];

+    channels[2] = fd[2];

+

+  bail_out:

+    (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0);

+    (*env)->ReleaseStringUTFChars(env, jdir, dirpath);

+    if (cmd)

+        free_c_array(cmd);

+    if (envp)

+        free_c_array(envp);

+    return pid;

+}

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_Spawner

+ * Method:    raise

+ * Signature: (II)I

+ */

+JNIEXPORT jint JNICALL

+Java_org_eclipse_cdt_utils_spawner_Spawner_raise(JNIEnv * env, jobject jobj,

+                                               jint pid, jint sig)

+{

+    int status = -1;

+

+    switch (sig) {

+    case 0:                    /* NOOP */

+        status = killpg(pid, 0);

+        if(status == -1) {

+	        status = kill(pid, 0);

+        }

+	break;

+

+    case 2:                    /* INTERRUPT */

+        status = killpg(pid, SIGINT);

+        if(status == -1) {

+	        status = kill(pid, SIGINT);

+        }

+	break;

+

+    case 9:                    /* KILL */

+        status = killpg(pid, SIGKILL);

+        if(status == -1) {

+        	status = kill(pid, SIGKILL);

+        }

+	break;

+

+    case 15:                   /* TERM */

+        status = killpg(pid, SIGTERM);

+        if(status == -1) {

+    	    status = kill(pid, SIGTERM);

+        }

+	break;

+

+    default:

+        status = killpg(pid, sig);        /* WHAT ?? */

+        if(status == -1) {

+	        status = kill(pid, sig);        /* WHAT ?? */

+        }

+	break;

+    }

+

+    return status;

+}

+

+

+

+/*

+ * Class:     org_eclipse_cdt_utils_spawner_Spawner

+ * Method:    waitFor

+ * Signature: (I)I

+ */

+JNIEXPORT jint JNICALL

+Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor(JNIEnv * env,

+                                                 jobject jobj, jint pid)

+{

+    return wait0(pid);

+}

diff --git a/com.google.eclipse.elt.pty.macosx/.classpath b/com.google.eclipse.elt.pty.macosx/.classpath
new file mode 100644
index 0000000..7d9b38e
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.pty.macosx/.gitignore b/com.google.eclipse.elt.pty.macosx/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/com.google.eclipse.elt.pty.macosx/.project b/com.google.eclipse.elt.pty.macosx/.project
new file mode 100644
index 0000000..6ca68f6
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.eclipse.elt.macosx</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty.macosx/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.pty.macosx/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c537b63
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/com.google.eclipse.elt.pty.macosx/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty.macosx/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..f30f562
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %fragmentName.macosx
+Bundle-SymbolicName: com.google.eclipse.elt.macosx;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-ClassPath: cdt_macosx.jar
+Bundle-Vendor: %providerName
+Fragment-Host: com.google.eclipse.elt.pty;bundle-version="1.1.0"
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Eclipse-PlatformFilter: (osgi.os=macosx)
diff --git a/com.google.eclipse.elt.pty.macosx/build.properties b/com.google.eclipse.elt.pty.macosx/build.properties
new file mode 100644
index 0000000..fc76b9b
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/build.properties
@@ -0,0 +1,18 @@
+###############################################################################
+#  Copyright (c) 2005, 2007 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
+###############################################################################
+bin.includes = fragment.xml,\
+               about.html,\
+               .,\
+               os/,\
+               META-INF/
+src.includes = about.html,\
+               library/
+source.. = src/
diff --git a/com.google.eclipse.elt.pty.macosx/fragment.xml b/com.google.eclipse.elt.pty.macosx/fragment.xml
new file mode 100644
index 0000000..aedc72a
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/fragment.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<fragment>
+
+</fragment>
diff --git a/com.google.eclipse.elt.pty.macosx/library/Makefile b/com.google.eclipse.elt.pty.macosx/library/Makefile
new file mode 100644
index 0000000..26de453
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/Makefile
@@ -0,0 +1,189 @@
+#*******************************************************************************
+# Copyright (c) 2002, 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
+#     Alex Blewitt - MacOSX with a 64-bit vm 
+#*******************************************************************************/
+
+# makefile for libspawner.so
+# See http://developer.apple.com/documentation/Java/Conceptual/Java141Development/Core_APIs/chapter_6_section_4.html
+
+JAVA_HOME = /Library/Java/Home
+ifeq ($(JAVA_HOME),)
+$(warning JAVA_HOME not set in environment)
+endif
+
+# Defaults which can be overridden.
+OS = macosx
+ARCH_PPC = ppc
+ARCH_X86 = x86
+ARCH_X86_64 = x86_64
+
+JDK_INCLUDES= $(JAVA_HOME)/include
+
+CC=gcc
+LD=libtool
+CPPFLAGS = -I. -I$(JDK_INCLUDES) #-I$(JDK_OS_INCLUDES)
+CFLAGS +=-fPIC -D_REENTRANT
+
+ARCH_FLAG_PPC = -arch ppc
+ARCH_FLAG_X86 = -arch i386
+ARCH_FLAG_X86_64 = -arch x86_64
+
+INSTALL_DIR_PPC = ../os/$(OS)/$(ARCH_PPC)
+INSTALL_DIR_X86 = ../os/$(OS)/$(ARCH_X86)
+INSTALL_DIR_X86_64 = ../os/$(OS)/$(ARCH_X86_64)
+
+LIB_NAME_SPAWNER = libspawner.dylib
+LIB_NAME_FULL_SPAWNER_PPC = $(INSTALL_DIR_PPC)/libspawner.jnilib
+LIB_NAME_FULL_SPAWNER_X86 = $(INSTALL_DIR_X86)/libspawner.jnilib
+LIB_NAME_FULL_SPAWNER_X86_64 = $(INSTALL_DIR_X86_64)/libspawner.jnilib
+OBJS_SPAWNER_PPC = spawner_$(ARCH_PPC).o \
+                   io_$(ARCH_PPC).o \
+                   exec_unix_$(ARCH_PPC).o \
+                   exec_pty_$(ARCH_PPC).o \
+                   openpty_$(ARCH_PPC).o \
+                   pfind_$(ARCH_PPC).o
+OBJS_SPAWNER_X86 = spawner_$(ARCH_X86).o \
+                   io_$(ARCH_X86).o \
+                   exec_unix_$(ARCH_X86).o \
+                   exec_pty_$(ARCH_X86).o \
+                   openpty_$(ARCH_X86).o \
+                   pfind_$(ARCH_X86).o
+OBJS_SPAWNER_X86_64 = spawner_$(ARCH_X86_64).o \
+                   io_$(ARCH_X86_64).o \
+                   exec_unix_$(ARCH_X86_64).o \
+                   exec_pty_$(ARCH_X86_64).o \
+                   openpty_$(ARCH_X86_64).o \
+                   pfind_$(ARCH_X86_64).o
+
+LIB_NAME_PTY = libpty.so
+LIB_NAME_FULL_PTY_PPC = $(INSTALL_DIR_PPC)/libpty.jnilib
+LIB_NAME_FULL_PTY_X86 = $(INSTALL_DIR_X86)/libpty.jnilib
+LIB_NAME_FULL_PTY_X86_64 = $(INSTALL_DIR_X86_64)/libpty.jnilib
+OBJS_PTY_PPC = openpty_$(ARCH_PPC).o pty_$(ARCH_PPC).o ptyio_$(ARCH_PPC).o
+OBJS_PTY_X86 = openpty_$(ARCH_X86).o pty_$(ARCH_X86).o ptyio_$(ARCH_X86).o
+OBJS_PTY_X86_64 = openpty_$(ARCH_X86_64).o pty_$(ARCH_X86_64).o ptyio_$(ARCH_X86_64).o
+
+OBJS_PPC = $(OBJS_SPAWNER_PPC) $(OBJS_PTY_PPC)
+OBJS_X86 = $(OBJS_SPAWNER_X86) $(OBJS_PTY_X86)
+OBJS_X86_64 = $(OBJS_SPAWNER_X86_64) $(OBJS_PTY_X86_64)
+
+all: ppc x86 x86_64
+
+ppc: $(LIB_NAME_FULL_SPAWNER_PPC) $(LIB_NAME_FULL_PTY_PPC)
+
+x86: $(LIB_NAME_FULL_SPAWNER_X86) $(LIB_NAME_FULL_PTY_X86)
+
+x86_64: $(LIB_NAME_FULL_SPAWNER_X86_64) $(LIB_NAME_FULL_PTY_X86_64)
+
+rebuild: clean all
+
+$(LIB_NAME_FULL_SPAWNER_PPC) : $(OBJS_SPAWNER_PPC)
+	mkdir -p $(INSTALL_DIR_PPC)
+	$(CC) -dynamiclib $(ARCH_FLAG_PPC) -o $(LIB_NAME_FULL_SPAWNER_PPC) $(OBJS_SPAWNER_PPC) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_SPAWNER_X86) : $(OBJS_SPAWNER_X86)
+	mkdir -p $(INSTALL_DIR_X86)
+	$(CC) -dynamiclib $(ARCH_FLAG_X86) -o $(LIB_NAME_FULL_SPAWNER_X86) $(OBJS_SPAWNER_X86) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_SPAWNER_X86_64) : $(OBJS_SPAWNER_X86_64)
+	mkdir -p $(INSTALL_DIR_X86_64)
+	$(CC) -dynamiclib $(ARCH_FLAG_X86_64) -o $(LIB_NAME_FULL_SPAWNER_X86_64) $(OBJS_SPAWNER_X86_64) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_PTY_PPC): $(OBJS_PTY_PPC)
+	mkdir -p $(INSTALL_DIR_PPC)
+	$(CC) -dynamiclib  $(ARCH_FLAG_PPC) -o $(LIB_NAME_FULL_PTY_PPC) $(OBJS_PTY_PPC) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_PTY_X86): $(OBJS_PTY_X86)
+	mkdir -p $(INSTALL_DIR_X86)
+	$(CC) -dynamiclib  $(ARCH_FLAG_X86) -o $(LIB_NAME_FULL_PTY_X86) $(OBJS_PTY_X86) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_PTY_X86_64): $(OBJS_PTY_X86_64)
+	mkdir -p $(INSTALL_DIR_X86_64)
+	$(CC) -dynamiclib  $(ARCH_FLAG_X86_64) -o $(LIB_NAME_FULL_PTY_X86_64) $(OBJS_PTY_X86_64) -lc -framework JavaVM
+
+spawner_$(ARCH_PPC).o: spawner.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ spawner.c
+
+io_$(ARCH_PPC).o: io.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ io.c
+
+exec_unix_$(ARCH_PPC).o: exec_unix.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ exec_unix.c
+
+exec_pty_$(ARCH_PPC).o: exec_pty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ exec_pty.c
+
+openpty_$(ARCH_PPC).o: openpty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ openpty.c
+
+pfind_$(ARCH_PPC).o: pfind.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ pfind.c
+
+openpty_$(ARCH_PPC).o: openpty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ openpty.c
+
+pty_$(ARCH_PPC).o: pty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ pty.c
+
+ptyio_$(ARCH_PPC).o: ptyio.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ ptyio.c
+
+spawner_$(ARCH_X86).o: spawner.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ spawner.c
+
+io_$(ARCH_X86).o: io.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ io.c
+
+exec_unix_$(ARCH_X86).o: exec_unix.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ exec_unix.c
+
+exec_pty_$(ARCH_X86).o: exec_pty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ exec_pty.c
+
+openpty_$(ARCH_X86).o: openpty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ openpty.c
+
+pfind_$(ARCH_X86).o: pfind.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ pfind.c
+
+pty_$(ARCH_X86).o: pty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ pty.c
+
+ptyio_$(ARCH_X86).o: ptyio.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ ptyio.c
+
+spawner_$(ARCH_X86_64).o: spawner.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ spawner.c
+
+io_$(ARCH_X86_64).o: io.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ io.c
+
+exec_unix_$(ARCH_X86_64).o: exec_unix.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ exec_unix.c
+
+exec_pty_$(ARCH_X86_64).o: exec_pty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ exec_pty.c
+
+openpty_$(ARCH_X86_64).o: openpty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ openpty.c
+
+pfind_$(ARCH_X86_64).o: pfind.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ pfind.c
+
+pty_$(ARCH_X86_64).o: pty.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ pty.c
+
+ptyio_$(ARCH_X86_64).o: ptyio.c
+	$(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ ptyio.c
+
+clean :
+	$(RM) $(OBJS_PPC) $(LIB_NAME_FULL_SPAWNER_PPC) $(LIB_NAME_FULL_PTY_PPC)
+	$(RM) $(OBJS_X86) $(LIB_NAME_FULL_SPAWNER_X86) $(LIB_NAME_FULL_PTY_X86)
+	$(RM) $(OBJS_X86_64) $(LIB_NAME_FULL_SPAWNER_X86_64) $(LIB_NAME_FULL_PTY_X86_64)
diff --git a/com.google.eclipse.elt.pty.macosx/library/PTY.h b/com.google.eclipse.elt.pty.macosx/library/PTY.h
new file mode 100644
index 0000000..f717942
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/PTY.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTY */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTY
+#define _Included_org_eclipse_cdt_utils_pty_PTY
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTY
+ * Method:    openMaster
+ * Signature: (Z)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_eclipse_cdt_utils_pty_PTY_openMaster
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTY
+ * Method:    change_window_size
+ * Signature: (III)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size
+  (JNIEnv *, jobject, jint, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/PTYInputStream.h b/com.google.eclipse.elt.pty.macosx/library/PTYInputStream.h
new file mode 100644
index 0000000..e734927
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/PTYInputStream.h
@@ -0,0 +1,32 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTYInputStream */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTYInputStream
+#define _Included_org_eclipse_cdt_utils_pty_PTYInputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_eclipse_cdt_utils_pty_PTYInputStream_SKIP_BUFFER_SIZE
+#define org_eclipse_cdt_utils_pty_PTYInputStream_SKIP_BUFFER_SIZE 2048L
+/* Inaccessible static: skipBuffer */
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method:    read0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0
+  (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0
+  (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/PTYOutputStream.h b/com.google.eclipse.elt.pty.macosx/library/PTYOutputStream.h
new file mode 100644
index 0000000..fb28491
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/PTYOutputStream.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTYOutputStream */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTYOutputStream
+#define _Included_org_eclipse_cdt_utils_pty_PTYOutputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method:    write0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0
+  (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0
+  (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/Spawner.h b/com.google.eclipse.elt.pty.macosx/library/Spawner.h
new file mode 100644
index 0000000..02727f9
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/Spawner.h
@@ -0,0 +1,53 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_spawner_Spawner */
+
+#ifndef _Included_org_eclipse_cdt_utils_spawner_Spawner
+#define _Included_org_eclipse_cdt_utils_spawner_Spawner
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    exec0
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0
+  (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    exec1
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1
+  (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    exec2
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILjava/lang/String;IZ)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2
+  (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray, jstring, jint, jboolean);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    raise
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise
+  (JNIEnv *, jobject, jint, jint);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    waitFor
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor
+  (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/SpawnerInputStream.h b/com.google.eclipse.elt.pty.macosx/library/SpawnerInputStream.h
new file mode 100644
index 0000000..ecf8f8c
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/SpawnerInputStream.h
@@ -0,0 +1,32 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_qnx_tools_utils_spawner_SpawnerInputStream */
+
+#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerInputStream
+#define _Included_com_qnx_tools_utils_spawner_SpawnerInputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE
+#define com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE 2048L
+/* Inaccessible static: skipBuffer */
+/*
+ * Class:     org_elipse_cdt_utils_spawner_SpawnerInputStream
+ * Method:    read0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0
+  (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerInputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0
+  (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/SpawnerOutputStream.h b/com.google.eclipse.elt.pty.macosx/library/SpawnerOutputStream.h
new file mode 100644
index 0000000..444d71c
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/SpawnerOutputStream.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_qnx_tools_utils_spawner_SpawnerOutputStream */
+
+#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream
+#define _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method:    write0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0
+  (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0
+  (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/exec0.h b/com.google.eclipse.elt.pty.macosx/library/exec0.h
new file mode 100644
index 0000000..fa23bd7
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/exec0.h
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 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
+ *******************************************************************************/
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+
+extern pid_t exec0(const char *path, char *const argv[],
+                   char *const envp[], const char *dirpath,
+                   int channels[3] );
+
+extern pid_t exec_pty(const char *path, char *const argv[],
+                      char *const envp[], const char *dirpath,
+                      int channels[3], const char *pts_name, int fdm,
+                      int console);
+
+extern int wait0(pid_t pid);
diff --git a/com.google.eclipse.elt.pty.macosx/library/exec_pty.c b/com.google.eclipse.elt.pty.macosx/library/exec_pty.c
new file mode 100644
index 0000000..c326805
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/exec_pty.c
@@ -0,0 +1,184 @@
+/*******************************************************************************

+ * Copyright (c) 2002, 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

+ *******************************************************************************/

+#include "exec0.h"

+#include "openpty.h"

+#include <unistd.h>

+#include <stdlib.h>

+#include <stdio.h>

+#include <errno.h>

+#include <string.h>

+#include <libgen.h>

+#include <stdlib.h>

+#include <termios.h>

+

+/* from pfind.c */

+extern char *pfind(const char *name, char *const envp[]);

+

+pid_t

+exec_pty(const char *path, char *const argv[], char *const envp[],

+      const char *dirpath, int channels[3], const char *pts_name, int fdm, int console)

+{

+	int pipe2[2];

+	pid_t childpid;

+	char *full_path;

+

+	/*

+	 * We use pfind() to check that the program exists and is an executable.

+	 * If not pass the error up.  Also execve() wants a full path.

+	 */ 

+	full_path = pfind(path, envp);

+	if (full_path == NULL) {

+		fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : "");

+		return -1;

+	}

+

+	/*

+	 *  Make sure we can create our pipes before forking.

+	 */ 

+	if (console && channels != NULL) {

+		if (pipe(pipe2) < 0) { 

+			fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));

+			free(full_path);

+			return -1;

+		}

+	}

+

+	childpid = fork();

+

+	if (childpid < 0) {

+		fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));

+		free(full_path);

+		return -1;

+	} else if (childpid == 0) { /* child */

+

+		chdir(dirpath);

+

+		if (channels != NULL) {

+			int fds;

+

+			if (!console && setsid() < 0) {

+				perror("setsid()");

+				return -1;

+			}

+

+			fds = ptys_open(fdm, pts_name);

+			if (fds < 0) {

+				fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));

+				return -1;

+			}

+

+			/* Close the read end of pipe2 */

+			if (console && close(pipe2[0]) == -1) {

+				perror("close(pipe2[0]))");

+			}

+

+			/* close the master, no need in the child */

+			close(fdm);

+

+			if (console) {

+				set_noecho(fds);

+				if (setpgid(getpid(), getpid()) < 0) {

+					perror("setpgid()");

+					return -1;

+				}

+			}

+			/* redirections */

+			dup2(fds, STDIN_FILENO);   /* dup stdin */

+			dup2(fds, STDOUT_FILENO);  /* dup stdout */

+			if (console) {

+				dup2(pipe2[1], STDERR_FILENO);  /* dup stderr */

+			} else {

+				dup2(fds, STDERR_FILENO);  /* dup stderr */

+			}

+			close(fds);  /* done with fds. */

+		}

+

+		/* Close all the fd's in the child */

+		{

+			int fdlimit = sysconf(_SC_OPEN_MAX);

+			int fd = 3;

+

+			while (fd < fdlimit)

+				close(fd++);

+		}

+

+		if (envp[0] == NULL) {

+			execv(full_path, argv);

+		} else {

+			execve(full_path, argv, envp);

+		}

+

+		_exit(127);

+

+	} else if (childpid != 0) { /* parent */

+

+		if (console) {

+			set_noecho(fdm);

+		}

+		if (channels != NULL) {

+			/* close the write end of pipe1 */

+			if (console && close(pipe2[1]) == -1)

+				perror("close(pipe2[1])");

+ 

+			channels[0] = fdm; /* Input Stream. */

+			channels[1] = fdm; /* Output Stream.  */

+			if (console) { /* stderr Stream.  */

+				channels[2] = pipe2[0]; 

+			} else {

+				channels[2] = fdm;

+			}

+		}

+

+		free(full_path);

+		return childpid;

+	}

+

+	free(full_path);

+	return -1;                  /*NOT REACHED */

+}

+#ifdef __STAND_ALONE__

+int main(int argc, char **argv, char **envp) {

+	const char *path = "./bufferring_test";

+	int channels[3] = { -1, -1, -1};

+	int status;	

+	FILE *app_stdin;

+	FILE *app_stdout;

+	FILE *app_stderr;

+	char pts_name[32];

+	int fdm;	

+	char buffer[32];

+

+	fdm = ptym_open(pts_name);

+	status =  exec_pty(path, argv, envp, ".", channels, pts_name, fdm);

+	if (status >= 0) {

+		app_stdin = fdopen(channels[0], "w");	

+		app_stdout = fdopen(channels[1], "r");	

+		app_stderr = fdopen(channels[2], "r");	

+		if (app_stdout == NULL || app_stderr == NULL || app_stdin == NULL) {

+			fprintf(stderr, "PROBLEMS\n");

+		} else {

+			fputs("foo\n", app_stdin);

+			fputs("bar\n", app_stdin);

+			while(fgets(buffer, sizeof buffer, app_stdout) != NULL) {

+				fprintf(stdout, "STDOUT: %s\n", buffer);

+			}

+			while(fgets(buffer, sizeof buffer, app_stderr) != NULL) {

+				fprintf(stdout, "STDERR: %s\n", buffer);

+			}

+		}

+	}

+	fputs("bye\n", stdout);

+	close(channels[0]);

+	close(channels[1]);

+	close(channels[2]);

+	return 0;	

+}

+#endif

diff --git a/com.google.eclipse.elt.pty.macosx/library/exec_unix.c b/com.google.eclipse.elt.pty.macosx/library/exec_unix.c
new file mode 100644
index 0000000..ee70513
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/exec_unix.c
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2002 - 2005 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
+ *******************************************************************************/
+#include "exec0.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <libgen.h>
+#include <stdlib.h>
+
+/* from pfind.c */
+extern char *pfind(const char *name, char *const envp[]);
+
+pid_t
+exec0(const char *path, char *const argv[], char *const envp[],
+      const char *dirpath, int channels[3])
+{
+	int pipe0[2], pipe1[2], pipe2[2];
+	pid_t childpid;
+	char *full_path;
+
+	/*
+	 * We use pfind() to check that the program exists and is an executable.
+	 * If not pass the error up.  Also execve() wants a full path.
+	 */ 
+	full_path = pfind(path, envp);
+	if (full_path == NULL) {
+		fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : "");
+		return -1;
+	}
+
+	/*
+	 *  Make sure we can create our pipes before forking.
+	 */ 
+	if (channels != NULL) {
+		if (pipe(pipe0) < 0 || pipe(pipe1) < 0 || pipe(pipe2) < 0) {
+			fprintf(stderr, "%s(%d): returning due to error.\n",
+				__FUNCTION__, __LINE__);
+			free(full_path);
+			return -1;
+		}
+	}
+
+	childpid = fork();
+
+	if (childpid < 0) {
+		fprintf(stderr, "%s(%d): returning due to error: %s\n",
+			__FUNCTION__, __LINE__, strerror(errno));
+		free(full_path);
+		return -1;
+	} else if (childpid == 0) { /* child */
+		char *ptr;
+
+		chdir(dirpath);
+
+		if (channels != NULL) {
+			/* Close the write end of pipe0 */
+			if (close(pipe0[1]) == -1)
+				perror("close(pipe0[1])");
+
+			/* Close the read end of pipe1 */
+			if (close(pipe1[0]) == -1)
+				perror("close(pipe1[0])");
+
+			/* Close the read end of pipe2 */
+			if (close(pipe2[0]) == -1)
+				perror("close(pipe2[0]))");
+
+			/* redirections */
+			dup2(pipe0[0], STDIN_FILENO);   /* dup stdin */
+			dup2(pipe1[1], STDOUT_FILENO);  /* dup stdout */
+			dup2(pipe2[1], STDERR_FILENO);  /* dup stderr */
+		}
+
+		/* Close all the fd's in the child */
+		{
+			int fdlimit = sysconf(_SC_OPEN_MAX);
+			int fd = 3;
+
+			while (fd < fdlimit)
+				close(fd++);
+		}
+
+		if (envp[0] == NULL) {
+			execv(full_path, argv);
+		} else {
+			execve(full_path, argv, envp);
+		}
+
+		_exit(127);
+
+	} else if (childpid != 0) { /* parent */
+
+		char b;
+
+		if (channels != NULL) {
+			/* close the read end of pipe1 */
+			if (close(pipe0[0]) == -1)
+				perror("close(pipe0[0])");
+ 
+			/* close the write end of pipe2 */
+			if (close(pipe1[1]) == -1) 
+				perror("close(pipe1[1])");
+
+			/* close the write end of pipe2 */
+			if (close(pipe2[1]) == -1) 
+				perror("close(pipe2[1])");
+
+			channels[0] = pipe0[1]; /* Output Stream. */
+			channels[1] = pipe1[0]; /* Input Stream.  */
+			channels[2] = pipe2[0]; /* Input Stream.  */
+		}
+
+		free(full_path);
+		return childpid;
+	}
+
+	free(full_path);
+	return -1;                  /*NOT REACHED */
+}
+
+
+int wait0(pid_t pid)
+{
+	int status;
+	int val = -1;
+
+	if (pid < 0 || waitpid(pid, &status, 0) < 0)
+		return -1;
+
+	if (WIFEXITED(status)) {
+		val = WEXITSTATUS(status);
+	}
+
+	return val;
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/io.c b/com.google.eclipse.elt.pty.macosx/library/io.c
new file mode 100644
index 0000000..8c45e5d
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/io.c
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2002 - 2005 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
+ *******************************************************************************/
+#include <jni.h>
+#include <stdio.h>
+#include <SpawnerInputStream.h>
+#include <SpawnerOutputStream.h>
+#include <unistd.h>
+
+/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerInputStream */
+/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerOutputStream */
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerInputStream
+ * Method:    read0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0(JNIEnv * env,
+                                                          jobject jobj,
+                                                          jint jfd,
+                                                          jbyteArray buf,
+                                                          jint buf_len)
+{
+    int fd;
+    int status;
+    jbyte *data;
+    int data_len;
+
+    data = (*env)->GetByteArrayElements(env, buf, 0);
+    data_len = buf_len;
+    fd = jfd;
+
+    status = read( fd, data, data_len );
+    (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+    if (status == 0) {
+        /* EOF. */
+        status = -1;
+    } else if (status == -1) {
+        /* Error, toss an exception */
+        jclass exception = (*env)->FindClass(env, "java/io/IOException");
+        if (exception == NULL) {
+            /* Give up.  */
+            return -1;
+        }
+        (*env)->ThrowNew(env, exception, "read error");
+    }
+
+    return status;
+}
+
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerInputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0(JNIEnv * env,
+                                                           jobject jobj,
+                                                           jint fd)
+{
+    return close(fd);
+}
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method:    write0
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0(JNIEnv * env,
+                                                            jobject jobj,
+                                                            jint jfd,
+                                                            jbyteArray buf,
+                                                            jint buf_len)
+{
+    int status;
+    int fd;
+    jbyte *data;
+    int data_len;
+
+    data = (*env)->GetByteArrayElements(env, buf, 0);
+    data_len = buf_len;
+    fd = jfd;
+
+    status = write(fd, data, data_len);
+    (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+    return status;
+}
+
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0(JNIEnv * env,
+                                                            jobject jobj,
+                                                            jint fd)
+{
+    return close(fd);
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/openpty.c b/com.google.eclipse.elt.pty.macosx/library/openpty.c
new file mode 100644
index 0000000..0d720f5
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/openpty.c
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 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
+ *******************************************************************************/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <grp.h>
+#include <termios.h>
+
+#include <stdlib.h>
+
+/**
+ * This is taken from R. W. Stevens book.
+ * Alain Magloire.
+ */
+
+int ptym_open (char *pts_name);
+int ptys_open (int fdm, const char * pts_name);
+
+int
+openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp)
+{
+	char line[20];
+	line[0]=0;
+	*amaster = ptym_open(line);
+	if (*amaster < 0)
+		return -1;
+	*aslave = ptys_open(*amaster, line);
+	if (*aslave < 0) {
+		close(*amaster);
+		return -1;
+	}
+	if (name)
+		strcpy(name, line);
+#ifndef TCSAFLUSH
+#define TCSAFLUSH TCSETAF
+#endif
+	if (termp)
+		(void) tcsetattr(*aslave, TCSAFLUSH, termp);
+#ifdef TIOCSWINSZ
+	if (winp)
+		(void) ioctl(*aslave, TIOCSWINSZ, (char *)winp);
+#endif
+	return 0;
+}
+
+int
+ptym_open(char * pts_name)
+{
+	char *ptr1, *ptr2;
+	int fdm;
+	
+	strcpy(pts_name, "/dev/ptyXY");
+	/* array index: 012345689 (for references in following code) */
+	for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1 != 0; ptr1++) {
+		pts_name[8] = *ptr1;
+		for (ptr2 = "0123456789abcdef"; *ptr2 != 0; ptr2++) {
+			pts_name[9] = *ptr2;
+			/* try to open master */
+			fdm = open(pts_name, O_RDWR);
+			if (fdm < 0) {
+				if (errno == ENOENT) {/* different from EIO */
+					return -1;  /* out of pty devices */
+				} else {
+					continue;  /* try next pty device */
+				}
+			}
+			pts_name[5] = 't'; /* chage "pty" to "tty" */
+			return fdm;   /* got it, return fd of master */
+		}
+	}
+	return -1; /* out of pty devices */
+}
+
+int
+ptys_open(int fdm, const char * pts_name)
+{
+	int gid, fds;
+	struct group *grptr;
+
+	grptr = getgrnam("tty");
+	if (grptr != NULL) {
+		gid = grptr->gr_gid;
+	} else {
+		gid = -1;  /* group tty is not in the group file */
+	}
+
+	/* following two functions don't work unless we're root */
+	chown(pts_name, getuid(), gid);
+	chmod(pts_name, S_IRUSR | S_IWUSR | S_IWGRP);
+	fds = open(pts_name, O_RDWR);
+	if (fds < 0) {
+		close(fdm);
+		return -1;
+	}
+	
+#if	defined(TIOCSCTTY)
+	/*  TIOCSCTTY is the BSD way to acquire a controlling terminal. */
+	if (ioctl(fds, TIOCSCTTY, (char *)0) < 0) {
+		// ignore error: this is expected in console-mode
+	}
+#endif
+	
+	return fds;
+}
+
+void
+set_noecho(int fd)
+{
+	struct termios stermios;
+	if (tcgetattr(fd, &stermios) < 0) {
+		return ;
+	}
+
+	/* turn off echo */
+	stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+	/* Turn off the NL to CR/NL mapping ou output.  */
+	/*stermios.c_oflag &= ~(ONLCR);*/
+
+	stermios.c_iflag |= (IGNCR);
+
+	tcsetattr(fd, TCSANOW, &stermios);
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/openpty.h b/com.google.eclipse.elt.pty.macosx/library/openpty.h
new file mode 100644
index 0000000..fd974da
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/openpty.h
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 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
+ *******************************************************************************/
+#ifndef _OPENPTY_H
+#define _OPENPTY_H
+int ptym_open (char *pts_name);
+int ptys_open (int fdm, const char * pts_name);
+void set_noecho(int fd);
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/pfind.c b/com.google.eclipse.elt.pty.macosx/library/pfind.c
new file mode 100644
index 0000000..83abf24
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/pfind.c
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * 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.
+ *     Mikhail Sennikovsky - bug 145737
+ *     Everton Rufino Constantino (IBM) - bug 237611
+ *******************************************************************************/
+/*
+ * pfind.c - Search for a binary in $PATH.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define PATH_DEF "PATH="
+const int path_def_len = 5; /* strlen(PATH_DEF); */
+
+char * path_val(char * const envp[])
+{
+	int i;
+	if (envp == NULL || envp[0] == NULL)
+		return getenv("PATH" );
+
+	for(i = 0; envp[i] != NULL; i++){
+		char* p = envp[i];
+		if(!strncmp(PATH_DEF, p, path_def_len)){
+			return p + path_def_len;
+		}
+	}
+
+	return NULL;
+}
+
+char * pfind(const char *name, char * const envp[])
+{
+	char *tok;
+	char *sp;
+	char *path;
+	char fullpath[PATH_MAX+1];
+
+	/* Sanity check.  */
+	if (name == NULL) {
+		fprintf(stderr, "pfind(): Null argument.\n");
+		return NULL;
+	}
+
+	/* For absolute name or name with a path, check if it is an executable.  */
+	if (name[0] == '/' || name[0] == '.') {
+		if (access(name, X_OK) == 0) {
+			return strdup(name);
+		}
+		return NULL;
+	}
+
+	/* Search in the PATH environment.  */
+	path = path_val( envp );
+
+	if (path == NULL || strlen(path) <= 0) {
+		fprintf(stderr, "Unable to get $PATH.\n");
+		return NULL;
+	}
+
+	/* The value return by getenv() is readonly */
+	path = strdup(path);
+
+	tok = strtok_r(path, ":", &sp);
+	while (tok != NULL) {
+		snprintf(fullpath, sizeof(fullpath) - 1, "%s/%s", tok, name);
+
+		if (access(fullpath, X_OK) == 0) {
+			free(path);
+			return strdup(fullpath);
+		}
+
+		tok = strtok_r( NULL, ":", &sp );
+	}
+
+	free(path);
+	return NULL;
+}
+
+#ifdef BUILD_WITH_MAIN
+int main(int argc, char **argv)
+{
+   int i;
+   char *fullpath;
+
+   for (i=1; i<argc; i++) {
+      fullpath = pfind(argv[i], NULL);
+      if (fullpath == NULL)
+        printf("Unable to find %s in $PATH.\n", argv[i]);
+      else 
+        printf("Found %s @ %s.\n", argv[i], fullpath);
+   }
+}
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/pty.c b/com.google.eclipse.elt.pty.macosx/library/pty.c
new file mode 100644
index 0000000..e7c23e8
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/pty.c
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 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
+ *******************************************************************************/
+#include "PTY.h"
+#include "openpty.h"
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTY
+ * Method:    forkpty
+ * Signature: ()I
+ */
+JNIEXPORT jstring JNICALL
+Java_org_eclipse_cdt_utils_pty_PTY_openMaster (JNIEnv *env, jobject jobj, jboolean console) {
+	jfieldID fid; /* Store the field ID */
+	jstring jstr = NULL;
+	int master = -1;
+	char line[1024];	/* FIXME: Should be enough */
+	jclass cls;
+
+	line[0] = '\0';
+
+	master = ptym_open(line);
+	if (master >= 0) {
+		if (console) {
+			// turn off echo
+			set_noecho(master);
+		}
+
+		/* Get a reference to the obj's class */
+		cls = (*env)->GetObjectClass(env, jobj);
+
+		/* Set the master fd.  */
+		fid = (*env)->GetFieldID(env, cls, "master", "I");
+		if (fid == NULL) {
+			return NULL;
+		}
+		(*env)->SetIntField(env, jobj, fid, (jint)master);
+
+		/* Create a new String for the slave.  */
+		jstr = (*env)->NewStringUTF(env, line);
+	}
+	return jstr;
+}
+
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size
+  (JNIEnv *env, jobject jobj, jint fdm, jint width, jint height)
+{
+#ifdef	TIOCGWINSZ
+	struct winsize win;
+
+	win.ws_col = width;
+	win.ws_row = height;
+	win.ws_xpixel = 0;
+	win.ws_ypixel = 0;
+
+	return ioctl(fdm, TIOCSWINSZ, &win);
+#else
+	return 0;
+#endif
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/ptyio.c b/com.google.eclipse.elt.pty.macosx/library/ptyio.c
new file mode 100644
index 0000000..a999787
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/ptyio.c
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2002 - 2005 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
+ *******************************************************************************/
+#include <jni.h>
+#include <stdio.h>
+#include <PTYInputStream.h>
+#include <PTYOutputStream.h>
+#include <unistd.h>
+
+/* Header for class _org_eclipse_cdt_utils_pty_PTYInputStream */
+/* Header for class _org_eclipse_cdt_utils_pty_PTYOutputStream */
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method:    read0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0(JNIEnv * env,
+                                                          jobject jobj,
+                                                          jint jfd,
+                                                          jbyteArray buf,
+                                                          jint buf_len)
+{
+    int fd;
+    int status;
+    jbyte *data;
+    int data_len;
+
+    data = (*env)->GetByteArrayElements(env, buf, 0);
+    data_len = buf_len;
+    fd = jfd;
+
+    status = read( fd, data, data_len );
+    (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+    if (status == 0) {
+        /* EOF. */
+        status = -1;
+    } else if (status == -1) {
+        /* Error, toss an exception */
+	/* Ignore the error for now, the debugger will attempt
+	 * to close this multiple time.  */
+#if 0
+        jclass exception = (*env)->FindClass(env, "java/io/IOException");
+        if (exception == NULL) {
+            /* Give up.  */
+            return -1;
+        }
+        (*env)->ThrowNew(env, exception, "read error");
+#endif
+    }
+
+    return status;
+}
+
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0(JNIEnv * env,
+                                                           jobject jobj,
+                                                           jint fd)
+{
+    return close(fd);
+}
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method:    write0
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0(JNIEnv * env,
+                                                            jobject jobj,
+                                                            jint jfd,
+                                                            jbyteArray buf,
+                                                            jint buf_len)
+{
+    int status;
+    int fd;
+    jbyte *data;
+    int data_len;
+
+    data = (*env)->GetByteArrayElements(env, buf, 0);
+    data_len = buf_len;
+    fd = jfd;
+
+    status = write(fd, data, data_len);
+    (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+    return status;
+}
+
+
+/*
+ * Class:     org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method:    close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0(JNIEnv * env,
+                                                            jobject jobj,
+                                                            jint fd)
+{
+    return close(fd);
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/spawner.c b/com.google.eclipse.elt.pty.macosx/library/spawner.c
new file mode 100644
index 0000000..99f7fde
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/spawner.c
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 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
+ *******************************************************************************/
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <jni.h>
+
+#include "exec0.h"
+#include <Spawner.h>
+
+
+#define DEBUGIT 0
+
+
+/*
+ * Header for class org_eclipse_cdt_utils_spawner_Spawner 
+ */
+
+
+#if DEBUGIT
+static void print_array(char **c_array)
+{
+    if (c_array) {
+        char **p = c_array;
+        for (; *p; p++) {
+            if (*p) {
+                fprintf(stderr, " %s", *p);
+            }
+        }
+    } else {
+        fprintf(stderr, "null");
+    }
+    fprintf(stderr, "\n");
+}
+#endif
+
+
+static char **alloc_c_array(JNIEnv * env, jobjectArray j_array)
+{
+    int i;
+    jint c_array_size = (*env)->GetArrayLength(env, j_array);
+    char **c_array = calloc(c_array_size + 1, sizeof(*c_array));
+
+    if (c_array == NULL)
+        return NULL;
+
+    for (i = 0; i < c_array_size; i++) {
+        jstring j_str =
+            (jstring) (*env)->GetObjectArrayElement(env, j_array, i);
+        const char *c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
+        c_array[i] = (char *) strdup(c_str);
+        (*env)->ReleaseStringUTFChars(env, j_str, c_str);
+        (*env)->DeleteLocalRef(env, j_str);
+    }
+
+    return c_array;
+}
+
+
+static void free_c_array(char **c_array)
+{
+    if (c_array) {
+        char **p = c_array;
+        for (; *p; p++) {
+            if (*p) {
+                free(*p);
+            }
+        }
+        free(c_array);
+    }
+}
+
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    exec2
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILorg/eclipse/cdt/utils/pty/PTY;)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2
+  (JNIEnv *env, jobject jobj, jobjectArray jcmd, jobjectArray jenv, jstring jdir, jintArray jchannels,
+   jstring jslaveName, jint masterFD, jboolean console)
+{
+	jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0);
+	const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
+	const char *pts_name = (*env)->GetStringUTFChars(env, jslaveName, NULL);
+	char **cmd = NULL;
+	char **envp = NULL;
+	int fd[3];
+	pid_t pid = -1;
+                                                                                                                             
+	if (channels == NULL)
+		goto bail_out;
+                                                                                                                             
+	cmd = alloc_c_array(env, jcmd);
+	if (cmd == NULL)
+		goto bail_out;
+ 
+	envp = alloc_c_array(env, jenv);
+	if (envp == NULL)
+		goto bail_out;
+ 
+#if DEBUGIT
+	fprintf(stderr, "command:");
+	print_array(cmd);
+	fprintf(stderr, "Envp:");
+	print_array(envp);
+	fprintf(stderr, "dirpath: %s\n", dirpath);
+	fprintf(stderr, "pts_name: %s\n", pts_name);
+#endif
+ 
+	pid = exec_pty(cmd[0], cmd, envp, dirpath, fd, pts_name, masterFD, console);
+	if (pid < 0)
+		goto bail_out;
+ 
+	channels[0] = fd[0];
+	channels[1] = fd[1];
+	channels[2] = fd[2];
+ 
+ bail_out:
+	(*env)->ReleaseIntArrayElements(env, jchannels, channels, 0);
+	(*env)->ReleaseStringUTFChars(env, jdir, dirpath);
+	(*env)->ReleaseStringUTFChars(env, jslaveName, pts_name);
+	if (cmd)
+		free_c_array(cmd);
+	if (envp)
+		free_c_array(envp);
+	return pid;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_exec1(JNIEnv * env, jobject jobj,
+                                               jobjectArray jcmd,
+                                               jobjectArray jenv,
+                                               jstring jdir)
+{
+    const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
+    char **cmd = NULL;
+    char **envp = NULL;
+    pid_t pid = -1;
+
+    cmd = alloc_c_array(env, jcmd);
+    if (cmd == NULL)
+        goto bail_out;
+
+    envp = alloc_c_array(env, jenv);
+    if (envp == NULL)
+        goto bail_out;
+
+#if DEBUGIT
+    fprintf(stderr, "command:");
+    print_array(cmd);
+    fprintf(stderr, "Envp:");
+    print_array(envp);
+    fprintf(stderr, "dirpath: %s\n", dirpath);
+#endif
+
+    pid = exec0(cmd[0], cmd, envp, dirpath, NULL);
+    if (pid < 0)
+        goto bail_out;
+
+  bail_out:
+    (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
+    if (cmd)
+        free_c_array(cmd);
+    if (envp)
+        free_c_array(envp);
+    return pid;
+}
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    exec0
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_exec0(JNIEnv * env, jobject jobj,
+                                               jobjectArray jcmd,
+                                               jobjectArray jenv,
+                                               jstring jdir,
+                                               jintArray jchannels)
+{
+    jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0);
+    const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
+    char **cmd = NULL;
+    char **envp = NULL;
+    int fd[3];
+    pid_t pid = -1;
+
+    if (channels == NULL)
+        goto bail_out;
+
+    cmd = alloc_c_array(env, jcmd);
+    if (cmd == NULL)
+        goto bail_out;
+
+    envp = alloc_c_array(env, jenv);
+    if (envp == NULL)
+        goto bail_out;
+
+#if DEBUGIT
+    fprintf(stderr, "command:");
+    print_array(cmd);
+    fprintf(stderr, "Envp:");
+    print_array(envp);
+    fprintf(stderr, "dirpath: %s\n", dirpath);
+#endif
+
+    pid = exec0(cmd[0], cmd, envp, dirpath, fd);
+    if (pid < 0)
+        goto bail_out;
+
+    channels[0] = fd[0];
+    channels[1] = fd[1];
+    channels[2] = fd[2];
+
+  bail_out:
+    (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0);
+    (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
+    if (cmd)
+        free_c_array(cmd);
+    if (envp)
+        free_c_array(envp);
+    return pid;
+}
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    raise
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_raise(JNIEnv * env, jobject jobj,
+                                               jint pid, jint sig)
+{
+    int status = -1;
+
+    switch (sig) {
+    case 0:                    /* NOOP */
+        status = kill(pid, 0);
+	break;
+
+    case 2:                    /* INTERRUPT */
+        status = kill(pid, SIGINT);
+	break;
+
+    case 9:                    /* KILL */
+        status = kill(pid, SIGKILL);
+	break;
+
+    case 15:                   /* TERM */
+        status = kill(pid, SIGTERM);
+	break;
+
+    default:
+        status = kill(pid, sig);        /* WHAT ?? */
+	break;
+    }
+
+    return status;
+}
+
+
+
+/*
+ * Class:     org_eclipse_cdt_utils_spawner_Spawner
+ * Method:    waitFor
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor(JNIEnv * env,
+                                                 jobject jobj, jint pid)
+{
+    return wait0(pid);
+}
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgpty.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgpty.jnilib
new file mode 100644
index 0000000..a7a98f9
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgpty.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgspawner.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgspawner.jnilib
new file mode 100644
index 0000000..96ae03d
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgspawner.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgpty.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgpty.jnilib
new file mode 100755
index 0000000..49ad7a7
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgpty.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgspawner.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgspawner.jnilib
new file mode 100755
index 0000000..1377d5d
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgspawner.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgpty.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgpty.jnilib
new file mode 100644
index 0000000..e0577a8
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgpty.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgspawner.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgspawner.jnilib
new file mode 100644
index 0000000..06c3818
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgspawner.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty/.classpath b/com.google.eclipse.elt.pty/.classpath
new file mode 100644
index 0000000..cbb71f6
--- /dev/null
+++ b/com.google.eclipse.elt.pty/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="utils"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.pty/.gitignore b/com.google.eclipse.elt.pty/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/com.google.eclipse.elt.pty/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/com.google.eclipse.elt.pty/.project b/com.google.eclipse.elt.pty/.project
new file mode 100644
index 0000000..3336d4a
--- /dev/null
+++ b/com.google.eclipse.elt.pty/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.eclipse.elt.pty</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.pty/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c537b63
--- /dev/null
+++ b/com.google.eclipse.elt.pty/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/com.google.eclipse.elt.pty/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..a57dfb7
--- /dev/null
+++ b/com.google.eclipse.elt.pty/META-INF/MANIFEST.MF
@@ -0,0 +1,18 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.google.eclipse.elt.pty;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Activator: com.google.eclipse.elt.pty.PtyPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: com.google.eclipse.elt.pty,
+ com.google.eclipse.elt.pty.internal,
+ com.google.eclipse.elt.pty.util,
+ org.eclipse.cdt.utils.pty,
+ org.eclipse.cdt.utils.spawner
+Require-Bundle: org.eclipse.core.resources,
+ org.eclipse.core.runtime;bundle-version="[3.7.0,4.0.0)",
+ com.ibm.icu;bundle-version="4.4.2"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
diff --git a/com.google.eclipse.elt.pty/build.properties b/com.google.eclipse.elt.pty/build.properties
new file mode 100644
index 0000000..b8073b7
--- /dev/null
+++ b/com.google.eclipse.elt.pty/build.properties
@@ -0,0 +1,19 @@
+###############################################################################
+#  Copyright (c) 2003, 2009 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
+###############################################################################
+bin.includes = plugin.properties,\
+               .,\
+               META-INF/
+source.. = src/,\
+           utils/
+
+jre.compilation.profile=JavaSE-1.6
+javacSource=1.6
+javacTarget=1.6
diff --git a/com.google.eclipse.elt.pty/plugin.properties b/com.google.eclipse.elt.pty/plugin.properties
new file mode 100644
index 0000000..8fb84bf
--- /dev/null
+++ b/com.google.eclipse.elt.pty/plugin.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2003, 2011 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
+#     Anton Leherbauer (Wind River Systems)
+###############################################################################
+pluginName=CDT's Pseudo-terminal Support
+providerName=Eclipse CDT
+
+fragmentName.linux = CDT's Pseudo-terminal Support for Linux
+fragmentName.linux.x86 = CDT's Pseudo-terminal Support for Linux (x86)
+fragmentName.linux.x86_64 = CDT's Pseudo-terminal Support for Linux (x86_64)
+fragmentName.macosx = CDT's Pseudo-terminal Support for MacOS X
diff --git a/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/PtyPlugin.java b/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/PtyPlugin.java
new file mode 100644
index 0000000..a2f4a34
--- /dev/null
+++ b/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/PtyPlugin.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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
+ *******************************************************************************/
+package com.google.eclipse.elt.pty;
+
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+import com.ibm.icu.text.MessageFormat;
+
+public class PtyPlugin extends Plugin {
+  public static final String PLUGIN_ID = "org.eclipse.cdt.core";
+
+  private static PtyPlugin plugin;
+  private static ResourceBundle resourceBundle;
+
+  static {
+    try {
+      resourceBundle = ResourceBundle.getBundle("com.google.eclipse.elt.pty.internal.PtyPluginResources");
+    } catch (MissingResourceException x) {
+      resourceBundle = null;
+    }
+  }
+
+  public PtyPlugin() {
+    plugin = this;
+  }
+
+  public static String getResourceString(String key) {
+    try {
+      return resourceBundle.getString(key);
+    } catch (MissingResourceException e) {
+      return "!" + key + "!";
+    } catch (NullPointerException e) {
+      return "#" + key + "#";
+    }
+  }
+
+  public static IWorkspace getWorkspace() {
+    return ResourcesPlugin.getWorkspace();
+  }
+
+  public static String getFormattedString(String key, String arg) {
+    return MessageFormat.format(getResourceString(key), new Object[] { arg });
+  }
+
+  public static String getFormattedString(String key, String[] args) {
+    return MessageFormat.format(getResourceString(key), (Object[]) args);
+  }
+
+  public static ResourceBundle getResourceBundle() {
+    return resourceBundle;
+  }
+
+  public static PtyPlugin getDefault() {
+    return plugin;
+  }
+
+  public static void log(Throwable e) {
+    String msg = e.getMessage();
+    if (msg == null) {
+      log("Error", e);
+    } else {
+      log("Error: " + msg, e);
+    }
+  }
+
+  public static void log(String message, Throwable e) {
+    log(createStatus(message, e));
+  }
+
+  public static IStatus createStatus(String message) {
+    return createStatus(message, null);
+  }
+
+  public static IStatus createStatus(String message, Throwable exception) {
+    return new Status(IStatus.ERROR, PLUGIN_ID, message, exception);
+  }
+
+  public static void log(IStatus status) {
+    getDefault().getLog().log(status);
+  }
+}
diff --git a/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/internal/PtyPluginResources.properties b/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/internal/PtyPluginResources.properties
new file mode 100644
index 0000000..62d2d05
--- /dev/null
+++ b/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/internal/PtyPluginResources.properties
@@ -0,0 +1,11 @@
+###############################################################################
+# Copyright (c) 2000, 2010, 2012 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
+###############################################################################
+Util.exception.cannotCreatePty=Cannot create pty
+Util.exception.cannotSetTerminalSize=Setting terminal size is not supported
+Util.error.cannotRun=Cannot run program "{0}": {1}
+Util.exception.closeError=close error
\ No newline at end of file
diff --git a/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Platform.java b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Platform.java
new file mode 100644
index 0000000..cf9c871
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Platform.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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
+ *******************************************************************************/
+package com.google.eclipse.elt.pty.util;
+
+import java.io.*;
+
+import org.osgi.framework.Bundle;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+public final class Platform {
+  // This class duplicates all of the methods in org.eclipse.core.runtime.Platform
+  // that are used by the CDT. getOSArch() needs a few tweaks because the value returned
+  // by org.eclipse.core.runtime.Platform.getOSArch represents what the JVM thinks the
+  // architecture is. In some cases, we may actually be running on a 64-bit machine,
+  // but the JVM thinks it's running on a 32-bit machine. Without this change, the CDT
+  // will not handle 64-bit executables on some ppc64. This method could easily be
+  // extended to handle other platforms with similar issues.
+  //
+  // Unfortunately, the org.eclipse.core.runtime.Platform is final, so we cannot just
+  // extend it and and then override the getOSArch method, so getBundle and getOS just
+  // encapsulate calls to the same methods in org.eclipse.core.runtime.Platform.
+
+  public static final String OS_LINUX = org.eclipse.core.runtime.Platform.OS_LINUX;
+
+  private static String cachedArch = null;
+
+  public static Bundle getBundle(String symbolicName) {
+    return org.eclipse.core.runtime.Platform.getBundle(symbolicName);
+  }
+
+  public static String getOS() {
+    return org.eclipse.core.runtime.Platform.getOS();
+  }
+
+  public static String getOSArch() {
+    if (cachedArch == null) {
+      String arch = org.eclipse.core.runtime.Platform.getOSArch();
+      if (arch.equals(org.eclipse.core.runtime.Platform.ARCH_PPC)) {
+        // Determine if the platform is actually a ppc64 machine
+        Process unameProcess;
+        String cmd[] = { "uname", "-p" };
+        try {
+          unameProcess = Runtime.getRuntime().exec(cmd);
+          InputStreamReader inputStreamReader = new InputStreamReader(unameProcess.getInputStream());
+          BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+          String unameOutput = bufferedReader.readLine();
+          if (unameOutput != null) {
+            arch = unameOutput;
+          }
+          bufferedReader.close();
+          unameProcess.waitFor(); // otherwise the process becomes a zombie
+        } catch (IOException e) {
+          PtyPlugin.log(e);
+        } catch (InterruptedException exc) {
+          // restore interrupted flag
+          Thread.currentThread().interrupt();
+        }
+      } else if (arch.equals(org.eclipse.core.runtime.Platform.ARCH_X86)) {
+        // Determine if the platform is actually a x86_64 machine
+        Process unameProcess;
+        String cmd[];
+        if (org.eclipse.core.runtime.Platform.OS_WIN32.equals(getOS())) {
+          cmd = new String[] { "cmd", "/d", "/c", "set", "PROCESSOR_ARCHITECTURE" };
+        } else {
+          // We don't use "uname -p" since it returns "unknown" on some Linux systems.
+          cmd = new String[] { "uname", "-m" };
+        }
+        try {
+          unameProcess = Runtime.getRuntime().exec(cmd);
+          unameProcess.getOutputStream().close();
+          unameProcess.getErrorStream().close();
+          InputStreamReader inputStreamReader = new InputStreamReader(unameProcess.getInputStream());
+          BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+          String unameOutput = bufferedReader.readLine();
+          if (unameOutput != null && unameOutput.endsWith("64")) {
+            arch = org.eclipse.core.runtime.Platform.ARCH_X86_64;
+          }
+          bufferedReader.close();
+          unameProcess.waitFor(); // otherwise the process becomes a zombie
+        } catch (IOException e) {
+          PtyPlugin.log(e);
+        } catch (InterruptedException e) {
+          // restore interrupted flag
+          Thread.currentThread().interrupt();
+        }
+      }
+      cachedArch = arch;
+    }
+    return cachedArch;
+  }
+
+  private Platform() {}
+}
diff --git a/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Processes.java b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Processes.java
new file mode 100644
index 0000000..70cc4c3
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Processes.java
@@ -0,0 +1,41 @@
+package com.google.eclipse.elt.pty.util;
+
+import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.cdt.utils.spawner.ProcessFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Utility methods related to processes.
+ */
+public final class Processes {
+  /**
+   * Starts a new process.
+   * @param command the command to execute.
+   * @param environment the environment variables.
+   * @param workingDirectory the working directory.
+   * @return the new process.
+   * @throws IOException if something went wrong.
+   */
+  public static Process executeInPty(
+      String[] command, Map<String, String> environment, File workingDirectory) throws IOException {
+    PTY pty = new PTY(true);
+    ProcessFactory processFactory = ProcessFactory.getFactory();
+    return processFactory.exec(command, toArray(environment), workingDirectory, pty);
+  }
+
+  private static String[] toArray(Map<String, String> environmentMap) {
+    List<String> environment = new ArrayList<String>();
+    for (Entry<String, String> entry : environmentMap.entrySet()) {
+      environment.add(entry.getKey() + "=" + entry.getValue());
+    }
+    return environment.toArray(new String[environment.size()]);
+  }
+
+  private Processes() {}
+}
diff --git a/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/spawner/EnvironmentReader.java b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/spawner/EnvironmentReader.java
new file mode 100644
index 0000000..214cb0f
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/spawner/EnvironmentReader.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package com.google.eclipse.elt.pty.util.spawner;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * This class provides environment variables supplied as {@link Properties} class.
+ */
+public final class EnvironmentReader {
+  private static Properties envVars = null;
+  private static Properties envVarsNormalized = null;
+  private static ArrayList<String> rawVars = null;
+
+  private static synchronized void init() {
+    if (envVars == null) {
+      envVars = new Properties();
+      // on Windows environment variable names are case-insensitive
+      if (Platform.getOS().equals(Platform.OS_WIN32)) {
+        envVarsNormalized = new Properties();
+      } else {
+        envVarsNormalized = envVars;
+      }
+      rawVars = new ArrayList<String>();
+      Map<String, String> envMap = System.getenv();
+      for (String var : envMap.keySet()) {
+        String value = envMap.get(var);
+        envVars.setProperty(var, value);
+        if (envVarsNormalized != envVars) {
+          envVarsNormalized.setProperty(var.toUpperCase(), value);
+        }
+        rawVars.add(var + "=" + value);
+      }
+      rawVars.trimToSize();
+    }
+  }
+
+  public static Properties getEnvVars() {
+    init();
+    return (Properties) envVars.clone();
+  }
+
+  public static String getEnvVar(String key) {
+    init();
+    return envVarsNormalized.getProperty(key);
+  }
+
+  private EnvironmentReader() {}
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTY.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTY.java
new file mode 100644
index 0000000..a51a8e1
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTY.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.cdt.utils.pty;
+
+import java.io.IOException;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+/**
+ * PTY - pseudo terminal support.
+ */
+public class PTY {
+  static final String LIBRARY_NAME = "gpty";
+
+  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)}.
+   * @throws IOException if something goes wrong.
+   */
+  public PTY() throws IOException {
+    this(true);
+  }
+
+  /**
+   * Create pseudo-terminal.
+   *
+   * <p>
+   * The provided flag indicates whether the pseudo terminal is used with the interactive Eclipse console:
+   * <ul>
+   * <li>If {@code true} the terminal is configured with no echo and stderr is redirected to a pipe instead of the
+   * PTY.</li>
+   * <li>If {@code 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: <code>linux-x86</code>, <code>linux-x86_64</code>,
+   * <code>solaris-sparc</code>, <code>macosx</code>.</li>
+   * </ul>
+   * </p>
+   *
+   * @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(PtyPlugin.getResourceString("Util.exception.cannotCreatePty"));
+    }
+
+    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.
+   */
+  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.
+   * <p>
+   * This should only be used when the pseudo terminal is configured for use with a terminal emulation, i.e. when
+   * {@link #isConsole()} returns {@code false}.
+   * </p>
+   * <p>
+   * <strong>Note:</strong> This method may not be supported on all platforms. Known platforms which support this method
+   * are: {@code linux-x86}, {@code linux-x86_64}, {@code solaris-sparc}, {@code macosx}.
+   * </p>
+   * @param width the given width.
+   * @param height the given height.
+   */
+  public final void setTerminalSize(int width, int height) {
+    try {
+      change_window_size(master, width, height);
+    } catch (UnsatisfiedLinkError e) {
+      if (!setTerminalSizeErrorAlreadyLogged) {
+        setTerminalSizeErrorAlreadyLogged = true;
+        PtyPlugin.log(PtyPlugin.getResourceString("Util.exception.cannotSetTerminalSize"), e);
+      }
+    }
+  }
+
+  public static boolean isSupported() {
+    return hasPTY;
+  }
+
+  native String openMaster(boolean console);
+
+  native int change_window_size(int fdm, int width, int height);
+
+  static {
+    System.loadLibrary(LIBRARY_NAME);
+    hasPTY = true;
+  }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYInputStream.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYInputStream.java
new file mode 100644
index 0000000..a2f1a1e
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYInputStream.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.cdt.utils.pty;
+
+import java.io.*;
+
+import org.eclipse.cdt.utils.pty.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];
+  }
+
+  @Override public int read(byte[] buf, int off, int len) throws IOException {
+    if (buf == null) {
+      throw new NullPointerException();
+    }
+    if ((off < 0) || (off > buf.length) || (len < 0) || ((off + len) > buf.length) || ((off + len) < 0)) {
+      throw new IndexOutOfBoundsException();
+    }
+    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;
+  }
+
+  @Override public void close() throws IOException {
+    if (master.getFD() == -1) {
+      return;
+    }
+    close0(master.getFD());
+    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.LIBRARY_NAME);
+  }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYOutputStream.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYOutputStream.java
new file mode 100644
index 0000000..265fd26
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYOutputStream.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.cdt.utils.pty;
+
+import java.io.*;
+
+import org.eclipse.cdt.utils.pty.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;
+  }
+
+  @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);
+  }
+
+  @Override public void write(int b) throws IOException {
+    byte[] buf = new byte[1];
+    buf[0] = (byte) b;
+    write(buf, 0, 1);
+  }
+
+  @Override public void close() throws IOException {
+    if (master.getFD() == -1) {
+      return;
+    }
+    int status = close0(master.getFD());
+    if (status == -1) {
+      throw new IOException("Close error");
+    }
+    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.LIBRARY_NAME);
+  }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/ProcessFactory.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/ProcessFactory.java
new file mode 100644
index 0000000..d01c706
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/ProcessFactory.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.cdt.utils.spawner;
+
+import java.io.*;
+
+import org.eclipse.cdt.utils.pty.PTY;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+public class ProcessFactory {
+  static private ProcessFactory instance;
+
+  private boolean hasSpawner;
+  private final Runtime runtime;
+
+  private ProcessFactory() {
+    hasSpawner = false;
+    String OS = System.getProperty("os.name").toLowerCase();
+    runtime = Runtime.getRuntime();
+    // Spawner does not work for Windows 98 fallback
+    if (OS != null && OS.equals("windows 98")) {
+      hasSpawner = false;
+    } else {
+      System.loadLibrary(Spawner.LIBRARY_NAME);
+      hasSpawner = true;
+    }
+  }
+
+  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(PtyPlugin.getResourceString("Util.exception.cannotCreatePty"));
+  }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/Spawner.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/Spawner.java
new file mode 100644
index 0000000..bf63ab6
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/Spawner.java
@@ -0,0 +1,438 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.cdt.utils.spawner;
+
+import java.io.*;
+import java.util.StringTokenizer;
+
+import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.osgi.util.NLS;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+public class Spawner extends Process {
+  static final String LIBRARY_NAME = "gspawner";
+
+  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.
+   *
+   * <ul>
+   * <li>If the process this is being raised against was launched by us (the Spawner)
+   * <ul>
+   * <li>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
+   * <li>If the process is <i>not</i> a cygwin program, send the process a CTRL-C
+   * </ul>
+   * <li>If the process this is being raised against was <i>not</i> launched by us, use DebugBreakProcess to interrupt
+   * it (sending a CTRL-C is easy only if we share a console with the target process)
+   * </ul>
+   *
+   * 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], ".");
+    } else {
+      exec(cmdarray, new String[0], ".");
+    }
+  }
+
+  protected Spawner(String[] command, String[] environment, File workingDirectory) throws IOException {
+    String dirpath = ".";
+    if (workingDirectory != null) {
+      dirpath = workingDirectory.getAbsolutePath();
+    }
+    exec(command, environment, dirpath);
+  }
+
+  protected Spawner(String[] cmdarray, String[] envp, File dir, PTY pty) throws IOException {
+    String dirpath = ".";
+    if (dir != null) {
+      dirpath = dir.getAbsolutePath();
+    }
+    fPty = pty;
+    exec_pty(cmdarray, envp, dirpath, pty);
+  }
+
+  protected Spawner(String command) throws IOException {
+    this(command, null);
+  }
+
+  protected Spawner(String[] command) throws IOException {
+    this(command, null);
+  }
+
+  protected Spawner(String[] command, String[] environment) throws IOException {
+    this(command, environment, null);
+  }
+
+  protected Spawner(String command, String[] environment) throws IOException {
+    this(command, environment, null);
+  }
+
+  protected Spawner(String command, String[] environment, File workingDirectory) 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 = ".";
+    if (workingDirectory != null) {
+      dirpath = workingDirectory.getAbsolutePath();
+    }
+    exec(cmdarray, environment, 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");
+    }
+    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();
+    }
+  }
+
+  public int interrupt() {
+    return raise(pid, INT);
+  }
+
+  public int interruptCTRLC() {
+    if (Platform.getOS().equals(Platform.OS_WIN32)) {
+      return raise(pid, CTRLC);
+    }
+    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) {
+          Thread.currentThread().interrupt();
+        }
+      }
+    }
+    // Check for errors.
+    if (pid == -1) {
+      throw new IOException(reaper.getErrorMessage());
+    }
+  }
+
+  private void exec_pty(String[] command, String[] environment, String workingDirectory, PTY pty) throws IOException {
+    String cmd = command[0];
+    SecurityManager s = System.getSecurityManager();
+    if (s != null) {
+      s.checkExec(cmd);
+    }
+    if (environment == null) {
+      environment = 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(command, environment, workingDirectory) {
+      @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) {
+          Thread.currentThread().interrupt();
+        }
+      }
+    }
+    if (pid == -1) {
+      throw new IOException("Exec_tty error:" + reaper.getErrorMessage());
+    }
+  }
+
+  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");
+    }
+    fChannels[0] = -1;
+    fChannels[1] = -1;
+    fChannels[2] = -1;
+  }
+
+  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 int exec0(String[] cmdarray, String[] envp, String dir, int[] chan) throws IOException;
+
+  native int exec1(String[] cmdarray, String[] envp, String dir) throws IOException;
+
+  native int exec2(String[] cmdarray, String[] envp, String dir, int[] chan, String slaveName, int masterFD,
+      boolean console) throws IOException;
+
+  public native int raise(int processID, int sig);
+
+  native int waitFor(int processID);
+
+  static {
+    System.loadLibrary(LIBRARY_NAME);
+  }
+
+  // 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 then wait in the
+  // same thread.
+  class Reaper extends Thread {
+    String[] command;
+    String[] environment;
+    String dirPath;
+    volatile Throwable exception;
+
+    public Reaper(String[] command, String[] environment, String dirPath) {
+      super("Spawner Reaper");
+      this.command = command;
+      this.environment = environment;
+      this.dirPath = dirPath;
+      exception = 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(command, environment, dirPath, fChannels);
+      } catch (Exception e) {
+        pid = -1;
+        exception = 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 = exception != null ? exception.getMessage() : "Unknown reason";
+      return NLS.bind(PtyPlugin.getResourceString("Util.error.cannotRun"), command[0], reason);
+    }
+  }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerInputStream.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerInputStream.java
new file mode 100644
index 0000000..37f5566
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerInputStream.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.cdt.utils.spawner;
+
+import java.io.*;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+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;
+  }
+
+  @Override public int read() throws IOException {
+    byte b[] = new byte[1];
+    if (1 != read(b, 0, 1)) {
+      return -1;
+    }
+    return b[0];
+  }
+
+  @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;
+  }
+
+  @Override public void close() throws IOException {
+    if (fd == -1) {
+      return;
+    }
+    int status = close0(fd);
+    if (status == -1) {
+      throw new IOException(PtyPlugin.getResourceString("Util.exception.closeError"));
+    }
+    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.LIBRARY_NAME);
+  }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerOutputStream.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerOutputStream.java
new file mode 100644
index 0000000..7ecc61e
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerOutputStream.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.cdt.utils.spawner;
+
+import java.io.*;
+
+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;
+  }
+
+  @Override public void write(byte[] b, int off, int len) throws IOException {
+    if (b == null) {
+      throw new NullPointerException();
+    }
+    if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
+      throw new IndexOutOfBoundsException();
+    }
+    if (len == 0) {
+      return;
+    }
+    byte[] tmpBuf = new byte[len];
+    System.arraycopy(b, off, tmpBuf, off, len);
+    write0(fd, tmpBuf, len);
+  }
+
+  @Override public void write(int b) throws IOException {
+    byte[] buf = new byte[1];
+    buf[0] = (byte) b;
+    write(buf, 0, 1);
+  }
+
+  @Override public void close() throws IOException {
+    if (fd == -1) {
+      return;
+    }
+    int status = close0(fd);
+    if (status == -1) {
+      throw new IOException("close error");
+    }
+    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.LIBRARY_NAME);
+  }
+}
diff --git a/com.google.eclipse.elt.view/.classpath b/com.google.eclipse.elt.view/.classpath
new file mode 100644
index 0000000..ad32c83
--- /dev/null
+++ b/com.google.eclipse.elt.view/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.view/.project b/com.google.eclipse.elt.view/.project
new file mode 100644
index 0000000..8264399
--- /dev/null
+++ b/com.google.eclipse.elt.view/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>com.google.eclipse.elt.view</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.view/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.view/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c537b63
--- /dev/null
+++ b/com.google.eclipse.elt.view/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/com.google.eclipse.elt.view/META-INF/MANIFEST.MF b/com.google.eclipse.elt.view/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..70e286f
--- /dev/null
+++ b/com.google.eclipse.elt.view/META-INF/MANIFEST.MF
@@ -0,0 +1,15 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Terminal
+Bundle-SymbolicName: com.google.eclipse.elt.view;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Activator: com.google.eclipse.elt.view.Activator
+Bundle-Vendor: Google, Inc.
+Require-Bundle: com.google.eclipse.elt.emulator;bundle-version="1.1.0",
+ com.google.eclipse.elt.pty;bundle-version="1.1.0",
+ org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.debug.core,
+ org.eclipse.jface.text
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
diff --git a/com.google.eclipse.elt.view/build.properties b/com.google.eclipse.elt.view/build.properties
new file mode 100644
index 0000000..6c480f3
--- /dev/null
+++ b/com.google.eclipse.elt.view/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               icons/
diff --git a/com.google.eclipse.elt.view/icons/change_title.gif b/com.google.eclipse.elt.view/icons/change_title.gif
new file mode 100644
index 0000000..efc77a7
--- /dev/null
+++ b/com.google.eclipse.elt.view/icons/change_title.gif
Binary files differ
diff --git a/com.google.eclipse.elt.view/icons/new_terminal.gif b/com.google.eclipse.elt.view/icons/new_terminal.gif
new file mode 100644
index 0000000..80201e0
--- /dev/null
+++ b/com.google.eclipse.elt.view/icons/new_terminal.gif
Binary files differ
diff --git a/com.google.eclipse.elt.view/icons/scroll_lock.gif b/com.google.eclipse.elt.view/icons/scroll_lock.gif
new file mode 100644
index 0000000..68fd6cf
--- /dev/null
+++ b/com.google.eclipse.elt.view/icons/scroll_lock.gif
Binary files differ
diff --git a/com.google.eclipse.elt.view/icons/terminal.gif b/com.google.eclipse.elt.view/icons/terminal.gif
new file mode 100644
index 0000000..557d001
--- /dev/null
+++ b/com.google.eclipse.elt.view/icons/terminal.gif
Binary files differ
diff --git a/com.google.eclipse.elt.view/plugin.xml b/com.google.eclipse.elt.view/plugin.xml
new file mode 100644
index 0000000..8ae47d4
--- /dev/null
+++ b/com.google.eclipse.elt.view/plugin.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+  <extension point="org.eclipse.ui.views">
+    <view
+      allowMultiple="true"
+      category="org.eclipse.ui"
+      class="com.google.eclipse.elt.view.ui.TerminalView"
+      icon="icons/terminal.gif"
+      id="com.google.eclipse.terminal.local.localTerminalView"
+      name="Terminal"
+      restorable="true">
+    </view>
+  </extension>
+  <extension point="org.eclipse.ui.perspectiveExtensions">
+    <perspectiveExtension targetID="*">
+      <view
+        id="com.google.eclipse.terminal.local.localTerminalView"
+        minimized="false"
+        relationship="stack"
+        relative="org.eclipse.ui.console.ConsoleView">
+      </view>
+    </perspectiveExtension>
+  </extension>
+  <extension point="org.eclipse.ui.preferencePages">
+    <page
+      category="com.google.eclipse.terminal.local.page.root"
+      class="com.google.eclipse.elt.view.preferences.ColorsAndFontsPreferencePage"
+      id="com.google.eclipse.terminal.local.page.colorsAndFonts"
+      name="Colors and Fonts">
+    </page>
+    <page
+      class="com.google.eclipse.elt.view.preferences.RootPreferencePage"
+      id="com.google.eclipse.terminal.local.page.root"
+      name="Terminal">
+    </page>
+  </extension>
+  <extension point="org.eclipse.core.runtime.preferences">
+    <initializer class="com.google.eclipse.elt.view.preferences.PreferenceInitializer">
+    </initializer>
+  </extension>
+  <extension point="org.eclipse.ui.commands">
+    <command
+      categoryId="org.eclipse.ui.category.views"
+      id="com.google.eclipse.terminal.local.open"
+      name="Open Terminal Here">
+    </command>
+  </extension>
+  <extension point="org.eclipse.ui.commandImages">
+    <image
+      commandId="com.google.eclipse.terminal.local.open"
+      icon="icons/terminal.gif">
+    </image>
+  </extension>
+  <extension point="org.eclipse.ui.menus">
+    <menuContribution
+      allPopups="false"
+      locationURI="popup:org.eclipse.ui.popup.any?after=additions">
+      <command
+        commandId="com.google.eclipse.terminal.local.open"
+        style="push">
+        <visibleWhen checkEnabled="false">
+          <and>
+            <with variable="selection">
+              <count value="1">
+              </count>
+            </with>
+            <iterate>
+              <adapt type="org.eclipse.core.resources.IResource">
+              </adapt>
+            </iterate>
+          </and>
+        </visibleWhen>
+      </command>
+    </menuContribution>
+  </extension>
+  <extension point="org.eclipse.ui.handlers">
+    <handler
+      class="com.google.eclipse.elt.view.command.OpenTerminalCommand"
+      commandId="com.google.eclipse.terminal.local.open">
+    </handler>
+  </extension>
+  <extension point="org.eclipse.ui.contexts">
+    <context
+      description="In Terminal"
+      id="com.google.eclipse.terminal.local.context.localTerminal"
+      name="Terminal"
+      parentId="org.eclipse.ui.contexts.window">
+    </context>
+  </extension>
+  <extension point="org.eclipse.ui.commands">
+    <category
+      id="com.google.eclipse.terminal.local.commands.category"
+      name="Terminal">
+    </category>
+    <command
+      categoryId="com.google.eclipse.terminal.local.commands.category"
+      id="com.google.eclipse.terminal.local.copy"
+      name="Copy">
+    </command>
+    <command
+      categoryId="com.google.eclipse.terminal.local.commands.category"
+      id="com.google.eclipse.terminal.local.paste"
+      name="Paste">
+    </command>
+  </extension>
+  <extension point="org.eclipse.ui.bindings">
+    <key
+      commandId="com.google.eclipse.terminal.local.copy"
+      contextId="com.google.eclipse.terminal.local.context.localTerminal"
+      platform="carbon"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="M1+C">
+    </key>
+    <key
+      commandId="com.google.eclipse.terminal.local.copy"
+      contextId="com.google.eclipse.terminal.local.context.localTerminal"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="M1+M2+C">
+    </key>
+    <key
+      commandId="com.google.eclipse.terminal.local.paste"
+      contextId="com.google.eclipse.terminal.local.context.localTerminal"
+      platform="carbon"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="M1+V">
+    </key>
+    <key
+      commandId="com.google.eclipse.terminal.local.paste"
+      contextId="com.google.eclipse.terminal.local.context.localTerminal"
+      schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+      sequence="M1+M2+V">
+    </key>
+  </extension>
+</plugin>
\ No newline at end of file
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/Activator.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/Activator.java
new file mode 100644
index 0000000..b5d42ee
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/Activator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view;
+
+import static org.eclipse.core.runtime.IStatus.*;
+
+import static com.google.eclipse.elt.view.ImageKeys.*;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.*;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class Activator extends AbstractUIPlugin {
+  public static final String PLUGIN_ID = "com.google.eclipse.terminal.local";
+
+  private static Activator plugin;
+
+  @Override public void start(BundleContext context) throws Exception {
+    super.start(context);
+    plugin = this;
+  }
+
+  @Override public void stop(BundleContext context) throws Exception {
+    plugin = null;
+    super.stop(context);
+  }
+
+  @Override protected void initializeImageRegistry(ImageRegistry registry) {
+    addImage(registry, CHANGE_TITLE, "icons/change_title.gif");
+    addImage(registry, NEW_TERMINAL, "icons/new_terminal.gif");
+    addImage(registry, SCROLL_LOCK, "icons/scroll_lock.gif");
+  }
+
+  private void addImage(ImageRegistry registry, String key, String path) {
+    URL imageUrl = instance().getBundle().getEntry(path);
+    registry.put(key, ImageDescriptor.createFromURL(imageUrl));
+
+  }
+
+  public static void log(String message, Throwable cause) {
+    log(new Status(ERROR, PLUGIN_ID, OK, message, cause));
+  }
+
+  public static void log(IStatus status) {
+    instance().getLog().log(status);
+  }
+
+  public static ImageDescriptor imageDescriptor(String key) {
+    return instance().getImageRegistry().getDescriptor(key);
+  }
+
+  public static Activator instance() {
+    return plugin;
+  }
+
+  public static IPreferenceStore preferenceStore() {
+    return instance().getPreferenceStore();
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ImageKeys.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ImageKeys.java
new file mode 100644
index 0000000..661c02e
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ImageKeys.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public final class ImageKeys {
+  public static final String CHANGE_TITLE = "changeTitle";
+  public static final String NEW_TERMINAL = "newTerminal";
+  public static final String SCROLL_LOCK = "scrollLock";
+
+  private ImageKeys() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/command/OpenTerminalCommand.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/command/OpenTerminalCommand.java
new file mode 100644
index 0000000..3b83cda
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/command/OpenTerminalCommand.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.command;
+
+import static com.google.eclipse.elt.view.ui.TerminalView.openTerminalView;
+
+import org.eclipse.core.commands.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class OpenTerminalCommand extends AbstractHandler {
+
+  @Override public Object execute(ExecutionEvent event) {
+    ISelection selection = HandlerUtil.getCurrentSelection(event);
+    IResource target = resourceFrom(selection);
+    if (target != null) {
+      if (!(target instanceof IContainer)) {
+        target = target.getParent();
+      }
+      openTerminalView(target.getLocation());
+    }
+    return null;
+  }
+
+  private IResource resourceFrom(ISelection selection) {
+    if (!(selection instanceof IStructuredSelection)) {
+      return null;
+    }
+    Object o = ((IStructuredSelection) selection).getFirstElement();
+    if (!(o instanceof IAdaptable)) {
+      return null;
+    }
+    return (IResource) ((IAdaptable) o).getAdapter(IResource.class);
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LifeCycleListener.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LifeCycleListener.java
new file mode 100644
index 0000000..7c7273f
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LifeCycleListener.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.connector;
+
+/**
+ * Listens for events related to the life cycle of a pseudo-terminal process.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public interface LifeCycleListener {
+  /**
+   * Notification that the execution pseudo-terminal process has finished.
+   */
+  void executionFinished();
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LocalTerminalConnector.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LocalTerminalConnector.java
new file mode 100644
index 0000000..0979c4a
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LocalTerminalConnector.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.connector;
+
+import static org.eclipse.core.runtime.IStatus.*;
+
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.*;
+import static com.google.eclipse.elt.pty.util.Platform.*;
+import static com.google.eclipse.elt.view.Activator.*;
+import static com.google.eclipse.elt.view.connector.Messages.*;
+import static com.google.eclipse.elt.view.connector.PseudoTerminal.isPlatformSupported;
+import static com.google.eclipse.elt.view.util.Platform.*;
+
+import java.io.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.debug.core.IStreamListener;
+import org.eclipse.debug.core.model.IStreamMonitor;
+import org.eclipse.debug.internal.core.StreamsProxy;
+import org.eclipse.osgi.util.NLS;
+
+import com.google.eclipse.elt.emulator.connector.TerminalConnector;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+import com.google.eclipse.elt.emulator.provisional.api.provider.TerminalConnectorDelegate;
+
+/**
+ * Connector to local terminal.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@SuppressWarnings("restriction") // StreamsProxy is internal API
+public class LocalTerminalConnector extends TerminalConnectorDelegate implements LifeCycleListener {
+  private static final String ID = "com.google.eclipse.terminal.local.core.connector";
+
+  public static ITerminalConnector createLocalTerminalConnector(final String encoding) {
+    TerminalConnector.Factory factory = new TerminalConnector.Factory(){
+      @Override public TerminalConnectorDelegate makeConnector() {
+        return new LocalTerminalConnector(encoding);
+      }
+    };
+    TerminalConnector connector = new TerminalConnector(factory, ID, localTerminalName);
+    String errorMessage = connector.getInitializationErrorMessage();
+    if (errorMessage != null) {
+      throw new IllegalStateException(errorMessage);
+    }
+    return connector;
+  }
+
+  private IPath workingDirectory;
+  private PseudoTerminal pseudoTerminal;
+
+  private StreamsProxy streamsProxy;
+  private OutputStream terminalToRemoteStream;
+
+  private final String encoding;
+
+  private LocalTerminalConnector(String encoding) {
+    this.encoding = encoding;
+  }
+
+  /**
+   * Verifies that PTY support is available on this platform.
+   * @throws CoreException if PTY support is <strong>not</strong> available on this platform.
+   * @see TerminalConnectorDelegate#initialize()
+   */
+  @Override public void initialize() throws CoreException {
+    if (!isPlatformSupported()) {
+      String message = NLS.bind(errorNoPseudoTerminalSupport, getOS(), getOSArch());
+      throw new CoreException(new Status(WARNING, PLUGIN_ID, message));
+    }
+  }
+
+  @Override protected void connect() {
+    terminalControl.setState(CONNECTING);
+    File workingDirectory = workingDirectory();
+    pseudoTerminal = new PseudoTerminal(workingDirectory);
+    pseudoTerminal.addLifeCycleListener(this);
+    try {
+      pseudoTerminal.launch();
+      streamsProxy = new StreamsProxy(pseudoTerminal.systemProcess(), encoding);
+      terminalToRemoteStream = new BufferedOutputStream(new TerminalOutputStream(streamsProxy, encoding), 1024);
+      addListeners(terminalControl, streamsProxy.getOutputStreamMonitor(), streamsProxy.getErrorStreamMonitor());
+      if (streamsProxy != null) {
+        terminalControl.setState(CONNECTED);
+        return;
+      }
+    } catch (Throwable t) {
+      log(new Status(INFO, PLUGIN_ID, OK, "Unable to start terminal", t));
+    }
+    terminalControl.setState(CLOSED);
+  }
+
+  private File workingDirectory() {
+    IPath path = (workingDirectory != null) ? workingDirectory : userHomeDirectory();
+    if (path == null) {
+      return null;
+    }
+    File file = path.toFile();
+    return (file.isDirectory()) ? file : null;
+  }
+
+  private void addListeners(ITerminalControl control, IStreamMonitor...monitors) throws UnsupportedEncodingException {
+    for (IStreamMonitor monitor : monitors) {
+      addListener(monitor, new TerminalOutputListener(control, encoding));
+    }
+  }
+
+  private void addListener(IStreamMonitor monitor, IStreamListener listener) {
+    monitor.addListener(listener);
+    listener.streamAppended(monitor.getContents(), monitor);
+  }
+
+  @Override public OutputStream getTerminalToRemoteStream() {
+    return terminalToRemoteStream;
+  }
+
+  /**
+   * Returns the system's default shell location as the settings summary.
+   * @return the system's default shell location as the settings summary.
+   */
+  @Override public String getSettingsSummary() {
+    return defaultShell().toString();
+  }
+
+  /**
+   * Notifies the pseudo-terminal that the size of the terminal has changed.
+   * @param newWidth the new terminal width (in columns.)
+   * @param newHeight the new terminal height (in lines.)
+   */
+  @Override public void setTerminalSize(int newWidth, int newHeight) {
+    if (pseudoTerminal != null) {
+      pseudoTerminal.updateSize(newWidth, newHeight);
+    }
+  }
+
+  public void setWorkingDirectory(IPath workingDirectory) {
+    this.workingDirectory = workingDirectory;
+  }
+
+  @Override protected void onDisconnect() {
+    pseudoTerminal.disconnect();
+  }
+
+  @Override public void executionFinished() {
+    terminalControl.setState(CLOSED);
+    if (streamsProxy != null) {
+      streamsProxy.close();
+    }
+  }
+
+  public void addLifeCycleListener(LifeCycleListener listener) {
+    pseudoTerminal.addLifeCycleListener(listener);
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.java
new file mode 100644
index 0000000..53ad98e
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.connector;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class Messages extends NLS {
+  public static String localTerminalName;
+  public static String errorNoPseudoTerminalSupport;
+
+  static {
+    Class<Messages> type = Messages.class;
+    NLS.initializeMessages(type.getName(), type);
+  }
+
+  private Messages() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.properties b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.properties
new file mode 100644
index 0000000..e9586a6
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.properties
@@ -0,0 +1,2 @@
+localTerminalName=Terminal
+errorNoPseudoTerminalSupport=Pseudo-terminal support is not available on your host ({0}.{1})
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/PseudoTerminal.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/PseudoTerminal.java
new file mode 100644
index 0000000..6082aff
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/PseudoTerminal.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.connector;
+
+import static java.lang.Thread.currentThread;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+
+import static com.google.eclipse.elt.view.util.Platform.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+
+import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.cdt.utils.spawner.ProcessFactory;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class PseudoTerminal {
+  private final List<LifeCycleListener> lifeCycleListeners = new ArrayList<LifeCycleListener>();
+  private final ExecutorService executor = newSingleThreadExecutor();
+
+  private final File workingDirectory;
+
+  private Process process;
+  private PTY pty;
+
+  private int width;
+  private int height;
+
+  PseudoTerminal(File workingDirectory) {
+    this.workingDirectory = workingDirectory;
+  }
+
+  void launch() throws IOException {
+    ProcessFactory factory = ProcessFactory.getFactory();
+    pty = new PTY(false);
+    process = factory.exec(command(), environment(), workingDirectory, pty);
+    executor.execute(new Runnable() {
+      @Override public void run() {
+        try {
+          process.waitFor();
+        } catch (InterruptedException ignored) {
+          currentThread().interrupt();
+        } finally {
+          notifyExecutionFinished();
+        }
+      }
+    });
+  }
+
+  private String[] command() {
+    return new String[] { defaultShell().getAbsolutePath(), "-i" };
+  }
+
+  private void notifyExecutionFinished() {
+    for (LifeCycleListener listener : lifeCycleListeners) {
+      listener.executionFinished();
+    }
+  }
+
+  void addLifeCycleListener(LifeCycleListener listener) {
+    lifeCycleListeners.add(listener);
+  }
+
+  Process systemProcess() {
+    return process;
+  }
+
+  void updateSize(int newWidth, int newHeight) {
+    if (pty != null && (newWidth != width || newHeight != height)) {
+      pty.setTerminalSize(width, height);
+      width = newWidth;
+      height = newHeight;
+    }
+  }
+
+  static boolean isPlatformSupported() {
+    return PTY.isSupported();
+  }
+
+  void disconnect() {
+    if (process != null) {
+      try {
+        process.exitValue();
+      } catch (IllegalThreadStateException e) {
+        process.destroy();
+      }
+    }
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputListener.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputListener.java
new file mode 100644
index 0000000..f99952f
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.connector;
+
+import java.io.*;
+
+import org.eclipse.debug.core.IStreamListener;
+import org.eclipse.debug.core.model.IStreamMonitor;
+
+import com.google.eclipse.elt.emulator.provisional.api.ITerminalControl;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class TerminalOutputListener implements IStreamListener {
+  private final PrintStream printStream;
+
+  TerminalOutputListener(ITerminalControl control, String encoding) throws UnsupportedEncodingException {
+    printStream = new PrintStream(control.getRemoteToTerminalOutputStream(), true, encoding);
+  }
+
+  @Override public void streamAppended(String text, IStreamMonitor monitor) {
+    if (text.contains("\u001b[1A\u001b[K")) {
+      // clean = text.replace("\u001b[1A\u001b[K", "\u001b[K");
+      // TODO(alruiz) figure out why 1+ lines deleted in blaze build.
+    }
+    printStream.print(text);
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputStream.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputStream.java
new file mode 100644
index 0000000..928d0fe
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputStream.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.connector;
+
+import java.io.*;
+
+import org.eclipse.debug.core.model.IStreamsProxy;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class TerminalOutputStream extends OutputStream {
+  private final IStreamsProxy streamsProxy;
+  private final String encoding;
+
+  TerminalOutputStream(IStreamsProxy streamsProxy, String encoding) {
+    this.streamsProxy = streamsProxy;
+    this.encoding = encoding;
+  }
+
+  @Override
+  public void write(int b) throws IOException {
+    streamsProxy.write(new String(new byte[] { (byte) (b & 0xFF) }, encoding));
+  }
+
+  @Override
+  public void write(byte[] b, int off, int len) throws IOException {
+    String input = new String(b, off, len, encoding);
+    streamsProxy.write(input);
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/AbstractPreferencesChangeListener.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/AbstractPreferencesChangeListener.java
new file mode 100644
index 0000000..52b0c2d
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/AbstractPreferencesChangeListener.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import org.eclipse.jface.util.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public abstract class AbstractPreferencesChangeListener implements IPropertyChangeListener {
+  @Override public final void propertyChange(PropertyChangeEvent event) {
+    String property = event.getProperty();
+    if (BUFFER_LINE_COUNT.equals(property)) {
+      onBufferLineCountChanged();
+    }
+    if (BACKGROUND_COLOR.equals(property) || FOREGROUND_COLOR.equals(property)) {
+      onColorChanged();
+    }
+    if (CUSTOM_FONT_DATA.equals(property)) {
+      onFontChanged();
+    }
+    if (USE_BLINKING_CURSOR.equals(property)) {
+      onUseBlinkingCursorChanged();
+    }
+  }
+
+  protected abstract void onBufferLineCountChanged();
+
+  protected abstract void onColorChanged();
+
+  protected abstract void onFontChanged();
+
+  protected abstract void onUseBlinkingCursorChanged();
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorSettingPreviewText.txt b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorSettingPreviewText.txt
new file mode 100644
index 0000000..b434966
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorSettingPreviewText.txt
@@ -0,0 +1,8 @@
+$ mkdir ~/Hello-World
+$ cd ~/Hello-World
+$ git init
+$ touch README
+$ git add README
+$ git commit -m 'first commit'
+$ git remote add origin https://code.google.com/p/elt/
+$ git push -u origin master
\ No newline at end of file
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferencePage.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferencePage.java
new file mode 100644
index 0000000..683c616
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferencePage.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static org.eclipse.jface.layout.GridDataFactory.fillDefaults;
+import static org.eclipse.jface.preference.ColorSelector.PROP_COLORCHANGE;
+import static org.eclipse.jface.preference.PreferenceConverter.*;
+import static org.eclipse.jface.resource.JFaceResources.TEXT_FONT;
+import static org.eclipse.ui.dialogs.PreferencesUtil.createPreferenceDialogOn;
+
+import static com.google.eclipse.elt.view.Activator.*;
+import static com.google.eclipse.elt.view.preferences.Messages.*;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import java.io.InputStream;
+import java.util.Scanner;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.preference.*;
+import org.eclipse.jface.resource.*;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.source.projection.ProjectionViewer;
+import org.eclipse.jface.util.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ColorsAndFontsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
+  private ProjectionViewer previewer;
+  private ColorSelector foregroundColorSelector;
+  private ColorSelector backgroundColorSelector;
+  private Button btnUseTextFont;
+  private Button btnUseCustomFont;
+  private Label lblFontData;
+  private Button btnChangeFont;
+
+  private FontData fontData;
+
+  private IPropertyChangeListener textFontChangeListener;
+
+  @Override public void init(IWorkbench workbench) {
+    setPreferenceStore(preferenceStore());
+  }
+
+  @Override protected Control createContents(Composite parent) {
+    Composite contents = new Composite(parent, SWT.NONE);
+    contents.setLayout(new GridLayout(1, false));
+
+    Label lblDescription = new Label(contents, SWT.NONE);
+    lblDescription.setText(colorsAndFontsTitle);
+
+    GridDataFactory gridDataFactory = fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).span(1, 1);
+
+    Composite controls = new Composite(contents, SWT.NONE);
+    gridDataFactory.applyTo(controls);
+    controls.setLayout(new GridLayout(1, false));
+
+    Group grpColors = new Group(controls, SWT.NONE);
+    gridDataFactory.applyTo(grpColors);
+    grpColors.setLayout(new GridLayout(2, false));
+
+    Label lblBackground = new Label(grpColors, SWT.NONE);
+    lblBackground.setText(backgroundPrompt);
+
+    backgroundColorSelector = new ColorSelector(grpColors);
+
+    Label lblForeground = new Label(grpColors, SWT.NONE);
+    lblForeground.setText(foregroundPrompt);
+
+    foregroundColorSelector = new ColorSelector(grpColors);
+
+    Group grpFont = new Group(controls, SWT.NONE);
+    gridDataFactory.applyTo(grpFont);
+    grpFont.setLayout(new GridLayout(2, false));
+
+    SelectionListener fontButtonSelectionListener = new SelectionAdapter() {
+      @Override public void widgetSelected(SelectionEvent e) {
+        boolean useTextFont = btnUseTextFont.getSelection();
+        btnChangeFont.setEnabled(!useTextFont);
+        if (useTextFont) {
+          updateFontDataWithTextFont();
+        }
+      }
+    };
+
+    btnUseTextFont = new Button(grpFont, SWT.RADIO);
+    gridDataFactory.span(2, 1).applyTo(btnUseTextFont);
+    btnUseTextFont.setText(useTextFont);
+    btnUseTextFont.addSelectionListener(fontButtonSelectionListener);
+
+    Link changeTextFontLink = new Link(grpFont, SWT.NONE);
+    changeTextFontLink.setText(textFontLink);
+    GridData gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
+    gridData.widthHint = 150; // only expand further if anyone else requires it
+    gridData.horizontalSpan = 2;
+    changeTextFontLink.setLayoutData(gridData);
+    changeTextFontLink.addSelectionListener(new SelectionAdapter() {
+      @Override public void widgetSelected(SelectionEvent e) {
+        if ("org.eclipse.ui.preferencePages.ColorsAndFonts".equals(e.text)) {
+          createPreferenceDialogOn(getShell(), e.text, null, TEXT_FONT);
+        }
+      }
+    });
+
+    btnUseCustomFont = new Button(grpFont, SWT.RADIO);
+    gridDataFactory.applyTo(btnUseCustomFont);
+    btnUseCustomFont.setText(useCustomFont);
+    btnUseCustomFont.addSelectionListener(fontButtonSelectionListener);
+
+    lblFontData = new Label(grpFont, SWT.NONE);
+    gridDataFactory.span(1, 1).applyTo(lblFontData);
+    lblFontData.setText("");
+
+    btnChangeFont = new Button(grpFont, SWT.NONE);
+    btnChangeFont.setText(change);
+    btnChangeFont.addSelectionListener(new SelectionAdapter() {
+      @Override public void widgetSelected(SelectionEvent e) {
+        FontDialog fontDialog = new FontDialog(getShell());
+        fontDialog.setFontList(new FontData[] { fontData });
+        FontData newFontData = fontDialog.open();
+        if (newFontData != null) {
+          updateFontData(newFontData);
+        }
+      }
+    });
+
+    Label lblPreview = new Label(contents, SWT.NONE);
+    lblPreview.setText(previewPrompt);
+
+    previewer = new ProjectionViewer(contents, null, null, false, SWT.V_SCROLL | SWT.H_SCROLL);
+    previewer.setEditable(false);
+    previewer.setDocument(new Document(loadContentsFrom("ColorSettingPreviewText.txt")));
+
+    StyledText previewerText = previewer.getTextWidget();
+    gridDataFactory.align(SWT.FILL, SWT.FILL).grab(true, true);
+    gridDataFactory.applyTo(previewerText);
+    Cursor arrowCursor = previewerText.getDisplay().getSystemCursor(SWT.CURSOR_ARROW);
+    previewerText.setCursor(arrowCursor);
+
+    backgroundColorSelector.addListener(new ColorChangeListener() {
+      @Override void onColorChanged(RGB newValue) {
+        validateInput();
+        if (isValid()) {
+          previewer.getTextWidget().setBackground(newColor(newValue));
+        }
+      }
+    });
+
+    foregroundColorSelector.addListener(new ColorChangeListener() {
+      @Override void onColorChanged(RGB newValue) {
+        validateInput();
+        if (isValid()) {
+          previewer.setTextColor(newColor(newValue));
+        }
+      }
+    });
+
+    textFontChangeListener = new IPropertyChangeListener() {
+      @Override public void propertyChange(PropertyChangeEvent event) {
+        if (TEXT_FONT.equals(event.getProperty()) && btnUseTextFont.getSelection()) {
+          updateFontDataWithTextFont();
+        }
+      }
+    };
+    JFaceResources.getFontRegistry().addListener(textFontChangeListener);
+
+    updateContents();
+    return contents;
+  }
+
+  private String loadContentsFrom(String fileName) {
+    String lineSeparator = System.getProperty("line.separator");
+    StringBuilder buffer = new StringBuilder();
+    Scanner scanner = null;
+    try {
+      InputStream inputStream = getClass().getResourceAsStream(fileName);
+      scanner = new Scanner(inputStream);
+      while (scanner.hasNextLine()) {
+        String line = scanner.nextLine();
+        buffer.append(line).append(lineSeparator);
+      }
+    } catch (RuntimeException e) {
+      log(unableToLoadPreviewContent, e);
+    } finally {
+      if (scanner != null) {
+        scanner.close();
+      }
+    }
+    return buffer.toString();
+  }
+
+  private void validateInput() {
+    if (backgroundColorSelector.getColorValue().equals(foregroundColorSelector.getColorValue())) {
+      setErrorMessage(backgroundAndForegroundCannotBeTheSame);
+      setValid(false);
+      return;
+    }
+    pageIsValid();
+  }
+
+  private void pageIsValid() {
+    setErrorMessage(null);
+    setValid(true);
+  }
+
+  private void updateFontDataWithTextFont() {
+    updateFontData(JFaceResources.getTextFont().getFontData()[0]);
+  }
+
+  private void updateFontData(FontData newValue) {
+    fontData = newValue;
+    displayFont();
+  }
+
+  private void updateContents() {
+    RGB background = getColor(getPreferenceStore(), BACKGROUND_COLOR);
+    RGB foreground = getColor(getPreferenceStore(), FOREGROUND_COLOR);
+    fontData = getFontData(getPreferenceStore(), CUSTOM_FONT_DATA);
+    boolean useCustomFont = getPreferenceStore().getBoolean(USE_CUSTOM_FONT);
+    updateContents(background, foreground, useCustomFont);
+  }
+
+  @Override public boolean performOk() {
+    setValue(getPreferenceStore(), BACKGROUND_COLOR, backgroundColorSelector.getColorValue());
+    setValue(getPreferenceStore(), FOREGROUND_COLOR, foregroundColorSelector.getColorValue());
+    preferenceStore().setValue(USE_CUSTOM_FONT, btnUseCustomFont.getSelection());
+    setValue(getPreferenceStore(), CUSTOM_FONT_DATA, fontData);
+    return true;
+  }
+
+  @Override protected void performDefaults() {
+    RGB background = getDefaultColor(getPreferenceStore(), BACKGROUND_COLOR);
+    RGB foreground = getDefaultColor(getPreferenceStore(), FOREGROUND_COLOR);
+    fontData = getDefaultFontData(getPreferenceStore(), CUSTOM_FONT_DATA);
+    boolean useCustomFont = getPreferenceStore().getDefaultBoolean(USE_CUSTOM_FONT);
+    updateContents(background, foreground, useCustomFont);
+  }
+
+  private void updateContents(RGB background, RGB foreground, boolean useCustomFont) {
+    backgroundColorSelector.setColorValue(background);
+    foregroundColorSelector.setColorValue(foreground);
+    btnUseTextFont.setSelection(!useCustomFont);
+    btnUseCustomFont.setSelection(useCustomFont);
+    btnChangeFont.setEnabled(useCustomFont);
+    previewer.getTextWidget().setBackground(newColor(background));
+    previewer.setTextColor(newColor(foreground));
+    displayFont();
+  }
+
+  private void displayFont() {
+    lblFontData.setText(StringConverter.asString(fontData));
+    previewer.getTextWidget().setFont(new Font(display(), fontData));
+  }
+
+  private Color newColor(RGB rgb) {
+    return new Color(display(), rgb);
+  }
+
+  private Display display() {
+    return getShell().getDisplay();
+  }
+
+  @Override public void dispose() {
+    if (textFontChangeListener != null) {
+      JFaceResources.getFontRegistry().removeListener(textFontChangeListener);
+    }
+    super.dispose();
+  }
+
+  private static abstract class ColorChangeListener implements IPropertyChangeListener {
+    @Override public final void propertyChange(PropertyChangeEvent event) {
+      if (PROP_COLORCHANGE.equals(event.getProperty())) {
+        RGB rgb = (RGB) event.getNewValue();
+        onColorChanged(rgb);
+      }
+    }
+
+    abstract void onColorChanged(RGB newValue);
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferences.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferences.java
new file mode 100644
index 0000000..2f37661
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferences.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static org.eclipse.jface.preference.PreferenceConverter.*;
+
+import static com.google.eclipse.elt.view.Activator.preferenceStore;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ColorsAndFontsPreferences {
+  public static RGB background() {
+    return getColor(preferenceStore(), BACKGROUND_COLOR);
+  }
+
+  public static RGB foreground() {
+    return getColor(preferenceStore(), FOREGROUND_COLOR);
+  }
+
+  public static boolean useCustomFont() {
+    return preferenceStore().getBoolean(USE_CUSTOM_FONT);
+  }
+
+  public static FontData customFontData() {
+    return getFontData(preferenceStore(), CUSTOM_FONT_DATA);
+  }
+
+  public static boolean useBlinkingCursor() {
+    return preferenceStore().getBoolean(USE_BLINKING_CURSOR);
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/GeneralPreferences.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/GeneralPreferences.java
new file mode 100644
index 0000000..21673d6
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/GeneralPreferences.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static com.google.eclipse.elt.view.Activator.preferenceStore;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public final class GeneralPreferences {
+  public static int bufferLineCount() {
+    return preferenceStore().getInt(BUFFER_LINE_COUNT);
+  }
+
+  public static boolean closeViewOnExit() {
+    return preferenceStore().getBoolean(CLOSE_VIEW_ON_EXIT);
+  }
+
+  public static boolean warnOnClose() {
+    return preferenceStore().getBoolean(WARN_ON_CLOSE);
+  }
+
+  public static void warnOnClose(boolean newValue) {
+    preferenceStore().setValue(WARN_ON_CLOSE, newValue);
+  }
+
+  private GeneralPreferences() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.java
new file mode 100644
index 0000000..c74bd80
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class Messages extends NLS {
+  public static String backgroundAndForegroundCannotBeTheSame;
+  public static String backgroundPrompt;
+  public static String bufferLineCount;
+  public static String change;
+  public static String closeViewOnExit;
+  public static String colorsAndFontsTitle;
+  public static String foregroundPrompt;
+  public static String generalPreferencesTitle;
+  public static String invalidBufferLineCount;
+  public static String previewPrompt;
+  public static String textFontLink;
+  public static String unableToLoadPreviewContent;
+  public static String useBlinkingCursor;
+  public static String useCustomFont;
+  public static String useTextFont;
+  public static String warnOnClose;
+
+  static {
+    Class<Messages> type = Messages.class;
+    NLS.initializeMessages(type.getName(), type);
+  }
+
+  private Messages() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.properties b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.properties
new file mode 100644
index 0000000..55d4de9
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.properties
@@ -0,0 +1,17 @@
+backgroundAndForegroundCannotBeTheSame=Background and foreground colors cannot be the same
+backgroundPrompt=Background:
+bufferLineCount=Terminal buffer lines:
+change=Change...
+closeViewOnExit=Close view when terminal exits
+colorsAndFontsTitle=Terminal colors and fonts preferences.
+foregroundPrompt=Foreground:
+generalPreferencesTitle=General preferences.
+invalidBufferLineCount=Value should be an integer between {0} and {1}
+previewPrompt=Preview:
+textFontLink=Eclipse's "Text Font" can be configured on the <a href=\"org.eclipse.ui.preferencePages.ColorsAndFonts\">'Colors and Fonts'</a> preference page.
+unableToLoadPreviewContent=Unable to load preview content
+useBlinkingCursor=Use blinking cursor
+useCustomFont=Use custom font
+useTextFont=Use Eclipse's "Text Font"
+warnOnClose=Warn on close
+
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceInitializer.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceInitializer.java
new file mode 100644
index 0000000..c30f4d2
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceInitializer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static com.google.eclipse.elt.view.Activator.preferenceStore;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class PreferenceInitializer extends AbstractPreferenceInitializer {
+  @Override public void initializeDefaultPreferences() {
+    preferenceStore().setDefault(BUFFER_LINE_COUNT, 1000);
+    preferenceStore().setDefault(CLOSE_VIEW_ON_EXIT, true);
+    preferenceStore().setDefault(WARN_ON_CLOSE, true);
+    setDefault(BACKGROUND_COLOR, new RGB(0, 0, 0));
+    setDefault(FOREGROUND_COLOR, new RGB(229, 229, 229));
+    preferenceStore().setDefault(USE_CUSTOM_FONT, false);
+    preferenceStore().setDefault(USE_BLINKING_CURSOR, true);
+    PreferenceConverter.setDefault(preferenceStore(), CUSTOM_FONT_DATA, JFaceResources.getTextFont().getFontData());
+  }
+
+  private void setDefault(String name, RGB value) {
+    PreferenceConverter.setDefault(preferenceStore(), name, value);
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceNames.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceNames.java
new file mode 100644
index 0000000..e4b8136
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceNames.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.preferences;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+final class PreferenceNames {
+  static final String BUFFER_LINE_COUNT = "bufferLineCount";
+  static final String CLOSE_VIEW_ON_EXIT = "exitViewOnExit";
+  static final String WARN_ON_CLOSE = "warnOnClose";
+  static final String BACKGROUND_COLOR = "backgroundColor";
+  static final String FOREGROUND_COLOR = "foregroundColor";
+  static final String USE_CUSTOM_FONT = "useCustomFont";
+  static final String CUSTOM_FONT_DATA = "customFontData";
+  static final String USE_BLINKING_CURSOR = "useBlinkingCursor";
+
+  private PreferenceNames() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/RootPreferencePage.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/RootPreferencePage.java
new file mode 100644
index 0000000..d13489a
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/RootPreferencePage.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static com.google.eclipse.elt.view.Activator.preferenceStore;
+import static com.google.eclipse.elt.view.preferences.Messages.*;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class RootPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
+  public RootPreferencePage() {
+  }
+  private static final int MINIMUM_BUFFER_LINE_COUNT = 100;
+  private static final int MAXIMUM_BUFFER_LINE_COUNT = 50000;
+  private static final String INVALID_BUFFER_LINE_COUNT_MESSAGE =
+      NLS.bind(invalidBufferLineCount, MINIMUM_BUFFER_LINE_COUNT, MAXIMUM_BUFFER_LINE_COUNT);
+
+  private Text txtBufferLineCount;
+  private Button btnCloseViewOnExit;
+  private Button btnWarnOnClose;
+  private Button btnUseBlinkingCursor;
+
+  private int newBufferLineCount;
+
+  @Override public void init(IWorkbench workbench) {
+    setPreferenceStore(preferenceStore());
+  }
+
+  @Override protected Control createContents(Composite parent) {
+    Composite contents = new Composite(parent, SWT.NONE);
+    contents.setLayout(new GridLayout(2, false));
+
+    Label lblGeneralPreferences = new Label(contents, SWT.NONE);
+    lblGeneralPreferences.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1));
+    lblGeneralPreferences.setText(generalPreferencesTitle);
+
+    Label filler = new Label(contents, SWT.NONE);
+    filler.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+
+    Label lblBufferLineCount = new Label(contents, SWT.NONE);
+    lblBufferLineCount.setText(bufferLineCount);
+
+    txtBufferLineCount = new Text(contents, SWT.BORDER);
+    txtBufferLineCount.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+    txtBufferLineCount.addModifyListener(new ModifyListener() {
+      @Override public void modifyText(ModifyEvent event) {
+        try {
+          newBufferLineCount = Integer.parseInt(txtBufferLineCount.getText());
+        } catch (NumberFormatException e) {
+          setInvalid(INVALID_BUFFER_LINE_COUNT_MESSAGE);
+          return;
+        }
+        if (newBufferLineCount < MINIMUM_BUFFER_LINE_COUNT || newBufferLineCount > MAXIMUM_BUFFER_LINE_COUNT) {
+          setInvalid(INVALID_BUFFER_LINE_COUNT_MESSAGE);
+          return;
+        }
+        setErrorMessage(null);
+        setValid(true);
+      }
+    });
+
+    btnCloseViewOnExit = new Button(contents, SWT.CHECK);
+    btnCloseViewOnExit.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+    btnCloseViewOnExit.setText(closeViewOnExit);
+
+    btnWarnOnClose = new Button(contents, SWT.CHECK);
+    btnWarnOnClose.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+    btnWarnOnClose.setText(warnOnClose);
+
+    btnUseBlinkingCursor = new Button(contents, SWT.CHECK);
+    btnUseBlinkingCursor.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+    btnUseBlinkingCursor.setText(useBlinkingCursor);
+
+    updateContents();
+    return contents;
+  }
+
+  private void setInvalid(String errorMessage) {
+    setErrorMessage(errorMessage);
+    setValid(false);
+  }
+
+  private void updateContents() {
+    txtBufferLineCount.setText(getPreferenceStore().getString(BUFFER_LINE_COUNT));
+    btnCloseViewOnExit.setSelection(getPreferenceStore().getBoolean(CLOSE_VIEW_ON_EXIT));
+    btnWarnOnClose.setSelection(getPreferenceStore().getBoolean(WARN_ON_CLOSE));
+    btnUseBlinkingCursor.setSelection(getPreferenceStore().getBoolean(USE_BLINKING_CURSOR));
+  }
+
+  @Override public boolean performOk() {
+    getPreferenceStore().setValue(BUFFER_LINE_COUNT, newBufferLineCount);
+    getPreferenceStore().setValue(CLOSE_VIEW_ON_EXIT, btnCloseViewOnExit.getSelection());
+    getPreferenceStore().setValue(WARN_ON_CLOSE, btnWarnOnClose.getSelection());
+    getPreferenceStore().setValue(USE_BLINKING_CURSOR, btnUseBlinkingCursor.getSelection());
+    return true;
+  }
+
+  @Override protected void performDefaults() {
+    txtBufferLineCount.setText(getPreferenceStore().getDefaultString(BUFFER_LINE_COUNT));
+    btnCloseViewOnExit.setSelection(getPreferenceStore().getDefaultBoolean(CLOSE_VIEW_ON_EXIT));
+    btnWarnOnClose.setSelection(getPreferenceStore().getDefaultBoolean(WARN_ON_CLOSE));
+    btnUseBlinkingCursor.setSelection(getPreferenceStore().getDefaultBoolean(USE_BLINKING_CURSOR));
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.java
new file mode 100644
index 0000000..b841a0d
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class Messages extends NLS {
+  public static String alwaysCloseWithoutWarn;
+  public static String changeTerminalTitle;
+  public static String closeTerminalQuestion;
+  public static String confirmCloseDialogTitle;
+  public static String defaultViewTitle;
+  public static String enterTerminalTitleDialogTitle;
+  public static String enterTerminalTitlePrompt;
+  public static String newLocalTerminal;
+  public static String scrollLock;
+
+  static {
+    Class<Messages> type = Messages.class;
+    NLS.initializeMessages(type.getName(), type);
+  }
+
+  private Messages() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.properties b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.properties
new file mode 100644
index 0000000..a2ebc48
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.properties
@@ -0,0 +1,9 @@
+alwaysCloseWithoutWarn=Always close without warn
+changeTerminalTitle=Change Title
+closeTerminalQuestion=Close terminal?
+confirmCloseDialogTitle=Confirm Close
+defaultViewTitle=Terminal
+enterTerminalTitleDialogTitle=Terminal Title
+enterTerminalTitlePrompt=Enter the new title for the terminal:
+newLocalTerminal=New Terminal
+scrollLock=Scroll Lock
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/PopupMenu.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/PopupMenu.java
new file mode 100644
index 0000000..1950397
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/PopupMenu.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.ui;
+
+import static org.eclipse.ui.actions.ActionFactory.*;
+
+import org.eclipse.jface.action.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.*;
+
+import com.google.eclipse.elt.emulator.actions.*;
+import com.google.eclipse.elt.emulator.core.VT100TerminalControl;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class PopupMenu {
+  private final AbstractTerminalAction copy;
+  private final AbstractTerminalAction paste;
+  private final AbstractTerminalAction selectAll;
+  private final AbstractTerminalAction clearAll;
+
+  @SuppressWarnings("deprecation")
+  PopupMenu(IViewSite viewSite, VT100TerminalControl terminalControl) {
+    PopupMenuManager menuManager = new PopupMenuManager();
+    copy = menuManager.add(new TerminalActionCopy(terminalControl));
+    copy.setActionDefinitionId("com.google.eclipse.terminal.local.copy");
+    paste = menuManager.add(new TerminalActionPaste(terminalControl));
+    paste.setActionDefinitionId("com.google.eclipse.terminal.local.paste");
+    menuManager.add(new Separator());
+    selectAll = menuManager.add(new TerminalActionSelectAll(terminalControl));
+    clearAll = menuManager.add(new TerminalActionClearAll(terminalControl));
+    IActionBars actionBars = viewSite.getActionBars();
+    actionBars.setGlobalActionHandler(COPY.getId(), copy);
+    actionBars.setGlobalActionHandler(PASTE.getId(), paste);
+    actionBars.setGlobalActionHandler(SELECT_ALL.getId(), selectAll);
+    IKeyBindingService keyBindingService = viewSite.getKeyBindingService();
+    keyBindingService.registerAction(copy);
+    keyBindingService.registerAction(paste);
+    menuManager.addMenuListener(new IMenuListener() {
+      @Override public void menuAboutToShow(IMenuManager manager) {
+        update();
+      }
+    });
+    Control control = terminalControl.getControl();
+    Menu menu = menuManager.createContextMenu(control);
+    control.setMenu(menu);
+    menu.addMenuListener(new MenuAdapter() {
+      @Override public void menuHidden(MenuEvent e) {
+        copy.updateAction(false);
+      }
+    });
+  }
+
+  void update() {
+    update(copy, paste, selectAll, clearAll);
+  }
+
+  private void update(AbstractTerminalAction...actions) {
+    for (AbstractTerminalAction action : actions) {
+      action.updateAction(true);
+    }
+  }
+
+  private static class PopupMenuManager extends MenuManager {
+    PopupMenuManager() {
+      super("#PopupMenu");
+    }
+
+    AbstractTerminalAction add(AbstractTerminalAction action) {
+      super.add(action);
+      return action;
+    }
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalView.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalView.java
new file mode 100644
index 0000000..70c6714
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalView.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.ui;
+
+import static org.eclipse.core.runtime.Path.fromOSString;
+import static org.eclipse.core.runtime.Status.OK_STATUS;
+import static org.eclipse.jface.resource.JFaceResources.TEXT_FONT;
+import static org.eclipse.jface.window.Window.OK;
+import static org.eclipse.ui.IWorkbenchPage.VIEW_ACTIVATE;
+
+import static com.google.eclipse.elt.view.Activator.*;
+import static com.google.eclipse.elt.view.ImageKeys.*;
+import static com.google.eclipse.elt.view.preferences.ColorsAndFontsPreferences.*;
+import static com.google.eclipse.elt.view.preferences.GeneralPreferences.*;
+import static com.google.eclipse.elt.view.ui.Messages.*;
+import static com.google.eclipse.elt.view.util.Platform.userHomeDirectory;
+
+import java.util.UUID;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.*;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.*;
+import org.eclipse.ui.contexts.*;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.ui.progress.UIJob;
+
+import com.google.eclipse.elt.emulator.control.ITerminalListener;
+import com.google.eclipse.elt.emulator.provisional.api.TerminalState;
+import com.google.eclipse.elt.view.connector.LifeCycleListener;
+import com.google.eclipse.elt.view.preferences.AbstractPreferencesChangeListener;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class TerminalView extends ViewPart implements ISaveablePart2 {
+  private static final String SCROLL_LOCK_ENABLED = "scrollLock";
+  private static final String TITLE_STATE_TYPE = "title";
+  private static final String WORKING_DIRECTORY_STATE_TYPE = "workingDirectory";
+
+  private static final String VIEW_ID = "com.google.eclipse.terminal.local.localTerminalView";
+
+  private IPropertyChangeListener preferencesChangeListener;
+  private IPropertyChangeListener textFontChangeListener;
+  private IMemento savedState;
+  private TerminalWidget terminalWidget;
+  private IPath workingDirectory;
+
+  private Action newTerminalAction;
+  private Action scrollLockAction;
+
+  private boolean checkCanBeClosed;
+  private boolean forceClose;
+
+  private IContextActivation contextActivation;
+
+  public static void openTerminalView(IPath workingDirectory) {
+    openTerminalView(null, workingDirectory);
+  }
+
+  private static void openTerminalView(String id, IPath workingDirectory) {
+    IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+    IPath safeWorkingDirectory = (workingDirectory != null) ? workingDirectory : userHomeDirectory();
+    try {
+      String directoryName = safeWorkingDirectory.lastSegment();
+      String secondaryId = (id != null) ? id : directoryName;
+      TerminalView view = (TerminalView) page.showView(VIEW_ID, secondaryId, VIEW_ACTIVATE);
+      view.setPartName(directoryName);
+      view.open(safeWorkingDirectory);
+    } catch (PartInitException e) {
+      log("Unable to create Terminal View", e);
+    }
+  }
+
+  @Override public void init(IViewSite site, IMemento memento) throws PartInitException {
+    super.init(site, memento);
+    savedState = memento;
+  }
+
+  @Override public void saveState(IMemento memento) {
+    saveState(memento, SCROLL_LOCK_ENABLED, String.valueOf(terminalWidget.isScrollLockEnabled()));
+    saveState(memento, TITLE_STATE_TYPE, getPartName());
+    saveState(memento, WORKING_DIRECTORY_STATE_TYPE, workingDirectory.toOSString());
+  }
+
+  private void saveState(IMemento memento, String type, String data) {
+    IMemento child = memento.createChild(type);
+    child.putTextData(data);
+  }
+
+  @Override public void createPartControl(Composite parent) {
+    terminalWidget = new TerminalWidget(parent, getViewSite());
+    terminalWidget.setLifeCycleListener(new LifeCycleListener() {
+      @Override public void executionFinished() {
+        closeViewOnExitIfPossible();
+      }
+    });
+    terminalWidget.setTerminalListener(new ITerminalListener() {
+      @Override public void setTerminalTitle(final String title) {
+        updatePartName(title);
+      }
+
+      @Override public void setState(TerminalState state) {}
+    });
+    IViewSite viewSite = getViewSite();
+    preferencesChangeListener = new AbstractPreferencesChangeListener() {
+      @Override protected void onBufferLineCountChanged() {
+        updateBufferLineCount();
+      }
+
+      @Override protected void onColorChanged() {
+        updateColors();
+      }
+
+      @Override protected void onFontChanged() {
+        updateFont();
+      }
+
+      @Override protected void onUseBlinkingCursorChanged() {
+        updateUsageOfBlinkingCursor();
+      }
+    };
+    preferenceStore().addPropertyChangeListener(preferencesChangeListener);
+    updateBufferLineCount();
+    updateColors();
+    updateUsageOfBlinkingCursor();
+    textFontChangeListener = new IPropertyChangeListener() {
+      @Override public void propertyChange(PropertyChangeEvent event) {
+        if (TEXT_FONT.equals(event.getProperty())) {
+          if (!useCustomFont()) {
+            setFont(JFaceResources.getTextFont());
+          }
+        }
+      }
+    };
+    JFaceResources.getFontRegistry().addListener(textFontChangeListener);
+    updateFont();
+    setupToolBarActions();
+    IContextService contextService = contextService();
+    if (contextService != null) {
+      contextActivation = contextService.activateContext("com.google.eclipse.terminal.local.context.localTerminal");
+    }
+    if (savedState != null) {
+      updateScrollLockUsingSavedState();
+      connectUsingSavedState();
+      return;
+    }
+    if (viewSite.getSecondaryId() == null) {
+      setPartName(defaultViewTitle);
+      open(userHomeDirectory());
+    }
+    enableScrollLock(scrollLockAction.isChecked());
+  }
+
+  private void closeViewOnExitIfPossible() {
+    if (closeViewOnExit() && terminalWidget != null && !terminalWidget.isDisposed()) {
+      // must run in UI thread.
+      forceClose = true;
+      terminalWidget.getDisplay().asyncExec(new Runnable() {
+        @Override public void run() {
+          IWorkbenchPartSite site = getSite();
+          site.getPage().hideView((IViewPart) site.getPart());
+        }
+      });
+    }
+  }
+
+  private void updateColors() {
+    terminalWidget.setColors(background(), foreground());
+  }
+
+  private void updateFont() {
+    setFont(terminalFont());
+  }
+
+  private void updateUsageOfBlinkingCursor() {
+    terminalWidget.setBlinkingCursor(useBlinkingCursor());
+  }
+
+  private Font terminalFont() {
+    if (useCustomFont()) {
+      return new Font(Display.getDefault(), customFontData());
+    }
+    return JFaceResources.getTextFont();
+  }
+
+  private void setFont(Font font) {
+    terminalWidget.setFont(font);
+  }
+
+  private void updateBufferLineCount() {
+    terminalWidget.setBufferLineCount(bufferLineCount());
+  }
+
+  private void setupToolBarActions() {
+    IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
+    toolBarManager.add(new ChangeViewNameAction());
+    toolBarManager.add(new Separator());
+    newTerminalAction = new NewTerminalAction();
+    toolBarManager.add(newTerminalAction);
+    scrollLockAction = new ScrollLockAction();
+    toolBarManager.add(scrollLockAction);
+  }
+
+  private void updateScrollLockUsingSavedState() {
+    boolean newValue = Boolean.valueOf(savedState(SCROLL_LOCK_ENABLED));
+    enableScrollLockAndUpdateAction(newValue);
+  }
+
+  private void enableScrollLockAndUpdateAction(boolean enabled) {
+    enableScrollLock(enabled);
+    scrollLockAction.setChecked(enabled);
+  }
+
+  private void enableScrollLock(boolean enabled) {
+    terminalWidget.enableScrollLock(enabled);
+  }
+
+  private void connectUsingSavedState() {
+    String title = savedState(TITLE_STATE_TYPE);
+    setPartName(title);
+    String savedWorkingDirectory = savedState(WORKING_DIRECTORY_STATE_TYPE);
+    if (savedWorkingDirectory != null) {
+      open(fromOSString(savedWorkingDirectory));
+    }
+  }
+
+  private String savedState(String type) {
+    IMemento child = savedState.getChild(type);
+    return (child != null) ? child.getTextData() : null;
+  }
+
+  private void open(IPath workingDirectory) {
+    if (terminalWidget.isConnected()) {
+      return;
+    }
+    this.workingDirectory = workingDirectory;
+    terminalWidget.setWorkingDirectory(workingDirectory);
+    terminalWidget.connect();
+  }
+
+  private void updatePartName(final String value) {
+    UIJob job = new UIJob("Update terminal view title") {
+      @Override public IStatus runInUIThread(IProgressMonitor monitor) {
+        setPartName(value);
+        return OK_STATUS;
+      }
+    };
+    job.schedule();
+  }
+
+  @Override public void setFocus() {
+    terminalWidget.setFocus();
+  }
+
+  @Override public void dispose() {
+    if (contextActivation != null) {
+      IContextService contextService = contextService();
+      if (contextService != null) {
+        contextService.deactivateContext(contextActivation);
+      }
+    }
+    if (preferencesChangeListener != null) {
+      preferenceStore().removePropertyChangeListener(preferencesChangeListener);
+    }
+    if (textFontChangeListener != null) {
+      JFaceResources.getFontRegistry().removeListener(textFontChangeListener);
+    }
+    super.dispose();
+  }
+
+  private IContextService contextService() {
+    return (IContextService) getSite().getService(IContextService.class);
+  }
+
+  @Override public boolean isDirty() {
+    if (checkCanBeClosed) {
+      checkCanBeClosed = false;
+      return true;
+    }
+    return false;
+  }
+
+  @Override public boolean isSaveOnCloseNeeded() {
+    if (forceClose) {
+      return false;
+    }
+    checkCanBeClosed = true;
+    return true;
+  }
+
+  @Override public int promptToSaveOnClose() {
+    if (warnOnClose()) {
+      boolean close = WarnOnCloseDialog.open(terminalWidget.getShell());
+      if (!close) {
+        return CANCEL;
+      }
+    }
+    return NO;
+  }
+
+  @Override public void doSave(IProgressMonitor monitor) {}
+
+  @Override public void doSaveAs() {}
+
+  @Override public boolean isSaveAsAllowed() {
+    return false;
+  }
+
+  private class NewTerminalAction extends Action {
+    NewTerminalAction() {
+      setImageDescriptor(imageDescriptor(NEW_TERMINAL));
+      setText(newLocalTerminal);
+    }
+
+    @Override public void run() {
+      openTerminalView(UUID.randomUUID().toString(), workingDirectory);
+    }
+  }
+
+  private class ScrollLockAction extends Action {
+    ScrollLockAction() {
+      super(scrollLock, AS_RADIO_BUTTON);
+      setChecked(false);
+      setImageDescriptor(imageDescriptor(SCROLL_LOCK));
+    }
+
+    @Override public void run() {
+      boolean newValue = !terminalWidget.isScrollLockEnabled();
+      enableScrollLockAndUpdateAction(newValue);
+    }
+  }
+
+  private class ChangeViewNameAction extends Action {
+    ChangeViewNameAction() {
+      setImageDescriptor(imageDescriptor(CHANGE_TITLE));
+      setText(changeTerminalTitle);
+    }
+
+    @Override public void run() {
+      Shell shell = getViewSite().getShell();
+      final String currentTitle = getPartName();
+      InputDialog input = new InputDialog(shell, enterTerminalTitleDialogTitle, enterTerminalTitlePrompt, currentTitle,
+          new IInputValidator() {
+            @Override public String isValid(String newText) {
+              if (newText == null || newText.isEmpty() || currentTitle.equals(newText)) {
+                return "";
+              }
+              return null;
+            }
+          });
+      input.setBlockOnOpen(true);
+      if (input.open() == OK) {
+        setPartName(input.getValue());
+      }
+    }
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalWidget.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalWidget.java
new file mode 100644
index 0000000..14729d0
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalWidget.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.ui;
+
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.CONNECTING;
+import static com.google.eclipse.elt.view.connector.LocalTerminalConnector.createLocalTerminalConnector;
+import static com.google.eclipse.elt.view.util.Encodings.DEFAULT_ENCODING;
+
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.layout.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.IViewSite;
+
+import com.google.eclipse.elt.emulator.control.ITerminalListener;
+import com.google.eclipse.elt.emulator.core.VT100TerminalControl;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+import com.google.eclipse.elt.view.connector.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class TerminalWidget extends Composite {
+  private final TerminalListener terminalListener = new TerminalListener();
+
+  private final VT100TerminalControl terminalControl;
+
+  private LifeCycleListener lifeCycleListener;
+  private final String encoding = DEFAULT_ENCODING;
+
+  TerminalWidget(Composite parent, IViewSite viewSite) {
+    super(parent, SWT.NONE);
+    GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(this);
+    ITerminalConnector terminalConnector = createLocalTerminalConnector(encoding);
+    terminalControl = new VT100TerminalControl(terminalListener, this, new ITerminalConnector[] { terminalConnector });
+    terminalControl.setConnector(terminalConnector);
+    try {
+      terminalControl.setEncoding(encoding);
+    } catch (UnsupportedEncodingException e) {
+      // TODO(alruiz): do something meaningful with the exception.
+      e.printStackTrace();
+    }
+    GridDataFactory.fillDefaults().grab(true, true).applyTo(terminalControl.getRootControl());
+    terminalControl.setInvertedColors(true);
+    addDisposeListener(new DisposeListener() {
+      @Override public void widgetDisposed(DisposeEvent e) {
+        disposeTerminalControl();
+      }
+    });
+    final PopupMenu popupMenu = new PopupMenu(viewSite, terminalControl);
+    terminalTextControl().addFocusListener(new FocusAdapter() {
+      @Override public void focusGained(FocusEvent e) {
+        popupMenu.update();
+      }
+    });
+  }
+
+  private Control terminalTextControl() {
+    return terminalControl.getControl();
+  }
+
+  void connect() {
+    if (terminalControl.getState() == CONNECTING || terminalControl.isDisposed()) {
+      return;
+    }
+    terminalControl.connectTerminal();
+    attachLifeCycleListener();
+  }
+
+  private void attachLifeCycleListener() {
+    LocalTerminalConnector connector = localTerminalConnector();
+    if (connector != null && lifeCycleListener != null) {
+      connector.addLifeCycleListener(lifeCycleListener);
+    }
+  }
+
+  void disposeTerminalControl() {
+    if (!terminalControl.isDisposed()) {
+      terminalControl.disposeTerminal();
+    }
+  }
+
+  boolean isConnected() {
+    return terminalControl.isConnected();
+  }
+
+  void setLifeCycleListener(LifeCycleListener listener) {
+    lifeCycleListener = listener;
+  }
+
+  void setTerminalListener(ITerminalListener listener) {
+    terminalListener.delegate = listener;
+  }
+
+  void setWorkingDirectory(IPath workingDirectory) {
+    LocalTerminalConnector connector = localTerminalConnector();
+    if (connector != null) {
+      connector.setWorkingDirectory(workingDirectory);
+    }
+  }
+
+  private LocalTerminalConnector localTerminalConnector() {
+    Object connector = terminalControl.getTerminalConnector().getAdapter(LocalTerminalConnector.class);
+    return (LocalTerminalConnector) connector;
+  }
+
+  void setColors(RGB background, RGB foreground) {
+    terminalControl.setColors(background, foreground);
+  }
+
+  @Override public void setFont(Font font) {
+    terminalControl.setFont(font);
+  }
+
+  void setBufferLineCount(int lineCount) {
+    terminalControl.setBufferLineLimit(lineCount);
+  }
+
+  @Override public boolean setFocus() {
+    return terminalControl.setFocus();
+  }
+
+  boolean isScrollLockEnabled() {
+    return terminalControl.isScrollLockOn();
+  }
+
+  void enableScrollLock(boolean enabled) {
+    terminalControl.setScrollLockOn(enabled);
+  }
+
+  void setBlinkingCursor(boolean useBlinkingCursor) {
+    terminalControl.setBlinkingCursor(useBlinkingCursor);
+  }
+
+  private static class TerminalListener implements ITerminalListener {
+    ITerminalListener delegate;
+
+    @Override public void setState(TerminalState state) {
+      if (delegate != null) {
+        delegate.setState(state);
+      }
+    }
+
+    @Override public void setTerminalTitle(String title) {
+      if (delegate != null) {
+        delegate.setTerminalTitle(title);
+      }
+    }
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/WarnOnCloseDialog.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/WarnOnCloseDialog.java
new file mode 100644
index 0000000..918d6d2
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/WarnOnCloseDialog.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.ui;
+
+import static org.eclipse.jface.dialogs.IDialogConstants.*;
+
+import static com.google.eclipse.elt.view.preferences.GeneralPreferences.warnOnClose;
+import static com.google.eclipse.elt.view.ui.Messages.*;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class WarnOnCloseDialog extends MessageDialog {
+  private static final String[] BUTTON_LABELS = { YES_LABEL, NO_LABEL };
+
+  private Button btnCloseWithoutWarn;
+
+  static boolean open(Shell parent) {
+    WarnOnCloseDialog dialog = new WarnOnCloseDialog(parent);
+    return dialog.open() == OK;
+  }
+
+  private WarnOnCloseDialog(Shell parentShell) {
+    super(parentShell, confirmCloseDialogTitle, null, closeTerminalQuestion, QUESTION, BUTTON_LABELS, 0);
+  }
+
+  @Override protected Control createCustomArea(Composite parent) {
+    btnCloseWithoutWarn = new Button(parent, SWT.CHECK);
+    btnCloseWithoutWarn.setText(alwaysCloseWithoutWarn);
+    btnCloseWithoutWarn.setSelection(!warnOnClose());
+    return btnCloseWithoutWarn;
+  }
+
+  @Override protected void buttonPressed(int buttonId) {
+    boolean closeWithoutWarn = btnCloseWithoutWarn.getSelection();
+    warnOnClose(!closeWithoutWarn);
+    super.buttonPressed(buttonId);
+  }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Encodings.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Encodings.java
new file mode 100644
index 0000000..6b6082f
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Encodings.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.util;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public final class Encodings {
+  public static final String DEFAULT_ENCODING = "UTF-8";
+
+  private Encodings() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Platform.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Platform.java
new file mode 100644
index 0000000..2f50a5f
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Platform.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.elt.view.util;
+
+import static java.util.Collections.singletonMap;
+
+import static org.eclipse.core.runtime.Platform.*;
+
+import java.io.File;
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.debug.core.*;
+
+/**
+ * Utility methods related to the underlying platform.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public final class Platform {
+  // TODO investigate if "TERM" should be "xterm-color"
+  private static final Map<String, String> TERM_ANSI = singletonMap("TERM", "ansi");
+  private static final String ENVIRONMENT_VARIABLE_FORMAT = "%s=%s";
+
+  /**
+   * Returns the system's default shell.
+   * @return the system's default shell.
+   */
+  public static File defaultShell() {
+    String shell = System.getenv("SHELL");
+    if (shell == null) {
+      shell = (runningOnWindows()) ? "C:\\Windows\\System32\\cmd.exe" : "/bin/sh";
+    }
+    return new File(shell);
+  }
+
+  private static boolean runningOnWindows() {
+    return OS_WIN32.equals(getOS());
+  }
+
+  /**
+   * Returns an array of environment variables. Each entry is of the form "<code>name=value</code>".
+   * @return an array of environment variables.
+   */
+  public static String[] environment() {
+    Map<String, String> environment = new HashMap<String, String>();
+    environment.putAll(TERM_ANSI);
+    environment.putAll(nativeEnvironment());
+    List<String> variables = new ArrayList<String>(environment.size());
+    for (Map.Entry<String, String> entry : environment.entrySet()) {
+      String variable = String.format(ENVIRONMENT_VARIABLE_FORMAT, entry.getKey(), entry.getValue());
+      variables.add(variable);
+    }
+    return variables.toArray(new String[variables.size()]);
+  }
+
+  @SuppressWarnings("unchecked")
+  private static Map<String, String> nativeEnvironment() {
+    ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+    return launchManager.getNativeEnvironmentCasePreserved();
+  }
+
+  /**
+   * Returns the path of the user home directory.
+   * @return the path of the user home directory or {@code null} if it cannot be found.
+   */
+  public static IPath userHomeDirectory() {
+    String path = System.getProperty("user.home", "/");
+    File userHome = new File(path);
+    return (userHome.isDirectory()) ? new Path(path) : null;
+  }
+
+  private Platform() {}
+}
diff --git a/update-site/artifacts.jar b/update-site/artifacts.jar
deleted file mode 100644
index ffbcb76..0000000
--- a/update-site/artifacts.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/content.jar b/update-site/content.jar
deleted file mode 100644
index 5ab9309..0000000
--- a/update-site/content.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/features/com.google.eclipse.terminal.local_1.1.0.201208061420.jar b/update-site/features/com.google.eclipse.terminal.local_1.1.0.201208061420.jar
deleted file mode 100644
index 366e1f4..0000000
--- a/update-site/features/com.google.eclipse.terminal.local_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_1.1.0.201208061420.jar
deleted file mode 100644
index a1fc56d..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_64_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_64_1.1.0.201208061420.jar
deleted file mode 100644
index 70f2687..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_64_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core.linux_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core.linux_1.1.0.201208061420.jar
deleted file mode 100644
index 24fca0d..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core.linux_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core.macosx_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core.macosx_1.1.0.201208061420.jar
deleted file mode 100644
index eee97a8..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core.macosx_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core_1.1.0.201208061420.jar
deleted file mode 100644
index 18a9256..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.terminal.local_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.terminal.local_1.1.0.201208061420.jar
deleted file mode 100644
index 4920500..0000000
--- a/update-site/plugins/com.google.eclipse.terminal.local_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.tm.terminal_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.tm.terminal_1.1.0.201208061420.jar
deleted file mode 100644
index f62fafa..0000000
--- a/update-site/plugins/com.google.eclipse.tm.terminal_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ