Initial import
Omari Stephens [Thu, 10 Nov 2011 00:20:51 +0000 (16:20 -0800)]
Downloaded jline-1.0.zip from and unpacked it:
http://sourceforge.net/projects/jline/files/jline/1.0/

Bug: 3457774
Change-Id: Ic7e456baeff00fa2be059d3762bb2dbf822c7c56

56 files changed:
LICENSE.txt [new file with mode: 0644]
jline-1.0.jar [new file with mode: 0644]
src/pom.xml [new file with mode: 0644]
src/src/assembly/assembly.xml [new file with mode: 0644]
src/src/main/java/jline/ANSIBuffer.java [new file with mode: 0644]
src/src/main/java/jline/ArgumentCompletor.java [new file with mode: 0644]
src/src/main/java/jline/CandidateCycleCompletionHandler.java [new file with mode: 0644]
src/src/main/java/jline/CandidateListCompletionHandler.java [new file with mode: 0644]
src/src/main/java/jline/ClassNameCompletor.java [new file with mode: 0644]
src/src/main/java/jline/CompletionHandler.java [new file with mode: 0644]
src/src/main/java/jline/Completor.java [new file with mode: 0644]
src/src/main/java/jline/ConsoleOperations.java [new file with mode: 0644]
src/src/main/java/jline/ConsoleReader.java [new file with mode: 0644]
src/src/main/java/jline/ConsoleReaderInputStream.java [new file with mode: 0644]
src/src/main/java/jline/ConsoleRunner.java [new file with mode: 0644]
src/src/main/java/jline/CursorBuffer.java [new file with mode: 0644]
src/src/main/java/jline/FileNameCompletor.java [new file with mode: 0644]
src/src/main/java/jline/History.java [new file with mode: 0644]
src/src/main/java/jline/MultiCompletor.java [new file with mode: 0644]
src/src/main/java/jline/NullCompletor.java [new file with mode: 0644]
src/src/main/java/jline/SimpleCompletor.java [new file with mode: 0644]
src/src/main/java/jline/Terminal.java [new file with mode: 0644]
src/src/main/java/jline/UnixTerminal.java [new file with mode: 0644]
src/src/main/java/jline/UnsupportedTerminal.java [new file with mode: 0644]
src/src/main/java/jline/WindowsTerminal.java [new file with mode: 0644]
src/src/main/java/jline/package.html [new file with mode: 0644]
src/src/main/native/Makefile [new file with mode: 0644]
src/src/main/native/jline_WindowsTerminal.c [new file with mode: 0644]
src/src/main/native/jline_WindowsTerminal.h [new file with mode: 0644]
src/src/main/resources/jline/CandidateListCompletionHandler.properties [new file with mode: 0644]
src/src/main/resources/jline/jline32.dll [new file with mode: 0644]
src/src/main/resources/jline/jline64.dll [new file with mode: 0644]
src/src/main/resources/jline/keybindings-mac.properties [new file with mode: 0644]
src/src/main/resources/jline/keybindings.properties [new file with mode: 0644]
src/src/main/resources/jline/windowsbindings.properties [new file with mode: 0644]
src/src/site/apt/building.apt [new file with mode: 0644]
src/src/site/apt/downloads.apt [new file with mode: 0644]
src/src/site/docbook/index.xml [new file with mode: 0644]
src/src/site/fml/faq.fml [new file with mode: 0644]
src/src/site/resources/css/site.css [new file with mode: 0755]
src/src/site/resources/images/collapsed.png [new file with mode: 0755]
src/src/site/resources/images/dotted.png [new file with mode: 0755]
src/src/site/resources/images/expanded.png [new file with mode: 0755]
src/src/site/resources/images/external.png [new file with mode: 0755]
src/src/site/resources/images/ico_file_pdf.png [new file with mode: 0644]
src/src/site/resources/images/logo.jpg [new file with mode: 0644]
src/src/site/resources/images/newwindow.png [new file with mode: 0755]
src/src/site/site.xml [new file with mode: 0644]
src/src/test/java/jline/ConsoleReaderTest.java [new file with mode: 0644]
src/src/test/java/jline/JLineTestCase.java [new file with mode: 0644]
src/src/test/java/jline/TestCompletion.java [new file with mode: 0644]
src/src/test/java/jline/TestEditLine.java [new file with mode: 0644]
src/src/test/java/jline/TestHistory.java [new file with mode: 0644]
src/src/test/java/jline/example/Example.java [new file with mode: 0644]
src/src/test/java/jline/example/PasswordReader.java [new file with mode: 0644]
src/src/test/resources/jline/example/english.gz [new file with mode: 0644]

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..1cdc44c
--- /dev/null
@@ -0,0 +1,33 @@
+Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/jline-1.0.jar b/jline-1.0.jar
new file mode 100644 (file)
index 0000000..6c949b2
Binary files /dev/null and b/jline-1.0.jar differ
diff --git a/src/pom.xml b/src/pom.xml
new file mode 100644 (file)
index 0000000..11ebd31
--- /dev/null
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+To build, you need to have Maven 2 installed.
+
+To compile, run:
+
+    mvn compile
+
+To run tests, run:
+
+    mvn test
+
+To run one particular test, e.g. TestSomeTest, run:
+
+    mvn test -Dtest=TestSomeTest
+
+To build the jars, run:
+
+    mvn package
+
+To create and upload a release, run:
+
+    mvn deploy
+
+To build the site and upload it, run:
+
+    mvn site:deploy
+
+To perform a complete release, run:
+
+    mvn clean compile package site assembly:assembly deploy site:deploy
+
+To actually upload the artifact to sourceforge, it must be manually ftp'd:
+
+    lftp ftp://upload.sourceforge.net/incoming/ -e "put `ls target/jline-*.zip`"
+
+To make a bundle and request that ibilio upload it, do:
+
+    mvn source:jar javadoc:jar repository:bundle-create
+
+    scp target/jline-*-bundle.jar shell.sourceforge.net:/home/groups/j/jl/jline/htdocs
+
+    Make a request like at http://jira.codehaus.org/browse/MAVENUPLOAD-1003
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+    http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>jline</groupId>
+  <artifactId>jline</artifactId>
+  <packaging>jar</packaging>
+  <name>JLine</name>
+  <version>1.0</version>
+  <description>JLine is a java library for reading and editing user input in console applications. It features tab-completion, command history, password masking, customizable keybindings, and pass-through handlers to use to chain to other console applications.</description>
+  <url>http://jline.sourceforge.net</url>
+  <issueManagement>
+    <system>sourceforge</system>
+    <url>http://sourceforge.net/tracker/?group_id=64033&amp;atid=506056</url>
+  </issueManagement>
+  <inceptionYear>2002</inceptionYear>
+  <mailingLists>
+    <mailingList>
+      <name>JLine users</name>
+      <subscribe>https://lists.sourceforge.net/lists/listinfo/jline-users</subscribe>
+      <post>jline-users@lists.sourceforge.net</post>
+      <archive>http://sourceforge.net/mailarchive/forum.php?forum=jline-users</archive>
+    </mailingList>
+  </mailingLists>
+
+  <developers>
+    <developer>
+      <id>mprudhom</id>
+      <name>Marc Prud'hommeaux</name>
+      <email>mwp1@cornell.edu</email>
+    </developer>
+    <developer>
+      <id>headius</id>
+      <name>Charles Oliver Nutter</name>
+      <email>headius@headius.com</email>
+    </developer>
+  </developers>
+  <licenses>
+    <license>
+      <name>BSD</name>
+      <url>LICENSE.txt</url>
+    </license>
+  </licenses>
+  <scm>
+    <connection>scm:git://github.com/jline/jline.git</connection>
+    <developerConnection>scm:git://github.com/jline/jline.git</developerConnection>
+    <url>https://github.com/jline/jline</url>
+  </scm>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>3.8.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <useFile>false</useFile>
+          <trimStackTrace>false</trimStackTrace>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.3</source>
+          <target>1.3</target>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-site-plugin</artifactId>
+        <configuration>
+          <stagingDirectory>../site-staging</stagingDirectory>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/assembly/assembly.xml</descriptor>
+          </descriptors>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.0.0</version>
+        <executions>
+          <execution>
+            <id>bundle-manifest</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+            <configuration>
+              <instructions>
+                <Import-Package>!jline*,javax.swing;resolution:=optional,*</Import-Package>
+                <DynamicImport-Package>*</DynamicImport-Package>
+              </instructions>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>jxr-maven-plugin</artifactId>
+        <configuration>
+          <aggregate>true</aggregate>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          <aggregate>true</aggregate>
+          <linksource>true</linksource>
+          <links>
+            <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
+          </links>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-project-info-reports-plugin</artifactId>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>project-team</report>
+              <report>mailing-list</report>
+              <report>issue-tracking</report>
+              <report>license</report>
+              <report>scm</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>surefire-report-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </reporting>
+  <distributionManagement>
+    <repository>
+      <id>jline</id>
+      <url>scp://shell.sourceforge.net/home/groups/j/jl/jline/htdocs/m2repo</url>
+    </repository>
+    <snapshotRepository>
+      <id>jline</id>
+      <url>scp://shell.sourceforge.net/home/groups/j/jl/jline/htdocs/m2snapshot</url>
+    </snapshotRepository>
+    <site>
+      <id>jline</id>
+      <name>jline</name>
+      <url>scpexe://shell.sourceforge.net/home/groups/j/jl/jline/htdocs/</url>
+    </site>
+  </distributionManagement>
+</project>
diff --git a/src/src/assembly/assembly.xml b/src/src/assembly/assembly.xml
new file mode 100644 (file)
index 0000000..216c697
--- /dev/null
@@ -0,0 +1,55 @@
+<assembly>
+    <id></id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <includeBaseDirectory>true</includeBaseDirectory>
+    <fileSets>
+        <fileSet>
+            <includes>
+                <include>README*</include>
+                <include>LICENSE*</include>
+                <include>NOTICE*</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+          <directory>target</directory>
+          <outputDirectory></outputDirectory>
+          <includes>
+            <include>*.jar</include>
+          </includes>
+        </fileSet>
+        <fileSet>
+            <directory>licenses</directory>
+            <outputDirectory>/lib</outputDirectory>
+            <includes>
+                <include>*</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>src/test/java/jline/example</directory>
+            <outputDirectory>/examples/jline/example</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>src/test/resources/jline/example</directory>
+            <outputDirectory>/examples/jline/example</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>target/site/apidocs</directory>
+            <outputDirectory>/apidocs</outputDirectory>
+        </fileSet>
+
+        <!-- also include sources -->
+        <fileSet>
+          <directory>src</directory>
+          <outputDirectory>/src/src</outputDirectory>
+        </fileSet>
+        <fileSet>
+          <directory></directory>
+          <outputDirectory>/src</outputDirectory>
+          <includes>
+            <include>pom.xml</include>
+          </includes>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/src/src/main/java/jline/ANSIBuffer.java b/src/src/main/java/jline/ANSIBuffer.java
new file mode 100644 (file)
index 0000000..c2e3318
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+
+/**
+ *  A buffer that can contain ANSI text.
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ANSIBuffer {
+    private boolean ansiEnabled = true;
+    private final StringBuffer ansiBuffer = new StringBuffer();
+    private final StringBuffer plainBuffer = new StringBuffer();
+
+    public ANSIBuffer() {
+    }
+
+    public ANSIBuffer(final String str) {
+        append(str);
+    }
+
+    public void setAnsiEnabled(final boolean ansi) {
+        this.ansiEnabled = ansi;
+    }
+
+    public boolean getAnsiEnabled() {
+        return this.ansiEnabled;
+    }
+
+    public String getAnsiBuffer() {
+        return ansiBuffer.toString();
+    }
+
+    public String getPlainBuffer() {
+        return plainBuffer.toString();
+    }
+
+    public String toString(final boolean ansi) {
+        return ansi ? getAnsiBuffer() : getPlainBuffer();
+    }
+
+    public String toString() {
+        return toString(ansiEnabled);
+    }
+
+    public ANSIBuffer append(final String str) {
+        ansiBuffer.append(str);
+        plainBuffer.append(str);
+
+        return this;
+    }
+
+    public ANSIBuffer attrib(final String str, final int code) {
+        ansiBuffer.append(ANSICodes.attrib(code)).append(str)
+                  .append(ANSICodes.attrib(ANSICodes.OFF));
+        plainBuffer.append(str);
+
+        return this;
+    }
+
+    public ANSIBuffer red(final String str) {
+        return attrib(str, ANSICodes.FG_RED);
+    }
+
+    public ANSIBuffer blue(final String str) {
+        return attrib(str, ANSICodes.FG_BLUE);
+    }
+
+    public ANSIBuffer green(final String str) {
+        return attrib(str, ANSICodes.FG_GREEN);
+    }
+
+    public ANSIBuffer black(final String str) {
+        return attrib(str, ANSICodes.FG_BLACK);
+    }
+
+    public ANSIBuffer yellow(final String str) {
+        return attrib(str, ANSICodes.FG_YELLOW);
+    }
+
+    public ANSIBuffer magenta(final String str) {
+        return attrib(str, ANSICodes.FG_MAGENTA);
+    }
+
+    public ANSIBuffer cyan(final String str) {
+        return attrib(str, ANSICodes.FG_CYAN);
+    }
+
+    public ANSIBuffer bold(final String str) {
+        return attrib(str, ANSICodes.BOLD);
+    }
+
+    public ANSIBuffer underscore(final String str) {
+        return attrib(str, ANSICodes.UNDERSCORE);
+    }
+
+    public ANSIBuffer blink(final String str) {
+        return attrib(str, ANSICodes.BLINK);
+    }
+
+    public ANSIBuffer reverse(final String str) {
+        return attrib(str, ANSICodes.REVERSE);
+    }
+
+    public static class ANSICodes {
+        static final int OFF = 0;
+        static final int BOLD = 1;
+        static final int UNDERSCORE = 4;
+        static final int BLINK = 5;
+        static final int REVERSE = 7;
+        static final int CONCEALED = 8;
+        static final int FG_BLACK = 30;
+        static final int FG_RED = 31;
+        static final int FG_GREEN = 32;
+        static final int FG_YELLOW = 33;
+        static final int FG_BLUE = 34;
+        static final int FG_MAGENTA = 35;
+        static final int FG_CYAN = 36;
+        static final int FG_WHITE = 37;
+        static final char ESC = 27;
+
+        /**
+         *  Constructor is private since this is a utility class.
+         */
+        private ANSICodes() {
+        }
+
+        /**
+          * Sets the screen mode. The mode will be one of the following values:
+          * <pre>
+          * mode     description
+          * ----------------------------------------
+          *   0      40 x 148 x 25 monochrome (text)
+          *   1      40 x 148 x 25 color (text)
+          *   2      80 x 148 x 25 monochrome (text)
+          *   3      80 x 148 x 25 color (text)
+          *   4      320 x 148 x 200 4-color (graphics)
+          *   5      320 x 148 x 200 monochrome (graphics)
+          *   6      640 x 148 x 200 monochrome (graphics)
+          *   7      Enables line wrapping
+          *  13      320 x 148 x 200 color (graphics)
+          *  14      640 x 148 x 200 color (16-color graphics)
+          *  15      640 x 148 x 350 monochrome (2-color graphics)
+          *  16      640 x 148 x 350 color (16-color graphics)
+          *  17      640 x 148 x 480 monochrome (2-color graphics)
+          *  18      640 x 148 x 480 color (16-color graphics)
+          *  19      320 x 148 x 200 color (256-color graphics)
+          * </pre>
+          */
+        public static String setmode(final int mode) {
+            return ESC + "[=" + mode + "h";
+        }
+
+        /**
+          * Same as setmode () except for mode = 7, which disables line
+          * wrapping (useful for writing the right-most column without
+          * scrolling to the next line).
+          */
+        public static String resetmode(final int mode) {
+            return ESC + "[=" + mode + "l";
+        }
+
+        /**
+          * Clears the screen and moves the cursor to the home postition.
+          */
+        public static String clrscr() {
+            return ESC + "[2J";
+        }
+
+        /**
+          * Removes all characters from the current cursor position until
+          * the end of the line.
+          */
+        public static String clreol() {
+            return ESC + "[K";
+        }
+
+        /**
+          * Moves the cursor n positions to the left. If n is greater or
+          * equal to the current cursor column, the cursor is moved to the
+          * first column.
+          */
+        public static String left(final int n) {
+            return ESC + "[" + n + "D";
+        }
+
+        /**
+          * Moves the cursor n positions to the right. If n plus the current
+          * cursor column is greater than the rightmost column, the cursor
+          * is moved to the rightmost column.
+          */
+        public static String right(final int n) {
+            return ESC + "[" + n + "C";
+        }
+
+        /**
+          * Moves the cursor n rows up without changing the current column.
+          * If n is greater than or equal to the current row, the cursor is
+          * placed in the first row.
+          */
+        public static String up(final int n) {
+            return ESC + "[" + n + "A";
+        }
+
+        /**
+          * Moves the cursor n rows down. If n plus the current row is greater
+          * than the bottom row, the cursor is moved to the bottom row.
+          */
+        public static String down(final int n) {
+            return ESC + "[" + n + "B";
+        }
+
+        /*
+          * Moves the cursor to the given row and column. (1,1) represents
+          * the upper left corner. The lower right corner of a usual DOS
+          * screen is (25, 80).
+          */
+        public static String gotoxy(final int row, final int column) {
+            return ESC + "[" + row + ";" + column + "H";
+        }
+
+        /**
+          * Saves the current cursor position.
+          */
+        public static String save() {
+            return ESC + "[s";
+        }
+
+        /**
+          * Restores the saved cursor position.
+          */
+        public static String restore() {
+            return ESC + "[u";
+        }
+
+        /**
+          * Sets the character attribute. It will be
+         * one of the following character attributes:
+          *
+          * <pre>
+          * Text attributes
+          *    0    All attributes off
+          *    1    Bold on
+          *    4    Underscore (on monochrome display adapter only)
+          *    5    Blink on
+          *    7    Reverse video on
+          *    8    Concealed on
+          *
+          *   Foreground colors
+          *    30    Black
+          *    31    Red
+          *    32    Green
+          *    33    Yellow
+          *    34    Blue
+          *    35    Magenta
+          *    36    Cyan
+          *    37    White
+          *
+          *   Background colors
+          *    40    Black
+          *    41    Red
+          *    42    Green
+          *    43    Yellow
+          *    44    Blue
+          *    45    Magenta
+          *    46    Cyan
+          *    47    White
+          * </pre>
+          *
+          * The attributes remain in effect until the next attribute command
+          * is sent.
+          */
+        public static String attrib(final int attr) {
+            return ESC + "[" + attr + "m";
+        }
+
+        /**
+          * Sets the key with the given code to the given value. code must be
+          * derived from the following table, value must
+         * be any semicolon-separated
+          * combination of String (enclosed in double quotes) and numeric values.
+          * For example, to set F1 to the String "Hello F1", followed by a CRLF
+          * sequence, one can use: ANSI.setkey ("0;59", "\"Hello F1\";13;10").
+          * Heres's the table of key values:
+          * <pre>
+          * Key                       Code      SHIFT+code  CTRL+code  ALT+code
+          * ---------------------------------------------------------------
+          * F1                        0;59      0;84        0;94       0;104
+          * F2                        0;60      0;85        0;95       0;105
+          * F3                        0;61      0;86        0;96       0;106
+          * F4                        0;62      0;87        0;97       0;107
+          * F5                        0;63      0;88        0;98       0;108
+          * F6                        0;64      0;89        0;99       0;109
+          * F7                        0;65      0;90        0;100      0;110
+          * F8                        0;66      0;91        0;101      0;111
+          * F9                        0;67      0;92        0;102      0;112
+          * F10                       0;68      0;93        0;103      0;113
+          * F11                       0;133     0;135       0;137      0;139
+          * F12                       0;134     0;136       0;138      0;140
+          * HOME (num keypad)         0;71      55          0;119      --
+          * UP ARROW (num keypad)     0;72      56          (0;141)    --
+          * PAGE UP (num keypad)      0;73      57          0;132      --
+          * LEFT ARROW (num keypad)   0;75      52          0;115      --
+          * RIGHT ARROW (num keypad)  0;77      54          0;116      --
+          * END (num keypad)          0;79      49          0;117      --
+          * DOWN ARROW (num keypad)   0;80      50          (0;145)    --
+          * PAGE DOWN (num keypad)    0;81      51          0;118      --
+          * INSERT (num keypad)       0;82      48          (0;146)    --
+          * DELETE  (num keypad)      0;83      46          (0;147)    --
+          * HOME                      (224;71)  (224;71)    (224;119)  (224;151)
+          * UP ARROW                  (224;72)  (224;72)    (224;141)  (224;152)
+          * PAGE UP                   (224;73)  (224;73)    (224;132)  (224;153)
+          * LEFT ARROW                (224;75)  (224;75)    (224;115)  (224;155)
+          * RIGHT ARROW               (224;77)  (224;77)    (224;116)  (224;157)
+          * END                       (224;79)  (224;79)    (224;117)  (224;159)
+          * DOWN ARROW                (224;80)  (224;80)    (224;145)  (224;154)
+          * PAGE DOWN                 (224;81)  (224;81)    (224;118)  (224;161)
+          * INSERT                    (224;82)  (224;82)    (224;146)  (224;162)
+          * DELETE                    (224;83)  (224;83)    (224;147)  (224;163)
+          * PRINT SCREEN              --        --          0;114      --
+          * PAUSE/BREAK               --        --          0;0        --
+          * BACKSPACE                 8         8           127        (0)
+          * ENTER                     13        --          10         (0
+          * TAB                       9         0;15        (0;148)    (0;165)
+          * NULL                      0;3       --          --         --
+          * A                         97        65          1          0;30
+          * B                         98        66          2          0;48
+          * C                         99        66          3          0;46
+          * D                         100       68          4          0;32
+          * E                         101       69          5          0;18
+          * F                         102       70          6          0;33
+          * G                         103       71          7          0;34
+          * H                         104       72          8          0;35
+          * I                         105       73          9          0;23
+          * J                         106       74          10         0;36
+          * K                         107       75          11         0;37
+          * L                         108       76          12         0;38
+          * M                         109       77          13         0;50
+          * N                         110       78          14         0;49
+          * O                         111       79          15         0;24
+          * P                         112       80          16         0;25
+          * Q                         113       81          17         0;16
+          * R                         114       82          18         0;19
+          * S                         115       83          19         0;31
+          * T                         116       84          20         0;20
+          * U                         117       85          21         0;22
+          * V                         118       86          22         0;47
+          * W                         119       87          23         0;17
+          * X                         120       88          24         0;45
+          * Y                         121       89          25         0;21
+          * Z                         122       90          26         0;44
+          * 1                         49        33          --         0;120
+          * 2                         50        64          0          0;121
+          * 3                         51        35          --         0;122
+          * 4                         52        36          --         0;123
+          * 5                         53        37          --         0;124
+          * 6                         54        94          30         0;125
+          * 7                         55        38          --         0;126
+          * 8                         56        42          --         0;126
+          * 9                         57        40          --         0;127
+          * 0                         48        41          --         0;129
+          * -                         45        95          31         0;130
+          * =                         61        43          ---        0;131
+          * [                         91        123         27         0;26
+          * ]                         93        125         29         0;27
+          *                           92        124         28         0;43
+          * ;                         59        58          --         0;39
+          * '                         39        34          --         0;40
+          * ,                         44        60          --         0;51
+          * .                         46        62          --         0;52
+          * /                         47        63          --         0;53
+          * `                         96        126         --         (0;41)
+          * ENTER (keypad)            13        --          10         (0;166)
+          * / (keypad)                47        47          (0;142)    (0;74)
+          * * (keypad)                42        (0;144)     (0;78)     --
+          * - (keypad)                45        45          (0;149)    (0;164)
+          * + (keypad)                43        43          (0;150)    (0;55)
+          * 5 (keypad)                (0;76)    53          (0;143)    --
+          */
+        public static String setkey(final String code, final String value) {
+            return ESC + "[" + code + ";" + value + "p";
+        }
+    }
+
+    public static void main(final String[] args) throws Exception {
+        // sequence, one can use: ANSI.setkey ("0;59", "\"Hello F1\";13;10").
+        BufferedReader reader =
+            new BufferedReader(new InputStreamReader(System.in));
+        System.out.print(ANSICodes.setkey("97", "97;98;99;13")
+                         + ANSICodes.attrib(ANSICodes.OFF));
+        System.out.flush();
+
+        String line;
+
+        while ((line = reader.readLine()) != null) {
+            System.out.println("GOT: " + line);
+        }
+    }
+}
diff --git a/src/src/main/java/jline/ArgumentCompletor.java b/src/src/main/java/jline/ArgumentCompletor.java
new file mode 100644 (file)
index 0000000..5493ad8
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ *  A {@link Completor} implementation that invokes a child completor
+ *  using the appropriate <i>separator</i> argument. This
+ *  can be used instead of the individual completors having to
+ *  know about argument parsing semantics.
+ *  <p>
+ *  <strong>Example 1</strong>: Any argument of the command line can
+ *  use file completion.
+ *  <p>
+ *  <pre>
+ *        consoleReader.addCompletor (new ArgumentCompletor (
+ *                new {@link FileNameCompletor} ()))
+ *  </pre>
+ *  <p>
+ *  <strong>Example 2</strong>: The first argument of the command line
+ *  can be completed with any of "foo", "bar", or "baz", and remaining
+ *  arguments can be completed with a file name.
+ *  <p>
+ *  <pre>
+ *        consoleReader.addCompletor (new ArgumentCompletor (
+ *                new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"})));
+ *        consoleReader.addCompletor (new ArgumentCompletor (
+ *                new {@link FileNameCompletor} ()));
+ *  </pre>
+ *
+ *  <p>
+ *        When the argument index is past the last embedded completors, the last
+ *        completors is always used. To disable this behavior, have the last
+ *        completor be a {@link NullCompletor}. For example:
+ *        </p>
+ *
+ *        <pre>
+ *        consoleReader.addCompletor (new ArgumentCompletor (
+ *                new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"}),
+ *                new {@link SimpleCompletor} (new String [] { "xxx", "yyy", "xxx"}),
+ *                new {@link NullCompletor}
+ *                ));
+ *        </pre>
+ *  <p>
+ *  TODO: handle argument quoting and escape characters
+ *  </p>
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ArgumentCompletor implements Completor {
+    final Completor[] completors;
+    final ArgumentDelimiter delim;
+    boolean strict = true;
+
+    /**
+     *  Constuctor: create a new completor with the default
+     *  argument separator of " ".
+     *
+     *  @param  completor  the embedded completor
+     */
+    public ArgumentCompletor(final Completor completor) {
+        this(new Completor[] {
+                 completor
+             });
+    }
+
+    /**
+     *  Constuctor: create a new completor with the default
+     *  argument separator of " ".
+     *
+     *  @param  completors  the List of completors to use
+     */
+    public ArgumentCompletor(final List completors) {
+        this((Completor[]) completors.toArray(new Completor[completors.size()]));
+    }
+
+    /**
+     *  Constuctor: create a new completor with the default
+     *  argument separator of " ".
+     *
+     *  @param  completors  the embedded argument completors
+     */
+    public ArgumentCompletor(final Completor[] completors) {
+        this(completors, new WhitespaceArgumentDelimiter());
+    }
+
+    /**
+     *  Constuctor: create a new completor with the specified
+     *  argument delimiter.
+     *
+     *  @param  completor the embedded completor
+     *  @param  delim     the delimiter for parsing arguments
+     */
+    public ArgumentCompletor(final Completor completor,
+                             final ArgumentDelimiter delim) {
+        this(new Completor[] {
+                 completor
+             }, delim);
+    }
+
+    /**
+     *  Constuctor: create a new completor with the specified
+     *  argument delimiter.
+     *
+     *  @param  completors the embedded completors
+     *  @param  delim      the delimiter for parsing arguments
+     */
+    public ArgumentCompletor(final Completor[] completors,
+                             final ArgumentDelimiter delim) {
+        this.completors = completors;
+        this.delim = delim;
+    }
+
+    /**
+     *  If true, a completion at argument index N will only succeed
+     *  if all the completions from 0-(N-1) also succeed.
+     */
+    public void setStrict(final boolean strict) {
+        this.strict = strict;
+    }
+
+    /**
+     *  Returns whether a completion at argument index N will succees
+     *  if all the completions from arguments 0-(N-1) also succeed.
+     */
+    public boolean getStrict() {
+        return this.strict;
+    }
+
+    public int complete(final String buffer, final int cursor,
+                        final List candidates) {
+        ArgumentList list = delim.delimit(buffer, cursor);
+        int argpos = list.getArgumentPosition();
+        int argIndex = list.getCursorArgumentIndex();
+
+        if (argIndex < 0) {
+            return -1;
+        }
+
+        final Completor comp;
+
+        // if we are beyond the end of the completors, just use the last one
+        if (argIndex >= completors.length) {
+            comp = completors[completors.length - 1];
+        } else {
+            comp = completors[argIndex];
+        }
+
+        // ensure that all the previous completors are successful before
+        // allowing this completor to pass (only if strict is true).
+        for (int i = 0; getStrict() && (i < argIndex); i++) {
+            Completor sub =
+                completors[(i >= completors.length) ? (completors.length - 1) : i];
+            String[] args = list.getArguments();
+            String arg = ((args == null) || (i >= args.length)) ? "" : args[i];
+
+            List subCandidates = new LinkedList();
+
+            if (sub.complete(arg, arg.length(), subCandidates) == -1) {
+                return -1;
+            }
+
+            if (subCandidates.size() == 0) {
+                return -1;
+            }
+        }
+
+        int ret = comp.complete(list.getCursorArgument(), argpos, candidates);
+
+        if (ret == -1) {
+            return -1;
+        }
+
+        int pos = ret + (list.getBufferPosition() - argpos);
+
+        /**
+         *  Special case: when completing in the middle of a line, and the
+         *  area under the cursor is a delimiter, then trim any delimiters
+         *  from the candidates, since we do not need to have an extra
+         *  delimiter.
+         *
+         *  E.g., if we have a completion for "foo", and we
+         *  enter "f bar" into the buffer, and move to after the "f"
+         *  and hit TAB, we want "foo bar" instead of "foo  bar".
+         */
+        if ((cursor != buffer.length()) && delim.isDelimiter(buffer, cursor)) {
+            for (int i = 0; i < candidates.size(); i++) {
+                String val = candidates.get(i).toString();
+
+                while ((val.length() > 0)
+                    && delim.isDelimiter(val, val.length() - 1)) {
+                    val = val.substring(0, val.length() - 1);
+                }
+
+                candidates.set(i, val);
+            }
+        }
+
+        ConsoleReader.debug("Completing " + buffer + "(pos=" + cursor + ") "
+            + "with: " + candidates + ": offset=" + pos);
+
+        return pos;
+    }
+
+    /**
+     *  The {@link ArgumentCompletor.ArgumentDelimiter} allows custom
+     *  breaking up of a {@link String} into individual arguments in
+     *  order to dispatch the arguments to the nested {@link Completor}.
+     *
+     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+     */
+    public static interface ArgumentDelimiter {
+        /**
+         *  Break the specified buffer into individual tokens
+         *  that can be completed on their own.
+         *
+         *  @param  buffer           the buffer to split
+         *  @param  argumentPosition the current position of the
+         *                           cursor in the buffer
+         *  @return                  the tokens
+         */
+        ArgumentList delimit(String buffer, int argumentPosition);
+
+        /**
+         *  Returns true if the specified character is a whitespace
+         *  parameter.
+         *
+         *  @param  buffer the complete command buffer
+         *  @param  pos    the index of the character in the buffer
+         *  @return        true if the character should be a delimiter
+         */
+        boolean isDelimiter(String buffer, int pos);
+    }
+
+    /**
+     *  Abstract implementation of a delimiter that uses the
+     *  {@link #isDelimiter} method to determine if a particular
+     *  character should be used as a delimiter.
+     *
+     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+     */
+    public abstract static class AbstractArgumentDelimiter
+        implements ArgumentDelimiter {
+        private char[] quoteChars = new char[] { '\'', '"' };
+        private char[] escapeChars = new char[] { '\\' };
+
+        public void setQuoteChars(final char[] quoteChars) {
+            this.quoteChars = quoteChars;
+        }
+
+        public char[] getQuoteChars() {
+            return this.quoteChars;
+        }
+
+        public void setEscapeChars(final char[] escapeChars) {
+            this.escapeChars = escapeChars;
+        }
+
+        public char[] getEscapeChars() {
+            return this.escapeChars;
+        }
+
+        public ArgumentList delimit(final String buffer, final int cursor) {
+            List args = new LinkedList();
+            StringBuffer arg = new StringBuffer();
+            int argpos = -1;
+            int bindex = -1;
+
+            for (int i = 0; (buffer != null) && (i <= buffer.length()); i++) {
+                // once we reach the cursor, set the
+                // position of the selected index
+                if (i == cursor) {
+                    bindex = args.size();
+                    // the position in the current argument is just the
+                    // length of the current argument
+                    argpos = arg.length();
+                }
+
+                if ((i == buffer.length()) || isDelimiter(buffer, i)) {
+                    if (arg.length() > 0) {
+                        args.add(arg.toString());
+                        arg.setLength(0); // reset the arg
+                    }
+                } else {
+                    arg.append(buffer.charAt(i));
+                }
+            }
+
+            return new ArgumentList((String[]) args.
+                toArray(new String[args.size()]), bindex, argpos, cursor);
+        }
+
+        /**
+         *  Returns true if the specified character is a whitespace
+         *  parameter. Check to ensure that the character is not
+         *  escaped by any of
+         *  {@link #getQuoteChars}, and is not escaped by ant of the
+         *  {@link #getEscapeChars}, and returns true from
+         *  {@link #isDelimiterChar}.
+         *
+         *  @param  buffer the complete command buffer
+         *  @param  pos    the index of the character in the buffer
+         *  @return        true if the character should be a delimiter
+         */
+        public boolean isDelimiter(final String buffer, final int pos) {
+            if (isQuoted(buffer, pos)) {
+                return false;
+            }
+
+            if (isEscaped(buffer, pos)) {
+                return false;
+            }
+
+            return isDelimiterChar(buffer, pos);
+        }
+
+        public boolean isQuoted(final String buffer, final int pos) {
+            return false;
+        }
+
+        public boolean isEscaped(final String buffer, final int pos) {
+            if (pos <= 0) {
+                return false;
+            }
+
+            for (int i = 0; (escapeChars != null) && (i < escapeChars.length);
+                     i++) {
+                if (buffer.charAt(pos) == escapeChars[i]) {
+                    return !isEscaped(buffer, pos - 1); // escape escape
+                }
+            }
+
+            return false;
+        }
+
+        /**
+         *  Returns true if the character at the specified position
+         *  if a delimiter. This method will only be called if the
+         *  character is not enclosed in any of the
+         *  {@link #getQuoteChars}, and is not escaped by ant of the
+         *  {@link #getEscapeChars}. To perform escaping manually,
+         *  override {@link #isDelimiter} instead.
+         */
+        public abstract boolean isDelimiterChar(String buffer, int pos);
+    }
+
+    /**
+     *  {@link ArgumentCompletor.ArgumentDelimiter}
+     *  implementation that counts all
+     *  whitespace (as reported by {@link Character#isWhitespace})
+     *  as being a delimiter.
+     *
+     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+     */
+    public static class WhitespaceArgumentDelimiter
+        extends AbstractArgumentDelimiter {
+        /**
+         *  The character is a delimiter if it is whitespace, and the
+         *  preceeding character is not an escape character.
+         */
+        public boolean isDelimiterChar(String buffer, int pos) {
+            return Character.isWhitespace(buffer.charAt(pos));
+        }
+    }
+
+    /**
+     *  The result of a delimited buffer.
+     *
+     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+     */
+    public static class ArgumentList {
+        private String[] arguments;
+        private int cursorArgumentIndex;
+        private int argumentPosition;
+        private int bufferPosition;
+
+        /**
+         *  @param  arguments           the array of tokens
+         *  @param  cursorArgumentIndex the token index of the cursor
+         *  @param  argumentPosition    the position of the cursor in the
+         *                              current token
+         *  @param  bufferPosition      the position of the cursor in
+         *                              the whole buffer
+         */
+        public ArgumentList(String[] arguments, int cursorArgumentIndex,
+            int argumentPosition, int bufferPosition) {
+            this.arguments = arguments;
+            this.cursorArgumentIndex = cursorArgumentIndex;
+            this.argumentPosition = argumentPosition;
+            this.bufferPosition = bufferPosition;
+        }
+
+        public void setCursorArgumentIndex(int cursorArgumentIndex) {
+            this.cursorArgumentIndex = cursorArgumentIndex;
+        }
+
+        public int getCursorArgumentIndex() {
+            return this.cursorArgumentIndex;
+        }
+
+        public String getCursorArgument() {
+            if ((cursorArgumentIndex < 0)
+                || (cursorArgumentIndex >= arguments.length)) {
+                return null;
+            }
+
+            return arguments[cursorArgumentIndex];
+        }
+
+        public void setArgumentPosition(int argumentPosition) {
+            this.argumentPosition = argumentPosition;
+        }
+
+        public int getArgumentPosition() {
+            return this.argumentPosition;
+        }
+
+        public void setArguments(String[] arguments) {
+            this.arguments = arguments;
+        }
+
+        public String[] getArguments() {
+            return this.arguments;
+        }
+
+        public void setBufferPosition(int bufferPosition) {
+            this.bufferPosition = bufferPosition;
+        }
+
+        public int getBufferPosition() {
+            return this.bufferPosition;
+        }
+    }
+}
diff --git a/src/src/main/java/jline/CandidateCycleCompletionHandler.java b/src/src/main/java/jline/CandidateCycleCompletionHandler.java
new file mode 100644 (file)
index 0000000..a0bf208
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *  <p>
+ *  A {@link CompletionHandler} that deals with multiple distinct completions
+ *  by cycling through each one every time tab is pressed. This
+ *  mimics the behavior of the
+ *  <a href="http://packages.qa.debian.org/e/editline.html">editline</a>
+ *  library.
+ *  </p>
+ *  <p><strong>This class is currently a stub; it does nothing</strong></p>
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class CandidateCycleCompletionHandler implements CompletionHandler {
+    public boolean complete(final ConsoleReader reader, final List candidates,
+                            final int position) throws IOException {
+        throw new IllegalStateException("CandidateCycleCompletionHandler unimplemented");
+    }
+}
diff --git a/src/src/main/java/jline/CandidateListCompletionHandler.java b/src/src/main/java/jline/CandidateListCompletionHandler.java
new file mode 100644 (file)
index 0000000..d9d43c2
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.text.MessageFormat;
+import java.util.*;
+
+/**
+ *  <p>
+ *  A {@link CompletionHandler} that deals with multiple distinct completions
+ *  by outputting the complete list of possibilities to the console. This
+ *  mimics the behavior of the
+ *  <a href="http://www.gnu.org/directory/readline.html">readline</a>
+ *  library.
+ *  </p>
+ *
+ *  <strong>TODO:</strong>
+ *  <ul>
+ *        <li>handle quotes and escaped quotes</li>
+ *        <li>enable automatic escaping of whitespace</li>
+ *  </ul>
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class CandidateListCompletionHandler implements CompletionHandler {
+    private static ResourceBundle loc = ResourceBundle.
+        getBundle(CandidateListCompletionHandler.class.getName());
+
+    private boolean eagerNewlines = true;
+
+    public void setAlwaysIncludeNewline(boolean eagerNewlines) {
+        this.eagerNewlines = eagerNewlines;
+    }
+
+    public boolean complete(final ConsoleReader reader, final List candidates,
+                            final int pos) throws IOException {
+        CursorBuffer buf = reader.getCursorBuffer();
+
+        // if there is only one completion, then fill in the buffer
+        if (candidates.size() == 1) {
+            String value = candidates.get(0).toString();
+
+            // fail if the only candidate is the same as the current buffer
+            if (value.equals(buf.toString())) {
+                return false;
+            }
+
+            setBuffer(reader, value, pos);
+
+            return true;
+        } else if (candidates.size() > 1) {
+            String value = getUnambiguousCompletions(candidates);
+            String bufString = buf.toString();
+            setBuffer(reader, value, pos);
+        }
+
+        if (eagerNewlines)
+            reader.printNewline();
+        printCandidates(reader, candidates, eagerNewlines);
+
+        // redraw the current console buffer
+        reader.drawLine();
+
+        return true;
+    }
+
+    public static void setBuffer(ConsoleReader reader, String value, int offset)
+                           throws IOException {
+        while ((reader.getCursorBuffer().cursor > offset)
+                   && reader.backspace()) {
+            ;
+        }
+
+        reader.putString(value);
+        reader.setCursorPosition(offset + value.length());
+    }
+
+    /**
+     *  Print out the candidates. If the size of the candidates
+     *  is greated than the {@link getAutoprintThreshhold},
+     *  they prompt with aq warning.
+     *
+     *  @param  candidates  the list of candidates to print
+     */
+    public static final void printCandidates(ConsoleReader reader,
+                                       Collection candidates, boolean eagerNewlines)
+                                throws IOException {
+        Set distinct = new HashSet(candidates);
+
+        if (distinct.size() > reader.getAutoprintThreshhold()) {
+            if (!eagerNewlines)
+                reader.printNewline();
+            reader.printString(MessageFormat.format
+                (loc.getString("display-candidates"), new Object[] {
+                    new Integer(candidates .size())
+                    }) + " ");
+
+            reader.flushConsole();
+
+            int c;
+
+            String noOpt = loc.getString("display-candidates-no");
+            String yesOpt = loc.getString("display-candidates-yes");
+
+            while ((c = reader.readCharacter(new char[] {
+                yesOpt.charAt(0), noOpt.charAt(0) })) != -1) {
+                if (noOpt.startsWith
+                    (new String(new char[] { (char) c }))) {
+                    reader.printNewline();
+                    return;
+                } else if (yesOpt.startsWith
+                    (new String(new char[] { (char) c }))) {
+                    break;
+                } else {
+                    reader.beep();
+                }
+            }
+        }
+
+        // copy the values and make them distinct, without otherwise
+        // affecting the ordering. Only do it if the sizes differ.
+        if (distinct.size() != candidates.size()) {
+            Collection copy = new ArrayList();
+
+            for (Iterator i = candidates.iterator(); i.hasNext();) {
+                Object next = i.next();
+
+                if (!(copy.contains(next))) {
+                    copy.add(next);
+                }
+            }
+
+            candidates = copy;
+        }
+
+        reader.printNewline();
+        reader.printColumns(candidates);
+    }
+
+    /**
+     *  Returns a root that matches all the {@link String} elements
+     *  of the specified {@link List}, or null if there are
+     *  no commalities. For example, if the list contains
+     *  <i>foobar</i>, <i>foobaz</i>, <i>foobuz</i>, the
+     *  method will return <i>foob</i>.
+     */
+    private final String getUnambiguousCompletions(final List candidates) {
+        if ((candidates == null) || (candidates.size() == 0)) {
+            return null;
+        }
+
+        // convert to an array for speed
+        String[] strings =
+            (String[]) candidates.toArray(new String[candidates.size()]);
+
+        String first = strings[0];
+        StringBuffer candidate = new StringBuffer();
+
+        for (int i = 0; i < first.length(); i++) {
+            if (startsWith(first.substring(0, i + 1), strings)) {
+                candidate.append(first.charAt(i));
+            } else {
+                break;
+            }
+        }
+
+        return candidate.toString();
+    }
+
+    /**
+     *  @return  true is all the elements of <i>candidates</i>
+     *                          start with <i>starts</i>
+     */
+    private final boolean startsWith(final String starts,
+                                     final String[] candidates) {
+        for (int i = 0; i < candidates.length; i++) {
+            if (!candidates[i].startsWith(starts)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/src/src/main/java/jline/ClassNameCompletor.java b/src/src/main/java/jline/ClassNameCompletor.java
new file mode 100644 (file)
index 0000000..3ef5802
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ *  A Completor implementation that completes java class names. By default,
+ *  it scans the java class path to locate all the classes.
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ClassNameCompletor extends SimpleCompletor {
+
+    /**
+     *  Complete candidates using all the classes available in the
+     *  java <em>CLASSPATH</em>.
+     */
+    public ClassNameCompletor() throws IOException {
+        this(null);
+    }
+
+    public ClassNameCompletor(final SimpleCompletorFilter filter)
+        throws IOException {
+        super(getClassNames(), filter);
+        setDelimiter(".");
+    }
+
+    public static String[] getClassNames() throws IOException {
+        Set urls = new HashSet();
+
+        for (ClassLoader loader = ClassNameCompletor.class
+            .getClassLoader(); loader != null;
+                 loader = loader.getParent()) {
+            if (!(loader instanceof URLClassLoader)) {
+                continue;
+            }
+
+            urls.addAll(Arrays.asList(((URLClassLoader) loader).getURLs()));
+        }
+
+        // Now add the URL that holds java.lang.String. This is because
+        // some JVMs do not report the core classes jar in the list of
+        // class loaders.
+        Class[] systemClasses = new Class[] {
+            String.class, javax.swing.JFrame.class
+            };
+
+        for (int i = 0; i < systemClasses.length; i++) {
+            URL classURL = systemClasses[i].getResource("/"
+                + systemClasses[i].getName() .replace('.', '/') + ".class");
+
+            if (classURL != null) {
+                URLConnection uc = (URLConnection) classURL.openConnection();
+
+                if (uc instanceof JarURLConnection) {
+                    urls.add(((JarURLConnection) uc).getJarFileURL());
+                }
+            }
+        }
+
+        Set classes = new HashSet();
+
+        for (Iterator i = urls.iterator(); i.hasNext();) {
+            URL url = (URL) i.next();
+            File file = new File(url.getFile());
+
+            if (file.isDirectory()) {
+                Set files = getClassFiles(file.getAbsolutePath(),
+                    new HashSet(), file, new int[] { 200 });
+                classes.addAll(files);
+
+                continue;
+            }
+
+            if ((file == null) || !file.isFile()) // TODO: handle directories
+             {
+                continue;
+            }
+            if (!file.toString().endsWith (".jar"))
+                continue;
+
+            JarFile jf = new JarFile(file);
+
+            for (Enumeration e = jf.entries(); e.hasMoreElements();) {
+                JarEntry entry = (JarEntry) e.nextElement();
+
+                if (entry == null) {
+                    continue;
+                }
+
+                String name = entry.getName();
+
+                if (!name.endsWith(".class")) // only use class files
+                 {
+                    continue;
+                }
+
+                classes.add(name);
+            }
+        }
+
+        // now filter classes by changing "/" to "." and trimming the
+        // trailing ".class"
+        Set classNames = new TreeSet();
+
+        for (Iterator i = classes.iterator(); i.hasNext();) {
+            String name = (String) i.next();
+            classNames.add(name.replace('/', '.').
+                substring(0, name.length() - 6));
+        }
+
+        return (String[]) classNames.toArray(new String[classNames.size()]);
+    }
+
+    private static Set getClassFiles(String root, Set holder, File directory,
+        int[] maxDirectories) {
+        // we have passed the maximum number of directories to scan
+        if (maxDirectories[0]-- < 0) {
+            return holder;
+        }
+
+        File[] files = directory.listFiles();
+
+        for (int i = 0; (files != null) && (i < files.length); i++) {
+            String name = files[i].getAbsolutePath();
+
+            if (!(name.startsWith(root))) {
+                continue;
+            } else if (files[i].isDirectory()) {
+                getClassFiles(root, holder, files[i], maxDirectories);
+            } else if (files[i].getName().endsWith(".class")) {
+                holder.add(files[i].getAbsolutePath().
+                    substring(root.length() + 1));
+            }
+        }
+
+        return holder;
+    }
+}
diff --git a/src/src/main/java/jline/CompletionHandler.java b/src/src/main/java/jline/CompletionHandler.java
new file mode 100644 (file)
index 0000000..6f524da
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *  Handler for dealing with candidates for tab-completion.
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public interface CompletionHandler {
+    boolean complete(ConsoleReader reader, List candidates, int position)
+              throws IOException;
+}
diff --git a/src/src/main/java/jline/Completor.java b/src/src/main/java/jline/Completor.java
new file mode 100644 (file)
index 0000000..ad29cf9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ *  A Completor is the mechanism by which tab-completion candidates
+ *  will be resolved.
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public interface Completor {
+    /**
+     *  Populates <i>candidates</i> with a list of possible
+     *  completions for the <i>buffer</i>. The <i>candidates</i>
+     *  list will not be sorted before being displayed to the
+     *  user: thus, the complete method should sort the
+     *  {@link List} before returning.
+     *
+     *
+     *  @param  buffer     the buffer
+     *  @param  candidates the {@link List} of candidates to populate
+     *  @return            the index of the <i>buffer</i> for which
+     *                     the completion will be relative
+     */
+    int complete(String buffer, int cursor, List candidates);
+}
diff --git a/src/src/main/java/jline/ConsoleOperations.java b/src/src/main/java/jline/ConsoleOperations.java
new file mode 100644 (file)
index 0000000..16aa0e7
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.awt.event.KeyEvent;
+
+/**
+ *  Symbolic constants for Console operations and virtual key bindings.
+ *  @see KeyEvent
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public interface ConsoleOperations {
+    final String CR = System.getProperty("line.separator");
+    final char BACKSPACE = '\b';
+    final char RESET_LINE = '\r';
+    final char KEYBOARD_BELL = '\07';
+    final char CTRL_A = 1;
+    final char CTRL_B = 2;
+    final char CTRL_C = 3;
+    final char CTRL_D = 4;
+    final char CTRL_E = 5;
+    final char CTRL_F = 6;
+    final char CTRL_G = 7;
+    final static char CTRL_K = 11;
+    final static char CTRL_L = 12;
+    final char CTRL_N = 14;
+    final char CTRL_P = 16;
+    final static char CTRL_OB = 27;
+    final static char DELETE = 127;
+    final static char CTRL_QM = 127;
+
+
+    /**
+     *        Logical constants for key operations.
+     */
+
+    /**
+     *  Unknown operation.
+     */
+    final short UNKNOWN = -99;
+
+    /**
+     *  Operation that moves to the beginning of the buffer.
+     */
+    final short MOVE_TO_BEG = -1;
+
+    /**
+     *  Operation that moves to the end of the buffer.
+     */
+    final short MOVE_TO_END = -3;
+
+    /**
+     *  Operation that moved to the previous character in the buffer.
+     */
+    final short PREV_CHAR = -4;
+
+    /**
+     *  Operation that issues a newline.
+     */
+    final short NEWLINE = -6;
+
+    /**
+     *  Operation that deletes the buffer from the current character to the end.
+     */
+    final short KILL_LINE = -7;
+
+    /**
+     *  Operation that clears the screen.
+     */
+    final short CLEAR_SCREEN = -8;
+
+    /**
+     *  Operation that sets the buffer to the next history item.
+     */
+    final short NEXT_HISTORY = -9;
+
+    /**
+     *  Operation that sets the buffer to the previous history item.
+     */
+    final short PREV_HISTORY = -11;
+
+    /**
+     *  Operation that redisplays the current buffer.
+     */
+    final short REDISPLAY = -13;
+
+    /**
+     *  Operation that deletes the buffer from the cursor to the beginning.
+     */
+    final short KILL_LINE_PREV = -15;
+
+    /**
+     *  Operation that deletes the previous word in the buffer.
+     */
+    final short DELETE_PREV_WORD = -16;
+
+    /**
+     *  Operation that moves to the next character in the buffer.
+     */
+    final short NEXT_CHAR = -19;
+
+    /**
+     *  Operation that moves to the previous character in the buffer.
+     */
+    final short REPEAT_PREV_CHAR = -20;
+
+    /**
+     *  Operation that searches backwards in the command history.
+     */
+    final short SEARCH_PREV = -21;
+
+    /**
+     *  Operation that repeats the character.
+     */
+    final short REPEAT_NEXT_CHAR = -24;
+
+    /**
+     *  Operation that searches forward in the command history.
+     */
+    final short SEARCH_NEXT = -25;
+
+    /**
+     *  Operation that moved to the previous whitespace.
+     */
+    final short PREV_SPACE_WORD = -27;
+
+    /**
+     *  Operation that moved to the end of the current word.
+     */
+    final short TO_END_WORD = -29;
+
+    /**
+     *  Operation that
+     */
+    final short REPEAT_SEARCH_PREV = -34;
+
+    /**
+     *  Operation that
+     */
+    final short PASTE_PREV = -36;
+
+    /**
+     *  Operation that
+     */
+    final short REPLACE_MODE = -37;
+
+    /**
+     *  Operation that
+     */
+    final short SUBSTITUTE_LINE = -38;
+
+    /**
+     *  Operation that
+     */
+    final short TO_PREV_CHAR = -39;
+
+    /**
+     *  Operation that
+     */
+    final short NEXT_SPACE_WORD = -40;
+
+    /**
+     *  Operation that
+     */
+    final short DELETE_PREV_CHAR = -41;
+
+    /**
+     *  Operation that
+     */
+    final short ADD = -42;
+
+    /**
+     *  Operation that
+     */
+    final short PREV_WORD = -43;
+
+    /**
+     *  Operation that
+     */
+    final short CHANGE_META = -44;
+
+    /**
+     *  Operation that
+     */
+    final short DELETE_META = -45;
+
+    /**
+     *  Operation that
+     */
+    final short END_WORD = -46;
+
+    /**
+     *  Operation that toggles insert/overtype
+     */
+    final short INSERT = -48;
+
+    /**
+     *  Operation that
+     */
+    final short REPEAT_SEARCH_NEXT = -49;
+
+    /**
+     *  Operation that
+     */
+    final short PASTE_NEXT = -50;
+
+    /**
+     *  Operation that
+     */
+    final short REPLACE_CHAR = -51;
+
+    /**
+     *  Operation that
+     */
+    final short SUBSTITUTE_CHAR = -52;
+
+    /**
+     *  Operation that
+     */
+    final short TO_NEXT_CHAR = -53;
+
+    /**
+     *  Operation that undoes the previous operation.
+     */
+    final short UNDO = -54;
+
+    /**
+     *  Operation that moved to the next word.
+     */
+    final short NEXT_WORD = -55;
+
+    /**
+     *  Operation that deletes the previous character.
+     */
+    final short DELETE_NEXT_CHAR = -56;
+
+    /**
+     *  Operation that toggles between uppercase and lowercase.
+     */
+    final short CHANGE_CASE = -57;
+
+    /**
+     *  Operation that performs completion operation on the current word.
+     */
+    final short COMPLETE = -58;
+
+    /**
+     *  Operation that exits the command prompt.
+     */
+    final short EXIT = -59;
+
+    /**
+     *  Operation that pastes the contents of the clipboard into the line
+     */
+    final short PASTE = -60;
+
+    /**
+     * Operation that moves the current History to the beginning.
+     */
+    final static short START_OF_HISTORY = -61;
+
+    /**
+     * Operation that moves the current History to the end.
+     */
+    final static short END_OF_HISTORY = -62;
+
+    /**
+     * Operation that clears whatever text is on the current line.
+     */
+    final static short CLEAR_LINE = -63;
+
+    /**
+     * Operation that aborts the current command (like searching)
+     */
+    final static short ABORT = -64;
+
+}
diff --git a/src/src/main/java/jline/ConsoleReader.java b/src/src/main/java/jline/ConsoleReader.java
new file mode 100644 (file)
index 0000000..18339d4
--- /dev/null
@@ -0,0 +1,1823 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.awt.*;
+import java.awt.datatransfer.*;
+import java.awt.event.ActionListener;
+
+import java.io.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * A reader for console applications. It supports custom tab-completion,
+ * saveable command history, and command line editing. On some platforms,
+ * platform-specific commands will need to be issued before the reader will
+ * function properly. See {@link Terminal#initializeTerminal} for convenience
+ * methods for issuing platform-specific setup commands.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ConsoleReader implements ConsoleOperations {
+
+    final static int TAB_WIDTH = 4;
+    String prompt;
+    private boolean useHistory = true;
+    private boolean usePagination = false;
+    public static final String CR = System.getProperty("line.separator");
+    private static ResourceBundle loc = ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName());
+    /**
+     * Map that contains the operation name to keymay operation mapping.
+     */
+    public static SortedMap KEYMAP_NAMES;
+
+
+    static {
+        Map names = new TreeMap();
+
+        names.put("MOVE_TO_BEG", new Short(MOVE_TO_BEG));
+        names.put("MOVE_TO_END", new Short(MOVE_TO_END));
+        names.put("PREV_CHAR", new Short(PREV_CHAR));
+        names.put("NEWLINE", new Short(NEWLINE));
+        names.put("KILL_LINE", new Short(KILL_LINE));
+        names.put("PASTE", new Short(PASTE));
+        names.put("CLEAR_SCREEN", new Short(CLEAR_SCREEN));
+        names.put("NEXT_HISTORY", new Short(NEXT_HISTORY));
+        names.put("PREV_HISTORY", new Short(PREV_HISTORY));
+        names.put("START_OF_HISTORY", new Short(START_OF_HISTORY));
+        names.put("END_OF_HISTORY", new Short(END_OF_HISTORY));
+        names.put("REDISPLAY", new Short(REDISPLAY));
+        names.put("KILL_LINE_PREV", new Short(KILL_LINE_PREV));
+        names.put("DELETE_PREV_WORD", new Short(DELETE_PREV_WORD));
+        names.put("NEXT_CHAR", new Short(NEXT_CHAR));
+        names.put("REPEAT_PREV_CHAR", new Short(REPEAT_PREV_CHAR));
+        names.put("SEARCH_PREV", new Short(SEARCH_PREV));
+        names.put("REPEAT_NEXT_CHAR", new Short(REPEAT_NEXT_CHAR));
+        names.put("SEARCH_NEXT", new Short(SEARCH_NEXT));
+        names.put("PREV_SPACE_WORD", new Short(PREV_SPACE_WORD));
+        names.put("TO_END_WORD", new Short(TO_END_WORD));
+        names.put("REPEAT_SEARCH_PREV", new Short(REPEAT_SEARCH_PREV));
+        names.put("PASTE_PREV", new Short(PASTE_PREV));
+        names.put("REPLACE_MODE", new Short(REPLACE_MODE));
+        names.put("SUBSTITUTE_LINE", new Short(SUBSTITUTE_LINE));
+        names.put("TO_PREV_CHAR", new Short(TO_PREV_CHAR));
+        names.put("NEXT_SPACE_WORD", new Short(NEXT_SPACE_WORD));
+        names.put("DELETE_PREV_CHAR", new Short(DELETE_PREV_CHAR));
+        names.put("ADD", new Short(ADD));
+        names.put("PREV_WORD", new Short(PREV_WORD));
+        names.put("CHANGE_META", new Short(CHANGE_META));
+        names.put("DELETE_META", new Short(DELETE_META));
+        names.put("END_WORD", new Short(END_WORD));
+        names.put("NEXT_CHAR", new Short(NEXT_CHAR));
+        names.put("INSERT", new Short(INSERT));
+        names.put("REPEAT_SEARCH_NEXT", new Short(REPEAT_SEARCH_NEXT));
+        names.put("PASTE_NEXT", new Short(PASTE_NEXT));
+        names.put("REPLACE_CHAR", new Short(REPLACE_CHAR));
+        names.put("SUBSTITUTE_CHAR", new Short(SUBSTITUTE_CHAR));
+        names.put("TO_NEXT_CHAR", new Short(TO_NEXT_CHAR));
+        names.put("UNDO", new Short(UNDO));
+        names.put("NEXT_WORD", new Short(NEXT_WORD));
+        names.put("DELETE_NEXT_CHAR", new Short(DELETE_NEXT_CHAR));
+        names.put("CHANGE_CASE", new Short(CHANGE_CASE));
+        names.put("COMPLETE", new Short(COMPLETE));
+        names.put("EXIT", new Short(EXIT));
+        names.put("CLEAR_LINE", new Short(CLEAR_LINE));
+        names.put("ABORT", new Short(ABORT));
+
+        KEYMAP_NAMES = new TreeMap(Collections.unmodifiableMap(names));
+    }
+    /**
+     * The map for logical operations.
+     */
+    private final short[] keybindings;
+    /**
+     * If true, issue an audible keyboard bell when appropriate.
+     */
+    private boolean bellEnabled = true;
+    /**
+     * The current character mask.
+     */
+    private Character mask = null;
+    /**
+     * The null mask.
+     */
+    private static final Character NULL_MASK = new Character((char) 0);
+    /**
+     * The number of tab-completion candidates above which a warning will be
+     * prompted before showing all the candidates.
+     */
+    private int autoprintThreshhold = Integer.getInteger(
+            "jline.completion.threshold", 100).intValue(); // same default as
+
+    // bash
+    /**
+     * The Terminal to use.
+     */
+    private final Terminal terminal;
+    private CompletionHandler completionHandler = new CandidateListCompletionHandler();
+    InputStream in;
+    final Writer out;
+    final CursorBuffer buf = new CursorBuffer();
+    static PrintWriter debugger;
+    History history = new History();
+    final List completors = new LinkedList();
+    private Character echoCharacter = null;
+    private Map triggeredActions = new HashMap();
+
+    private StringBuffer searchTerm = null;
+    private String previousSearchTerm = "";
+    private int searchIndex = -1;
+
+    /**
+     * Adding a triggered Action allows to give another course of action
+     * if a character passed the preprocessing.
+     *
+     * Say you want to close the application if the user enter q.
+     * addTriggerAction('q', new ActionListener(){ System.exit(0); });
+     * would do the trick.
+     *
+     * @param c
+     * @param listener
+     */
+    public void addTriggeredAction(char c, ActionListener listener) {
+        triggeredActions.put(new Character(c), listener);
+    }
+
+    /**
+     * Create a new reader using {@link FileDescriptor#in} for input and
+     * {@link System#out} for output. {@link FileDescriptor#in} is used because
+     * it has a better chance of being unbuffered.
+     */
+    public ConsoleReader() throws IOException {
+        this(new FileInputStream(FileDescriptor.in),
+                new PrintWriter(
+                new OutputStreamWriter(System.out,
+                System.getProperty("jline.WindowsTerminal.output.encoding", System.getProperty("file.encoding")))));
+    }
+
+    /**
+     * Create a new reader using the specified {@link InputStream} for input and
+     * the specific writer for output, using the default keybindings resource.
+     */
+    public ConsoleReader(final InputStream in, final Writer out)
+            throws IOException {
+        this(in, out, null);
+    }
+
+    public ConsoleReader(final InputStream in, final Writer out,
+            final InputStream bindings) throws IOException {
+        this(in, out, bindings, Terminal.getTerminal());
+    }
+
+    /**
+     * Create a new reader.
+     *
+     * @param in
+     *            the input
+     * @param out
+     *            the output
+     * @param bindings
+     *            the key bindings to use
+     * @param term
+     *            the terminal to use
+     */
+    public ConsoleReader(InputStream in, Writer out, InputStream bindings,
+            Terminal term) throws IOException {
+        this.terminal = term;
+        setInput(in);
+        this.out = out;
+        if (bindings == null) {
+            try {
+                String bindingFile = System.getProperty("jline.keybindings",
+                    new File(System.getProperty("user.home"),
+                        ".jlinebindings.properties").getAbsolutePath());
+
+                if (new File(bindingFile).isFile()) {
+                    bindings = new FileInputStream(new File(bindingFile));
+                }
+            } catch (Exception e) {
+                // swallow exceptions with option debugging
+                if (debugger != null) {
+                    e.printStackTrace(debugger);
+                }
+            }
+        }
+
+        if (bindings == null) {
+            bindings = terminal.getDefaultBindings();
+        }
+
+        this.keybindings = new short[Character.MAX_VALUE * 2];
+
+        Arrays.fill(this.keybindings, UNKNOWN);
+
+        /**
+         * Loads the key bindings. Bindings file is in the format:
+         *
+         * keycode: operation name
+         */
+        if (bindings != null) {
+            Properties p = new Properties();
+            p.load(bindings);
+            bindings.close();
+
+            for (Iterator i = p.keySet().iterator(); i.hasNext();) {
+                String val = (String) i.next();
+
+                try {
+                    Short code = new Short(val);
+                    String op = (String) p.getProperty(val);
+
+                    Short opval = (Short) KEYMAP_NAMES.get(op);
+
+                    if (opval != null) {
+                        keybindings[code.shortValue()] = opval.shortValue();
+                    }
+                } catch (NumberFormatException nfe) {
+                    consumeException(nfe);
+                }
+            }
+
+        // hardwired arrow key bindings
+        // keybindings[VK_UP] = PREV_HISTORY;
+        // keybindings[VK_DOWN] = NEXT_HISTORY;
+        // keybindings[VK_LEFT] = PREV_CHAR;
+        // keybindings[VK_RIGHT] = NEXT_CHAR;
+        }
+    }
+
+    public Terminal getTerminal() {
+        return this.terminal;
+    }
+
+    /**
+     * Set the stream for debugging. Development use only.
+     */
+    public void setDebug(final PrintWriter debugger) {
+        ConsoleReader.debugger = debugger;
+    }
+
+    /**
+     * Set the stream to be used for console input.
+     */
+    public void setInput(final InputStream in) {
+        this.in = in;
+    }
+
+    /**
+     * Returns the stream used for console input.
+     */
+    public InputStream getInput() {
+        return this.in;
+    }
+
+    /**
+     * Read the next line and return the contents of the buffer.
+     */
+    public String readLine() throws IOException {
+        return readLine((String) null);
+    }
+
+    /**
+     * Read the next line with the specified character mask. If null, then
+     * characters will be echoed. If 0, then no characters will be echoed.
+     */
+    public String readLine(final Character mask) throws IOException {
+        return readLine(null, mask);
+    }
+
+    /**
+     * @param bellEnabled
+     *            if true, enable audible keyboard bells if an alert is
+     *            required.
+     */
+    public void setBellEnabled(final boolean bellEnabled) {
+        this.bellEnabled = bellEnabled;
+    }
+
+    /**
+     * @return true is audible keyboard bell is enabled.
+     */
+    public boolean getBellEnabled() {
+        return this.bellEnabled;
+    }
+
+    /**
+     * Query the terminal to find the current width;
+     *
+     * @see Terminal#getTerminalWidth
+     * @return the width of the current terminal.
+     */
+    public int getTermwidth() {
+        return getTerminal().getTerminalWidth();
+    }
+
+    /**
+     * Query the terminal to find the current width;
+     *
+     * @see Terminal#getTerminalHeight
+     *
+     * @return the height of the current terminal.
+     */
+    public int getTermheight() {
+        return getTerminal().getTerminalHeight();
+    }
+
+    /**
+     * @param autoprintThreshhold
+     *            the number of candidates to print without issuing a warning.
+     */
+    public void setAutoprintThreshhold(final int autoprintThreshhold) {
+        this.autoprintThreshhold = autoprintThreshhold;
+    }
+
+    /**
+     * @return the number of candidates to print without issing a warning.
+     */
+    public int getAutoprintThreshhold() {
+        return this.autoprintThreshhold;
+    }
+
+    int getKeyForAction(short logicalAction) {
+        for (int i = 0; i < keybindings.length; i++) {
+            if (keybindings[i] == logicalAction) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Clear the echoed characters for the specified character code.
+     */
+    int clearEcho(int c) throws IOException {
+        // if the terminal is not echoing, then just return...
+        if (!terminal.getEcho()) {
+            return 0;
+        }
+
+        // otherwise, clear
+        int num = countEchoCharacters((char) c);
+        back(num);
+        drawBuffer(num);
+
+        return num;
+    }
+
+    int countEchoCharacters(char c) {
+        // tabs as special: we need to determine the number of spaces
+        // to cancel based on what out current cursor position is
+        if (c == 9) {
+            int tabstop = 8; // will this ever be different?
+            int position = getCursorPosition();
+
+            return tabstop - (position % tabstop);
+        }
+
+        return getPrintableCharacters(c).length();
+    }
+
+    /**
+     * Return the number of characters that will be printed when the specified
+     * character is echoed to the screen. Adapted from cat by Torbjorn Granlund,
+     * as repeated in stty by David MacKenzie.
+     */
+    StringBuffer getPrintableCharacters(char ch) {
+        StringBuffer sbuff = new StringBuffer();
+
+        if (ch >= 32) {
+            if (ch < 127) {
+                sbuff.append(ch);
+            } else if (ch == 127) {
+                sbuff.append('^');
+                sbuff.append('?');
+            } else {
+                sbuff.append('M');
+                sbuff.append('-');
+
+                if (ch >= (128 + 32)) {
+                    if (ch < (128 + 127)) {
+                        sbuff.append((char) (ch - 128));
+                    } else {
+                        sbuff.append('^');
+                        sbuff.append('?');
+                    }
+                } else {
+                    sbuff.append('^');
+                    sbuff.append((char) (ch - 128 + 64));
+                }
+            }
+        } else {
+            sbuff.append('^');
+            sbuff.append((char) (ch + 64));
+        }
+
+        return sbuff;
+    }
+
+    int getCursorPosition() {
+        // FIXME: does not handle anything but a line with a prompt
+        // absolute position
+        return getStrippedAnsiLength(prompt) + buf.cursor;
+    }
+
+    /**
+     * Strips ANSI escape sequences starting with CSI and ending with char in range 64-126
+     * @param ansiString String possibly containing ANSI codes, may be null
+     * @return length after stripping ANSI codes
+     */
+    int getStrippedAnsiLength(String ansiString) {
+        if (ansiString ==  null) return 0;
+        boolean inAnsi = false;
+        int strippedLength = 0;
+        char[] chars = ansiString.toCharArray();
+        for (int i = 0; i < chars.length; i++) {
+            char c = chars[i];
+            if (!inAnsi && c == 27 && i < chars.length - 1 && chars[i+1] == '[') {
+                i++; // skip '['
+                inAnsi = true;
+            } else if (inAnsi) {
+                if (64 <= c && c <= 126) {
+                    inAnsi = false;
+                }
+            } else {
+                strippedLength++;
+            }
+        }
+        return strippedLength;
+    }
+
+    public String readLine(final String prompt) throws IOException {
+        return readLine(prompt, null);
+    }
+
+    /**
+     * The default prompt that will be issued.
+     */
+    public void setDefaultPrompt(String prompt) {
+        this.prompt = prompt;
+    }
+
+    /**
+     * The default prompt that will be issued.
+     */
+    public String getDefaultPrompt() {
+        return prompt;
+    }
+
+    /**
+     * Read a line from the <i>in</i> {@link InputStream}, and return the line
+     * (without any trailing newlines).
+     *
+     * @param prompt
+     *            the prompt to issue to the console, may be null.
+     * @return a line that is read from the terminal, or null if there was null
+     *         input (e.g., <i>CTRL-D</i> was pressed).
+     */
+    public String readLine(final String prompt, final Character mask)
+            throws IOException {
+        this.mask = mask;
+        if (prompt != null) {
+            this.prompt = prompt;
+        }
+
+        try {
+            terminal.beforeReadLine(this, this.prompt, mask);
+
+            if ((this.prompt != null) && (this.prompt.length() > 0)) {
+                out.write(this.prompt);
+                out.flush();
+            }
+
+            // if the terminal is unsupported, just use plain-java reading
+            if (!terminal.isSupported()) {
+                return readLine(in);
+            }
+
+            final int NORMAL = 1;
+            final int SEARCH = 2;
+            int state = NORMAL;
+
+            boolean success = true;
+
+            while (true) {
+                // Read next key and look up the command binding.
+                int[] next = readBinding();
+
+                if (next == null) {
+                    return null;
+                }
+
+                int c = next[0];
+                int code = next[1];
+
+                if (c == -1) {
+                    return null;
+                }
+
+                // Search mode.
+                //
+                // Note that we have to do this first, because if there is a command
+                // not linked to a search command, we leave the search mode and fall
+                // through to the normal state.
+                if (state == SEARCH) {
+                    switch (code) {
+                        // This doesn't work right now, it seems CTRL-G is not passed
+                        // down correctly. :(
+                        case ABORT:
+                            state = NORMAL;
+                            break;
+
+                        case SEARCH_PREV:
+                            if (searchTerm.length() == 0) {
+                                searchTerm.append(previousSearchTerm);
+                            }
+
+                            if (searchIndex == -1) {
+                                searchIndex = history.searchBackwards(searchTerm.toString());
+                            } else {
+                                searchIndex = history.searchBackwards(searchTerm.toString(), searchIndex);
+                            }
+                            break;
+                            
+                        case DELETE_PREV_CHAR:
+                            if (searchTerm.length() > 0) {
+                                searchTerm.deleteCharAt(searchTerm.length() - 1);
+                                searchIndex = history.searchBackwards(searchTerm.toString());
+                            }
+                            break;
+                            
+                        case UNKNOWN:
+                            searchTerm.appendCodePoint(c);
+                            searchIndex = history.searchBackwards(searchTerm.toString());
+                            break;
+
+                        default:
+                            // Set buffer and cursor position to the found string.
+                            if (searchIndex != -1) {
+                                history.setCurrentIndex(searchIndex);
+                                setBuffer(history.current());
+                                buf.cursor = history.current().indexOf(searchTerm.toString());
+                            }
+                            state = NORMAL;
+                            break;
+                    }
+
+                    // if we're still in search mode, print the search status
+                    if (state == SEARCH) {
+                        if (searchTerm.length() == 0) {
+                            printSearchStatus("", "");
+                        } else {
+                            if (searchIndex == -1) {
+                                beep();
+                            } else {
+                                printSearchStatus(searchTerm.toString(), history.getHistory(searchIndex));
+                            }
+                        }
+                    }
+                    // otherwise, restore the line
+                    else {
+                        restoreLine();
+                    }
+                }
+
+                if (state == NORMAL) {
+                    switch (code) {
+                        case EXIT: // ctrl-d
+
+                            if (buf.buffer.length() == 0) {
+                                return null;
+                            }
+                            else {
+                                success = deleteCurrentCharacter();
+                            }
+                            break;
+
+                        case COMPLETE: // tab
+                            success = complete();
+                            break;
+
+                        case MOVE_TO_BEG:
+                            success = setCursorPosition(0);
+                            break;
+
+                        case KILL_LINE: // CTRL-K
+                            success = killLine();
+                            break;
+
+                        case CLEAR_SCREEN: // CTRL-L
+                            success = clearScreen();
+                            break;
+
+                        case KILL_LINE_PREV: // CTRL-U
+                            success = resetLine();
+                            break;
+
+                        case NEWLINE: // enter
+                            moveToEnd();
+                            printNewline(); // output newline
+                            return finishBuffer();
+
+                        case DELETE_PREV_CHAR: // backspace
+                            success = backspace();
+                            break;
+
+                        case DELETE_NEXT_CHAR: // delete
+                            success = deleteCurrentCharacter();
+                            break;
+
+                        case MOVE_TO_END:
+                            success = moveToEnd();
+                            break;
+
+                        case PREV_CHAR:
+                            success = moveCursor(-1) != 0;
+                            break;
+
+                        case NEXT_CHAR:
+                            success = moveCursor(1) != 0;
+                            break;
+
+                        case NEXT_HISTORY:
+                            success = moveHistory(true);
+                            break;
+
+                        case PREV_HISTORY:
+                            success = moveHistory(false);
+                            break;
+
+                        case ABORT:
+                        case REDISPLAY:
+                            break;
+
+                        case PASTE:
+                            success = paste();
+                            break;
+
+                        case DELETE_PREV_WORD:
+                            success = deletePreviousWord();
+                            break;
+
+                        case PREV_WORD:
+                            success = previousWord();
+                            break;
+
+                        case NEXT_WORD:
+                            success = nextWord();
+                            break;
+
+                        case START_OF_HISTORY:
+                            success = history.moveToFirstEntry();
+                            if (success) {
+                                setBuffer(history.current());
+                            }
+                            break;
+
+                        case END_OF_HISTORY:
+                            success = history.moveToLastEntry();
+                            if (success) {
+                                setBuffer(history.current());
+                            }
+                            break;
+
+                        case CLEAR_LINE:
+                            moveInternal(-(buf.buffer.length()));
+                            killLine();
+                            break;
+
+                        case INSERT:
+                            buf.setOvertyping(!buf.isOvertyping());
+                            break;
+
+                        case SEARCH_PREV: // CTRL-R
+                            if (searchTerm != null) {
+                                previousSearchTerm = searchTerm.toString();
+                            }
+                            searchTerm = new StringBuffer(buf.buffer);
+                            state = SEARCH;
+                            if (searchTerm.length() > 0) {
+                                searchIndex = history.searchBackwards(searchTerm.toString());
+                                if (searchIndex == -1) {
+                                    beep();
+                                }
+                                printSearchStatus(searchTerm.toString(),
+                                        searchIndex > -1 ? history.getHistory(searchIndex) : "");
+                            } else {
+                                searchIndex = -1;
+                                printSearchStatus("", "");
+                            }
+                            break;
+
+                        case UNKNOWN:
+                        default:
+                            if (c != 0) { // ignore null chars
+                                ActionListener action = (ActionListener) triggeredActions.get(new Character((char) c));
+                                if (action != null) {
+                                    action.actionPerformed(null);
+                                } else {
+                                    putChar(c, true);
+                                }
+                            } else {
+                                success = false;
+                            }
+                    }
+
+                    if (!(success)) {
+                        beep();
+                    }
+
+                    flushConsole();
+                }
+            }
+        } finally {
+            terminal.afterReadLine(this, this.prompt, mask);
+        }
+    }
+
+    private String readLine(InputStream in) throws IOException {
+        StringBuffer buf = new StringBuffer();
+
+        while (true) {
+            int i = in.read();
+
+            if ((i == -1) || (i == '\n') || (i == '\r')) {
+                return buf.toString();
+            }
+
+            buf.append((char) i);
+        }
+
+    // return new BufferedReader (new InputStreamReader (in)).readLine ();
+    }
+
+    /**
+     * Reads the console input and returns an array of the form [raw, key
+     * binding].
+     */
+    private int[] readBinding() throws IOException {
+        int c = readVirtualKey();
+
+        if (c == -1) {
+            return null;
+        }
+
+        // extract the appropriate key binding
+        short code = keybindings[c];
+
+        if (debugger != null) {
+            // debug("    translated: " + (int) c + ": " + code);
+        }
+
+        return new int[]{c, code};
+    }
+
+    /**
+     * Move up or down the history tree.
+     */
+    private final boolean moveHistory(final boolean next) throws IOException {
+        if (next && !history.next()) {
+            return false;
+        } else if (!next && !history.previous()) {
+            return false;
+        }
+
+        setBuffer(history.current());
+
+        return true;
+    }
+
+    /**
+     * Paste the contents of the clipboard into the console buffer
+     *
+     * @return true if clipboard contents pasted
+     */
+    public boolean paste() throws IOException {
+        Clipboard clipboard;
+        try { // May throw ugly exception on system without X
+            clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+        } catch (Exception e) {
+            return false;
+        }
+
+        if (clipboard == null) {
+            return false;
+        }
+
+        Transferable transferable = clipboard.getContents(null);
+
+        if (transferable == null) {
+            return false;
+        }
+
+        try {
+            Object content = transferable.getTransferData(DataFlavor.plainTextFlavor);
+
+            /*
+             * This fix was suggested in bug #1060649 at
+             * http://sourceforge.net/tracker/index.php?func=detail&aid=1060649&group_id=64033&atid=506056
+             * to get around the deprecated DataFlavor.plainTextFlavor, but it
+             * raises a UnsupportedFlavorException on Mac OS X
+             */
+            if (content == null) {
+                try {
+                    content = new DataFlavor().getReaderForText(transferable);
+                } catch (Exception e) {
+                }
+            }
+
+            if (content == null) {
+                return false;
+            }
+
+            String value;
+
+            if (content instanceof Reader) {
+                // TODO: we might want instead connect to the input stream
+                // so we can interpret individual lines
+                value = "";
+
+                String line = null;
+
+                for (BufferedReader read = new BufferedReader((Reader) content); (line = read.readLine()) != null;) {
+                    if (value.length() > 0) {
+                        value += "\n";
+                    }
+
+                    value += line;
+                }
+            } else {
+                value = content.toString();
+            }
+
+            if (value == null) {
+                return true;
+            }
+
+            putString(value);
+
+            return true;
+        } catch (UnsupportedFlavorException ufe) {
+            if (debugger != null) {
+                debug(ufe + "");
+            }
+
+            return false;
+        }
+    }
+
+    /**
+     * Kill the buffer ahead of the current cursor position.
+     *
+     * @return true if successful
+     */
+    public boolean killLine() throws IOException {
+        int cp = buf.cursor;
+        int len = buf.buffer.length();
+
+        if (cp >= len) {
+            return false;
+        }
+
+        int num = buf.buffer.length() - cp;
+        clearAhead(num);
+
+        for (int i = 0; i < num; i++) {
+            buf.buffer.deleteCharAt(len - i - 1);
+        }
+
+        return true;
+    }
+
+    /**
+     * Clear the screen by issuing the ANSI "clear screen" code.
+     */
+    public boolean clearScreen() throws IOException {
+        if (!terminal.isANSISupported()) {
+            return false;
+        }
+
+        // send the ANSI code to clear the screen
+        printANSISequence("2J");
+
+        // then send the ANSI code to go to position 1,1
+        printANSISequence("1;1H");
+
+        redrawLine();
+
+        return true;
+    }
+
+    /**
+     * Use the completors to modify the buffer with the appropriate completions.
+     *
+     * @return true if successful
+     */
+    private final boolean complete() throws IOException {
+        // debug ("tab for (" + buf + ")");
+        if (completors.size() == 0) {
+            return false;
+        }
+
+        List candidates = new LinkedList();
+        String bufstr = buf.buffer.toString();
+        int cursor = buf.cursor;
+
+        int position = -1;
+
+        for (Iterator i = completors.iterator(); i.hasNext();) {
+            Completor comp = (Completor) i.next();
+
+            if ((position = comp.complete(bufstr, cursor, candidates)) != -1) {
+                break;
+            }
+        }
+
+        // no candidates? Fail.
+        if (candidates.size() == 0) {
+            return false;
+        }
+
+        return completionHandler.complete(this, candidates, position);
+    }
+
+    public CursorBuffer getCursorBuffer() {
+        return buf;
+    }
+
+    /**
+     * Output the specified {@link Collection} in proper columns.
+     *
+     * @param stuff
+     *            the stuff to print
+     */
+    public void printColumns(final Collection stuff) throws IOException {
+        if ((stuff == null) || (stuff.size() == 0)) {
+            return;
+        }
+
+        int width = getTermwidth();
+        int maxwidth = 0;
+
+        for (Iterator i = stuff.iterator(); i.hasNext(); maxwidth = Math.max(
+                        maxwidth, i.next().toString().length())) {
+            ;
+        }
+
+        StringBuffer line = new StringBuffer();
+
+        int showLines;
+
+        if (usePagination) {
+            showLines = getTermheight() - 1; // page limit
+        } else {
+            showLines = Integer.MAX_VALUE;
+        }
+
+        for (Iterator i = stuff.iterator(); i.hasNext();) {
+            String cur = (String) i.next();
+
+            if ((line.length() + maxwidth) > width) {
+                printString(line.toString().trim());
+                printNewline();
+                line.setLength(0);
+                if (--showLines == 0) { // Overflow
+                    printString(loc.getString("display-more"));
+                    flushConsole();
+                    int c = readVirtualKey();
+                    if (c == '\r' || c == '\n') {
+                        showLines = 1; // one step forward
+                    } else if (c != 'q') {
+                        showLines = getTermheight() - 1; // page forward
+                    }
+                    back(loc.getString("display-more").length());
+                    if (c == 'q') {
+                        break; // cancel
+                    }
+                }
+            }
+
+            pad(cur, maxwidth + 3, line);
+        }
+
+        if (line.length() > 0) {
+            printString(line.toString().trim());
+            printNewline();
+            line.setLength(0);
+        }
+    }
+
+    /**
+     * Append <i>toPad</i> to the specified <i>appendTo</i>, as well as (<i>toPad.length () -
+     * len</i>) spaces.
+     *
+     * @param toPad
+     *            the {@link String} to pad
+     * @param len
+     *            the target length
+     * @param appendTo
+     *            the {@link StringBuffer} to which to append the padded
+     *            {@link String}.
+     */
+    private final void pad(final String toPad, final int len,
+            final StringBuffer appendTo) {
+        appendTo.append(toPad);
+
+        for (int i = 0; i < (len - toPad.length()); i++, appendTo.append(' ')) {
+            ;
+        }
+    }
+
+    /**
+     * Add the specified {@link Completor} to the list of handlers for
+     * tab-completion.
+     *
+     * @param completor
+     *            the {@link Completor} to add
+     * @return true if it was successfully added
+     */
+    public boolean addCompletor(final Completor completor) {
+        return completors.add(completor);
+    }
+
+    /**
+     * Remove the specified {@link Completor} from the list of handlers for
+     * tab-completion.
+     *
+     * @param completor
+     *            the {@link Completor} to remove
+     * @return true if it was successfully removed
+     */
+    public boolean removeCompletor(final Completor completor) {
+        return completors.remove(completor);
+    }
+
+    /**
+     * Returns an unmodifiable list of all the completors.
+     */
+    public Collection getCompletors() {
+        return Collections.unmodifiableList(completors);
+    }
+
+    /**
+     * Erase the current line.
+     *
+     * @return false if we failed (e.g., the buffer was empty)
+     */
+    final boolean resetLine() throws IOException {
+        if (buf.cursor == 0) {
+            return false;
+        }
+
+        backspaceAll();
+
+        return true;
+    }
+
+    /**
+     * Move the cursor position to the specified absolute index.
+     */
+    public final boolean setCursorPosition(final int position)
+            throws IOException {
+        return moveCursor(position - buf.cursor) != 0;
+    }
+
+    /**
+     * Set the current buffer's content to the specified {@link String}. The
+     * visual console will be modified to show the current buffer.
+     *
+     * @param buffer
+     *            the new contents of the buffer.
+     */
+    private final void setBuffer(final String buffer) throws IOException {
+        // don't bother modifying it if it is unchanged
+        if (buffer.equals(buf.buffer.toString())) {
+            return;
+        }
+
+        // obtain the difference between the current buffer and the new one
+        int sameIndex = 0;
+
+        for (int i = 0, l1 = buffer.length(), l2 = buf.buffer.length(); (i < l1) && (i < l2); i++) {
+            if (buffer.charAt(i) == buf.buffer.charAt(i)) {
+                sameIndex++;
+            } else {
+                break;
+            }
+        }
+
+        int diff = buf.cursor - sameIndex;
+        if (diff < 0) { // we can't backspace here so try from the end of the buffer
+                moveToEnd();
+                diff = buf.buffer.length() - sameIndex;
+        }
+
+        backspace(diff); // go back for the differences
+        killLine(); // clear to the end of the line
+        buf.buffer.setLength(sameIndex); // the new length
+        putString(buffer.substring(sameIndex)); // append the differences
+    }
+
+    /**
+     * Clear the line and redraw it.
+     */
+    public final void redrawLine() throws IOException {
+        printCharacter(RESET_LINE);
+        flushConsole();
+        drawLine();
+    }
+
+    /**
+     * Output put the prompt + the current buffer
+     */
+    public final void drawLine() throws IOException {
+        if (prompt != null) {
+            printString(prompt);
+        }
+
+        printString(buf.buffer.toString());
+
+        if (buf.length() != buf.cursor) // not at end of line
+        {
+            back(buf.length() - buf.cursor - 1); // sync
+        }
+    }
+
+    /**
+     * Output a platform-dependant newline.
+     */
+    public final void printNewline() throws IOException {
+        printString(CR);
+        flushConsole();
+    }
+
+    /**
+     * Clear the buffer and add its contents to the history.
+     *
+     * @return the former contents of the buffer.
+     */
+    final String finishBuffer() {
+        String str = buf.buffer.toString();
+
+        // we only add it to the history if the buffer is not empty
+        // and if mask is null, since having a mask typically means
+        // the string was a password. We clear the mask after this call
+        if (str.length() > 0) {
+            if (mask == null && useHistory) {
+                history.addToHistory(str);
+            } else {
+                mask = null;
+            }
+        }
+
+        history.moveToEnd();
+
+        buf.buffer.setLength(0);
+        buf.cursor = 0;
+
+        return str;
+    }
+
+    /**
+     * Write out the specified string to the buffer and the output stream.
+     */
+    public final void putString(final String str) throws IOException {
+        buf.write(str);
+        printString(str);
+        drawBuffer();
+    }
+
+    /**
+     * Output the specified string to the output stream (but not the buffer).
+     */
+    public final void printString(final String str) throws IOException {
+        printCharacters(str.toCharArray());
+    }
+
+    /**
+     * Output the specified character, both to the buffer and the output stream.
+     */
+    private final void putChar(final int c, final boolean print)
+            throws IOException {
+        buf.write((char) c);
+
+        if (print) {
+            // no masking...
+            if (mask == null) {
+                printCharacter(c);
+            } // null mask: don't print anything...
+            else if (mask.charValue() == 0) {
+                ;
+            } // otherwise print the mask...
+            else {
+                printCharacter(mask.charValue());
+            }
+
+            drawBuffer();
+        }
+    }
+
+    /**
+     * Redraw the rest of the buffer from the cursor onwards. This is necessary
+     * for inserting text into the buffer.
+     *
+     * @param clear
+     *            the number of characters to clear after the end of the buffer
+     */
+    private final void drawBuffer(final int clear) throws IOException {
+        // debug ("drawBuffer: " + clear);
+        if (buf.cursor == buf.length() && clear == 0) {
+            return;
+        }
+        char[] chars = buf.buffer.substring(buf.cursor).toCharArray();
+        if (mask != null) {
+            Arrays.fill(chars, mask.charValue());
+        }
+
+        printCharacters(chars);
+        clearAhead(clear);
+        if (terminal.isANSISupported()) {
+            if (chars.length > 0) {
+                // don't ask, it seems to work
+                back(Math.max(chars.length - 1, 1));
+            }
+        } else {
+            back(chars.length);
+        }
+        flushConsole();
+    }
+
+    /**
+     * Redraw the rest of the buffer from the cursor onwards. This is necessary
+     * for inserting text into the buffer.
+     */
+    private final void drawBuffer() throws IOException {
+        drawBuffer(0);
+    }
+
+    /**
+     * Clear ahead the specified number of characters without moving the cursor.
+     */
+    private final void clearAhead(final int num) throws IOException {
+        if (num == 0) {
+            return;
+        }
+
+        if (terminal.isANSISupported()) {
+            printANSISequence("J");
+            return;
+        }
+
+        // debug ("clearAhead: " + num);
+
+        // print blank extra characters
+        printCharacters(' ', num);
+
+        // we need to flush here so a "clever" console
+        // doesn't just ignore the redundancy of a space followed by
+        // a backspace.
+        flushConsole();
+
+        // reset the visual cursor
+        back(num);
+
+        flushConsole();
+    }
+
+    /**
+     * Move the visual cursor backwards without modifying the buffer cursor.
+     */
+    private final void back(final int num) throws IOException {
+        if (num == 0) return;
+        if (terminal.isANSISupported()) {
+            int width = getTermwidth();
+            int cursor = getCursorPosition();
+            // debug("back: " + cursor + " + " + num + " on " + width);
+            int currRow = (cursor + num) / width;
+            int newRow = cursor / width;
+            int newCol = cursor % width + 1;
+            // debug("    old row: " + currRow + " new row: " + newRow);
+            if (newRow < currRow) {
+                printANSISequence((currRow - newRow) + "A");
+            }
+            printANSISequence(newCol + "G");
+            flushConsole();
+            return;
+        }
+        printCharacters(BACKSPACE, num);
+        flushConsole();
+    }
+
+    /**
+     * Issue an audible keyboard bell, if {@link #getBellEnabled} return true.
+     */
+    public final void beep() throws IOException {
+        if (!(getBellEnabled())) {
+            return;
+        }
+
+        printCharacter(KEYBOARD_BELL);
+        // need to flush so the console actually beeps
+        flushConsole();
+    }
+
+    /**
+     * Output the specified character to the output stream without manipulating
+     * the current buffer.
+     */
+    private final void printCharacter(final int c) throws IOException {
+        if (c == '\t') {
+            char cbuf[] = new char[TAB_WIDTH];
+            Arrays.fill(cbuf, ' ');
+            out.write(cbuf);
+            return;
+        }
+
+        out.write(c);
+    }
+
+    /**
+     * Output the specified characters to the output stream without manipulating
+     * the current buffer.
+     */
+    private final void printCharacters(final char[] c) throws IOException {
+        int len = 0;
+        for (int i = 0; i < c.length; i++) {
+            if (c[i] == '\t') {
+                len += TAB_WIDTH;
+            } else {
+                len++;
+            }
+        }
+
+        char cbuf[];
+        if (len == c.length) {
+            cbuf = c;
+        } else {
+            cbuf = new char[len];
+            int pos = 0;
+            for (int i = 0; i < c.length; i++) {
+                if (c[i] == '\t') {
+                    Arrays.fill(cbuf, pos, pos + TAB_WIDTH, ' ');
+                    pos += TAB_WIDTH;
+                } else {
+                    cbuf[pos] = c[i];
+                    pos++;
+                }
+            }
+        }
+
+        out.write(cbuf);
+    }
+
+    private final void printCharacters(final char c, final int num)
+            throws IOException {
+        if (num == 1) {
+            printCharacter(c);
+        } else {
+            char[] chars = new char[num];
+            Arrays.fill(chars, c);
+            printCharacters(chars);
+        }
+    }
+
+    /**
+     * Flush the console output stream. This is important for printout out
+     * single characters (like a backspace or keyboard) that we want the console
+     * to handle immedately.
+     */
+    public final void flushConsole() throws IOException {
+        out.flush();
+    }
+
+    private final int backspaceAll() throws IOException {
+        return backspace(Integer.MAX_VALUE);
+    }
+
+    /**
+     * Issue <em>num</em> backspaces.
+     *
+     * @return the number of characters backed up
+     */
+    private final int backspace(final int num) throws IOException {
+        if (buf.cursor == 0) {
+            return 0;
+        }
+
+        int count = 0;
+        int termwidth = getTermwidth();
+        int lines = getCursorPosition() / termwidth;
+        count = moveCursor(-1 * num) * -1;
+        // debug ("Deleting from " + buf.cursor + " for " + count);
+        buf.buffer.delete(buf.cursor, buf.cursor + count);
+        if (getCursorPosition() / termwidth != lines) {
+            if (terminal.isANSISupported()) {
+                // debug("doing backspace redraw: " + getCursorPosition() + " on " + termwidth + ": " + lines);
+                printANSISequence("J");
+                flushConsole();
+            }
+        }
+        drawBuffer(count);
+
+        return count;
+    }
+
+    /**
+     * Issue a backspace.
+     *
+     * @return true if successful
+     */
+    public final boolean backspace() throws IOException {
+        return backspace(1) == 1;
+    }
+
+    private final boolean moveToEnd() throws IOException {
+        return moveCursor(buf.length() - buf.cursor) > 0;
+    }
+
+    /**
+     * Delete the character at the current position and redraw the remainder of
+     * the buffer.
+     */
+    private final boolean deleteCurrentCharacter() throws IOException {
+        if (buf.length() == 0 || buf.cursor == buf.length()) {
+            return false;
+        }
+
+        buf.buffer.deleteCharAt(buf.cursor);
+        drawBuffer(1);
+        return true;
+    }
+
+    private final boolean previousWord() throws IOException {
+        while (isDelimiter(buf.current()) && (moveCursor(-1) != 0)) {
+            ;
+        }
+
+        while (!isDelimiter(buf.current()) && (moveCursor(-1) != 0)) {
+            ;
+        }
+
+        return true;
+    }
+
+    private final boolean nextWord() throws IOException {
+        while (isDelimiter(buf.current()) && (moveCursor(1) != 0)) {
+            ;
+        }
+
+        while (!isDelimiter(buf.current()) && (moveCursor(1) != 0)) {
+            ;
+        }
+
+        return true;
+    }
+
+    private final boolean deletePreviousWord() throws IOException {
+        while (isDelimiter(buf.current()) && backspace()) {
+            ;
+        }
+
+        while (!isDelimiter(buf.current()) && backspace()) {
+            ;
+        }
+
+        return true;
+    }
+
+    /**
+     * Move the cursor <i>where</i> characters.
+     *
+     * @param num
+     *            if less than 0, move abs(<i>num</i>) to the left,
+     *            otherwise move <i>num</i> to the right.
+     *
+     * @return the number of spaces we moved
+     */
+    public final int moveCursor(final int num) throws IOException {
+        int where = num;
+
+        if ((buf.cursor == 0) && (where <= 0)) {
+            return 0;
+        }
+
+        if ((buf.cursor == buf.buffer.length()) && (where >= 0)) {
+            return 0;
+        }
+
+        if ((buf.cursor + where) < 0) {
+            where = -buf.cursor;
+        } else if ((buf.cursor + where) > buf.buffer.length()) {
+            where = buf.buffer.length() - buf.cursor;
+        }
+
+        moveInternal(where);
+
+        return where;
+    }
+
+    /**
+     * debug.
+     *
+     * @param str
+     *            the message to issue.
+     */
+    public static void debug(final String str) {
+        if (debugger != null) {
+            debugger.println(str);
+            debugger.flush();
+        }
+    }
+
+    /**
+     * Move the cursor <i>where</i> characters, withough checking the current
+     * buffer.
+     *
+     * @param where
+     *            the number of characters to move to the right or left.
+     */
+    private final void moveInternal(final int where) throws IOException {
+        // debug ("move cursor " + where + " ("
+        // + buf.cursor + " => " + (buf.cursor + where) + ")");
+        buf.cursor += where;
+
+        if (terminal.isANSISupported()) {
+            if (where < 0) {
+                back(Math.abs(where));
+            } else {
+                int width = getTermwidth();
+                int cursor = getCursorPosition();
+                int oldLine = (cursor - where) / width;
+                int newLine = cursor / width;
+                if (newLine > oldLine) {
+                    printANSISequence((newLine - oldLine) + "B");
+                }
+                printANSISequence(1 +(cursor % width) + "G");
+            }
+            flushConsole();
+            return;
+        }
+
+        char c;
+
+        if (where < 0) {
+            int len = 0;
+            for (int i = buf.cursor; i < buf.cursor - where; i++) {
+                if (buf.getBuffer().charAt(i) == '\t') {
+                    len += TAB_WIDTH;
+                } else {
+                    len++;
+                }
+            }
+
+            char cbuf[] = new char[len];
+            Arrays.fill(cbuf, BACKSPACE);
+            out.write(cbuf);
+
+            return;
+        } else if (buf.cursor == 0) {
+            return;
+        } else if (mask != null) {
+            c = mask.charValue();
+        } else {
+            printCharacters(buf.buffer.substring(buf.cursor - where, buf.cursor).toCharArray());
+            return;
+        }
+
+        // null character mask: don't output anything
+        if (NULL_MASK.equals(mask)) {
+            return;
+        }
+
+        printCharacters(c, Math.abs(where));
+    }
+
+    /**
+     * Read a character from the console.
+     *
+     * @return the character, or -1 if an EOF is received.
+     */
+    public final int readVirtualKey() throws IOException {
+        int c = terminal.readVirtualKey(in);
+
+        if (debugger != null) {
+            // debug("keystroke: " + c + "");
+        }
+
+        // clear any echo characters
+        clearEcho(c);
+
+        return c;
+    }
+
+    public final int readCharacter(final char[] allowed) throws IOException {
+        // if we restrict to a limited set and the current character
+        // is not in the set, then try again.
+        char c;
+
+        Arrays.sort(allowed); // always need to sort before binarySearch
+
+        while (Arrays.binarySearch(allowed, c = (char) readVirtualKey()) < 0);
+
+        return c;
+    }
+
+    /**
+     *  Issue <em>num</em> deletes.
+     *
+     *  @return  the number of characters backed up
+     */
+    private final int delete(final int num)
+            throws IOException {
+        /* Commented out beacuse of DWA-2949:
+        if (buf.cursor == 0)
+        return 0;*/
+
+        buf.buffer.delete(buf.cursor, buf.cursor + 1);
+        drawBuffer(1);
+
+        return 1;
+    }
+
+    public final boolean replace(int num, String replacement) {
+        buf.buffer.replace(buf.cursor - num, buf.cursor, replacement);
+        try {
+            moveCursor(-num);
+            drawBuffer(Math.max(0, num - replacement.length()));
+            moveCursor(replacement.length());
+        } catch (IOException e) {
+            e.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     *  Issue a delete.
+     *
+     *  @return  true if successful
+     */
+    public final boolean delete()
+            throws IOException {
+        return delete(1) == 1;
+    }
+
+    public void setHistory(final History history) {
+        this.history = history;
+    }
+
+    public History getHistory() {
+        return this.history;
+    }
+
+    public void setCompletionHandler(final CompletionHandler completionHandler) {
+        this.completionHandler = completionHandler;
+    }
+
+    public CompletionHandler getCompletionHandler() {
+        return this.completionHandler;
+    }
+
+    /**
+     * <p>
+     * Set the echo character. For example, to have "*" entered when a password
+     * is typed:
+     * </p>
+     *
+     * <pre>
+     * myConsoleReader.setEchoCharacter(new Character('*'));
+     * </pre>
+     *
+     * <p>
+     * Setting the character to
+     *
+     * <pre>
+     * null
+     * </pre>
+     *
+     * will restore normal character echoing. Setting the character to
+     *
+     * <pre>
+     * new Character(0)
+     * </pre>
+     *
+     * will cause nothing to be echoed.
+     * </p>
+     *
+     * @param echoCharacter
+     *            the character to echo to the console in place of the typed
+     *            character.
+     */
+    public void setEchoCharacter(final Character echoCharacter) {
+        this.echoCharacter = echoCharacter;
+    }
+
+    /**
+     * Returns the echo character.
+     */
+    public Character getEchoCharacter() {
+        return this.echoCharacter;
+    }
+
+    /**
+     * No-op for exceptions we want to silently consume.
+     */
+    private void consumeException(final Throwable e) {
+    }
+
+    /**
+     * Checks to see if the specified character is a delimiter. We consider a
+     * character a delimiter if it is anything but a letter or digit.
+     *
+     * @param c
+     *            the character to test
+     * @return true if it is a delimiter
+     */
+    private boolean isDelimiter(char c) {
+        return !Character.isLetterOrDigit(c);
+    }
+
+    private void printANSISequence(String sequence) throws IOException {
+        printCharacter(27);
+        printCharacter('[');
+        printString(sequence);
+        flushConsole();
+    }
+
+    /*
+    private int currentCol, currentRow;
+
+    private void getCurrentPosition() {
+        // check for ByteArrayInputStream to disable for unit tests
+        if (terminal.isANSISupported() && !(in instanceof ByteArrayInputStream)) {
+            try {
+                printANSISequence("[6n");
+                flushConsole();
+                StringBuffer b = new StringBuffer(8);
+                // position is sent as <ESC>[{ROW};{COLUMN}R
+                int r;
+                while((r = in.read()) > -1 && r != 'R') {
+                    if (r != 27 && r != '[') {
+                        b.append((char) r);
+                    }
+                }
+                String[] pos = b.toString().split(";");
+                currentRow = Integer.parseInt(pos[0]);
+                currentCol = Integer.parseInt(pos[1]);
+            } catch (Exception x) {
+                // no luck
+                currentRow = currentCol = -1;
+            }
+        }
+    }
+    */
+
+    /**
+     * Whether or not to add new commands to the history buffer.
+     */
+    public void setUseHistory(boolean useHistory) {
+        this.useHistory = useHistory;
+    }
+
+    /**
+     * Whether or not to add new commands to the history buffer.
+     */
+    public boolean getUseHistory() {
+        return useHistory;
+    }
+
+    /**
+     * Whether to use pagination when the number of rows of candidates exceeds
+     * the height of the temrinal.
+     */
+    public void setUsePagination(boolean usePagination) {
+        this.usePagination = usePagination;
+    }
+
+    /**
+     * Whether to use pagination when the number of rows of candidates exceeds
+     * the height of the temrinal.
+     */
+    public boolean getUsePagination() {
+        return this.usePagination;
+    }
+
+    public void printSearchStatus(String searchTerm, String match) throws IOException {
+        int i = match.indexOf(searchTerm);
+        printString("\r(reverse-i-search) `" + searchTerm + "': " + match + "\u001b[K");
+        // FIXME: our ANSI using back() does not work here
+        printCharacters(BACKSPACE, match.length() - i);
+        flushConsole();
+    }
+
+    public void restoreLine() throws IOException {
+        printString("\u001b[2K"); // ansi/vt100 for clear whole line
+        redrawLine();
+        flushConsole();
+    }
+}
diff --git a/src/src/main/java/jline/ConsoleReaderInputStream.java b/src/src/main/java/jline/ConsoleReaderInputStream.java
new file mode 100644 (file)
index 0000000..22a7b6d
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *  An {@link InputStream} implementation that wraps a {@link ConsoleReader}.
+ *  It is useful for setting up the {@link System#in} for a generic
+ *  console.
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ConsoleReaderInputStream extends SequenceInputStream {
+    private static InputStream systemIn = System.in;
+
+    public static void setIn() throws IOException {
+        setIn(new ConsoleReader());
+    }
+
+    public static void setIn(final ConsoleReader reader) {
+        System.setIn(new ConsoleReaderInputStream(reader));
+    }
+
+    /**
+     *  Restore the original {@link System#in} input stream.
+     */
+    public static void restoreIn() {
+        System.setIn(systemIn);
+    }
+
+    public ConsoleReaderInputStream(final ConsoleReader reader) {
+        super(new ConsoleEnumeration(reader));
+    }
+
+    private static class ConsoleEnumeration implements Enumeration {
+        private final ConsoleReader reader;
+        private ConsoleLineInputStream next = null;
+        private ConsoleLineInputStream prev = null;
+
+        public ConsoleEnumeration(final ConsoleReader reader) {
+            this.reader = reader;
+        }
+
+        public Object nextElement() {
+            if (next != null) {
+                InputStream n = next;
+                prev = next;
+                next = null;
+
+                return n;
+            }
+
+            return new ConsoleLineInputStream(reader);
+        }
+
+        public boolean hasMoreElements() {
+            // the last line was null
+            if ((prev != null) && (prev.wasNull == true)) {
+                return false;
+            }
+
+            if (next == null) {
+                next = (ConsoleLineInputStream) nextElement();
+            }
+
+            return next != null;
+        }
+    }
+
+    private static class ConsoleLineInputStream extends InputStream {
+        private final ConsoleReader reader;
+        private String line = null;
+        private int index = 0;
+        private boolean eol = false;
+        protected boolean wasNull = false;
+
+        public ConsoleLineInputStream(final ConsoleReader reader) {
+            this.reader = reader;
+        }
+
+        public int read() throws IOException {
+            if (eol) {
+                return -1;
+            }
+
+            if (line == null) {
+                line = reader.readLine();
+            }
+
+            if (line == null) {
+                wasNull = true;
+                return -1;
+            }
+
+            if (index >= line.length()) {
+                eol = true;
+                return '\n'; // lines are ended with a newline
+            }
+
+            return line.charAt(index++);
+        }
+    }
+}
diff --git a/src/src/main/java/jline/ConsoleRunner.java b/src/src/main/java/jline/ConsoleRunner.java
new file mode 100644 (file)
index 0000000..36356de
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *  <p>
+ *  A pass-through application that sets the system input stream to a
+ *  {@link ConsoleReader} and invokes the specified main method.
+ *  </p>
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ConsoleRunner {
+    private static ConsoleReader reader;
+
+    public static ConsoleReader getReader() { return reader; }
+
+    public static final String property = "jline.history";
+
+    public static void main(final String[] args) throws Exception {
+        String historyFileName = null;
+
+        List argList = new ArrayList(Arrays.asList(args));
+
+        if (argList.size() == 0) {
+            usage();
+
+            return;
+        }
+
+        historyFileName = System.getProperty(ConsoleRunner.property, null);
+
+        // invoke the main() method
+        String mainClass = (String) argList.remove(0);
+
+        // setup the inpout stream
+        reader = new ConsoleReader();
+
+        if (historyFileName != null) {
+            reader.setHistory(new History (new File
+                (System.getProperty("user.home"),
+                    ".jline-" + mainClass
+                        + "." + historyFileName + ".history")));
+        } else {
+            reader.setHistory(new History(new File
+                (System.getProperty("user.home"),
+                    ".jline-" + mainClass + ".history")));
+        }
+
+        String completors = System.getProperty
+            (ConsoleRunner.class.getName() + ".completors", "");
+        List completorList = new ArrayList();
+
+        for (StringTokenizer tok = new StringTokenizer(completors, ",");
+            tok.hasMoreTokens();) {
+            completorList.add
+                ((Completor) Class.forName(tok.nextToken()).newInstance());
+        }
+
+        if (completorList.size() > 0) {
+            reader.addCompletor(new ArgumentCompletor(completorList));
+        }
+
+        ConsoleReaderInputStream.setIn(reader);
+
+        try {
+            Class.forName(mainClass).
+                getMethod("main", new Class[] { String[].class }).
+                invoke(null, new Object[] { argList.toArray(new String[0]) });
+        } finally {
+            // just in case this main method is called from another program
+            ConsoleReaderInputStream.restoreIn();
+        }
+    }
+
+    private static void usage() {
+        System.out.println("Usage: \n   java " + "[-Djline.history='name'] "
+            + ConsoleRunner.class.getName()
+            + " <target class name> [args]"
+            + "\n\nThe -Djline.history option will avoid history"
+            + "\nmangling when running ConsoleRunner on the same application."
+            + "\n\nargs will be passed directly to the target class name.");
+    }
+}
diff --git a/src/src/main/java/jline/CursorBuffer.java b/src/src/main/java/jline/CursorBuffer.java
new file mode 100644 (file)
index 0000000..120d705
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+/**
+ * A CursorBuffer is a holder for a {@link StringBuffer} that also contains the
+ * current cursor position.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class CursorBuffer {
+    public int cursor = 0;
+
+    StringBuffer buffer = new StringBuffer();
+
+    private boolean overtyping = false;
+
+    public int length() {
+        return buffer.length();
+    }
+
+    public char current() {
+        if (cursor <= 0) {
+            return 0;
+        }
+
+        return buffer.charAt(cursor - 1);
+    }
+
+    public boolean clearBuffer() {
+        if (buffer.length() == 0) {
+            return false;
+        }
+
+        buffer.delete(0, buffer.length());
+        cursor = 0;
+        return true;
+    }
+
+    /**
+     * Write the specific character into the buffer, setting the cursor position
+     * ahead one. The text may overwrite or insert based on the current setting
+     * of isOvertyping().
+     *
+     * @param c
+     *            the character to insert
+     */
+    public void write(final char c) {
+        buffer.insert(cursor++, c);
+        if (isOvertyping() && cursor < buffer.length()) {
+            buffer.deleteCharAt(cursor);
+        }
+    }
+
+    /**
+     * Insert the specified {@link String} into the buffer, setting the cursor
+     * to the end of the insertion point.
+     *
+     * @param str
+     *            the String to insert. Must not be null.
+     */
+    public void write(final String str) {
+        if (buffer.length() == 0) {
+            buffer.append(str);
+        } else {
+            buffer.insert(cursor, str);
+        }
+
+        cursor += str.length();
+
+        if (isOvertyping() && cursor < buffer.length()) {
+            buffer.delete(cursor, (cursor + str.length()));
+        }
+    }
+
+    public String toString() {
+        return buffer.toString();
+    }
+
+    public boolean isOvertyping() {
+        return overtyping;
+    }
+
+    public void setOvertyping(boolean b) {
+        overtyping = b;
+    }
+
+       public StringBuffer getBuffer() {
+               return buffer;
+       }
+
+       public void setBuffer(StringBuffer buffer) {
+               buffer.setLength(0);
+               buffer.append(this.buffer.toString());
+               
+               this.buffer = buffer;
+       }
+    
+    
+}
diff --git a/src/src/main/java/jline/FileNameCompletor.java b/src/src/main/java/jline/FileNameCompletor.java
new file mode 100644 (file)
index 0000000..19c4525
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *  A file name completor takes the buffer and issues a list of
+ *  potential completions.
+ *
+ *  <p>
+ *  This completor tries to behave as similar as possible to
+ *  <i>bash</i>'s file name completion (using GNU readline)
+ *  with the following exceptions:
+ *
+ *  <ul>
+ *  <li>Candidates that are directories will end with "/"</li>
+ *  <li>Wildcard regular expressions are not evaluated or replaced</li>
+ *  <li>The "~" character can be used to represent the user's home,
+ *  but it cannot complete to other users' homes, since java does
+ *  not provide any way of determining that easily</li>
+ *  </ul>
+ *
+ *  <p>TODO</p>
+ *  <ul>
+ *  <li>Handle files with spaces in them</li>
+ *  <li>Have an option for file type color highlighting</li>
+ *  </ul>
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class FileNameCompletor implements Completor {
+    public int complete(final String buf, final int cursor,
+                        final List candidates) {
+        String buffer = (buf == null) ? "" : buf;
+
+        String translated = buffer;
+
+        // special character: ~ maps to the user's home directory
+        if (translated.startsWith("~" + File.separator)) {
+            translated = System.getProperty("user.home")
+                         + translated.substring(1);
+        } else if (translated.startsWith("~")) {
+            translated = new File(System.getProperty("user.home")).getParentFile()
+                                                                  .getAbsolutePath();
+        } else if (!(translated.startsWith(File.separator))) {
+            translated = new File("").getAbsolutePath() + File.separator
+                         + translated;
+        }
+
+        File f = new File(translated);
+
+        final File dir;
+
+        if (translated.endsWith(File.separator)) {
+            dir = f;
+        } else {
+            dir = f.getParentFile();
+        }
+
+        final File[] entries = (dir == null) ? new File[0] : dir.listFiles();
+
+        try {
+            return matchFiles(buffer, translated, entries, candidates);
+        } finally {
+            // we want to output a sorted list of files
+            sortFileNames(candidates);
+        }
+    }
+
+    protected void sortFileNames(final List fileNames) {
+        Collections.sort(fileNames);
+    }
+
+    /**
+     *  Match the specified <i>buffer</i> to the array of <i>entries</i>
+     *  and enter the matches into the list of <i>candidates</i>. This method
+     *  can be overridden in a subclass that wants to do more
+     *  sophisticated file name completion.
+     *
+     *  @param        buffer                the untranslated buffer
+     *  @param        translated        the buffer with common characters replaced
+     *  @param        entries                the list of files to match
+     *  @param        candidates        the list of candidates to populate
+     *
+     *  @return  the offset of the match
+     */
+    public int matchFiles(String buffer, String translated, File[] entries,
+                          List candidates) {
+        if (entries == null) {
+            return -1;
+        }
+
+        int matches = 0;
+
+        // first pass: just count the matches
+        for (int i = 0; i < entries.length; i++) {
+            if (entries[i].getAbsolutePath().startsWith(translated)) {
+                matches++;
+            }
+        }
+
+        // green - executable
+        // blue - directory
+        // red - compressed
+        // cyan - symlink
+        for (int i = 0; i < entries.length; i++) {
+            if (entries[i].getAbsolutePath().startsWith(translated)) {
+                String name =
+                    entries[i].getName()
+                    + (((matches == 1) && entries[i].isDirectory())
+                       ? File.separator : " ");
+
+                /*
+                if (entries [i].isDirectory ())
+                {
+                        name = new ANSIBuffer ().blue (name).toString ();
+                }
+                */
+                candidates.add(name);
+            }
+        }
+
+        final int index = buffer.lastIndexOf(File.separator);
+
+        return index + File.separator.length();
+    }
+}
diff --git a/src/src/main/java/jline/History.java b/src/src/main/java/jline/History.java
new file mode 100644 (file)
index 0000000..aefadc3
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * A command history buffer.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class History {
+
+    private List history = new ArrayList();
+    private PrintWriter output = null;
+    private int maxSize = 500;
+    private int currentIndex = 0;
+
+    /**
+     * Construstor: initialize a blank history.
+     */
+    public History() {
+    }
+
+    /**
+     * Construstor: initialize History object the the specified {@link File} for
+     * storage.
+     */
+    public History(final File historyFile) throws IOException {
+        setHistoryFile(historyFile);
+    }
+
+    public void setHistoryFile(final File historyFile) throws IOException {
+        if (historyFile.isFile()) {
+            load(new FileInputStream(historyFile));
+        }
+
+        setOutput(new PrintWriter(new FileWriter(historyFile), true));
+        flushBuffer();
+    }
+
+    /**
+     * Load the history buffer from the specified InputStream.
+     */
+    public void load(final InputStream in) throws IOException {
+        load(new InputStreamReader(in));
+    }
+
+    /**
+     * Load the history buffer from the specified Reader.
+     */
+    public void load(final Reader reader) throws IOException {
+        BufferedReader breader = new BufferedReader(reader);
+        List lines = new ArrayList();
+        String line;
+
+        while ((line = breader.readLine()) != null) {
+            lines.add(line);
+        }
+
+        for (Iterator i = lines.iterator(); i.hasNext();) {
+            addToHistory((String) i.next());
+        }
+    }
+
+    public int size() {
+        return history.size();
+    }
+
+    /**
+     * Clear the history buffer
+     */
+    public void clear() {
+        history.clear();
+        currentIndex = 0;
+    }
+
+    /**
+     * Add the specified buffer to the end of the history. The pointer is set to
+     * the end of the history buffer.
+     */
+    public void addToHistory(final String buffer) {
+        // don't append duplicates to the end of the buffer
+        if ((history.size() != 0) && buffer.equals(history.get(history.size() - 1))) {
+            return;
+        }
+
+        history.add(buffer);
+
+        while (history.size() > getMaxSize()) {
+            history.remove(0);
+        }
+
+        currentIndex = history.size();
+
+        if (getOutput() != null) {
+            getOutput().println(buffer);
+            getOutput().flush();
+        }
+    }
+
+    /**
+     * Flush the entire history buffer to the output PrintWriter.
+     */
+    public void flushBuffer() throws IOException {
+        if (getOutput() != null) {
+            for (Iterator i = history.iterator(); i.hasNext(); getOutput().println((String) i.next())) {
+                ;
+            }
+
+            getOutput().flush();
+        }
+    }
+
+    /**
+     * This moves the history to the last entry. This entry is one position
+     * before the moveToEnd() position.
+     *
+     * @return Returns false if there were no history entries or the history
+     *         index was already at the last entry.
+     */
+    public boolean moveToLastEntry() {
+        int lastEntry = history.size() - 1;
+        if (lastEntry >= 0 && lastEntry != currentIndex) {
+            currentIndex = history.size() - 1;
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Move to the end of the history buffer. This will be a blank entry, after
+     * all of the other entries.
+     */
+    public void moveToEnd() {
+        currentIndex = history.size();
+    }
+
+    /**
+     * Set the maximum size that the history buffer will store.
+     */
+    public void setMaxSize(final int maxSize) {
+        this.maxSize = maxSize;
+    }
+
+    /**
+     * Get the maximum size that the history buffer will store.
+     */
+    public int getMaxSize() {
+        return this.maxSize;
+    }
+
+    /**
+     * The output to which all history elements will be written (or null of
+     * history is not saved to a buffer).
+     */
+    public void setOutput(final PrintWriter output) {
+        this.output = output;
+    }
+
+    /**
+     * Returns the PrintWriter that is used to store history elements.
+     */
+    public PrintWriter getOutput() {
+        return this.output;
+    }
+
+    /**
+     * Returns the current history index.
+     */
+    public int getCurrentIndex() {
+        return this.currentIndex;
+    }
+
+    /**
+     * Return the content of the current buffer.
+     */
+    public String current() {
+        if (currentIndex >= history.size()) {
+            return "";
+        }
+
+        return (String) history.get(currentIndex);
+    }
+
+    /**
+     * Move the pointer to the previous element in the buffer.
+     *
+     * @return true if we successfully went to the previous element
+     */
+    public boolean previous() {
+        if (currentIndex <= 0) {
+            return false;
+        }
+
+        currentIndex--;
+
+        return true;
+    }
+
+    /**
+     * Move the pointer to the next element in the buffer.
+     *
+     * @return true if we successfully went to the next element
+     */
+    public boolean next() {
+        if (currentIndex >= history.size()) {
+            return false;
+        }
+
+        currentIndex++;
+
+        return true;
+    }
+
+    /**
+     * Returns an immutable list of the history buffer.
+     */
+    public List getHistoryList() {
+        return Collections.unmodifiableList(history);
+    }
+
+    /**
+     * Returns the standard {@link AbstractCollection#toString} representation
+     * of the history list.
+     */
+    public String toString() {
+        return history.toString();
+    }
+
+    /**
+     * Moves the history index to the first entry.
+     *
+     * @return Return false if there are no entries in the history or if the
+     *         history is already at the beginning.
+     */
+    public boolean moveToFirstEntry() {
+        if (history.size() > 0 && currentIndex != 0) {
+            currentIndex = 0;
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Search backward in history from a given position.
+     *
+     * @param searchTerm substring to search for.
+     * @param startIndex the index from which on to search
+     * @return index where this substring has been found, or -1 else.
+     */
+    public int searchBackwards(String searchTerm, int startIndex) {
+        for (int i = startIndex - 1; i >= 0; i--) {
+            if (i >= size())
+                continue;
+            if (getHistory(i).indexOf(searchTerm) != -1) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Search backwards in history from the current position.
+     *
+     * @param searchTerm substring to search for.
+     * @return index where the substring has been found, or -1 else.
+     */
+    public int searchBackwards(String s) {
+        return searchBackwards(s, getCurrentIndex());
+    }
+
+    /**
+     * Get the history string for the given index.
+     *
+     * @param index
+     * @return
+     */
+    public String getHistory(int index) {
+        return (String) history.get(index);
+    }
+
+    /**
+     * Set current index to given number.
+     * 
+     * @param index
+     */
+    public void setCurrentIndex(int index) {
+        if (index >= 0 && index < history.size())
+            currentIndex = index;
+    }
+}
diff --git a/src/src/main/java/jline/MultiCompletor.java b/src/src/main/java/jline/MultiCompletor.java
new file mode 100644 (file)
index 0000000..e3cd4e3
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ *  <p>
+ *  A completor that contains multiple embedded completors. This differs
+ *  from the {@link ArgumentCompletor}, in that the nested completors
+ *  are dispatched individually, rather than delimited by arguments.
+ *  </p>
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class MultiCompletor implements Completor {
+    Completor[] completors = new Completor[0];
+
+    /**
+     *  Construct a MultiCompletor with no embedded completors.
+     */
+    public MultiCompletor() {
+        this(new Completor[0]);
+    }
+
+    /**
+     *  Construct a MultiCompletor with the specified list of
+     *  {@link Completor} instances.
+     */
+    public MultiCompletor(final List completors) {
+        this((Completor[]) completors.toArray(new Completor[completors.size()]));
+    }
+
+    /**
+     *  Construct a MultiCompletor with the specified
+     *  {@link Completor} instances.
+     */
+    public MultiCompletor(final Completor[] completors) {
+        this.completors = completors;
+    }
+
+    public int complete(final String buffer, final int pos, final List cand) {
+        int[] positions = new int[completors.length];
+        List[] copies = new List[completors.length];
+
+        for (int i = 0; i < completors.length; i++) {
+            // clone and save the candidate list
+            copies[i] = new LinkedList(cand);
+            positions[i] = completors[i].complete(buffer, pos, copies[i]);
+        }
+
+        int maxposition = -1;
+
+        for (int i = 0; i < positions.length; i++) {
+            maxposition = Math.max(maxposition, positions[i]);
+        }
+
+        // now we have the max cursor value: build up all the
+        // candidate lists that have the same cursor value
+        for (int i = 0; i < copies.length; i++) {
+            if (positions[i] == maxposition) {
+                cand.addAll(copies[i]);
+            }
+        }
+
+        return maxposition;
+    }
+
+    public void setCompletors(final Completor[] completors) {
+        this.completors = completors;
+    }
+
+    public Completor[] getCompletors() {
+        return this.completors;
+    }
+}
diff --git a/src/src/main/java/jline/NullCompletor.java b/src/src/main/java/jline/NullCompletor.java
new file mode 100644 (file)
index 0000000..aa6cdf7
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ *  <p>
+ *  A completor that does nothing. Useful as the last item in an
+ *  {@link ArgumentCompletor}.
+ *  </p>
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class NullCompletor implements Completor {
+    /**
+     *  Returns -1 always, indicating that the the buffer is never
+     *  handled.
+     */
+    public int complete(final String buffer, int cursor, List candidates) {
+        return -1;
+    }
+}
diff --git a/src/src/main/java/jline/SimpleCompletor.java b/src/src/main/java/jline/SimpleCompletor.java
new file mode 100644 (file)
index 0000000..2d488b6
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *  <p>
+ *  A simple {@link Completor} implementation that handles a pre-defined
+ *  list of completion words.
+ *  </p>
+ *
+ *  <p>
+ *  Example usage:
+ *  </p>
+ *  <pre>
+ *  myConsoleReader.addCompletor (new SimpleCompletor (new String [] { "now", "yesterday", "tomorrow" }));
+ *  </pre>
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class SimpleCompletor implements Completor, Cloneable {
+    /**
+     *  The list of candidates that will be completed.
+     */
+    SortedSet candidates;
+
+    /**
+     *  A delimiter to use to qualify completions.
+     */
+    String delimiter;
+    final SimpleCompletorFilter filter;
+
+    /**
+     *  Create a new SimpleCompletor with a single possible completion
+     *  values.
+     */
+    public SimpleCompletor(final String candidateString) {
+        this(new String[] {
+                 candidateString
+             });
+    }
+
+    /**
+     *  Create a new SimpleCompletor with a list of possible completion
+     *  values.
+     */
+    public SimpleCompletor(final String[] candidateStrings) {
+        this(candidateStrings, null);
+    }
+
+    public SimpleCompletor(final String[] strings,
+                           final SimpleCompletorFilter filter) {
+        this.filter = filter;
+        setCandidateStrings(strings);
+    }
+
+    /**
+     *  Complete candidates using the contents of the specified Reader.
+     */
+    public SimpleCompletor(final Reader reader) throws IOException {
+        this(getStrings(reader));
+    }
+
+    /**
+     *  Complete candidates using the whitespearated values in
+     *  read from the specified Reader.
+     */
+    public SimpleCompletor(final InputStream in) throws IOException {
+        this(getStrings(new InputStreamReader(in)));
+    }
+
+    private static String[] getStrings(final Reader in)
+                                throws IOException {
+        final Reader reader =
+            (in instanceof BufferedReader) ? in : new BufferedReader(in);
+
+        List words = new LinkedList();
+        String line;
+
+        while ((line = ((BufferedReader) reader).readLine()) != null) {
+            for (StringTokenizer tok = new StringTokenizer(line);
+                     tok.hasMoreTokens(); words.add(tok.nextToken())) {
+                ;
+            }
+        }
+
+        return (String[]) words.toArray(new String[words.size()]);
+    }
+
+    public int complete(final String buffer, final int cursor, final List clist) {
+        String start = (buffer == null) ? "" : buffer;
+
+        SortedSet matches = candidates.tailSet(start);
+
+        for (Iterator i = matches.iterator(); i.hasNext();) {
+            String can = (String) i.next();
+
+            if (!(can.startsWith(start))) {
+                break;
+            }
+
+            if (delimiter != null) {
+                int index = can.indexOf(delimiter, cursor);
+
+                if (index != -1) {
+                    can = can.substring(0, index + 1);
+                }
+            }
+
+            clist.add(can);
+        }
+
+        if (clist.size() == 1) {
+            clist.set(0, ((String) clist.get(0)) + " ");
+        }
+
+        // the index of the completion is always from the beginning of
+        // the buffer.
+        return (clist.size() == 0) ? (-1) : 0;
+    }
+
+    public void setDelimiter(final String delimiter) {
+        this.delimiter = delimiter;
+    }
+
+    public String getDelimiter() {
+        return this.delimiter;
+    }
+
+    public void setCandidates(final SortedSet candidates) {
+        if (filter != null) {
+            TreeSet filtered = new TreeSet();
+
+            for (Iterator i = candidates.iterator(); i.hasNext();) {
+                String element = (String) i.next();
+                element = filter.filter(element);
+
+                if (element != null) {
+                    filtered.add(element);
+                }
+            }
+
+            this.candidates = filtered;
+        } else {
+            this.candidates = candidates;
+        }
+    }
+
+    public SortedSet getCandidates() {
+        return Collections.unmodifiableSortedSet(this.candidates);
+    }
+
+    public void setCandidateStrings(final String[] strings) {
+        setCandidates(new TreeSet(Arrays.asList(strings)));
+    }
+
+    public void addCandidateString(final String candidateString) {
+        final String string =
+            (filter == null) ? candidateString : filter.filter(candidateString);
+
+        if (string != null) {
+            candidates.add(string);
+        }
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    /**
+     *  Filter for elements in the completor.
+     *
+     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+     */
+    public static interface SimpleCompletorFilter {
+        /**
+         *  Filter the specified String. To not filter it, return the
+         *  same String as the parameter. To exclude it, return null.
+         */
+        public String filter(String element);
+    }
+
+    public static class NoOpFilter implements SimpleCompletorFilter {
+        public String filter(final String element) {
+            return element;
+        }
+    }
+}
diff --git a/src/src/main/java/jline/Terminal.java b/src/src/main/java/jline/Terminal.java
new file mode 100644 (file)
index 0000000..6eecbe0
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+
+/**
+ *  Representation of the input terminal for a platform. Handles
+ *  any initialization that the platform may need to perform
+ *  in order to allow the {@link ConsoleReader} to correctly handle
+ *  input.
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public abstract class Terminal implements ConsoleOperations {
+    private static Terminal term;
+
+    /**
+     *  @see #setupTerminal
+     */
+    public static Terminal getTerminal() {
+        return setupTerminal();
+    }
+
+    /** 
+     *  Reset the current terminal to null. 
+     */
+    public static void resetTerminal() {
+        term = null;
+    }
+
+    /**
+     *  <p>Configure and return the {@link Terminal} instance for the
+     *  current platform. This will initialize any system settings
+     *  that are required for the console to be able to handle
+     *  input correctly, such as setting tabtop, buffered input, and
+     *  character echo.</p>
+     *
+     *  <p>This class will use the Terminal implementation specified in the
+     *  <em>jline.terminal</em> system property, or, if it is unset, by
+     *  detecting the operating system from the <em>os.name</em>
+     *  system property and instantiating either the
+     *  {@link WindowsTerminalTest} or {@link UnixTerminal}.
+     *
+     *  @see #initializeTerminal
+     */
+    public static synchronized Terminal setupTerminal() {
+        if (term != null) {
+            return term;
+        }
+
+        final Terminal t;
+
+        String os = System.getProperty("os.name").toLowerCase();
+        String termProp = System.getProperty("jline.terminal");
+
+        if ((termProp != null) && (termProp.length() > 0)) {
+            try {
+                t = (Terminal) Class.forName(termProp).newInstance();
+            } catch (Exception e) {
+                throw (IllegalArgumentException) new IllegalArgumentException(e
+                    .toString()).fillInStackTrace();
+            }
+        } else if (os.indexOf("windows") != -1) {
+            t = new WindowsTerminal();
+        } else {
+            t = new UnixTerminal();
+        }
+
+        try {
+            t.initializeTerminal();
+        } catch (Exception e) {
+            e.printStackTrace();
+
+            return term = new UnsupportedTerminal();
+        }
+
+        return term = t;
+    }
+
+    /**
+     *  Returns true if the current console supports ANSI
+     *  codes.
+     */
+    public boolean isANSISupported() {
+        return true;
+    }
+
+    /**
+     *  Read a single character from the input stream. This might
+     *  enable a terminal implementation to better handle nuances of
+     *  the console.
+     */
+    public int readCharacter(final InputStream in) throws IOException {
+        return in.read();
+    }
+
+    /**
+     *  Reads a virtual key from the console. Typically, this will
+     *  just be the raw character that was entered, but in some cases,
+     *  multiple input keys will need to be translated into a single
+     *  virtual key.
+     *
+     *  @param  in  the InputStream to read from
+     *  @return  the virtual key (e.g., {@link ConsoleOperations#VK_UP})
+     */
+    public int readVirtualKey(InputStream in) throws IOException {
+        return readCharacter(in);
+    }
+
+    /**
+     *  Initialize any system settings
+     *  that are required for the console to be able to handle
+     *  input correctly, such as setting tabtop, buffered input, and
+     *  character echo.
+     */
+    public abstract void initializeTerminal() throws Exception;
+
+    /**
+     *  Returns the current width of the terminal (in characters)
+     */
+    public abstract int getTerminalWidth();
+
+    /**
+     *  Returns the current height of the terminal (in lines)
+     */
+    public abstract int getTerminalHeight();
+
+    /**
+     *  Returns true if this terminal is capable of initializing the
+     *  terminal to use jline.
+     */
+    public abstract boolean isSupported();
+
+    /**
+     *  Returns true if the terminal will echo all characters type.
+     */
+    public abstract boolean getEcho();
+
+    /**
+     *  Invokes before the console reads a line with the prompt and mask.
+     */
+    public void beforeReadLine(ConsoleReader reader, String prompt,
+                               Character mask) {
+    }
+
+    /**
+     *  Invokes after the console reads a line with the prompt and mask.
+     */
+    public void afterReadLine(ConsoleReader reader, String prompt,
+                              Character mask) {
+    }
+
+    /**
+     *  Returns false if character echoing is disabled.
+     */
+    public abstract boolean isEchoEnabled();
+
+
+    /**
+     *  Enable character echoing. This can be used to re-enable character
+     *  if the ConsoleReader is no longer being used.
+     */
+    public abstract void enableEcho();
+
+
+    /**
+     *  Disable character echoing. This can be used to manually re-enable
+     *  character if the ConsoleReader has been disabled.
+     */
+    public abstract void disableEcho();
+
+    public InputStream getDefaultBindings() {
+        // Mac bindings are slightly different from Unix/Linux.
+        // For instance, the Delete key behavior is different between them.
+        return Terminal.class.getResourceAsStream(
+                System.getProperty("os.name").toLowerCase().startsWith("mac") ?
+                    "keybindings-mac.properties" : "keybindings.properties");
+    }
+}
diff --git a/src/src/main/java/jline/UnixTerminal.java b/src/src/main/java/jline/UnixTerminal.java
new file mode 100644 (file)
index 0000000..83f5194
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *  <p>
+ *  Terminal that is used for unix platforms. Terminal initialization
+ *  is handled by issuing the <em>stty</em> command against the
+ *  <em>/dev/tty</em> file to disable character echoing and enable
+ *  character input. All known unix systems (including
+ *  Linux and Macintosh OS X) support the <em>stty</em>), so this
+ *  implementation should work for an reasonable POSIX system.
+ *        </p>
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ *  @author  Updates <a href="mailto:dwkemp@gmail.com">Dale Kemp</a> 2005-12-03
+ */
+public class UnixTerminal extends Terminal {
+    public static final short ARROW_START = 27;
+    public static final short ARROW_PREFIX = 91;
+    public static final short ARROW_LEFT = 68;
+    public static final short ARROW_RIGHT = 67;
+    public static final short ARROW_UP = 65;
+    public static final short ARROW_DOWN = 66;
+    public static final short O_PREFIX = 79;
+    public static final short HOME_CODE = 72;
+    public static final short END_CODE = 70;
+
+    public static final short DEL_THIRD = 51;
+    public static final short DEL_SECOND = 126;
+
+    private boolean echoEnabled;
+    private String ttyConfig;
+    private String ttyProps;
+    private long ttyPropsLastFetched;
+    private boolean backspaceDeleteSwitched = false;
+    private static String sttyCommand =
+        System.getProperty("jline.sttyCommand", "stty");
+
+    
+    String encoding = System.getProperty("input.encoding", "UTF-8");
+    ReplayPrefixOneCharInputStream replayStream = new ReplayPrefixOneCharInputStream(encoding);
+    InputStreamReader replayReader;
+
+    public UnixTerminal() {
+        try {
+            replayReader = new InputStreamReader(replayStream, encoding);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+   
+    protected void checkBackspace(){
+        String[] ttyConfigSplit = ttyConfig.split(":|=");
+        backspaceDeleteSwitched = ttyConfigSplit.length >= 7 && "7f".equals(ttyConfigSplit[6]);
+    }
+    
+    /**
+     *  Remove line-buffered input by invoking "stty -icanon min 1"
+     *  against the current terminal.
+     */
+    public void initializeTerminal() throws IOException, InterruptedException {
+        // save the initial tty configuration
+        ttyConfig = stty("-g");
+
+        // sanity check
+        if ((ttyConfig.length() == 0)
+                || ((ttyConfig.indexOf("=") == -1)
+                       && (ttyConfig.indexOf(":") == -1))) {
+            throw new IOException("Unrecognized stty code: " + ttyConfig);
+        }
+
+        checkBackspace();
+
+        // set the console to be character-buffered instead of line-buffered
+        stty("-icanon min 1");
+
+        // disable character echoing
+        stty("-echo");
+        echoEnabled = false;
+
+        // at exit, restore the original tty configuration (for JDK 1.3+)
+        try {
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                    public void start() {
+                        try {
+                            restoreTerminal();
+                        } catch (Exception e) {
+                            consumeException(e);
+                        }
+                    }
+                });
+        } catch (AbstractMethodError ame) {
+            // JDK 1.3+ only method. Bummer.
+            consumeException(ame);
+        }
+    }
+
+    /** 
+     * Restore the original terminal configuration, which can be used when
+     * shutting down the console reader. The ConsoleReader cannot be
+     * used after calling this method.
+     */
+    public void restoreTerminal() throws Exception {
+        if (ttyConfig != null) {
+            stty(ttyConfig);
+            ttyConfig = null;
+        }
+        resetTerminal();
+    }
+
+    
+    
+    public int readVirtualKey(InputStream in) throws IOException {
+        int c = readCharacter(in);
+
+        if (backspaceDeleteSwitched)
+            if (c == DELETE)
+                c = BACKSPACE;
+            else if (c == BACKSPACE)
+                c = DELETE;
+
+        // in Unix terminals, arrow keys are represented by
+        // a sequence of 3 characters. E.g., the up arrow
+        // key yields 27, 91, 68
+        if (c == ARROW_START && in.available() > 0) {
+            // Escape key is also 27, so we use InputStream.available()
+            // to distinguish those. If 27 represents an arrow, there
+            // should be two more chars immediately available.
+            while (c == ARROW_START) {
+                c = readCharacter(in);
+            }
+            if (c == ARROW_PREFIX || c == O_PREFIX) {
+                c = readCharacter(in);
+                if (c == ARROW_UP) {
+                    return CTRL_P;
+                } else if (c == ARROW_DOWN) {
+                    return CTRL_N;
+                } else if (c == ARROW_LEFT) {
+                    return CTRL_B;
+                } else if (c == ARROW_RIGHT) {
+                    return CTRL_F;
+                } else if (c == HOME_CODE) {
+                    return CTRL_A;
+                } else if (c == END_CODE) {
+                    return CTRL_E;
+                } else if (c == DEL_THIRD) {
+                    c = readCharacter(in); // read 4th
+                    return DELETE;
+                }
+            } 
+        } 
+        // handle unicode characters, thanks for a patch from amyi@inf.ed.ac.uk
+        if (c > 128) {
+          // handle unicode characters longer than 2 bytes,
+          // thanks to Marc.Herbert@continuent.com
+            replayStream.setInput(c, in);
+//            replayReader = new InputStreamReader(replayStream, encoding);
+            c = replayReader.read();
+            
+        }
+
+        return c;
+    }
+
+    /**
+     *  No-op for exceptions we want to silently consume.
+     */
+    private void consumeException(Throwable e) {
+    }
+
+    public boolean isSupported() {
+        return true;
+    }
+
+    public boolean getEcho() {
+        return false;
+    }
+
+    /**
+     *  Returns the value of "stty size" width param.
+     *
+     *  <strong>Note</strong>: this method caches the value from the
+     *  first time it is called in order to increase speed, which means
+     *  that changing to size of the terminal will not be reflected
+     *  in the console.
+     */
+    public int getTerminalWidth() {
+        int val = -1;
+
+        try {
+            val = getTerminalProperty("columns");
+        } catch (Exception e) {
+        }
+
+        if (val == -1) {
+            val = 80;
+        }
+
+        return val;
+    }
+
+    /**
+     *  Returns the value of "stty size" height param.
+     *
+     *  <strong>Note</strong>: this method caches the value from the
+     *  first time it is called in order to increase speed, which means
+     *  that changing to size of the terminal will not be reflected
+     *  in the console.
+     */
+    public int getTerminalHeight() {
+        int val = -1;
+
+        try {
+            val = getTerminalProperty("rows");
+        } catch (Exception e) {
+        }
+
+        if (val == -1) {
+            val = 24;
+        }
+
+        return val;
+    }
+
+    private int getTerminalProperty(String prop)
+                                    throws IOException, InterruptedException {
+        // tty properties are cached so we don't have to worry too much about getting term widht/height
+        if (ttyProps == null || System.currentTimeMillis() - ttyPropsLastFetched > 1000) {
+            ttyProps = stty("-a");
+            ttyPropsLastFetched = System.currentTimeMillis();
+        }
+        // need to be able handle both output formats:
+        // speed 9600 baud; 24 rows; 140 columns;
+        // and:
+        // speed 38400 baud; rows = 49; columns = 111; ypixels = 0; xpixels = 0;
+        for (StringTokenizer tok = new StringTokenizer(ttyProps, ";\n");
+                 tok.hasMoreTokens();) {
+            String str = tok.nextToken().trim();
+
+            if (str.startsWith(prop)) {
+                int index = str.lastIndexOf(" ");
+
+                return Integer.parseInt(str.substring(index).trim());
+            } else if (str.endsWith(prop)) {
+                int index = str.indexOf(" ");
+
+                return Integer.parseInt(str.substring(0, index).trim());
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     *  Execute the stty command with the specified arguments
+     *  against the current active terminal.
+     */
+    protected static String stty(final String args)
+                        throws IOException, InterruptedException {
+        return exec("stty " + args + " < /dev/tty").trim();
+    }
+
+    /**
+     *  Execute the specified command and return the output
+     *  (both stdout and stderr).
+     */
+    private static String exec(final String cmd)
+                        throws IOException, InterruptedException {
+        return exec(new String[] {
+                        "sh",
+                        "-c",
+                        cmd
+                    });
+    }
+
+    /**
+     *  Execute the specified command and return the output
+     *  (both stdout and stderr).
+     */
+    private static String exec(final String[] cmd)
+                        throws IOException, InterruptedException {
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+
+        Process p = Runtime.getRuntime().exec(cmd);
+        int c;
+        InputStream in = null;
+        InputStream err = null;
+        OutputStream out = null;
+
+        try {
+               in = p.getInputStream();
+
+               while ((c = in.read()) != -1) {
+                   bout.write(c);
+               }
+
+               err = p.getErrorStream();
+
+               while ((c = err.read()) != -1) {
+                   bout.write(c);
+               }
+       
+               out = p.getOutputStream();
+
+               p.waitFor();
+           } finally {
+                   try {in.close();} catch (Exception e) {}
+                   try {err.close();} catch (Exception e) {}
+                   try {out.close();} catch (Exception e) {}
+           }
+
+        String result = new String(bout.toByteArray());
+
+        return result;
+    }
+
+    /**
+     *  The command to use to set the terminal options. Defaults
+     *  to "stty", or the value of the system property "jline.sttyCommand".
+     */
+    public static void setSttyCommand(String cmd) {
+        sttyCommand = cmd;
+    }
+
+    /**
+     *  The command to use to set the terminal options. Defaults
+     *  to "stty", or the value of the system property "jline.sttyCommand".
+     */
+    public static String getSttyCommand() {
+        return sttyCommand;
+    }
+
+    public synchronized boolean isEchoEnabled() {
+        return echoEnabled;
+    }
+
+
+    public synchronized void enableEcho() {
+       try {
+                       stty("echo");
+            echoEnabled = true;
+               } catch (Exception e) {
+                       consumeException(e);
+               }
+    }
+
+    public synchronized void disableEcho() {
+       try {
+                       stty("-echo");
+            echoEnabled = false;
+               } catch (Exception e) {
+                       consumeException(e);
+               }
+    }
+
+    /**
+     * This is awkward and inefficient, but probably the minimal way to add
+     * UTF-8 support to JLine
+     *
+     * @author <a href="mailto:Marc.Herbert@continuent.com">Marc Herbert</a>
+     */
+    static class ReplayPrefixOneCharInputStream extends InputStream {
+        byte firstByte;
+        int byteLength;
+        InputStream wrappedStream;
+        int byteRead;
+
+        final String encoding;
+        
+        public ReplayPrefixOneCharInputStream(String encoding) {
+            this.encoding = encoding;
+        }
+        
+        public void setInput(int recorded, InputStream wrapped) throws IOException {
+            this.byteRead = 0;
+            this.firstByte = (byte) recorded;
+            this.wrappedStream = wrapped;
+
+            byteLength = 1;
+            if (encoding.equalsIgnoreCase("UTF-8"))
+                setInputUTF8(recorded, wrapped);
+            else if (encoding.equalsIgnoreCase("UTF-16"))
+                byteLength = 2;
+            else if (encoding.equalsIgnoreCase("UTF-32"))
+                byteLength = 4;
+        }
+            
+            
+        public void setInputUTF8(int recorded, InputStream wrapped) throws IOException {
+            // 110yyyyy 10zzzzzz
+            if ((firstByte & (byte) 0xE0) == (byte) 0xC0)
+                this.byteLength = 2;
+            // 1110xxxx 10yyyyyy 10zzzzzz
+            else if ((firstByte & (byte) 0xF0) == (byte) 0xE0)
+                this.byteLength = 3;
+            // 11110www 10xxxxxx 10yyyyyy 10zzzzzz
+            else if ((firstByte & (byte) 0xF8) == (byte) 0xF0)
+                this.byteLength = 4;
+            else
+                throw new IOException("invalid UTF-8 first byte: " + firstByte);
+        }
+
+        public int read() throws IOException {
+            if (available() == 0)
+                return -1;
+
+            byteRead++;
+
+            if (byteRead == 1)
+                return firstByte;
+
+            return wrappedStream.read();
+        }
+
+        /**
+        * InputStreamReader is greedy and will try to read bytes in advance. We
+        * do NOT want this to happen since we use a temporary/"losing bytes"
+        * InputStreamReader above, that's why we hide the real
+        * wrappedStream.available() here.
+        */
+        public int available() {
+            return byteLength - byteRead;
+        }
+    }
+}
diff --git a/src/src/main/java/jline/UnsupportedTerminal.java b/src/src/main/java/jline/UnsupportedTerminal.java
new file mode 100644 (file)
index 0000000..2d87a18
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.IOException;
+
+/**
+ *  A no-op unsupported terminal.
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class UnsupportedTerminal extends Terminal {
+    private Thread maskThread = null;
+
+    public void initializeTerminal() {
+        // nothing we need to do (or can do) for windows.
+    }
+
+    public boolean getEcho() {
+        return true;
+    }
+
+
+    public boolean isEchoEnabled() {
+        return true;
+    }
+
+
+    public void enableEcho() {
+    }
+
+
+    public void disableEcho() {
+    }
+
+
+    /**
+     *  Always returng 80, since we can't access this info on Windows.
+     */
+    public int getTerminalWidth() {
+        return 80;
+    }
+
+    /**
+     *  Always returng 24, since we can't access this info on Windows.
+     */
+    public int getTerminalHeight() {
+        return 80;
+    }
+
+    public boolean isSupported() {
+        return false;
+    }
+
+    public void beforeReadLine(final ConsoleReader reader, final String prompt,
+       final Character mask) {
+        if ((mask != null) && (maskThread == null)) {
+            final String fullPrompt = "\r" + prompt
+                + "                 "
+                + "                 "
+                + "                 "
+                + "\r" + prompt;
+
+            maskThread = new Thread("JLine Mask Thread") {
+                public void run() {
+                    while (!interrupted()) {
+                        try {
+                            reader.out.write(fullPrompt);
+                            reader.out.flush();
+                            sleep(3);
+                        } catch (IOException ioe) {
+                            return;
+                        } catch (InterruptedException ie) {
+                            return;
+                        }
+                    }
+                }
+            };
+
+            maskThread.setPriority(Thread.MAX_PRIORITY);
+            maskThread.setDaemon(true);
+            maskThread.start();
+        }
+    }
+
+    public void afterReadLine(final ConsoleReader reader, final String prompt,
+        final Character mask) {
+        if ((maskThread != null) && maskThread.isAlive()) {
+            maskThread.interrupt();
+        }
+
+        maskThread = null;
+    }
+}
diff --git a/src/src/main/java/jline/WindowsTerminal.java b/src/src/main/java/jline/WindowsTerminal.java
new file mode 100644 (file)
index 0000000..d036088
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+
+import jline.UnixTerminal.ReplayPrefixOneCharInputStream;
+
+/**
+ * <p>
+ * Terminal implementation for Microsoft Windows. Terminal initialization in
+ * {@link #initializeTerminal} is accomplished by extracting the
+ * <em>jline_<i>version</i>.dll</em>, saving it to the system temporary
+ * directoy (determined by the setting of the <em>java.io.tmpdir</em> System
+ * property), loading the library, and then calling the Win32 APIs <a
+ * href="http://msdn.microsoft.com/library/default.asp?
+ * url=/library/en-us/dllproc/base/setconsolemode.asp">SetConsoleMode</a> and
+ * <a href="http://msdn.microsoft.com/library/default.asp?
+ * url=/library/en-us/dllproc/base/getconsolemode.asp">GetConsoleMode</a> to
+ * disable character echoing.
+ * </p>
+ *
+ * <p>
+ * By default, the {@link #readCharacter} method will attempt to test to see if
+ * the specified {@link InputStream} is {@link System#in} or a wrapper around
+ * {@link FileDescriptor#in}, and if so, will bypass the character reading to
+ * directly invoke the readc() method in the JNI library. This is so the class
+ * can read special keys (like arrow keys) which are otherwise inaccessible via
+ * the {@link System#in} stream. Using JNI reading can be bypassed by setting
+ * the <code>jline.WindowsTerminal.directConsole</code> system property
+ * to <code>false</code>.
+ * </p>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class WindowsTerminal extends Terminal {
+    // constants copied from wincon.h
+
+    /**
+     * The ReadFile or ReadConsole function returns only when a carriage return
+     * character is read. If this mode is disable, the functions return when one
+     * or more characters are available.
+     */
+    private static final int ENABLE_LINE_INPUT = 2;
+
+    /**
+     * Characters read by the ReadFile or ReadConsole function are written to
+     * the active screen buffer as they are read. This mode can be used only if
+     * the ENABLE_LINE_INPUT mode is also enabled.
+     */
+    private static final int ENABLE_ECHO_INPUT = 4;
+
+    /**
+     * CTRL+C is processed by the system and is not placed in the input buffer.
+     * If the input buffer is being read by ReadFile or ReadConsole, other
+     * control keys are processed by the system and are not returned in the
+     * ReadFile or ReadConsole buffer. If the ENABLE_LINE_INPUT mode is also
+     * enabled, backspace, carriage return, and linefeed characters are handled
+     * by the system.
+     */
+    private static final int ENABLE_PROCESSED_INPUT = 1;
+
+    /**
+     * User interactions that change the size of the console screen buffer are
+     * reported in the console's input buffee. Information about these events
+     * can be read from the input buffer by applications using
+     * theReadConsoleInput function, but not by those using ReadFile
+     * orReadConsole.
+     */
+    private static final int ENABLE_WINDOW_INPUT = 8;
+
+    /**
+     * If the mouse pointer is within the borders of the console window and the
+     * window has the keyboard focus, mouse events generated by mouse movement
+     * and button presses are placed in the input buffer. These events are
+     * discarded by ReadFile or ReadConsole, even when this mode is enabled.
+     */
+    private static final int ENABLE_MOUSE_INPUT = 16;
+
+    /**
+     * When enabled, text entered in a console window will be inserted at the
+     * current cursor location and all text following that location will not be
+     * overwritten. When disabled, all following text will be overwritten. An OR
+     * operation must be performed with this flag and the ENABLE_EXTENDED_FLAGS
+     * flag to enable this functionality.
+     */
+    private static final int ENABLE_PROCESSED_OUTPUT = 1;
+
+    /**
+     * This flag enables the user to use the mouse to select and edit text. To
+     * enable this option, use the OR to combine this flag with
+     * ENABLE_EXTENDED_FLAGS.
+     */
+    private static final int ENABLE_WRAP_AT_EOL_OUTPUT = 2;
+
+    /**
+     * On windows terminals, this character indicates that a 'special' key has
+     * been pressed. This means that a key such as an arrow key, or delete, or
+     * home, etc. will be indicated by the next character.
+     */
+    public static final int SPECIAL_KEY_INDICATOR = 224;
+
+    /**
+     * On windows terminals, this character indicates that a special key on the
+     * number pad has been pressed.
+     */
+    public static final int NUMPAD_KEY_INDICATOR = 0;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR,
+     * this character indicates an left arrow key press.
+     */
+    public static final int LEFT_ARROW_KEY = 75;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+     * this character indicates an
+     * right arrow key press.
+     */
+    public static final int RIGHT_ARROW_KEY = 77;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+     * this character indicates an up
+     * arrow key press.
+     */
+    public static final int UP_ARROW_KEY = 72;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+     * this character indicates an
+     * down arrow key press.
+     */
+    public static final int DOWN_ARROW_KEY = 80;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+     * this character indicates that
+     * the delete key was pressed.
+     */
+    public static final int DELETE_KEY = 83;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+     * this character indicates that
+     * the home key was pressed.
+     */
+    public static final int HOME_KEY = 71;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+     * this character indicates that
+     * the end key was pressed.
+     */
+    public static final char END_KEY = 79;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+     * this character indicates that
+     * the page up key was pressed.
+     */
+    public static final char PAGE_UP_KEY = 73;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+     * this character indicates that
+     * the page down key was pressed.
+     */
+    public static final char PAGE_DOWN_KEY = 81;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+     * this character indicates that
+     * the insert key was pressed.
+     */
+    public static final char INSERT_KEY = 82;
+
+    /**
+     * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR,
+     * this character indicates that the escape key was pressed.
+     */
+    public static final char ESCAPE_KEY = 0;
+
+    private Boolean directConsole;
+
+    private boolean echoEnabled;
+    
+    String encoding = System.getProperty("jline.WindowsTerminal.input.encoding", System.getProperty("file.encoding"));
+    ReplayPrefixOneCharInputStream replayStream = new ReplayPrefixOneCharInputStream(encoding);
+    InputStreamReader replayReader;
+    
+    public WindowsTerminal() {
+        String dir = System.getProperty("jline.WindowsTerminal.directConsole");
+
+        if ("true".equals(dir)) {
+            directConsole = Boolean.TRUE;
+        } else if ("false".equals(dir)) {
+            directConsole = Boolean.FALSE;
+        }
+        
+        try {
+            replayReader = new InputStreamReader(replayStream, encoding);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        
+    }
+
+    private native int getConsoleMode();
+
+    private native void setConsoleMode(final int mode);
+
+    private native int readByte();
+
+    private native int getWindowsTerminalWidth();
+
+    private native int getWindowsTerminalHeight();
+
+    public int readCharacter(final InputStream in) throws IOException {
+        // if we can detect that we are directly wrapping the system
+        // input, then bypass the input stream and read directly (which
+        // allows us to access otherwise unreadable strokes, such as
+        // the arrow keys)
+        if (directConsole == Boolean.FALSE) {
+            return super.readCharacter(in);
+        } else if ((directConsole == Boolean.TRUE)
+            || ((in == System.in) || (in instanceof FileInputStream
+                && (((FileInputStream) in).getFD() == FileDescriptor.in)))) {
+            return readByte();
+        } else {
+            return super.readCharacter(in);
+        }
+    }
+
+    public void initializeTerminal() throws Exception {
+        loadLibrary("jline");
+
+        final int originalMode = getConsoleMode();
+
+        setConsoleMode(originalMode & ~ENABLE_ECHO_INPUT);
+
+        // set the console to raw mode
+        int newMode = originalMode
+            & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT
+                | ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT);
+        echoEnabled = false;
+        setConsoleMode(newMode);
+
+        // at exit, restore the original tty configuration (for JDK 1.3+)
+        try {
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                public void start() {
+                    // restore the old console mode
+                    setConsoleMode(originalMode);
+                }
+            });
+        } catch (AbstractMethodError ame) {
+            // JDK 1.3+ only method. Bummer.
+            consumeException(ame);
+        }
+    }
+
+    private void loadLibrary(final String name) throws IOException {
+        // store the DLL in the temporary directory for the System
+        String version = WindowsTerminal.class.getPackage().getImplementationVersion();
+
+        if (version == null) {
+            version = "";
+        }
+
+        version = version.replace('.', '_');
+
+        File f = new File(System.getProperty("java.io.tmpdir"), name + "_"
+                + version + ".dll");
+        boolean exists = f.isFile(); // check if it already exists
+
+        // extract the embedded jline.dll file from the jar and save
+        // it to the current directory
+        int bits = 32;
+
+        // check for 64-bit systems and use to appropriate DLL
+        if (System.getProperty("os.arch").indexOf("64") != -1)
+            bits = 64;
+
+        InputStream in = new BufferedInputStream(WindowsTerminal.class.getResourceAsStream(name + bits + ".dll"));
+
+        OutputStream fout = null;
+        try {
+            fout = new BufferedOutputStream(
+                    new FileOutputStream(f));
+            byte[] bytes = new byte[1024 * 10];
+
+            for (int n = 0; n != -1; n = in.read(bytes)) {
+                fout.write(bytes, 0, n);
+            }
+
+        } catch (IOException ioe) {
+            // We might get an IOException trying to overwrite an existing
+            // jline.dll file if there is another process using the DLL.
+            // If this happens, ignore errors.
+            if (!exists) {
+                throw ioe;
+            }
+        } finally {
+               if (fout != null) {
+                       try {
+                               fout.close();
+                       } catch (IOException ioe) {
+                               // ignore
+                       }
+               }
+        }
+
+        // try to clean up the DLL after the JVM exits
+        f.deleteOnExit();
+
+        // now actually load the DLL
+        System.load(f.getAbsolutePath());
+    }
+
+    public int readVirtualKey(InputStream in) throws IOException {
+        int indicator = readCharacter(in);
+
+        // in Windows terminals, arrow keys are represented by
+        // a sequence of 2 characters. E.g., the up arrow
+        // key yields 224, 72
+        if (indicator == SPECIAL_KEY_INDICATOR
+                || indicator == NUMPAD_KEY_INDICATOR) {
+            int key = readCharacter(in);
+
+            switch (key) {
+            case UP_ARROW_KEY:
+                return CTRL_P; // translate UP -> CTRL-P
+            case LEFT_ARROW_KEY:
+                return CTRL_B; // translate LEFT -> CTRL-B
+            case RIGHT_ARROW_KEY:
+                return CTRL_F; // translate RIGHT -> CTRL-F
+            case DOWN_ARROW_KEY:
+                return CTRL_N; // translate DOWN -> CTRL-N
+            case DELETE_KEY:
+                return CTRL_QM; // translate DELETE -> CTRL-?
+            case HOME_KEY:
+                return CTRL_A;
+            case END_KEY:
+                return CTRL_E;
+            case PAGE_UP_KEY:
+                return CTRL_K;
+            case PAGE_DOWN_KEY:
+                return CTRL_L;
+            case ESCAPE_KEY:
+                return CTRL_OB; // translate ESCAPE -> CTRL-[
+            case INSERT_KEY:
+                return CTRL_C;
+            default:
+                return 0;
+            }
+        } else if (indicator > 128) {
+               // handle unicode characters longer than 2 bytes,
+               // thanks to Marc.Herbert@continuent.com
+                replayStream.setInput(indicator, in);
+                // replayReader = new InputStreamReader(replayStream, encoding);
+                indicator = replayReader.read();
+                
+        }
+        
+        return indicator;
+        
+       }
+
+    public boolean isSupported() {
+        return true;
+    }
+
+    /**
+     * Windows doesn't support ANSI codes by default; disable them.
+     */
+    public boolean isANSISupported() {
+        return false;
+    }
+
+    public boolean getEcho() {
+        return false;
+    }
+
+    /**
+     * Unsupported; return the default.
+     *
+     * @see Terminal#getTerminalWidth
+     */
+    public int getTerminalWidth() {
+        return getWindowsTerminalWidth();
+    }
+
+    /**
+     * Unsupported; return the default.
+     *
+     * @see Terminal#getTerminalHeight
+     */
+    public int getTerminalHeight() {
+        return getWindowsTerminalHeight();
+    }
+
+    /**
+     * No-op for exceptions we want to silently consume.
+     */
+    private void consumeException(final Throwable e) {
+    }
+
+    /**
+     * Whether or not to allow the use of the JNI console interaction.
+     */
+    public void setDirectConsole(Boolean directConsole) {
+        this.directConsole = directConsole;
+    }
+
+    /**
+     * Whether or not to allow the use of the JNI console interaction.
+     */
+    public Boolean getDirectConsole() {
+        return this.directConsole;
+    }
+
+    public synchronized boolean isEchoEnabled() {
+        return echoEnabled;
+    }
+
+    public synchronized void enableEcho() {
+        // Must set these four modes at the same time to make it work fine.
+        setConsoleMode(getConsoleMode() | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT
+            | ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT);
+        echoEnabled = true;
+    }
+
+    public synchronized void disableEcho() {
+        // Must set these four modes at the same time to make it work fine.
+        setConsoleMode(getConsoleMode()
+            & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT
+                | ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT));
+        echoEnabled = true;
+    }
+
+    public InputStream getDefaultBindings() {
+        return WindowsTerminal.class.getResourceAsStream("windowsbindings.properties");
+    }
+    
+    /**
+     * This is awkward and inefficient, but probably the minimal way to add
+     * UTF-8 support to JLine
+     *
+     * @author <a href="mailto:Marc.Herbert@continuent.com">Marc Herbert</a>
+     */
+    static class ReplayPrefixOneCharInputStream extends InputStream {
+        byte firstByte;
+        int byteLength;
+        InputStream wrappedStream;
+        int byteRead;
+
+        final String encoding;
+        
+        public ReplayPrefixOneCharInputStream(String encoding) {
+            this.encoding = encoding;
+        }
+        
+        public void setInput(int recorded, InputStream wrapped) throws IOException {
+            this.byteRead = 0;
+            this.firstByte = (byte) recorded;
+            this.wrappedStream = wrapped;
+
+            byteLength = 1;
+            if (encoding.equalsIgnoreCase("UTF-8"))
+                setInputUTF8(recorded, wrapped);
+            else if (encoding.equalsIgnoreCase("UTF-16"))
+                byteLength = 2;
+            else if (encoding.equalsIgnoreCase("UTF-32"))
+                byteLength = 4;
+        }
+            
+            
+        public void setInputUTF8(int recorded, InputStream wrapped) throws IOException {
+            // 110yyyyy 10zzzzzz
+            if ((firstByte & (byte) 0xE0) == (byte) 0xC0)
+                this.byteLength = 2;
+            // 1110xxxx 10yyyyyy 10zzzzzz
+            else if ((firstByte & (byte) 0xF0) == (byte) 0xE0)
+                this.byteLength = 3;
+            // 11110www 10xxxxxx 10yyyyyy 10zzzzzz
+            else if ((firstByte & (byte) 0xF8) == (byte) 0xF0)
+                this.byteLength = 4;
+            else
+                throw new IOException("invalid UTF-8 first byte: " + firstByte);
+        }
+
+        public int read() throws IOException {
+            if (available() == 0)
+                return -1;
+
+            byteRead++;
+
+            if (byteRead == 1)
+                return firstByte;
+
+            return wrappedStream.read();
+        }
+
+        /**
+        * InputStreamReader is greedy and will try to read bytes in advance. We
+        * do NOT want this to happen since we use a temporary/"losing bytes"
+        * InputStreamReader above, that's why we hide the real
+        * wrappedStream.available() here.
+        */
+        public int available() {
+            return byteLength - byteRead;
+        }
+    }
+    
+}
diff --git a/src/src/main/java/jline/package.html b/src/src/main/java/jline/package.html
new file mode 100644 (file)
index 0000000..c807431
--- /dev/null
@@ -0,0 +1,9 @@
+<body>
+<p>
+The core JLine API. The central class is
+<a href="ConsoleReader">jline.ConsoleReader</a>}, which
+is a reader for obtaining input from an arbitrary
+InputStream (usually <em>System.in</em>).
+</p>
+</body>
+
diff --git a/src/src/main/native/Makefile b/src/src/main/native/Makefile
new file mode 100644 (file)
index 0000000..c620814
--- /dev/null
@@ -0,0 +1,8 @@
+
+
+#export PATH=${PATH}:/usr/lib/gcc-lib/i686-pc-cygwin/3.3.3
+JDK='/C/Program Files/Java/jdk1.5.0/'
+
+native:
+       #gcc -I'C:/Program Files/Java/'*'/include/' -I'C:/Program Files/Java/'*'/include//win32/' -mno-cygwin -Wl,--add-stdcall-alias -shared -o jline.dll jline_WindowsTerminal.c
+       gcc -L /usr/lib/mingw/ -I${JDK}/include -I${JDK}/include/win32 -mwindows -mno-cygwin -Wl,--add-stdcall-alias -shared -o jline.dll jline_WindowsTerminal.c
diff --git a/src/src/main/native/jline_WindowsTerminal.c b/src/src/main/native/jline_WindowsTerminal.c
new file mode 100644 (file)
index 0000000..4e78a66
--- /dev/null
@@ -0,0 +1,57 @@
+#include "jline_WindowsTerminal.h"
+#include <windows.h>
+
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getConsoleMode
+  (JNIEnv *env, jobject ob)
+{
+       DWORD mode;
+       HANDLE hConsole = GetStdHandle (STD_INPUT_HANDLE);
+
+       if (hConsole == INVALID_HANDLE_VALUE)
+               return -1;
+
+       if (!GetConsoleMode (hConsole, &mode))
+               return -1;
+
+       // CloseHandle (hConsole);
+
+       // printf ("JNI get mode=%d\n", mode);
+       return mode;
+}
+
+JNIEXPORT void JNICALL Java_jline_WindowsTerminal_setConsoleMode
+  (JNIEnv *env, jobject ob, jint mode)
+{
+       DWORD m = (DWORD)mode;
+       HANDLE hConsole = GetStdHandle (STD_INPUT_HANDLE);
+
+       if (hConsole == INVALID_HANDLE_VALUE)
+               return;
+
+       // printf ("JNI set mode=%d\n", m);
+       SetConsoleMode (hConsole, m);
+       // CloseHandle (hConsole);
+}
+
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_readByte (JNIEnv * env, jclass class)
+{
+       return getch ();
+}
+
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getWindowsTerminalWidth (JNIEnv * env, jclass class)
+{
+       HANDLE inputHandle = GetStdHandle (STD_INPUT_HANDLE);
+       HANDLE outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
+       PCONSOLE_SCREEN_BUFFER_INFO info =  malloc (sizeof (CONSOLE_SCREEN_BUFFER_INFO));
+       GetConsoleScreenBufferInfo (outputHandle, info);
+       return info->srWindow.Right - info->srWindow.Left+1;
+}
+
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getWindowsTerminalHeight (JNIEnv * env, jclass class)
+{
+       HANDLE inputHandle = GetStdHandle (STD_INPUT_HANDLE);
+       HANDLE outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
+       PCONSOLE_SCREEN_BUFFER_INFO info =  malloc (sizeof (CONSOLE_SCREEN_BUFFER_INFO));
+       GetConsoleScreenBufferInfo (outputHandle, info);
+       return info->srWindow.Bottom - info->srWindow.Top+1;
+}
diff --git a/src/src/main/native/jline_WindowsTerminal.h b/src/src/main/native/jline_WindowsTerminal.h
new file mode 100644 (file)
index 0000000..5078b93
--- /dev/null
@@ -0,0 +1,68 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class jline_WindowsTerminal */
+
+#ifndef _Included_jline_WindowsTerminal
+#define _Included_jline_WindowsTerminal
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Inaccessible static: term */
+#undef jline_WindowsTerminal_ENABLE_LINE_INPUT
+#define jline_WindowsTerminal_ENABLE_LINE_INPUT 2L
+#undef jline_WindowsTerminal_ENABLE_ECHO_INPUT
+#define jline_WindowsTerminal_ENABLE_ECHO_INPUT 4L
+#undef jline_WindowsTerminal_ENABLE_PROCESSED_INPUT
+#define jline_WindowsTerminal_ENABLE_PROCESSED_INPUT 1L
+#undef jline_WindowsTerminal_ENABLE_WINDOW_INPUT
+#define jline_WindowsTerminal_ENABLE_WINDOW_INPUT 8L
+#undef jline_WindowsTerminal_ENABLE_MOUSE_INPUT
+#define jline_WindowsTerminal_ENABLE_MOUSE_INPUT 16L
+#undef jline_WindowsTerminal_ENABLE_PROCESSED_OUTPUT
+#define jline_WindowsTerminal_ENABLE_PROCESSED_OUTPUT 1L
+#undef jline_WindowsTerminal_ENABLE_WRAP_AT_EOL_OUTPUT
+#define jline_WindowsTerminal_ENABLE_WRAP_AT_EOL_OUTPUT 2L
+/*
+ * Class:     jline_WindowsTerminal
+ * Method:    getConsoleMode
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getConsoleMode
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     jline_WindowsTerminal
+ * Method:    setConsoleMode
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_jline_WindowsTerminal_setConsoleMode
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     jline_WindowsTerminal
+ * Method:    readByte
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_readByte
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     jline_WindowsTerminal
+ * Method:    getWindowsTerminalWidth
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getWindowsTerminalWidth
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     jline_WindowsTerminal
+ * Method:    getWindowsTerminalHeight
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getWindowsTerminalHeight
+  (JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/src/main/resources/jline/CandidateListCompletionHandler.properties b/src/src/main/resources/jline/CandidateListCompletionHandler.properties
new file mode 100644 (file)
index 0000000..18ee221
--- /dev/null
@@ -0,0 +1,5 @@
+display-candidates: Display all {0} possibilities? (y or n)
+display-candidates-yes: y
+display-candidates-no: n
+display-more: --More--
+
diff --git a/src/src/main/resources/jline/jline32.dll b/src/src/main/resources/jline/jline32.dll
new file mode 100644 (file)
index 0000000..a0d3b11
Binary files /dev/null and b/src/src/main/resources/jline/jline32.dll differ
diff --git a/src/src/main/resources/jline/jline64.dll b/src/src/main/resources/jline/jline64.dll
new file mode 100644 (file)
index 0000000..922d6b0
Binary files /dev/null and b/src/src/main/resources/jline/jline64.dll differ
diff --git a/src/src/main/resources/jline/keybindings-mac.properties b/src/src/main/resources/jline/keybindings-mac.properties
new file mode 100644 (file)
index 0000000..6f13615
--- /dev/null
@@ -0,0 +1,62 @@
+# Keybinding mapping for JLine. The format is:
+#    [key code]: [logical operation]
+
+# CTRL-B: move to the previous character
+2: PREV_CHAR
+
+# CTRL-G: move to the previous word
+7: PREV_WORD
+
+# CTRL-F: move to the next character
+6: NEXT_CHAR
+
+# CTRL-A: move to the beginning of the line
+1: MOVE_TO_BEG
+
+# CTRL-D: close out the input stream
+4: EXIT
+
+# CTRL-E: move the cursor to the end of the line
+5: MOVE_TO_END
+
+# BACKSPACE, CTRL-H: delete the previous character
+# 8 is the ASCII code for backspace and therefor
+# deleting the previous character
+8: DELETE_PREV_CHAR
+
+# TAB, CTRL-I: signal that console completion should be attempted
+9: COMPLETE
+
+# CTRL-J, CTRL-M: newline
+10: NEWLINE
+
+# CTRL-K: erase the current line
+11: KILL_LINE
+
+# ENTER: newline
+13: NEWLINE
+
+# CTRL-L: clear screen
+12: CLEAR_SCREEN
+
+# CTRL-N: scroll to the next element in the history buffer
+14: NEXT_HISTORY
+
+# CTRL-P: scroll to the previous element in the history buffer
+16: PREV_HISTORY
+
+# CTRL-R: redraw the current line
+18: REDISPLAY
+
+# CTRL-U: delete all the characters before the cursor position
+21: KILL_LINE_PREV
+
+# CTRL-V: paste the contents of the clipboard (useful for Windows terminal)
+22: PASTE
+
+# CTRL-W: delete the word directly before the cursor
+23: DELETE_PREV_WORD
+
+# DELETE, CTRL-?: delete the previous character
+# 127 is the ASCII code for delete
+127: DELETE_PREV_CHAR
diff --git a/src/src/main/resources/jline/keybindings.properties b/src/src/main/resources/jline/keybindings.properties
new file mode 100644 (file)
index 0000000..9585e3a
--- /dev/null
@@ -0,0 +1,68 @@
+# Keybinding mapping for JLine. The format is:
+#    [key code]: [logical operation]
+
+# CTRL-A: move to the beginning of the line
+1: MOVE_TO_BEG
+
+# CTRL-B: move to the previous character
+2: PREV_CHAR
+
+# CTRL-D: close out the input stream
+4: EXIT
+
+# CTRL-E: move the cursor to the end of the line
+5: MOVE_TO_END
+
+# CTRL-F: move to the next character
+6: NEXT_CHAR
+
+# CTRL-G: move to the previous word
+7: ABORT
+
+# BACKSPACE, CTRL-H: delete the previous character
+# 8 is the ASCII code for backspace and therefor
+# deleting the previous character
+8: DELETE_PREV_CHAR
+
+# TAB, CTRL-I: signal that console completion should be attempted
+9: COMPLETE
+
+# CTRL-J, CTRL-M: newline
+10: NEWLINE
+
+# CTRL-K: erase the current line
+11: KILL_LINE
+
+# CTRL-L: clear screen
+12: CLEAR_SCREEN
+
+# ENTER: newline
+13: NEWLINE
+
+# CTRL-N: scroll to the next element in the history buffer
+14: NEXT_HISTORY
+
+# CTRL-P: scroll to the previous element in the history buffer
+16: PREV_HISTORY
+
+# CTRL-R: redraw the current line
+18: SEARCH_PREV
+
+# CTRL-U: delete all the characters before the cursor position
+21: KILL_LINE_PREV
+
+# CTRL-V: paste the contents of the clipboard (useful for Windows terminal)
+22: PASTE
+
+# CTRL-W: delete the word directly before the cursor
+23: DELETE_PREV_WORD
+
+# CTRL-X: temporary location for PREV_WORD to make tests pass
+24: PREV_WORD
+
+# ESCAPE probably not intended this way, but it does the right thing for now 
+27: REDISPLAY
+
+# DELETE, CTRL-?: delete the next character
+# 127 is the ASCII code for delete
+127: DELETE_NEXT_CHAR
diff --git a/src/src/main/resources/jline/windowsbindings.properties b/src/src/main/resources/jline/windowsbindings.properties
new file mode 100644 (file)
index 0000000..d599c69
--- /dev/null
@@ -0,0 +1,68 @@
+# Keybinding mapping for JLine. The format is:
+#    [key code]: [logical operation]
+
+# CTRL-A: move to the beginning of the line
+1: MOVE_TO_BEG
+
+# CTRL-B: move to the previous character
+2: PREV_CHAR
+
+# CTRL-C: toggle overtype mode (frankly, I wasn't sure where to bind this)
+3: INSERT
+
+# CTRL-D: close out the input stream
+4: EXIT
+
+# CTRL-E: move the cursor to the end of the line
+5: MOVE_TO_END
+
+# CTRL-F: move to the next character
+6: NEXT_CHAR
+
+# CTRL-G: move to the previous word
+7: ABORT
+
+# CTRL-H: delete the previous character
+8: DELETE_PREV_CHAR
+
+# TAB, CTRL-I: signal that console completion should be attempted
+9: COMPLETE
+
+# CTRL-J, CTRL-M: newline
+10: NEWLINE
+
+# CTRL-K: Vertical tab - on windows we'll move to the start of the history
+11: START_OF_HISTORY
+
+# CTRL-L: Form feed - on windows, we'll move to the end of the history
+12: END_OF_HISTORY
+
+# ENTER: newline
+13: NEWLINE
+
+# CTRL-N: scroll to the next element in the history buffer
+14: NEXT_HISTORY
+
+# CTRL-P: scroll to the previous element in the history buffer
+16: PREV_HISTORY
+
+# CTRL-R: search backwards in history
+18: SEARCH_PREV
+
+# CTRL-U: delete all the characters before the cursor position
+21: KILL_LINE_PREV
+
+# CTRL-V: paste the contents of the clipboard (useful for Windows terminal)
+22: PASTE
+
+# CTRL-W: delete the word directly before the cursor
+23: DELETE_PREV_WORD
+
+# CTRL-X: temporary location for PREV_WORD to make tests pass
+24: PREV_WORD
+
+# CTRL-[: escape - clear the current line.
+27: CLEAR_LINE
+
+# CTRL-?: delete the previous character
+127: DELETE_NEXT_CHAR
diff --git a/src/src/site/apt/building.apt b/src/src/site/apt/building.apt
new file mode 100644 (file)
index 0000000..a091378
--- /dev/null
@@ -0,0 +1,39 @@
+ ------
+ jline
+ ------
+
+Building JLine
+
+ Building JLine requires an installation of {{{http://maven.apache.org/}Maven 2}}.
+
+ Source code is included in the JLine distribution, or can be checked out from CVS as described {{{source-repository.html}here}}.
+
+Maven Repository
+
+ If you are using Maven 2, you can add JLine as an automatic dependency by adding the following to your project's pom.xml file:
+
++--------------------------------+                        
+
+    <repositories>
+        ...
+        <repository>
+            <id>jline</id>
+            <name>JLine Project Repository</name>
+            <url>http://jline.sourceforge.net/m2repo</url>
+        </repository>
+    </repositories>
+    <dependencies>
+        ...
+        <dependency>
+            <groupId>jline</groupId>
+            <artifactId>jline</artifactId>
+            <version>0.9.9</version>
+        </dependency>
+    </dependencies>
+
++--------------------------------+                        
+
+
+
+
+
diff --git a/src/src/site/apt/downloads.apt b/src/src/site/apt/downloads.apt
new file mode 100644 (file)
index 0000000..de90db9
--- /dev/null
@@ -0,0 +1,39 @@
+ ------
+ jline
+ ------
+
+Download JLine
+
+ JLine packages can be downloaded from:
+
+ {{{http://sourceforge.net/project/showfiles.php?group_id=64033}http://sourceforge.net/project/showfiles.php?group_id=64033}}
+
+
+Maven Repository
+
+ If you are using Maven 2, you can add JLine as an automatic dependency by adding the following to your project's pom.xml file:
+
++--------------------------------+                        
+
+    <repositories>
+        ...
+        <repository>
+            <id>jline</id>
+            <name>JLine Project Repository</name>
+            <url>http://jline.sourceforge.net/m2repo</url>
+        </repository>
+    </repositories>
+    <dependencies>
+        ...
+        <dependency>
+            <groupId>jline</groupId>
+            <artifactId>jline</artifactId>
+            <version>0.9.9</version>
+        </dependency>
+    </dependencies>
+
++--------------------------------+                        
+
+
+
+
diff --git a/src/src/site/docbook/index.xml b/src/src/site/docbook/index.xml
new file mode 100644 (file)
index 0000000..c68b42f
--- /dev/null
@@ -0,0 +1,492 @@
+<!DOCTYPE book
+  PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+  "http://www.docbook.org/xml/4.1.2/docbookx.dtd">
+
+<book>
+       <bookinfo>
+               <title>
+                       JLine
+               </title>
+               <copyright>
+                       <year>2002, 2003,  2004, 2005, 2006, 2007</year>
+                       <holder>Marc Prud'hommeaux</holder>
+               </copyright>
+       </bookinfo>
+       <part id="manual">
+               <title>JLine Manual</title>
+
+               <chapter id="introduction">
+                       <title>Introduction</title>
+                       <para>
+                       JLine is a Java library for handling console input.
+                       It is similar in functionality to BSD editline and GNU
+                       readline. People familiar with the readline/editline
+                       capabilities for modern shells (such as bash and tcsh) will
+                       find most of the command editing features of JLine to
+                       be familiar.
+                       </para>
+               </chapter>
+
+               <chapter id="license">
+                       <title>License and Terms of Use</title>
+                       <para>
+                               JLine is distributed under the BSD license, meaning that
+                               you are completely free to redistribute, modify, or sell it
+                               with almost no restrictins.
+                               For more information on the BSD license, see
+                               <ulink url="http://www.opensource.org/licenses/bsd-license.php"
+                               >http://www.opensource.org/licenses/bsd-license.php</ulink>.
+                       </para>
+                       <para>
+                               For information on obtaining the software under another
+                               license, contact the copyright holder:
+                               <ulink url="mailto:mwp1@cornell.edu">mwp1@cornell.edu</ulink>.
+                       </para>
+               </chapter>
+
+               <chapter id="obtaining">
+                       <title>Obtaining JLine</title>
+                       <para>
+                               JLine is hosted on SourceForge, and is located at
+                               <ulink url="http://jline.sf.net">http://jline.sf.net</ulink>.
+                               The latest release can be downloaded from 
+                               <ulink url=
+                               "http://sourceforge.net/project/showfiles.php?group_id=64033">http://sourceforge.net/project/showfiles.php?group_id=64033</ulink>.
+                               API documentation can be found in the
+                <ulink url="apidocs">apidocs/</ulink> directory.
+                       </para>
+               </chapter>
+
+               <chapter id="installation">
+                       <title>Installation</title>
+                       <para>
+                               JLine has no library dependencies, aside from a JVM
+                               of version 1.2 or higher. To install JLine, download the
+                               <filename>jline.jar</filename> file, and either place it in
+                               the system-wide java extensions directory, or
+                               manually add it to your
+                               <computeroutput>CLASSPATH</computeroutput>.
+                               The extensions directory is dependent on your operating
+                               system. Some few examples are:
+                               <itemizedlist>
+                                       <listitem><para>
+                                       Macintosh OS X:
+                                       <filename>/Library/Java/Extensions</filename> or
+                                       <filename>/System/Library/Java/Extensions</filename>
+                                       </para></listitem>
+                                       <listitem><para>
+                                       Microsoft Windows:
+                                       <filename>JAVAHOME\jre\lib\ext</filename>
+                                       (example: 
+                                       <filename>C:\j2sdk1.4.1_03\jre\lib\ext</filename>)
+                                       </para></listitem>
+                                       <listitem><para>
+                                       UNIX Systems:
+                                       <filename>JAVAHOME/jre/lib/ext</filename>
+                                       (example: 
+                                       <filename>/usr/local/java/jre/lib/ext</filename>)
+                                       </para></listitem>
+                               </itemizedlist>
+                       </para>
+                       <para>
+                               JLine is not 100% pure Java. On Windows, it relies on a
+                               <filename>.dll</filename> file to initialize the terminal
+                               to be able to accept unbuffered input. However,
+                               no installation is necessary for this: when initialized,
+                               JLine will dynamically extract the DLL to a temporary
+                               directory and load it. For more details, see the
+                               documentation for the <classname>jline.WindowsTerminal</classname> class.
+                       </para>
+                       <para>
+                               On UNIX systems (including Macintosh OS X), JLine will
+                               execute the <filename>stty</filename> command to initialize
+                               the terminal to allow unbuffered input. For more details,
+                               see the documentation for the <classname>jline.UnixTerminal</classname> class.
+                       </para>
+                       <para>
+                               For both Windows and UNIX systems, JLine will fail to
+                               initialize if it is run inside a strict security manager
+                               that does not allow the loading of libraries, writing
+                               to the file system, or executing external programs. However,
+                               for most console applications, this is usually not the case.
+                       </para>
+               </chapter>
+               <chapter id="supported_platforms">
+                       <title>Supported Platforms</title>
+                       <para>
+                               JLine should work on any Windows system, or any
+                               minimally compliant POSIX system (includling Linux and
+                               Macintosh OS X).
+                       </para>
+                       <para>
+                               The platforms on which JLine has been confirmed to work are:
+                               <itemizedlist>
+                                       <listitem><para>
+                                               Microsoft Windows XP
+                                       </para></listitem>
+                                       <listitem><para>
+                                               RedHat Linux 9.0
+                                       </para></listitem>
+                                       <listitem><para>
+                                               Debian Linux 3.0
+                                       </para></listitem>
+                                       <listitem><para>
+                                               Macintosh OS X 10.3
+                                       </para></listitem>
+                               </itemizedlist>
+                       </para>
+                       <para>
+                               Please report successes or failures to the author:
+                               <ulink url="mailto:mwp1@cornell.edu">mwp1@cornell.edu</ulink>.
+                       </para>
+               </chapter>
+               <chapter id="features">
+                       <title>Features</title>
+                       <section id="features_history">
+                               <title>Command History</title>
+                               <para>
+                               </para>
+                       </section>
+                       <section id="features_completion">
+                               <title>Tab completion</title>
+                               <para>
+                               </para>
+                       </section>
+                       <section id="features_line_editing">
+                               <title>Line editing</title>
+                               <para>
+                               </para>
+                       </section>
+                       <section id="features_keybindings">
+                               <title>Custom Keybindings</title>
+                               <para>
+                               You can create your own keybindings by creating a
+                               <filename>HOME/.jlinebindings.properties"</filename>
+                               file. You can override the location of this file with
+                               the "<computeroutput>jline.keybindings</computeroutput>"
+                               system property.
+                               </para>
+                <!--
+                               <para>
+                               The default keybindings are as follows:
+                                       <programlisting>
+                                       </programlisting>
+                               </para>
+                -->
+                       </section>
+                       <section id="features_masking">
+                               <title>Character masking</title>
+                               <para>
+                               </para>
+                       </section>
+               </chapter>
+               <chapter id="api">
+                       <title>API</title>
+                       <para>
+                               This section discusses some common usages of the JLine API.
+                               For in-depth usage of the JLine API, see the
+                               <ulink url="apidocs">apidocs</ulink>.
+                       </para>
+                       <section id="reading_password">
+                               <title>Reading a password from the console</title>
+                               <para>
+                                       A common task that console applications need to do is
+                                       read in a password. While it is standard for software
+                                       to not echo password strings as they are typed,
+                                       the Java core APIs surprisingly do not provide any
+                                       means to do this.
+                               </para>
+                               <para>
+                                       JLine can read a password with the following code:
+                                       <programlisting>
+String password = new <classname>jline.ConsoleReader</classname>().readLine(new Character('*'));
+                                       </programlisting>
+                                       This will replace every character types on the console
+                                       with a star character.
+                               </para>
+                               <para>
+                                       Alternately, you can have it not echo password
+                                       character at all:
+                                       <programlisting>
+String password = new <classname>jline.ConsoleReader</classname>().readLine(new Character(0));
+                                       </programlisting>
+                               </para>
+                               <para>
+                                       The <filename>jline-demo.jar</filename> file contains
+                                       a sample application that reads the password. To run
+                                       the sample, execute:
+                                       <programlisting>
+java -cp jline-demo.jar jline.example.PasswordReader "*"
+                                       </programlisting>
+                               </para>
+                       </section>
+               </chapter>
+               <chapter id="faq">
+                       <title>Frequently Asked Questions</title>
+                       <section id="faq_unsupported_platform"><title>
+                               Can I disable JLine if it isn't working on my platform?
+                               </title>
+                               <para>
+                               You can disable JLine by setting the System property
+                               "<computeroutput>jline.terminal</computeroutput>"
+                               to
+                               "<classname>jline.UnsupportedTerminal</classname>". For example:
+                               <programlisting>
+java -Djline.terminal=jline.UnsupportedTerminal jline.example.Example simple
+                               </programlisting>
+                               </para>
+                       </section>
+                       <section id="faq_custom_keybindings"><title>
+                               How do I customize the key bindings?
+                               </title>
+                               <para>
+                               You can create your own keybindings by creating a
+                               <filename>HOME/.jlinebindings.properties"</filename>
+                               file. You can override the location of this file with
+                               the "<computeroutput>jline.keybindings</computeroutput>"
+                               system property. To examine the format to use, see the
+                               <filename>src/jline/keybindings.properties</filename>
+                               file in the source distribution.
+                               </para>
+                       </section>
+                       <section id="faq_jline_as_default"><title>
+                               Can I use JLine as the default console input stream for
+                               all applications?
+                               </title>
+                               <para>
+                               No, but you can use the <classname>jline.ConsoleRunner</classname> application
+                               to set up the system input stream and continue on
+                               the launch another program. For example, to use JLine
+                               as the input handler for the popular 
+                               <ulink url="http://www.beanshell.org">BeanShell</ulink>
+                               console application, you can run:
+                               <programlisting>
+java <classname>jline.ConsoleRunner</classname> <ulink url="http://www.beanshell.org/manual/standalonemode.html">bsh.Interpreter</ulink>
+                               </programlisting>
+                               </para>
+                       </section>
+                       <section id="faq_jline_beanshell"><title>
+                               Can I use JLine as the input handler for <ulink url="http://www.beanshell.org">BeanShell</ulink>?
+                               </title>
+                               <para>
+                               Yes. Try running:
+                               <programlisting>
+java <classname>jline.ConsoleRunner</classname> <ulink url="http://www.beanshell.org/manual/standalonemode.html">bsh.Interpreter</ulink>
+                               </programlisting>
+                               </para>
+                       </section>
+                       <section id="faq_jline_jdb"><title>
+                               Can I use JLine as the input handler for <ulink url="http://java.sun.com/j2se/1.3/docs/tooldocs/solaris/jdb.html">jdb</ulink> (the java debugger)?
+                               </title>
+                               <para>
+                               Yes. Try running:
+                               <programlisting>
+java <classname>jline.ConsoleRunner</classname> com.sun.tools.example.debug.tty.TTY <emphasis>args</emphasis>
+                               </programlisting>
+                               </para>
+                       </section>
+                       <section id="faq_pure_java"><title>
+                               Is JLine <trademark>100% pure Java</trademark>?
+                               </title>
+                               <para>
+                               No: JLine uses a couple small native methods in the Windows
+                               platform. On Unix, it is technically pure java, but relies
+                               on the execution of external (non-java) programs. See the
+                               <link linkend="installation">installation section</link>
+                               for more details.
+                               </para>
+                       </section>
+                       <section id="faq_password"><title>
+                               How do I make it so password characters are no echoed
+                               to the screen?
+                               </title>
+                               <para>
+                               See <link linkend="reading_password"/>.
+                               </para>
+                       </section>
+                       <section id="faq_cursrs"><title>
+                               Is JLine a full-featured curses implementation?
+                               </title>
+                               <para>
+                               No: JLine has no ability to position the cursor on the
+                               console. It might someday evolve into a plausible
+                               Java curses implementation.
+                               </para>
+                       </section>
+               </chapter>
+       </part>
+
+       <appendix id="known_bugs">
+               <title>Known Bugs</title>       
+               <itemizedlist>
+                       <listitem><para>
+                       Clearing the screen (CTRL-L) doesn't currently work on Windows.
+                       </para></listitem>
+               </itemizedlist>
+       </appendix>
+
+       <appendix id="contributors">
+               <title>Contributors</title>     
+        <para>
+        The following people have contributed to improving JLine over the
+        years:
+        </para>
+               <itemizedlist>
+                       <listitem><para>
+            Marc Prud'hommeaux
+                       </para></listitem>
+                       <listitem><para>
+            Damian Steer
+                       </para></listitem>
+                       <listitem><para>
+            Dale Kemp
+                       </para></listitem>
+                       <listitem><para>
+            Jun Liu
+                       </para></listitem>
+                       <listitem><para>
+            malcolm@epcc.ed.ac.uk
+                       </para></listitem>
+                       <listitem><para>
+            Simon Patarin
+                       </para></listitem>
+                       <listitem><para>
+            Amy Isard
+                       </para></listitem>
+                       <listitem><para>
+            Ryan Bell
+                       </para></listitem>
+                       <listitem><para>
+            Marc Herbert
+                       </para></listitem>
+                       <listitem><para>
+            Christian Salm
+                       </para></listitem>
+               </itemizedlist>
+       </appendix>
+
+       <appendix id="todo">
+               <title>Future enhancements</title>      
+               <itemizedlist>
+                       <listitem><para>
+                               Add localization for all strings.
+                       </para></listitem>
+                       <listitem><para>
+                               Create a BNFCompletor that can handle any BNF.
+                       </para></listitem>
+               </itemizedlist>
+       </appendix>
+
+       <appendix id="changelog">
+               <title>Change Log</title>
+               <itemizedlist>
+                       <title>0.9.93 2007-11-13</title>
+                       <listitem><para>
+                Fixed backspace handling on Unix/OS X.
+                       </para></listitem>
+               </itemizedlist>
+               <itemizedlist>
+                       <title>0.9.92 2007-10-30</title>
+                       <listitem><para>
+                JLine now works with 64-bit Windows systems.
+                       </para></listitem>
+               </itemizedlist>
+               <itemizedlist>
+                       <title>0.9.91 2007-03-11</title>
+                       <listitem><para>
+                Added ConsoleReader.setUsePagination() method which allows
+                configuration of pagination when the number of rows of
+                candidates exceeds the height of the detected terminal, thanks
+                to a patch by Damian Steer.
+                       </para></listitem>
+                       <listitem><para>
+                Better support for UTF-8 inputs (issue #1623521).
+                       </para></listitem>
+                       <listitem><para>
+                Improved list of supported keys on Windows (issue #1649790).
+                       </para></listitem>
+               </itemizedlist>
+               <itemizedlist>
+                       <title>0.9.5 2006-03-08</title>
+                       <listitem><para>
+                               Fixed problem with "stty" on Solaris, which doesn't
+                               understand "stty size" to query the terminal size. It now
+                               uses "stty -a", which supposedly outputs a POSIX standard 
+                               format.
+                       </para></listitem>
+                       <listitem><para>
+                               Support HOME and END keys, thanks to a patch by
+                               Dale Kemp.
+                       </para></listitem>
+               </itemizedlist>
+               <itemizedlist>
+                       <title>0.9.1 2005-01-29</title>
+                       <listitem><para>
+                               Fixed problem with the 0.9.0 distribution that
+                               failed to include the Windows jline.dll in the jline.jar,
+                               rendering it inoperable on Windows.
+                       </para></listitem>
+                       <listitem><para>
+                               Implemented proper interception or arrow keys on Windows,
+                               meaning that history can now be navigated with the UP
+                               and DOWN keys, and line editing can take place with
+                               the LEFT and RIGHT arrow keys.
+                       </para></listitem>
+               </itemizedlist>
+               <itemizedlist>
+                       <title>0.9.0 2005-01-23</title>
+                       <listitem><para>
+                               Changed license from GPL to BSD.
+                       </para></listitem>
+                       <listitem><para>
+                               Made "CTRL-L" map to clearing the screen.
+                       </para></listitem>
+               </itemizedlist>
+               <itemizedlist>
+                       <title>0.8.1 2003-11-18</title>
+                       <listitem><para>
+                               Fixed accidental dependency on JVM 1.4.
+                       </para></listitem>
+               </itemizedlist>
+               <itemizedlist>
+                       <title>0.8.0 2003-11-17</title>
+                       <listitem><para>
+                               Windows support using a native .dll
+                       </para></listitem>
+                       <listitem><para>
+                               A new ClassNameCompletor
+                       </para></listitem>
+                       <listitem><para>
+                               Many doc improvements
+                       </para></listitem>
+               </itemizedlist>
+               <itemizedlist>
+                       <title>0.6.0 2003-07-08</title>
+                       <listitem><para>
+                               Many bugfixes
+                       </para></listitem>
+                       <listitem><para>
+                               Better release system
+                       </para></listitem>
+                       <listitem><para>
+                               Automatically set terminal property by
+                               issuing stty on UNIX systems
+                       </para></listitem>
+                       <listitem><para>
+                               Additional tab-completion handlers
+                       </para></listitem>
+                       <listitem><para>
+                               Tested on Debian Linux and Mac OS 10.2
+                       </para></listitem>
+                       <listitem><para>
+                               Example includes dictionary, filename, and simple completion
+                       </para></listitem>
+               </itemizedlist>
+               <itemizedlist>
+                       <title>0.3.0 2002-10-05</title>
+                       <listitem><para>
+                               Initial release
+                       </para></listitem>
+               </itemizedlist>
+       </appendix>
+</book>
diff --git a/src/src/site/fml/faq.fml b/src/src/site/fml/faq.fml
new file mode 100644 (file)
index 0000000..b9902b0
--- /dev/null
@@ -0,0 +1,26 @@
+ <?xml version="1.0"?>
+<faqs title="JLine FAQ">
+  <part id="general">
+    <faq>
+      <question>How does JLine work?</question>
+      <answer>
+        <p>
+        On Windows, JLine uses a native .dll (which it automatically
+        extracts from the jline jar and loads at runtime) to access
+        native console features. On UNIX systems, JLine will perform
+        the necessary terminal setup by launching the "stty" command. 
+        </p>
+      </answer>
+    </faq>
+    <faq>
+      <question>What platforms has JLine been tested on?</question>
+      <answer>
+        <p>
+        Various flavors of Windows (95, 98, NT, XP, 2000), Mac OS X,
+        and multiple UNIX systems (Linux, Solaris, HPUX). 
+        </p>
+      </answer>
+    </faq>
+
+  </part>
+</faqs> 
diff --git a/src/src/site/resources/css/site.css b/src/src/site/resources/css/site.css
new file mode 100755 (executable)
index 0000000..771dc3f
--- /dev/null
@@ -0,0 +1,311 @@
+
+body {
+    min-width: 600px;
+    width: 600px;
+    width: auto !important;
+    background-color: #fff;
+    font-family: Verdana, sans;
+}
+
+body,div,span,td,p,h2,h3,h4,h5,h6,a,ul,li {
+    font-family: Verdana, sans;
+    font-size: 11px;
+    color: #5A5A5A;
+    font-style: normal;
+}
+
+a,a:hover,a:visited,a:active {
+    color: #5A5A5A;
+    /* text-decoration: underline; */
+}
+
+/* main layout */
+#banner {
+    color: #FFA500;
+    border: none;
+    margin: 0 0 0 0;
+    background-color: #fff;
+    background-image: url(../images/header.jpg);
+    background-position: right;
+    background-repeat: no-repeat;
+    height: 100px;
+}
+
+#bannerLeft img{
+    margin: 10px 0 0 10px;
+}
+
+#leftColumn {
+    background-color: transparent;
+    position: absolute;
+    top: 140px;
+    left: 20px;
+    width: 180px;
+    margin: 0px;
+    padding: 0px;
+    border: none;
+}
+
+#bodyColumn {
+    margin: 0 0 20px 220px;
+    background-color: #fff;
+    padding: 30px;
+    position: relative;
+    background-image: url(../images/dotted.png);
+    background-repeat: repeat-y;
+}
+
+#footer div.xright {
+    color: #fff;
+    margin-right: 10px;
+}
+
+/* end main layout */
+.deprecated {
+    text-decoration: line-through;
+}
+
+.comment {
+    color: green;
+}
+
+.source pre {
+    font-family: "Andale Mono", monospace;
+    font-size: 11px;
+    background-color: #ddd;
+    width: 100%;
+    color: #5A5A5A;
+    border-width: 0px;
+    padding-top: 6px;
+    padding-left: 3px;
+}
+
+#breadcrumbs {
+    background-color: #FE1100;
+    border: none;
+    height: 15px;
+}
+
+/*
+    workaround for bug in the Doxia docbook renderer that opens
+    anchors (e.g. <a name="index">), but doesn't ever close them
+*/
+#section a,a:hover,a:visited,a:active {
+    text-decoration: none;
+}
+
+
+#breadcrumbs a {
+    color: #fff;
+    margin-left: 20px;
+    text-decoration: none;
+}
+
+h1 {
+    border: none;
+    padding-left: 0;
+    font-weight: bold;
+    text-transform: capitalize;
+    background-color: #7FAABB !important;
+    color: #FFFFFF !important;
+    font-size: 19px !important;
+}
+
+h2 {
+    border: none;
+    padding-left: 0;
+    font-size: 13px;
+    font-weight: bold;
+    text-transform: capitalize;
+    background-color: #7FAABB !important;
+    color: #FFFFFF !important;
+    font-size: 17px !important;
+}
+
+h3 {
+    border: none;
+    font-weight: bolder;
+    padding-left: 0;
+    background-color: #8BBBD1 !important;
+    color: #FFFFFF !important;
+}
+
+#navcolumn {
+    padding: 0;
+    overflow: hidden;
+}
+
+#navcolumn ul {
+    margin: 0px 0 3px 0;
+    background-repeat: repeat-x;
+}
+
+#navcolumn h5 {
+    border: none;
+    background-image: url(../images/dotted.png);
+    background-repeat: repeat-x;
+    padding: 4px 0 3px 20px;
+    font-size: 11px !important;
+    margin-top: -1px;
+}
+
+#navcolumn ul {
+    margin-bottom: 8px;
+}
+
+#navcolumn li {
+    margin: 0px 0 0px 3px;
+    padding: 2px;
+    list-style-position: outside;
+    font-size: 7.5pt !important;
+    padding-left: 16px;
+    padding-left /**/: 2px !important;
+    /* warning, don't reformat, there should be no comment between padding-left and comment, to fix IE5 issues */
+}
+
+#menuDownloadable_docs li {
+    background-image: url(../images/ico_file_pdf.png);
+    padding-top: 3px;
+    padding-bottom: 1px;
+}
+
+#navcolumn strong {
+    color: #000000;
+    font-weight: bold;
+}
+
+#navcolumn strong a {
+    color: #000000;
+    font-weight: bold;
+}
+
+#navcolumn a {
+    padding-left: 14px;
+    text-decoration: underline;
+    padding-bottom: 2px;
+    color: #5a5a5a;
+}
+
+#navcolumn a img {
+    margin-top: 0;
+}
+
+#navcolumn a#poweredBy img {
+    margin: 0 0 15px 20px;
+    width: 90px;
+    height: 30px;
+}
+
+#navcolumn #lastPublished {
+    color: #999;
+    margin: 0 0 0 20px;
+}
+
+#navcolumn a:hover {
+    color: Olive;
+    padding-left: 14px;
+    text-decoration: underline;
+    padding-bottom: 2px;
+}
+
+#breadcrumbs div.xright,#breadcrumbs div.xleft {
+    color: #fff;
+    display: inline;
+    font-size: 7pt !important;
+}
+
+#banner a#projectLogo img {
+    float: left;
+    background-color: #fff !important;
+    margin: 20px 0 0 20px !important;
+}
+
+#navcolumn li {
+    color: #000000;
+}
+
+#navcolumn strong {
+    color: #000000;
+    font-weight: bold;
+    margin-left: 15px;
+}
+
+div.source {
+    background-color: #ddd;
+}
+
+div.source pre,code,td.code {
+    font-size: 8pt !important;
+    font-family: monospace;
+    margin: 0;
+}
+
+td.code {
+    font-size: 10pt !important;
+    font-family: monospace;
+}
+
+div#legend {
+    display: none;
+}
+
+table td.source {
+    border: none !important;
+}
+
+table td,table th {
+    font-size: 8pt !important;
+    font-family: verdana;
+}
+
+table th {
+    font-weight: bold;
+}
+
+.collapsed {
+    background-image: url(../images/collapsed.png) !important;
+}
+
+li.expanded {
+    background-image: url(../images/expanded.png) !important;
+}
+
+/*
+li.expanded ul {
+    margin-top: 5px !important;
+}
+*/
+
+a.externalLink,a.newWindow {
+    padding-right: 9px !important;
+    background-image: none !important; /*ie5*/
+}
+
+a.externalLink /* */ {
+    background-image: url(../images/external.png) !important;
+}
+
+a.newWindow /* */ {
+    background-image: url(../images/newwindow.png) !important;
+}
+
+table {
+    width: 68%; /* fix for ie 5.x */
+}
+
+i {
+    content: "\"/*"
+}
+
+table {
+    width: 100%;
+}
+/* remove banner: comment the following lines for the full layout */ /*
+#banner, #breadcrumbs {
+    display: none !important;
+}
+#leftColumn {
+    position: relative;
+    top: 0;
+}
+*/
diff --git a/src/src/site/resources/images/collapsed.png b/src/src/site/resources/images/collapsed.png
new file mode 100755 (executable)
index 0000000..a02c1e6
Binary files /dev/null and b/src/src/site/resources/images/collapsed.png differ
diff --git a/src/src/site/resources/images/dotted.png b/src/src/site/resources/images/dotted.png
new file mode 100755 (executable)
index 0000000..8a4d443
Binary files /dev/null and b/src/src/site/resources/images/dotted.png differ
diff --git a/src/src/site/resources/images/expanded.png b/src/src/site/resources/images/expanded.png
new file mode 100755 (executable)
index 0000000..8a19dbf
Binary files /dev/null and b/src/src/site/resources/images/expanded.png differ
diff --git a/src/src/site/resources/images/external.png b/src/src/site/resources/images/external.png
new file mode 100755 (executable)
index 0000000..19f2895
Binary files /dev/null and b/src/src/site/resources/images/external.png differ
diff --git a/src/src/site/resources/images/ico_file_pdf.png b/src/src/site/resources/images/ico_file_pdf.png
new file mode 100644 (file)
index 0000000..9ceb00f
Binary files /dev/null and b/src/src/site/resources/images/ico_file_pdf.png differ
diff --git a/src/src/site/resources/images/logo.jpg b/src/src/site/resources/images/logo.jpg
new file mode 100644 (file)
index 0000000..1f1da5c
Binary files /dev/null and b/src/src/site/resources/images/logo.jpg differ
diff --git a/src/src/site/resources/images/newwindow.png b/src/src/site/resources/images/newwindow.png
new file mode 100755 (executable)
index 0000000..1374c22
Binary files /dev/null and b/src/src/site/resources/images/newwindow.png differ
diff --git a/src/src/site/site.xml b/src/src/site/site.xml
new file mode 100644 (file)
index 0000000..274ec83
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="JLine">
+    <bannerLeft>
+        <name>JLine</name>
+        <src>images/logo.jpg</src>
+        <href>http://jline.sourceforge.net/</href>
+    </bannerLeft>     
+    <bannerRight>
+        <name>SourceForge</name>
+        <src>http://sourceforge.net/sflogo.php?group_id=64033</src>
+        <href>http://sourceforge.net/</href>
+    </bannerRight>     
+    <body>
+        <menu name="JLine">
+            <item name="Manual" href="index.html"/>
+            <item name="FAQs" href="faq.html"/>
+            <item name="Download" href="downloads.html"/>
+            <item name="Building" href="building.html"/>
+        </menu>
+
+        <menu name="Community">
+            <item name="Forums"
+                href="http://sourceforge.net/forum/?group_id=64033"/>
+            <item name="Issue Tracker"
+                href="http://sourceforge.net/tracker/?group_id=64033"/>
+            <item name="News"
+                href="http://sourceforge.net/news/?group_id=64033"/>
+        </menu>
+
+        <menu name="Project">
+            <item name="Javadocs"
+                href="apidocs/index.html"/>
+            <item name="Browse Source Code"
+                href="http://jline.cvs.sourceforge.net/jline/"/>
+        </menu>
+
+        ${reports}
+
+    </body>
+</project>
diff --git a/src/src/test/java/jline/ConsoleReaderTest.java b/src/src/test/java/jline/ConsoleReaderTest.java
new file mode 100644 (file)
index 0000000..4a25783
--- /dev/null
@@ -0,0 +1,162 @@
+package jline;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import junit.framework.TestCase;
+
+public class ConsoleReaderTest extends TestCase {
+
+    public ConsoleReaderTest(String name) {
+        super(name);
+    }
+
+    protected void setUp() throws Exception {
+        System.setProperty("jline.WindowsTerminal.directConsole", "false");
+    }
+
+    public void testDeleteAndBackspaceKeymappings() throws Exception {
+        // test only works on Windows
+        if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+            return;
+
+        ConsoleReader consoleReader = new ConsoleReader();
+        assertNotNull(consoleReader);
+        assertEquals(127, consoleReader
+                .getKeyForAction(ConsoleReader.DELETE_NEXT_CHAR));
+        assertEquals(8, consoleReader
+                .getKeyForAction(ConsoleReader.DELETE_PREV_CHAR));
+    }
+
+    public void testReadline() throws Exception {
+        ConsoleReader consoleReader = createConsole("Sample String\r\n"
+                .getBytes());
+        assertNotNull(consoleReader);
+        String line = consoleReader.readLine();
+        assertEquals("Sample String", line);
+
+    }
+
+    public void testDeleteOnWindowsTerminal() throws Exception {
+        // test only works on Windows
+        if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+            return;
+
+        char[] characters = new char[] { 'S', 's',
+                WindowsTerminal.SPECIAL_KEY_INDICATOR,
+                WindowsTerminal.LEFT_ARROW_KEY,
+                WindowsTerminal.SPECIAL_KEY_INDICATOR,
+                WindowsTerminal.DELETE_KEY, '\r', 'n' };
+        assertWindowsKeyBehavior("S", characters);
+    }
+
+    public void testNumpadDeleteOnWindowsTerminal() throws Exception {
+        // test only works on Windows
+        if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+            return;
+
+        char[] characters = new char[] { 'S', 's',
+                WindowsTerminal.NUMPAD_KEY_INDICATOR,
+                WindowsTerminal.LEFT_ARROW_KEY,
+                WindowsTerminal.NUMPAD_KEY_INDICATOR,
+                WindowsTerminal.DELETE_KEY, '\r', 'n' };
+        assertWindowsKeyBehavior("S", characters);
+    }
+
+    public void testHomeKeyOnWindowsTerminal() throws Exception {
+        // test only works on Windows
+        if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+            return;
+
+        char[] characters = new char[] { 'S', 's',
+                WindowsTerminal.SPECIAL_KEY_INDICATOR,
+                WindowsTerminal.HOME_KEY, 'x', '\r', '\n' };
+        assertWindowsKeyBehavior("xSs", characters);
+
+    }
+
+    public void testEndKeyOnWindowsTerminal() throws Exception {
+        // test only works on Windows
+        if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+            return;
+
+        char[] characters = new char[] { 'S', 's',
+                WindowsTerminal.SPECIAL_KEY_INDICATOR,
+                WindowsTerminal.HOME_KEY, 'x',
+                WindowsTerminal.SPECIAL_KEY_INDICATOR, WindowsTerminal.END_KEY,
+                'j', '\r', '\n' };
+        assertWindowsKeyBehavior("xSsj", characters);
+    }
+
+    public void testPageUpOnWindowsTerminal() throws Exception {
+        // test only works on Windows
+        if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+            return;
+
+        char[] characters = new char[] { WindowsTerminal.SPECIAL_KEY_INDICATOR,
+                WindowsTerminal.PAGE_UP_KEY, '\r', '\n' };
+        assertWindowsKeyBehavior("dir", characters);
+    }
+
+    public void testPageDownOnWindowsTerminal() throws Exception {
+        // test only works on Windows
+        if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+            return;
+
+        char[] characters = new char[] { WindowsTerminal.SPECIAL_KEY_INDICATOR,
+                WindowsTerminal.PAGE_DOWN_KEY, '\r', '\n' };
+        assertWindowsKeyBehavior("mkdir monkey", characters);
+    }
+
+    public void testEscapeOnWindowsTerminal() throws Exception {
+        // test only works on Windows
+        if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+            return;
+
+        char[] characters = new char[] { 's', 's', 's',
+                WindowsTerminal.SPECIAL_KEY_INDICATOR,
+                WindowsTerminal.ESCAPE_KEY, '\r', '\n' };
+        assertWindowsKeyBehavior("", characters);
+    }
+
+    public void testInsertOnWindowsTerminal() throws Exception {
+        // test only works on Windows
+        if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+            return;
+
+        char[] characters = new char[] { 'o', 'p', 's',
+                WindowsTerminal.SPECIAL_KEY_INDICATOR,
+                WindowsTerminal.HOME_KEY,
+                WindowsTerminal.SPECIAL_KEY_INDICATOR,
+                WindowsTerminal.INSERT_KEY, 'o', 'o', 'p', 's', '\r', '\n' };
+        assertWindowsKeyBehavior("oops", characters);
+    }
+
+    private void assertWindowsKeyBehavior(String expected, char[] input)
+            throws Exception {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(input);
+        ConsoleReader reader = createConsole(buffer.toString().getBytes());
+        assertNotNull(reader);
+        String line = reader.readLine();
+        assertEquals(expected, line);
+    }
+
+    private ConsoleReader createConsole(byte[] bytes) throws Exception {
+        InputStream in = new ByteArrayInputStream(bytes);
+        Writer writer = new StringWriter();
+        ConsoleReader reader = new ConsoleReader(in, writer);
+        reader.setHistory(createSeededHistory());
+        return reader;
+    }
+
+    private History createSeededHistory() {
+        History history = new History();
+        history.addToHistory("dir");
+        history.addToHistory("cd c:\\");
+        history.addToHistory("mkdir monkey");
+        return history;
+    }
+}
diff --git a/src/src/test/java/jline/JLineTestCase.java b/src/src/test/java/jline/JLineTestCase.java
new file mode 100644 (file)
index 0000000..92e09d4
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import junit.framework.*;
+
+import java.io.*;
+
+public abstract class JLineTestCase extends TestCase {
+    ConsoleReader console;
+
+    public JLineTestCase(String test) {
+        super(test);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+        console = new ConsoleReader(null, new PrintWriter(
+                new OutputStreamWriter(new ByteArrayOutputStream())), null,
+                new UnixTerminal());
+    }
+
+    public void assertBuffer(String expected, Buffer buffer) throws IOException {
+        assertBuffer(expected, buffer, true);
+    }
+
+    public void assertBuffer(String expected, Buffer buffer, boolean clear)
+            throws IOException {
+        // clear current buffer, if any
+        if (clear) {
+            console.finishBuffer();
+            console.getHistory().clear();
+        }
+
+        console.setInput(new ByteArrayInputStream(buffer.getBytes()));
+
+        // run it through the reader
+        while (console.readLine((String) null) != null) {
+            ;
+        }
+
+        assertEquals(expected, console.getCursorBuffer().toString());
+    }
+
+    private int getKeyForAction(short logicalAction) {
+        int action = console.getKeyForAction(logicalAction);
+
+        if (action == -1) {
+            fail("Keystroke for logical action " + logicalAction
+                    + " was not bound in the console");
+        }
+
+        return action;
+    }
+
+    /**
+     * TODO: Fix this so tests don't break on windows machines.
+     *
+     * @author Ryan
+     *
+     */
+    class Buffer {
+        private final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+
+        public Buffer() {
+        }
+
+        public Buffer(String str) {
+            append(str);
+        }
+
+        public byte[] getBytes() {
+            return bout.toByteArray();
+        }
+
+        public Buffer op(short operation) {
+            return append(getKeyForAction(operation));
+        }
+
+        public Buffer ctrlA() {
+            return append(getKeyForAction(ConsoleReader.MOVE_TO_BEG));
+        }
+
+        public Buffer ctrlU() {
+            return append(getKeyForAction(ConsoleReader.KILL_LINE_PREV));
+        }
+
+        public Buffer tab() {
+            return append(getKeyForAction(ConsoleReader.COMPLETE));
+        }
+
+        public Buffer back() {
+            return append(getKeyForAction(ConsoleReader.DELETE_PREV_CHAR));
+        }
+
+        public Buffer left() {
+            return append(UnixTerminal.ARROW_START).append(
+                    UnixTerminal.ARROW_PREFIX).append(UnixTerminal.ARROW_LEFT);
+        }
+
+        public Buffer right() {
+            return append(UnixTerminal.ARROW_START).append(
+                    UnixTerminal.ARROW_PREFIX).append(UnixTerminal.ARROW_RIGHT);
+        }
+
+        public Buffer up() {
+            return append(UnixTerminal.ARROW_START).append(
+                    UnixTerminal.ARROW_PREFIX).append(UnixTerminal.ARROW_UP);
+        }
+
+        public Buffer down() {
+            return append(UnixTerminal.ARROW_START).append(
+                    UnixTerminal.ARROW_PREFIX).append(UnixTerminal.ARROW_DOWN);
+        }
+
+        public Buffer append(String str) {
+            byte[] bytes = str.getBytes();
+
+            for (int i = 0; i < bytes.length; i++) {
+                append(bytes[i]);
+            }
+
+            return this;
+        }
+
+        public Buffer append(int i) {
+            return append((byte) i);
+        }
+
+        public Buffer append(byte b) {
+            bout.write(b);
+
+            return this;
+        }
+    }
+}
diff --git a/src/src/test/java/jline/TestCompletion.java b/src/src/test/java/jline/TestCompletion.java
new file mode 100644 (file)
index 0000000..a481e26
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ *  Tests command history.
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class TestCompletion extends JLineTestCase {
+    public TestCompletion(String test) {
+        super(test);
+    }
+
+    public void testSimpleCompletor() throws Exception {
+        // clear any current completors
+        for (Iterator i = console.getCompletors().iterator(); i.hasNext();
+                 console.removeCompletor((Completor) i.next())) {
+            ;
+        }
+
+        console.addCompletor
+            (new SimpleCompletor(new String[] { "foo", "bar", "baz" }));
+
+        assertBuffer("foo ", new Buffer("f").op(ConsoleReader.COMPLETE));
+        // single tab completes to unabbiguous "ba"
+        assertBuffer("ba", new Buffer("b").op(ConsoleReader.COMPLETE));
+        assertBuffer("ba", new Buffer("ba").op(ConsoleReader.COMPLETE));
+        assertBuffer("baz ", new Buffer("baz").op(ConsoleReader.COMPLETE));
+    }
+
+    public void testArgumentCompletor() throws Exception {
+        // clear any current completors
+        for (Iterator i = console.getCompletors().iterator(); i.hasNext();
+                 console.removeCompletor((Completor) i.next())) {
+            ;
+        }
+
+        console.addCompletor(new ArgumentCompletor
+            (new SimpleCompletor(new String[] { "foo", "bar", "baz" })));
+
+        assertBuffer("foo foo ", new Buffer("foo f").
+            op(ConsoleReader.COMPLETE));
+        assertBuffer("foo ba", new Buffer("foo b").
+            op(ConsoleReader.COMPLETE));
+        assertBuffer("foo ba", new Buffer("foo ba").
+            op(ConsoleReader.COMPLETE));
+        assertBuffer("foo baz ", new Buffer("foo baz").
+            op(ConsoleReader.COMPLETE));
+
+        // test completion in the mid range
+        assertBuffer("foo baz",
+            new Buffer("f baz").left().left().left().left().
+                op(ConsoleReader.COMPLETE));
+        assertBuffer("ba foo",
+            new Buffer("b foo").left().left().left().left().
+                op(ConsoleReader.COMPLETE));
+        assertBuffer("foo ba baz",
+            new Buffer("foo b baz").left().left().left().left().
+                op(ConsoleReader.COMPLETE));
+        assertBuffer("foo foo baz",
+            new Buffer("foo f baz").left().left().left().left().
+                op(ConsoleReader.COMPLETE));
+    }
+}
diff --git a/src/src/test/java/jline/TestEditLine.java b/src/src/test/java/jline/TestEditLine.java
new file mode 100644 (file)
index 0000000..88b4524
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+
+/**
+ *  Tests various features of editing lines.
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class TestEditLine extends JLineTestCase {
+    public TestEditLine(String test) {
+        super(test);
+    }
+
+    public void testDeletePreviousWord() throws Exception {
+        Buffer b = new Buffer("This is a test");
+
+        assertBuffer("This is a ", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+        assertBuffer("This is ", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+        assertBuffer("This ", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+        assertBuffer("", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+        assertBuffer("", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+        assertBuffer("", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+    }
+
+    public void testMoveToEnd() throws Exception {
+        Buffer b = new Buffer("This is a test");
+
+        assertBuffer("This is a XtestX",
+                     new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+                                                 .append('X')
+                                                 .op(ConsoleReader.MOVE_TO_END)
+                                                 .append('X'));
+
+        assertBuffer("This is Xa testX",
+                     new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .append('X')
+                                                 .op(ConsoleReader.MOVE_TO_END)
+                                                 .append('X'));
+
+        assertBuffer("This Xis a testX",
+                     new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .append('X')
+                                                 .op(ConsoleReader.MOVE_TO_END)
+                                                 .append('X'));
+    }
+
+    public void testPreviousWord() throws Exception {
+        assertBuffer("This is a Xtest",
+                     new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+                                                 .append('X'));
+        assertBuffer("This is Xa test",
+                     new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .append('X'));
+        assertBuffer("This Xis a test",
+                     new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .append('X'));
+        assertBuffer("XThis is a test",
+                     new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .append('X'));
+        assertBuffer("XThis is a test",
+                     new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .append('X'));
+        assertBuffer("XThis is a test",
+                     new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .op(ConsoleReader.PREV_WORD)
+                                                 .append('X'));
+    }
+
+    public void testLineStart() throws Exception {
+        assertBuffer("XThis is a test",
+                     new Buffer("This is a test").ctrlA().append('X'));
+        assertBuffer("TXhis is a test",
+                     new Buffer("This is a test").ctrlA().right().append('X'));
+    }
+
+    public void testClearLine() throws Exception {
+        assertBuffer("", new Buffer("This is a test").ctrlU());
+        assertBuffer("t", new Buffer("This is a test").left().ctrlU());
+        assertBuffer("st", new Buffer("This is a test").left().left().ctrlU());
+    }
+
+    public void testRight() throws Exception {
+        Buffer b = new Buffer("This is a test");
+        b = b.left().right().back();
+        assertBuffer("This is a tes", b);
+        b = b.left().left().left().right().left().back();
+        assertBuffer("This is ates", b);
+        b.append('X');
+        assertBuffer("This is aXtes", b);
+    }
+
+    public void testLeft() throws Exception {
+        Buffer b = new Buffer("This is a test");
+        b = b.left().left().left();
+        assertBuffer("This is a est", b = b.back());
+        assertBuffer("This is aest", b = b.back());
+        assertBuffer("This is est", b = b.back());
+        assertBuffer("This isest", b = b.back());
+        assertBuffer("This iest", b = b.back());
+        assertBuffer("This est", b = b.back());
+        assertBuffer("Thisest", b = b.back());
+        assertBuffer("Thiest", b = b.back());
+        assertBuffer("Thest", b = b.back());
+        assertBuffer("Test", b = b.back());
+        assertBuffer("est", b = b.back());
+        assertBuffer("est", b = b.back());
+        assertBuffer("est", b = b.back());
+        assertBuffer("est", b = b.back());
+        assertBuffer("est", b = b.back());
+    }
+
+    public void testBackspace() throws Exception {
+        Buffer b = new Buffer("This is a test");
+        assertBuffer("This is a tes", b = b.back());
+        assertBuffer("This is a te", b = b.back());
+        assertBuffer("This is a t", b = b.back());
+        assertBuffer("This is a ", b = b.back());
+        assertBuffer("This is a", b = b.back());
+        assertBuffer("This is ", b = b.back());
+        assertBuffer("This is", b = b.back());
+        assertBuffer("This i", b = b.back());
+        assertBuffer("This ", b = b.back());
+        assertBuffer("This", b = b.back());
+        assertBuffer("Thi", b = b.back());
+        assertBuffer("Th", b = b.back());
+        assertBuffer("T", b = b.back());
+        assertBuffer("", b = b.back());
+        assertBuffer("", b = b.back());
+        assertBuffer("", b = b.back());
+        assertBuffer("", b = b.back());
+        assertBuffer("", b = b.back());
+    }
+
+    public void testBuffer() throws Exception {
+        assertBuffer("This is a test", new Buffer("This is a test"));
+    }
+}
diff --git a/src/src/test/java/jline/TestHistory.java b/src/src/test/java/jline/TestHistory.java
new file mode 100644 (file)
index 0000000..32bebd7
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+
+/**
+ *  Tests command history.
+ *
+ *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class TestHistory extends JLineTestCase {
+    public TestHistory(String test) {
+        super(test);
+    }
+
+    public void testSingleHistory() throws Exception {
+        Buffer b = new Buffer().
+            append("test line 1").op(ConsoleReader.NEWLINE).
+            append("test line 2").op(ConsoleReader.NEWLINE).
+            append("test line 3").op(ConsoleReader.NEWLINE).
+            append("test line 4").op(ConsoleReader.NEWLINE).
+            append("test line 5").op(ConsoleReader.NEWLINE).
+            append("");
+
+        assertBuffer("", b);
+
+        assertBuffer("test line 5", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 5", b = b.op(ConsoleReader.PREV_CHAR));
+        assertBuffer("test line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 5", b = b.op(ConsoleReader.NEXT_HISTORY));
+        assertBuffer("test line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 3", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 2", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+
+        // beginning of history
+        assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+
+        assertBuffer("test line 2", b = b.op(ConsoleReader.NEXT_HISTORY));
+        assertBuffer("test line 3", b = b.op(ConsoleReader.NEXT_HISTORY));
+        assertBuffer("test line 4", b = b.op(ConsoleReader.NEXT_HISTORY));
+        assertBuffer("test line 5", b = b.op(ConsoleReader.NEXT_HISTORY));
+
+        // end of history
+        assertBuffer("", b = b.op(ConsoleReader.NEXT_HISTORY));
+        assertBuffer("", b = b.op(ConsoleReader.NEXT_HISTORY));
+        assertBuffer("", b = b.op(ConsoleReader.NEXT_HISTORY));
+
+        assertBuffer("test line 5", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+        b = b.op(ConsoleReader.MOVE_TO_BEG).append("XXX")
+             .op(ConsoleReader.NEWLINE);
+        assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 5", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("test line 5", b = b.op(ConsoleReader.NEXT_HISTORY));
+        assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEXT_HISTORY));
+        assertBuffer("", b = b.op(ConsoleReader.NEXT_HISTORY));
+
+        assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEWLINE).
+            op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEWLINE).
+            op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEWLINE).
+            op(ConsoleReader.PREV_HISTORY));
+        assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEWLINE).
+            op(ConsoleReader.PREV_HISTORY));
+    }
+}
diff --git a/src/src/test/java/jline/example/Example.java b/src/src/test/java/jline/example/Example.java
new file mode 100644 (file)
index 0000000..c019b29
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2002-2006, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline.example;
+
+import jline.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+public class Example {
+    public static void usage() {
+        System.out.println("Usage: java " + Example.class.getName()
+                + " [none/simple/files/dictionary [trigger mask]]");
+        System.out.println("  none - no completors");
+        System.out.println("  simple - a simple completor that comples "
+                + "\"foo\", \"bar\", and \"baz\"");
+        System.out
+                .println("  files - a completor that comples " + "file names");
+        System.out.println("  dictionary - a completor that comples "
+                + "english dictionary words");
+        System.out.println("  classes - a completor that comples "
+                + "java class names");
+        System.out
+                .println("  trigger - a special word which causes it to assume "
+                        + "the next line is a password");
+        System.out.println("  mask - is the character to print in place of "
+                + "the actual password character");
+        System.out.println("\n  E.g - java Example simple su '*'\n"
+                + "will use the simple compleator with 'su' triggering\n"
+                + "the use of '*' as a password mask.");
+    }
+
+    public static void main(String[] args) throws IOException {
+        Character mask = null;
+        String trigger = null;
+
+        ConsoleReader reader = new ConsoleReader();
+        reader.setBellEnabled(false);
+        reader.setDebug(new PrintWriter(new FileWriter("writer.debug", true)));
+
+        if ((args == null) || (args.length == 0)) {
+            usage();
+
+            return;
+        }
+
+        List completors = new LinkedList();
+
+        if (args.length > 0) {
+            if (args[0].equals("none")) {
+            } else if (args[0].equals("files")) {
+                completors.add(new FileNameCompletor());
+            } else if (args[0].equals("classes")) {
+                completors.add(new ClassNameCompletor());
+            } else if (args[0].equals("dictionary")) {
+                completors.add(new SimpleCompletor(new GZIPInputStream(
+                        Example.class.getResourceAsStream("english.gz"))));
+            } else if (args[0].equals("simple")) {
+                completors.add(new SimpleCompletor(new String[] { "foo", "bar",
+                        "baz" }));
+            } else {
+                usage();
+
+                return;
+            }
+        }
+
+        if (args.length == 3) {
+            mask = new Character(args[2].charAt(0));
+            trigger = args[1];
+        }
+
+        reader.addCompletor(new ArgumentCompletor(completors));
+
+        String line;
+        PrintWriter out = new PrintWriter(System.out);
+
+        while ((line = reader.readLine("prompt> ")) != null) {
+            out.println("======>\"" + line + "\"");
+            out.flush();
+
+            // If we input the special word then we will mask
+            // the next line.
+            if ((trigger != null) && (line.compareTo(trigger) == 0)) {
+                line = reader.readLine("password> ", mask);
+            }
+            if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) {
+                break;
+            }
+        }
+    }
+}
diff --git a/src/src/test/java/jline/example/PasswordReader.java b/src/src/test/java/jline/example/PasswordReader.java
new file mode 100644 (file)
index 0000000..1331855
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline.example;
+
+import jline.*;
+
+import java.io.*;
+
+public class PasswordReader {
+    public static void usage() {
+        System.out.println("Usage: java "
+            + PasswordReader.class.getName() + " [mask]");
+    }
+
+    public static void main(String[] args) throws IOException {
+        ConsoleReader reader = new ConsoleReader();
+
+        Character mask = (args.length == 0)
+            ? new Character((char) 0)
+            : new Character(args[0].charAt(0));
+
+        String line = null;
+        do {
+            line = reader.readLine("enter password> ", mask);
+            System.out.println("Got password: " + line);
+        } while(line != null && line.length() > 0);
+    }
+}
diff --git a/src/src/test/resources/jline/example/english.gz b/src/src/test/resources/jline/example/english.gz
new file mode 100644 (file)
index 0000000..f0a85c0
Binary files /dev/null and b/src/src/test/resources/jline/example/english.gz differ