diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fd6e97d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,27 @@
+# IDE
+.idea/
+.settings/
+
+# Keys and credentials
+.songe.key*
+.songe.trust*
+config.cfg
+
+# Build files
+release/
+build/
+out/
+
+# Database
+base/
+login-list.cfg
+
+# Other exec files
+lunasql.sh
+lunasql-srv.sh
+other/
+history
+
+# Temp files
+*~
+
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..99a22c0
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,520 @@
+
+ CeCILL FREE SOFTWARE LICENSE AGREEMENT
+
+Version 2.1 dated 2013-06-21
+
+
+ Notice
+
+This Agreement is a Free Software license agreement that is the result
+of discussions between its authors in order to ensure compliance with
+the two main principles guiding its drafting:
+
+ * firstly, compliance with the principles governing the distribution
+ of Free Software: access to source code, broad rights granted to users,
+ * secondly, the election of a governing law, French law, with which it
+ is conformant, both as regards the law of torts and intellectual
+ property law, and the protection that it offers to both authors and
+ holders of the economic rights over software.
+
+The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
+license are:
+
+Commissariat à l'énergie atomique et aux énergies alternatives - CEA, a
+public scientific, technical and industrial research establishment,
+having its principal place of business at 25 rue Leblanc, immeuble Le
+Ponant D, 75015 Paris, France.
+
+Centre National de la Recherche Scientifique - CNRS, a public scientific
+and technological establishment, having its principal place of business
+at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
+
+Institut National de Recherche en Informatique et en Automatique -
+Inria, a public scientific and technological establishment, having its
+principal place of business at Domaine de Voluceau, Rocquencourt, BP
+105, 78153 Le Chesnay cedex, France.
+
+
+ Preamble
+
+The purpose of this Free Software license agreement is to grant users
+the right to modify and redistribute the software governed by this
+license within the framework of an open source distribution model.
+
+The exercising of this right is conditional upon certain obligations for
+users so as to preserve this status for all subsequent redistributions.
+
+In consideration of access to the source code and the rights to copy,
+modify and redistribute granted by the license, users are provided only
+with a limited warranty and the software's author, the holder of the
+economic rights, and the successive licensors only have limited liability.
+
+In this respect, the risks associated with loading, using, modifying
+and/or developing or reproducing the software by the user are brought to
+the user's attention, given its Free Software status, which may make it
+complicated to use, with the result that its use is reserved for
+developers and experienced professionals having in-depth computer
+knowledge. Users are therefore encouraged to load and test the
+suitability of the software as regards their requirements in conditions
+enabling the security of their systems and/or data to be ensured and,
+more generally, to use and operate it in the same conditions of
+security. This Agreement may be freely reproduced and published,
+provided it is not altered, and that no provisions are either added or
+removed herefrom.
+
+This Agreement may apply to any or all software for which the holder of
+the economic rights decides to submit the use thereof to its provisions.
+
+Frequently asked questions can be found on the official website of the
+CeCILL licenses family (http://www.cecill.info/index.en.html) for any
+necessary clarification.
+
+
+ Article 1 - DEFINITIONS
+
+For the purpose of this Agreement, when the following expressions
+commence with a capital letter, they shall have the following meaning:
+
+Agreement: means this license agreement, and its possible subsequent
+versions and annexes.
+
+Software: means the software in its Object Code and/or Source Code form
+and, where applicable, its documentation, "as is" when the Licensee
+accepts the Agreement.
+
+Initial Software: means the Software in its Source Code and possibly its
+Object Code form and, where applicable, its documentation, "as is" when
+it is first distributed under the terms and conditions of the Agreement.
+
+Modified Software: means the Software modified by at least one
+Contribution.
+
+Source Code: means all the Software's instructions and program lines to
+which access is required so as to modify the Software.
+
+Object Code: means the binary files originating from the compilation of
+the Source Code.
+
+Holder: means the holder(s) of the economic rights over the Initial
+Software.
+
+Licensee: means the Software user(s) having accepted the Agreement.
+
+Contributor: means a Licensee having made at least one Contribution.
+
+Licensor: means the Holder, or any other individual or legal entity, who
+distributes the Software under the Agreement.
+
+Contribution: means any or all modifications, corrections, translations,
+adaptations and/or new functions integrated into the Software by any or
+all Contributors, as well as any or all Internal Modules.
+
+Module: means a set of sources files including their documentation that
+enables supplementary functions or services in addition to those offered
+by the Software.
+
+External Module: means any or all Modules, not derived from the
+Software, so that this Module and the Software run in separate address
+spaces, with one calling the other when they are run.
+
+Internal Module: means any or all Module, connected to the Software so
+that they both execute in the same address space.
+
+GNU GPL: means the GNU General Public License version 2 or any
+subsequent version, as published by the Free Software Foundation Inc.
+
+GNU Affero GPL: means the GNU Affero General Public License version 3 or
+any subsequent version, as published by the Free Software Foundation Inc.
+
+EUPL: means the European Union Public License version 1.1 or any
+subsequent version, as published by the European Commission.
+
+Parties: mean both the Licensee and the Licensor.
+
+These expressions may be used both in singular and plural form.
+
+
+ Article 2 - PURPOSE
+
+The purpose of the Agreement is the grant by the Licensor to the
+Licensee of a non-exclusive, transferable and worldwide license for the
+Software as set forth in Article 5 <#scope> hereinafter for the whole
+term of the protection granted by the rights over said Software.
+
+
+ Article 3 - ACCEPTANCE
+
+3.1 The Licensee shall be deemed as having accepted the terms and
+conditions of this Agreement upon the occurrence of the first of the
+following events:
+
+ * (i) loading the Software by any or all means, notably, by
+ downloading from a remote server, or by loading from a physical medium;
+ * (ii) the first time the Licensee exercises any of the rights granted
+ hereunder.
+
+3.2 One copy of the Agreement, containing a notice relating to the
+characteristics of the Software, to the limited warranty, and to the
+fact that its use is restricted to experienced users has been provided
+to the Licensee prior to its acceptance as set forth in Article 3.1
+<#accepting> hereinabove, and the Licensee hereby acknowledges that it
+has read and understood it.
+
+
+ Article 4 - EFFECTIVE DATE AND TERM
+
+
+ 4.1 EFFECTIVE DATE
+
+The Agreement shall become effective on the date when it is accepted by
+the Licensee as set forth in Article 3.1 <#accepting>.
+
+
+ 4.2 TERM
+
+The Agreement shall remain in force for the entire legal term of
+protection of the economic rights over the Software.
+
+
+ Article 5 - SCOPE OF RIGHTS GRANTED
+
+The Licensor hereby grants to the Licensee, who accepts, the following
+rights over the Software for any or all use, and for the term of the
+Agreement, on the basis of the terms and conditions set forth hereinafter.
+
+Besides, if the Licensor owns or comes to own one or more patents
+protecting all or part of the functions of the Software or of its
+components, the Licensor undertakes not to enforce the rights granted by
+these patents against successive Licensees using, exploiting or
+modifying the Software. If these patents are transferred, the Licensor
+undertakes to have the transferees subscribe to the obligations set
+forth in this paragraph.
+
+
+ 5.1 RIGHT OF USE
+
+The Licensee is authorized to use the Software, without any limitation
+as to its fields of application, with it being hereinafter specified
+that this comprises:
+
+ 1. permanent or temporary reproduction of all or part of the Software
+ by any or all means and in any or all form.
+
+ 2. loading, displaying, running, or storing the Software on any or all
+ medium.
+
+ 3. entitlement to observe, study or test its operation so as to
+ determine the ideas and principles behind any or all constituent
+ elements of said Software. This shall apply when the Licensee
+ carries out any or all loading, displaying, running, transmission or
+ storage operation as regards the Software, that it is entitled to
+ carry out hereunder.
+
+
+ 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
+
+The right to make Contributions includes the right to translate, adapt,
+arrange, or make any or all modifications to the Software, and the right
+to reproduce the resulting software.
+
+The Licensee is authorized to make any or all Contributions to the
+Software provided that it includes an explicit notice that it is the
+author of said Contribution and indicates the date of the creation thereof.
+
+
+ 5.3 RIGHT OF DISTRIBUTION
+
+In particular, the right of distribution includes the right to publish,
+transmit and communicate the Software to the general public on any or
+all medium, and by any or all means, and the right to market, either in
+consideration of a fee, or free of charge, one or more copies of the
+Software by any means.
+
+The Licensee is further authorized to distribute copies of the modified
+or unmodified Software to third parties according to the terms and
+conditions set forth hereinafter.
+
+
+ 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
+
+The Licensee is authorized to distribute true copies of the Software in
+Source Code or Object Code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+ 1. a copy of the Agreement,
+
+ 2. a notice relating to the limitation of both the Licensor's warranty
+ and liability as set forth in Articles 8 and 9,
+
+and that, in the event that only the Object Code of the Software is
+redistributed, the Licensee allows effective access to the full Source
+Code of the Software for a period of at least three years from the
+distribution of the Software, it being understood that the additional
+acquisition cost of the Source Code shall not exceed the cost of the
+data transfer.
+
+
+ 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
+
+When the Licensee makes a Contribution to the Software, the terms and
+conditions for the distribution of the resulting Modified Software
+become subject to all the provisions of this Agreement.
+
+The Licensee is authorized to distribute the Modified Software, in
+source code or object code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+ 1. a copy of the Agreement,
+
+ 2. a notice relating to the limitation of both the Licensor's warranty
+ and liability as set forth in Articles 8 and 9,
+
+and, in the event that only the object code of the Modified Software is
+redistributed,
+
+ 3. a note stating the conditions of effective access to the full source
+ code of the Modified Software for a period of at least three years
+ from the distribution of the Modified Software, it being understood
+ that the additional acquisition cost of the source code shall not
+ exceed the cost of the data transfer.
+
+
+ 5.3.3 DISTRIBUTION OF EXTERNAL MODULES
+
+When the Licensee has developed an External Module, the terms and
+conditions of this Agreement do not apply to said External Module, that
+may be distributed under a separate license agreement.
+
+
+ 5.3.4 COMPATIBILITY WITH OTHER LICENSES
+
+The Licensee can include a code that is subject to the provisions of one
+of the versions of the GNU GPL, GNU Affero GPL and/or EUPL in the
+Modified or unmodified Software, and distribute that entire code under
+the terms of the same version of the GNU GPL, GNU Affero GPL and/or EUPL.
+
+The Licensee can include the Modified or unmodified Software in a code
+that is subject to the provisions of one of the versions of the GNU GPL,
+GNU Affero GPL and/or EUPL and distribute that entire code under the
+terms of the same version of the GNU GPL, GNU Affero GPL and/or EUPL.
+
+
+ Article 6 - INTELLECTUAL PROPERTY
+
+
+ 6.1 OVER THE INITIAL SOFTWARE
+
+The Holder owns the economic rights over the Initial Software. Any or
+all use of the Initial Software is subject to compliance with the terms
+and conditions under which the Holder has elected to distribute its work
+and no one shall be entitled to modify the terms and conditions for the
+distribution of said Initial Software.
+
+The Holder undertakes that the Initial Software will remain ruled at
+least by this Agreement, for the duration set forth in Article 4.2 <#term>.
+
+
+ 6.2 OVER THE CONTRIBUTIONS
+
+The Licensee who develops a Contribution is the owner of the
+intellectual property rights over this Contribution as defined by
+applicable law.
+
+
+ 6.3 OVER THE EXTERNAL MODULES
+
+The Licensee who develops an External Module is the owner of the
+intellectual property rights over this External Module as defined by
+applicable law and is free to choose the type of agreement that shall
+govern its distribution.
+
+
+ 6.4 JOINT PROVISIONS
+
+The Licensee expressly undertakes:
+
+ 1. not to remove, or modify, in any manner, the intellectual property
+ notices attached to the Software;
+
+ 2. to reproduce said notices, in an identical manner, in the copies of
+ the Software modified or not.
+
+The Licensee undertakes not to directly or indirectly infringe the
+intellectual property rights on the Software of the Holder and/or
+Contributors, and to take, where applicable, vis-Ã -vis its staff, any
+and all measures required to ensure respect of said intellectual
+property rights of the Holder and/or Contributors.
+
+
+ Article 7 - RELATED SERVICES
+
+7.1 Under no circumstances shall the Agreement oblige the Licensor to
+provide technical assistance or maintenance services for the Software.
+
+However, the Licensor is entitled to offer this type of services. The
+terms and conditions of such technical assistance, and/or such
+maintenance, shall be set forth in a separate instrument. Only the
+Licensor offering said maintenance and/or technical assistance services
+shall incur liability therefor.
+
+7.2 Similarly, any Licensor is entitled to offer to its licensees, under
+its sole responsibility, a warranty, that shall only be binding upon
+itself, for the redistribution of the Software and/or the Modified
+Software, under terms and conditions that it is free to decide. Said
+warranty, and the financial terms and conditions of its application,
+shall be subject of a separate instrument executed between the Licensor
+and the Licensee.
+
+
+ Article 8 - LIABILITY
+
+8.1 Subject to the provisions of Article 8.2, the Licensee shall be
+entitled to claim compensation for any direct loss it may have suffered
+from the Software as a result of a fault on the part of the relevant
+Licensor, subject to providing evidence thereof.
+
+8.2 The Licensor's liability is limited to the commitments made under
+this Agreement and shall not be incurred as a result of in particular:
+(i) loss due the Licensee's total or partial failure to fulfill its
+obligations, (ii) direct or consequential loss that is suffered by the
+Licensee due to the use or performance of the Software, and (iii) more
+generally, any consequential loss. In particular the Parties expressly
+agree that any or all pecuniary or business loss (i.e. loss of data,
+loss of profits, operating loss, loss of customers or orders,
+opportunity cost, any disturbance to business activities) or any or all
+legal proceedings instituted against the Licensee by a third party,
+shall constitute consequential loss and shall not provide entitlement to
+any or all compensation from the Licensor.
+
+
+ Article 9 - WARRANTY
+
+9.1 The Licensee acknowledges that the scientific and technical
+state-of-the-art when the Software was distributed did not enable all
+possible uses to be tested and verified, nor for the presence of
+possible defects to be detected. In this respect, the Licensee's
+attention has been drawn to the risks associated with loading, using,
+modifying and/or developing and reproducing the Software which are
+reserved for experienced users.
+
+The Licensee shall be responsible for verifying, by any or all means,
+the suitability of the product for its requirements, its good working
+order, and for ensuring that it shall not cause damage to either persons
+or properties.
+
+9.2 The Licensor hereby represents, in good faith, that it is entitled
+to grant all the rights over the Software (including in particular the
+rights set forth in Article 5 <#scope>).
+
+9.3 The Licensee acknowledges that the Software is supplied "as is" by
+the Licensor without any other express or tacit warranty, other than
+that provided for in Article 9.2 <#good-faith> and, in particular,
+without any warranty as to its commercial value, its secured, safe,
+innovative or relevant nature.
+
+Specifically, the Licensor does not warrant that the Software is free
+from any error, that it will operate without interruption, that it will
+be compatible with the Licensee's own equipment and software
+configuration, nor that it will meet the Licensee's requirements.
+
+9.4 The Licensor does not either expressly or tacitly warrant that the
+Software does not infringe any third party intellectual property right
+relating to a patent, software or any other property right. Therefore,
+the Licensor disclaims any and all liability towards the Licensee
+arising out of any or all proceedings for infringement that may be
+instituted in respect of the use, modification and redistribution of the
+Software. Nevertheless, should such proceedings be instituted against
+the Licensee, the Licensor shall provide it with technical and legal
+expertise for its defense. Such technical and legal expertise shall be
+decided on a case-by-case basis between the relevant Licensor and the
+Licensee pursuant to a memorandum of understanding. The Licensor
+disclaims any and all liability as regards the Licensee's use of the
+name of the Software. No warranty is given as regards the existence of
+prior rights over the name of the Software or as regards the existence
+of a trademark.
+
+
+ Article 10 - TERMINATION
+
+10.1 In the event of a breach by the Licensee of its obligations
+hereunder, the Licensor may automatically terminate this Agreement
+thirty (30) days after notice has been sent to the Licensee and has
+remained ineffective.
+
+10.2 A Licensee whose Agreement is terminated shall no longer be
+authorized to use, modify or distribute the Software. However, any
+licenses that it may have granted prior to termination of the Agreement
+shall remain valid subject to their having been granted in compliance
+with the terms and conditions hereof.
+
+
+ Article 11 - MISCELLANEOUS
+
+
+ 11.1 EXCUSABLE EVENTS
+
+Neither Party shall be liable for any or all delay, or failure to
+perform the Agreement, that may be attributable to an event of force
+majeure, an act of God or an outside cause, such as defective
+functioning or interruptions of the electricity or telecommunications
+networks, network paralysis following a virus attack, intervention by
+government authorities, natural disasters, water damage, earthquakes,
+fire, explosions, strikes and labor unrest, war, etc.
+
+11.2 Any failure by either Party, on one or more occasions, to invoke
+one or more of the provisions hereof, shall under no circumstances be
+interpreted as being a waiver by the interested Party of its right to
+invoke said provision(s) subsequently.
+
+11.3 The Agreement cancels and replaces any or all previous agreements,
+whether written or oral, between the Parties and having the same
+purpose, and constitutes the entirety of the agreement between said
+Parties concerning said purpose. No supplement or modification to the
+terms and conditions hereof shall be effective as between the Parties
+unless it is made in writing and signed by their duly authorized
+representatives.
+
+11.4 In the event that one or more of the provisions hereof were to
+conflict with a current or future applicable act or legislative text,
+said act or legislative text shall prevail, and the Parties shall make
+the necessary amendments so as to comply with said act or legislative
+text. All other provisions shall remain effective. Similarly, invalidity
+of a provision of the Agreement, for any reason whatsoever, shall not
+cause the Agreement as a whole to be invalid.
+
+
+ 11.5 LANGUAGE
+
+The Agreement is drafted in both French and English and both versions
+are deemed authentic.
+
+
+ Article 12 - NEW VERSIONS OF THE AGREEMENT
+
+12.1 Any person is authorized to duplicate and distribute copies of this
+Agreement.
+
+12.2 So as to ensure coherence, the wording of this Agreement is
+protected and may only be modified by the authors of the License, who
+reserve the right to periodically publish updates or new versions of the
+Agreement, each with a separate number. These subsequent versions may
+address new issues encountered by Free Software.
+
+12.3 Any Software distributed under a given version of the Agreement may
+only be subsequently distributed under the same version of the Agreement
+or a subsequent version, subject to the provisions of Article 5.3.4
+<#compatibility>.
+
+
+ Article 13 - GOVERNING LAW AND JURISDICTION
+
+13.1 The Agreement is governed by French law. The Parties agree to
+endeavor to seek an amicable solution to any disagreements or disputes
+that may arise during the performance of the Agreement.
+
+13.2 Failing an amicable solution within two (2) months as from their
+occurrence, and unless emergency proceedings are necessary, the
+disagreements or disputes shall be referred to the Paris Courts having
+jurisdiction, by the more diligent Party.
+
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2aea5d3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,26 @@
+lunasql
+=======
+
+## Description
+
+AAnother naive but positive and fun Java JDBC SQL shell client. Designed to simply query databases by JDBC, or automate database tasks and prototype applications before real implementation.
+
+This is an old personal project that I used to develop occasionally, and that I just put on GitHub under an opensource license, but the development started in 2009! So please bear with me as to the quality of the code. It works, and it can be useful!
+
+## Features
+
+ * connects to any database by JDBC driver,
+ * sends SQL commands to database server,
+ * adds predefined useful commands and macros,
+ * is highly configurable by command line or file,
+ * accepts user-defined macros and command plugins,
+ * evaluates expressions by JSR-223,
+ * embeds a minimalistic graphical IDE,
+ * includes an HTTP server for remote querying,
+ * secures scripts execution by digital signature,
+ * provides useful javascript scripting libraries,
+ * sources recently released on GitHub,
+ * only one (small) jar file!
+
+Enjoy!
+
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..f2bc9a3
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,4 @@
+# Security Policy
+
+If you discover a security vulnerability, please contact me. Please do not post the vulnerability publicly, as it could be exploited by a hacker. I can also provide a PGP key. Thank you.
+
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..5eee811
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,1402 @@
+
+
+
+ * Copie de la commande PRINT pour test des greffons
+ *
+ * Utilisation :
+ * SQL> plugin echo CmdEchoPlugin
+ * SQL> echo ?a fonctionne
+ *
+ * @author M.P.
+ */
+public class CmdEchoPlugin extends Instruction {
+
+ public CmdEchoPlugin(Contexte cont) {
+ super(cont, TYPE_CMDPLG, "ECHO", null);
+ }
+
+ public int execute() {
+ if (cont.getVerbose() >= Contexte.VERB_AFF) cont.println(getSCommand(1));
+ cont.setVar(Contexte.ENV_CMD_STATE, Contexte.STATE_FALSE);
+ cont.setValeur(null); // ne retourne rien
+ return RET_CONTINUE;
+ }
+ public String getDesc(){
+ return " echo Affiche un message\n";
+ }
+ public String getHelp(){
+ return "ECHO msg : affiche le message msg";
+ }
+}// class
+
diff --git a/scripts/Calendar.bsh b/scripts/Calendar.bsh
new file mode 100644
index 0000000..bd20508
--- /dev/null
+++ b/scripts/Calendar.bsh
@@ -0,0 +1,116 @@
+/*
+ * MyCalendar.java a simple program to print the calendar of a month / year.
+ * This program gets two integers from user:
+ * - the index of the month (1-12)
+ * - the year number
+ *
+ * For example, the user would enter
+ * 7 2010
+ * for July 2010.
+ * Then, this program will print the calendar of that month.
+ *
+ * En LunaSQL:
+def calendar {
+ if $(arg_nb)==1 {
+ def month $(arg1)
+ def year $(_DAY_DATE_F&0-3)
+ } elseif $(arg_nb)==2 {
+ def month $(arg1)
+ def year $(arg2)
+ } else {
+ def month $(_DAY_DATE_F&4-5)
+ def year $(_DAY_DATE_F&0-3)
+ }
+ exec calendar.bsh $(month) $(year)
+ undef month year
+}
+ */
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+
+/*
+ * prints a calendar month based on month / year info
+ */
+private static void printCalendarMonthYear(int month, int year) {
+ // create a new GregorianCalendar object
+ Calendar cal = new GregorianCalendar();
+ // set its date to the first day of the month/year given by user
+ cal.clear();
+ cal.set(year, month - 1, 1);
+ // print calendar header
+ System.out.println("\n" + cal.getDisplayName(Calendar.MONTH, Calendar.LONG,
+ Locale.US) + " " + cal.get(Calendar.YEAR));
+ // obtain the weekday of the first day of month.
+ int firstWeekdayOfMonth = cal.get(Calendar.DAY_OF_WEEK);
+ // obtain the number of days in month.
+ int numberOfMonthDays = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+ // print anonymous calendar month based on the weekday of the first
+ // day of the month and the number of days in month:
+ printCalendar(numberOfMonthDays, firstWeekdayOfMonth);
+}
+
+/*
+ * prints an anonymous calendar month based on the weekday of the first
+ * day of the month and the number of days in month:
+ */
+private static void printCalendar(int numberOfMonthDays, int firstWeekdayOfMonth) {
+ // reset index of weekday
+ int weekdayIndex = 0;
+ // print calendar weekday header
+ System.out.println("Su Mo Tu We Th Fr Sa");
+ // leave/skip weekdays before the first day of month
+ for (int day = 1; day < firstWeekdayOfMonth; day++) {
+ System.out.print(" ");
+ weekdayIndex++;
+ }
+ // print the days of month in tabular format.
+ for (int day = 1; day <= numberOfMonthDays; day++) {
+ // print day
+ System.out.printf("%1$2d", day);
+ // next weekday
+ weekdayIndex++;
+ // if it is the last weekday
+ if (weekdayIndex == 7) {
+ // reset it
+ weekdayIndex = 0;
+ // and go to next line
+ System.out.println();
+ } else { // otherwise
+ // print space
+ System.out.print(" ");
+ }
+ }
+ // print a final new-line.
+ System.out.println();
+}
+
+/*
+ * The main program asks users for month and years,
+ * then it evaluates the weekday of the first day
+ * of that month as well as the number of days in that
+ * month.
+ */
+// represents the month (1-12)
+String monthText = scr_args.get(0);
+// represents the year
+String yearText = scr_args.get(1);
+try {
+ // convert month and year to integer.
+ // throws NumberFormatException if not convertible.
+ // It would be caught below:
+ int month = Integer.parseInt(monthText);
+ int year = Integer.parseInt(yearText);
+ // check if it is a valid month
+ if (month < 1 || month > 12)
+ throw new Exception("Invalid index for month: " + month);
+ // print the calendar for the given month/year.
+ printCalendarMonthYear(month, year);
+} catch (NumberFormatException e) {
+ // handles NumberFormatException
+ System.err.println("Number Format Error: " + e.getMessage());
+} catch (Exception e) {
+ // handles any other Exception
+ System.err.println(e.getMessage());
+}
diff --git a/scripts/creation.sql b/scripts/creation.sql
new file mode 100644
index 0000000..a430390
--- /dev/null
+++ b/scripts/creation.sql
@@ -0,0 +1,16 @@
+
+drop table commandes
+drop table rapports
+drop table produits
+drop table clients
+drop table typespro
+
+create table clients (id identity not null primary key, nom varchar)
+create table typespro (id identity not null primary key, lib varchar)
+create table produits (id identity not null primary key, lib varchar, prix real,
+ idtyp bigint not null references typespro(id))
+create table rapports (id identity not null primary key, montant real)
+ idpro bigint not null references produits(id),
+create table commandes (idpro bigint not null references produits(id),
+ idcli bigint not null references clients(id),
+ datecmd datetime not null)
diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..bb42e7d
--- /dev/null
+++ b/src/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: lunasql.Main
+
diff --git a/src/jline/ANSIBuffer.java b/src/jline/ANSIBuffer.java
new file mode 100755
index 0000000..1bbd4ef
--- /dev/null
+++ b/src/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.BufferedReader;
+import java.io.InputStreamReader;
+/**
+ * A buffer that can contain ANSI text.
+ *
+ * @author Marc Prud'hommeaux
+ */
+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:
+ *
+ * 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) + *+ */ + 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: + * + *
+ * 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 + *+ * + * 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: + *
+ * 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/ArgumentCompletor.java b/src/jline/ArgumentCompletor.java new file mode 100755 index 0000000..5493ad8 --- /dev/null +++ b/src/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 separator argument. This + * can be used instead of the individual completors having to + * know about argument parsing semantics. + *+ * Example 1: Any argument of the command line can + * use file completion. + *
+ *
+ * consoleReader.addCompletor (new ArgumentCompletor ( + * new {@link FileNameCompletor} ())) + *+ *+ * Example 2: 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. + *
+ *
+ * consoleReader.addCompletor (new ArgumentCompletor ( + * new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"}))); + * consoleReader.addCompletor (new ArgumentCompletor ( + * new {@link FileNameCompletor} ())); + *+ * + *+ * 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: + *
+ * + *+ * consoleReader.addCompletor (new ArgumentCompletor ( + * new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"}), + * new {@link SimpleCompletor} (new String [] { "xxx", "yyy", "xxx"}), + * new {@link NullCompletor} + * )); + *+ *+ * TODO: handle argument quoting and escape characters + *
+ * + * @author Marc Prud'hommeaux + */ +public class ArgumentCompletor implements Completor { + final Completor[] completors; + final ArgumentDelimiter delim; + boolean strict = true; + + /** + * Constuctor: create a new completor with the default + * argument separator of " ". + * + * @param completor the embedded completor + */ + public ArgumentCompletor(final Completor completor) { + this(new Completor[] { + completor + }); + } + + /** + * Constuctor: create a new completor with the default + * argument separator of " ". + * + * @param completors the List of completors to use + */ + public ArgumentCompletor(final List completors) { + this((Completor[]) completors.toArray(new Completor[completors.size()])); + } + + /** + * Constuctor: create a new completor with the default + * argument separator of " ". + * + * @param completors the embedded argument completors + */ + public ArgumentCompletor(final Completor[] completors) { + this(completors, new WhitespaceArgumentDelimiter()); + } + + /** + * Constuctor: create a new completor with the specified + * argument delimiter. + * + * @param completor the embedded completor + * @param delim the delimiter for parsing arguments + */ + public ArgumentCompletor(final Completor completor, + final ArgumentDelimiter delim) { + this(new Completor[] { + completor + }, delim); + } + + /** + * Constuctor: create a new completor with the specified + * argument delimiter. + * + * @param completors the embedded completors + * @param delim the delimiter for parsing arguments + */ + public ArgumentCompletor(final Completor[] completors, + final ArgumentDelimiter delim) { + this.completors = completors; + this.delim = delim; + } + + /** + * If true, a completion at argument index N will only succeed + * if all the completions from 0-(N-1) also succeed. + */ + public void setStrict(final boolean strict) { + this.strict = strict; + } + + /** + * Returns whether a completion at argument index N will succees + * if all the completions from arguments 0-(N-1) also succeed. + */ + public boolean getStrict() { + return this.strict; + } + + public int complete(final String buffer, final int cursor, + final List candidates) { + ArgumentList list = delim.delimit(buffer, cursor); + int argpos = list.getArgumentPosition(); + int argIndex = list.getCursorArgumentIndex(); + + if (argIndex < 0) { + return -1; + } + + final Completor comp; + + // if we are beyond the end of the completors, just use the last one + if (argIndex >= completors.length) { + comp = completors[completors.length - 1]; + } else { + comp = completors[argIndex]; + } + + // ensure that all the previous completors are successful before + // allowing this completor to pass (only if strict is true). + for (int i = 0; getStrict() && (i < argIndex); i++) { + Completor sub = + completors[(i >= completors.length) ? (completors.length - 1) : i]; + String[] args = list.getArguments(); + String arg = ((args == null) || (i >= args.length)) ? "" : args[i]; + + List subCandidates = new LinkedList(); + + if (sub.complete(arg, arg.length(), subCandidates) == -1) { + return -1; + } + + if (subCandidates.size() == 0) { + return -1; + } + } + + int ret = comp.complete(list.getCursorArgument(), argpos, candidates); + + if (ret == -1) { + return -1; + } + + int pos = ret + (list.getBufferPosition() - argpos); + + /** + * Special case: when completing in the middle of a line, and the + * area under the cursor is a delimiter, then trim any delimiters + * from the candidates, since we do not need to have an extra + * delimiter. + * + * E.g., if we have a completion for "foo", and we + * enter "f bar" into the buffer, and move to after the "f" + * and hit TAB, we want "foo bar" instead of "foo bar". + */ + if ((cursor != buffer.length()) && delim.isDelimiter(buffer, cursor)) { + for (int i = 0; i < candidates.size(); i++) { + String val = candidates.get(i).toString(); + + while ((val.length() > 0) + && delim.isDelimiter(val, val.length() - 1)) { + val = val.substring(0, val.length() - 1); + } + + candidates.set(i, val); + } + } + + ConsoleReader.debug("Completing " + buffer + "(pos=" + cursor + ") " + + "with: " + candidates + ": offset=" + pos); + + return pos; + } + + /** + * The {@link ArgumentCompletor.ArgumentDelimiter} allows custom + * breaking up of a {@link String} into individual arguments in + * order to dispatch the arguments to the nested {@link Completor}. + * + * @author Marc Prud'hommeaux + */ + 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 Marc Prud'hommeaux + */ + public abstract static class AbstractArgumentDelimiter + implements ArgumentDelimiter { + private char[] quoteChars = new char[] { '\'', '"' }; + private char[] escapeChars = new char[] { '\\' }; + + public void setQuoteChars(final char[] quoteChars) { + this.quoteChars = quoteChars; + } + + public char[] getQuoteChars() { + return this.quoteChars; + } + + public void setEscapeChars(final char[] escapeChars) { + this.escapeChars = escapeChars; + } + + public char[] getEscapeChars() { + return this.escapeChars; + } + + public ArgumentList delimit(final String buffer, final int cursor) { + List args = new LinkedList(); + StringBuffer arg = new StringBuffer(); + int argpos = -1; + int bindex = -1; + + for (int i = 0; (buffer != null) && (i <= buffer.length()); i++) { + // once we reach the cursor, set the + // position of the selected index + if (i == cursor) { + bindex = args.size(); + // the position in the current argument is just the + // length of the current argument + argpos = arg.length(); + } + + if ((i == buffer.length()) || isDelimiter(buffer, i)) { + if (arg.length() > 0) { + args.add(arg.toString()); + arg.setLength(0); // reset the arg + } + } else { + arg.append(buffer.charAt(i)); + } + } + + return new ArgumentList((String[]) args. + toArray(new String[args.size()]), bindex, argpos, cursor); + } + + /** + * Returns true if the specified character is a whitespace + * parameter. Check to ensure that the character is not + * escaped by any of + * {@link #getQuoteChars}, and is not escaped by ant of the + * {@link #getEscapeChars}, and returns true from + * {@link #isDelimiterChar}. + * + * @param buffer the complete command buffer + * @param pos the index of the character in the buffer + * @return true if the character should be a delimiter + */ + public boolean isDelimiter(final String buffer, final int pos) { + if (isQuoted(buffer, pos)) { + return false; + } + + if (isEscaped(buffer, pos)) { + return false; + } + + return isDelimiterChar(buffer, pos); + } + + public boolean isQuoted(final String buffer, final int pos) { + return false; + } + + public boolean isEscaped(final String buffer, final int pos) { + if (pos <= 0) { + return false; + } + + for (int i = 0; (escapeChars != null) && (i < escapeChars.length); + i++) { + if (buffer.charAt(pos) == escapeChars[i]) { + return !isEscaped(buffer, pos - 1); // escape escape + } + } + + return false; + } + + /** + * Returns true if the character at the specified position + * if a delimiter. This method will only be called if the + * character is not enclosed in any of the + * {@link #getQuoteChars}, and is not escaped by ant of the + * {@link #getEscapeChars}. To perform escaping manually, + * override {@link #isDelimiter} instead. + */ + public abstract boolean isDelimiterChar(String buffer, int pos); + } + + /** + * {@link ArgumentCompletor.ArgumentDelimiter} + * implementation that counts all + * whitespace (as reported by {@link Character#isWhitespace}) + * as being a delimiter. + * + * @author Marc Prud'hommeaux + */ + 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 Marc Prud'hommeaux + */ + 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/CandidateCycleCompletionHandler.java b/src/jline/CandidateCycleCompletionHandler.java new file mode 100755 index 0000000..a0bf208 --- /dev/null +++ b/src/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.*; + +/** + *+ * 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 + * editline + * library. + *
+ *This class is currently a stub; it does nothing
+ * @author Marc Prud'hommeaux + */ +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/CandidateListCompletionHandler.java b/src/jline/CandidateListCompletionHandler.java new file mode 100755 index 0000000..d9d43c2 --- /dev/null +++ b/src/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.*; + +/** + *+ * 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 + * readline + * library. + *
+ * + * TODO: + *
+ * Set the echo character. For example, to have "*" entered when a password + * is typed: + *
+ * + *+ * myConsoleReader.setEchoCharacter(new Character('*')); + *+ * + *
+ * Setting the character to + * + *
+ * null + *+ * + * will restore normal character echoing. Setting the character to + * + *
+ * new Character(0) + *+ * + * will cause nothing to be echoed. + * + * + * @param echoCharacter + * the character to echo to the console in place of the typed + * character. + */ + public void setEchoCharacter(final Character echoCharacter) { + this.echoCharacter = echoCharacter; + } + + /** + * Returns the echo character. + */ + public Character getEchoCharacter() { + return this.echoCharacter; + } + + /** + * No-op for exceptions we want to silently consume. + */ + private void consumeException(final Throwable e) { + } + + /** + * Checks to see if the specified character is a delimiter. We consider a + * character a delimiter if it is anything but a letter or digit. + * + * @param c + * the character to test + * @return true if it is a delimiter + */ + private boolean isDelimiter(char c) { + return !Character.isLetterOrDigit(c); + } + + private void printANSISequence(String sequence) throws IOException { + printCharacter(27); + printCharacter('['); + printString(sequence); + flushConsole(); + } + + /* + private int currentCol, currentRow; + + private void getCurrentPosition() { + // check for ByteArrayInputStream to disable for unit tests + if (terminal.isANSISupported() && !(in instanceof ByteArrayInputStream)) { + try { + printANSISequence("[6n"); + flushConsole(); + StringBuffer b = new StringBuffer(8); + // position is sent as
+ * A pass-through application that sets the system input stream to a + * {@link ConsoleReader} and invokes the specified main method. + *
+ * @author Marc Prud'hommeaux + */ +public class ConsoleRunner { + private static ConsoleReader reader; + + public static ConsoleReader getReader() { return reader; } + + public static final String property = "jline.history"; + + public static void main(final String[] args) throws Exception { + String historyFileName = null; + + List argList = new ArrayList(Arrays.asList(args)); + + if (argList.size() == 0) { + usage(); + + return; + } + + historyFileName = System.getProperty(ConsoleRunner.property, null); + + // invoke the main() method + String mainClass = (String) argList.remove(0); + + // setup the inpout stream + reader = new ConsoleReader(); + + if (historyFileName != null) { + reader.setHistory(new History (new File + (System.getProperty("user.home"), + ".jline-" + mainClass + + "." + historyFileName + ".history"))); + } else { + reader.setHistory(new History(new File + (System.getProperty("user.home"), + ".jline-" + mainClass + ".history"))); + } + + String completors = System.getProperty + (ConsoleRunner.class.getName() + ".completors", ""); + List completorList = new ArrayList(); + + for (StringTokenizer tok = new StringTokenizer(completors, ","); + tok.hasMoreTokens();) { + completorList.add + ((Completor) Class.forName(tok.nextToken()).newInstance()); + } + + if (completorList.size() > 0) { + reader.addCompletor(new ArgumentCompletor(completorList)); + } + + ConsoleReaderInputStream.setIn(reader); + + try { + Class.forName(mainClass). + getMethod("main", new Class[] { String[].class }). + invoke(null, new Object[] { argList.toArray(new String[0]) }); + } finally { + // just in case this main method is called from another program + ConsoleReaderInputStream.restoreIn(); + } + } + + private static void usage() { + System.out.println("Usage: \n java " + "[-Djline.history='name'] " + + ConsoleRunner.class.getName() + + "+ * This completor tries to behave as similar as possible to + * bash's file name completion (using GNU readline) + * with the following exceptions: + * + *
TODO
+ *+ * 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. + *
+ * + * @author Marc Prud'hommeaux + */ +public class MultiCompletor implements Completor { + Completor[] completors = new Completor[0]; + + /** + * Construct a MultiCompletor with no embedded completors. + */ + public MultiCompletor() { + this(new Completor[0]); + } + + /** + * Construct a MultiCompletor with the specified list of + * {@link Completor} instances. + */ + public MultiCompletor(final List completors) { + this((Completor[]) completors.toArray(new Completor[completors.size()])); + } + + /** + * Construct a MultiCompletor with the specified + * {@link Completor} instances. + */ + public MultiCompletor(final Completor[] completors) { + this.completors = completors; + } + + public int complete(final String buffer, final int pos, final List cand) { + int[] positions = new int[completors.length]; + List[] copies = new List[completors.length]; + + for (int i = 0; i < completors.length; i++) { + // clone and save the candidate list + copies[i] = new LinkedList(cand); + positions[i] = completors[i].complete(buffer, pos, copies[i]); + } + + int maxposition = -1; + + for (int i = 0; i < positions.length; i++) { + maxposition = Math.max(maxposition, positions[i]); + } + + // now we have the max cursor value: build up all the + // candidate lists that have the same cursor value + for (int i = 0; i < copies.length; i++) { + if (positions[i] == maxposition) { + cand.addAll(copies[i]); + } + } + + return maxposition; + } + + public void setCompletors(final Completor[] completors) { + this.completors = completors; + } + + public Completor[] getCompletors() { + return this.completors; + } +} diff --git a/src/jline/NullCompletor.java b/src/jline/NullCompletor.java new file mode 100755 index 0000000..aa6cdf7 --- /dev/null +++ b/src/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.*; + +/** + *+ * A completor that does nothing. Useful as the last item in an + * {@link ArgumentCompletor}. + *
+ * + * @author Marc Prud'hommeaux + */ +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/SimpleCompletor.java b/src/jline/SimpleCompletor.java new file mode 100755 index 0000000..2d488b6 --- /dev/null +++ b/src/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.*; + +/** + *+ * A simple {@link Completor} implementation that handles a pre-defined + * list of completion words. + *
+ * + *+ * Example usage: + *
+ *+ * myConsoleReader.addCompletor (new SimpleCompletor (new String [] { "now", "yesterday", "tomorrow" })); + *+ * + * @author Marc Prud'hommeaux + */ +public class SimpleCompletor implements Completor, Cloneable { + /** + * The list of candidates that will be completed. + */ + SortedSet candidates; + + /** + * A delimiter to use to qualify completions. + */ + String delimiter; + final SimpleCompletorFilter filter; + + /** + * Create a new SimpleCompletor with a single possible completion + * values. + */ + public SimpleCompletor(final String candidateString) { + this(new String[] { + candidateString + }); + } + + /** + * Create a new SimpleCompletor with a list of possible completion + * values. + */ + public SimpleCompletor(final String[] candidateStrings) { + this(candidateStrings, null); + } + + public SimpleCompletor(final String[] strings, + final SimpleCompletorFilter filter) { + this.filter = filter; + setCandidateStrings(strings); + } + + /** + * Complete candidates using the contents of the specified Reader. + */ + public SimpleCompletor(final Reader reader) throws IOException { + this(getStrings(reader)); + } + + /** + * Complete candidates using the whitespearated values in + * read from the specified Reader. + */ + public SimpleCompletor(final InputStream in) throws IOException { + this(getStrings(new InputStreamReader(in))); + } + + private static String[] getStrings(final Reader in) + throws IOException { + final Reader reader = + (in instanceof BufferedReader) ? in : new BufferedReader(in); + + List words = new LinkedList(); + String line; + + while ((line = ((BufferedReader) reader).readLine()) != null) { + for (StringTokenizer tok = new StringTokenizer(line); + tok.hasMoreTokens(); words.add(tok.nextToken())) { + ; + } + } + + return (String[]) words.toArray(new String[words.size()]); + } + + public int complete(final String buffer, final int cursor, final List clist) { + String start = (buffer == null) ? "" : buffer; + + SortedSet matches = candidates.tailSet(start); + + for (Iterator i = matches.iterator(); i.hasNext();) { + String can = (String) i.next(); + + if (!(can.startsWith(start))) { + break; + } + + if (delimiter != null) { + int index = can.indexOf(delimiter, cursor); + + if (index != -1) { + can = can.substring(0, index + 1); + } + } + + clist.add(can); + } + + if (clist.size() == 1) { + clist.set(0, ((String) clist.get(0)) + " "); + } + + // the index of the completion is always from the beginning of + // the buffer. + return (clist.size() == 0) ? (-1) : 0; + } + + public void setDelimiter(final String delimiter) { + this.delimiter = delimiter; + } + + public String getDelimiter() { + return this.delimiter; + } + + public void setCandidates(final SortedSet candidates) { + if (filter != null) { + TreeSet filtered = new TreeSet(); + + for (Iterator i = candidates.iterator(); i.hasNext();) { + String element = (String) i.next(); + element = filter.filter(element); + + if (element != null) { + filtered.add(element); + } + } + + this.candidates = filtered; + } else { + this.candidates = candidates; + } + } + + public SortedSet getCandidates() { + return Collections.unmodifiableSortedSet(this.candidates); + } + + public void setCandidateStrings(final String[] strings) { + setCandidates(new TreeSet(Arrays.asList(strings))); + } + + public void addCandidateString(final String candidateString) { + final String string = + (filter == null) ? candidateString : filter.filter(candidateString); + + if (string != null) { + candidates.add(string); + } + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + /** + * Filter for elements in the completor. + * + * @author Marc Prud'hommeaux + */ + 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/Terminal.java b/src/jline/Terminal.java new file mode 100755 index 0000000..6eecbe0 --- /dev/null +++ b/src/jline/Terminal.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ +package jline; + +import java.io.*; + +/** + * Representation of the input terminal for a platform. Handles + * any initialization that the platform may need to perform + * in order to allow the {@link ConsoleReader} to correctly handle + * input. + * + * @author Marc Prud'hommeaux + */ +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; + } + + /** + *
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.
+ * + *This class will use the Terminal implementation specified in the + * jline.terminal system property, or, if it is unset, by + * detecting the operating system from the os.name + * system property and instantiating either the + * {@link WindowsTerminalTest} or {@link UnixTerminal}. + * + * @see #initializeTerminal + */ + public static synchronized Terminal setupTerminal() { + if (term != null) { + return term; + } + + final Terminal t; + + String os = System.getProperty("os.name").toLowerCase(); + String termProp = System.getProperty("jline.terminal"); + + if ((termProp != null) && (termProp.length() > 0)) { + try { + t = (Terminal) Class.forName(termProp).newInstance(); + } catch (Exception e) { + throw (IllegalArgumentException) new IllegalArgumentException(e + .toString()).fillInStackTrace(); + } + } else if (os.indexOf("windows") != -1) { + t = new WindowsTerminal(); + } else { + t = new UnixTerminal(); + } + + try { + t.initializeTerminal(); + } catch (Exception e) { + e.printStackTrace(); + + return term = new UnsupportedTerminal(); + } + + return term = t; + } + + /** + * Returns true if the current console supports ANSI + * codes. + */ + public boolean isANSISupported() { + return true; + } + + /** + * Read a single character from the input stream. This might + * enable a terminal implementation to better handle nuances of + * the console. + */ + public int readCharacter(final InputStream in) throws IOException { + return in.read(); + } + + /** + * Reads a virtual key from the console. Typically, this will + * just be the raw character that was entered, but in some cases, + * multiple input keys will need to be translated into a single + * virtual key. + * + * @param in the InputStream to read from + * @return the virtual key (e.g., {@link ConsoleOperations#VK_UP}) + */ + public int readVirtualKey(InputStream in) throws IOException { + return readCharacter(in); + } + + /** + * Initialize any system settings + * that are required for the console to be able to handle + * input correctly, such as setting tabtop, buffered input, and + * character echo. + */ + public abstract void initializeTerminal() throws Exception; + + /** + * Returns the current width of the terminal (in characters) + */ + public abstract int getTerminalWidth(); + + /** + * Returns the current height of the terminal (in lines) + */ + public abstract int getTerminalHeight(); + + /** + * Returns true if this terminal is capable of initializing the + * terminal to use jline. + */ + public abstract boolean isSupported(); + + /** + * Returns true if the terminal will echo all characters type. + */ + public abstract boolean getEcho(); + + /** + * Invokes before the console reads a line with the prompt and mask. + */ + public void beforeReadLine(ConsoleReader reader, String prompt, + Character mask) { + } + + /** + * Invokes after the console reads a line with the prompt and mask. + */ + public void afterReadLine(ConsoleReader reader, String prompt, + Character mask) { + } + + /** + * Returns false if character echoing is disabled. + */ + public abstract boolean isEchoEnabled(); + + + /** + * Enable character echoing. This can be used to re-enable character + * if the ConsoleReader is no longer being used. + */ + public abstract void enableEcho(); + + + /** + * Disable character echoing. This can be used to manually re-enable + * character if the ConsoleReader has been disabled. + */ + public abstract void disableEcho(); + + public InputStream getDefaultBindings() { + // Mac bindings are slightly different from Unix/Linux. + // For instance, the Delete key behavior is different between them. + return Terminal.class.getResourceAsStream( + System.getProperty("os.name").toLowerCase().startsWith("mac") ? + "keybindings-mac.properties" : "keybindings.properties"); + } +} diff --git a/src/jline/UnixTerminal.java b/src/jline/UnixTerminal.java new file mode 100755 index 0000000..83f5194 --- /dev/null +++ b/src/jline/UnixTerminal.java @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ +package jline; + +import java.io.*; +import java.util.*; + +/** + *
+ * Terminal that is used for unix platforms. Terminal initialization + * is handled by issuing the stty command against the + * /dev/tty file to disable character echoing and enable + * character input. All known unix systems (including + * Linux and Macintosh OS X) support the stty), so this + * implementation should work for an reasonable POSIX system. + *
+ * + * @author Marc Prud'hommeaux + * @author Updates Dale Kemp 2005-12-03 + */ +public class UnixTerminal extends Terminal { + public static final short ARROW_START = 27; + public static final short ARROW_PREFIX = 91; + public static final short ARROW_LEFT = 68; + public static final short ARROW_RIGHT = 67; + public static final short ARROW_UP = 65; + public static final short ARROW_DOWN = 66; + public static final short O_PREFIX = 79; + public static final short HOME_CODE = 72; + public static final short END_CODE = 70; + + public static final short DEL_THIRD = 51; + public static final short DEL_SECOND = 126; + + private boolean echoEnabled; + private String ttyConfig; + private String ttyProps; + private long ttyPropsLastFetched; + private boolean backspaceDeleteSwitched = false; + private static String sttyCommand = + System.getProperty("jline.sttyCommand", "stty"); + + + String encoding = System.getProperty("input.encoding", "UTF-8"); + ReplayPrefixOneCharInputStream replayStream = new ReplayPrefixOneCharInputStream(encoding); + InputStreamReader replayReader; + + public UnixTerminal() { + try { + replayReader = new InputStreamReader(replayStream, encoding); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected void checkBackspace(){ + String[] ttyConfigSplit = ttyConfig.split(":|="); + backspaceDeleteSwitched = ttyConfigSplit.length >= 7 && "7f".equals(ttyConfigSplit[6]); + } + + /** + * Remove line-buffered input by invoking "stty -icanon min 1" + * against the current terminal. + */ + public void initializeTerminal() throws IOException, InterruptedException { + // save the initial tty configuration + ttyConfig = stty("-g"); + + // sanity check + if ((ttyConfig.length() == 0) + || ((ttyConfig.indexOf("=") == -1) + && (ttyConfig.indexOf(":") == -1))) { + throw new IOException("Unrecognized stty code: " + ttyConfig); + } + + checkBackspace(); + + // set the console to be character-buffered instead of line-buffered + stty("-icanon min 1"); + + // disable character echoing + stty("-echo"); + echoEnabled = false; + + // at exit, restore the original tty configuration (for JDK 1.3+) + try { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void start() { + try { + restoreTerminal(); + } catch (Exception e) { + consumeException(e); + } + } + }); + } catch (AbstractMethodError ame) { + // JDK 1.3+ only method. Bummer. + consumeException(ame); + } + } + + /** + * Restore the original terminal configuration, which can be used when + * shutting down the console reader. The ConsoleReader cannot be + * used after calling this method. + */ + public void restoreTerminal() throws Exception { + if (ttyConfig != null) { + stty(ttyConfig); + ttyConfig = null; + } + resetTerminal(); + } + + + + public int readVirtualKey(InputStream in) throws IOException { + int c = readCharacter(in); + + if (backspaceDeleteSwitched) + if (c == DELETE) + c = BACKSPACE; + else if (c == BACKSPACE) + c = DELETE; + + // in Unix terminals, arrow keys are represented by + // a sequence of 3 characters. E.g., the up arrow + // key yields 27, 91, 68 + if (c == ARROW_START && in.available() > 0) { + // Escape key is also 27, so we use InputStream.available() + // to distinguish those. If 27 represents an arrow, there + // should be two more chars immediately available. + while (c == ARROW_START) { + c = readCharacter(in); + } + if (c == ARROW_PREFIX || c == O_PREFIX) { + c = readCharacter(in); + if (c == ARROW_UP) { + return CTRL_P; + } else if (c == ARROW_DOWN) { + return CTRL_N; + } else if (c == ARROW_LEFT) { + return CTRL_B; + } else if (c == ARROW_RIGHT) { + return CTRL_F; + } else if (c == HOME_CODE) { + return CTRL_A; + } else if (c == END_CODE) { + return CTRL_E; + } else if (c == DEL_THIRD) { + c = readCharacter(in); // read 4th + return DELETE; + } + } + } + // handle unicode characters, thanks for a patch from amyi@inf.ed.ac.uk + if (c > 128) { + // handle unicode characters longer than 2 bytes, + // thanks to Marc.Herbert@continuent.com + replayStream.setInput(c, in); +// replayReader = new InputStreamReader(replayStream, encoding); + c = replayReader.read(); + + } + + return c; + } + + /** + * No-op for exceptions we want to silently consume. + */ + private void consumeException(Throwable e) { + } + + public boolean isSupported() { + return true; + } + + public boolean getEcho() { + return false; + } + + /** + * Returns the value of "stty size" width param. + * + * Note: 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. + * + * Note: this method caches the value from the + * first time it is called in order to increase speed, which means + * that changing to size of the terminal will not be reflected + * in the console. + */ + public int getTerminalHeight() { + int val = -1; + + try { + val = getTerminalProperty("rows"); + } catch (Exception e) { + } + + if (val == -1) { + val = 24; + } + + return val; + } + + private int getTerminalProperty(String prop) + throws IOException, InterruptedException { + // tty properties are cached so we don't have to worry too much about getting term widht/height + if (ttyProps == null || System.currentTimeMillis() - ttyPropsLastFetched > 1000) { + ttyProps = stty("-a"); + ttyPropsLastFetched = System.currentTimeMillis(); + } + // need to be able handle both output formats: + // speed 9600 baud; 24 rows; 140 columns; + // and: + // speed 38400 baud; rows = 49; columns = 111; ypixels = 0; xpixels = 0; + for (StringTokenizer tok = new StringTokenizer(ttyProps, ";\n"); + tok.hasMoreTokens();) { + String str = tok.nextToken().trim(); + + if (str.startsWith(prop)) { + int index = str.lastIndexOf(" "); + + return Integer.parseInt(str.substring(index).trim()); + } else if (str.endsWith(prop)) { + int index = str.indexOf(" "); + + return Integer.parseInt(str.substring(0, index).trim()); + } + } + + return -1; + } + + /** + * Execute the stty command with the specified arguments + * against the current active terminal. + */ + protected static String stty(final String args) + throws IOException, InterruptedException { + return exec("stty " + args + " < /dev/tty").trim(); + } + + /** + * Execute the specified command and return the output + * (both stdout and stderr). + */ + private static String exec(final String cmd) + throws IOException, InterruptedException { + return exec(new String[] { + "sh", + "-c", + cmd + }); + } + + /** + * Execute the specified command and return the output + * (both stdout and stderr). + */ + private static String exec(final String[] cmd) + throws IOException, InterruptedException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + Process p = Runtime.getRuntime().exec(cmd); + int c; + InputStream in = null; + InputStream err = null; + OutputStream out = null; + + try { + in = p.getInputStream(); + + while ((c = in.read()) != -1) { + bout.write(c); + } + + err = p.getErrorStream(); + + while ((c = err.read()) != -1) { + bout.write(c); + } + + out = p.getOutputStream(); + + p.waitFor(); + } finally { + try {in.close();} catch (Exception e) {} + try {err.close();} catch (Exception e) {} + try {out.close();} catch (Exception e) {} + } + + String result = new String(bout.toByteArray()); + + return result; + } + + /** + * The command to use to set the terminal options. Defaults + * to "stty", or the value of the system property "jline.sttyCommand". + */ + public static void setSttyCommand(String cmd) { + sttyCommand = cmd; + } + + /** + * The command to use to set the terminal options. Defaults + * to "stty", or the value of the system property "jline.sttyCommand". + */ + public static String getSttyCommand() { + return sttyCommand; + } + + public synchronized boolean isEchoEnabled() { + return echoEnabled; + } + + + public synchronized void enableEcho() { + try { + stty("echo"); + echoEnabled = true; + } catch (Exception e) { + consumeException(e); + } + } + + public synchronized void disableEcho() { + try { + stty("-echo"); + echoEnabled = false; + } catch (Exception e) { + consumeException(e); + } + } + + /** + * This is awkward and inefficient, but probably the minimal way to add + * UTF-8 support to JLine + * + * @author Marc Herbert + */ + 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/UnsupportedTerminal.java b/src/jline/UnsupportedTerminal.java new file mode 100755 index 0000000..2d87a18 --- /dev/null +++ b/src/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 Marc Prud'hommeaux + */ +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/WindowsTerminal.java b/src/jline/WindowsTerminal.java new file mode 100755 index 0000000..d2e72e9 --- /dev/null +++ b/src/jline/WindowsTerminal.java @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ +package jline; + +import java.io.*; + +//import jline.UnixTerminal.ReplayPrefixOneCharInputStream; + +/** + *+ * Terminal implementation for Microsoft Windows. Terminal initialization in + * {@link #initializeTerminal} is accomplished by extracting the + * jline_version.dll, saving it to the system temporary + * directoy (determined by the setting of the java.io.tmpdir System + * property), loading the library, and then calling the Win32 APIs SetConsoleMode and + * GetConsoleMode to + * disable character echoing. + *
+ * + *
+ * 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 jline.WindowsTerminal.directConsole
system property
+ * to false
.
+ *
+The core JLine API. The central class is +jline.ConsoleReader}, which +is a reader for obtaining input from an arbitrary +InputStream (usually System.in). +
+ + diff --git a/src/jline/windowsbindings.properties b/src/jline/windowsbindings.properties new file mode 100755 index 0000000..d599c69 --- /dev/null +++ b/src/jline/windowsbindings.properties @@ -0,0 +1,68 @@ +# Keybinding mapping for JLine. The format is: +# [key code]: [logical operation] + +# CTRL-A: move to the beginning of the line +1: MOVE_TO_BEG + +# CTRL-B: move to the previous character +2: PREV_CHAR + +# CTRL-C: toggle overtype mode (frankly, I wasn't sure where to bind this) +3: INSERT + +# CTRL-D: close out the input stream +4: EXIT + +# CTRL-E: move the cursor to the end of the line +5: MOVE_TO_END + +# CTRL-F: move to the next character +6: NEXT_CHAR + +# CTRL-G: move to the previous word +7: ABORT + +# CTRL-H: delete the previous character +8: DELETE_PREV_CHAR + +# TAB, CTRL-I: signal that console completion should be attempted +9: COMPLETE + +# CTRL-J, CTRL-M: newline +10: NEWLINE + +# CTRL-K: Vertical tab - on windows we'll move to the start of the history +11: START_OF_HISTORY + +# CTRL-L: Form feed - on windows, we'll move to the end of the history +12: END_OF_HISTORY + +# ENTER: newline +13: NEWLINE + +# CTRL-N: scroll to the next element in the history buffer +14: NEXT_HISTORY + +# CTRL-P: scroll to the previous element in the history buffer +16: PREV_HISTORY + +# CTRL-R: search backwards in history +18: SEARCH_PREV + +# CTRL-U: delete all the characters before the cursor position +21: KILL_LINE_PREV + +# CTRL-V: paste the contents of the clipboard (useful for Windows terminal) +22: PASTE + +# CTRL-W: delete the word directly before the cursor +23: DELETE_PREV_WORD + +# CTRL-X: temporary location for PREV_WORD to make tests pass +24: PREV_WORD + +# CTRL-[: escape - clear the current line. +27: CLEAR_LINE + +# CTRL-?: delete the previous character +127: DELETE_NEXT_CHAR diff --git a/src/joptsimple/AbstractOptionSpec.java b/src/joptsimple/AbstractOptionSpec.java new file mode 100755 index 0000000..cc90e6f --- /dev/null +++ b/src/joptsimple/AbstractOptionSpec.java @@ -0,0 +1,153 @@ +/* + The MIT License + + Copyright (c) 2004-2014 Paul R. Holser, Jr. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +package joptsimple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static java.util.Collections.*; + +import joptsimple.internal.Reflection; +import joptsimple.internal.ReflectionException; + +import static joptsimple.internal.Strings.*; + +/** + * @paramSpecification of an option that accepts an argument.
+ * + *Instances are returned from {@link OptionSpecBuilder} methods to allow the formation of parser directives as + * sentences in a "fluent interface" language. For example:
+ * + *
+ *
+ * OptionParser parser = new OptionParser();
+ * parser.accepts( "c" ).withRequiredArg().ofType( Integer.class );
+ *
+ *
+ *
+ * If no methods are invoked on an instance of this class, then that instance's option will treat its argument as + * a {@link String}.
+ * + * @paramSpecifies a type to which arguments of this spec's option are to be converted.
+ * + *JOpt Simple accepts types that have either:
+ * + *This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked + * before a one-{@link String}-arg constructor would.
+ * + *Invoking this method will trump any previous calls to this method or to + * {@link #withValuesConvertedBy(ValueConverter)}.
+ * + * @paramSpecifies a converter to use to translate arguments of this spec's option into Java objects. This is useful + * when converting to types that do not have the requisite factory method or constructor for + * {@link #ofType(Class)}.
+ * + *Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}.
+ *
+ * @param Specifies a description for the argument of the option that this spec represents. This description is used
+ * when generating help information about the parser. Specifies a value separator for the argument of the option that this spec represents. This allows a single
+ * option argument to represent multiple values for the option. For example: Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}. You cannot use Unicode U+0000 as the separator. Specifies a value separator for the argument of the option that this spec represents. This allows a single
+ * option argument to represent multiple values for the option. For example: Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}. You cannot use Unicode U+0000 in the separator. Wrapper for an array of command line arguments. A help formatter that allows configuration of overall row width and column separator width. The formatter produces a two-column output. The left column is for the options, and the right column for their
+ * descriptions. The formatter will allow as much space as possible for the descriptions, by minimizing the option
+ * column's width, no greater than slightly less than half the overall desired width. Represents objects charged with taking a set of option descriptions and producing some help text from them. Specification of a command line's non-option arguments. Instances are returned from {@link OptionParser} methods to allow the formation of parser directives as
+ * sentences in a "fluent interface" language. For example: If no methods are invoked on an instance of this class, then that instance's option will treat the non-option
+ * arguments as {@link String}s. Specifies a type to which the non-option arguments are to be converted. JOpt Simple accepts types that have either: This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked
+ * before a one-{@link String}-arg constructor would. Invoking this method will trump any previous calls to this method or to
+ * {@link #withValuesConvertedBy(ValueConverter)}. Specifies a converter to use to translate non-option arguments into Java objects. This is useful
+ * when converting to types that do not have the requisite factory method or constructor for
+ * {@link #ofType(Class)}. Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}.
+ *
+ * @param Specifies a description for the non-option arguments that this spec represents. This description is used
+ * when generating help information about the parser. This method returns an instance of {@link OptionSpecBuilder} to allow the formation of parser directives
+ * as sentences in a fluent interface language. For example: If no methods are invoked on the returned {@link OptionSpecBuilder}, then the parser treats the option as
+ * accepting no argument. Tells the parser to treat unrecognized options as non-option arguments. If not called, then the parser raises an {@link OptionException} when it encounters an unrecognized
+ * option. Parses command line arguments, using a syntax that attempts to take from the best of POSIX {@code getopt()}
+ * and GNU {@code getopt_long()}. This parser supports short options and long options. There are two ways to tell the parser what options to recognize: Each of the options in a list of options given to {@link #acceptsAll(Collection) acceptsAll} is treated as a
+ * synonym of the others. For example:
+ * By default, as with GNU {@code getopt()}, the parser allows intermixing of options and non-options. If, however,
+ * the parser has been created to be "POSIX-ly correct", then the first argument that does not look lexically like an
+ * option, and is not a required argument of a preceding option, signals the end of options. You can still bind
+ * optional arguments to their options using the abutting (for short options) or = syntax. Unlike GNU {@code getopt()}, this parser does not honor the environment variable {@code POSIXLY_CORRECT}.
+ * "POSIX-ly correct" parsers are configured by either: This method recognizes only instances of options returned from the fluent interface methods. Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[])} default argument value}
+ * for an option does not cause this method to return {@code true} if the option was not detected on the command
+ * line. This method recognizes only instances of options returned from the fluent interface methods. Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+ * for an option does not cause this method to return {@code true} if the option was not detected on the command
+ * line, or if the option can take an optional argument but did not have one on the command line. Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+ * for an option will cause this method to return that default value even if the option was not detected on the
+ * command line, or if the option can take an optional argument but did not have one on the command line. This method recognizes only instances of options returned from the fluent interface methods. Gives any arguments associated with the given option. If the option was given an argument type, the
+ * arguments will take on that type; otherwise, they will be {@link String}s. Gives any arguments associated with the given option. If the option was given an argument type, the
+ * arguments will take on that type; otherwise, they will be {@link String}s. This method recognizes only instances of options returned from the fluent interface methods. Instances of this interface are returned by the "fluent interface" methods to allow retrieval of option arguments
+ * in a type-safe manner. Here's an example: Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+ * for this option will cause this method to return that default value even if this option was not detected on the
+ * command line, or if this option can take an optional argument but did not have one on the command line. Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+ * for this option will cause this method to return that default value even if this option was not detected on the
+ * command line, or if this option can take an optional argument but did not have one on the command line. Instances are returned from {@link OptionParser#accepts(String)} to allow the formation of parser directives as
+ * sentences in a "fluent interface" language. For example: If no methods are invoked on an instance of this class, then that instance's option will accept no argument. Note that you should not use the fluent interface clauses in a way that would defeat the typing of option
+ * arguments: Informs an option parser that this builder's option is required if the given option is present on the command
+ * line. For a given option, you should not mix this with {@link #requiredUnless(String, String...)
+ * requiredUnless} to avoid conflicts. Informs an option parser that this builder's option is required if the given option is present on the command
+ * line. For a given option, you should not mix this with {@link #requiredUnless(OptionSpec, OptionSpec[])
+ * requiredUnless} to avoid conflicts. This method recognizes only instances of options returned from the fluent interface methods. Informs an option parser that this builder's option is required if the given option is absent on the command
+ * line. For a given option, you should not mix this with {@link #requiredIf(OptionSpec, OptionSpec[])
+ * requiredIf} to avoid conflicts. Informs an option parser that this builder's option is required if the given option is absent on the command
+ * line. For a given option, you should not mix this with {@link #requiredIf(OptionSpec, OptionSpec[])
+ * requiredIf} to avoid conflicts. This method recognizes only instances of options returned from the fluent interface methods. A map whose keys are strings; when a key/value pair is added to the map, the longest unique abbreviations of that
+ * key are added as well, and associated with the value. Thus: would make it such that you could retrieve the value {@code "bye"} from the map using the keys {@code "good"},
+ * {@code "goo"}, {@code "go"}, and {@code "g"}. A subsequent invocation of: would make it such that you could retrieve the value {@code "bye"} using the keys {@code "good"} and
+ * {@code "goo"}, and the value {@code "fish"} using the key {@code "go"}. The key {@code "g"} would yield
+ * {@code null}, since it would no longer be a unique abbreviation. The data structure is much like a "trie". Tells whether the given key is in the map, or whether the given key is a unique
+ * abbreviation of a key that is in the map. Answers the value associated with the given key. The key can be a unique
+ * abbreviation of a key that is in the map. Associates a given value with a given key. If there was a previous
+ * association, the old value is replaced with the new one. Associates a given value with a given set of keys. If there was a previous
+ * association, the old value is replaced with the new one. If the map contains the given key, dissociates the key from its value.
+ *
+ *
+ *
+ * parser.accepts( "z" ).withRequiredArg()
+ * .withValuesSeparatedBy( ',' );
+ * OptionSet options = parser.parse( new String[] { "-z", "foo,bar,baz", "-z",
+ * "fizz", "-z", "buzz" } );
+ *
+ *
+ *
+ *
+ *
+ * parser.accepts( "z" ).withRequiredArg()
+ * .withValuesSeparatedBy( ":::" );
+ * OptionSet options = parser.parse( new String[] { "-z", "foo:::bar:::baz", "-z",
+ * "fizz", "-z", "buzz" } );
+ *
+ *
+ *
+ *
+ *
+ * OptionParser parser = new OptionParser();
+ * parser.nonOptions( "files to be processed" ).ofType( File.class );
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * OptionDeclarer parser = new OptionParser();
+ * parser.accepts( "c" ).withRequiredArg().ofType( Integer.class );
+ *
+ *
+ *
+ *
+ *
+ * To specify n arguments for an option, specify the option n times, once for each argument,
+ * as in -d /tmp -d /var -d /opt; or, when using the
+ * {@linkplain ArgumentAcceptingOptionSpec#withValuesSeparatedBy(char) "separated values"} clause of the "fluent
+ * interface" (see below), give multiple values separated by a given character as a single argument to the
+ * option.
+ *
+ * Specify multiple arguments for a long option in the same manner as for short options (see above).
+ * In this case, the option set contains "a" with argument -2, not both "a" and
+ * "2". Swapping the elements in the args array gives the latter.
+ * OptionParser parser = new OptionParser();
+ * parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
+ * parser.accepts( "2" );
+ * OptionSet options = parser.parse( "-a", "-2" );
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * In this case,
+ * OptionParser parser = new OptionParser();
+ * parser.acceptsAll( asList( "w", "interactive", "confirmation" ) );
+ * OptionSet options = parser.parse( "-w" );
+ *
+ * options.{@link OptionSet#has(String) has}
would answer {@code true} when given arguments
+ * "w", "interactive", and "confirmation". The {@link OptionSet} would give the same
+ * responses to these arguments for its other methods as well.
+ *
+ *
+ * @author Paul Holser
+ * @see The GNU C Library
+ */
+public class OptionParser implements OptionDeclarer {
+ private final AbbreviationMap
+ *
+ * @param
+ * OptionParser parser = new OptionParser();
+ * OptionSpec<Integer> count =
+ * parser.accepts( "count" ).withRequiredArg().ofType( Integer.class );
+ * OptionSet options = parser.parse( "--count", "2" );
+ * assert options.has( count );
+ * int countValue = options.valueOf( count );
+ * assert countValue == count.value( options );
+ * List<Integer> countValues = options.valuesOf( count );
+ * assert countValues.equals( count.values( options ) );
+ *
+ *
+ *
+ * OptionParser parser = new OptionParser();
+ * parser.accepts( "c" ).withRequiredArg().ofType( Integer.class );
+ *
+ *
+ * @author Paul Holser
+ */
+public class OptionSpecBuilder extends NoArgumentOptionSpec {
+ private final OptionParser parser;
+
+ OptionSpecBuilder( OptionParser parser, Collection
+ * OptionParser parser = new OptionParser();
+ * ArgumentAcceptingOptionSpec<String> optionC =
+ * parser.accepts( "c" ).withRequiredArg();
+ * optionC.ofType( Integer.class ); // DON'T THROW AWAY THE TYPE!
+ *
+ * String value = parser.parse( "-c", "2" ).valueOf( optionC ); // ClassCastException
+ *
+ *
+ *
+ *
+ * abbreviations.put( "good", "bye" );
+ *
+ *
+ *
+ *
+ *
+ * abbreviations.put( "go", "fish" );
+ *
+ *