summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2009-10-06 17:04:13 +0000
committerPaul Phillips <paulp@improving.org>2009-10-06 17:04:13 +0000
commitc107643d20f3c68051c0dd8dd12b2a3919909d20 (patch)
tree3627db8190153c8ad687333aa9b1f65448ff835d
parent43ce7fbc82f68f971ed3ddce3401e933f87686d2 (diff)
downloadscala-c107643d20f3c68051c0dd8dd12b2a3919909d20.tar.gz
scala-c107643d20f3c68051c0dd8dd12b2a3919909d20.tar.bz2
scala-c107643d20f3c68051c0dd8dd12b2a3919909d20.zip
Importing the jline sources, since it seems to ...
Importing the jline sources, since it seems to have been abandoned and we are slowly diverging. This checkin differs from the version in the CVS repo[*] only in having been generified by ricky clarkson. [*] See http://jline.sourceforge.net/source-repository.html
-rw-r--r--src/jline/LICENSE.txt33
-rw-r--r--src/jline/pom.xml221
-rw-r--r--src/jline/src/assembly/assembly.xml55
-rw-r--r--src/jline/src/main/java/jline/ANSIBuffer.java405
-rw-r--r--src/jline/src/main/java/jline/ArgumentCompletor.java439
-rw-r--r--src/jline/src/main/java/jline/CandidateCycleCompletionHandler.java28
-rw-r--r--src/jline/src/main/java/jline/CandidateListCompletionHandler.java189
-rw-r--r--src/jline/src/main/java/jline/ClassNameCompletor.java146
-rw-r--r--src/jline/src/main/java/jline/CompletionHandler.java20
-rw-r--r--src/jline/src/main/java/jline/Completor.java32
-rw-r--r--src/jline/src/main/java/jline/ConsoleOperations.java276
-rw-r--r--src/jline/src/main/java/jline/ConsoleReader.java1624
-rw-r--r--src/jline/src/main/java/jline/ConsoleReaderInputStream.java108
-rw-r--r--src/jline/src/main/java/jline/ConsoleRunner.java86
-rw-r--r--src/jline/src/main/java/jline/CursorBuffer.java104
-rw-r--r--src/jline/src/main/java/jline/FileNameCompletor.java133
-rw-r--r--src/jline/src/main/java/jline/History.java255
-rw-r--r--src/jline/src/main/java/jline/MultiCompletor.java83
-rw-r--r--src/jline/src/main/java/jline/NullCompletor.java27
-rw-r--r--src/jline/src/main/java/jline/SimpleCompletor.java194
-rw-r--r--src/jline/src/main/java/jline/Terminal.java180
-rw-r--r--src/jline/src/main/java/jline/UnixTerminal.java428
-rw-r--r--src/jline/src/main/java/jline/UnsupportedTerminal.java98
-rw-r--r--src/jline/src/main/java/jline/WindowsTerminal.java513
-rw-r--r--src/jline/src/main/java/jline/package.html9
-rw-r--r--src/jline/src/main/native/Makefile8
-rw-r--r--src/jline/src/main/native/jline_WindowsTerminal.c57
-rw-r--r--src/jline/src/main/native/jline_WindowsTerminal.h68
-rw-r--r--src/jline/src/main/resources/jline/CandidateListCompletionHandler.properties5
-rw-r--r--src/jline/src/main/resources/jline/jline32.dllbin0 -> 16069 bytes
-rw-r--r--src/jline/src/main/resources/jline/jline64.dllbin0 -> 48128 bytes
-rw-r--r--src/jline/src/main/resources/jline/keybindings.properties62
-rw-r--r--src/jline/src/main/resources/jline/windowsbindings.properties65
-rw-r--r--src/jline/src/site/apt/building.apt39
-rw-r--r--src/jline/src/site/apt/downloads.apt39
-rw-r--r--src/jline/src/site/docbook/index.xml492
-rw-r--r--src/jline/src/site/fml/faq.fml26
-rwxr-xr-xsrc/jline/src/site/resources/css/site.css311
-rwxr-xr-xsrc/jline/src/site/resources/images/collapsed.pngbin0 -> 222 bytes
-rwxr-xr-xsrc/jline/src/site/resources/images/dotted.pngbin0 -> 190 bytes
-rwxr-xr-xsrc/jline/src/site/resources/images/expanded.pngbin0 -> 198 bytes
-rwxr-xr-xsrc/jline/src/site/resources/images/external.pngbin0 -> 223 bytes
-rw-r--r--src/jline/src/site/resources/images/ico_file_pdf.pngbin0 -> 280 bytes
-rw-r--r--src/jline/src/site/resources/images/logo.jpgbin0 -> 4121 bytes
-rwxr-xr-xsrc/jline/src/site/resources/images/newwindow.pngbin0 -> 224 bytes
-rw-r--r--src/jline/src/site/site.xml40
-rw-r--r--src/jline/src/test/java/jline/ConsoleReaderTest.java162
-rw-r--r--src/jline/src/test/java/jline/JLineTestCase.java140
-rw-r--r--src/jline/src/test/java/jline/TestCompletion.java71
-rw-r--r--src/jline/src/test/java/jline/TestEditLine.java160
-rw-r--r--src/jline/src/test/java/jline/TestHistory.java76
-rw-r--r--src/jline/src/test/java/jline/example/Example.java97
-rw-r--r--src/jline/src/test/java/jline/example/PasswordReader.java32
-rw-r--r--src/jline/src/test/resources/jline/example/english.gzbin0 -> 130975 bytes
54 files changed, 7636 insertions, 0 deletions
diff --git a/src/jline/LICENSE.txt b/src/jline/LICENSE.txt
new file mode 100644
index 0000000000..1cdc44c211
--- /dev/null
+++ b/src/jline/LICENSE.txt
@@ -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/src/jline/pom.xml b/src/jline/pom.xml
new file mode 100644
index 0000000000..25356ff432
--- /dev/null
+++ b/src/jline/pom.xml
@@ -0,0 +1,221 @@
+<?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>0.9.95-SNAPSHOT</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>
+ </developers>
+ <licenses>
+ <license>
+ <name>BSD</name>
+ <url>LICENSE.txt</url>
+ </license>
+ </licenses>
+ <scm>
+ <connection>scm:cvs:pserver:anonymous@jline.cvs.sourceforge.net:/cvsroot/jline:jline</connection>
+ <developerConnection>scm:cvs:ext:${maven.username}@jline.cvs.sourceforge.net:/cvsroot/jline:jline</developerConnection>
+ <url>http://jline.cvs.sourceforge.net/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.codehaus.mojo</groupId>
+ <artifactId>jalopy-maven-plugin</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <configuration>
+ <fileFormat>UNIX</fileFormat>
+ <convention>codestyle.xml</convention>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>format</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <!-- <testFailureIgnore>true</testFailureIgnore> -->
+ <useFile>false</useFile>
+ <trimStackTrace>false</trimStackTrace>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ <showWarnings>true</showWarnings>
+ <compilerArgument>-Xlint:unchecked</compilerArgument>
+ </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>
+ </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>dependencies</report> -->
+ <!-- <report>cim</report> -->
+ <!-- <report>cobertura</report> -->
+ <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/jline/src/assembly/assembly.xml b/src/jline/src/assembly/assembly.xml
new file mode 100644
index 0000000000..216c697a53
--- /dev/null
+++ b/src/jline/src/assembly/assembly.xml
@@ -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/jline/src/main/java/jline/ANSIBuffer.java b/src/jline/src/main/java/jline/ANSIBuffer.java
new file mode 100644
index 0000000000..c2e33180bb
--- /dev/null
+++ b/src/jline/src/main/java/jline/ANSIBuffer.java
@@ -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/jline/src/main/java/jline/ArgumentCompletor.java b/src/jline/src/main/java/jline/ArgumentCompletor.java
new file mode 100644
index 0000000000..2cd572ce2f
--- /dev/null
+++ b/src/jline/src/main/java/jline/ArgumentCompletor.java
@@ -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<Completor> 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<String> 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<String> subCandidates = new LinkedList<String>();
+
+ 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<String> args = new LinkedList<String>();
+ 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/jline/src/main/java/jline/CandidateCycleCompletionHandler.java b/src/jline/src/main/java/jline/CandidateCycleCompletionHandler.java
new file mode 100644
index 0000000000..a0bf208cdc
--- /dev/null
+++ b/src/jline/src/main/java/jline/CandidateCycleCompletionHandler.java
@@ -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/jline/src/main/java/jline/CandidateListCompletionHandler.java b/src/jline/src/main/java/jline/CandidateListCompletionHandler.java
new file mode 100644
index 0000000000..17f03d5e68
--- /dev/null
+++ b/src/jline/src/main/java/jline/CandidateListCompletionHandler.java
@@ -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<String> 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<String> candidates, boolean eagerNewlines)
+ throws IOException {
+ Set<String> distinct = new HashSet<String>(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<String> copy = new ArrayList<String>();
+
+ for (Iterator<String> i = candidates.iterator(); i.hasNext();) {
+ String 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<String> 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/jline/src/main/java/jline/ClassNameCompletor.java b/src/jline/src/main/java/jline/ClassNameCompletor.java
new file mode 100644
index 0000000000..5c3ca87dca
--- /dev/null
+++ b/src/jline/src/main/java/jline/ClassNameCompletor.java
@@ -0,0 +1,146 @@
+/*
+ * 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<URL> urls = new HashSet<URL>();
+
+ 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<String> classes = new HashSet<String>();
+
+ for (Iterator i = urls.iterator(); i.hasNext();) {
+ URL url = (URL) i.next();
+ File file = new File(url.getFile());
+
+ if (file.isDirectory()) {
+ Set<String> files = getClassFiles(file.getAbsolutePath(),
+ new HashSet<String>(), file, new int[] { 200 });
+ classes.addAll(files);
+
+ continue;
+ }
+
+ if ((file == null) || !file.isFile()) // TODO: handle directories
+ {
+ 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<String> classNames = new TreeSet<String>();
+
+ for (Iterator<String> 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<String> getClassFiles(String root, Set<String> 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/jline/src/main/java/jline/CompletionHandler.java b/src/jline/src/main/java/jline/CompletionHandler.java
new file mode 100644
index 0000000000..5dffdcbaae
--- /dev/null
+++ b/src/jline/src/main/java/jline/CompletionHandler.java
@@ -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<String> candidates, int position)
+ throws IOException;
+}
diff --git a/src/jline/src/main/java/jline/Completor.java b/src/jline/src/main/java/jline/Completor.java
new file mode 100644
index 0000000000..ed1238a93d
--- /dev/null
+++ b/src/jline/src/main/java/jline/Completor.java
@@ -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<String> candidates);
+}
diff --git a/src/jline/src/main/java/jline/ConsoleOperations.java b/src/jline/src/main/java/jline/ConsoleOperations.java
new file mode 100644
index 0000000000..585ed401d9
--- /dev/null
+++ b/src/jline/src/main/java/jline/ConsoleOperations.java
@@ -0,0 +1,276 @@
+/*
+ * 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 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;
+
+}
diff --git a/src/jline/src/main/java/jline/ConsoleReader.java b/src/jline/src/main/java/jline/ConsoleReader.java
new file mode 100644
index 0000000000..6c7fff1aed
--- /dev/null
+++ b/src/jline/src/main/java/jline/ConsoleReader.java
@@ -0,0 +1,1624 @@
+/*
+ * 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<String, Short> KEYMAP_NAMES;
+
+ static {
+ Map<String, Short> names = new TreeMap<String, Short>();
+
+ 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));
+
+ KEYMAP_NAMES = new TreeMap<String, Short>(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<Completor> completors = new LinkedList<Completor>();
+
+ private Character echoCharacter = null;
+
+ private Map<Character, ActionListener> triggeredActions = new HashMap<Character, ActionListener>();
+
+
+ /**
+ * Adding a triggered Action allows to give another curse 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 Terminal.setupTerminal().getTerminalWidth();
+ }
+
+ /**
+ * Query the terminal to find the current width;
+ *
+ * @see Terminal#getTerminalHeight
+ *
+ * @return the height of the current terminal.
+ */
+ public int getTermheight() {
+ return Terminal.setupTerminal().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 ((prompt == null) ? 0 : prompt.length()) + buf.cursor;
+ }
+
+ 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);
+ }
+
+ while (true) {
+ int[] next = readBinding();
+
+ if (next == null) {
+ return null;
+ }
+
+ int c = next[0];
+ int code = next[1];
+
+ if (c == -1) {
+ return null;
+ }
+
+ boolean success = true;
+
+ switch (code) {
+ case EXIT: // ctrl-d
+
+ if (buf.buffer.length() == 0) {
+ return null;
+ }
+ 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 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 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
+ printString(((char) 27) + "[2J");
+ flushConsole();
+
+ // then send the ANSI code to go to position 1,1
+ printString(((char) 27) + "[1;1H");
+ flushConsole();
+
+ 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<String> candidates = new LinkedList<String>();
+ 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<Completor> 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.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); // 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);
+ char[] chars = buf.buffer.substring(buf.cursor).toCharArray();
+ if (mask != null)
+ Arrays.fill(chars, mask.charValue());
+
+ printCharacters(chars);
+
+ clearAhead(clear);
+ 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;
+ }
+
+ // 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 {
+ 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;
+
+ count = moveCursor(-1 * num) * -1;
+ // debug ("Deleting from " + buf.cursor + " for " + count);
+ buf.buffer.delete(buf.cursor, buf.cursor + count);
+ 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 {
+ if (moveCursor(1) == 0) {
+ return false;
+ }
+
+ while (moveCursor(1) != 0) {
+ ;
+ }
+
+ return true;
+ }
+
+ /**
+ * Delete the character at the current position and redraw the remainder of
+ * the buffer.
+ */
+ private final boolean deleteCurrentCharacter() throws IOException {
+ boolean success = buf.buffer.length() > 0;
+ if (!success) {
+ return false;
+ }
+
+ if (buf.cursor == buf.buffer.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 where
+ * if less than 0, move abs(<i>where</i>) to the left,
+ * otherwise move <i>where</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.
+ *
+ * @see #where
+ *
+ * @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;
+
+ 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);
+ }
+
+ /**
+ * 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;
+ }
+
+}
diff --git a/src/jline/src/main/java/jline/ConsoleReaderInputStream.java b/src/jline/src/main/java/jline/ConsoleReaderInputStream.java
new file mode 100644
index 0000000000..d2a14d8e72
--- /dev/null
+++ b/src/jline/src/main/java/jline/ConsoleReaderInputStream.java
@@ -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<InputStream> {
+ private final ConsoleReader reader;
+ private ConsoleLineInputStream next = null;
+ private ConsoleLineInputStream prev = null;
+
+ public ConsoleEnumeration(final ConsoleReader reader) {
+ this.reader = reader;
+ }
+
+ public InputStream 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/jline/src/main/java/jline/ConsoleRunner.java b/src/jline/src/main/java/jline/ConsoleRunner.java
new file mode 100644
index 0000000000..dac4b7c88a
--- /dev/null
+++ b/src/jline/src/main/java/jline/ConsoleRunner.java
@@ -0,0 +1,86 @@
+/*
+ * 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 {
+ public static final String property = "jline.history";
+
+ public static void main(final String[] args) throws Exception {
+ String historyFileName = null;
+
+ List<String> argList = new ArrayList<String>(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
+ ConsoleReader 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<Completor> completorList = new ArrayList<Completor>();
+
+ 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/jline/src/main/java/jline/CursorBuffer.java b/src/jline/src/main/java/jline/CursorBuffer.java
new file mode 100644
index 0000000000..3aec20af51
--- /dev/null
+++ b/src/jline/src/main/java/jline/CursorBuffer.java
@@ -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/jline/src/main/java/jline/FileNameCompletor.java b/src/jline/src/main/java/jline/FileNameCompletor.java
new file mode 100644
index 0000000000..d1d63735d1
--- /dev/null
+++ b/src/jline/src/main/java/jline/FileNameCompletor.java
@@ -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<String> 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<String> 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<String> 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/jline/src/main/java/jline/History.java b/src/jline/src/main/java/jline/History.java
new file mode 100644
index 0000000000..9e96f69146
--- /dev/null
+++ b/src/jline/src/main/java/jline/History.java
@@ -0,0 +1,255 @@
+/*
+ * 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<String> history = new ArrayList<String>();
+
+ 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<String> lines = new ArrayList<String>();
+ 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<String> 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;
+ }
+}
diff --git a/src/jline/src/main/java/jline/MultiCompletor.java b/src/jline/src/main/java/jline/MultiCompletor.java
new file mode 100644
index 0000000000..2d43a204cc
--- /dev/null
+++ b/src/jline/src/main/java/jline/MultiCompletor.java
@@ -0,0 +1,83 @@
+/*
+ * 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<Completor> 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<String> cand) {
+ int[] positions = new int[completors.length];
+ List<List<String>> copies = new ArrayList<List<String>>();
+ for (int i = 0; i < completors.length; i++) {
+ copies.add(null);
+ }
+
+ for (int i = 0; i < completors.length; i++) {
+ // clone and save the candidate list
+ copies.set(i, new LinkedList<String>(cand));
+ positions[i] = completors[i].complete(buffer, pos, copies.get(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.size(); i++) {
+ if (positions[i] == maxposition) {
+ cand.addAll(copies.get(i));
+ }
+ }
+
+ return maxposition;
+ }
+
+ public void setCompletors(final Completor[] completors) {
+ this.completors = completors;
+ }
+
+ public Completor[] getCompletors() {
+ return this.completors;
+ }
+}
diff --git a/src/jline/src/main/java/jline/NullCompletor.java b/src/jline/src/main/java/jline/NullCompletor.java
new file mode 100644
index 0000000000..aa6cdf744e
--- /dev/null
+++ b/src/jline/src/main/java/jline/NullCompletor.java
@@ -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/jline/src/main/java/jline/SimpleCompletor.java b/src/jline/src/main/java/jline/SimpleCompletor.java
new file mode 100644
index 0000000000..3581277409
--- /dev/null
+++ b/src/jline/src/main/java/jline/SimpleCompletor.java
@@ -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<String> 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<String> words = new LinkedList<String>();
+ 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<String> clist) {
+ String start = (buffer == null) ? "" : buffer;
+
+ SortedSet<String> 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<String> candidates) {
+ if (filter != null) {
+ TreeSet<String> filtered = new TreeSet<String>();
+
+ 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<String>(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/jline/src/main/java/jline/Terminal.java b/src/jline/src/main/java/jline/Terminal.java
new file mode 100644
index 0000000000..53a5aff4d4
--- /dev/null
+++ b/src/jline/src/main/java/jline/Terminal.java
@@ -0,0 +1,180 @@
+/*
+ * 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() {
+ return Terminal.class.getResourceAsStream("keybindings.properties");
+ }
+}
diff --git a/src/jline/src/main/java/jline/UnixTerminal.java b/src/jline/src/main/java/jline/UnixTerminal.java
new file mode 100644
index 0000000000..e9f76053ff
--- /dev/null
+++ b/src/jline/src/main/java/jline/UnixTerminal.java
@@ -0,0 +1,428 @@
+/*
+ * 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 Map terminfo;
+ private boolean echoEnabled;
+ private String ttyConfig;
+ 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(":|=");
+
+ if (ttyConfigSplit.length < 7)
+ return;
+
+ if (ttyConfigSplit[6] == null)
+ return;
+
+ backspaceDeleteSwitched = ttyConfigSplit[6].equals("7f");
+ }
+
+ /**
+ * 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 = '\b';
+ else if (c == '\b')
+ 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) {
+ //also the escape key is 27
+ //thats why we read until we
+ //have something different than 27
+ //this is a bugfix, because otherwise
+ //pressing escape and than an arrow key
+ //was an undefined state
+ 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 static int getTerminalProperty(String prop)
+ throws IOException, InterruptedException {
+ // 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;
+ String props = stty("-a");
+
+ for (StringTokenizer tok = new StringTokenizer(props, ";\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.
+ */
+ private 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;
+
+ in = p.getInputStream();
+
+ while ((c = in.read()) != -1) {
+ bout.write(c);
+ }
+
+ in = p.getErrorStream();
+
+ while ((c = in.read()) != -1) {
+ bout.write(c);
+ }
+
+ p.waitFor();
+
+ 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/jline/src/main/java/jline/UnsupportedTerminal.java b/src/jline/src/main/java/jline/UnsupportedTerminal.java
new file mode 100644
index 0000000000..2d87a18f6c
--- /dev/null
+++ b/src/jline/src/main/java/jline/UnsupportedTerminal.java
@@ -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/jline/src/main/java/jline/WindowsTerminal.java b/src/jline/src/main/java/jline/WindowsTerminal.java
new file mode 100644
index 0000000000..5d603cbcf0
--- /dev/null
+++ b/src/jline/src/main/java/jline/WindowsTerminal.java
@@ -0,0 +1,513 @@
+/*
+ * 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.disableDirectConsole</code> system property
+ * to <code>true</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 = getClass().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(getClass()
+ .getResourceAsStream(name + bits + ".dll"));
+
+ try {
+ OutputStream 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);
+ }
+
+ fout.close();
+ } 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;
+ }
+ }
+
+ // 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 getClass().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/jline/src/main/java/jline/package.html b/src/jline/src/main/java/jline/package.html
new file mode 100644
index 0000000000..c80743165a
--- /dev/null
+++ b/src/jline/src/main/java/jline/package.html
@@ -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/jline/src/main/native/Makefile b/src/jline/src/main/native/Makefile
new file mode 100644
index 0000000000..c620814825
--- /dev/null
+++ b/src/jline/src/main/native/Makefile
@@ -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/jline/src/main/native/jline_WindowsTerminal.c b/src/jline/src/main/native/jline_WindowsTerminal.c
new file mode 100644
index 0000000000..f020cae52b
--- /dev/null
+++ b/src/jline/src/main/native/jline_WindowsTerminal.c
@@ -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/jline/src/main/native/jline_WindowsTerminal.h b/src/jline/src/main/native/jline_WindowsTerminal.h
new file mode 100644
index 0000000000..5078b939cb
--- /dev/null
+++ b/src/jline/src/main/native/jline_WindowsTerminal.h
@@ -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/jline/src/main/resources/jline/CandidateListCompletionHandler.properties b/src/jline/src/main/resources/jline/CandidateListCompletionHandler.properties
new file mode 100644
index 0000000000..18ee221cc9
--- /dev/null
+++ b/src/jline/src/main/resources/jline/CandidateListCompletionHandler.properties
@@ -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/jline/src/main/resources/jline/jline32.dll b/src/jline/src/main/resources/jline/jline32.dll
new file mode 100644
index 0000000000..a0d3b117ce
--- /dev/null
+++ b/src/jline/src/main/resources/jline/jline32.dll
Binary files differ
diff --git a/src/jline/src/main/resources/jline/jline64.dll b/src/jline/src/main/resources/jline/jline64.dll
new file mode 100644
index 0000000000..922d6b01c5
--- /dev/null
+++ b/src/jline/src/main/resources/jline/jline64.dll
Binary files differ
diff --git a/src/jline/src/main/resources/jline/keybindings.properties b/src/jline/src/main/resources/jline/keybindings.properties
new file mode 100644
index 0000000000..6f13615d84
--- /dev/null
+++ b/src/jline/src/main/resources/jline/keybindings.properties
@@ -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/jline/src/main/resources/jline/windowsbindings.properties b/src/jline/src/main/resources/jline/windowsbindings.properties
new file mode 100644
index 0000000000..51ba4919c9
--- /dev/null
+++ b/src/jline/src/main/resources/jline/windowsbindings.properties
@@ -0,0 +1,65 @@
+# Keybinding mapping for JLine. The format is:
+# [key code]: [logical operation]
+
+# 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-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
+
+# 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
+
+# ENTER: newline
+13: NEWLINE
+
+# CTRL-L: Form feed - on windows, we'll move to the end of the history
+12: END_OF_HISTORY
+
+# 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
+
+# CTRL-[: escape - clear the current line.
+27: CLEAR_LINE
+
+# CTRL-?: delete the previous character
+127: DELETE_NEXT_CHAR
diff --git a/src/jline/src/site/apt/building.apt b/src/jline/src/site/apt/building.apt
new file mode 100644
index 0000000000..a09137873a
--- /dev/null
+++ b/src/jline/src/site/apt/building.apt
@@ -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/jline/src/site/apt/downloads.apt b/src/jline/src/site/apt/downloads.apt
new file mode 100644
index 0000000000..de90db9595
--- /dev/null
+++ b/src/jline/src/site/apt/downloads.apt
@@ -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/jline/src/site/docbook/index.xml b/src/jline/src/site/docbook/index.xml
new file mode 100644
index 0000000000..c68b42f1e9
--- /dev/null
+++ b/src/jline/src/site/docbook/index.xml
@@ -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/jline/src/site/fml/faq.fml b/src/jline/src/site/fml/faq.fml
new file mode 100644
index 0000000000..b9902b0fa7
--- /dev/null
+++ b/src/jline/src/site/fml/faq.fml
@@ -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/jline/src/site/resources/css/site.css b/src/jline/src/site/resources/css/site.css
new file mode 100755
index 0000000000..5d71608c15
--- /dev/null
+++ b/src/jline/src/site/resources/css/site.css
@@ -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/jline/src/site/resources/images/collapsed.png b/src/jline/src/site/resources/images/collapsed.png
new file mode 100755
index 0000000000..a02c1e67c9
--- /dev/null
+++ b/src/jline/src/site/resources/images/collapsed.png
Binary files differ
diff --git a/src/jline/src/site/resources/images/dotted.png b/src/jline/src/site/resources/images/dotted.png
new file mode 100755
index 0000000000..8a4d443a4a
--- /dev/null
+++ b/src/jline/src/site/resources/images/dotted.png
Binary files differ
diff --git a/src/jline/src/site/resources/images/expanded.png b/src/jline/src/site/resources/images/expanded.png
new file mode 100755
index 0000000000..8a19dbf103
--- /dev/null
+++ b/src/jline/src/site/resources/images/expanded.png
Binary files differ
diff --git a/src/jline/src/site/resources/images/external.png b/src/jline/src/site/resources/images/external.png
new file mode 100755
index 0000000000..19f28955ca
--- /dev/null
+++ b/src/jline/src/site/resources/images/external.png
Binary files differ
diff --git a/src/jline/src/site/resources/images/ico_file_pdf.png b/src/jline/src/site/resources/images/ico_file_pdf.png
new file mode 100644
index 0000000000..9ceb00f2dd
--- /dev/null
+++ b/src/jline/src/site/resources/images/ico_file_pdf.png
Binary files differ
diff --git a/src/jline/src/site/resources/images/logo.jpg b/src/jline/src/site/resources/images/logo.jpg
new file mode 100644
index 0000000000..1f1da5c8f2
--- /dev/null
+++ b/src/jline/src/site/resources/images/logo.jpg
Binary files differ
diff --git a/src/jline/src/site/resources/images/newwindow.png b/src/jline/src/site/resources/images/newwindow.png
new file mode 100755
index 0000000000..1374c228af
--- /dev/null
+++ b/src/jline/src/site/resources/images/newwindow.png
Binary files differ
diff --git a/src/jline/src/site/site.xml b/src/jline/src/site/site.xml
new file mode 100644
index 0000000000..274ec83ee4
--- /dev/null
+++ b/src/jline/src/site/site.xml
@@ -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/jline/src/test/java/jline/ConsoleReaderTest.java b/src/jline/src/test/java/jline/ConsoleReaderTest.java
new file mode 100644
index 0000000000..4a25783c58
--- /dev/null
+++ b/src/jline/src/test/java/jline/ConsoleReaderTest.java
@@ -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/jline/src/test/java/jline/JLineTestCase.java b/src/jline/src/test/java/jline/JLineTestCase.java
new file mode 100644
index 0000000000..92e09d485d
--- /dev/null
+++ b/src/jline/src/test/java/jline/JLineTestCase.java
@@ -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/jline/src/test/java/jline/TestCompletion.java b/src/jline/src/test/java/jline/TestCompletion.java
new file mode 100644
index 0000000000..a481e2625b
--- /dev/null
+++ b/src/jline/src/test/java/jline/TestCompletion.java
@@ -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/jline/src/test/java/jline/TestEditLine.java b/src/jline/src/test/java/jline/TestEditLine.java
new file mode 100644
index 0000000000..88b45240ed
--- /dev/null
+++ b/src/jline/src/test/java/jline/TestEditLine.java
@@ -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/jline/src/test/java/jline/TestHistory.java b/src/jline/src/test/java/jline/TestHistory.java
new file mode 100644
index 0000000000..a39afa5c19
--- /dev/null
+++ b/src/jline/src/test/java/jline/TestHistory.java
@@ -0,0 +1,76 @@
+/*
+ * 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 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/jline/src/test/java/jline/example/Example.java b/src/jline/src/test/java/jline/example/Example.java
new file mode 100644
index 0000000000..80a8d99e33
--- /dev/null
+++ b/src/jline/src/test/java/jline/example/Example.java
@@ -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<Completor> completors = new LinkedList<Completor>();
+
+ 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/jline/src/test/java/jline/example/PasswordReader.java b/src/jline/src/test/java/jline/example/PasswordReader.java
new file mode 100644
index 0000000000..133185540a
--- /dev/null
+++ b/src/jline/src/test/java/jline/example/PasswordReader.java
@@ -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/jline/src/test/resources/jline/example/english.gz b/src/jline/src/test/resources/jline/example/english.gz
new file mode 100644
index 0000000000..f0a85c08d9
--- /dev/null
+++ b/src/jline/src/test/resources/jline/example/english.gz
Binary files differ