jline3-jline-3.3.1/000077500000000000000000000000001311544710100137765ustar00rootroot00000000000000jline3-jline-3.3.1/.gitignore000066400000000000000000000000731311544710100157660ustar00rootroot00000000000000*.i?? target .classpath .project .settings/ .idea build.rc jline3-jline-3.3.1/.travis.yml000066400000000000000000000034221311544710100161100ustar00rootroot00000000000000language: java sudo: false env: global: - secure: 'PajKlInMvlkhMNFPSvwgOr8GnvQ/1/uAm+ZVbFCFW3CS40CdaSwCT5vWiyENoQsbAjXMVHFzpUSIHjXpLCA7jl3c0AM1uzjXTsQGtFVyxLPn1f7K0ePy0g3EKZPka4SGVpSG0GwW6kfWVfPNPDEIZJM0ZQRvta4/aOAldFzcb9X7hKjM2H4PbgYEhVfNP50QGsuonZf7PLu4Bl5bNH8wXIGn5iNGPauYa/mlFsI5Gsar0F4PJaVd7efszh+lPAHUaW/DUpoN0S9gM18tC28R7OIDvuniFPzIWVPm2P0aIYAktVf+GVTgACzuL7/D6OhxEyyCTqdet2jMFFkP1wcEvugPsArkZRPeqwuA+2XXV9amWvvSopkMJIkad6TnfK+zfVkMH0vXa/cu/a/5WPE2QS5VhCXocVze3sOUlncifxftd57kENley29+K57K4BWhxtsb/Uh1nvUpxgXnodgWQ7M5i0e4/iX14oVjCic1VDIUYFagGl1texVJ1jTsvflO2LEozof0f+ZTK1r7xRKabIj4YOugA5dz658ZdjZrds5n88Uv6KVD1T2ixhhnPVxeimpHdu5j43G9P7nR7T/n/nl50CsTOtS7Hnrh6KdUGMvBm42nFKra9nyInea5kwkMhHu182+01t+jYF3y/TIeU55nGjiXJPEf/pQK6nzxDV8=' - secure: 'HqKdgQbxwuP0HkTzklTofLU4Wv7a1uM1BU5B0C7Hk7JuEmMBH0iC6VcyWd6C2zQgm1jRqZ6Dly9t7iwtmR7eDdOSpA0X2LsZuZZMuYzrQVW9M7i8qzNECl80UvOKnEiqlQD2qxUDCp+hX0UiOuAF4DX9upIo7SMplWYdsDHWpcIRz+b5HUMnnS32gBaHZ2Y02FS5LMOI9HV0UdPXSwTI4LzrztAYEj8yGn8inYwOTD5JTC9rF8It58zCtRJ1e9cM7BINOa8Ri0TpVOJGJlze9oNKEKnBS+zTRCphsBlomkyNBxknm86yXr0smGqiMp4wqrG8G45JNN7034koU5b+m796sf6fFFV8rEdkQDdkrbBSFlDwEuFc+yWDFhDJqQCJRKbBfL+n2tJJAyLse/OtrYvizdMrBX1tnhp/lrBEVa3GrtCa1B5a3XrV20sdowLveQERnMPfJDcTKP60FB6lQIAHg/yNjSFV84SmHrisMIH6xbkZHZsh50foUlo73XaGRa8XhBOTcJ9Gh6pwcrNLv4rCUv4UE+CV7AUC90jEnbHrJbHtbCo/QOJgQ7cT83kzA7iP3JDppO79MV/6QfEHqRNTt/XkzhIt+PrGfUakTNe32lB6uFni+jv8/xyjGsgFDjOJqWoWIDUCjUTYrjx3iv702Q0dQLrau05eEuzf53g=' - MAVEN_SETTINGS='.travis/settings.xml' - MAVEN_OPTIONS="-V -B -s $MAVEN_SETTINGS" branches: only: - master jdk: - oraclejdk8 # FIXME: for now just complain; do not fail as the present license headers have inconsistent date ranges install: ./build ci-prepare -Dlicense.failIfMissing=false $MAVEN_OPTIONS script: ./build ci-build $MAVEN_OPTIONS jline3-jline-3.3.1/.travis/000077500000000000000000000000001311544710100153645ustar00rootroot00000000000000jline3-jline-3.3.1/.travis/settings.xml000066400000000000000000000006541311544710100177530ustar00rootroot00000000000000 sonatype-nexus-snapshots ${env.SONATYPE_USERNAME} ${env.SONATYPE_PASSWORD} sonatype-nexus-staging ${env.SONATYPE_USERNAME} ${env.SONATYPE_PASSWORD} jline3-jline-3.3.1/LICENSE.txt000066400000000000000000000027771311544710100156360ustar00rootroot00000000000000Copyright (c) 2002-2016, the original author or authors. All rights reserved. http://www.opensource.org/licenses/bsd-license.php Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of JLine nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jline3-jline-3.3.1/README.md000066400000000000000000000041771311544710100152660ustar00rootroot00000000000000 # Description JLine is a Java library for handling console input. It is similar in functionality to [BSD editline](http://www.thrysoee.dk/editline/) and [GNU readline](http://www.gnu.org/s/readline/) but with additional features that bring it in par with [ZSH line editor](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html). People familiar with the readline/editline capabilities for modern shells (such as bash and tcsh) will find most of the command editing features of JLine to be familiar. JLine 3.x is an evolution of [JLine 2.x](https://github.com/jline/jline2). [![Build Status](https://travis-ci.org/jline/jline3.svg?branch=master)](https://travis-ci.org/jline/jline3) # License JLine is distributed under the [BSD License](http://www.opensource.org/licenses/bsd-license.php), meaning that you are completely free to redistribute, modify, or sell it with almost no restrictions. # Documentation * [demos](https://github.com/jline/jline3/wiki/Demos) * [wiki](https://github.com/jline/jline3/wiki) # Forums * [jline-users](https://groups.google.com/group/jline-users) * [jline-dev](https://groups.google.com/group/jline-dev) # Maven Usage Use the following definition to use JLine in your maven project: org.jline jline 3.3.0 JLine can also be used with more low-level jars: org.jline jline-terminal 3.3.0 org.jline jline-reader 3.3.0 # Building ## Requirements * Maven 3.3+ (prefer included maven-wrapper) * Java 8+ Check out and build: git clone git://github.com/jline/jline3.git cd jline3 mvn install jline3-jline-3.3.1/build000077500000000000000000000044741311544710100150340ustar00rootroot00000000000000#!/bin/bash -e basename=`basename $0` dirname=`dirname $0` dirname=`cd "$dirname" && pwd` cd "$dirname" source build.config # load optional config if [ -f ./build.rc ]; then source ./build.rc fi # show message and exit function die { echo "$1" exit 1 } # show usage and exit function usage { echo "usage: $basename [options]" exit 2 } # run self function self { $0 $* } # run maven function mvn { ./mvnw $* } command="$1"; shift # complain if no command is given if [ -z "$command" ]; then usage fi case "$command" in # change the version of the project change-version) newVersion="$1" if [ -z "$newVersion" ]; then usage "$command " fi mvn org.eclipse.tycho:tycho-versions-plugin:0.25.0:set-version \ -Dtycho.mode=maven \ -Dartifacts=${project} \ -Dproperties=${project}.version \ -DnewVersion="$newVersion" ;; # check license headers license-check) mvn -Plicense-check -N $* ;; # format license headers license-format) mvn -Plicense-format -N $* ;; # prepare CI build ci-prepare) self license-check $* ;; # build for CI ci-build) if [ "$TRAVIS_PULL_REQUEST" != 'false' ]; then goal=install else goal=deploy fi mvn clean ${goal} $* ;; # release automation release) version="$1" nextVersion="$2" if [ -z "$version" -o -z "$nextVersion" ]; then usage "$command " fi releaseTag="release-$version" # update version and tag self change-version "$version" git commit -a -m "update version: $version" git tag $releaseTag # deploy release mvn -Pbuildsupport-release clean deploy # update to next version self change-version "$nextVersion" git commit -a -m "update version: $nextVersion" ;; *) # attempt to lookup command function fn="command_$command" if [ "$(type -t $fn)" = 'function' ]; then $fn $* else # complain about missing command function echo "Unknown command: $command" usage fi ;; esac jline3-jline-3.3.1/build.config000066400000000000000000000002121311544710100162570ustar00rootroot00000000000000#!/bin/bash project=jline function command_rebuild { mvn clean install $* } function command_demo() { exec demo/jline-gogo.sh $* } jline3-jline-3.3.1/builtins/000077500000000000000000000000001311544710100156275ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/pom.xml000066400000000000000000000061261311544710100171510ustar00rootroot00000000000000 4.0.0 org.jline jline-parent 3.3.1 jline-builtins JLine Builtins org.jline jline-reader com.googlecode.juniversalchardet juniversalchardet true junit junit test org.apache.maven.plugins maven-compiler-plugin true true default-compile **/TTop.java -Xlint:all,-options -Werror -profile compact1 noncompact compile **/TTop.java -Xlint:all,-options -Werror -profile compact3 jline3-jline-3.3.1/builtins/src/000077500000000000000000000000001311544710100164165ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/000077500000000000000000000000001311544710100173425ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/java/000077500000000000000000000000001311544710100202635ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/java/org/000077500000000000000000000000001311544710100210525ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/java/org/jline/000077500000000000000000000000001311544710100221535ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/000077500000000000000000000000001311544710100240045ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/Commands.java000066400000000000000000001006331311544710100264130ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import java.io.IOException; import java.io.PrintStream; import java.nio.file.Path; import java.time.LocalTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import org.jline.builtins.Completers.CompletionData; import org.jline.builtins.Source.StdInSource; import org.jline.builtins.Source.URLSource; import org.jline.keymap.KeyMap; import org.jline.reader.Binding; import org.jline.reader.History; import org.jline.reader.LineReader; import org.jline.reader.LineReader.Option; import org.jline.reader.Macro; import org.jline.reader.Reference; import org.jline.reader.Widget; import org.jline.terminal.Terminal; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; public class Commands { public static void tmux(Terminal terminal, PrintStream out, PrintStream err, Supplier getter, Consumer setter, Consumer runner, String[] argv) throws Exception { final String[] usage = { "tmux - terminal multiplexer", "Usage: tmux [command]", " -? --help Show help", }; // Simplified parsing if (argv.length == 1 && ("--help".equals(argv[0]) || "-?".equals(argv[0]))) { for (String s : usage) { err.println(s); } return; } // Tmux with no args if (argv.length == 0) { Object instance = getter.get(); if (instance != null) { err.println("tmux: can't run tmux inside itself"); } else { Tmux tmux = new Tmux(terminal, err, runner); setter.accept(tmux); try { tmux.run(); } finally { setter.accept(null); } } } else { Object instance = getter.get(); if (instance != null) { ((Tmux) instance).execute(out, err, Arrays.asList(argv)); } else { err.println("tmux: no instance running"); } } } public static void nano(Terminal terminal, PrintStream out, PrintStream err, Path currentDir, String[] argv) throws Exception { final String[] usage = { "nano - edit files", "Usage: nano [FILES]", " -? --help Show help", }; Options opt = Options.compile(usage).parse(argv); if (opt.isSet("help")) { opt.usage(err); return; } Nano edit = new Nano(terminal, currentDir); edit.open(opt.args()); edit.run(); } public static void less(Terminal terminal, PrintStream out, PrintStream err, Path currentDir, String[] argv) throws IOException, InterruptedException { final String[] usage = { "less - file pager", "Usage: less [OPTIONS] [FILES]", " -? --help Show help", " -e --quit-at-eof Exit on second EOF", " -E --QUIT-AT-EOF Exit on EOF", " -q --quiet --silent Silent mode", " -Q --QUIET --SILENT Completely silent", " -S --chop-long-lines Do not fold long lines", " -i --ignore-case Search ignores lowercase case", " -I --IGNORE-CASE Search ignores all case", " -x --tabs Set tab stops", " -N --LINE-NUMBERS Display line number for each line" }; Options opt = Options.compile(usage).parse(argv); if (opt.isSet("help")) { opt.usage(err); return; } Less less = new Less(terminal); less.quitAtFirstEof = opt.isSet("QUIT-AT-EOF"); less.quitAtSecondEof = opt.isSet("quit-at-eof"); less.quiet = opt.isSet("quiet"); less.veryQuiet = opt.isSet("QUIET"); less.chopLongLines = opt.isSet("chop-long-lines"); less.ignoreCaseAlways = opt.isSet("IGNORE-CASE"); less.ignoreCaseCond = opt.isSet("ignore-case"); if (opt.isSet("tabs")) { less.tabs = opt.getNumber("tabs"); } less.printLineNumbers = opt.isSet("LINE-NUMBERS"); List sources = new ArrayList<>(); if (opt.args().isEmpty()) { opt.args().add("-"); } for (String arg : opt.args()) { if ("-".equals(arg)) { sources.add(new StdInSource()); } else { sources.add(new URLSource(currentDir.resolve(arg).toUri().toURL(), arg)); } } less.run(sources); } public static void history(LineReader reader, PrintStream out, PrintStream err, String[] argv) throws IOException { final String[] usage = { "history - list history of commands", "Usage: history [OPTIONS]", " -? --help Displays command help", " --clear Clear history", " --save Save history", " -d Print timestamps for each event"}; Options opt = Options.compile(usage).parse(argv); if (opt.isSet("help")) { opt.usage(err); return; } if (!opt.args().isEmpty()) { err.println("usage: history [OPTIONS]"); return; } History history = reader.getHistory(); if (opt.isSet("clear")) { history.purge(); } if (opt.isSet("save")) { history.save(); } if (opt.isSet("clear") || opt.isSet("save")) { return; } for (History.Entry entry : history) { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.append(" "); sb.styled(AttributedStyle::bold, String.format("%3d", entry.index() + 1)); if (opt.isSet("d")) { sb.append(" "); LocalTime lt = LocalTime.from(entry.time().atZone(ZoneId.systemDefault())) .truncatedTo(ChronoUnit.SECONDS); DateTimeFormatter.ISO_LOCAL_TIME.formatTo(lt, sb); } sb.append(" "); sb.append(entry.line()); out.println(sb.toAnsi(reader.getTerminal())); } } public static void complete(LineReader reader, PrintStream out, PrintStream err, Map> completions, String[] argv) { final String[] usage = { "complete - edit command specific tab-completions", "Usage: complete", " -? --help Displays command help", " -c --command=COMMAND Command to add completion to", " -d --description=DESCRIPTION Description of this completions", " -e --erase Erase the completions", " -s --short-option=SHORT_OPTION Posix-style option to complete", " -l --long-option=LONG_OPTION GNU-style option to complete", " -a --argument=ARGUMENTS A list of possible arguments", " -n --condition=CONDITION The completion should only be used if the", " specified command has a zero exit status"}; Options opt = Options.compile(usage).parse(argv); if (opt.isSet("help")) { opt.usage(err); return; } String command = opt.get("command"); if (opt.isSet("erase")) { completions.remove(command); return; } List cmdCompletions = completions.computeIfAbsent(command, s -> new ArrayList<>()); List options = null; if (opt.isSet("short-option")) { for (String op : opt.getList("short-option")) { if (options == null) { options = new ArrayList<>(); } options.add("-" + op); } } if (opt.isSet("long-option")) { for (String op : opt.getList("long-option")) { if (options == null) { options = new ArrayList<>(); } options.add("--" + op); } } String description = opt.isSet("description") ? opt.get("description") : null; String argument = opt.isSet("argument") ? opt.get("argument") : null; String condition = opt.isSet("condition") ? opt.get("condition") : null; cmdCompletions.add(new CompletionData(options, description, argument, condition)); } public static void widget(LineReader reader, PrintStream out, PrintStream err, Function widgetCreator, String[] argv) throws Exception { final String[] usage = { "widget - manipulate widgets", "Usage: widget [options] -N new-widget [function-name]", " widget [options] -D widget ...", " widget [options] -A old-widget new-widget", " widget [options] -U string ...", " widget [options] -l", " -? --help Displays command help", " -A Create alias to widget", " -N Create new widget", " -D Delete widgets", " -U Push characters to the stack", " -l List user-defined widgets", " -a With -l, list all widgets" }; Options opt = Options.compile(usage).parse(argv); if (opt.isSet("help")) { opt.usage(err); return; } int actions = (opt.isSet("N") ? 1 : 0) + (opt.isSet("D") ? 1 : 0) + (opt.isSet("U") ? 1 : 0) + (opt.isSet("l") ? 1 : 0) + (opt.isSet("A") ? 1 : 0); if (actions > 1) { err.println("widget: incompatible operation selection options"); return; } if (opt.isSet("l")) { Set widgets = new TreeSet<>(reader.getWidgets().keySet()); if (!opt.isSet("a")){ widgets.removeAll(reader.getBuiltinWidgets().keySet()); } widgets.forEach(out::println); } else if (opt.isSet("N")) { if (opt.args().size() < 1) { err.println("widget: not enough arguments for -N"); return; } if (opt.args().size() > 2) { err.println("widget: too many arguments for -N"); return; } final String name = opt.args().get(0); final String func = opt.args().size() == 2 ? opt.args().get(1) : name; reader.getWidgets().put(name, widgetCreator.apply(func)); } else if (opt.isSet("D")) { for (String name : opt.args()) { reader.getWidgets().remove(name); } } else if (opt.isSet("A")) { if (opt.args().size() < 2) { err.println("widget: not enough arguments for -A"); return; } if (opt.args().size() > 2) { err.println("widget: too many arguments for -A"); return; } Widget org = reader.getWidgets().get(opt.args().get(0)); if (org == null) { err.println("widget: no such widget `" + opt.args().get(0) + "'"); return; } reader.getWidgets().put(opt.args().get(1), org); } else if (opt.isSet("U")) { for (String arg : opt.args()) { reader.runMacro(KeyMap.translate(arg)); } } else if (opt.args().size() == 1) { reader.callWidget(opt.args().get(0)); } } public static void keymap(LineReader reader, PrintStream out, PrintStream err, String[] argv) { final String[] usage = { "keymap - manipulate keymaps", "Usage: keymap [options] -l [-L] [keymap ...]", " keymap [options] -d", " keymap [options] -D keymap ...", " keymap [options] -A old-keymap new-keymap", " keymap [options] -N new-keymap [old-keymap]", " keymap [options] -m", " keymap [options] -r in-string ...", " keymap [options] -s in-string out-string ...", " keymap [options] in-string command ...", " keymap [options] [in-string]", " -? --help Displays command help", " -A Create alias to keymap", " -D Delete named keymaps", " -L Output in form of keymap commands", " -M (default=main) Specify keymap to select", " -N Create new keymap", " -R Interpret in-strings as ranges", " -a Select vicmd keymap", " -d Delete existing keymaps and reset to default state", " -e Select emacs keymap and bind it to main", " -l List existing keymap names", " -p List bindings which have given key sequence as a a prefix", " -r Unbind specified in-strings", " -s Bind each in-string to each out-string", " -v Select viins keymap and bind it to main", }; Options opt = Options.compile(usage).parse(argv); if (opt.isSet("help")) { opt.usage(err); return; } Map> keyMaps = reader.getKeyMaps(); int actions = (opt.isSet("N") ? 1 : 0) + (opt.isSet("d") ? 1 : 0) + (opt.isSet("D") ? 1 : 0) + (opt.isSet("l") ? 1 : 0) + (opt.isSet("r") ? 1 : 0) + (opt.isSet("s") ? 1 : 0) + (opt.isSet("A") ? 1 : 0); if (actions > 1) { err.println("keymap: incompatible operation selection options"); return; } if (opt.isSet("l")) { boolean commands = opt.isSet("L"); // TODO: handle commands if (opt.args().size() > 0) { for (String arg : opt.args()) { KeyMap map = keyMaps.get(arg); if (map == null) { err.println("keymap: no such keymap: `" + arg + "'"); } else { out.println(arg); } } } else { keyMaps.keySet().forEach(out::println); } } else if (opt.isSet("N")) { if (opt.isSet("e") || opt.isSet("v") || opt.isSet("a") || opt.isSet("M")) { err.println("keymap: keymap can not be selected with -N"); return; } if (opt.args().size() < 1) { err.println("keymap: not enough arguments for -N"); return; } if (opt.args().size() > 2) { err.println("keymap: too many arguments for -N"); return; } KeyMap org = null; if (opt.args().size() == 2) { org = keyMaps.get(opt.args().get(1)); if (org == null) { err.println("keymap: no such keymap `" + opt.args().get(1) + "'"); return; } } KeyMap map = new KeyMap<>(); if (org != null) { for (Map.Entry bound : org.getBoundKeys().entrySet()) { map.bind(bound.getValue(), bound.getKey()); } } keyMaps.put(opt.args().get(0), map); } else if (opt.isSet("A")) { if (opt.isSet("e") || opt.isSet("v") || opt.isSet("a") || opt.isSet("M")) { err.println("keymap: keymap can not be selected with -N"); return; } if (opt.args().size() < 2) { err.println("keymap: not enough arguments for -A"); return; } if (opt.args().size() > 2) { err.println("keymap: too many arguments for -A"); return; } KeyMap org = keyMaps.get(opt.args().get(0)); if (org == null) { err.println("keymap: no such keymap `" + opt.args().get(0) + "'"); return; } keyMaps.put(opt.args().get(1), org); } else if (opt.isSet("d")) { if (opt.isSet("e") || opt.isSet("v") || opt.isSet("a") || opt.isSet("M")) { err.println("keymap: keymap can not be selected with -N"); return; } if (opt.args().size() > 0) { err.println("keymap: too many arguments for -d"); return; } keyMaps.clear(); keyMaps.putAll(reader.defaultKeyMaps()); } else if (opt.isSet("D")) { if (opt.isSet("e") || opt.isSet("v") || opt.isSet("a") || opt.isSet("M")) { err.println("keymap: keymap can not be selected with -N"); return; } if (opt.args().size() < 1) { err.println("keymap: not enough arguments for -A"); return; } for (String name : opt.args()) { if (keyMaps.remove(name) == null) { err.println("keymap: no such keymap `" + name + "'"); return; } } } else if (opt.isSet("r")) { // Select keymap String keyMapName = LineReader.MAIN; int sel = (opt.isSet("a") ? 1 : 0) + (opt.isSet("e") ? 1 : 0) + (opt.isSet("v") ? 1 : 0) + (opt.isSet("M") ? 1 : 0); if (sel > 1) { err.println("keymap: incompatible keymap selection options"); return; } else if (opt.isSet("a")) { keyMapName = LineReader.VICMD; } else if (opt.isSet("e")) { keyMapName = LineReader.EMACS; } else if (opt.isSet("v")) { keyMapName = LineReader.VIINS; } else if (opt.isSet("M")) { if (opt.args().isEmpty()) { err.println("keymap: argument expected: -M"); return; } keyMapName = opt.args().remove(0); } KeyMap map = keyMaps.get(keyMapName); if (map == null) { err.println("keymap: no such keymap `" + keyMapName + "'"); return; } // Unbind boolean range = opt.isSet("R"); boolean prefix = opt.isSet("p"); Set toRemove = new HashSet<>(); Map bound = map.getBoundKeys(); for (String arg : opt.args()) { if (range) { Collection r = KeyMap.range(opt.args().get(0)); if (r == null) { err.println("keymap: malformed key range `" + opt.args().get(0) + "'"); return; } toRemove.addAll(r); } else { String seq = KeyMap.translate(arg); for (String k : bound.keySet()) { if (prefix && k.startsWith(seq) && k.length() > seq.length() || !prefix && k.equals(seq)) { toRemove.add(k); } } } } for (String seq : toRemove) { map.unbind(seq); } if (opt.isSet("e") || opt.isSet("v")) { keyMaps.put(LineReader.MAIN, map); } } else if (opt.isSet("s") || opt.args().size() > 1) { // Select keymap String keyMapName = LineReader.MAIN; int sel = (opt.isSet("a") ? 1 : 0) + (opt.isSet("e") ? 1 : 0) + (opt.isSet("v") ? 1 : 0) + (opt.isSet("M") ? 1 : 0); if (sel > 1) { err.println("keymap: incompatible keymap selection options"); return; } else if (opt.isSet("a")) { keyMapName = LineReader.VICMD; } else if (opt.isSet("e")) { keyMapName = LineReader.EMACS; } else if (opt.isSet("v")) { keyMapName = LineReader.VIINS; } else if (opt.isSet("M")) { if (opt.args().isEmpty()) { err.println("keymap: argument expected: -M"); return; } keyMapName = opt.args().remove(0); } KeyMap map = keyMaps.get(keyMapName); if (map == null) { err.println("keymap: no such keymap `" + keyMapName + "'"); return; } // Bind boolean range = opt.isSet("R"); if (opt.args().size() % 2 == 1) { err.println("keymap: even number of arguments required"); return; } for (int i = 0; i < opt.args().size(); i += 2) { Binding bout = opt.isSet("s") ? new Macro(KeyMap.translate(opt.args().get(i + 1))) : new Reference(opt.args().get(i + 1)); if (range) { Collection r = KeyMap.range(opt.args().get(i)); if (r == null) { err.println("keymap: malformed key range `" + opt.args().get(i) + "'"); return; } map.bind(bout, r); } else { String in = KeyMap.translate(opt.args().get(i)); map.bind(bout, in); } } if (opt.isSet("e") || opt.isSet("v")) { keyMaps.put(LineReader.MAIN, map); } } else { // Select keymap String keyMapName = LineReader.MAIN; int sel = (opt.isSet("a") ? 1 : 0) + (opt.isSet("e") ? 1 : 0) + (opt.isSet("v") ? 1 : 0) + (opt.isSet("M") ? 1 : 0); if (sel > 1) { err.println("keymap: incompatible keymap selection options"); return; } else if (opt.isSet("a")) { keyMapName = LineReader.VICMD; } else if (opt.isSet("e")) { keyMapName = LineReader.EMACS; } else if (opt.isSet("v")) { keyMapName = LineReader.VIINS; } else if (opt.isSet("M")) { if (opt.args().isEmpty()) { err.println("keymap: argument expected: -M"); return; } keyMapName = opt.args().remove(0); } KeyMap map = keyMaps.get(keyMapName); if (map == null) { err.println("keymap: no such keymap `" + keyMapName + "'"); return; } // Display boolean prefix = opt.isSet("p"); boolean commands = opt.isSet("L"); if (prefix && opt.args().isEmpty()) { err.println("keymap: option -p requires a prefix string"); return; } if (opt.args().size() > 0 || !opt.isSet("e") && !opt.isSet("v")) { Map bound = map.getBoundKeys(); String seq = opt.args().size() > 0 ? KeyMap.translate(opt.args().get(0)) : null; Map.Entry begin = null; String last = null; Iterator> iterator = bound.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); String key = entry.getKey(); if (seq == null || prefix && key.startsWith(seq) && !key.equals(seq) || !prefix && key.equals(seq)) { if (begin != null || !iterator.hasNext()) { String n = (last.length() > 1 ? last.substring(0, last.length() - 1) : "") + (char) (last.charAt(last.length() - 1) + 1); if (key.equals(n) && entry.getValue().equals(begin.getValue())) { last = key; } else { // We're not in a range, so we need to close the previous range StringBuilder sb = new StringBuilder(); if (commands) { sb.append("keymap -M "); sb.append(keyMapName); sb.append(" "); } if (begin.getKey().equals(last)) { sb.append(KeyMap.display(last)); sb.append(" "); displayValue(sb, begin.getValue()); out.println(sb.toString()); } else { if (commands) { sb.append("-R "); } sb.append(KeyMap.display(begin.getKey())); sb.append("-"); sb.append(KeyMap.display(last)); sb.append(" "); displayValue(sb, begin.getValue()); out.println(sb.toString()); } begin = entry; last = key; } } else { begin = entry; last = key; } } } } if (opt.isSet("e") || opt.isSet("v")) { keyMaps.put(LineReader.MAIN, map); } } } public static void setopt(LineReader reader, PrintStream out, PrintStream err, String[] argv) { final String[] usage = { "setopt - set options", "Usage: setopt [-m] option ...", " setopt", " -? --help Displays command help", " -m Use pattern matching" }; Options opt = Options.compile(usage).parse(argv); if (opt.isSet("help")) { opt.usage(err); return; } if (opt.args().isEmpty()) { for (Option option : Option.values()) { if (reader.isSet(option) != option.isDef()) { out.println((option.isDef() ? "no-" : "") + option.toString().toLowerCase().replace('_', '-')); } } } else { boolean match = opt.isSet("m"); doSetOpts(reader, out, err, opt.args(), match, true); } } public static void unsetopt(LineReader reader, PrintStream out, PrintStream err, String[] argv) { final String[] usage = { "unsetopt - unset options", "Usage: unsetopt [-m] option ...", " unsetopt", " -? --help Displays command help", " -m Use pattern matching" }; Options opt = Options.compile(usage).parse(argv); if (opt.isSet("help")) { opt.usage(err); return; } if (opt.args().isEmpty()) { for (Option option : Option.values()) { if (reader.isSet(option) == option.isDef()) { out.println((option.isDef() ? "no-" : "") + option.toString().toLowerCase().replace('_', '-')); } } } else { boolean match = opt.isSet("m"); doSetOpts(reader, out, err, opt.args(), match, false); } } private static void doSetOpts(LineReader reader, PrintStream out, PrintStream err, List options, boolean match, boolean set) { for (String name : options) { String tname = name.toLowerCase().replaceAll("[-_]", ""); if (match) { tname = tname.replaceAll("\\*", "[a-z]*"); tname = tname.replaceAll("\\?", "[a-z]"); } boolean found = false; for (LineReader.Option option : LineReader.Option.values()) { String optName = option.name().toLowerCase().replaceAll("[-_]", ""); if (match ? optName.matches(tname) : optName.equals(tname)) { if (set) { reader.setOpt(option); } else { reader.unsetOpt(option); } found = true; if (!match) { break; } } else if (match ? ("no" + optName).matches(tname) : ("no" + optName).equals(tname)) { if (set) { reader.unsetOpt(option); } else { reader.setOpt(option); } if (!match) { found = true; } break; } } if (!found) { err.println("No matching option: " + name); } } } private static void displayValue(StringBuilder sb, Object value) { if (value == null) { sb.append("undefined-key"); } else if (value instanceof Macro) { sb.append(KeyMap.display(((Macro) value).getSequence())); } else if (value instanceof Reference) { sb.append(((Reference) value).name()); } else { sb.append(value.toString()); } } } jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/Completers.java000066400000000000000000000463311311544710100267730ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import java.io.File; import java.io.IOException; import java.lang.reflect.Array; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.Function; import org.jline.reader.Candidate; import org.jline.reader.LineReader; import org.jline.reader.ParsedLine; import org.jline.terminal.Terminal; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; public class Completers { public interface CompletionEnvironment { Map> getCompletions(); Set getCommands(); String resolveCommand(String command); String commandName(String command); Object evaluate(LineReader reader, ParsedLine line, String func) throws Exception; } public static class CompletionData { public final List options; public final String description; public final String argument; public final String condition; public CompletionData(List options, String description, String argument, String condition) { this.options = options; this.description = description; this.argument = argument; this.condition = condition; } } public static class Completer implements org.jline.reader.Completer { private final CompletionEnvironment environment; public Completer(CompletionEnvironment environment) { this.environment = environment; } public void complete(LineReader reader, ParsedLine line, List candidates) { if (line.wordIndex() == 0) { completeCommand(candidates); } else { tryCompleteArguments(reader, line, candidates); } } @SuppressWarnings("unchecked") protected void tryCompleteArguments(LineReader reader, ParsedLine line, List candidates) { String command = line.words().get(0); String resolved = environment.resolveCommand(command); Map> comp = environment.getCompletions(); if (comp != null) { List cmd = comp.get(resolved); if (cmd != null) { completeCommandArguments(reader, line, candidates, cmd); } } } @SuppressWarnings("unchecked") protected void completeCommandArguments(LineReader reader, ParsedLine line, List candidates, List completions) { for (CompletionData completion : completions) { boolean isOption = line.word().startsWith("-"); String prevOption = line.wordIndex() >= 2 && line.words().get(line.wordIndex() - 1).startsWith("-") ? line.words().get(line.wordIndex() - 1) : null; String key = UUID.randomUUID().toString(); boolean conditionValue = true; if (completion.condition != null) { Object res = Boolean.FALSE; try { res = environment.evaluate(reader, line, completion.condition); } catch (Throwable t) { t.getCause(); // Ignore } conditionValue = isTrue(res); } if (conditionValue && isOption && completion.options != null) { for (String opt : completion.options) { candidates.add(new Candidate(opt, opt, "options", completion.description, null, key, true)); } } else if (!isOption && prevOption != null && completion.argument != null && (completion.options != null && completion.options.contains(prevOption))) { Object res = null; try { res = environment.evaluate(reader, line, completion.argument); } catch (Throwable t) { // Ignore } if (res instanceof Candidate) { candidates.add((Candidate) res); } else if (res instanceof String) { candidates.add(new Candidate((String) res, (String) res, null, null, null, null, true)); } else if (res instanceof Collection) { for (Object s : (Collection) res) { if (s instanceof Candidate) { candidates.add((Candidate) s); } else if (s instanceof String) { candidates.add(new Candidate((String) s, (String) s, null, null, null, null, true)); } } } else if (res != null && res.getClass().isArray()) { for (int i = 0, l = Array.getLength(res); i < l; i++) { Object s = Array.get(res, i); if (s instanceof Candidate) { candidates.add((Candidate) s); } else if (s instanceof String) { candidates.add(new Candidate((String) s, (String) s, null, null, null, null, true)); } } } } else if (!isOption && completion.argument != null) { Object res = null; try { res = environment.evaluate(reader, line, completion.argument); } catch (Throwable t) { // Ignore } if (res instanceof Candidate) { candidates.add((Candidate) res); } else if (res instanceof String) { candidates.add(new Candidate((String) res, (String) res, null, completion.description, null, null, true)); } else if (res instanceof Collection) { for (Object s : (Collection) res) { if (s instanceof Candidate) { candidates.add((Candidate) s); } else if (s instanceof String) { candidates.add(new Candidate((String) s, (String) s, null, completion.description, null, null, true)); } } } } } } @SuppressWarnings("unchecked") protected void completeCommand(List candidates) { Set commands = environment.getCommands(); for (String command : commands) { String name = environment.commandName(command); boolean resolved = command.equals(environment.resolveCommand(name)); if (!name.startsWith("_")) { String desc = null; Map> comp = environment.getCompletions(); if (comp != null) { List completions = comp.get(command); if (completions != null) { for (CompletionData completion : completions) { if (completion.description != null && completion.options == null && completion.argument == null && completion.condition == null) { desc = completion.description; } } } } String key = UUID.randomUUID().toString(); if (desc != null) { candidates.add(new Candidate(command, command, null, desc, null, key, true)); if (resolved) { candidates.add(new Candidate(name, name, null, desc, null, key, true)); } } else { candidates.add(new Candidate(command, command, null, null, null, key, true)); if (resolved) { candidates.add(new Candidate(name, name, null, null, null, key, true)); } } } } } private boolean isTrue(Object result) { if (result == null) return false; if (result instanceof Boolean) return (Boolean) result; if (result instanceof Number && 0 == ((Number) result).intValue()) { return false; } return !("".equals(result) || "0".equals(result)); } } public static class DirectoriesCompleter extends FileNameCompleter { private final Path currentDir; public DirectoriesCompleter(File currentDir) { this(currentDir.toPath()); } public DirectoriesCompleter(Path currentDir) { this.currentDir = currentDir; } @Override protected Path getUserDir() { return currentDir; } @Override protected boolean accept(Path path) { return Files.isDirectory(path) && super.accept(path); } } public static class FilesCompleter extends FileNameCompleter { private final Path currentDir; public FilesCompleter(File currentDir) { this(currentDir.toPath()); } public FilesCompleter(Path currentDir) { this.currentDir = currentDir; } @Override protected Path getUserDir() { return currentDir; } } /** * A file name completer takes the buffer and issues a list of * potential completions. *

* This completer tries to behave as similar as possible to * bash's file name completion (using GNU readline) * with the following exceptions: *

*

    *
  • Candidates that are directories will end with "/"
  • *
  • Wildcard regular expressions are not evaluated or replaced
  • *
  • The "~" character can be used to represent the user's home, * but it cannot complete to other users' homes, since java does * not provide any way of determining that easily
  • *
* * @author Marc Prud'hommeaux * @author Jason Dillon * @since 2.3 */ public static class FileNameCompleter implements org.jline.reader.Completer { public void complete(LineReader reader, ParsedLine commandLine, final List candidates) { assert commandLine != null; assert candidates != null; String buffer = commandLine.word().substring(0, commandLine.wordCursor()); Path current; String curBuf; int lastSep = buffer.lastIndexOf(File.separator); if (lastSep >= 0) { curBuf = buffer.substring(0, lastSep + 1); if (curBuf.startsWith("~")) { if (curBuf.startsWith("~/")) { current = getUserHome().resolve(curBuf.substring(2)); } else { current = getUserHome().getParent().resolve(curBuf.substring(1)); } } else { current = getUserDir().resolve(curBuf); } } else { curBuf = ""; current = getUserDir(); } try { Files.newDirectoryStream(current, this::accept).forEach(p -> { String value = curBuf + p.getFileName().toString(); if (Files.isDirectory(p)) { candidates.add(new Candidate( value + (reader.isSet(LineReader.Option.AUTO_PARAM_SLASH) ? "/" : ""), getDisplay(reader.getTerminal(), p), null, null, reader.isSet(LineReader.Option.AUTO_REMOVE_SLASH) ? "/" : null, null, false)); } else { candidates.add(new Candidate(value, getDisplay(reader.getTerminal(), p), null, null, null, null, true)); } }); } catch (IOException e) { // Ignore } } protected boolean accept(Path path) { try { return !Files.isHidden(path); } catch (IOException e) { return false; } } protected Path getUserDir() { return Paths.get(System.getProperty("user.dir")); } protected Path getUserHome() { return Paths.get(System.getProperty("user.home")); } protected String getDisplay(Terminal terminal, Path p) { // TODO: use $LS_COLORS for output String name = p.getFileName().toString(); if (Files.isDirectory(p)) { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.styled(AttributedStyle.BOLD.foreground(AttributedStyle.RED), name); sb.append("/"); name = sb.toAnsi(terminal); } else if (Files.isSymbolicLink(p)) { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.styled(AttributedStyle.BOLD.foreground(AttributedStyle.RED), name); sb.append("@"); name = sb.toAnsi(terminal); } return name; } } public static class TreeCompleter implements org.jline.reader.Completer { final Map completers = new HashMap<>(); final RegexCompleter completer; public TreeCompleter(Node... nodes) { this(Arrays.asList(nodes)); } public TreeCompleter(List nodes) { StringBuilder sb = new StringBuilder(); addRoots(sb, nodes); completer = new RegexCompleter(sb.toString(), completers::get); } public static Node node(Object... objs) { org.jline.reader.Completer comp = null; List cands = new ArrayList<>(); List nodes = new ArrayList<>(); for (Object obj : objs) { if (obj instanceof String) { cands.add(new Candidate((String) obj)); } else if (obj instanceof Candidate) { cands.add((Candidate) obj); } else if (obj instanceof Node) { nodes.add((Node) obj); } else if (obj instanceof Completer) { comp = (org.jline.reader.Completer) obj; } else { throw new IllegalArgumentException(); } } if (comp != null) { if (!cands.isEmpty()) { throw new IllegalArgumentException(); } return new Node(comp, nodes); } else if (!cands.isEmpty()) { return new Node((r, l, c) -> c.addAll(cands), nodes); } else { throw new IllegalArgumentException(); } } void addRoots(StringBuilder sb, List nodes) { if (!nodes.isEmpty()) { sb.append(" ( "); boolean first = true; for (Node n : nodes) { if (first) { first = false; } else { sb.append(" | "); } String name = "c" + completers.size(); completers.put(name, n.completer); sb.append(name); addRoots(sb, n.nodes); } sb.append(" ) "); } } @Override public void complete(LineReader reader, ParsedLine line, List candidates) { completer.complete(reader, line, candidates); } public static class Node { final org.jline.reader.Completer completer; final List nodes; public Node(org.jline.reader.Completer completer, List nodes) { this.completer = completer; this.nodes = nodes; } } } public static class RegexCompleter implements org.jline.reader.Completer { private final NfaMatcher matcher; private final Function completers; public RegexCompleter(String syntax, Function completers) { this.matcher = new NfaMatcher<>(syntax, this::doMatch); this.completers = completers; } @Override public synchronized void complete(LineReader reader, ParsedLine line, List candidates) { List words = line.words().subList(0, line.wordIndex()); Set next = matcher.matchPartial(words); for (String n : next) { completers.apply(n).complete(reader, new ArgumentLine(n, n.length()), candidates); } } private boolean doMatch(String arg, String name) { List candidates = new ArrayList<>(); completers.apply(name).complete(null, new ArgumentLine(arg, arg.length()), candidates); return candidates.stream().anyMatch(c -> c.value().equals(arg)); } public static class ArgumentLine implements ParsedLine { private final String word; private final int cursor; public ArgumentLine(String word, int cursor) { this.word = word; this.cursor = cursor; } @Override public String word() { return word; } @Override public int wordCursor() { return cursor; } @Override public int wordIndex() { return 0; } @Override public List words() { return Collections.singletonList(word); } @Override public String line() { return word; } @Override public int cursor() { return cursor; } } } } jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/Less.java000066400000000000000000000721631311544710100255660ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import java.io.BufferedReader; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.regex.Pattern; import org.jline.keymap.BindingReader; import org.jline.keymap.KeyMap; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.Terminal.Signal; import org.jline.terminal.Terminal.SignalHandler; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; import org.jline.utils.Display; import org.jline.utils.InfoCmp.Capability; import org.jline.utils.NonBlockingReader; import static org.jline.keymap.KeyMap.alt; import static org.jline.keymap.KeyMap.ctrl; import static org.jline.keymap.KeyMap.key; public class Less { private static final int ESCAPE = 27; public boolean quitAtSecondEof; public boolean quitAtFirstEof; public boolean quitIfOneScreen; public boolean printLineNumbers; public boolean quiet; public boolean veryQuiet; public boolean chopLongLines; public boolean ignoreCaseCond; public boolean ignoreCaseAlways; public boolean noKeypad; public boolean noInit; public int tabs = 4; protected final Terminal terminal; protected final Display display; protected final BindingReader bindingReader; protected List sources; protected int sourceIdx; protected BufferedReader reader; protected KeyMap keys; protected int firstLineInMemory = 0; protected List lines = new ArrayList<>(); protected int firstLineToDisplay = 0; protected int firstColumnToDisplay = 0; protected int offsetInLine = 0; protected String message; protected final StringBuilder buffer = new StringBuilder(); protected final Map options = new TreeMap<>(); protected int window; protected int halfWindow; protected int nbEof; protected String pattern; protected final Size size = new Size(); public Less(Terminal terminal) { this.terminal = terminal; this.display = new Display(terminal, true); this.bindingReader = new BindingReader(terminal.reader()); } public void handle(Signal signal) { size.copy(terminal.getSize()); try { display.clear(); display(false); } catch (IOException e) { e.printStackTrace(); } } public void run(Source... sources) throws IOException, InterruptedException { run(Arrays.asList(sources)); } public void run(List sources) throws IOException, InterruptedException { if (sources == null || sources.isEmpty()) { throw new IllegalArgumentException("No sources"); } this.sources = sources; sourceIdx = 0; openSource(); try { size.copy(terminal.getSize()); if (quitIfOneScreen && sources.size() == 1) { if (display(true)) { return; } } SignalHandler prevHandler = terminal.handle(Signal.WINCH, this::handle); Attributes attr = terminal.enterRawMode(); try { window = size.getRows() - 1; halfWindow = window / 2; keys = new KeyMap<>(); bindKeys(keys); // Use alternate buffer if (!noInit) { terminal.puts(Capability.enter_ca_mode); } if (!noKeypad) { terminal.puts(Capability.keypad_xmit); } terminal.writer().flush(); display(false); checkInterrupted(); options.put("-e", Operation.OPT_QUIT_AT_SECOND_EOF); options.put("--quit-at-eof", Operation.OPT_QUIT_AT_SECOND_EOF); options.put("-E", Operation.OPT_QUIT_AT_FIRST_EOF); options.put("-QUIT-AT-EOF", Operation.OPT_QUIT_AT_FIRST_EOF); options.put("-N", Operation.OPT_PRINT_LINES); options.put("--LINE-NUMBERS", Operation.OPT_PRINT_LINES); options.put("-q", Operation.OPT_QUIET); options.put("--quiet", Operation.OPT_QUIET); options.put("--silent", Operation.OPT_QUIET); options.put("-Q", Operation.OPT_VERY_QUIET); options.put("--QUIET", Operation.OPT_VERY_QUIET); options.put("--SILENT", Operation.OPT_VERY_QUIET); options.put("-S", Operation.OPT_CHOP_LONG_LINES); options.put("--chop-long-lines", Operation.OPT_CHOP_LONG_LINES); options.put("-i", Operation.OPT_IGNORE_CASE_COND); options.put("--ignore-case", Operation.OPT_IGNORE_CASE_COND); options.put("-I", Operation.OPT_IGNORE_CASE_ALWAYS); options.put("--IGNORE-CASE", Operation.OPT_IGNORE_CASE_ALWAYS); Operation op; do { checkInterrupted(); op = null; // // Option edition // if (buffer.length() > 0 && buffer.charAt(0) == '-') { int c = terminal.reader().read(); message = null; if (buffer.length() == 1) { buffer.append((char) c); if (c != '-') { op = options.get(buffer.toString()); if (op == null) { message = "There is no " + printable(buffer.toString()) + " option"; buffer.setLength(0); } } } else if (c == '\r') { op = options.get(buffer.toString()); if (op == null) { message = "There is no " + printable(buffer.toString()) + " option"; buffer.setLength(0); } } else { buffer.append((char) c); Map matching = new HashMap<>(); for (Map.Entry entry : options.entrySet()) { if (entry.getKey().startsWith(buffer.toString())) { matching.put(entry.getKey(), entry.getValue()); } } switch (matching.size()) { case 0: buffer.setLength(0); break; case 1: buffer.setLength(0); buffer.append(matching.keySet().iterator().next()); break; } } } // // Pattern edition // else if (buffer.length() > 0 && (buffer.charAt(0) == '/' || buffer.charAt(0) == '?')) { int c = terminal.reader().read(); message = null; if (c == '\r') { pattern = buffer.toString().substring(1); if (buffer.charAt(0) == '/') { moveToNextMatch(); } else { moveToPreviousMatch(); } buffer.setLength(0); } else { buffer.append((char) c); } } // // Command reading // else { Operation obj = bindingReader.readBinding(keys, null, false); if (obj == Operation.CHAR) { char c = bindingReader.getLastBinding().charAt(0); // Enter option mode or pattern edit mode if (c == '-' || c == '/' || c == '?') { buffer.setLength(0); } buffer.append(c); } else { op = obj; } } if (op != null) { message = null; switch (op) { case FORWARD_ONE_LINE: moveForward(getStrictPositiveNumberInBuffer(1)); break; case BACKWARD_ONE_LINE: moveBackward(getStrictPositiveNumberInBuffer(1)); break; case FORWARD_ONE_WINDOW_OR_LINES: moveForward(getStrictPositiveNumberInBuffer(window)); break; case FORWARD_ONE_WINDOW_AND_SET: window = getStrictPositiveNumberInBuffer(window); moveForward(window); break; case FORWARD_ONE_WINDOW_NO_STOP: moveForward(window); // TODO: handle no stop break; case FORWARD_HALF_WINDOW_AND_SET: halfWindow = getStrictPositiveNumberInBuffer(halfWindow); moveForward(halfWindow); break; case BACKWARD_ONE_WINDOW_AND_SET: window = getStrictPositiveNumberInBuffer(window); moveBackward(window); break; case BACKWARD_ONE_WINDOW_OR_LINES: moveBackward(getStrictPositiveNumberInBuffer(window)); break; case BACKWARD_HALF_WINDOW_AND_SET: halfWindow = getStrictPositiveNumberInBuffer(halfWindow); moveBackward(halfWindow); break; case GO_TO_FIRST_LINE_OR_N: // TODO: handle number firstLineToDisplay = firstLineInMemory; offsetInLine = 0; break; case GO_TO_LAST_LINE_OR_N: // TODO: handle number moveForward(Integer.MAX_VALUE); break; case LEFT_ONE_HALF_SCREEN: firstColumnToDisplay = Math.max(0, firstColumnToDisplay - size.getColumns() / 2); break; case RIGHT_ONE_HALF_SCREEN: firstColumnToDisplay += size.getColumns() / 2; break; case REPEAT_SEARCH_BACKWARD: case REPEAT_SEARCH_BACKWARD_SPAN_FILES: moveToPreviousMatch(); break; case REPEAT_SEARCH_FORWARD: case REPEAT_SEARCH_FORWARD_SPAN_FILES: moveToNextMatch(); break; case UNDO_SEARCH: pattern = null; break; case OPT_PRINT_LINES: buffer.setLength(0); printLineNumbers = !printLineNumbers; message = printLineNumbers ? "Constantly display line numbers" : "Don't use line numbers"; break; case OPT_QUIET: buffer.setLength(0); quiet = !quiet; veryQuiet = false; message = quiet ? "Ring the bell for errors but not at eof/bof" : "Ring the bell for errors AND at eof/bof"; break; case OPT_VERY_QUIET: buffer.setLength(0); veryQuiet = !veryQuiet; quiet = false; message = veryQuiet ? "Never ring the bell" : "Ring the bell for errors AND at eof/bof"; break; case OPT_CHOP_LONG_LINES: buffer.setLength(0); offsetInLine = 0; chopLongLines = !chopLongLines; message = chopLongLines ? "Chop long lines" : "Fold long lines"; break; case OPT_IGNORE_CASE_COND: ignoreCaseCond = !ignoreCaseCond; ignoreCaseAlways = false; message = ignoreCaseCond ? "Ignore case in searches" : "Case is significant in searches"; break; case OPT_IGNORE_CASE_ALWAYS: ignoreCaseAlways = !ignoreCaseAlways; ignoreCaseCond = false; message = ignoreCaseAlways ? "Ignore case in searches and in patterns" : "Case is significant in searches"; break; case NEXT_FILE: if (sourceIdx < sources.size() - 1) { sourceIdx++; openSource(); } else { message = "No next file"; } break; case PREV_FILE: if (sourceIdx > 0) { sourceIdx--; openSource(); } else { message = "No previous file"; } break; } buffer.setLength(0); } if (quitAtFirstEof && nbEof > 0 || quitAtSecondEof && nbEof > 1) { if (sourceIdx < sources.size() - 1) { sourceIdx++; openSource(); } else { op = Operation.EXIT; } } display(false); } while (op != Operation.EXIT); } catch (InterruptedException ie) { // Do nothing } finally { terminal.setAttributes(attr); if (prevHandler != null) { terminal.handle(Terminal.Signal.WINCH, prevHandler); } // Use main buffer if (!noInit) { terminal.puts(Capability.exit_ca_mode); } if (!noKeypad) { terminal.puts(Capability.keypad_local); } terminal.writer().flush(); } } finally { reader.close(); } } protected void openSource() throws IOException { if (reader != null) { reader.close(); } Source source = sources.get(sourceIdx); InputStream in = source.read(); if (sources.size() == 1) { message = source.getName(); } else { message = source.getName() + " (file " + (sourceIdx + 1) + " of " + sources.size() + ")"; } reader = new BufferedReader(new InputStreamReader(new InterruptibleInputStream(in))); firstLineInMemory = 0; lines = new ArrayList<>(); firstLineToDisplay = 0; firstColumnToDisplay = 0; offsetInLine = 0; } private void moveToNextMatch() throws IOException { Pattern compiled = getPattern(); if (compiled != null) { for (int lineNumber = firstLineToDisplay + 1; ; lineNumber++) { AttributedString line = getLine(lineNumber); if (line == null) { break; } else if (compiled.matcher(line).find()) { firstLineToDisplay = lineNumber; offsetInLine = 0; return; } } } message = "Pattern not found"; } private void moveToPreviousMatch() throws IOException { Pattern compiled = getPattern(); if (compiled != null) { for (int lineNumber = firstLineToDisplay - 1; lineNumber >= firstLineInMemory; lineNumber--) { AttributedString line = getLine(lineNumber); if (line == null) { break; } else if (compiled.matcher(line).find()) { firstLineToDisplay = lineNumber; offsetInLine = 0; return; } } } message = "Pattern not found"; } private String printable(String s) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == ESCAPE) { sb.append("ESC"); } else if (c < 32) { sb.append('^').append((char) (c + '@')); } else if (c < 128) { sb.append(c); } else { sb.append('\\').append(String.format("%03o", (int) c)); } } return sb.toString(); } void moveForward(int lines) throws IOException { int width = size.getColumns() - (printLineNumbers ? 8 : 0); int height = size.getRows(); while (--lines >= 0) { int lastLineToDisplay = firstLineToDisplay; if (firstColumnToDisplay > 0 || chopLongLines) { lastLineToDisplay += height - 1; } else { int off = offsetInLine; for (int l = 0; l < height - 1; l++) { AttributedString line = getLine(lastLineToDisplay); if (line == null) { break; } if (line.columnLength() > off + width) { off += width; } else { off = 0; lastLineToDisplay++; } } } if (getLine(lastLineToDisplay) == null) { eof(); return; } AttributedString line = getLine(firstLineToDisplay); if (line.columnLength() > width + offsetInLine) { offsetInLine += width; } else { offsetInLine = 0; firstLineToDisplay++; } } } void moveBackward(int lines) throws IOException { int width = size.getColumns() - (printLineNumbers ? 8 : 0); while (--lines >= 0) { if (offsetInLine > 0) { offsetInLine = Math.max(0, offsetInLine - width); } else if (firstLineInMemory < firstLineToDisplay) { firstLineToDisplay--; AttributedString line = getLine(firstLineToDisplay); int length = line.columnLength(); offsetInLine = length - length % width; } else { bof(); return; } } } private void eof() { nbEof++; if (sourceIdx < sources.size() - 1) { message = "(END) - Next: " + sources.get(sourceIdx + 1).getName(); } else { message = "(END)"; } if (!quiet && !veryQuiet && !quitAtFirstEof && !quitAtSecondEof) { terminal.puts(Capability.bell); terminal.writer().flush(); } } private void bof() { if (!quiet && !veryQuiet) { terminal.puts(Capability.bell); terminal.writer().flush(); } } int getStrictPositiveNumberInBuffer(int def) { try { int n = Integer.parseInt(buffer.toString()); return (n > 0) ? n : def; } catch (NumberFormatException e) { return def; } finally { buffer.setLength(0); } } boolean display(boolean oneScreen) throws IOException { List newLines = new ArrayList<>(); int width = size.getColumns() - (printLineNumbers ? 8 : 0); int height = size.getRows(); int inputLine = firstLineToDisplay; AttributedString curLine = null; Pattern compiled = getPattern(); boolean fitOnOneScreen = false; for (int terminalLine = 0; terminalLine < height - 1; terminalLine++) { if (curLine == null) { curLine = getLine(inputLine++); if (curLine == null) { if (oneScreen) { fitOnOneScreen = true; break; } curLine = new AttributedString(""); } if (compiled != null) { curLine = curLine.styleMatches(compiled, AttributedStyle.DEFAULT.inverse()); } } AttributedString toDisplay; if (firstColumnToDisplay > 0 || chopLongLines) { int off = firstColumnToDisplay; if (terminalLine == 0 && offsetInLine > 0) { off = Math.max(offsetInLine, off); } toDisplay = curLine.columnSubSequence(off, off + width); curLine = null; } else { if (terminalLine == 0 && offsetInLine > 0) { curLine = curLine.columnSubSequence(offsetInLine, Integer.MAX_VALUE); } toDisplay = curLine.columnSubSequence(0, width); curLine = curLine.columnSubSequence(width, Integer.MAX_VALUE); if (curLine.length() == 0) { curLine = null; } } if (printLineNumbers) { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.append(String.format("%7d ", inputLine)); sb.append(toDisplay); newLines.add(sb.toAttributedString()); } else { newLines.add(toDisplay); } } if (oneScreen) { if (fitOnOneScreen) { newLines.forEach(l -> terminal.writer().println(l.toAnsi(terminal))); } return fitOnOneScreen; } AttributedStringBuilder msg = new AttributedStringBuilder(); if (buffer.length() > 0) { msg.append(" ").append(buffer); } else if (bindingReader.getCurrentBuffer().length() > 0 && terminal.reader().peek(1) == NonBlockingReader.READ_EXPIRED) { msg.append(" ").append(printable(bindingReader.getCurrentBuffer())); } else if (message != null) { msg.style(AttributedStyle.INVERSE); msg.append(message); msg.style(AttributedStyle.INVERSE.inverseOff()); } else { msg.append(":"); } newLines.add(msg.toAttributedString()); display.resize(size.getRows(), size.getColumns()); display.update(newLines, -1); return false; } private Pattern getPattern() { Pattern compiled = null; if (pattern != null) { boolean insensitive = ignoreCaseAlways || ignoreCaseCond && pattern.toLowerCase().equals(pattern); compiled = Pattern.compile("(" + pattern + ")", insensitive ? Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE : 0); } return compiled; } AttributedString getLine(int line) throws IOException { while (line >= lines.size()) { String str = reader.readLine(); if (str != null) { lines.add(AttributedString.fromAnsi(str, tabs)); } else { break; } } if (line < lines.size()) { return lines.get(line); } return null; } /** * This is for long running commands to be interrupted by ctrl-c * * @throws InterruptedException */ public static void checkInterrupted() throws InterruptedException { Thread.yield(); if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } } private void bindKeys(KeyMap map) { map.bind(Operation.HELP, "h", "H"); map.bind(Operation.EXIT, "q", ":q", "Q", ":Q", "ZZ"); map.bind(Operation.FORWARD_ONE_LINE, "e", ctrl('E'), "j", ctrl('N'), "\r", key(terminal, Capability.key_down)); map.bind(Operation.BACKWARD_ONE_LINE, "y", ctrl('Y'), "k", ctrl('K'), ctrl('P'), key(terminal, Capability.key_up)); map.bind(Operation.FORWARD_ONE_WINDOW_OR_LINES, "f", ctrl('F'), ctrl('V'), " "); map.bind(Operation.BACKWARD_ONE_WINDOW_OR_LINES, "b", ctrl('B'), alt('v')); map.bind(Operation.FORWARD_ONE_WINDOW_AND_SET, "z"); map.bind(Operation.BACKWARD_ONE_WINDOW_AND_SET, "w"); map.bind(Operation.FORWARD_ONE_WINDOW_NO_STOP, alt(' ')); map.bind(Operation.FORWARD_HALF_WINDOW_AND_SET, "d", ctrl('D')); map.bind(Operation.BACKWARD_HALF_WINDOW_AND_SET, "u", ctrl('U')); map.bind(Operation.RIGHT_ONE_HALF_SCREEN, alt(')'), key(terminal, Capability.key_right)); map.bind(Operation.LEFT_ONE_HALF_SCREEN, alt('('), key(terminal, Capability.key_left)); map.bind(Operation.FORWARD_FOREVER, "F"); map.bind(Operation.REPEAT_SEARCH_FORWARD, "n", "N"); map.bind(Operation.REPEAT_SEARCH_FORWARD_SPAN_FILES, alt('n'), alt('N')); map.bind(Operation.UNDO_SEARCH, alt('u')); map.bind(Operation.GO_TO_FIRST_LINE_OR_N, "g", "<", alt('<')); map.bind(Operation.GO_TO_LAST_LINE_OR_N, "G", ">", alt('>')); map.bind(Operation.NEXT_FILE, ":n"); map.bind(Operation.PREV_FILE, ":p"); "-/0123456789?".chars().forEach(c -> map.bind(Operation.CHAR, Character.toString((char) c))); } protected enum Operation { // General HELP, EXIT, // Moving FORWARD_ONE_LINE, BACKWARD_ONE_LINE, FORWARD_ONE_WINDOW_OR_LINES, BACKWARD_ONE_WINDOW_OR_LINES, FORWARD_ONE_WINDOW_AND_SET, BACKWARD_ONE_WINDOW_AND_SET, FORWARD_ONE_WINDOW_NO_STOP, FORWARD_HALF_WINDOW_AND_SET, BACKWARD_HALF_WINDOW_AND_SET, LEFT_ONE_HALF_SCREEN, RIGHT_ONE_HALF_SCREEN, FORWARD_FOREVER, REPAINT, REPAINT_AND_DISCARD, // Searching REPEAT_SEARCH_FORWARD, REPEAT_SEARCH_BACKWARD, REPEAT_SEARCH_FORWARD_SPAN_FILES, REPEAT_SEARCH_BACKWARD_SPAN_FILES, UNDO_SEARCH, // Jumping GO_TO_FIRST_LINE_OR_N, GO_TO_LAST_LINE_OR_N, GO_TO_PERCENT_OR_N, GO_TO_NEXT_TAG, GO_TO_PREVIOUS_TAG, FIND_CLOSE_BRACKET, FIND_OPEN_BRACKET, // Options OPT_PRINT_LINES, OPT_CHOP_LONG_LINES, OPT_QUIT_AT_FIRST_EOF, OPT_QUIT_AT_SECOND_EOF, OPT_QUIET, OPT_VERY_QUIET, OPT_IGNORE_CASE_COND, OPT_IGNORE_CASE_ALWAYS, // Files NEXT_FILE, PREV_FILE, // CHAR } static class InterruptibleInputStream extends FilterInputStream { InterruptibleInputStream(InputStream in) { super(in); } @Override public int read(byte[] b, int off, int len) throws IOException { if (Thread.currentThread().isInterrupted()) { throw new InterruptedIOException(); } return super.read(b, off, len); } } } jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/Nano.java000066400000000000000000002237421311544710100255540ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jline.keymap.BindingReader; import org.jline.keymap.KeyMap; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.MouseEvent; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.Terminal.Signal; import org.jline.terminal.Terminal.SignalHandler; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; import org.jline.utils.Display; import org.jline.utils.InfoCmp.Capability; import org.mozilla.universalchardet.UniversalDetector; import static org.jline.keymap.KeyMap.KEYMAP_LENGTH; import static org.jline.keymap.KeyMap.alt; import static org.jline.keymap.KeyMap.ctrl; import static org.jline.keymap.KeyMap.del; import static org.jline.keymap.KeyMap.key; public class Nano { // Final fields protected final Terminal terminal; protected final Display display; protected final BindingReader bindingReader; protected final Size size; protected final Path root; // Keys protected KeyMap keys; // Configuration public String title = "JLine Nano 3.0.0"; public boolean printLineNumbers = true; public boolean wrapping = true; public boolean smoothScrolling = true; public boolean mouseSupport = false; public boolean oneMoreLine = true; public boolean constantCursor; public int tabs = 4; public String brackets = "\"’)>]}"; public String matchBrackets = "(<[{)>]}"; public String punct = "!.?"; public String quoteStr = "^([ \\t]*[#:>\\|}])+"; // Input protected final List buffers = new ArrayList<>(); protected int bufferIndex; protected Buffer buffer; protected String message; protected int nbBindings = 0; protected LinkedHashMap shortcuts; protected String editMessage; protected final StringBuilder editBuffer = new StringBuilder(); protected boolean searchCaseSensitive; protected boolean searchRegexp; protected boolean searchBackwards; protected String searchTerm; protected WriteMode writeMode = WriteMode.WRITE; protected boolean writeBackup; protected boolean readNewBuffer = true; protected enum WriteMode { WRITE, APPEND, PREPEND } protected enum WriteFormat { UNIX, DOS, MAC } protected class Buffer { String file; Charset charset; WriteFormat format = WriteFormat.UNIX; List lines; int firstLineToDisplay; int firstColumnToDisplay; int offsetInLineToDisplay; int line; List> offsets = new ArrayList<>(); int offsetInLine; int column; int wantedColumn; boolean dirty; protected Buffer(String file) { this.file = file; } void open() throws IOException { if (lines != null) { return; } lines = new ArrayList<>(); lines.add(""); charset = Charset.defaultCharset(); computeAllOffsets(); if (file == null) { return; } Path path = root.resolve(file); if (Files.isDirectory(path)) { setMessage("\"" + file + "\" is a directory"); return; } try (InputStream fis = Files.newInputStream(path)) { read(fis); } catch (IOException e) { setMessage("Error reading " + file + ": " + e.getMessage()); } } void open(InputStream is) throws IOException { if (lines != null) { return; } lines = new ArrayList<>(); lines.add(""); charset = Charset.defaultCharset(); computeAllOffsets(); read(is); } void read(InputStream fis) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int remaining; while ((remaining = fis.read(buffer)) > 0) { bos.write(buffer, 0, remaining); } byte[] bytes = bos.toByteArray(); try { UniversalDetector detector = new UniversalDetector(null); detector.handleData(bytes, 0, bytes.length); detector.dataEnd(); if (detector.getDetectedCharset() != null) { charset = Charset.forName(detector.getDetectedCharset()); } } catch (Throwable t) { // Ignore } // TODO: detect format, do not eat last newline try (BufferedReader reader = new BufferedReader( new InputStreamReader(new ByteArrayInputStream(bytes), charset))) { String line; lines.clear(); while ((line = reader.readLine()) != null) { lines.add(line); } } if (lines.isEmpty()) { lines.add(""); } computeAllOffsets(); moveToChar(0); } void insert(String insert) { String text = lines.get(line); int pos = offsetInLine + column; insert = insert.replaceAll("\r\n", "\n"); insert = insert.replaceAll("\r", "\n"); String mod; if (pos == text.length()) { mod = text + insert; } else { mod = text.substring(0, pos) + insert + text.substring(pos); } List ins = new ArrayList<>(); int last = 0; int idx = mod.indexOf('\n', last); while (idx >= 0) { ins.add(mod.substring(last, idx)); last = idx + 1; idx = mod.indexOf('\n', last); } ins.add(mod.substring(last)); lines.set(line, ins.get(0)); offsets.set(line, computeOffsets(ins.get(0))); for (int i = 1; i < ins.size(); i++) { ++line; lines.add(line, ins.get(i)); offsets.add(line, computeOffsets(ins.get(i))); } moveToChar(ins.get(ins.size() - 1).length() - (text.length() - pos)); dirty = true; } void computeAllOffsets() { offsets.clear(); for (String text : lines) { offsets.add(computeOffsets(text)); } } LinkedList computeOffsets(String text) { int width = size.getColumns() - (printLineNumbers ? 8 : 0); LinkedList offsets = new LinkedList<>(); offsets.add(0); int last = 0; int prevword = 0; boolean inspace = false; for (int i = 0; i < text.length(); i++) { if (isBreakable(text.charAt(i))) { inspace = true; } else if (inspace) { prevword = i; inspace = false; } if (i == last + width - 1) { if (prevword == last) { prevword = i; } offsets.add(prevword); last = prevword; } } return offsets; } boolean isBreakable(char ch) { return ch == ' '; } void moveToChar(int pos) { offsetInLine = prevLineOffset(line, pos + 1).get(); column = pos - offsetInLine; } void delete(int count) { while (--count >= 0 && moveRight(1) && backspace(1)); } boolean backspace(int count) { while (count > 0) { String text = lines.get(line); int pos = offsetInLine + column; if (pos == 0) { if (line == 0) { bof(); return false; } String prev = lines.get(--line); lines.set(line, prev + text); offsets.set(line, computeOffsets(prev + text)); moveToChar(length(prev, tabs)); lines.remove(line + 1); offsets.remove(line + 1); count--; dirty = true; } else { int nb = Math.min(pos, count); text = text.substring(0, pos - nb) + text.substring(pos); lines.set(line, text); offsets.set(line, computeOffsets(text)); moveToChar(offsetInLine + column - nb); count -= nb; dirty = true; } } return true; } boolean moveLeft(int chars) { boolean ret = true; while (--chars >= 0) { if (offsetInLine + column > 0) { moveToChar(offsetInLine + column - 1); } else if (line > 0) { line--; moveToChar(length(getLine(line), tabs)); } else { bof(); ret = false; break; } } wantedColumn = column; ensureCursorVisible(); return ret; } boolean moveRight(int chars) { boolean ret = true; while (--chars >= 0) { int len = length(getLine(line), tabs); if (offsetInLine + column + 1 <= len) { moveToChar(offsetInLine + column + 1); } else if (getLine(line + 1) != null) { line++; offsetInLine = 0; column = 0; } else { eof(); ret = false; break; } } wantedColumn = column; ensureCursorVisible(); return ret; } void moveDown(int lines) { cursorDown(lines); ensureCursorVisible(); } void moveUp(int lines) { cursorUp(lines); ensureCursorVisible(); } private Optional prevLineOffset(int line, int offsetInLine) { if (line >= offsets.size()) { return Optional.empty(); } Iterator it = offsets.get(line).descendingIterator(); while (it.hasNext()) { int off = it.next(); if (off < offsetInLine) { return Optional.of(off); } } return Optional.empty(); } private Optional nextLineOffset(int line, int offsetInLine) { if (line >= offsets.size()) { return Optional.empty(); } return offsets.get(line).stream() .filter(o -> o > offsetInLine) .findFirst(); } void moveDisplayDown(int lines) { int height = size.getRows() - computeHeader().size() - computeFooter().size(); // Adjust cursor while (--lines >= 0) { int lastLineToDisplay = firstLineToDisplay; if (firstColumnToDisplay > 0 || !wrapping) { lastLineToDisplay += height - 1; } else { int off = offsetInLineToDisplay; for (int l = 0; l < height - 1; l++) { Optional next = nextLineOffset(lastLineToDisplay, off); if (next.isPresent()) { off = next.get(); } else { off = 0; lastLineToDisplay++; } } } if (getLine(lastLineToDisplay) == null) { eof(); return; } Optional next = nextLineOffset(firstLineToDisplay, offsetInLineToDisplay); if (next.isPresent()) { offsetInLineToDisplay = next.get(); } else { offsetInLineToDisplay = 0; firstLineToDisplay++; } } } void moveDisplayUp(int lines) { int width = size.getColumns() - (printLineNumbers ? 8 : 0); while (--lines >= 0) { if (offsetInLineToDisplay > 0) { offsetInLineToDisplay = Math.max(0, offsetInLineToDisplay - (width - 1)); } else if (firstLineToDisplay > 0) { firstLineToDisplay--; offsetInLineToDisplay = prevLineOffset(firstLineToDisplay, Integer.MAX_VALUE).get(); } else { bof(); return; } } } private void cursorDown(int lines) { // Adjust cursor while (--lines >= 0) { if (firstColumnToDisplay > 0 || !wrapping) { if (getLine(line + 1) != null) { line++; offsetInLine = 0; column = Math.min(getLine(line).length(), wantedColumn); } else { bof(); break; } } else { String txt = getLine(line); Optional off = nextLineOffset(line, offsetInLine); if (off.isPresent()) { offsetInLine = off.get(); } else if (getLine(line + 1) == null) { eof(); break; } else { line++; offsetInLine = 0; txt = getLine(line); } String curLine = txt; int next = nextLineOffset(line, offsetInLine).orElseGet(curLine::length); column = Math.min(wantedColumn, next - offsetInLine); } } } private void cursorUp(int lines) { while (--lines >= 0) { if (firstColumnToDisplay > 0 || !wrapping) { if (line > 0) { line--; column = Math.min(length(getLine(line), tabs) - offsetInLine, wantedColumn); } else { bof(); break; } } else { Optional prev = prevLineOffset(line, offsetInLine); if (prev.isPresent()) { offsetInLine = prev.get(); } else if (line > 0) { line--; offsetInLine = prevLineOffset(line, Integer.MAX_VALUE).get(); int next = nextLineOffset(line, offsetInLine).orElse(getLine(line).length()); column = Math.min(wantedColumn, next - offsetInLine); } else { bof(); break; } } } } void ensureCursorVisible() { List header = computeHeader(); int rwidth = size.getColumns(); int height = size.getRows() - header.size() - computeFooter().size(); while (line < firstLineToDisplay || line == firstLineToDisplay && offsetInLine < offsetInLineToDisplay) { moveDisplayUp(smoothScrolling ? 1 : height / 2); } while (true) { int cursor = header.size() * size.getColumns() + (printLineNumbers ? 8 : 0); int cur = firstLineToDisplay; int off = offsetInLineToDisplay; while (true) { if (cur < line || off < offsetInLine) { if (firstColumnToDisplay > 0 || !wrapping) { cursor += rwidth; cur++; } else { cursor += rwidth; Optional next = nextLineOffset(cur, off); if (next.isPresent()) { off = next.get(); } else { cur++; off = 0; } } } else if (cur == line) { cursor += column; break; } else { throw new IllegalStateException(); } } if (cursor >= (height + header.size()) * rwidth) { moveDisplayDown(smoothScrolling ? 1 : height / 2); } else { break; } } } void eof() { } void bof() { } void resetDisplay() { int width = size.getColumns() - (printLineNumbers ? 8 : 0); column = offsetInLine + column; offsetInLine = (column / width) * (width - 1); column = column - offsetInLine; } String getLine(int line) { return line < lines.size() ? lines.get(line) : null; } String getTitle() { return file != null ? "File: " + file : "New Buffer"; } List computeHeader() { String left = Nano.this.getTitle(); String middle = null; String right = dirty ? "Modified" : " "; int width = size.getColumns(); int mstart = 2 + left.length() + 1; int mend = width - 2 - 8; if (file == null) { middle = "New Buffer"; } else { int max = mend - mstart; String src = file; if ("File: ".length() + src.length() > max) { int lastSep = src.lastIndexOf('/'); if (lastSep > 0) { String p1 = src.substring(lastSep); String p0 = src.substring(0, lastSep); while (p0.startsWith(".")) { p0 = p0.substring(1); } int nb = max - p1.length() - "File: ...".length(); int cut; cut = Math.max(0, Math.min(p0.length(), p0.length() - nb)); middle = "File: ..." + p0.substring(cut, p0.length()) + p1; } if (middle == null || middle.length() > max) { left = null; max = mend - 2; int nb = max - "File: ...".length(); int cut = Math.max(0, Math.min(src.length(), src.length() - nb)); middle = "File: ..." + src.substring(cut, src.length()); if (middle.length() > max) { middle = middle.substring(0, max); } } } else { middle = "File: " + src; } } int pos = 0; AttributedStringBuilder sb = new AttributedStringBuilder(); sb.style(AttributedStyle.INVERSE); sb.append(" "); pos += 2; if (left != null) { sb.append(left); pos += left.length(); sb.append(" "); pos += 1; for (int i = 1; i < (size.getColumns() - middle.length()) / 2 - left.length() - 1 - 2; i++) { sb.append(" "); pos++; } } sb.append(middle); pos += middle.length(); while (pos < width - 8 - 2) { sb.append(" "); pos++; } sb.append(right); sb.append(" \n"); if (oneMoreLine) { return Collections.singletonList(sb.toAttributedString()); } else { return Arrays.asList(sb.toAttributedString(), new AttributedString("\n")); } } List getDisplayedLines(int nbLines) { AttributedStyle s = AttributedStyle.DEFAULT.foreground(AttributedStyle.BLACK + AttributedStyle.BRIGHT); AttributedString cut = new AttributedString("…", s); AttributedString ret = new AttributedString("↩", s); List newLines = new ArrayList<>(); int rwidth = size.getColumns(); int width = rwidth - (printLineNumbers ? 8 : 0); int curLine = firstLineToDisplay; int curOffset = offsetInLineToDisplay; int prevLine = -1; for (int terminalLine = 0; terminalLine < nbLines; terminalLine++) { AttributedStringBuilder line = new AttributedStringBuilder().tabs(tabs); if (printLineNumbers && curLine < lines.size()) { line.style(s); if (curLine != prevLine) { line.append(String.format("%7d ", curLine + 1)); } else { line.append(" ‧ "); } line.style(AttributedStyle.DEFAULT); prevLine = curLine; } if (curLine >= lines.size()) { // Nothing to do } else if (firstColumnToDisplay > 0 || !wrapping) { AttributedString disp = new AttributedString(getLine(curLine)); disp = disp.columnSubSequence(firstColumnToDisplay, Integer.MAX_VALUE); if (disp.columnLength() >= width) { line.append(disp.columnSubSequence(0, width - cut.columnLength())); line.append(cut); } else { line.append(disp); } curLine++; } else { Optional nextOffset = nextLineOffset(curLine, curOffset); if (nextOffset.isPresent()) { AttributedString disp = new AttributedString(getLine(curLine)); line.append(disp.columnSubSequence(curOffset, nextOffset.get())); line.append(ret); curOffset = nextOffset.get(); } else { AttributedString disp = new AttributedString(getLine(curLine)); line.append(disp.columnSubSequence(curOffset, Integer.MAX_VALUE)); curLine++; curOffset = 0; } } line.append('\n'); newLines.add(line.toAttributedString()); } return newLines; } public void moveTo(int x, int y) { if (printLineNumbers) { x = Math.max(x - 8, 0); } line = firstLineToDisplay; offsetInLine = offsetInLineToDisplay; wantedColumn = x; cursorDown(y); } public int getDisplayedCursor() { int rwidth = size.getColumns() + 1; int cursor = (printLineNumbers ? 8 : 0); int cur = firstLineToDisplay; int off = offsetInLineToDisplay; while (true) { if (cur < line || off < offsetInLine) { if (firstColumnToDisplay > 0 || !wrapping) { cursor += rwidth; cur++; } else { cursor += rwidth; Optional next = nextLineOffset(cur, off); if (next.isPresent()) { off = next.get(); } else { cur++; off = 0; } } } else if (cur == line) { cursor += column; break; } else { throw new IllegalStateException(); } } return cursor; } char getCurrentChar() { String str = lines.get(line); if (column + offsetInLine < str.length()) { return str.charAt(column + offsetInLine); } else if (line < lines.size() - 1) { return '\n'; } else { return 0; } } @SuppressWarnings("StatementWithEmptyBody") public void prevWord() { while (Character.isAlphabetic(getCurrentChar()) && moveLeft(1)); while (!Character.isAlphabetic(getCurrentChar()) && moveLeft(1)); while (Character.isAlphabetic(getCurrentChar()) && moveLeft(1)); moveRight(1); } @SuppressWarnings("StatementWithEmptyBody") public void nextWord() { while (Character.isAlphabetic(getCurrentChar()) && moveRight(1)); while (!Character.isAlphabetic(getCurrentChar()) && moveRight(1)); } public void beginningOfLine() { column = offsetInLine = 0; wantedColumn = 0; } public void endOfLine() { column = length(lines.get(line), tabs); int width = size.getColumns() - (printLineNumbers ? 8 : 0); offsetInLine = (column / width) * (width - 1); column = column - offsetInLine; wantedColumn = column; } public void prevPage() { int height = size.getRows() - computeHeader().size() - computeFooter().size(); scrollUp(height - 2); } public void nextPage() { int height = size.getRows() - computeHeader().size() - computeFooter().size(); scrollDown(height - 2); } public void scrollUp(int lines) { cursorUp(lines); moveDisplayUp(lines); } public void scrollDown(int lines) { cursorDown(lines); moveDisplayDown(lines); } public void firstLine() { line = 0; offsetInLine = column = 0; ensureCursorVisible(); } public void lastLine() { line = lines.size() - 1; offsetInLine = column = 0; ensureCursorVisible(); } void nextSearch() { if (searchTerm == null) { setMessage("No current search pattern"); return; } setMessage(null); int cur = line; int dir = searchBackwards ? -1 : +1; int newPos = -1; int newLine = -1; // Search on current line List curRes = doSearch(lines.get(line)); if (searchBackwards) { Collections.reverse(curRes); } for (int r : curRes) { if (searchBackwards ? r < offsetInLine + column : r > offsetInLine + column) { newPos = r; newLine = line; break; } } // Check other lines if (newPos < 0) { while (true) { cur = (cur + dir + lines.size()) % lines.size(); if (cur == line) { break; } List res = doSearch(lines.get(cur)); if (!res.isEmpty()) { newPos = searchBackwards ? res.get(res.size() - 1) : res.get(0); newLine = cur; break; } } } if (newPos < 0) { if (!curRes.isEmpty()) { newPos = curRes.get(0); newLine = line; } } if (newPos >= 0) { if (newLine == line && newPos == offsetInLine + column) { setMessage("This is the only occurence"); return; } if ((searchBackwards && (newLine > line || (newLine == line && newPos > offsetInLine + column))) || (!searchBackwards && (newLine < line || (newLine == line && newPos < offsetInLine + column)))) { setMessage("Search Wrapped"); } int width = size.getColumns() - (printLineNumbers ? 8 : 0); line = newLine; column = newPos; offsetInLine = (column / width) * (width - 1); ensureCursorVisible(); } else { setMessage("\"" + searchTerm + "\" not found"); } } private List doSearch(String text) { Pattern pat = Pattern.compile(searchTerm, (searchCaseSensitive ? 0 : Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) | (searchRegexp ? 0 : Pattern.LITERAL)); Matcher m = pat.matcher(text); List res = new ArrayList<>(); while (m.find()) { res.add(m.start()); } return res; } public void matching() { int opening = getCurrentChar(); int idx = matchBrackets.indexOf(opening); if (idx >= 0) { int dir = (idx >= matchBrackets.length() / 2) ? -1 : +1; int closing = matchBrackets.charAt((idx + matchBrackets.length() / 2) % matchBrackets.length()); int lvl = 1; int cur = line; int pos = offsetInLine + column; while (true) { if ((pos + dir >= 0) && (pos + dir < getLine(cur).length())) { pos += dir; } else if ((cur + dir >= 0) && (cur + dir < lines.size())) { cur += dir; pos = dir > 0 ? 0 : lines.get(cur).length() - 1; // Skip empty lines if (pos < 0 || pos >= lines.get(cur).length()) { continue; } } else { setMessage("No matching bracket"); return; } int c = lines.get(cur).charAt(pos); if (c == opening) { lvl++; } else if (c == closing) { if (--lvl == 0) { line = cur; moveToChar(pos); ensureCursorVisible(); return; } } } } else { setMessage("Not a bracket"); } } private int length(String line, int tabs) { return new AttributedStringBuilder().tabs(tabs).append(line).columnLength(); } } public Nano(Terminal terminal, File root) { this(terminal, root.toPath()); } public Nano(Terminal terminal, Path root) { this.terminal = terminal; this.root = root; this.display = new Display(terminal, true); this.bindingReader = new BindingReader(terminal.reader()); this.size = new Size(); bindKeys(); } public void open(String... files) throws IOException { open(Arrays.asList(files)); } public void open(List files) throws IOException { for (String file : files) { buffers.add(new Buffer(file)); } } public void run() throws IOException { if (buffers.isEmpty()) { buffers.add(new Buffer(null)); } buffer = buffers.get(bufferIndex); Attributes attributes = terminal.getAttributes(); Attributes newAttr = new Attributes(attributes); newAttr.setLocalFlags(EnumSet.of(LocalFlag.ICANON, LocalFlag.ECHO, LocalFlag.IEXTEN), false); newAttr.setInputFlags(EnumSet.of(InputFlag.IXON, InputFlag.ICRNL, InputFlag.INLCR), false); newAttr.setControlChar(ControlChar.VMIN, 1); newAttr.setControlChar(ControlChar.VTIME, 0); newAttr.setControlChar(ControlChar.VINTR, 0); terminal.setAttributes(newAttr); SignalHandler prevHandler = terminal.handle(Signal.WINCH, this::handle); terminal.puts(Capability.enter_ca_mode); terminal.puts(Capability.keypad_xmit); size.copy(terminal.getSize()); display.clear(); display.reset(); display.resize(size.getRows(), size.getColumns()); if (mouseSupport) { terminal.trackMouse(Terminal.MouseTracking.Normal); } this.shortcuts = standardShortcuts(); try { buffer.open(); if (buffer.file != null) { setMessage("Read " + buffer.lines.size() + " lines"); } display(); while (true) { Operation op; switch (op = readOperation(keys)) { case QUIT: if (quit()) { return; } break; case WRITE: write(); break; case READ: read(); break; case UP: buffer.moveUp(1); break; case DOWN: buffer.moveDown(1); break; case LEFT: buffer.moveLeft(1); break; case RIGHT: buffer.moveRight(1); break; case INSERT: buffer.insert(bindingReader.getLastBinding()); break; case BACKSPACE: buffer.backspace(1); break; case DELETE: buffer.delete(1); break; case WRAP: wrap(); break; case NUMBERS: numbers(); break; case SMOOTH_SCROLLING: smoothScrolling(); break; case MOUSE_SUPPORT: mouseSupport(); break; case ONE_MORE_LINE: oneMoreLine(); break; case CLEAR_SCREEN: clearScreen(); break; case PREV_BUFFER: prevBuffer(); break; case NEXT_BUFFER: nextBuffer(); break; case CUR_POS: curPos(); break; case PREV_WORD: buffer.prevWord(); break; case NEXT_WORD: buffer.nextWord(); break; case BEGINNING_OF_LINE: buffer.beginningOfLine(); break; case END_OF_LINE: buffer.endOfLine(); break; case FIRST_LINE: buffer.firstLine(); break; case LAST_LINE: buffer.lastLine(); break; case PREV_PAGE: buffer.prevPage(); break; case NEXT_PAGE: buffer.nextPage(); break; case SCROLL_UP: buffer.scrollUp(1); break; case SCROLL_DOWN: buffer.scrollDown(1); break; case SEARCH: search(); break; case NEXT_SEARCH: buffer.nextSearch(); break; case HELP: help("nano-main-help.txt"); break; case CONSTANT_CURSOR: constantCursor(); break; case VERBATIM: buffer.insert(new String(Character.toChars(bindingReader.readCharacter()))); break; case MATCHING: buffer.matching(); break; case MOUSE_EVENT: mouseEvent(); break; default: setMessage("Unsupported " + op.name().toLowerCase().replace('_', '-')); break; } display(); } } finally { if (mouseSupport) { terminal.trackMouse(Terminal.MouseTracking.Off); } terminal.puts(Capability.exit_ca_mode); terminal.puts(Capability.keypad_local); terminal.flush(); terminal.setAttributes(attributes); terminal.handle(Signal.WINCH, prevHandler); } } boolean write() throws IOException { KeyMap writeKeyMap = new KeyMap<>(); writeKeyMap.setUnicode(Operation.INSERT); for (char i = 32; i < 256; i++) { writeKeyMap.bind(Operation.INSERT, Character.toString(i)); } for (char i = 'A'; i <= 'Z'; i++) { writeKeyMap.bind(Operation.DO_LOWER_CASE, alt(i)); } writeKeyMap.bind(Operation.BACKSPACE, del()); writeKeyMap.bind(Operation.MAC_FORMAT, alt('m')); writeKeyMap.bind(Operation.DOS_FORMAT, alt('d')); writeKeyMap.bind(Operation.APPEND_MODE, alt('a')); writeKeyMap.bind(Operation.PREPEND_MODE, alt('p')); writeKeyMap.bind(Operation.BACKUP, alt('b')); writeKeyMap.bind(Operation.TO_FILES, ctrl('T')); writeKeyMap.bind(Operation.ACCEPT, "\r"); writeKeyMap.bind(Operation.CANCEL, ctrl('C')); writeKeyMap.bind(Operation.HELP, ctrl('G'), key(terminal, Capability.key_f1)); writeKeyMap.bind(Operation.MOUSE_EVENT, key(terminal, Capability.key_mouse)); editMessage = getWriteMessage(); editBuffer.setLength(0); editBuffer.append(buffer.file == null ? "" : buffer.file); this.shortcuts = writeShortcuts(); display(); while (true) { switch (readOperation(writeKeyMap)) { case INSERT: editBuffer.append(bindingReader.getLastBinding()); break; case BACKSPACE: if (editBuffer.length() > 0) { editBuffer.setLength(editBuffer.length() - 1); } break; case CANCEL: editMessage = null; this.shortcuts = standardShortcuts(); return false; case ACCEPT: editMessage = null; if (save(editBuffer.toString())) { this.shortcuts = standardShortcuts(); return true; } return false; case HELP: help("nano-write-help.txt"); break; case MAC_FORMAT: buffer.format = (buffer.format == WriteFormat.MAC) ? WriteFormat.UNIX : WriteFormat.MAC; break; case DOS_FORMAT: buffer.format = (buffer.format == WriteFormat.DOS) ? WriteFormat.UNIX : WriteFormat.DOS; break; case APPEND_MODE: writeMode = (writeMode == WriteMode.APPEND) ? WriteMode.WRITE : WriteMode.APPEND; break; case PREPEND_MODE: writeMode = (writeMode == WriteMode.PREPEND) ? WriteMode.WRITE : WriteMode.PREPEND; break; case BACKUP: writeBackup = !writeBackup; break; case MOUSE_EVENT: mouseEvent(); break; } editMessage = getWriteMessage(); display(); } } private Operation readOperation(KeyMap keymap) { while (true) { Operation op = bindingReader.readBinding(keymap); if (op == Operation.DO_LOWER_CASE) { bindingReader.runMacro(bindingReader.getLastBinding().toLowerCase()); } else { return op; } } } private boolean save(String name) throws IOException { Path orgPath = buffer.file != null ? root.resolve(buffer.file) : null; Path newPath = root.resolve(name); boolean isSame = orgPath != null && Files.isSameFile(orgPath, newPath); if (!isSame && Files.exists(Paths.get(name))) { Operation op = getYNC("File exists, OVERWRITE ? "); if (op != Operation.YES) { return false; } } // TODO: support backup / prepend / append Path t = Files.createTempFile(newPath.getParent(), "jline-", ".temp"); try (OutputStream os = Files.newOutputStream(t, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { if (writeMode == WriteMode.APPEND) { if (Files.isReadable(newPath)) { Files.copy(newPath, os); } } Writer w = new OutputStreamWriter(os, buffer.charset); for (int i = 0; i < buffer.lines.size(); i++) { if (i > 0) { switch (buffer.format) { case UNIX: w.write("\n"); break; case DOS: w.write("\r\n"); break; case MAC: w.write("\r"); break; } } w.write(buffer.lines.get(i)); } w.flush(); if (writeMode == WriteMode.PREPEND) { if (Files.isReadable(newPath)) { Files.copy(newPath, os); } } if (writeBackup) { Files.move(newPath, newPath.resolveSibling(newPath.getFileName().toString() + "~"), StandardCopyOption.REPLACE_EXISTING); } Files.move(t, newPath, StandardCopyOption.REPLACE_EXISTING); buffer.file = name; buffer.dirty = false; setMessage("Wrote " + buffer.lines.size() + " lines"); return true; } catch (IOException e) { setMessage("Error writing " + name + ": " + e.toString()); return false; } finally { Files.deleteIfExists(t); } } private Operation getYNC(String message) { String oldEditMessage = editMessage; String oldEditBuffer = editBuffer.toString(); LinkedHashMap oldShortcuts = shortcuts; try { editMessage = message; editBuffer.setLength(0); KeyMap yncKeyMap = new KeyMap<>(); yncKeyMap.bind(Operation.YES, "y", "Y"); yncKeyMap.bind(Operation.NO, "n", "N"); yncKeyMap.bind(Operation.CANCEL, ctrl('C')); shortcuts = new LinkedHashMap<>(); shortcuts.put(" Y", "Yes"); shortcuts.put(" N", "No"); shortcuts.put("^C", "Cancel"); display(); return readOperation(yncKeyMap); } finally { editMessage = oldEditMessage; editBuffer.append(oldEditBuffer); shortcuts = oldShortcuts; } } private String getWriteMessage() { StringBuilder sb = new StringBuilder(); sb.append("File Name to "); switch (writeMode) { case WRITE: sb.append("Write"); break; case APPEND: sb.append("Append"); break; case PREPEND: sb.append("Prepend"); break; } switch (buffer.format) { case UNIX: break; case DOS: sb.append(" [DOS Format]"); break; case MAC: sb.append(" [Mac Format]"); break; } if (writeBackup) { sb.append(" [Backup]"); } sb.append(": "); return sb.toString(); } void read() { KeyMap readKeyMap = new KeyMap<>(); readKeyMap.setUnicode(Operation.INSERT); for (char i = 32; i < 256; i++) { readKeyMap.bind(Operation.INSERT, Character.toString(i)); } for (char i = 'A'; i <= 'Z'; i++) { readKeyMap.bind(Operation.DO_LOWER_CASE, alt(i)); } readKeyMap.bind(Operation.BACKSPACE, del()); readKeyMap.bind(Operation.NEW_BUFFER, alt('f')); readKeyMap.bind(Operation.TO_FILES, ctrl('T')); readKeyMap.bind(Operation.EXECUTE, ctrl('X')); readKeyMap.bind(Operation.ACCEPT, "\r"); readKeyMap.bind(Operation.CANCEL, ctrl('C')); readKeyMap.bind(Operation.HELP, ctrl('G'), key(terminal, Capability.key_f1)); readKeyMap.bind(Operation.MOUSE_EVENT, key(terminal, Capability.key_mouse)); editMessage = getReadMessage(); editBuffer.setLength(0); this.shortcuts = readShortcuts(); display(); while (true) { switch (readOperation(readKeyMap)) { case INSERT: editBuffer.append(bindingReader.getLastBinding()); break; case BACKSPACE: if (editBuffer.length() > 0) { editBuffer.setLength(editBuffer.length() - 1); } break; case CANCEL: editMessage = null; this.shortcuts = standardShortcuts(); return; case ACCEPT: editMessage = null; String file = editBuffer.toString(); boolean empty = file.isEmpty(); Path p = empty ? null : root.resolve(file); if (!readNewBuffer && !empty && !Files.exists(p)) { setMessage("\"" + file + "\" not found"); } else if (!empty && Files.isDirectory(p)) { setMessage("\"" + file + "\" is a directory"); } else if (!empty && !Files.isRegularFile(p)) { setMessage("\"" + file + "\" is not a regular file"); } else { Buffer buf = new Buffer(empty ? null : file); try { buf.open(); if (readNewBuffer) { buffers.add(++bufferIndex, buf); buffer = buf; } else { buffer.insert(String.join("\n", buf.lines)); } setMessage(null); } catch (IOException e) { setMessage("Error reading " + file + ": " + e.getMessage()); } } this.shortcuts = standardShortcuts(); return; case HELP: help("nano-read-help.txt"); break; case NEW_BUFFER: readNewBuffer = !readNewBuffer; break; case MOUSE_EVENT: mouseEvent(); break; } editMessage = getReadMessage(); display(); } } private String getReadMessage() { StringBuilder sb = new StringBuilder(); sb.append("File to insert"); if (readNewBuffer) { sb.append(" into new buffer"); } sb.append(" [from ./]: "); return sb.toString(); } private LinkedHashMap readShortcuts() { LinkedHashMap shortcuts = new LinkedHashMap<>(); shortcuts.put("^G", "Get Help"); shortcuts.put("^T", "To Files"); shortcuts.put("M-F", "New Buffer"); shortcuts.put("^C", "Cancel"); shortcuts.put("^X", "Execute Command"); return shortcuts; } private LinkedHashMap writeShortcuts() { LinkedHashMap s = new LinkedHashMap<>(); s.put("^G", "Get Help"); s.put("^T", "To Files"); s.put("M-M", "Mac Format"); s.put("M-P", "Prepend"); s.put("^C", "Cancel"); s.put("M-D", "DOS Format"); s.put("M-A", "Append"); s.put("M-B", "Backup File"); return s; } private LinkedHashMap helpShortcuts() { LinkedHashMap s = new LinkedHashMap<>(); s.put("^L", "Refresh"); s.put("^Y", "Prev Page"); s.put("^P", "Prev Line"); s.put("M-\\", "First Line"); s.put("^X", "Exit"); s.put("^V", "Next Page"); s.put("^N", "Next Line"); s.put("M-/", "Last Line"); return s; } private LinkedHashMap searchShortcuts() { LinkedHashMap s = new LinkedHashMap<>(); s.put("^G", "Get Help"); s.put("^Y", "First Line"); s.put("^R", "Replace"); s.put("^W", "Beg of Par"); s.put("M-C", "Case Sens"); s.put("M-R", "Regexp"); s.put("^C", "Cancel"); s.put("^V", "Last Line"); s.put("^T", "Go To Line"); s.put("^O", "End of Par"); s.put("M-B", "Backwards"); s.put("^P", "PrevHstory"); return s; } private LinkedHashMap standardShortcuts() { LinkedHashMap s = new LinkedHashMap<>(); s.put("^G", "Get Help"); s.put("^O", "WriteOut"); s.put("^R", "Read File"); s.put("^O", "WriteOut"); s.put("^Y", "Prev Page"); s.put("^K", "Cut Text"); s.put("^C", "Cur Pos"); s.put("^X", "Exit"); s.put("^J", "Justify"); s.put("^W", "Where Is"); s.put("^V", "Next Page"); s.put("^U", "UnCut Text"); s.put("^T", "To Spell"); return s; } void help(String help) { Buffer org = this.buffer; Buffer newBuf = new Buffer(null); try (InputStream is = getClass().getResourceAsStream(help)) { newBuf.open(is); } catch (IOException e) { setMessage("Unable to read help"); return; } LinkedHashMap oldShortcuts = this.shortcuts; this.shortcuts = helpShortcuts(); boolean oldWrapping = this.wrapping; boolean oldPrintLineNumbers = this.printLineNumbers; boolean oldConstantCursor = this.constantCursor; this.wrapping = true; this.printLineNumbers = false; this.constantCursor = false; this.buffer = newBuf; try { this.message = null; terminal.puts(Capability.cursor_invisible); display(); while (true) { switch (readOperation(keys)) { case QUIT: return; case FIRST_LINE: buffer.firstLine(); break; case LAST_LINE: buffer.lastLine(); break; case PREV_PAGE: buffer.prevPage(); break; case NEXT_PAGE: buffer.nextPage(); break; case UP: buffer.scrollUp(1); break; case DOWN: buffer.scrollDown(1); break; case CLEAR_SCREEN: clearScreen(); break; case MOUSE_EVENT: mouseEvent(); break; } display(); } } finally { this.buffer = org; this.wrapping = oldWrapping; this.printLineNumbers = oldPrintLineNumbers; this.constantCursor = oldConstantCursor; this.shortcuts = oldShortcuts; terminal.puts(Capability.cursor_visible); } } void search() throws IOException { KeyMap searchKeyMap = new KeyMap<>(); searchKeyMap.setUnicode(Operation.INSERT); for (char i = 'A'; i <= 'Z'; i++) { searchKeyMap.bind(Operation.DO_LOWER_CASE, alt(i)); } searchKeyMap.bind(Operation.CASE_SENSITIVE, alt('c')); searchKeyMap.bind(Operation.BACKWARDS, alt('b')); searchKeyMap.bind(Operation.REGEXP, alt('r')); searchKeyMap.bind(Operation.ACCEPT, "\r"); searchKeyMap.bind(Operation.CANCEL, ctrl('C')); searchKeyMap.bind(Operation.FIRST_LINE, ctrl('Y')); searchKeyMap.bind(Operation.LAST_LINE, ctrl('V')); searchKeyMap.bind(Operation.MOUSE_EVENT, key(terminal, Capability.key_mouse)); editMessage = getSearchMessage(); editBuffer.setLength(0); this.shortcuts = searchShortcuts(); display(); try { while (true) { switch (readOperation(searchKeyMap)) { case INSERT: editBuffer.append(bindingReader.getLastBinding()); break; case CASE_SENSITIVE: searchCaseSensitive = !searchCaseSensitive; break; case BACKWARDS: searchBackwards = !searchBackwards; break; case REGEXP: searchRegexp = !searchRegexp; break; case CANCEL: return; case BACKSPACE: if (editBuffer.length() > 0) { editBuffer.setLength(editBuffer.length() - 1); } break; case ACCEPT: if (editBuffer.length() > 0) { searchTerm = editBuffer.toString(); } if (searchTerm == null || searchTerm.isEmpty()) { setMessage("Cancelled"); } else { buffer.nextSearch(); } return; case HELP: help("nano-search-help.txt"); break; case FIRST_LINE: buffer.firstLine(); return; case LAST_LINE: buffer.lastLine(); return; case MOUSE_EVENT: mouseEvent(); break; } editMessage = getSearchMessage(); display(); } } finally { this.shortcuts = standardShortcuts(); editMessage = null; } } private String getSearchMessage() { StringBuilder sb = new StringBuilder(); sb.append("Search"); if (searchCaseSensitive) { sb.append(" [Case Sensitive]"); } if (searchRegexp) { sb.append(" [Regexp]"); } if (searchBackwards) { sb.append(" [Backwards]"); } if (searchTerm != null) { sb.append(" ["); sb.append(searchTerm); sb.append("]"); } sb.append(": "); return sb.toString(); } String computeCurPos() { int chari = 0; int chart = 0; for (int i = 0; i < buffer.lines.size(); i++) { int l = buffer.lines.get(i).length() + 1; if (i < buffer.line) { chari += l; } else if (i == buffer.line) { chari += buffer.offsetInLine + buffer.column; } chart += l; } StringBuilder sb = new StringBuilder(); sb.append("line "); sb.append(buffer.line + 1); sb.append("/"); sb.append(buffer.lines.size()); sb.append(" ("); sb.append(Math.round((100.0 * buffer.line) / buffer.lines.size())); sb.append("%), "); sb.append("col "); sb.append(buffer.offsetInLine + buffer.column + 1); sb.append("/"); sb.append(buffer.lines.get(buffer.line).length() + 1); sb.append(" ("); if (buffer.lines.get(buffer.line).length() > 0) { sb.append(Math.round((100.0 * (buffer.offsetInLine + buffer.column)) / (buffer.lines.get(buffer.line).length()))); } else { sb.append("100"); } sb.append("%), "); sb.append("char "); sb.append(chari + 1); sb.append("/"); sb.append(chart); sb.append(" ("); sb.append(Math.round((100.0 * chari) / chart)); sb.append("%)"); return sb.toString(); } void curPos() { setMessage(computeCurPos()); } void prevBuffer() throws IOException { if (buffers.size() > 1) { bufferIndex = (bufferIndex + buffers.size() - 1) % buffers.size(); buffer = buffers.get(bufferIndex); setMessage("Switched to " + buffer.getTitle()); buffer.open(); display.clear(); } else { setMessage("No more open file buffers"); } } void nextBuffer() throws IOException { if (buffers.size() > 1) { bufferIndex = (bufferIndex + 1) % buffers.size(); buffer = buffers.get(bufferIndex); setMessage("Switched to " + buffer.getTitle()); buffer.open(); display.clear(); } else { setMessage("No more open file buffers"); } } void setMessage(String message) { this.message = message; this.nbBindings = 25; } boolean quit() throws IOException { if (buffer.dirty) { Operation op = getYNC("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "); switch (op) { case CANCEL: return false; case NO: break; case YES: if (!write()) { return false; } } } buffers.remove(bufferIndex); if (bufferIndex == buffers.size() && bufferIndex > 0) { bufferIndex = buffers.size() - 1; } if (buffers.isEmpty()) { buffer = null; return true; } else { buffer = buffers.get(bufferIndex); buffer.open(); display.clear(); setMessage("Switched to " + buffer.getTitle()); return false; } } void numbers() { printLineNumbers = !printLineNumbers; resetDisplay(); setMessage("Lines numbering " + (printLineNumbers ? "enabled" : "disabled")); } void smoothScrolling() { smoothScrolling = !smoothScrolling; setMessage("Smooth scrolling " + (smoothScrolling ? "enabled" : "disabled")); } void mouseSupport() throws IOException { mouseSupport = !mouseSupport; setMessage("Mouse support " + (mouseSupport ? "enabled" : "disabled")); terminal.trackMouse(mouseSupport ? Terminal.MouseTracking.Normal : Terminal.MouseTracking.Off); } void constantCursor() { constantCursor = !constantCursor; setMessage("Constant cursor position display " + (constantCursor ? "enabled" : "disabled")); } void oneMoreLine() { oneMoreLine = !oneMoreLine; setMessage("Use of one more line for editing " + (oneMoreLine ? "enabled" : "disabled")); } void wrap() { wrapping = !wrapping; resetDisplay(); setMessage("Lines wrapping " + (wrapping ? "enabled" : "disabled")); } void clearScreen() { resetDisplay(); } void mouseEvent() { MouseEvent event = terminal.readMouseEvent(); if (event.getModifiers().isEmpty() && event.getType() == MouseEvent.Type.Released && event.getButton() == MouseEvent.Button.Button1) { int x = event.getX(); int y = event.getY(); int hdr = buffer.computeHeader().size(); int ftr = computeFooter().size(); if (y < hdr) { // nothing } else if (y < size.getRows() - ftr) { buffer.moveTo(x, y - hdr); } else { int cols = (shortcuts.size() + 1) / 2; int cw = size.getColumns() / cols; int l = y - (size.getRows() - ftr) - 1; int si = l * cols + x / cw; String shortcut = null; Iterator it = shortcuts.keySet().iterator(); while (si-- >= 0 && it.hasNext()) { shortcut = it.next(); } if (shortcut != null) { shortcut = shortcut.replaceAll("M-", "\\\\E"); String seq = KeyMap.translate(shortcut); bindingReader.runMacro(seq); } } } else if (event.getType() == MouseEvent.Type.Wheel) { if (event.getButton() == MouseEvent.Button.WheelDown) { buffer.moveDown(1); } else if (event.getButton() == MouseEvent.Button.WheelUp) { buffer.moveUp(1); } } } public String getTitle() { return title; } void resetDisplay() { display.clear(); display.resize(size.getRows(), size.getColumns()); for (Buffer buffer : buffers) { buffer.resetDisplay(); } } synchronized void display() { if (nbBindings > 0) { if (--nbBindings == 0) { message = null; } } List header = buffer.computeHeader(); List footer = computeFooter(); int nbLines = size.getRows() - header.size() - footer.size(); List newLines = buffer.getDisplayedLines(nbLines); newLines.addAll(0, header); newLines.addAll(footer); // Compute cursor position int cursor; if (editMessage != null) { cursor = editMessage.length() + editBuffer.length(); cursor = size.cursorPos(size.getRows() - footer.size(), cursor); } else { cursor = size.cursorPos(header.size(), buffer.getDisplayedCursor()); } display.update(newLines, cursor); } protected List computeFooter() { List footer = new ArrayList<>(); if (editMessage != null) { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.style(AttributedStyle.INVERSE); sb.append(editMessage); sb.append(editBuffer); for (int i = editMessage.length() + editBuffer.length(); i < size.getColumns(); i++) { sb.append(' '); } sb.append('\n'); footer.add(sb.toAttributedString()); } else if (message != null || constantCursor) { int rwidth = size.getColumns(); String text = "[ " + (message == null ? computeCurPos() : message) + " ]"; int len = text.length(); AttributedStringBuilder sb = new AttributedStringBuilder(); for (int i = 0; i < (rwidth - len) / 2; i++) { sb.append(' '); } sb.style(AttributedStyle.INVERSE); sb.append(text); sb.append('\n'); footer.add(sb.toAttributedString()); } else { footer.add(new AttributedString("\n")); } Iterator> sit = shortcuts.entrySet().iterator(); int cols = (shortcuts.size() + 1) / 2; int cw = (size.getColumns() - 1) / cols; int rem = (size.getColumns() - 1) % cols; for (int l = 0; l < 2; l++) { AttributedStringBuilder sb = new AttributedStringBuilder(); for (int c = 0; c < cols; c++) { Map.Entry entry = sit.hasNext() ? sit.next() : null; String key = entry != null ? entry.getKey() : ""; String val = entry != null ? entry.getValue() : ""; sb.style(AttributedStyle.INVERSE); sb.append(key); sb.style(AttributedStyle.DEFAULT); sb.append(" "); int nb = cw - key.length() - 1 + (c < rem ? 1 : 0); if (val.length() > nb) { sb.append(val.substring(0, nb)); } else { sb.append(val); if (c < cols - 1) { for (int i = 0; i < nb - val.length(); i++) { sb.append(" "); } } } } sb.append('\n'); footer.add(sb.toAttributedString()); } return footer; } protected void handle(Signal signal) { size.copy(terminal.getSize()); buffer.computeAllOffsets(); buffer.moveToChar(buffer.offsetInLine + buffer.column); resetDisplay(); display(); } protected void bindKeys() { keys = new KeyMap<>(); keys.setUnicode(Operation.INSERT); for (char i = 32; i < KEYMAP_LENGTH; i++) { keys.bind(Operation.INSERT, Character.toString(i)); } keys.bind(Operation.BACKSPACE, del()); for (char i = 'A'; i <= 'Z'; i++) { keys.bind(Operation.DO_LOWER_CASE, alt(i)); } keys.bind(Operation.HELP, ctrl('G'), key(terminal, Capability.key_f1)); keys.bind(Operation.QUIT, ctrl('X'), key(terminal, Capability.key_f2)); keys.bind(Operation.WRITE, ctrl('O'), key(terminal, Capability.key_f3)); keys.bind(Operation.JUSTIFY_PARAGRAPH, ctrl('J'), key(terminal, Capability.key_f4)); keys.bind(Operation.READ, ctrl('R'), key(terminal, Capability.key_f5)); keys.bind(Operation.SEARCH, ctrl('W'), key(terminal, Capability.key_f6)); keys.bind(Operation.PREV_PAGE, ctrl('Y'), key(terminal, Capability.key_f7)); keys.bind(Operation.NEXT_PAGE, ctrl('V'), key(terminal, Capability.key_f8)); keys.bind(Operation.CUT, ctrl('K'), key(terminal, Capability.key_f9)); keys.bind(Operation.UNCUT, ctrl('U'), key(terminal, Capability.key_f10)); keys.bind(Operation.CUR_POS, ctrl('C'), key(terminal, Capability.key_f11)); keys.bind(Operation.TO_SPELL, ctrl('T'), key(terminal, Capability.key_f11)); keys.bind(Operation.GOTO, ctrl('_'), key(terminal, Capability.key_f13), alt('g')); keys.bind(Operation.REPLACE, ctrl('\\'), key(terminal, Capability.key_f14), alt('r')); keys.bind(Operation.MARK, ctrl('^'), key(terminal, Capability.key_f15), alt('a')); keys.bind(Operation.NEXT_SEARCH, key(terminal, Capability.key_f16), alt('w')); keys.bind(Operation.COPY, alt('^')); keys.bind(Operation.INDENT, alt('}')); keys.bind(Operation.UNINDENT, alt('{')); keys.bind(Operation.RIGHT, ctrl('F')); keys.bind(Operation.LEFT, ctrl('B')); keys.bind(Operation.NEXT_WORD, ctrl(' ')); keys.bind(Operation.PREV_WORD, alt(' ')); keys.bind(Operation.UP, ctrl('P')); keys.bind(Operation.DOWN, ctrl('N')); keys.bind(Operation.BEGINNING_OF_LINE, ctrl('A')); keys.bind(Operation.END_OF_LINE, ctrl('E')); keys.bind(Operation.BEGINNING_OF_PARAGRAPH, alt('('), alt('9')); keys.bind(Operation.END_OF_PARAGRAPH, alt(')'), alt('0')); keys.bind(Operation.FIRST_LINE, alt('\\'), alt('|')); keys.bind(Operation.LAST_LINE, alt('/'), alt('?')); keys.bind(Operation.MATCHING, alt(']')); keys.bind(Operation.SCROLL_UP, alt('-'), alt('_')); keys.bind(Operation.SCROLL_DOWN, alt('+'), alt('=')); keys.bind(Operation.PREV_BUFFER, alt('<')); keys.bind(Operation.NEXT_BUFFER, alt('>')); keys.bind(Operation.PREV_BUFFER, alt(',')); keys.bind(Operation.NEXT_BUFFER, alt('.')); keys.bind(Operation.VERBATIM, alt('v')); keys.bind(Operation.INSERT, ctrl('I'), ctrl('M')); keys.bind(Operation.DELETE, ctrl('D')); keys.bind(Operation.BACKSPACE, ctrl('H')); keys.bind(Operation.CUT_TO_END, alt('t')); keys.bind(Operation.JUSTIFY_FILE, alt('j')); keys.bind(Operation.COUNT, alt('d')); keys.bind(Operation.CLEAR_SCREEN, ctrl('L')); keys.bind(Operation.HELP, alt('x')); keys.bind(Operation.CONSTANT_CURSOR, alt('c')); keys.bind(Operation.ONE_MORE_LINE, alt('o')); keys.bind(Operation.SMOOTH_SCROLLING, alt('s')); keys.bind(Operation.MOUSE_SUPPORT, alt('m')); keys.bind(Operation.WHITESPACE, alt('p')); keys.bind(Operation.HIGHLIGHT, alt('y')); keys.bind(Operation.SMART_HOME_KEY, alt('h')); keys.bind(Operation.AUTO_INDENT, alt('i')); keys.bind(Operation.CUT_TO_END_TOGGLE, alt('k')); // TODO: reenable wrapping after fixing #120 // keys.bind(Operation.WRAP, alt('l')); keys.bind(Operation.TABS_TO_SPACE, alt('q')); keys.bind(Operation.BACKUP, alt('b')); keys.bind(Operation.NUMBERS, alt('n')); // TODO: map other keys keys.bind(Operation.UP, key(terminal, Capability.key_up)); keys.bind(Operation.DOWN, key(terminal, Capability.key_down)); keys.bind(Operation.RIGHT, key(terminal, Capability.key_right)); keys.bind(Operation.LEFT, key(terminal, Capability.key_left)); keys.bind(Operation.MOUSE_EVENT, key(terminal, Capability.key_mouse)); } protected enum Operation { DO_LOWER_CASE, QUIT, WRITE, READ, GOTO, FIND, WRAP, NUMBERS, SMOOTH_SCROLLING, MOUSE_SUPPORT, ONE_MORE_LINE, CLEAR_SCREEN, UP, DOWN, LEFT, RIGHT, INSERT, BACKSPACE, NEXT_BUFFER, PREV_BUFFER, HELP, NEXT_PAGE, PREV_PAGE, SCROLL_UP, SCROLL_DOWN, NEXT_WORD, PREV_WORD, BEGINNING_OF_LINE, END_OF_LINE, FIRST_LINE, LAST_LINE, CUR_POS, CASE_SENSITIVE, BACKWARDS, REGEXP, ACCEPT, CANCEL, SEARCH, MAC_FORMAT, DOS_FORMAT, APPEND_MODE, PREPEND_MODE, BACKUP, TO_FILES, YES, NO, NEW_BUFFER, EXECUTE, NEXT_SEARCH, MATCHING, VERBATIM, DELETE, JUSTIFY_PARAGRAPH, TO_SPELL, CUT, REPLACE, MARK, COPY, INDENT, UNINDENT, BEGINNING_OF_PARAGRAPH, END_OF_PARAGRAPH, CUT_TO_END, JUSTIFY_FILE, COUNT, CONSTANT_CURSOR, WHITESPACE, HIGHLIGHT, SMART_HOME_KEY, AUTO_INDENT, CUT_TO_END_TOGGLE, TABS_TO_SPACE, UNCUT, MOUSE_EVENT } } jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/NfaMatcher.java000066400000000000000000000215201311544710100266570ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import java.util.*; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.stream.Collectors; /** * NFA implementation. * See https://swtch.com/~rsc/regexp/regexp1.html */ public class NfaMatcher { private final String regexp; private final BiFunction matcher; private volatile State start; public NfaMatcher(String regexp, BiFunction matcher) { this.regexp = regexp; this.matcher = matcher; } public void compile() { if (start == null) { start = toNfa(toPostFix(regexp)); } } public boolean match(List args) { Set clist = new HashSet<>(); compile(); addState(clist, start); for (T arg : args) { Set nlist = new HashSet<>(); clist.stream() .filter(s -> !Objects.equals(State.Match, s.c) && !Objects.equals(State.Split, s.c)) .filter(s -> matcher.apply(arg, s.c)) .forEach(s -> addState(nlist, s.out)); clist = nlist; } return clist.stream() .anyMatch(s -> Objects.equals(State.Match, s.c)); } /** * Returns the list of possible matcher names for the next object */ public Set matchPartial(List args) { Set clist = new HashSet<>(); compile(); addState(clist, start); for (T arg : args) { Set nlist = new HashSet<>(); clist.stream() .filter(s -> !Objects.equals(State.Match, s.c) && !Objects.equals(State.Split, s.c)) .filter(s -> matcher.apply(arg, s.c)) .forEach(s -> addState(nlist, s.out)); clist = nlist; } return clist.stream() .filter(s -> !Objects.equals(State.Match, s.c) && !Objects.equals(State.Split, s.c)) .map(s -> s.c) .collect(Collectors.toSet()); } void addState(Set l, State s) { if (s != null && l.add(s)) { if (Objects.equals(State.Split, s.c)) { addState(l, s.out); addState(l, s.out1); } } } static State toNfa(List postfix) { Deque stack = new ArrayDeque<>(); Frag e1, e2, e; State s; for (String p : postfix) { switch (p) { case ".": e2 = stack.pollLast(); e1 = stack.pollLast(); e1.patch(e2.start); stack.offerLast(new Frag(e1.start, e2.out)); break; case "|": e2 = stack.pollLast(); e1 = stack.pollLast(); s = new State(State.Split, e1.start, e2.start); stack.offerLast(new Frag(s, e1.out, e2.out)); break; case "?": e = stack.pollLast(); s = new State(State.Split, e.start, null); stack.offerLast(new Frag(s, e.out, s::setOut1)); break; case "*": e = stack.pollLast(); s = new State(State.Split, e.start, null); e.patch(s); stack.offerLast(new Frag(s, s::setOut1)); break; case "+": e = stack.pollLast(); s = new State(State.Split, e.start, null); e.patch(s); stack.offerLast(new Frag(e.start, s::setOut1)); break; default: s = new State(p, null, null); stack.offerLast(new Frag(s, s::setOut)); break; } } e = stack.pollLast(); if (!stack.isEmpty()) { throw new IllegalStateException("Wrong postfix expression, " + stack.size() + " elements remaining"); } e.patch(new State(State.Match, null, null)); return e.start; } static List toPostFix(String regexp) { List postfix = new ArrayList<>(); int s = -1; int natom = 0; int nalt = 0; Deque natoms = new ArrayDeque<>(); Deque nalts = new ArrayDeque<>(); for (int i = 0; i < regexp.length(); i++) { char c = regexp.charAt(i); // Scan identifiers if (Character.isJavaIdentifierPart(c)) { if (s < 0) { s = i; } continue; } // End of identifier if (s >= 0) { if (natom > 1) { --natom; postfix.add("."); } postfix.add(regexp.substring(s, i)); natom++; s = -1; } // Ignore space if (Character.isWhitespace(c)) { continue; } // Special characters switch (c) { case '(': if (natom > 1) { --natom; postfix.add("."); } nalts.offerLast(nalt); natoms.offerLast(natom); nalt = 0; natom = 0; break; case '|': if (natom == 0) { throw new IllegalStateException("unexpected '" + c + "' at pos " + i); } while (--natom > 0) { postfix.add("."); } nalt++; break; case ')': if (nalts.isEmpty() || natom == 0) { throw new IllegalStateException("unexpected '" + c + "' at pos " + i); } while (--natom > 0) { postfix.add("."); } for (; nalt > 0; nalt--) { postfix.add("|"); } nalt = nalts.pollLast(); natom = natoms.pollLast(); natom++; break; case '*': case '+': case '?': if (natom == 0) { throw new IllegalStateException("unexpected '" + c + "' at pos " + i); } postfix.add(String.valueOf(c)); break; default: throw new IllegalStateException("unexpected '" + c + "' at pos " + i); } } // End of identifier if (s >= 0) { if (natom > 1) { --natom; postfix.add("."); } postfix.add(regexp.substring(s)); natom++; } // Append while (--natom > 0) { postfix.add("."); } // Alternatives for (; nalt > 0; nalt--) { postfix.add("|"); } return postfix; } static class State { static final String Match = "++MATCH++"; static final String Split = "++SPLIT++"; final String c; State out; State out1; public State(String c, State out, State out1) { this.c = c; this.out = out; this.out1 = out1; } public void setOut(State out) { this.out = out; } public void setOut1(State out1) { this.out1 = out1; } } private static class Frag { final State start; final List> out = new ArrayList<>(); public Frag(State start, Collection> l) { this.start = start; this.out.addAll(l); } public Frag(State start, Collection> l1, Collection> l2) { this.start = start; this.out.addAll(l1); this.out.addAll(l2); } public Frag(State start, Consumer c) { this.start = start; this.out.add(c); } public Frag(State start, Collection> l, Consumer c) { this.start = start; this.out.addAll(l); this.out.add(c); } public void patch(State s) { out.forEach(c -> c.accept(s)); } } } jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/Options.java000066400000000000000000000405321311544710100263060ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jline.builtins; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Yet another GNU long options parser. This one is configured by parsing its Usage string. * * Code coming from Apache Felix Gogo Shell */ public class Options { public static final String NL = System.getProperty("line.separator", "\n"); // Note: need to double \ within "" private static final String regex = "(?x)\\s*" + "(?:-([^-]))?" + // 1: short-opt-1 "(?:,?\\s*-(\\w))?" + // 2: short-opt-2 "(?:,?\\s*--(\\w[\\w-]*)(=\\w+)?)?" + // 3: long-opt-1 and 4:arg-1 "(?:,?\\s*--(\\w[\\w-]*))?" + // 5: long-opt-2 ".*?(?:\\(default=(.*)\\))?\\s*"; // 6: default private static final int GROUP_SHORT_OPT_1 = 1; private static final int GROUP_SHORT_OPT_2 = 2; private static final int GROUP_LONG_OPT_1 = 3; private static final int GROUP_ARG_1 = 4; private static final int GROUP_LONG_OPT_2 = 5; private static final int GROUP_DEFAULT = 6; private final Pattern parser = Pattern.compile(regex); private final Pattern uname = Pattern.compile("^Usage:\\s+(\\w+)"); private final Map unmodifiableOptSet; private final Map unmodifiableOptArg; private final Map optSet = new HashMap<>(); private final Map optArg = new HashMap<>(); private final Map optName = new HashMap<>(); private final Map optAlias = new HashMap<>(); private final List xargs = new ArrayList<>(); private List args = null; private static final String UNKNOWN = "unknown"; private String usageName = UNKNOWN; private int usageIndex = 0; private final String[] spec; private final String[] gspec; private final String defOpts; private final String[] defArgs; private String error = null; private boolean optionsFirst = false; private boolean stopOnBadOption = false; public static Options compile(String[] optSpec) { return new Options(optSpec, null, null, System::getenv); } public static Options compile(String[] optSpec, Function env) { return new Options(optSpec, null, null, env); } public static Options compile(String optSpec) { return compile(optSpec.split("\\n"), System::getenv); } public static Options compile(String optSpec, Function env) { return compile(optSpec.split("\\n"), env); } public static Options compile(String[] optSpec, Options gopt) { return new Options(optSpec, null, gopt, System::getenv); } public static Options compile(String[] optSpec, String[] gspec) { return new Options(optSpec, gspec, null, System::getenv); } public Options setStopOnBadOption(boolean stopOnBadOption) { this.stopOnBadOption = stopOnBadOption; return this; } public Options setOptionsFirst(boolean optionsFirst) { this.optionsFirst = optionsFirst; return this; } public boolean isSet(String name) { if (!optSet.containsKey(name)) throw new IllegalArgumentException("option not defined in spec: " + name); return optSet.get(name); } public Object getObject(String name) { if (!optArg.containsKey(name)) throw new IllegalArgumentException("option not defined with argument: " + name); List list = getObjectList(name); return list.isEmpty() ? "" : list.get(list.size() - 1); } @SuppressWarnings("unchecked") public List getObjectList(String name) { List list; Object arg = optArg.get(name); if ( arg == null ) { throw new IllegalArgumentException("option not defined with argument: " + name); } if (arg instanceof String) { // default value list = new ArrayList<>(); if (!"".equals(arg)) list.add(arg); } else { list = (List) arg; } return list; } public List getList(String name) { ArrayList list = new ArrayList<>(); for (Object o : getObjectList(name)) { try { list.add((String) o); } catch (ClassCastException e) { throw new IllegalArgumentException("option not String: " + name); } } return list; } @SuppressWarnings("unchecked") private void addArg(String name, Object value) { List list; Object arg = optArg.get(name); if (arg instanceof String) { // default value list = new ArrayList<>(); optArg.put(name, list); } else { list = (List) arg; } list.add(value); } public String get(String name) { try { return (String) getObject(name); } catch (ClassCastException e) { throw new IllegalArgumentException("option not String: " + name); } } public int getNumber(String name) { String number = get(name); try { if (number != null) return Integer.parseInt(number); return 0; } catch (NumberFormatException e) { throw new IllegalArgumentException("option '" + name + "' not Number: " + number); } } public List argObjects() { return xargs; } public List args() { if (args == null) { args = new ArrayList<>(); for (Object arg : xargs) { args.add(arg == null ? "null" : arg.toString()); } } return args; } public void usage(PrintStream err) { StringBuilder buf = new StringBuilder(); int index = 0; if (error != null) { buf.append(error); buf.append(NL); index = usageIndex; } for (int i = index; i < spec.length; ++i) { buf.append(spec[i]); buf.append(NL); } String msg = buf.toString(); err.print(msg); } /** * prints usage message and returns IllegalArgumentException, for you to throw. */ public IllegalArgumentException usageError(String s) { error = usageName + ": " + s; return new IllegalArgumentException(error); } // internal constructor private Options(String[] spec, String[] gspec, Options opt, Function env) { this.gspec = gspec; if (gspec == null && opt == null) { this.spec = spec; } else { ArrayList list = new ArrayList<>(); list.addAll(Arrays.asList(spec)); list.addAll(Arrays.asList(gspec != null ? gspec : opt.gspec)); this.spec = list.toArray(new String[list.size()]); } Map myOptSet = new HashMap<>(); Map myOptArg = new HashMap<>(); parseSpec(myOptSet, myOptArg); if (opt != null) { for (Entry e : opt.optSet.entrySet()) { if (e.getValue()) myOptSet.put(e.getKey(), true); } for (Entry e : opt.optArg.entrySet()) { if (!e.getValue().equals("")) myOptArg.put(e.getKey(), e.getValue()); } opt.reset(); } unmodifiableOptSet = Collections.unmodifiableMap(myOptSet); unmodifiableOptArg = Collections.unmodifiableMap(myOptArg); defOpts = env != null ? env.apply(usageName.toUpperCase() + "_OPTS") : null; defArgs = (defOpts != null) ? defOpts.split("\\s+") : new String[0]; } /** * parse option spec. */ private void parseSpec(Map myOptSet, Map myOptArg) { int index = 0; for (String line : spec) { Matcher m = parser.matcher(line); if (m.matches()) { final String opt = m.group(GROUP_LONG_OPT_1); final String name = (opt != null) ? opt : m.group(GROUP_SHORT_OPT_1); if (name != null) { if (myOptSet.containsKey(name)) throw new IllegalArgumentException("duplicate option in spec: --" + name); myOptSet.put(name, false); } String dflt = (m.group(GROUP_DEFAULT) != null) ? m.group(GROUP_DEFAULT) : ""; if (m.group(GROUP_ARG_1) != null) myOptArg.put(opt, dflt); String opt2 = m.group(GROUP_LONG_OPT_2); if (opt2 != null) { optAlias.put(opt2, opt); myOptSet.put(opt2, false); if (m.group(GROUP_ARG_1) != null) myOptArg.put(opt2, ""); } for (int i = 0; i < 2; ++i) { String sopt = m.group(i == 0 ? GROUP_SHORT_OPT_1 : GROUP_SHORT_OPT_2); if (sopt != null) { if (optName.containsKey(sopt)) throw new IllegalArgumentException("duplicate option in spec: -" + sopt); optName.put(sopt, name); } } } if (Objects.equals(usageName, UNKNOWN)) { Matcher u = uname.matcher(line); if (u.find()) { usageName = u.group(1); usageIndex = index; } } index++; } } private void reset() { optSet.clear(); optSet.putAll(unmodifiableOptSet); optArg.clear(); optArg.putAll(unmodifiableOptArg); xargs.clear(); args = null; error = null; } public Options parse(Object[] argv) { return parse(argv, false); } public Options parse(List argv) { return parse(argv, false); } public Options parse(Object[] argv, boolean skipArg0) { if (null == argv) throw new IllegalArgumentException("argv is null"); return parse(Arrays.asList(argv), skipArg0); } public Options parse(List argv, boolean skipArg0) { reset(); List args = new ArrayList<>(); args.addAll(Arrays.asList(defArgs)); for (Object arg : argv) { if (skipArg0) { skipArg0 = false; usageName = arg.toString(); } else { args.add(arg); } } String needArg = null; String needOpt = null; boolean endOpt = false; for (Object oarg : args) { String arg = oarg == null ? "null" : oarg.toString(); if (endOpt) { xargs.add(oarg); } else if (needArg != null) { addArg(needArg, oarg); needArg = null; needOpt = null; } else if (!arg.startsWith("-") || "-".equals(oarg)) { if (optionsFirst) endOpt = true; xargs.add(oarg); } else { if (arg.equals("--")) endOpt = true; else if (arg.startsWith("--")) { int eq = arg.indexOf("="); String value = (eq == -1) ? null : arg.substring(eq + 1); String name = arg.substring(2, ((eq == -1) ? arg.length() : eq)); List names = new ArrayList<>(); if (optSet.containsKey(name)) { names.add(name); } else { for (String k : optSet.keySet()) { if (k.startsWith(name)) names.add(k); } } switch (names.size()) { case 1: name = names.get(0); optSet.put(name, true); if (optArg.containsKey(name)) { if (value != null) addArg(name, value); else needArg = name; } else if (value != null) { throw usageError("option '--" + name + "' doesn't allow an argument"); } break; case 0: if (stopOnBadOption) { endOpt = true; xargs.add(oarg); break; } else throw usageError("invalid option '--" + name + "'"); default: throw usageError("option '--" + name + "' is ambiguous: " + names); } } else { for (int i = 1; i < arg.length(); i++) { String c = String.valueOf(arg.charAt(i)); if (optName.containsKey(c)) { String name = optName.get(c); optSet.put(name, true); if (optArg.containsKey(name)) { int k = i + 1; if (k < arg.length()) { addArg(name, arg.substring(k)); } else { needOpt = c; needArg = name; } break; } } else { if (stopOnBadOption) { xargs.add("-" + c); endOpt = true; } else throw usageError("invalid option '" + c + "'"); } } } } } if (needArg != null) { String name = (needOpt != null) ? needOpt : "--" + needArg; throw usageError("option '" + name + "' requires an argument"); } // remove long option aliases for (Entry alias : optAlias.entrySet()) { if (optSet.get(alias.getKey())) { optSet.put(alias.getValue(), true); if (optArg.containsKey(alias.getKey())) optArg.put(alias.getValue(), optArg.get(alias.getKey())); } optSet.remove(alias.getKey()); optArg.remove(alias.getKey()); } return this; } @Override public String toString() { return "isSet" + optSet + "\nArg" + optArg + "\nargs" + xargs; } } jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/ScreenTerminal.java000066400000000000000000001704351311544710100275740ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Based on http://antony.lesuisse.org/software/ajaxterm/ * Public Domain License */ /** * See http://www.ecma-international.org/publications/standards/Ecma-048.htm * and http://vt100.net/docs/vt510-rm/ */ package org.jline.builtins; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.jline.utils.AttributedCharSequence; import org.jline.utils.WCWidth; /** * Screen terminal implementation. * This class is copied from Apache Karaf WebConsole Gogo plugin * and slightly adapted to support alternate screen / resizing / 256 colors. */ public class ScreenTerminal { enum State { None, Esc, Str, Csi, } private int width; private int height; private long attr; private boolean eol; private int cx; private int cy; private long[][] screen; private long[][] screen2; private State vt100_parse_state = State.None; private int vt100_parse_len; private int vt100_lastchar; private int vt100_parse_func; private String vt100_parse_param; private boolean vt100_mode_autowrap; private boolean vt100_mode_insert; private boolean vt100_charset_is_single_shift; private boolean vt100_charset_is_graphical; private boolean vt100_mode_lfnewline; private boolean vt100_mode_origin; private boolean vt100_mode_inverse; private boolean vt100_mode_cursorkey; private boolean vt100_mode_cursor; private boolean vt100_mode_alt_screen; private boolean vt100_mode_backspace; private boolean vt100_mode_column_switch; private boolean vt100_keyfilter_escape; private int[] vt100_charset_graph = new int[]{ 0x25ca, 0x2026, 0x2022, 0x3f, 0xb6, 0x3f, 0xb0, 0xb1, 0x3f, 0x3f, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0xaf, 0x2014, 0x2014, 0x2014, 0x5f, 0x2b, 0x2b, 0x2b, 0x2b, 0x7c, 0x2264, 0x2265, 0xb6, 0x2260, 0xa3, 0xb7, 0x7f }; private int vt100_charset_g_sel; private int[] vt100_charset_g = {0, 0}; private Map vt100_saved; private Map vt100_saved2; private int vt100_alternate_saved_cx; private int vt100_alternate_saved_cy; private int vt100_saved_cx; private int vt100_saved_cy; private String vt100_out; private int scroll_area_y0; private int scroll_area_y1; private List tab_stops; private final List history = new ArrayList<>(); private AtomicBoolean dirty = new AtomicBoolean(true); public ScreenTerminal() { this(80, 24); } public ScreenTerminal(int width, int height) { this.width = width; this.height = height; reset_hard(); } private void reset_hard() { // Attribute mask: 0xYXFFFBBB00000000L // X: Bit 0 - Underlined // Bit 1 - Negative // Bit 2 - Concealed // Bit 3 - Bold // Y: Bit 0 - Foreground set // Bit 1 - Background set // F: Foreground r-g-b // B: Background r-g-b attr = 0x0000000000000000L; // Key filter vt100_keyfilter_escape = false; // Last char vt100_lastchar = 0; // Control sequences vt100_parse_len = 0; vt100_parse_state = State.None; vt100_parse_func = 0; vt100_parse_param = ""; // Buffers vt100_out = ""; // Invoke other resets reset_screen(); reset_soft(); } private void reset_soft() { // Attribute mask: 0xYXFFFBBB00000000L // X: Bit 0 - Underlined // Bit 1 - Negative // Bit 2 - Concealed // Bit 3 - Bold // Y: Bit 0 - Foreground set // Bit 1 - Background set // F: Foreground r-g-b // B: Background r-g-b attr = 0x0000000000000000L; // Scroll parameters scroll_area_y0 = 0; scroll_area_y1 = height; // Character sets vt100_charset_is_single_shift = false; vt100_charset_is_graphical = false; vt100_charset_g_sel = 0; vt100_charset_g = new int[]{0, 0}; // Modes vt100_mode_insert = false; vt100_mode_lfnewline = false; vt100_mode_cursorkey = false; vt100_mode_column_switch = false; vt100_mode_inverse = false; vt100_mode_origin = false; vt100_mode_autowrap = true; vt100_mode_cursor = true; vt100_mode_alt_screen = false; vt100_mode_backspace = false; // Init DECSC state esc_DECSC(); vt100_saved2 = vt100_saved; esc_DECSC(); } private void reset_screen() { // Screen screen = (long[][]) Array.newInstance(long.class, height, width); screen2 = (long[][]) Array.newInstance(long.class, height, width); for (int i = 0; i < height; i++) { Arrays.fill(screen[i], attr | 0x00000020); Arrays.fill(screen2[i], attr | 0x00000020); } // Scroll parameters scroll_area_y0 = 0; scroll_area_y1 = height; // Cursor position cx = 0; cy = 0; // Tab stops tab_stops = new ArrayList<>(); for (int i = 7; i < width; i += 8) { tab_stops.add(i); } } // // UTF-8 functions // private int utf8_charwidth(int c) { return WCWidth.wcwidth(c); } // // Low-level terminal functions // private long[] peek(int y0, int x0, int y1, int x1) { int from = width * y0 + x0; int to = width * (y1 - 1) + x1; int newLength = to - from; if (newLength < 0) throw new IllegalArgumentException(from + " > " + to); long[] copy = new long[newLength]; int cur = from; while (cur < to) { int y = cur / width; int x = cur % width; int nb = Math.min(width - x, to - cur); System.arraycopy(screen[y], x, copy, cur - from, nb); cur += nb; } return copy; } private void poke(int y, int x, long[] s) { int cur = 0; int max = s.length; while (cur < max) { int nb = Math.min(width - x, max - cur); System.arraycopy(s, 0, screen[y++], x, nb); x = 0; cur += nb; } setDirty(); } private void fill(int y0, int x0, int y1, int x1, long c) { if (y0 == y1 - 1) { if (x0 < x1 - 1) { Arrays.fill(screen[y0], x0, x1, c); setDirty(); } } else if (y0 < y1 - 1) { Arrays.fill(screen[y0], x0, width, c); for (int i = y0; i < y1 - 1; i++) { Arrays.fill(screen[i], c); } Arrays.fill(screen[y1 - 1], 0, x1, c); setDirty(); } } private void clear(int y0, int x0, int y1, int x1) { fill(y0, x0, y1, x1, attr | 0x00000020); } // // Scrolling functions // private void scroll_area_up(int y0, int y1) { scroll_area_up(y0, y1, 1); } private void scroll_area_up(int y0, int y1, int n) { n = Math.min(y1 - y0, n); if (y0 == 0 && y1 == height) { for (int i = 0; i < n; i++) { history.add(screen[i]); } System.arraycopy(screen, n, screen, 0, height - n); for (int i = 1; i <= n; i++) { screen[y1 - i] = new long[width]; Arrays.fill(screen[y1 - 1], attr | 0x0020); } } else { poke(y0, 0, peek(y0 + n, 0, y1, width)); clear(y1 - n, 0, y1, width); } } private void scroll_area_down(int y0, int y1) { scroll_area_down(y0, y1, 1); } private void scroll_area_down(int y0, int y1, int n) { n = Math.min(y1 - y0, n); poke(y0 + n, 0, peek(y0, 0, y1 - n, width)); clear(y0, 0, y0 + n, width); } private void scroll_area_set(int y0, int y1) { y0 = Math.max(0, Math.min(height - 1, y0)); y1 = Math.max(1, Math.min(height, y1)); if (y1 > y0) { scroll_area_y0 = y0; scroll_area_y1 = y1; } } private void scroll_line_right(int y, int x) { scroll_line_right(y, x, 1); } private void scroll_line_right(int y, int x, int n) { if (x < width) { n = Math.min(width - cx, n); poke(y, x + n, peek(y, x, y + 1, width - n)); clear(y, x, y + 1, x + n); } } private void scroll_line_left(int y, int x) { scroll_line_left(y, x, 1); } private void scroll_line_left(int y, int x, int n) { if (x < width) { n = Math.min(width - cx, n); poke(y, x, peek(y, x + n, y + 1, width)); clear(y, width - n, y + 1, width); } } // // Cursor functions // private int[] cursor_line_width(int next_char) { int wx = utf8_charwidth(next_char); int lx = 0; for (int x = 0; x < Math.min(cx, width); x++) { int c = (int) (peek(cy, x, cy + 1, x + 1)[0] & 0x00000000ffffffffL); wx += utf8_charwidth(c); lx += 1; } return new int[]{wx, lx}; } private void cursor_up() { cursor_up(1); } private void cursor_up(int n) { cy = Math.max(scroll_area_y0, cy - n); setDirty(); } private void cursor_down() { cursor_down(1); } private void cursor_down(int n) { cy = Math.min(scroll_area_y1 - 1, cy + n); setDirty(); } private void cursor_left() { cursor_left(1); } private void cursor_left(int n) { eol = false; cx = Math.max(0, cx - n); setDirty(); } private void cursor_right() { cursor_right(1); } private void cursor_right(int n) { eol = cx + n >= width; cx = Math.min(width - 1, cx + n); setDirty(); } private void cursor_set_x(int x) { eol = false; cx = Math.max(0, x); setDirty(); } private void cursor_set_y(int y) { cy = Math.max(0, Math.min(height - 1, y)); setDirty(); } private void cursor_set(int y, int x) { cursor_set_x(x); cursor_set_y(y); } // // Dumb terminal // private void ctrl_BS() { int dy = (cx - 1) / width; cursor_set(Math.max(scroll_area_y0, cy + dy), (cx - 1) % width); } private void ctrl_HT() { ctrl_HT(1); } private void ctrl_HT(int n) { if (n > 0 && cx >= width) { return; } if (n <= 0 && cx == 0) { return; } int ts = -1; for (int i = 0; i < tab_stops.size(); i++) { if (cx >= tab_stops.get(i)) { ts = i; } } ts += n; if (ts < tab_stops.size() && ts >= 0) { cursor_set_x(tab_stops.get(ts)); } else { cursor_set_x(width - 1); } } private void ctrl_LF() { if (vt100_mode_lfnewline) { ctrl_CR(); } if (cy == scroll_area_y1 - 1) { scroll_area_up(scroll_area_y0, scroll_area_y1); } else { cursor_down(); } } private void ctrl_CR() { cursor_set_x(0); } private boolean dumb_write(int c) { if (c < 32) { if (c == 8) { ctrl_BS(); } else if (c == 9) { ctrl_HT(); } else if (c >= 10 && c <= 12) { ctrl_LF(); } else if (c == 13) { ctrl_CR(); } return true; } return false; } private void dumb_echo(int c) { if (eol) { if (vt100_mode_autowrap) { ctrl_CR(); ctrl_LF(); } else { cx = cursor_line_width(c)[1] - 1; } } if (vt100_mode_insert) { scroll_line_right(cy, cx); } if (vt100_charset_is_single_shift) { vt100_charset_is_single_shift = false; } else if (vt100_charset_is_graphical && ((c & 0xffe0) == 0x0060)) { c = vt100_charset_graph[c - 0x60]; } poke(cy, cx, new long[]{attr | c}); cursor_right(); } // // VT100 // private void vt100_charset_update() { vt100_charset_is_graphical = (vt100_charset_g[vt100_charset_g_sel] == 2); } private void vt100_charset_set(int g) { // Invoke active character set vt100_charset_g_sel = g; vt100_charset_update(); } private void vt100_charset_select(int g, int charset) { // Select charset vt100_charset_g[g] = charset; vt100_charset_update(); } private void vt100_setmode(String p, boolean state) { // Set VT100 mode String[] ps = vt100_parse_params(p, new String[0]); for (String m : ps) { // 1 : GATM: Guarded area transfer // 2 : KAM: Keyboard action // 3 : CRM: Control representation switch (m) { case "4": // Insertion replacement mode vt100_mode_insert = state; break; // 5 : SRTM: Status reporting transfer // 7 : VEM: Vertical editing // 10 : HEM: Horizontal editing // 11 : PUM: Positioning nit // 12 : SRM: Send/receive // 13 : FEAM: Format effector action // 14 : FETM: Format effector transfer // 15 : MATM: Multiple area transfer // 16 : TTM: Transfer termination // 17 : SATM: Selected area transfer // 18 : TSM: Tabulation stop // 19 : EBM: Editing boundary case "20": // LNM: Line feed/new line vt100_mode_lfnewline = state; break; case "?1": // DECCKM: Cursor keys vt100_mode_cursorkey = state; break; // ?2 : DECANM: ANSI case "?3": // DECCOLM: Column if (vt100_mode_column_switch) { if (state) { width = 132; } else { width = 80; } reset_screen(); } break; // ?4 : DECSCLM: Scrolling case "?5": // DECSCNM: Screen vt100_mode_inverse = state; break; case "?6": // DECOM: Origin vt100_mode_origin = state; if (state) { cursor_set(scroll_area_y0, 0); } else { cursor_set(0, 0); } break; case "?7": // DECAWM: Autowrap vt100_mode_autowrap = state; break; // ?8 : DECARM: Autorepeat // ?9 : Interlacing // ?18 : DECPFF: Print form feed // ?19 : DECPEX: Printer extent case "?25": // DECTCEM: Text cursor enable vt100_mode_cursor = state; break; // ?34 : DECRLM: Cursor direction, right to left // ?35 : DECHEBM: Hebrew keyboard mapping // ?36 : DECHEM: Hebrew encoding mode case "?40": // Column switch control vt100_mode_column_switch = state; break; // ?42 : DECNRCM: National replacement character set case "?1049": // Alternate screen mode if ((state && !vt100_mode_alt_screen) || (!state && vt100_mode_alt_screen)) { long[][] s = screen; screen = screen2; screen2 = s; Map map = vt100_saved; vt100_saved = vt100_saved2; vt100_saved2 = map; int c; c = vt100_alternate_saved_cx; vt100_alternate_saved_cx = cx; cx = Math.min(c, width - 1); c = vt100_alternate_saved_cy; vt100_alternate_saved_cy = cy; cy = Math.min(c, height - 1); } vt100_mode_alt_screen = state; break; // ?57 : DECNAKB: Greek keyboard mapping case "?67": // DECBKM: Backarrow key vt100_mode_backspace = state; break; // ?98 : DECARSM: auto-resize // ?101 : DECCANSM: Conceal answerback message // ?109 : DECCAPSLK: caps lock } } } private void ctrl_SO() { vt100_charset_set(1); } private void ctrl_SI() { vt100_charset_set(0); } private void esc_CSI() { vt100_parse_reset(State.Csi); } private void esc_DECALN() { fill(0, 0, height, width, 0x00ff0045); } private void esc_G0_0() { vt100_charset_select(0, 0); } private void esc_G0_1() { vt100_charset_select(0, 1); } private void esc_G0_2() { vt100_charset_select(0, 2); } private void esc_G0_3() { vt100_charset_select(0, 3); } private void esc_G0_4() { vt100_charset_select(0, 4); } private void esc_G1_0() { vt100_charset_select(1, 0); } private void esc_G1_1() { vt100_charset_select(1, 1); } private void esc_G1_2() { vt100_charset_select(1, 2); } private void esc_G1_3() { vt100_charset_select(1, 3); } private void esc_G1_4() { vt100_charset_select(1, 4); } private void esc_DECSC() { vt100_saved = new HashMap<>(); vt100_saved.put("cx", cx); vt100_saved.put("cy", cy); vt100_saved.put("attr", attr); vt100_saved.put("vt100_charset_g_sel", vt100_charset_g_sel); vt100_saved.put("vt100_charset_g", vt100_charset_g); vt100_saved.put("vt100_mode_autowrap", vt100_mode_autowrap); vt100_saved.put("vt100_mode_origin", vt100_mode_origin); } private void esc_DECRC() { cx = (Integer) vt100_saved.get("cx"); cy = (Integer) vt100_saved.get("cy"); attr = (Long) vt100_saved.get("attr"); vt100_charset_g_sel = (Integer) vt100_saved.get("vt100_charset_g_sel"); vt100_charset_g = (int[]) vt100_saved.get("vt100_charset_g"); vt100_charset_update(); vt100_mode_autowrap = (Boolean) vt100_saved.get("vt100_mode_autowrap"); vt100_mode_origin = (Boolean) vt100_saved.get("vt100_mode_origin"); } private void esc_IND() { ctrl_LF(); } private void esc_NEL() { ctrl_CR(); ctrl_LF(); } private void esc_HTS() { csi_CTC("0"); } private void esc_RI() { if (cy == scroll_area_y0) { scroll_area_down(scroll_area_y0, scroll_area_y1); } else { cursor_up(); } } private void esc_SS2() { vt100_charset_is_single_shift = true; } private void esc_SS3() { vt100_charset_is_single_shift = true; } private void esc_DCS() { vt100_parse_reset(State.Str); } private void esc_SOS() { vt100_parse_reset(State.Str); } private void esc_DECID() { csi_DA("0"); } private void esc_ST() { } private void esc_OSC() { vt100_parse_reset(State.Str); } private void esc_PM() { vt100_parse_reset(State.Str); } private void esc_APC() { vt100_parse_reset(State.Str); } private void esc_RIS() { reset_hard(); } private void csi_ICH(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); scroll_line_right(cy, cx, ps[0]); } private void csi_CUU(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); cursor_up(Math.max(1, ps[0])); } private void csi_CUD(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); cursor_down(Math.max(1, ps[0])); } private void csi_CUF(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); cursor_right(Math.max(1, ps[0])); } private void csi_CUB(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); cursor_left(Math.max(1, ps[0])); } private void csi_CNL(String p) { csi_CUD(p); ctrl_CR(); } private void csi_CPL(String p) { csi_CUU(p); ctrl_CR(); } private void csi_CHA(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); cursor_set_x(ps[0] - 1); } private void csi_CUP(String p) { int[] ps = vt100_parse_params(p, new int[]{1, 1}); if (vt100_mode_origin) { cursor_set(scroll_area_y0 + ps[0] - 1, ps[1] - 1); } else { cursor_set(ps[0] - 1, ps[1] - 1); } } private void csi_CHT(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); ctrl_HT(Math.max(1, ps[0])); } private void csi_ED(String p) { String[] ps = vt100_parse_params(p, new String[]{"0"}); if ("0".equals(ps[0])) { clear(cy, cx, height, width); } else if ("1".equals(ps[0])) { clear(0, 0, cy + 1, cx + 1); } else if ("2".equals(ps[0])) { clear(0, 0, height, width); } } private void csi_EL(String p) { String[] ps = vt100_parse_params(p, new String[]{"0"}); if ("0".equals(ps[0])) { clear(cy, cx, cy + 1, width); } else if ("1".equals(ps[0])) { clear(cy, 0, cy + 1, cx + 1); } else if ("2".equals(ps[0])) { clear(cy, 0, cy + 1, width); } } private void csi_IL(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); if (cy >= scroll_area_y0 && cy < scroll_area_y1) { scroll_area_down(cy, scroll_area_y1, Math.max(1, ps[0])); } } private void csi_DL(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); if (cy >= scroll_area_y0 && cy < scroll_area_y1) { scroll_area_up(cy, scroll_area_y1, Math.max(1, ps[0])); } } private void csi_DCH(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); scroll_line_left(cy, cx, Math.max(1, ps[0])); } private void csi_SU(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); scroll_area_up(scroll_area_y0, scroll_area_y1, Math.max(1, ps[0])); } private void csi_SD(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); scroll_area_down(scroll_area_y0, scroll_area_y1, Math.max(1, ps[0])); } private void csi_CTC(String p) { String[] ps = vt100_parse_params(p, new String[]{"0"}); for (String m : ps) { if ("0".equals(m)) { if (tab_stops.indexOf(cx) < 0) { tab_stops.add(cx); Collections.sort(tab_stops); } } else if ("2".equals(m)) { tab_stops.remove(Integer.valueOf(cx)); } else if ("5".equals(m)) { tab_stops = new ArrayList<>(); } } } private void csi_ECH(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); int n = Math.min(width - cx, Math.max(1, ps[0])); clear(cy, cx, cy + 1, cx + n); } private void csi_CBT(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); ctrl_HT(1 - Math.max(1, ps[0])); } private void csi_HPA(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); cursor_set_x(ps[0] - 1); } private void csi_HPR(String p) { csi_CUF(p); } private void csi_REP(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); if (vt100_lastchar < 32) { return; } int n = Math.min(2000, Math.max(1, ps[0])); while (n-- > 0) { dumb_echo(vt100_lastchar); } vt100_lastchar = 0; } private void csi_DA(String p) { String[] ps = vt100_parse_params(p, new String[]{"0"}); if ("0".equals(ps[0])) { vt100_out = "\u001b[?1;2c"; } else if (">0".equals(ps[0]) || ">".equals(ps[0])) { vt100_out = "\u001b[>0;184;0c"; } } private void csi_VPA(String p) { int[] ps = vt100_parse_params(p, new int[]{1}); cursor_set_y(ps[0] - 1); } private void csi_VPR(String p) { csi_CUD(p); } private void csi_HVP(String p) { csi_CUP(p); } private void csi_TBC(String p) { String[] ps = vt100_parse_params(p, new String[]{"0"}); if ("0".equals(ps[0])) { csi_CTC("2"); } else if ("3".equals(ps[0])) { csi_CTC("5"); } } private void csi_SM(String p) { vt100_setmode(p, true); } private void csi_RM(String p) { vt100_setmode(p, false); } private void csi_SGR(String p) { // Attribute mask: 0xYXFFFBBB00000000L // X: Bit 0 - Underlined // Bit 1 - Negative // Bit 2 - Concealed // Bit 3 - Bold // Y: Bit 0 - Foreground set // Bit 1 - Background set // F: Foreground r-g-b // B: Background r-g-b int[] ps = vt100_parse_params(p, new int[]{0}); for (int i = 0; i < ps.length; i++) { int m = ps[i]; if (m == 0) { attr = 0x00000000L << 32; } else if (m == 1) { attr |= 0x08000000L << 32; // bold } else if (m == 4) { attr |= 0x01000000L << 32; // underline } else if (m == 7) { attr |= 0x02000000L << 32; // negative } else if (m == 8) { attr |= 0x04000000L << 32; // conceal } else if (m == 21) { attr &= 0xf7ffffffL << 32; // bold off } else if (m == 24) { attr &= 0xfeffffffL << 32; // underline off } else if (m == 27) { attr &= 0xfdffffffL << 32; // negative off } else if (m == 28) { attr &= 0xfbffffffL << 32; // conceal off } else if (m >= 30 && m <= 37) { attr = (attr & (0xef000fffL << 32)) | (0x10000000L << 32) | (col24(m - 30) << 44); // foreground } else if (m == 38) { m = ++i < ps.length ? ps[i] : 0; if (m == 5) { m = ++i < ps.length ? ps[i] : 0; attr = (attr & (0xef000fffL << 32)) | (0x10000000L << 32) | (col24(m) << 44); // foreground } } else if (m == 39) { attr &= 0xef000fffL << 32; } else if (m >= 40 && m <= 47) { attr = (attr & (0xdffff000L << 32)) | (0x20000000L << 32) | (col24(m - 40) << 32); // background } else if (m == 48) { m = ++i < ps.length ? ps[i] : 0; if (m == 5) { m = ++i < ps.length ? ps[i] : 0; attr = (attr & (0xdffff000L << 32)) | (0x20000000L << 32) | (col24(m) << 32); // background } } else if (m == 49) { attr &= 0xdf000fffL << 32; } else if (m >= 90 && m <= 97) { attr = (attr & (0xef000fffL << 32)) | (0x10000000L << 32) | (col24(m - 90 + 8) << 44); // foreground } else if (m >= 100 && m <= 107) { attr = (attr & (0xdffff000L << 32)) | (0x20000000L << 32) | (col24(m - 100 + 8) << 32); // background } } } private long col24(int col) { int c = AttributedCharSequence.rgbColor(col); int r = (c >> 16) & 0xFF; int g = (c >> 8) & 0xFF; int b = (c >> 0) & 0xFF; return ((r >> 4) << 8) | ((g >> 4) << 4) | ((b >> 4) << 0); } private void csi_DSR(String p) { String[] ps = vt100_parse_params(p, new String[]{"0"}); if ("5".equals(ps[0])) { vt100_out = "\u001b[0n"; } else if ("6".equals(ps[0])) { vt100_out = "\u001b[" + (cy + 1) + ";" + (cx + 1) + "R"; } else if ("7".equals(ps[0])) { vt100_out = "gogo-term"; } else if ("8".equals(ps[0])) { vt100_out = "1.0-SNAPSHOT"; } else if ("?6".equals(ps[0])) { vt100_out = "\u001b[" + (cy + 1) + ";" + (cx + 1) + ";0R"; } else if ("?15".equals(ps[0])) { vt100_out = "\u001b[?13n"; } else if ("?25".equals(ps[0])) { vt100_out = "\u001b[?20n"; } else if ("?26".equals(ps[0])) { vt100_out = "\u001b[?27;1n"; } else if ("?53".equals(ps[0])) { vt100_out = "\u001b[?53n"; } // ?75 : Data Integrity report // ?62 : Macro Space report // ?63 : Memory Checksum report } private void csi_DECSTBM(String p) { int[] ps = vt100_parse_params(p, new int[]{1, height}); scroll_area_set(ps[0] - 1, ps[1]); if (vt100_mode_origin) { cursor_set(scroll_area_y0, 0); } else { cursor_set(0, 0); } } private void csi_SCP(String p) { vt100_saved_cx = cx; vt100_saved_cy = cy; } private void csi_RCP(String p) { cx = vt100_saved_cx; cy = vt100_saved_cy; } private void csi_DECREQTPARM(String p) { String[] ps = vt100_parse_params(p, new String[0]); if ("0".equals(ps[0])) { vt100_out = "\u001b[2;1;1;112;112;1;0x"; } else if ("1".equals(ps[0])) { vt100_out = "\u001b[3;1;1;112;112;1;0x"; } } private void csi_DECSTR(String p) { reset_soft(); } // // VT100 parser // private String[] vt100_parse_params(String p, String[] defaults) { String prefix = ""; if (p.length() > 0) { if (p.charAt(0) >= '<' && p.charAt(0) <= '?') { prefix = "" + p.charAt(0); p = p.substring(1); } } String[] ps = p.split(";"); int n = Math.max(ps.length, defaults.length); String[] values = new String[n]; for (int i = 0; i < n; i++) { String value = null; if (i < ps.length && ps[i].length() > 0) { value = prefix + ps[i]; } if (value == null && i < defaults.length) { value = defaults[i]; } if (value == null) { value = ""; } values[i] = value; } return values; } private int[] vt100_parse_params(String p, int[] defaults) { String prefix = ""; p = p == null ? "" : p; if (p.length() > 0) { if (p.charAt(0) >= '<' && p.charAt(0) <= '?') { prefix = p.substring(0, 1); p = p.substring(1); } } String[] ps = p.split(";"); int n = Math.max(ps.length, defaults.length); int[] values = new int[n]; for (int i = 0; i < n; i++) { Integer value = null; if (i < ps.length) { String v = prefix + ps[i]; try { value = Integer.parseInt(v); } catch (NumberFormatException e) { } } if (value == null && i < defaults.length) { value = defaults[i]; } if (value == null) { value = 0; } values[i] = value; } return values; } private void vt100_parse_reset() { vt100_parse_reset(State.None); } private void vt100_parse_reset(State state) { vt100_parse_state = state; vt100_parse_len = 0; vt100_parse_func = 0; vt100_parse_param = ""; } private void vt100_parse_process() { if (vt100_parse_state == State.Esc) { switch (vt100_parse_func) { case 0x0036: /* DECBI */ break; case 0x0037: esc_DECSC(); break; case 0x0038: esc_DECRC(); break; case 0x0042: /* BPH */ break; case 0x0043: /* NBH */ break; case 0x0044: esc_IND(); break; case 0x0045: esc_NEL(); break; case 0x0046: /* SSA */ esc_NEL(); break; case 0x0048: esc_HTS(); break; case 0x0049: /* HTJ */ break; case 0x004A: /* VTS */ break; case 0x004B: /* PLD */ break; case 0x004C: /* PLU */ break; case 0x004D: esc_RI(); break; case 0x004E: esc_SS2(); break; case 0x004F: esc_SS3(); break; case 0x0050: esc_DCS(); break; case 0x0051: /* PU1 */ break; case 0x0052: /* PU2 */ break; case 0x0053: /* STS */ break; case 0x0054: /* CCH */ break; case 0x0055: /* MW */ break; case 0x0056: /* SPA */ break; case 0x0057: /* ESA */ break; case 0x0058: esc_SOS(); break; case 0x005A: /* SCI */ break; case 0x005B: esc_CSI(); break; case 0x005C: esc_ST(); break; case 0x005D: esc_OSC(); break; case 0x005E: esc_PM(); break; case 0x005F: esc_APC(); break; case 0x0060: /* DMI */ break; case 0x0061: /* INT */ break; case 0x0062: /* EMI */ break; case 0x0063: esc_RIS(); break; case 0x0064: /* CMD */ break; case 0x006C: /* RM */ break; case 0x006E: /* LS2 */ break; case 0x006F: /* LS3 */ break; case 0x007C: /* LS3R */ break; case 0x007D: /* LS2R */ break; case 0x007E: /* LS1R */ break; case 0x2338: esc_DECALN(); break; case 0x2841: esc_G0_0(); break; case 0x2842: esc_G0_1(); break; case 0x2830: esc_G0_2(); break; case 0x2831: esc_G0_3(); break; case 0x2832: esc_G0_4(); break; case 0x2930: esc_G1_2(); break; case 0x2931: esc_G1_3(); break; case 0x2932: esc_G1_4(); break; case 0x2941: esc_G1_0(); break; case 0x2942: esc_G1_1(); break; } if (vt100_parse_state == State.Esc) { vt100_parse_reset(); } } else { switch (vt100_parse_func) { case 0x0040: csi_ICH(vt100_parse_param); break; case 0x0041: csi_CUU(vt100_parse_param); break; case 0x0042: csi_CUD(vt100_parse_param); break; case 0x0043: csi_CUF(vt100_parse_param); break; case 0x0044: csi_CUB(vt100_parse_param); break; case 0x0045: csi_CNL(vt100_parse_param); break; case 0x0046: csi_CPL(vt100_parse_param); break; case 0x0047: csi_CHA(vt100_parse_param); break; case 0x0048: csi_CUP(vt100_parse_param); break; case 0x0049: csi_CHT(vt100_parse_param); break; case 0x004A: csi_ED(vt100_parse_param); break; case 0x004B: csi_EL(vt100_parse_param); break; case 0x004C: csi_IL(vt100_parse_param); break; case 0x004D: csi_DL(vt100_parse_param); break; case 0x004E: /* EF */ break; case 0x004F: /* EA */ break; case 0x0050: csi_DCH(vt100_parse_param); break; case 0x0051: /* SEE */ break; case 0x0052: /* CPR */ break; case 0x0053: csi_SU(vt100_parse_param); break; case 0x0054: csi_SD(vt100_parse_param); break; case 0x0055: /* NP */ break; case 0x0056: /* PP */ break; case 0x0057: csi_CTC(vt100_parse_param); break; case 0x0058: csi_ECH(vt100_parse_param); break; case 0x0059: /* CVT */ break; case 0x005A: csi_CBT(vt100_parse_param); break; case 0x005B: /* SRS */ break; case 0x005C: /* PTX */ break; case 0x005D: /* SDS */ break; case 0x005E: /* SIMD */ break; case 0x0060: csi_HPA(vt100_parse_param); break; case 0x0061: csi_HPR(vt100_parse_param); break; case 0x0062: csi_REP(vt100_parse_param); break; case 0x0063: csi_DA(vt100_parse_param); break; case 0x0064: csi_VPA(vt100_parse_param); break; case 0x0065: csi_VPR(vt100_parse_param); break; case 0x0066: csi_HVP(vt100_parse_param); break; case 0x0067: csi_TBC(vt100_parse_param); break; case 0x0068: csi_SM(vt100_parse_param); break; case 0x0069: /* MC */ break; case 0x006A: /* HPB */ break; case 0x006B: /* VPB */ break; case 0x006C: csi_RM(vt100_parse_param); break; case 0x006D: csi_SGR(vt100_parse_param); break; case 0x006E: csi_DSR(vt100_parse_param); break; case 0x006F: /* DAQ */ break; case 0x0072: csi_DECSTBM(vt100_parse_param); break; case 0x0073: csi_SCP(vt100_parse_param); break; case 0x0075: csi_RCP(vt100_parse_param); break; case 0x0078: csi_DECREQTPARM(vt100_parse_param); break; case 0x2040: /* SL */ break; case 0x2041: /* SR */ break; case 0x2042: /* GSM */ break; case 0x2043: /* GSS */ break; case 0x2044: /* FNT */ break; case 0x2045: /* TSS */ break; case 0x2046: /* JFY */ break; case 0x2047: /* SPI */ break; case 0x2048: /* QUAD */ break; case 0x2049: /* SSU */ break; case 0x204A: /* PFS */ break; case 0x204B: /* SHS */ break; case 0x204C: /* SVS */ break; case 0x204D: /* IGS */ break; case 0x204E: /* deprecated: HTSA */ break; case 0x204F: /* IDCS */ break; case 0x2050: /* PPA */ break; case 0x2051: /* PPR */ break; case 0x2052: /* PPB */ break; case 0x2053: /* SPD */ break; case 0x2054: /* DTA */ break; case 0x2055: /* SLH */ break; case 0x2056: /* SLL */ break; case 0x2057: /* FNK */ break; case 0x2058: /* SPQR */ break; case 0x2059: /* SEF */ break; case 0x205A: /* PEC */ break; case 0x205B: /* SSW */ break; case 0x205C: /* SACS */ break; case 0x205D: /* SAPV */ break; case 0x205E: /* STAB */ break; case 0x205F: /* GCC */ break; case 0x2060: /* TAPE */ break; case 0x2061: /* TALE */ break; case 0x2062: /* TAC */ break; case 0x2063: /* TCC */ break; case 0x2064: /* TSR */ break; case 0x2065: /* SCO */ break; case 0x2066: /* SRCS */ break; case 0x2067: /* SCS */ break; case 0x2068: /* SLS */ break; case 0x2069: /* SPH */ break; case 0x206A: /* SPL */ break; case 0x206B: /* SCP */ break; case 0x2170: csi_DECSTR(vt100_parse_param); break; case 0x2472: /* DECCARA */ break; case 0x2477: /* DECRQPSR */ break; } if (vt100_parse_state == State.Csi) { vt100_parse_reset(); } } } private boolean vt100_write(int c) { if (c < 32) { if (c == 27) { vt100_parse_reset(State.Esc); return true; } else if (c == 14) { ctrl_SO(); } else if (c == 15) { ctrl_SI(); } } else if ((c & 0xffe0) == 0x0080) { vt100_parse_reset(State.Esc); vt100_parse_func = (char) (c - 0x0040); vt100_parse_process(); return true; } if (vt100_parse_state != State.None) { if (vt100_parse_state == State.Str) { if (c >= 32) { return true; } vt100_parse_reset(); } else { if (c < 32) { if (c == 24 || c == 26) { vt100_parse_reset(); return true; } } else { vt100_parse_len += 1; if (vt100_parse_len > 32) { vt100_parse_reset(); } else { int msb = c & 0xf0; if (msb == 0x20) { vt100_parse_func <<= 8; vt100_parse_func += (char) c; } else if (msb == 0x30 && vt100_parse_state == State.Csi) { vt100_parse_param += new String(new char[]{(char) c}); } else { vt100_parse_func <<= 8; vt100_parse_func += (char) c; vt100_parse_process(); } return true; } } } } vt100_lastchar = c; return false; } // // Dirty // public boolean isDirty() { return dirty.compareAndSet(true, false); } public synchronized void waitDirty() throws InterruptedException { while (!dirty.compareAndSet(true, false)) { wait(); } } protected synchronized void setDirty() { dirty.set(true); notifyAll(); } // // External interface // public synchronized boolean setSize(int w, int h) { if (w < 2 || w > 256 || h < 2 || h > 256) { return false; } // Set width for (int i = 0; i < height; i++) { if (screen[i].length < w) { screen[i] = Arrays.copyOf(screen[i], w); } if (screen2[i].length < w) { screen2[i] = Arrays.copyOf(screen2[i], w); } } if (cx >= w) { cx = w - 1; } // Set height if (h < height) { int needed = height - h; // Delete as many lines as possible from the bottom int avail = height - 1 - cy; if (avail > 0) { if (avail > needed) { avail = needed; } screen = Arrays.copyOfRange(screen, 0, height - avail); } needed -= avail; // Move lines to history for (int i = 0; i < needed; i++) { history.add(screen[i]); } screen = Arrays.copyOfRange(screen, needed, screen.length); cy -= needed; } else if (h > height) { int needed = h - height; // Pull lines from history int avail = history.size(); if (avail > needed) { avail = needed; } long[][] sc = new long[h][]; if (avail > 0) { for (int i = 0; i < avail; i++) { sc[i] = history.remove(history.size() - avail + i); } cy += avail; } System.arraycopy(screen, 0, sc, avail, screen.length); for (int i = avail + screen.length; i < sc.length; i++) { sc[i] = new long[w]; Arrays.fill(sc[i], attr | 0x00000020); } screen = sc; } screen2 = (long[][]) Array.newInstance(long.class, h, w); for (int i = 0; i < h; i++) { Arrays.fill(screen2[i], attr | 0x00000020); } // Scroll parameters scroll_area_y0 = Math.min(h, scroll_area_y0); scroll_area_y1 = scroll_area_y1 == height ? h : Math.min(h, scroll_area_y1); // Cursor position cx = Math.min(w - 1, cx); cy = Math.min(h - 1, cy); width = w; height = h; setDirty(); return true; } public synchronized String read() { String d = vt100_out; vt100_out = ""; return d; } public synchronized String pipe(String d) { String o = ""; for (char c : d.toCharArray()) { if (vt100_keyfilter_escape) { vt100_keyfilter_escape = false; if (vt100_mode_cursorkey) { switch (c) { case '~': o += "~"; break; case 'A': o += "\u001bOA"; break; case 'B': o += "\u001bOB"; break; case 'C': o += "\u001bOC"; break; case 'D': o += "\u001bOD"; break; case 'F': o += "\u001bOF"; break; case 'H': o += "\u001bOH"; break; case '1': o += "\u001b[5~"; break; case '2': o += "\u001b[6~"; break; case '3': o += "\u001b[2~"; break; case '4': o += "\u001b[3~"; break; case 'a': o += "\u001bOP"; break; case 'b': o += "\u001bOQ"; break; case 'c': o += "\u001bOR"; break; case 'd': o += "\u001bOS"; break; case 'e': o += "\u001b[15~"; break; case 'f': o += "\u001b[17~"; break; case 'g': o += "\u001b[18~"; break; case 'h': o += "\u001b[19~"; break; case 'i': o += "\u001b[20~"; break; case 'j': o += "\u001b[21~"; break; case 'k': o += "\u001b[23~"; break; case 'l': o += "\u001b[24~"; break; } } else { switch (c) { case '~': o += "~"; break; case 'A': o += "\u001b[A"; break; case 'B': o += "\u001b[B"; break; case 'C': o += "\u001b[C"; break; case 'D': o += "\u001b[D"; break; case 'F': o += "\u001b[F"; break; case 'H': o += "\u001b[H"; break; case '1': o += "\u001b[5~"; break; case '2': o += "\u001b[6~"; break; case '3': o += "\u001b[2~"; break; case '4': o += "\u001b[3~"; break; case 'a': o += "\u001bOP"; break; case 'b': o += "\u001bOQ"; break; case 'c': o += "\u001bOR"; break; case 'd': o += "\u001bOS"; break; case 'e': o += "\u001b[15~"; break; case 'f': o += "\u001b[17~"; break; case 'g': o += "\u001b[18~"; break; case 'h': o += "\u001b[19~"; break; case 'i': o += "\u001b[20~"; break; case 'j': o += "\u001b[21~"; break; case 'k': o += "\u001b[23~"; break; case 'l': o += "\u001b[24~"; break; } } } else if (c == '~') { vt100_keyfilter_escape = true; } else if (c == 127) { if (vt100_mode_backspace) { o += (char) 8; } else { o += (char) 127; } } else { o += c; if (vt100_mode_lfnewline && c == 13) { o += (char) 10; } } } return o; } public synchronized boolean write(CharSequence d) { d.codePoints().forEachOrdered(c -> { if (!vt100_write(c) && !dumb_write(c) && c <= 0xffff) { dumb_echo(c); } }); return true; } public synchronized void dump(long[] fullscreen, int ftop, int fleft, int fheight, int fwidth, int[] cursor) { int cx = Math.min(this.cx, width - 1); int cy = this.cy; for (int y = 0; y < Math.min(height, fheight - ftop); y++) { System.arraycopy(screen[y], 0, fullscreen, (y + ftop) * fwidth + fleft, width); } if (cursor != null) { cursor[0] = cx + fleft; cursor[1] = cy + ftop; } } public synchronized String dump(long timeout, boolean forceDump) throws InterruptedException { if (!dirty.get() && timeout > 0) { wait(timeout); } if (dirty.compareAndSet(true, false) || forceDump) { StringBuilder sb = new StringBuilder(); int prev_attr = -1; int cx = Math.min(this.cx, width - 1); int cy = this.cy; sb.append("
");
            for (int y = 0; y < height; y++) {
                int wx = 0;
                for (int x = 0; x < width; x++) {
                    long d = screen[y][x];
                    int c = (int) (d & 0xffffffff);
                    int a = (int) (d >> 32);
                    if (cy == y && cx == x && vt100_mode_cursor) {
                        a = a & 0xfff0 | 0x000c;
                    }
                    if (a != prev_attr) {
                        if (prev_attr != -1) {
                            sb.append("");
                        }
                        int bg = a & 0x000000ff;
                        int fg = (a & 0x0000ff00) >> 8;
                        boolean inv = (a & 0x00020000) != 0;
                        boolean inv2 = vt100_mode_inverse;
                        if (inv && !inv2 || inv2 && !inv) {
                            int i = fg;
                            fg = bg;
                            bg = i;
                        }
                        if ((a & 0x00040000) != 0) {
                            fg = 0x0c;
                        }
                        String ul;
                        if ((a & 0x00010000) != 0) {
                            ul = " ul";
                        } else {
                            ul = "";
                        }
                        String b;
                        if ((a & 0x00080000) != 0) {
                            b = " b";
                        } else {
                            b = "";
                        }
                        sb.append("");
                        prev_attr = a;
                    }
                    switch (c) {
                        case '&':
                            sb.append("&");
                            break;
                        case '<':
                            sb.append("<");
                            break;
                        case '>':
                            sb.append(">");
                            break;
                        default:
                            wx += utf8_charwidth(c);
                            if (wx <= width) {
                                sb.append((char) c);
                            }
                            break;
                    }
                }
                sb.append("\n");
            }
            sb.append("
"); return sb.toString(); } return null; } public String toString() { StringBuilder sb = new StringBuilder(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { sb.appendCodePoint((int) (screen[y][x] & 0xffffffffL)); } sb.append("\n"); } return sb.toString(); } } jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/Source.java000066400000000000000000000047501311544710100261150ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import java.io.File; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; public interface Source { String getName(); InputStream read() throws IOException; class URLSource implements Source { final URL url; final String name; public URLSource(URL url, String name) { this.url = Objects.requireNonNull(url); this.name = name; } @Override public String getName() { return name; } @Override public InputStream read() throws IOException { return url.openStream(); } } class PathSource implements Source { final Path path; final String name; public PathSource(File file, String name) { this(Objects.requireNonNull(file).toPath(), name); } public PathSource(Path path, String name) { this.path = Objects.requireNonNull(path); this.name = name; } @Override public String getName() { return name; } @Override public InputStream read() throws IOException { return Files.newInputStream(path); } } class InputStreamSource implements Source { final InputStream in; final String name; public InputStreamSource(InputStream in, boolean close, String name) { Objects.requireNonNull(in); if (close) { this.in = in; } else { this.in = new FilterInputStream(in) { @Override public void close() throws IOException { } }; } this.name = name; } @Override public String getName() { return name; } @Override public InputStream read() throws IOException { return in; } } class StdInSource extends InputStreamSource { public StdInSource() { super(System.in, false, null); } } } jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/TTop.java000066400000000000000000000603571311544710100255500ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import org.jline.keymap.BindingReader; import org.jline.keymap.KeyMap; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.utils.*; import java.io.IOException; import java.io.PrintStream; import java.lang.management.*; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import static org.jline.builtins.TTop.Align.Left; import static org.jline.builtins.TTop.Align.Right; /** * Thread Top implementation. * * TODO: option modification at runtime (such as implemented in less) is not currently supported * TODO: one possible addition would be to detect deadlock threads and display them in a specific way */ public class TTop { public static final String STAT_UPTIME = "uptime"; public static final String STAT_TID = "tid"; public static final String STAT_NAME = "name"; public static final String STAT_STATE = "state"; public static final String STAT_BLOCKED_TIME = "blocked_time"; public static final String STAT_BLOCKED_COUNT = "blocked_count"; public static final String STAT_WAITED_TIME = "waited_time"; public static final String STAT_WAITED_COUNT = "waited_count"; public static final String STAT_LOCK_NAME = "lock_name"; public static final String STAT_LOCK_OWNER_ID = "lock_owner_id"; public static final String STAT_LOCK_OWNER_NAME = "lock_owner_name"; public static final String STAT_USER_TIME = "user_time"; public static final String STAT_USER_TIME_PERC = "user_time_perc"; public static final String STAT_CPU_TIME = "cpu_time"; public static final String STAT_CPU_TIME_PERC = "cpu_time_perc"; public List sort; public long delay; public List stats; public int nthreads; public enum Align { Left, Right }; public enum Operation { INCREASE_DELAY, DECREASE_DELAY, HELP, EXIT, CLEAR, REVERSE } public static void ttop(Terminal terminal, PrintStream out, PrintStream err, String[] argv) throws Exception { final String[] usage = { "ttop - display and update sorted information about threads", "Usage: ttop [OPTIONS]", " -? --help Show help", " -o --order=ORDER Comma separated list of sorting keys", " -t --stats=STATS Comma separated list of stats to display", " -s --seconds=SECONDS Delay between updates in seconds", " -m --millis=MILLIS Delay between updates in milliseconds", " -n --nthreads=NTHREADS Only display up to NTHREADS threads", }; Options opt = Options.compile(usage).parse(argv); if (opt.isSet("help")) { opt.usage(err); return; } TTop ttop = new TTop(terminal); ttop.sort = opt.isSet("order") ? Arrays.asList(opt.get("order").split(",")) : null; ttop.delay = opt.isSet("seconds") ? opt.getNumber("seconds") * 1000 : ttop.delay; ttop.delay = opt.isSet("millis") ? opt.getNumber("millis") : ttop.delay; ttop.stats = opt.isSet("stats") ? Arrays.asList(opt.get("stats").split(",")) : null; ttop.nthreads = opt.isSet("nthreads") ? opt.getNumber("nthreads") : ttop.nthreads; ttop.run(); } private final Map columns = new LinkedHashMap<>(); private final Terminal terminal; private final Display display; private final BindingReader bindingReader; private final KeyMap keys; private final Size size = new Size(); private Comparator>> comparator; // Internal cache data private Map> previous = new HashMap<>(); private Map> changes = new HashMap<>(); private Map widths = new HashMap<>(); public TTop(Terminal terminal) { this.terminal = terminal; this.display = new Display(terminal, true); this.bindingReader = new BindingReader(terminal.reader()); DecimalFormatSymbols dfs = new DecimalFormatSymbols(); dfs.setDecimalSeparator('.'); DecimalFormat perc = new DecimalFormat("0.00%", dfs); register(STAT_TID, Right, "TID", o -> String.format("%3d", (Long) o)); register(STAT_NAME, Left, "NAME", padcut(40)); register(STAT_STATE, Left, "STATE", o -> o.toString().toLowerCase()); register(STAT_BLOCKED_TIME, Right, "T-BLOCKED", o -> millis((Long) o)); register(STAT_BLOCKED_COUNT, Right, "#-BLOCKED", Object::toString); register(STAT_WAITED_TIME, Right, "T-WAITED", o -> millis((Long) o)); register(STAT_WAITED_COUNT, Right, "#-WAITED", Object::toString); register(STAT_LOCK_NAME, Left, "LOCK-NAME", Object::toString); register(STAT_LOCK_OWNER_ID, Right, "LOCK-OWNER-ID", id -> ((Long) id) >= 0 ? id.toString() : ""); register(STAT_LOCK_OWNER_NAME, Left, "LOCK-OWNER-NAME", name -> name != null ? name.toString() : ""); register(STAT_USER_TIME, Right, "T-USR", o -> nanos((Long) o)); register(STAT_CPU_TIME, Right, "T-CPU", o -> nanos((Long) o)); register(STAT_USER_TIME_PERC, Right, "%-USR", perc::format); register(STAT_CPU_TIME_PERC, Right, "%-CPU", perc::format); keys = new KeyMap<>(); bindKeys(keys); } public KeyMap getKeys() { return keys; } public void run() throws IOException, InterruptedException { comparator = buildComparator(sort); delay = delay > 0 ? Math.max(delay, 100) : 1000; if (stats == null || stats.isEmpty()) { stats = Arrays.asList(STAT_TID, STAT_NAME, STAT_STATE, STAT_CPU_TIME, STAT_LOCK_OWNER_ID); } Boolean isThreadContentionMonitoringEnabled = null; ThreadMXBean threadsBean = ManagementFactory.getThreadMXBean(); if (stats.contains(STAT_BLOCKED_TIME) || stats.contains(STAT_BLOCKED_COUNT) || stats.contains(STAT_WAITED_TIME) || stats.contains(STAT_WAITED_COUNT)) { if (threadsBean.isThreadContentionMonitoringSupported()) { isThreadContentionMonitoringEnabled = threadsBean.isThreadContentionMonitoringEnabled(); if (!isThreadContentionMonitoringEnabled) { threadsBean.setThreadContentionMonitoringEnabled(true); } } else { stats.removeAll(Arrays.asList(STAT_BLOCKED_TIME, STAT_BLOCKED_COUNT, STAT_WAITED_TIME, STAT_WAITED_COUNT)); } } Boolean isThreadCpuTimeEnabled = null; if (stats.contains(STAT_USER_TIME) || stats.contains(STAT_CPU_TIME)) { if (threadsBean.isThreadCpuTimeSupported()) { isThreadCpuTimeEnabled = threadsBean.isThreadCpuTimeEnabled(); if (!isThreadCpuTimeEnabled) { threadsBean.setThreadCpuTimeEnabled(true); } } else { stats.removeAll(Arrays.asList(STAT_USER_TIME, STAT_CPU_TIME)); } } size.copy(terminal.getSize()); Terminal.SignalHandler prevHandler = terminal.handle(Terminal.Signal.WINCH, this::handle); Attributes attr = terminal.enterRawMode(); try { // Use alternate buffer terminal.puts(InfoCmp.Capability.enter_ca_mode); terminal.puts(InfoCmp.Capability.keypad_xmit); terminal.puts(InfoCmp.Capability.cursor_invisible); terminal.writer().flush(); long t0 = System.currentTimeMillis(); Operation op; do { display(); checkInterrupted(); op = null; long delta = ((System.currentTimeMillis() - t0) / delay + 1) * delay + t0 - System.currentTimeMillis(); int ch = bindingReader.peekCharacter(delta); if (ch == -1) { op = Operation.EXIT; } else if (ch != NonBlockingReader.READ_EXPIRED) { op = bindingReader.readBinding(keys, null, false); } if (op == null) { continue; } switch (op) { case INCREASE_DELAY: delay = delay * 2; t0 = System.currentTimeMillis(); break; case DECREASE_DELAY: delay = Math.max(delay / 2, 16); t0 = System.currentTimeMillis(); break; case CLEAR: display.clear(); break; case REVERSE: comparator = comparator.reversed(); break; } } while (op != Operation.EXIT); } catch (InterruptedException ie) { // Do nothing } finally { terminal.setAttributes(attr); if (prevHandler != null) { terminal.handle(Terminal.Signal.WINCH, prevHandler); } // Use main buffer terminal.puts(InfoCmp.Capability.exit_ca_mode); terminal.puts(InfoCmp.Capability.keypad_local); terminal.puts(InfoCmp.Capability.cursor_visible); terminal.writer().flush(); if (isThreadContentionMonitoringEnabled != null) { threadsBean.setThreadContentionMonitoringEnabled(isThreadContentionMonitoringEnabled); } if (isThreadCpuTimeEnabled != null) { threadsBean.setThreadCpuTimeEnabled(isThreadCpuTimeEnabled); } } } private void handle(Terminal.Signal signal) { int prevw = size.getColumns(); size.copy(terminal.getSize()); try { if (size.getColumns() < prevw) { display.clear(); } display(); } catch (IOException e) { // ignore } } private List>> infos() { long ctime = ManagementFactory.getRuntimeMXBean().getUptime(); Long ptime = (Long) previous.computeIfAbsent(-1L, id -> new HashMap<>()).put(STAT_UPTIME, ctime); long delta = ptime != null ? ctime - ptime : 0L; ThreadMXBean threadsBean = ManagementFactory.getThreadMXBean(); ThreadInfo[] infos = threadsBean.dumpAllThreads(false, false); List>> threads = new ArrayList<>(); for (ThreadInfo ti : infos) { Map> t = new HashMap<>(); t.put(STAT_TID, ti.getThreadId()); t.put(STAT_NAME, ti.getThreadName()); t.put(STAT_STATE, ti.getThreadState()); if (threadsBean.isThreadContentionMonitoringEnabled()) { t.put(STAT_BLOCKED_TIME, ti.getBlockedTime()); t.put(STAT_BLOCKED_COUNT, ti.getBlockedCount()); t.put(STAT_WAITED_TIME, ti.getWaitedTime()); t.put(STAT_WAITED_COUNT, ti.getWaitedCount()); } t.put(STAT_LOCK_NAME, ti.getLockName()); t.put(STAT_LOCK_OWNER_ID, ti.getLockOwnerId()); t.put(STAT_LOCK_OWNER_NAME, ti.getLockOwnerName()); if (threadsBean.isThreadCpuTimeSupported() && threadsBean.isThreadCpuTimeEnabled()) { long tid = ti.getThreadId(), t0, t1; // Cpu t1 = threadsBean.getThreadCpuTime(tid); t0 = (Long) previous.computeIfAbsent(tid, id -> new HashMap<>()).getOrDefault(STAT_CPU_TIME, t1); t.put(STAT_CPU_TIME, t1); t.put(STAT_CPU_TIME_PERC, (delta != 0) ? (t1 - t0) / ((double) delta * 1000000) : 0.0d); // User t1 = threadsBean.getThreadUserTime(tid); t0 = (Long) previous.computeIfAbsent(tid, id -> new HashMap<>()).getOrDefault(STAT_USER_TIME, t1); t.put(STAT_USER_TIME, t1); t.put(STAT_USER_TIME_PERC, (delta != 0) ? (t1 - t0) / ((double) delta * 1000000) : 0.0d); } threads.add(t); } return threads; } private void align(AttributedStringBuilder sb, String val, int width, Align align) { if (align == Align.Left) { sb.append(val); for (int i = 0; i < width - val.length(); i++) { sb.append(' '); } } else { for (int i = 0; i < width - val.length(); i++) { sb.append(' '); } sb.append(val); } } private void display() throws IOException { long now = System.currentTimeMillis(); display.resize(size.getRows(), size.getColumns()); List lines = new ArrayList<>(); AttributedStringBuilder sb = new AttributedStringBuilder(size.getColumns()); // Top headers sb.style(sb.style().bold()); sb.append("ttop"); sb.style(sb.style().boldOff()); sb.append(" - "); sb.append(String.format("%8tT", new Date())); sb.append("."); OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean(); String osinfo = "OS: " + os.getName() + " " + os.getVersion() + ", " + os.getArch() + ", " + os.getAvailableProcessors() + " cpus."; if (sb.length() + 1 + osinfo.length() < size.getColumns()) { sb.append(" "); } else { lines.add(sb.toAttributedString()); sb.setLength(0); } sb.append(osinfo); ClassLoadingMXBean cl = ManagementFactory.getClassLoadingMXBean(); String clsinfo = "Classes: " + cl.getLoadedClassCount() + " loaded, " + cl.getUnloadedClassCount() + " unloaded, " + cl.getTotalLoadedClassCount() + " loaded total."; if (sb.length() + 1 + clsinfo.length() < size.getColumns()) { sb.append(" "); } else { lines.add(sb.toAttributedString()); sb.setLength(0); } sb.append(clsinfo); ThreadMXBean th = ManagementFactory.getThreadMXBean(); String thinfo = "Threads: " + th.getThreadCount() + ", peak: " + th.getPeakThreadCount() + ", started: " + th.getTotalStartedThreadCount() + "."; if (sb.length() + 1 + thinfo.length() < size.getColumns()) { sb.append(" "); } else { lines.add(sb.toAttributedString()); sb.setLength(0); } sb.append(thinfo); MemoryMXBean me = ManagementFactory.getMemoryMXBean(); String meinfo = "Memory: " + "heap: " + memory(me.getHeapMemoryUsage().getUsed(), me.getHeapMemoryUsage().getMax()) + ", non heap: " + memory(me.getNonHeapMemoryUsage().getUsed(), me.getNonHeapMemoryUsage().getMax()) + "."; if (sb.length() + 1 + meinfo.length() < size.getColumns()) { sb.append(" "); } else { lines.add(sb.toAttributedString()); sb.setLength(0); } sb.append(meinfo); StringBuilder sbc = new StringBuilder(); sbc.append("GC: "); boolean first = true; for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) { if (first) { first = false; } else { sbc.append(", "); } long count = gc.getCollectionCount(); long time = gc.getCollectionTime(); sbc.append(gc.getName()).append(": ") .append(Long.toString(count)).append(" col. / ") .append(String.format("%d", time / 1000)) .append(".") .append(String.format("%03d", time % 1000)) .append(" s"); } sbc.append("."); if (sb.length() + 1 + sbc.length() < size.getColumns()) { sb.append(" "); } else { lines.add(sb.toAttributedString()); sb.setLength(0); } sb.append(sbc); lines.add(sb.toAttributedString()); sb.setLength(0); lines.add(sb.toAttributedString()); // Threads List>> threads = infos(); Collections.sort(threads, comparator); int nb = Math.min(size.getRows() - lines.size() - 2, nthreads > 0 ? nthreads : threads.size()); // Compute values List> values = threads.subList(0, nb).stream() .map(thread -> stats.stream() .collect(Collectors.toMap( Function.identity(), key -> columns.get(key).format.apply(thread.get(key))))) .collect(Collectors.toList()); for (String key : stats) { int width = values.stream().mapToInt(map -> map.get(key).length()).max().orElse(0); widths.put(key, Math.max(columns.get(key).header.length(), Math.max(width, widths.getOrDefault(key, 0)))); } List cstats; if (widths.values().stream().mapToInt(Integer::intValue).sum() + stats.size() - 1 < size.getColumns()) { cstats = stats; } else { cstats = new ArrayList<>(); int sz = 0; for (String stat : stats) { int nsz = sz; if (nsz > 0) { nsz++; } nsz += widths.get(stat); if (nsz < size.getColumns()) { sz = nsz; cstats.add(stat); } else { break; } } } // Headers for (String key : cstats) { if (sb.length() > 0) { sb.append(" "); } Column col = columns.get(key); align(sb, col.header, widths.get(key), col.align); } lines.add(sb.toAttributedString()); sb.setLength(0); // Threads for (int i = 0; i < nb; i++) { Map> thread = threads.get(i); long tid = (Long) thread.get(STAT_TID); for (String key : cstats) { if (sb.length() > 0) { sb.append(" "); } long last; Object cur = thread.get(key); Object prv = previous.computeIfAbsent(tid, id -> new HashMap<>()).put(key, cur); if (prv != null && !prv.equals(cur)) { changes.computeIfAbsent(tid, id -> new HashMap<>()).put(key, now); last = now; } else { last = changes.computeIfAbsent(tid, id -> new HashMap<>()).getOrDefault(key, 0L); } long fade = delay * 24; if (now - last < fade) { int r = (int) ((now - last) / (fade / 24)); sb.style(sb.style().foreground(255 - r).background(9)); } align(sb, values.get(i).get(key), widths.get(key), columns.get(key).align); sb.style(sb.style().backgroundOff().foregroundOff()); } lines.add(sb.toAttributedString()); sb.setLength(0); } display.update(lines, 0); } private Comparator>> buildComparator(List sort) { if (sort == null || sort.isEmpty()) { sort = Collections.singletonList(STAT_TID); } Comparator>> comparator = null; for (String key : sort) { String fkey; boolean asc; if (key.startsWith("+")) { fkey = key.substring(1); asc = true; } else if (key.startsWith("-")) { fkey = key.substring(1); asc = false; } else { fkey = key; asc = true; } if (!columns.containsKey(fkey)) { throw new IllegalArgumentException("Unsupported sort key: " + fkey); } @SuppressWarnings("unchecked") Comparator>> comp = Comparator.comparing(m -> (Comparable) m.get(fkey)); if (asc) { comp = comp.reversed(); } if (comparator != null) { comparator = comparator.thenComparing(comp); } else { comparator = comp; } } return comparator; } private void register(String name, Align align, String header, Function format) { columns.put(name, new Column(name, align, header, format)); } private static String nanos(long nanos) { return millis(nanos / 1_000_000L); } private static String millis(long millis) { long secs = millis / 1_000; millis = millis % 1000; long mins = secs / 60; secs = secs % 60; long hours = mins / 60; mins = mins % 60; if (hours > 0) { return String.format("%d:%02d:%02d.%03d", hours, mins, secs, millis); } else if (mins > 0) { return String.format("%d:%02d.%03d", mins, secs, millis); } else { return String.format("%d.%03d", secs, millis); } } private static Function padcut(int nb) { return o -> padcut(o.toString(), nb); } private static String padcut(String str, int nb) { if (str.length() <= nb) { StringBuilder sb = new StringBuilder(nb); sb.append(str); while (sb.length() < nb) { sb.append(' '); } return sb.toString(); } else { StringBuilder sb = new StringBuilder(nb); sb.append(str, 0, nb - 3); sb.append("..."); return sb.toString(); } } private static String memory(long cur, long max) { if (max > 0) { String smax = humanReadableByteCount(max, false); String cmax = humanReadableByteCount(cur, false); StringBuilder sb = new StringBuilder(smax.length() * 2 + 3); for (int i = cmax.length(); i < smax.length(); i++) { sb.append(' '); } sb.append(cmax).append(" / ").append(smax); return sb.toString(); } else { return humanReadableByteCount(cur, false); } } private static String humanReadableByteCount(long bytes, boolean si) { int unit = si ? 1000 : 1024; if (bytes < 1024) return bytes + " B"; int exp = (int) (Math.log(bytes) / Math.log(1024)); String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i"); return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); } /** * This is for long running commands to be interrupted by ctrl-c */ private void checkInterrupted() throws InterruptedException { Thread.yield(); if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } } private void bindKeys(KeyMap map) { map.bind(Operation.HELP, "h", "?"); map.bind(Operation.EXIT, "q", ":q", "Q", ":Q", "ZZ"); map.bind(Operation.INCREASE_DELAY, "+"); map.bind(Operation.DECREASE_DELAY, "-"); map.bind(Operation.CLEAR, KeyMap.ctrl('L')); map.bind(Operation.REVERSE, "r"); } private static class Column { final String name; final Align align; final String header; final Function format; Column(String name, Align align, String header, Function format) { this.name = name; this.align = align; this.header = header; this.format = format; } } } jline3-jline-3.3.1/builtins/src/main/java/org/jline/builtins/Tmux.java000066400000000000000000002050351311544710100256110ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; import java.text.DateFormat; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jline.keymap.BindingReader; import org.jline.keymap.KeyMap; import org.jline.reader.ParsedLine; import org.jline.reader.impl.DefaultParser; import org.jline.terminal.Attributes; import org.jline.terminal.MouseEvent; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.Terminal.Signal; import org.jline.terminal.Terminal.SignalHandler; import org.jline.terminal.impl.LineDisciplineTerminal; import org.jline.utils.*; import org.jline.utils.InfoCmp.Capability; import static org.jline.builtins.Tmux.Layout.Type.LeftRight; import static org.jline.builtins.Tmux.Layout.Type.TopBottom; import static org.jline.builtins.Tmux.Layout.Type.WindowPane; import static org.jline.keymap.KeyMap.*; /** * Terminal multiplexer */ public class Tmux { public static final String OPT_PREFIX = "prefix"; public static final String CMD_COMMANDS = "commands"; public static final String CMD_SEND_PREFIX = "send-prefix"; public static final String CMD_SPLIT_WINDOW = "split-window"; public static final String CMD_SPLITW = "splitw"; public static final String CMD_SELECT_PANE = "select-pane"; public static final String CMD_SELECTP = "selectp"; public static final String CMD_RESIZE_PANE = "resize-pane"; public static final String CMD_RESIZEP = "resizep"; public static final String CMD_DISPLAY_PANES = "display-panes"; public static final String CMD_DISPLAYP = "displayp"; public static final String CMD_CLOCK_MODE = "clock-mode"; public static final String CMD_SET_OPTION = "set-option"; public static final String CMD_SET = "set"; public static final String CMD_LIST_KEYS = "list-keys"; public static final String CMD_LSK = "lsk"; public static final String CMD_SEND_KEYS = "send-keys"; public static final String CMD_SEND = "send"; public static final String CMD_BIND_KEY = "bind-key"; public static final String CMD_BIND = "bind"; public static final String CMD_UNBIND_KEY = "unbind-key"; public static final String CMD_UNBIND = "unbind"; private static final int[][][] WINDOW_CLOCK_TABLE = { { { 1,1,1,1,1 }, /* 0 */ { 1,0,0,0,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 0,0,0,0,1 }, /* 1 */ { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, { { 1,1,1,1,1 }, /* 2 */ { 0,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,0 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 3 */ { 0,0,0,0,1 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,0,0,0,1 }, /* 4 */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, { { 1,1,1,1,1 }, /* 5 */ { 1,0,0,0,0 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 6 */ { 1,0,0,0,0 }, { 1,1,1,1,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 7 */ { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, { { 1,1,1,1,1 }, /* 8 */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 9 */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 1,1,1,1,1 } }, { { 0,0,0,0,0 }, /* : */ { 0,0,1,0,0 }, { 0,0,0,0,0 }, { 0,0,1,0,0 }, { 0,0,0,0,0 } }, { { 1,1,1,1,1 }, /* A */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 } }, { { 1,1,1,1,1 }, /* P */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,0 }, { 1,0,0,0,0 } }, { { 1,0,0,0,1 }, /* M */ { 1,1,0,1,1 }, { 1,0,1,0,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 } }, }; private final AtomicBoolean dirty = new AtomicBoolean(true); private final AtomicBoolean resized = new AtomicBoolean(true); private final Terminal terminal; private final Display display; private final PrintStream err; private final String term; private final Consumer runner; private List panes = new ArrayList<>(); private VirtualConsole active; private int lastActive; private final AtomicBoolean running = new AtomicBoolean(true); private final Size size = new Size(); private final AtomicInteger paneId = new AtomicInteger(); private Layout layout; private boolean identify; private ScheduledExecutorService executor; private ScheduledFuture clockFuture; private final Map serverOptions = new HashMap<>(); private KeyMap keyMap; enum Binding { Discard, SelfInsert, Mouse } public Tmux(Terminal terminal, PrintStream err, Consumer runner) throws IOException { this.terminal = terminal; this.err = err; this.runner = runner; display = new Display(terminal, true); // Find terminal to use Integer colors = terminal.getNumericCapability(Capability.max_colors); term = (colors != null && colors >= 256) ? "screen-256color" : "screen"; // Setup defaults bindings serverOptions.put(OPT_PREFIX, "`"); keyMap = createKeyMap(serverOptions.get(OPT_PREFIX)); } protected KeyMap createKeyMap(String prefix) { KeyMap keyMap = createEmptyKeyMap(prefix); keyMap.bind(CMD_SEND_PREFIX, prefix + prefix); keyMap.bind(CMD_SPLIT_WINDOW + " -v", prefix + "\""); keyMap.bind(CMD_SPLIT_WINDOW + " -h", prefix + "%"); keyMap.bind(CMD_SELECT_PANE + " -U", prefix + key(terminal, Capability.key_up)); keyMap.bind(CMD_SELECT_PANE + " -D", prefix + key(terminal, Capability.key_down)); keyMap.bind(CMD_SELECT_PANE + " -L", prefix + key(terminal, Capability.key_left)); keyMap.bind(CMD_SELECT_PANE + " -R", prefix + key(terminal, Capability.key_right)); keyMap.bind(CMD_RESIZE_PANE + " -U 5", prefix + esc() + key(terminal, Capability.key_up)); keyMap.bind(CMD_RESIZE_PANE + " -D 5", prefix + esc() + key(terminal, Capability.key_down)); keyMap.bind(CMD_RESIZE_PANE + " -L 5", prefix + esc() + key(terminal, Capability.key_left)); keyMap.bind(CMD_RESIZE_PANE + " -R 5", prefix + esc() + key(terminal, Capability.key_right)); keyMap.bind(CMD_RESIZE_PANE + " -U", prefix + translate("^[[1;5A"), prefix + alt(translate("^[[A"))); // ctrl-up keyMap.bind(CMD_RESIZE_PANE + " -D", prefix + translate("^[[1;5B"), prefix + alt(translate("^[[B"))); // ctrl-down keyMap.bind(CMD_RESIZE_PANE + " -L", prefix + translate("^[[1;5C"), prefix + alt(translate("^[[C"))); // ctrl-left keyMap.bind(CMD_RESIZE_PANE + " -R", prefix + translate("^[[1;5D"), prefix + alt(translate("^[[D"))); // ctrl-right keyMap.bind(CMD_DISPLAY_PANES, prefix + "q"); keyMap.bind(CMD_CLOCK_MODE, prefix + "t"); return keyMap; } protected KeyMap createEmptyKeyMap(String prefix) { KeyMap keyMap = new KeyMap<>(); keyMap.setUnicode(Binding.SelfInsert); keyMap.setNomatch(Binding.SelfInsert); for (int i = 0; i < 255; i++) { keyMap.bind(Binding.Discard, prefix + (char)(i)); } keyMap.bind(Binding.Mouse, key(terminal, Capability.key_mouse)); return keyMap; } public void run() throws IOException { SignalHandler prevWinchHandler = terminal.handle(Signal.WINCH, this::resize); SignalHandler prevIntHandler = terminal.handle(Signal.INT, this::interrupt); SignalHandler prevSuspHandler = terminal.handle(Signal.TSTP, this::suspend); Attributes attributes = terminal.enterRawMode(); terminal.puts(Capability.enter_ca_mode); terminal.puts(Capability.keypad_xmit); terminal.trackMouse(Terminal.MouseTracking.Any); terminal.flush(); executor = Executors.newSingleThreadScheduledExecutor(); try { // Create first pane size.copy(terminal.getSize()); layout = new Layout(); layout.sx = size.getColumns(); layout.sy = size.getRows(); layout.type = WindowPane; active = new VirtualConsole(paneId.incrementAndGet(), term, 0, 0, size.getColumns(), size.getRows() - 1, this::setDirty, this::close, layout); active.active = lastActive++; active.getConsole().setAttributes(terminal.getAttributes()); panes.add(active); runner.accept(active.getConsole()); // Start input loop new Thread(this::inputLoop, "Mux input loop").start(); // Redraw loop redrawLoop(); } catch (RuntimeException e) { throw e; } finally { executor.shutdown(); terminal.trackMouse(Terminal.MouseTracking.Off); terminal.puts(Capability.keypad_local); terminal.puts(Capability.exit_ca_mode); terminal.flush(); terminal.setAttributes(attributes); terminal.handle(Signal.WINCH, prevWinchHandler); terminal.handle(Signal.INT, prevIntHandler); terminal.handle(Signal.TSTP, prevSuspHandler); } } private void redrawLoop() { while (running.get()) { try { synchronized (dirty) { while (running.get() && !dirty.compareAndSet(true, false)) { dirty.wait(); } } } catch (InterruptedException e) { e.printStackTrace(); } handleResize(); redraw(); } } private void setDirty() { synchronized (dirty) { dirty.set(true); dirty.notifyAll(); } } private void inputLoop() { try { BindingReader reader = new BindingReader(terminal.reader()); boolean first = true; while (running.get()) { Object b; if (first) { b = reader.readBinding(keyMap); } else if (reader.peekCharacter(100) >= 0) { b = reader.readBinding(keyMap, null, false); } else { b = null; } if (b == Binding.SelfInsert) { if (active.clock) { active.clock = false; if (clockFuture != null && panes.stream().noneMatch(vc -> vc.clock)) { clockFuture.cancel(false); clockFuture = null; } setDirty(); } else { active.getMasterInputOutput().write(reader.getLastBinding().getBytes()); first = false; } } else { if (first) { first = false; } else { active.getMasterInputOutput().flush(); first = true; } if (b == Binding.Mouse) { MouseEvent event = terminal.readMouseEvent(); //System.err.println(event.toString()); } else if (b instanceof String || b instanceof String[]) { ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream err = new ByteArrayOutputStream(); try (PrintStream pout = new PrintStream(out); PrintStream perr = new PrintStream(err)) { if (b instanceof String) { execute(pout, perr, (String) b); } else { execute(pout, perr, Arrays.asList((String[]) b)); } } catch (Exception e) { // TODO: log } } } } } catch (IOException e) { if (running.get()) { Log.info("Error in tmux input loop", e); } } finally { running.set(false); setDirty(); } } private synchronized void close(VirtualConsole terminal) { int idx = panes.indexOf(terminal); if (idx >= 0) { panes.remove(idx); if (panes.isEmpty()) { running.set(false); setDirty(); } else { terminal.layout.remove(); if (active == terminal) { active = panes.stream() .sorted(Comparator.comparingInt(p -> p.active).reversed()) .findFirst().get(); } layout = active.layout; while (layout.parent != null) { layout = layout.parent; } layout.fixOffsets(); layout.fixPanes(size.getColumns(), size.getRows()); resize(Signal.WINCH); } } } private void resize(Signal signal) { resized.set(true); setDirty(); } private void interrupt(Signal signal) { active.getConsole().raise(signal); } private void suspend(Signal signal) { active.getConsole().raise(signal); } private void handleResize() { // Re-compute the layout if (resized.compareAndSet(true, false)) { size.copy(terminal.getSize()); } layout.resize(size.getColumns(), size.getRows() - 1); panes.forEach(vc -> { if (vc.width() != vc.layout.sx || vc.height() != vc.layout.sy || vc.left() != vc.layout.xoff || vc.top() != vc.layout.yoff) { vc.resize(vc.layout.xoff, vc.layout.yoff, vc.layout.sx, vc.layout.sy); display.clear(); } }); } public void execute(PrintStream out, PrintStream err, String command) throws Exception { ParsedLine line = new DefaultParser().parse(command.trim(), 0); execute(out, err, line.words()); } public synchronized void execute(PrintStream out, PrintStream err, List command) throws Exception { String name = command.get(0); List args = command.subList(1, command.size()); switch (name) { case CMD_SEND_PREFIX: sendPrefix(out, err, args); break; case CMD_SPLIT_WINDOW: case CMD_SPLITW: splitWindow(out, err, args); break; case CMD_SELECT_PANE: case CMD_SELECTP: selectPane(out, err, args); break; case CMD_RESIZE_PANE: case CMD_RESIZEP: resizePane(out, err, args); break; case CMD_DISPLAY_PANES: case CMD_DISPLAYP: displayPanes(out, err, args); break; case CMD_CLOCK_MODE: clockMode(out, err, args); break; case CMD_BIND_KEY: case CMD_BIND: bindKey(out, err, args); break; case CMD_UNBIND_KEY: case CMD_UNBIND: unbindKey(out, err, args); break; case CMD_LIST_KEYS: case CMD_LSK: listKeys(out, err, args); break; case CMD_SEND_KEYS: case CMD_SEND: sendKeys(out, err, args); break; case CMD_SET_OPTION: case CMD_SET: setOption(out, err, args); break; } } protected void setOption(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "set-option - ", "Usage: set-option [-agosquw] option [value]", " -? --help Show help", " -u --unset Unset the option" }; Options opt = Options.compile(usage).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } int nbargs = opt.args().size(); if (nbargs < 1 || nbargs > 2) { opt.usage(err); return; } String name = opt.args().get(0); String value = nbargs > 1 ? opt.args().get(1) : null; if (name.startsWith("@")) { // set user option } else { // set server option switch (name) { case OPT_PREFIX: if (value == null) { throw new IllegalArgumentException("Missing argument"); } String prefix = translate(value); String oldPrefix = serverOptions.put(OPT_PREFIX, prefix); KeyMap newKeys = createEmptyKeyMap(prefix); for (Map.Entry e : keyMap.getBoundKeys().entrySet()) { if (e.getValue() instanceof String) { if (e.getKey().equals(oldPrefix + oldPrefix)) { newKeys.bind(e.getValue(), prefix + prefix); } else if (e.getKey().startsWith(oldPrefix)) { newKeys.bind(e.getValue(), prefix + e.getKey().substring(oldPrefix.length())); } else { newKeys.bind(e.getValue(), e.getKey()); } } } keyMap = newKeys; break; } } } protected void bindKey(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "bind-key - ", "Usage: bind-key key command [arguments]", /* [-cnr] [-t mode-table] [-T key-table] */ " -? --help Show help" }; Options opt = Options.compile(usage).setOptionsFirst(true).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } List vargs = opt.args(); if (vargs.size() < 2) { opt.usage(err); return; } String prefix = serverOptions.get(OPT_PREFIX); String key = prefix + KeyMap.translate(vargs.remove(0)); keyMap.unbind(key.substring(0, 2)); keyMap.bind(vargs.toArray(new String[vargs.size()]), key); } protected void unbindKey(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "unbind-key - ", "Usage: unbind-key key", /* [-an] [-t mode-table] [-T key-table] */ " -? --help Show help" }; Options opt = Options.compile(usage).setOptionsFirst(true).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } List vargs = opt.args(); if (vargs.size() != 1) { opt.usage(err); return; } String prefix = serverOptions.get(OPT_PREFIX); String key = prefix + KeyMap.translate(vargs.remove(0)); keyMap.unbind(key); keyMap.bind(Binding.Discard, key); } protected void listKeys(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "list-keys - ", "Usage: list-keys ", /* [-t mode-table] [-T key-table] */ " -? --help Show help", }; Options opt = Options.compile(usage).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } String prefix = serverOptions.get(OPT_PREFIX); keyMap.getBoundKeys().entrySet().stream() .filter(e -> e.getValue() instanceof String) .map(e -> { String key = e.getKey(); String val = (String) e.getValue(); StringBuilder sb = new StringBuilder(); sb.append("bind-key -T "); if (key.startsWith(prefix)) { sb.append("prefix "); key = key.substring(prefix.length()); } else { sb.append("root "); } sb.append(display(key)); while (sb.length() < 32) { sb.append(" "); } sb.append(val); return sb.toString(); }) .sorted() .forEach(out::println); } protected void sendKeys(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "send-keys - ", "Usage: send-keys [-lXRM] [-N repeat-count] [-t target-pane] key...", " -? --help Show help", " -l --literal Send key literally", " -N --number=repeat-count Specifies a repeat count" }; Options opt = Options.compile(usage).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } for (int i = 0, n = opt.getNumber("number"); i < n; i++) { for (String arg : opt.args()) { String s = opt.isSet("literal") ? arg : KeyMap.translate(arg); active.getMasterInputOutput().write(s.getBytes()); } } } protected void clockMode(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "clock-mode - ", "Usage: clock-mode", " -? --help Show help" }; Options opt = Options.compile(usage).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } active.clock = true; if (clockFuture == null) { long initial = Instant.now().until(Instant.now().truncatedTo(ChronoUnit.MINUTES).plusSeconds(60), ChronoUnit.MILLIS); long delay = TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS); clockFuture = executor.scheduleWithFixedDelay(this::setDirty, initial, delay, TimeUnit.MILLISECONDS); } setDirty(); } protected void displayPanes(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "display-panes - ", "Usage: display-panes", " -? --help Show help" }; Options opt = Options.compile(usage).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } identify = true; setDirty(); executor.schedule(() -> { identify = false; setDirty(); }, 1, TimeUnit.SECONDS); } protected void resizePane(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "resize-pane - ", "Usage: resize-pane [-UDLR] [-x width] [-y height] [-t target-pane] [adjustment]", " -? --help Show help", " -U Resize pane upward", " -D Select pane downward", " -L Select pane to the left", " -R Select pane to the right", " -x --width=width Set the width of the pane", " -y --height=height Set the height of the pane" }; Options opt = Options.compile(usage).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } int adjust; if (opt.args().size() == 0) { adjust = 1; } else if (opt.args().size() == 1) { adjust = Integer.parseInt(opt.args().get(0)); } else { opt.usage(err); return; } if (opt.isSet("width")) { int x = opt.getNumber("width"); active.layout().resizeTo(LeftRight, x); } if (opt.isSet("height")) { int y = opt.getNumber("height"); active.layout().resizeTo(TopBottom, y); } if (opt.isSet("L")) { active.layout().resize(LeftRight, -adjust, true); } else if (opt.isSet("R")) { active.layout().resize(LeftRight, adjust, true); } else if (opt.isSet("U")) { active.layout().resize(TopBottom, -adjust, true); } else if (opt.isSet("D")) { active.layout().resize(TopBottom, adjust, true); } setDirty(); } protected void selectPane(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "select-pane - ", "Usage: select-pane [-UDLR] [-t target-pane]", " -? --help Show help", " -U Select pane up", " -D Select pane down", " -L Select pane left", " -R Select pane right", }; Options opt = Options.compile(usage).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } VirtualConsole prevActive = active; if (opt.isSet("L")) { active = panes.stream() .filter(c -> c.bottom() > active.top() && c.top() < active.bottom()) .filter(c -> c != active) .sorted(Comparator .comparingInt(c -> c.left() > active.left() ? c.left() : c.left() + size.getColumns()).reversed() .thenComparingInt(c -> - c.active)) .findFirst().orElse(active); } else if (opt.isSet("R")) { active = panes.stream() .filter(c -> c.bottom() > active.top() && c.top() < active.bottom()) .filter(c -> c != active) .sorted(Comparator .comparingInt(c -> c.left() > active.left() ? c.left() : c.left() + size.getColumns()) .thenComparingInt(c -> - c.active)) .findFirst().orElse(active); } else if (opt.isSet("U")) { active = panes.stream() .filter(c -> c.right() > active.left() && c.left() < active.right()) .filter(c -> c != active) .sorted(Comparator .comparingInt(c -> c.top() > active.top() ? c.top() : c.top() + size.getRows()).reversed() .thenComparingInt(c -> - c.active)) .findFirst().orElse(active); } else if (opt.isSet("D")) { active = panes.stream() .filter(c -> c.right() > active.left() && c.left() < active.right()) .filter(c -> c != active) .sorted(Comparator .comparingInt(c -> c.top() > active.top() ? c.top() : c.top() + size.getRows()) .thenComparingInt(c -> - c.active)) .findFirst().orElse(active); } if (prevActive != active) { setDirty(); active.active = lastActive++; } } protected void sendPrefix(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "send-prefix - ", "Usage: send-prefix [-2] [-t target-pane]", " -? --help Show help", }; Options opt = Options.compile(usage).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } active.getMasterInputOutput().write(serverOptions.get(OPT_PREFIX).getBytes()); } protected void splitWindow(PrintStream out, PrintStream err, List args) throws IOException { final String[] usage = { "split-window - ", "Usage: split-window [-bdfhvP] [-c start-directory] [-F format] [-p percentage|-l size] [-t target-pane] [command]", " -? --help Show help", " -h --horizontal Horizontal split", " -v --vertical Vertical split", " -l --size=size Size", " -p --perc=percentage Percentage", " -b --before Insert the new pane before the active one", " -f Split the full window instead of the active pane", " -d Do not make the new pane the active one" }; Options opt = Options.compile(usage).parse(args); if (opt.isSet("help")) { opt.usage(err); return; } Layout.Type type = opt.isSet("horizontal") ? LeftRight : TopBottom; // If we're splitting the main pane, create a parent if (layout.type == WindowPane) { Layout p = new Layout(); p.sx = layout.sx; p.sy = layout.sy; p.type = type; p.cells.add(layout); layout.parent = p; layout = p; } Layout cell = active.layout(); if (opt.isSet("f")) { while (cell.parent != layout) { cell = cell.parent; } } int size = -1; if (opt.isSet("size")) { size = opt.getNumber("size"); } else if (opt.isSet("perc")) { int p = opt.getNumber("perc"); if (type == TopBottom) { size = (cell.sy * p) / 100; } else { size = (cell.sx * p) / 100; } } // Split now Layout newCell = cell.split(type, size, opt.isSet("before")); if (newCell == null) { err.println("create pane failed: pane too small"); return; } VirtualConsole newConsole = new VirtualConsole(paneId.incrementAndGet(), term, newCell.xoff, newCell.yoff, newCell.sx, newCell.sy, this::setDirty, this::close, newCell); panes.add(newConsole); newConsole.getConsole().setAttributes(terminal.getAttributes()); if (!opt.isSet("d")) { active = newConsole; active.active = lastActive++; } runner.accept(newConsole.getConsole()); setDirty(); } protected void layoutResize() { // See layout_resize } int ACTIVE_COLOR = 0xF44; int INACTIVE_COLOR = 0x44F; int CLOCK_COLOR = 0x44F; protected synchronized void redraw() { long[] screen = new long[size.getRows() * size.getColumns()]; // Fill Arrays.fill(screen, 0x00000020L); int[] cursor = new int[2]; for (VirtualConsole terminal : panes) { if (terminal.clock) { String str = DateFormat.getTimeInstance(DateFormat.SHORT).format(new Date()); print(screen, terminal, str, CLOCK_COLOR); } else { // Dump terminal terminal.dump(screen, terminal.top(), terminal.left(), size.getRows(), size.getColumns(), terminal == active ? cursor : null); } if (identify) { String id = Integer.toString(terminal.id); print(screen, terminal, id, terminal == active ? ACTIVE_COLOR : INACTIVE_COLOR); } // Draw border drawBorder(screen, size, terminal, 0x0L); } drawBorder(screen, size, active, 0x010080000L << 32); // Draw status Arrays.fill(screen, (size.getRows() - 1) * size.getColumns(), size.getRows() * size.getColumns(), 0x20000080L << 32 | 0x0020L); // Attribute mask: 0xYXFFFBBB00000000L // X: Bit 0 - Underlined // Bit 1 - Negative // Bit 2 - Concealed // Bit 3 - Bold // Y: Bit 0 - Foreground set // Bit 1 - Background set // F: Foreground r-g-b // B: Background r-g-b List lines = new ArrayList<>(); int prevBg = 0; int prevFg = 0; boolean prevInv = false; boolean prevUl = false; boolean prevBold = false; boolean prevConceal = false; boolean prevHasFg = false; boolean prevHasBg = false; for (int y = 0; y < size.getRows(); y++) { AttributedStringBuilder sb = new AttributedStringBuilder(size.getColumns()); for (int x = 0; x < size.getColumns(); x++) { long d = screen[y * size.getColumns() + x]; int c = (int) (d & 0xffffffffL); int a = (int) (d >> 32); int bg = a & 0x000fff; int fg = (a & 0xfff000) >> 12; boolean ul = ((a & 0x01000000) != 0); boolean inv = ((a & 0x02000000) != 0); boolean conceal = ((a & 0x04000000) != 0); boolean bold = ((a & 0x08000000) != 0); boolean hasFg = ((a & 0x10000000) != 0); boolean hasBg = ((a & 0x20000000) != 0); if ((hasBg && prevHasBg && bg != prevBg) || prevHasBg != hasBg) { if (!hasBg) { sb.style(sb.style().backgroundDefault()); } else { int col = bg; col = AttributedCharSequence.roundRgbColor((col & 0xF00) >> 4, (col & 0x0F0), (col & 0x00F) << 4, 256); sb.style(sb.style().background(col)); } prevBg = bg; prevHasBg = hasBg; } if ((hasFg && prevHasFg && fg != prevFg) || prevHasFg != hasFg) { if (!hasFg) { sb.style(sb.style().foregroundDefault()); } else { int col = fg; col = AttributedCharSequence.roundRgbColor((col & 0xF00) >> 4, (col & 0x0F0), (col & 0x00F) << 4, 256); sb.style(sb.style().foreground(col)); } prevFg = fg; prevHasFg = hasFg; } if (conceal != prevConceal) { sb.style(conceal ? sb.style().conceal() : sb.style().concealOff()); prevConceal = conceal; } if (inv != prevInv) { sb.style(inv ? sb.style().inverse() : sb.style().inverseOff()); prevInv = inv; } if (ul != prevUl) { sb.style(ul ? sb.style().underline() : sb.style().underlineOff()); prevUl = ul; } if (bold != prevBold) { sb.style(bold ? sb.style().bold() : sb.style().boldOff()); prevBold = bold; } sb.append((char) c); } lines.add(sb.toAttributedString()); } display.resize(size.getRows(), size.getColumns()); display.update(lines, size.cursorPos(cursor[1], cursor[0])); } private void print(long[] screen, VirtualConsole terminal, String id, int color) { if (terminal.height() > 5) { long attr = ((long) color << 32) | 0x02000000000000000L; int yoff = (terminal.height() - 5) / 2; int xoff = (terminal.width() - id.length() * 6) / 2; for (int i = 0; i < id.length(); i++) { char ch = id.charAt(i); int idx; switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': idx = ch - '0'; break; case ':': idx = 10; break; case 'A': idx = 11; break; case 'P': idx = 12; break; case 'M': idx = 13; break; default: idx = -1; break; } if (idx >= 0) { int[][] data = WINDOW_CLOCK_TABLE[idx]; for (int y = 0; y < data.length; y++) { for (int x = 0; x < data[y].length; x++) { if (data[y][x] != 0) { int off = (terminal.top + yoff + y) * size.getColumns() + terminal.left() + xoff + x + 6 * i; screen[off] = attr | ' '; } } } } } } else { long attr = ((long) color << 44) | 0x01000000000000000L; int yoff = (terminal.height() + 1) / 2; int xoff = (terminal.width() - id.length()) / 2; int off = (terminal.top + yoff) * size.getColumns() + terminal.left() + xoff; for (int i = 0; i < id.length(); i++) { screen[off + i] = attr | id.charAt(i); } } } private void drawBorder(long[] screen, Size size, VirtualConsole terminal, long attr) { for (int i = terminal.left(); i < terminal.right(); i++) { int y0 = terminal.top() - 1; int y1 = terminal.bottom(); drawBorderChar(screen, size, i, y0, attr, '─'); drawBorderChar(screen, size, i, y1, attr, '─'); } for (int i = terminal.top(); i < terminal.bottom(); i++) { int x0 = terminal.left() - 1; int x1 = terminal.right(); drawBorderChar(screen, size, x0, i, attr, '│'); drawBorderChar(screen, size, x1, i, attr, '│'); } drawBorderChar(screen, size, terminal.left() - 1, terminal.top() - 1, attr, '┌'); drawBorderChar(screen, size, terminal.right(), terminal.top() - 1, attr, '┐'); drawBorderChar(screen, size, terminal.left() - 1, terminal.bottom(), attr, '└'); drawBorderChar(screen, size, terminal.right(), terminal.bottom(), attr, '┘'); } private void drawBorderChar(long[] screen, Size size, int x, int y, long attr, int c) { if (x >= 0 && x < size.getColumns() && y >= 0 && y < size.getRows() - 1) { int oldc = (int)(screen[y * size.getColumns() + x] & 0xFFFFFFFFL); c = addBorder(c, oldc); screen[y * size.getColumns() + x] = attr | c; } } private int addBorder(int c, int oldc) { if (oldc == ' ') { return c; } if (oldc == '┼') { return '┼'; } switch (c) { case '│': return addBorder('╷', addBorder('╵', oldc)); case '─': return addBorder('╴', addBorder('╶', oldc)); case '┌': return addBorder('╶', addBorder('╷', oldc)); case '┐': return addBorder('╴', addBorder('╷', oldc)); case '└': return addBorder('╶', addBorder('╵', oldc)); case '┘': return addBorder('╴', addBorder('╵', oldc)); case '├': return addBorder('╶', addBorder('│', oldc)); case '┤': return addBorder('╴', addBorder('│', oldc)); case '┬': return addBorder('╷', addBorder('─', oldc)); case '┴': return addBorder('╵', addBorder('─', oldc)); case '╴': switch (oldc) { case '│': return '┤'; case '─': return '─'; case '┌': return '┬'; case '┐': return '┐'; case '└': return '┴'; case '┘': return '┘'; case '├': return '┼'; case '┤': return '┤'; case '┬': return '┬'; case '┴': return '┴'; default: throw new IllegalArgumentException(); } case '╵': switch (oldc) { case '│': return '│'; case '─': return '┴'; case '┌': return '├'; case '┐': return '┤'; case '└': return '└'; case '┘': return '┘'; case '├': return '├'; case '┤': return '┤'; case '┬': return '┼'; case '┴': return '┴'; default: throw new IllegalArgumentException(); } case '╶': switch (oldc) { case '│': return '├'; case '─': return '─'; case '┌': return '┌'; case '┐': return '┬'; case '└': return '└'; case '┘': return '┴'; case '├': return '├'; case '┤': return '┼'; case '┬': return '┬'; case '┴': return '┴'; default: throw new IllegalArgumentException(); } case '╷': switch (oldc) { case '│': return '│'; case '─': return '┬'; case '┌': return '┌'; case '┐': return '┐'; case '└': return '├'; case '┘': return '┤'; case '├': return '├'; case '┤': return '┤'; case '┬': return '┬'; case '┴': return '┼'; default: throw new IllegalArgumentException(); } default: throw new IllegalArgumentException(); } } static class Layout { static final Pattern PATTERN = Pattern.compile("([0-9]+)x([0-9]+),([0-9]+),([0-9]+)([^0-9]\\S*)?"); private static final int PANE_MINIMUM = 3; enum Type { LeftRight, TopBottom, WindowPane } Type type; Layout parent; int sx; int sy; int xoff; int yoff; List cells = new ArrayList<>(); public static Layout parse(String layout) { if (layout.length() < 6) { throw new IllegalArgumentException("Bad syntax"); } String chk = layout.substring(0, 4); if (layout.charAt(4) != ',') { throw new IllegalArgumentException("Bad syntax"); } layout = layout.substring(5); if (Integer.parseInt(chk, 16) != checksum(layout)) { throw new IllegalArgumentException("Bad checksum"); } return parseCell(null, layout); } public String dump() { StringBuilder sb = new StringBuilder(64); sb.append("0000,"); doDump(sb); int chk = checksum(sb, 5); sb.setCharAt(0, toHexChar((chk >> 12) & 0x000F)); sb.setCharAt(1, toHexChar((chk >> 8) & 0x000F)); sb.setCharAt(2, toHexChar((chk >> 4) & 0x000F)); sb.setCharAt(3, toHexChar(chk & 0x000F)); return sb.toString(); } private static char toHexChar(int i) { return (i < 10) ? (char)(i + '0') : (char)(i - 10 + 'a'); } private void doDump(StringBuilder sb) { sb.append(sx).append('x').append(sy).append(',').append(xoff).append(',').append(yoff); switch (type) { case WindowPane: sb.append(',').append('0'); break; case TopBottom: case LeftRight: sb.append(type == Type.TopBottom ? '[' : '{'); boolean first = true; for (Layout c : cells) { if (first) { first = false; } else { sb.append(','); } c.doDump(sb); } sb.append(type == Type.TopBottom ? ']' : '}'); break; } } public void resize(Type type, int change, boolean opposite) { /* Find next parent of the same type. */ Layout lc = this; Layout lcparent = lc.parent; while (lcparent != null && lcparent.type != type) { lc = lcparent; lcparent = lc.parent; } if (lcparent == null) { return; } /* If this is the last cell, move back one. */ if (lc.nextSibling() == null) { lc = lc.prevSibling(); } /* Grow or shrink the cell. */ int size; int needed = change; while (needed != 0) { if (change > 0) { size = lc.resizePaneGrow(type, needed, opposite); needed -= size; } else { size = lc.resizePaneShrink(type, needed); needed += size; } if (size == 0) { /* no more change possible */ break; } } fixOffsets(); fixPanes(); } int resizePaneGrow(Type type, int needed, boolean opposite) { int size = 0; /* Growing. Always add to the current cell. */ Layout lcadd = this; /* Look towards the tail for a suitable cell for reduction. */ Layout lcremove = this.nextSibling(); while (lcremove != null) { size = lcremove.resizeCheck(type); if (size > 0) { break; } lcremove = lcremove.nextSibling(); } /* If none found, look towards the head. */ if (opposite && lcremove == null) { lcremove = this.prevSibling(); while (lcremove != null) { size = lcremove.resizeCheck(type); if (size > 0) { break; } lcremove = lcremove.prevSibling(); } } if (lcremove == null) { return 0; } /* Change the cells. */ if (size > needed) { size = needed; } lcadd.resizeAdjust(type, size); lcremove.resizeAdjust(type, -size); return size; } int resizePaneShrink(Type type, int needed) { int size = 0; /* Shrinking. Find cell to remove from by walking towards head. */ Layout lcremove = this; do { size = lcremove.resizeCheck(type); if (size > 0) { break; } lcremove = lcremove.prevSibling(); } while (lcremove != null); if (lcremove == null) { return 0; } /* And add onto the next cell (from the original cell). */ Layout lcadd = this.nextSibling(); if (lcadd == null) { return 0; } /* Change the cells. */ if (size > -needed) { size = -needed; } lcadd.resizeAdjust(type, size); lcremove.resizeAdjust(type, -size); return size; } Layout prevSibling() { int idx = parent.cells.indexOf(this); if (idx > 0) { return parent.cells.get(idx - 1); } else { return null; } } Layout nextSibling() { int idx = parent.cells.indexOf(this); if (idx < parent.cells.size() - 1) { return parent.cells.get(idx + 1); } else { return null; } } public void resizeTo(Type type, int new_size) { /* Find next parent of the same type. */ Layout lc = this; Layout lcparent = lc.parent; while (lcparent != null && lcparent.type != type) { lc = lcparent; lcparent = lc.parent; } if (lcparent == null) { return; } /* Work out the size adjustment. */ int size = type == LeftRight ? lc.sx : lc.sy; int change = lc.nextSibling() == null ? size - new_size : new_size - size; /* Resize the pane. */ lc.resize(type, change, true); } public void resize(int sx, int sy) { // Horizontal int xchange = sx - this.sx; int xlimit = resizeCheck(LeftRight); if (xchange < 0 && xchange < -xlimit) { xchange = -xlimit; } if (xlimit == 0) { if (sx <= this.sx) { xchange = 0; } else { xchange = sx - this.sx; } } if (xchange != 0) { resizeAdjust(LeftRight, xchange); } // Horizontal int ychange = sy - this.sy; int ylimit = resizeCheck(Type.TopBottom); if (ychange < 0 && ychange < -ylimit) { ychange = -ylimit; } if (ylimit == 0) { if (sy <= this.sy) { ychange = 0; } else { ychange = sy - this.sy; } } if (ychange != 0) { resizeAdjust(Type.TopBottom, ychange); } // Fix offsets fixOffsets(); fixPanes(sx, sy); } public void remove() { if (parent == null) { throw new IllegalStateException(); } int idx = parent.cells.indexOf(this); Layout other = parent.cells.get(idx == 0 ? 1 : idx - 1); other.resizeAdjust(parent.type, parent.type == LeftRight ? (sx + 1) : (sy + 1)); parent.cells.remove(this); if (other.parent.cells.size() == 1) { if (other.parent.parent == null) { other.parent = null; } else { other.parent.parent.cells.set(other.parent.parent.cells.indexOf(other.parent), other); other.parent = other.parent.parent; } } } private int resizeCheck(Type type) { if (this.type == Type.WindowPane) { int min = PANE_MINIMUM; int avail; if (type == LeftRight) { avail = this.sx; } else { avail = this.sy; min += 1; // TODO: need status } if (avail > min) { avail -= min; } else { avail = 0; } return avail; } else if (this.type == type) { return this.cells.stream() .mapToInt(c -> c.resizeCheck(type)) .sum(); } else { return this.cells.stream() .mapToInt(c -> c.resizeCheck(type)) .min() .orElse(Integer.MAX_VALUE); } } private void resizeAdjust(Type type, int change) { if (type == LeftRight) { this.sx += change; } else { this.sy += change; } if (this.type == Type.WindowPane) { return; } if (this.type != type) { for (Layout c : cells) { c.resizeAdjust(type, change); } return; } while (change != 0) { for (Layout c : cells) { if (change == 0) { break; } if (change > 0) { c.resizeAdjust(type, 1); change--; continue; } if (c.resizeCheck(type) > 0) { c.resizeAdjust(type, -1); change++; } }; } } public void fixOffsets() { if (type == LeftRight) { int xoff = this.xoff; for (Layout cell : cells) { cell.xoff = xoff; cell.yoff = this.yoff; cell.fixOffsets(); xoff += cell.sx + 1; } } else if (type == TopBottom) { int yoff = this.yoff; for (Layout cell : cells) { cell.xoff = this.xoff; cell.yoff = yoff; cell.fixOffsets(); yoff += cell.sy + 1; } } } public void fixPanes() { } public void fixPanes(int sx, int sy) { } public int countCells() { switch (type) { case LeftRight: case TopBottom: return cells.stream().mapToInt(Layout::countCells).sum(); default: return 1; } } public Layout split(Type type, int size, boolean insertBefore) { if (type == WindowPane) { throw new IllegalStateException(); } if ((type == LeftRight ? sx : sy) < PANE_MINIMUM * 2 + 1) { return null; } if (parent == null) { throw new IllegalStateException(); } int saved_size = type == LeftRight ? sx : sy; int size2 = size < 0 ? ((saved_size + 1) / 2) - 1 : insertBefore ? saved_size - size - 1 : size; if (size2 < PANE_MINIMUM) { size2 = PANE_MINIMUM; } else if (size2 > saved_size - 2) { size2 = saved_size - 2; } int size1 = saved_size - 1 - size2; if (parent.type != type) { Layout p = new Layout(); p.type = type; p.parent = parent; p.sx = sx; p.sy = sy; p.xoff = xoff; p.yoff = yoff; parent.cells.set(parent.cells.indexOf(this), p); p.cells.add(this); parent = p; } Layout cell = new Layout(); cell.type = WindowPane; cell.parent = parent; parent.cells.add(parent.cells.indexOf(this) + (insertBefore ? 0 : 1), cell); int sx = this.sx; int sy = this.sy; int xoff = this.xoff; int yoff = this.yoff; Layout cell1, cell2; if (insertBefore) { cell1 = cell; cell2 = this; } else { cell1 = this; cell2 = cell; } if (type == LeftRight) { cell1.setSize(size1, sy, xoff, yoff); cell2.setSize(size2, sy, xoff + size1 + 1, yoff); } else { cell1.setSize(sx, size1, xoff, yoff); cell2.setSize(sx, size2, xoff, yoff + size1 + 1); } return cell; } private void setSize(int sx, int sy, int xoff, int yoff) { this.sx = sx; this.sy = sy; this.xoff = xoff; this.yoff = yoff; } private static int checksum(CharSequence layout) { return checksum(layout, 0); } private static int checksum(CharSequence layout, int start) { int csum = 0; for (int i = start; i < layout.length(); i++) { csum = (csum >> 1) + ((csum & 1) << 15); csum += layout.charAt(i); } return csum; } private static Layout parseCell(Layout parent, String layout) { Matcher matcher = PATTERN.matcher(layout); if (matcher.matches()) { Layout cell = new Layout(); cell.type = Type.WindowPane; cell.parent = parent; cell.sx = Integer.parseInt(matcher.group(1)); cell.sy = Integer.parseInt(matcher.group(2)); cell.xoff = Integer.parseInt(matcher.group(3)); cell.yoff = Integer.parseInt(matcher.group(4)); if (parent != null) { parent.cells.add(cell); } layout = matcher.group(5); if (layout == null || layout.isEmpty()) { return cell; } if (layout.charAt(0) == ',') { int i = 1; while (i < layout.length() && Character.isDigit(layout.charAt(i))) { i++; } if (i == layout.length()) { return cell; } if (layout.charAt(i) == ',') { layout = layout.substring(i); } } int i; switch (layout.charAt(0)) { case '{': cell.type = LeftRight; i = findMatch(layout, '{', '}'); parseCell(cell, layout.substring(1, i)); layout = layout.substring(i + 1); if (!layout.isEmpty() && layout.charAt(0) == ',') { parseCell(parent, layout.substring(1)); } return cell; case '[': cell.type = Type.TopBottom; i = findMatch(layout, '[', ']'); parseCell(cell, layout.substring(1, i)); layout = layout.substring(i + 1); if (!layout.isEmpty() && layout.charAt(0) == ',') { parseCell(parent, layout.substring(1)); } return cell; case ',': parseCell(parent, layout.substring(1)); return cell; default: throw new IllegalArgumentException("Unexpected '" + layout.charAt(0) + "'"); } } else { throw new IllegalArgumentException("Bad syntax"); } } } private static int findMatch(String layout, char c0, char c1) { if (layout.charAt(0) != c0) { throw new IllegalArgumentException(); } int nb = 0; int i = 0; while (i < layout.length()) { char c = layout.charAt(i); if (c == c0) { nb++; } else if (c == c1) { if (--nb == 0) { return i; } } i++; } if (nb > 0) { throw new IllegalArgumentException("No matching '" + c1 + "'"); } return i; } private static class VirtualConsole implements Closeable { private final ScreenTerminal terminal; private final Consumer closer; private final int id; private int left; private int top; private final Layout layout; private int active; private boolean clock; private final OutputStream masterOutput; private final OutputStream masterInputOutput; private final LineDisciplineTerminal console; public VirtualConsole(int id, String type, int left, int top, int columns, int rows, Runnable dirty, Consumer closer, Layout layout) throws IOException { String name = String.format("tmux%02d", id); this.id = id; this.left = left; this.top = top; this.closer = closer; this.terminal = new ScreenTerminal(columns, rows) { @Override protected void setDirty() { super.setDirty(); dirty.run(); } }; this.masterOutput = new MasterOutputStream(); this.masterInputOutput = new OutputStream() { @Override public void write(int b) throws IOException { console.processInputByte(b); } }; this.console = new LineDisciplineTerminal( name, type, masterOutput, Charset.defaultCharset().name()) { @Override public void close() throws IOException { super.close(); closer.accept(VirtualConsole.this); } }; this.console.setSize(new Size(columns, rows)); this.layout = layout; } Layout layout() { return layout; } public int left() { return left; } public int top() { return top; } public int right() { return left() + width(); } public int bottom() { return top() + height(); } public int width() { return console.getWidth(); } public int height() { return console.getHeight(); } public LineDisciplineTerminal getConsole() { return console; } public OutputStream getMasterInputOutput() { return masterInputOutput; } public void resize(int left, int top, int width, int height) { this.left = left; this.top = top; console.setSize(new Size(width, height)); terminal.setSize(width, height); console.raise(Signal.WINCH); } public void dump(long[] fullscreen, int ftop, int fleft, int fheight, int fwidth, int[] cursor) { terminal.dump(fullscreen, ftop, fleft, fheight, fwidth, cursor); } @Override public void close() throws IOException { console.close(); } private class MasterOutputStream extends OutputStream { private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); private final CharsetDecoder decoder = Charset.defaultCharset().newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); @Override public synchronized void write(int b) { buffer.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { buffer.write(b, off, len); } @Override public synchronized void flush() throws IOException { int size = buffer.size(); if (size > 0) { CharBuffer out; for (; ; ) { out = CharBuffer.allocate(size); ByteBuffer in = ByteBuffer.wrap(buffer.toByteArray()); CoderResult result = decoder.decode(in, out, false); if (result.isOverflow()) { size *= 2; } else { buffer.reset(); buffer.write(in.array(), in.arrayOffset(), in.remaining()); break; } } if (out.position() > 0) { out.flip(); terminal.write(out); masterInputOutput.write(terminal.read().getBytes()); } } } @Override public void close() throws IOException { flush(); } } } } jline3-jline-3.3.1/builtins/src/main/resources/000077500000000000000000000000001311544710100213545ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/resources/org/000077500000000000000000000000001311544710100221435ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/resources/org/jline/000077500000000000000000000000001311544710100232445ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/resources/org/jline/builtins/000077500000000000000000000000001311544710100250755ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/main/resources/org/jline/builtins/nano-browser-help.txt000066400000000000000000000016011311544710100311760ustar00rootroot00000000000000File Browser Help Text The file browser is used to visually browse the directory structure to select a file for reading or writing. You may use the arrow keys or Page Up/Down to browse through the files, and S or Enter to choose the selected file or enter the selected directory. To move up one level, select the directory called ".." at the top of the file list. The following function keys are available in the file browser: ^G (F1) Display this help text ^X (F2) Exit from the file browser ^W Search for a string or a regular expression (F16) (M-W) Repeat last search ^Y (F7) Move to the previous screen ^V (F8) Move to the next screen M-\ (M-|) Go to the first file in the list M-/ (M-?) Go to the last file in the list ^_ (F13) (M-G) Go to directory jline3-jline-3.3.1/builtins/src/main/resources/org/jline/builtins/nano-main-help.txt000066400000000000000000000112131311544710100304370ustar00rootroot00000000000000 Main nano help text The nano editor is designed to emulate the functionality and ease-of-use of the UW Pico text editor. There are four main sections of the editor. The top line shows the program version, the current filename being edited, and whether or not the file has been modified. Next is the main editor window showing the file being edited. The status line is the third line from the bottom and shows important messages. The bottom two lines show the most commonly used shortcuts in the editor. The notation for shortcuts is as follows: Control-key sequences are notated with a caret (^) symbol and can be entered either by using the Control (Ctrl) key or pressing the Escape (Esc) key twice. Escape-key sequences are notated with the Meta (M-) symbol and can be entered using either the Esc, Alt, or Meta key depending on your keyboard setup. Also, pressing Esc twice and then typing a three-digit decimal number from 000 to 255 will enter the character with the corresponding value. The following keystrokes are available in the main editor window. Alternative keys are shown in parentheses: ^G (F1) Display this help text ^X (F2) Close the current file buffer / Exit from nano ^O (F3) Write the current file to disk ^J (F4) Justify the current paragraph ^R (F5) Insert another file into the current one ^W (F6) Search for a string or a regular expression ^Y (F7) Move to the previous screen ^V (F8) Move to the next screen ^K (F9) Cut the current line and store it in the cutbuffer ^U (F10) Uncut from the cutbuffer into the current line ^C (F11) Display the position of the cursor ^T (F12) Invoke the spell checker, if available ^_ (F13) (M-G) Go to line and column number ^\ (F14) (M-R) Replace a string or a regular expression ^^ (F15) (M-A) Mark text at the cursor position (F16) (M-W) Repeat last search M-^ (M-6) Copy the current line and store it in the cutbuffer M-} Indent the current line M-{ Unindent the current line ^F Move forward one character ^B Move back one character ^Space Move forward one word M-Space Move back one word ^P Move to the previous line ^N Move to the next line ^A Move to the beginning of the current line ^E Move to the end of the current line M-( (M-9) Move to the beginning of the current paragraph M-) (M-0) Move to the end of the current paragraph M-\ (M-|) Move to the first line of the file M-/ (M-?) Move to the last line of the file M-] Move to the matching bracket M-- (M-_) Scroll up one line without scrolling the cursor M-+ (M-=) Scroll down one line without scrolling the cursor M-< (M-,) Switch to the previous file buffer M-> (M-.) Switch to the next file buffer M-V Insert the next keystroke verbatim ^I Insert a tab at the cursor position ^M Insert a newline at the cursor position ^D Delete the character under the cursor ^H Delete the character to the left of the cursor M-T Cut from the cursor position to the end of the file M-J Justify the entire file M-D Count the number of words, lines, and characters ^L Refresh (redraw) the current screen M-X Help mode enable/disable M-C Constant cursor position display enable/disable M-O Use of one more line for editing enable/disable M-S Smooth scrolling enable/disable M-P Whitespace display enable/disable M-Y Color syntax highlighting enable/disable M-H Smart home key enable/disable M-I Auto indent enable/disable M-K Cut to end enable/disable M-L Long line wrapping enable/disable M-Q Conversion of typed tabs to spaces enable/disable M-B Backup files enable/disable M-F Multiple file buffers enable/disable M-M Mouse support enable/disable M-N No conversion from DOS/Mac format enable/disable M-Z Suspension enable/disable jline3-jline-3.3.1/builtins/src/main/resources/org/jline/builtins/nano-read-help.txt000066400000000000000000000006671311544710100304410ustar00rootroot00000000000000Insert File Help Text Type in the name of a file to be inserted into the current file buffer at the current cursor location. The following function keys are available in Insert File mode: ^G (F1) Display this help text ^C Cancel the current function ^T Go to file browser ^X Execute external command M-F Toggle the use of a new buffer jline3-jline-3.3.1/builtins/src/main/resources/org/jline/builtins/nano-search-help.txt000066400000000000000000000026061311544710100307660ustar00rootroot00000000000000Search Command Help Text Enter the words or characters you would like to search for, and then press Enter. If there is a match for the text you entered, the screen will be updated to the location of the nearest match for the search string. The previous search string will be shown in brackets after the search prompt. Hitting Enter without entering any text will perform the previous search. If you have selected text with the mark and then search to replace, only matches in the selected text will be replaced. The following function keys are available in Search mode: ^G (F1) Display this help text ^C Cancel the current function ^Y (F7) (M-\) Move to the first line of the file ^V (F8) (M-/) Move to the last line of the file ^R (F14) Replace a string or a regular expression ^T (F13) Go to line and column number ^W (M-() (M-9) Move to the beginning of the current paragraph ^O (M-)) (M-0) Move to the end of the current paragraph M-C Toggle the case sensitivity of the search M-B Reverse the direction of the search M-R Toggle the use of regular expressions ^P Recall the previous search/replace string ^N Recall the next search/replace string ^U (M-J) Justify the entire file jline3-jline-3.3.1/builtins/src/main/resources/org/jline/builtins/nano-write-help.txt000066400000000000000000000014721311544710100306530ustar00rootroot00000000000000Write File Help Text Type the name that you wish to save the current file as and press Enter to save the file. If you have selected text with the mark, you will be prompted to save only the selected portion to a separate file. To reduce the chance of overwriting the current file with just a portion of it, the current filename is not the default in this mode. The following function keys are available in Write File mode: ^G (F1) Display this help text ^C Cancel the current function ^T Go to file browser M-D Toggle the use of DOS format M-M Toggle the use of Mac format M-A Toggle appending M-P Toggle prepending M-B Toggle backing up of the original file jline3-jline-3.3.1/builtins/src/test/000077500000000000000000000000001311544710100173755ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/test/java/000077500000000000000000000000001311544710100203165ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/test/java/org/000077500000000000000000000000001311544710100211055ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/test/java/org/jline/000077500000000000000000000000001311544710100222065ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/test/java/org/jline/builtins/000077500000000000000000000000001311544710100240375ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/test/java/org/jline/builtins/NanoTest.java000066400000000000000000000021471311544710100264410ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import org.jline.keymap.KeyMap; import org.jline.terminal.Size; import org.jline.terminal.impl.LineDisciplineTerminal; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.File; public class NanoTest { @Test(timeout = 1000) public void nanoBufferLineOverflow() throws Exception { ByteArrayOutputStream output = new ByteArrayOutputStream(); LineDisciplineTerminal terminal = new LineDisciplineTerminal("nano", "xterm", output, "UTF-8"); terminal.setSize(new Size(80, 25)); for (int i = 0; i < 100; i++) { terminal.processInputByte(' '); } terminal.processInputByte(KeyMap.ctrl('X').getBytes()[0]); terminal.processInputByte('n'); Nano nano = new Nano(terminal, new File("target/test.txt")); nano.run(); } } jline3-jline-3.3.1/builtins/src/test/java/org/jline/builtins/NfaMatcherTest.java000066400000000000000000000057241311544710100275620ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import org.junit.Test; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class NfaMatcherTest { @Test public void testMultiplicity() { assertFalse(match("C5")); assertTrue(match("C5", "arg")); assertFalse(match("C5", "arg", "foo")); assertTrue(match("C5?")); assertTrue(match("C5?", "arg")); assertFalse(match("C5?", "arg", "foo")); assertFalse(match("C5+")); assertTrue(match("C5+", "arg")); assertTrue(match("C5+", "arg", "foo")); assertTrue(match("C5*")); assertTrue(match("C5*", "arg")); assertTrue(match("C5*", "arg", "foo")); } @Test public void testWeird() { assertTrue(match("a? a? a? a a a", "a", "a", "a", "a")); assertTrue(match("a ? * +", "a", "a", "a", "a")); } @Test public void testConcat() { assertTrue(match("C4? C5+", "arg", "foo")); assertTrue(match("(C1 | C2 | C3)* C4? C5+", "arg", "foo")); assertTrue(match("(C1 | C2 | C3)* C4? C5+", "--opt1=a", "--opt2=b", "--myopt", "arg", "foo")); } @Test public void testPartial() { assertEquals(asSet("C1", "C2", "C3", "C4", "C5"), matchPartial("(C1 | C2 | C3)* C4? C5+", "--opt1=a")); assertEquals(asSet("C5"), matchPartial("(C1 | C2 | C3)* C4? C5+", "--opt1=a", "--myopt")); } @Test public void testPartial2() { assertEquals(asSet("C3"), matchPartial(" ( C1 ( C2 ( C3 ) | C4 | C5 ) ) ", "--opt1", "--opt2")); } boolean match(String regexp, String... args) { return new NfaMatcher<>(regexp, this::matchArg).match(Arrays.asList(args)); } Set matchPartial(String regexp, String... args) { return new NfaMatcher<>(regexp, this::matchArg).matchPartial(Arrays.asList(args)); } boolean matchArg(String arg, String name) { switch (name) { case "C1": return arg.startsWith("--opt1"); case "C2": return arg.startsWith("--opt2"); case "C3": return arg.startsWith("--opt3"); case "C4": return arg.startsWith("--myopt"); case "C5": return true; case "a": return arg.equals("a"); default: throw new IllegalStateException("Unsupported: " + name); } } static Set asSet(String... ts) { Set s = new HashSet<>(); for (String t : ts) { s.add(t); } return s; } } jline3-jline-3.3.1/builtins/src/test/java/org/jline/builtins/OptionsTest.java000066400000000000000000000061721311544710100272030ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jline.builtins; import java.util.Arrays; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class OptionsTest { @Test public void testOptions() { final String[] usage = { "test - test Options usage", " text before Usage: is displayed when usage() is called and no error has occurred.", " so can be used as a simple help message.", "", "Usage: testOptions [OPTION]... PATTERN [FILES]...", " Output control: arbitary non-option text can be included.", " -? --help show help", " -c --count=COUNT show COUNT lines", " -h --no-filename suppress the prefixing filename on output", " -q --quiet, --silent suppress all normal output", " --binary-files=TYPE assume that binary files are TYPE", " TYPE is 'binary', 'text', or 'without-match'", " -I equivalent to --binary-files=without-match", " -d --directories=ACTION how to handle directories (default=skip)", " ACTION is 'read', 'recurse', or 'skip'", " -D --devices=ACTION how to handle devices, FIFOs and sockets", " ACTION is 'read' or 'skip'", " -R, -r --recursive equivalent to --directories=recurse" }; Options opt = Options.compile(usage).parse("test -c 2 --binary-files=foo --binary-files bar pattern".split("\\s")); assertTrue(opt.isSet("count")); assertEquals(2, opt.getNumber("count")); assertFalse(opt.isSet("no-filename")); assertTrue(opt.isSet("binary-files")); assertEquals(Arrays.asList("foo", "bar"), opt.getList("binary-files")); assertEquals(Arrays.asList("test", "pattern"), opt.args()); } } jline3-jline-3.3.1/builtins/src/test/java/org/jline/builtins/TmuxTest.java000066400000000000000000000021051311544710100264750ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; public class TmuxTest { @Test public void testLayoutParse() { Tmux.Layout l = Tmux.Layout.parse("b7c7,148x44,0,0[148x26,0,0{69x26,0,0,0,78x26,70,0,3},148x17,0,27{74x17,0,27,1,36x17,75,27,4,36x17,112,27,5}]"); assertNotNull(l); } @Test public void testLayoutResize() { Tmux.Layout l = Tmux.Layout.parse("b7c7,148x44,0,0[148x26,0,0{69x26,0,0,0,78x26,70,0,3},148x17,0,27{74x17,0,27,1,36x17,75,27,4,36x17,112,27,5}]"); l.resize(140, 44); assertEquals("ebac,140x44,0,0[140x26,0,0{65x26,0,0,0,74x26,66,0,0},140x17,0,27{71x17,0,27,0,33x17,72,27,0,34x17,106,27,0}]", l.dump()); System.out.println(l.dump()); } } jline3-jline-3.3.1/builtins/src/test/java/org/jline/example/000077500000000000000000000000001311544710100236415ustar00rootroot00000000000000jline3-jline-3.3.1/builtins/src/test/java/org/jline/example/Example.java000066400000000000000000000406431311544710100261060ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.example; import java.io.IOException; import java.time.LocalDate; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.jline.builtins.Completers; import org.jline.builtins.Completers.TreeCompleter; import org.jline.keymap.KeyMap; import org.jline.reader.*; import org.jline.reader.impl.DefaultParser; import org.jline.reader.impl.completer.ArgumentCompleter; import org.jline.reader.impl.completer.StringsCompleter; import org.jline.terminal.Cursor; import org.jline.terminal.MouseEvent; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; import org.jline.utils.InfoCmp.Capability; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static org.jline.builtins.Completers.TreeCompleter.node; public class Example { public static void usage() { System.out.println("Usage: java " + Example.class.getName() + " [none/simple/files/dictionary [trigger mask]]"); System.out.println(" none - no completors"); System.out.println(" simple - a simple completor that comples " + "\"foo\", \"bar\", and \"baz\""); System.out .println(" files - a completor that comples " + "file names"); System.out.println(" classes - a completor that comples " + "java class names"); System.out .println(" trigger - a special word which causes it to assume " + "the next line is a password"); System.out.println(" mask - is the character to print in place of " + "the actual password character"); System.out.println(" color - colored prompt and feedback"); System.out.println("\n E.g - java Example simple su '*'\n" + "will use the simple completor with 'su' triggering\n" + "the use of '*' as a password mask."); } public static void main(String[] args) throws IOException { try { String prompt = "prompt> "; String rightPrompt = null; Character mask = null; String trigger = null; boolean color = false; boolean timer = false; TerminalBuilder builder = TerminalBuilder.builder(); if ((args == null) || (args.length == 0)) { usage(); return; } int mouse = 0; Completer completer = null; Parser parser = null; int index = 0; label: while (args.length > index) { switch (args[index]) { /* SANDBOX JANSI case "-posix": builder.posix(false); index++; break; case "+posix": builder.posix(true); index++; break; case "-native-pty": builder.nativePty(false); index++; break; case "+native-pty": builder.nativePty(true); index++; break; */ case "timer": timer = true; index++; break; case "-system": builder.system(false); index++; break; case "+system": builder.system(true); index++; break; case "none": break label; case "files": completer = new Completers.FileNameCompleter(); break label; case "simple": completer = new StringsCompleter("foo", "bar", "baz"); break label; case "quotes": DefaultParser p = new DefaultParser(); p.setEofOnUnclosedQuote(true); parser = p; break label; case "foo": completer = new ArgumentCompleter( new StringsCompleter("foo11", "foo12", "foo13"), new StringsCompleter("foo21", "foo22", "foo23"), new Completer() { @Override public void complete(LineReader reader, ParsedLine line, List candidates) { candidates.add(new Candidate("", "", null, "frequency in MHz", null, null, false)); } }); break label; case "param": completer = (reader, line, candidates) -> { if (line.wordIndex() == 0) { candidates.add(new Candidate("Command1")); } else if (line.words().get(0).equals("Command1")) { if (line.words().get(line.wordIndex() - 1).equals("Option1")) { candidates.add(new Candidate("Param1")); candidates.add(new Candidate("Param2")); } else { if (line.wordIndex() == 1) { candidates.add(new Candidate("Option1")); } if (!line.words().contains("Option2")) { candidates.add(new Candidate("Option2")); } if (!line.words().contains("Option3")) { candidates.add(new Candidate("Option3")); } } } }; break label; case "tree": completer = new TreeCompleter( node("Command1", node("Option1", node("Param1", "Param2")), node("Option2"), node("Option3"))); break label; case "regexp": Map comp = new HashMap<>(); comp.put("C1", new StringsCompleter("cmd1")); comp.put("C11", new StringsCompleter("--opt11", "--opt12")); comp.put("C12", new StringsCompleter("arg11", "arg12", "arg13")); comp.put("C2", new StringsCompleter("cmd2")); comp.put("C21", new StringsCompleter("--opt21", "--opt22")); comp.put("C22", new StringsCompleter("arg21", "arg22", "arg23")); completer = new Completers.RegexCompleter("C1 C11* C12+ | C2 C21* C22+", comp::get); break label; case "color": color = true; prompt = new AttributedStringBuilder() .style(AttributedStyle.DEFAULT.background(AttributedStyle.GREEN)) .append("foo") .style(AttributedStyle.DEFAULT) .append("@bar") .style(AttributedStyle.DEFAULT.foreground(AttributedStyle.GREEN)) .append("\nbaz") .style(AttributedStyle.DEFAULT) .append("> ").toAnsi(); rightPrompt = new AttributedStringBuilder() .style(AttributedStyle.DEFAULT.background(AttributedStyle.RED)) .append(LocalDate.now().format(DateTimeFormatter.ISO_DATE)) .append("\n") .style(AttributedStyle.DEFAULT.foreground(AttributedStyle.RED | AttributedStyle.BRIGHT)) .append(LocalTime.now().format(new DateTimeFormatterBuilder() .appendValue(HOUR_OF_DAY, 2) .appendLiteral(':') .appendValue(MINUTE_OF_HOUR, 2) .toFormatter())) .toAnsi(); completer = new StringsCompleter("\u001B[1mfoo\u001B[0m", "bar", "\u001B[32mbaz\u001B[0m", "foobar"); break label; case "mouse": mouse = 1; break label; case "mousetrack": mouse = 2; break label; default: usage(); return; } } if (args.length == index + 2) { mask = args[index+1].charAt(0); trigger = args[index]; } Terminal terminal = builder.build(); LineReader reader = LineReaderBuilder.builder() .terminal(terminal) .completer(completer) .parser(parser) .build(); if (timer) { Executors.newScheduledThreadPool(1) .scheduleAtFixedRate(() -> { reader.callWidget(LineReader.CLEAR); reader.getTerminal().writer().println("Hello world!"); reader.callWidget(LineReader.REDRAW_LINE); reader.callWidget(LineReader.REDISPLAY); reader.getTerminal().writer().flush(); }, 1, 1, TimeUnit.SECONDS); } if (mouse != 0) { reader.setOpt(LineReader.Option.MOUSE); if (mouse == 2) { reader.getWidgets().put(LineReader.CALLBACK_INIT, () -> { terminal.trackMouse(Terminal.MouseTracking.Any); return true; }); reader.getWidgets().put(LineReader.MOUSE, () -> { MouseEvent event = reader.readMouseEvent(); StringBuilder tsb = new StringBuilder(); Cursor cursor = terminal.getCursorPosition(c -> tsb.append((char) c)); reader.runMacro(tsb.toString()); String msg = " " + event.toString(); int w = terminal.getWidth(); terminal.puts(Capability.cursor_address, 0, Math.max(0, w - msg.length())); terminal.writer().append(msg); terminal.puts(Capability.cursor_address, cursor.getY(), cursor.getX()); terminal.flush(); return true; }); } } while (true) { String line = null; try { line = reader.readLine(prompt, rightPrompt, null, null); } catch (UserInterruptException e) { // Ignore } catch (EndOfFileException e) { return; } if (line == null) { continue; } line = line.trim(); if (color) { terminal.writer().println( AttributedString.fromAnsi("\u001B[33m======>\u001B[0m\"" + line + "\"") .toAnsi(terminal)); } else { terminal.writer().println("======>\"" + line + "\""); } terminal.flush(); // If we input the special word then we will mask // the next line. if ((trigger != null) && (line.compareTo(trigger) == 0)) { line = reader.readLine("password> ", mask); } if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) { break; } ParsedLine pl = reader.getParser().parse(line, 0); if ("set".equals(pl.word())) { if (pl.words().size() == 3) { reader.setVariable(pl.words().get(1), pl.words().get(2)); } } else if ("tput".equals(pl.word())) { if (pl.words().size() == 2) { Capability vcap = Capability.byName(pl.words().get(1)); if (vcap != null) { terminal.puts(vcap); } else { terminal.writer().println("Unknown capability"); } } } else if ("bindkey".equals(pl.word())) { if (pl.words().size() == 1) { StringBuilder sb = new StringBuilder(); Map bound = reader.getKeys().getBoundKeys(); for (Map.Entry entry : bound.entrySet()) { sb.append("\""); entry.getKey().chars().forEachOrdered(c -> { if (c < 32) { sb.append('^'); sb.append((char) (c + 'A' - 1)); } else { sb.append((char) c); } }); sb.append("\" "); if (entry.getValue() instanceof Macro) { sb.append("\""); ((Macro) entry.getValue()).getSequence().chars().forEachOrdered(c -> { if (c < 32) { sb.append('^'); sb.append((char) (c + 'A' - 1)); } else { sb.append((char) c); } }); sb.append("\""); } else if (entry.getValue() instanceof Reference) { sb.append(((Reference) entry.getValue()).name().toLowerCase().replace('_', '-')); } else { sb.append(entry.getValue().toString()); } sb.append("\n"); } terminal.writer().print(sb.toString()); terminal.flush(); } else if (pl.words().size() == 3) { reader.getKeys().bind( new Reference(pl.words().get(2)), KeyMap.translate(pl.words().get(1)) ); } } else if ("cls".equals(pl.word())) { terminal.puts(Capability.clear_screen); terminal.flush(); } else if ("sleep".equals(pl.word())) { Thread.sleep(3000); } } } catch (Throwable t) { t.printStackTrace(); } } } jline3-jline-3.3.1/builtins/src/test/java/org/jline/example/PasswordReader.java000066400000000000000000000022631311544710100274340ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.example; import java.io.IOException; import org.jline.reader.LineReader; import org.jline.reader.LineReaderBuilder; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; public class PasswordReader { public static void usage() { System.out.println("Usage: java " + PasswordReader.class.getName() + " [mask]"); } public static void main(String[] args) throws IOException { Terminal terminal = TerminalBuilder.terminal(); LineReader reader = LineReaderBuilder.builder() .terminal(terminal).build(); Character mask = (args.length == 0) ? (char) 0 : args[0].charAt(0); String line; do { line = reader.readLine("Enter password> ", mask); System.out.println("Got password: " + line); } while (line != null && line.length() > 0); } } jline3-jline-3.3.1/demo/000077500000000000000000000000001311544710100147225ustar00rootroot00000000000000jline3-jline-3.3.1/demo/etc/000077500000000000000000000000001311544710100154755ustar00rootroot00000000000000jline3-jline-3.3.1/demo/etc/gosh_profile000066400000000000000000000131061311544710100201010ustar00rootroot00000000000000# Load base profile if { $.reader } { source ((($.reader parser) class) getResource "/gosh_profile") } __load_class_from = { (($1 class) classLoader) loadClass $2 } __get_static_field = { ((__load_class_from $.session $1) getField $2) get null } __call_static_method_no_arg = { ((__load_class_from $.session $1) getMethod $2) invoke null } __new_candidate = { new org.jline.reader.Candidate $1 $1 null $2 null null true } # Define the $.process variable \#.process = { __call_static_method_no_arg "org.apache.felix.service.command.Process\$Utils" "current" } # Define the $.job variable \#.job = { __call_static_method_no_arg "org.apache.felix.service.command.Job\$Utils" "current" } __process_stream = { (($.job processes) get 0) $1 } # with gogo-jline-1.0.4, should not be needed at all, see FELIX-5463 and FELIX-5462 (($.processor class) getMethod "addConverter" (__load_class_from $.processor "org.apache.felix.service.command.Converter")) invoke $.processor (new org.jline.demo.FunctionConverter) if { $.terminal } { try { # # TTop command # __ttop = { _cl = (__load_class_from $.reader "org.jline.builtins.TTop") $_cl "ttop" $.terminal (__process_stream "out") (__process_stream "err") ${argv[@]} } $.processor addcommand "gogo" $__ttop "ttop" 0 complete -c gogo:ttop -e complete -c gogo:ttop -d "Display and update sorted information about threads" complete -c gogo:ttop -s o -l order --description "Comma separated list of sorting keys" complete -c gogo:ttop -s t -l stats --description "Comma separated list of stats to display" max_colors = ($.terminal getNumericCapability "max_colors") if { %(max_colors >= 256) } { HIGHLIGHTER_COLORS = "rs=35:st=32:nu=32:co=32:va=36:vn=36:fu=1;38;5;69:bf=1;38;5;197:re=90" } { HIGHLIGHTER_COLORS = "rs=35:st=32:nu=32:co=32:va=36:vn=36:fu=94:bf=91:re=90" } __tmux_commands = [ (__new_candidate send-prefix "Send prefix") (__new_candidate split-window "Split the current window") (__new_candidate select-pane "Select pane") (__new_candidate resize-pane "Resize pane") (__new_candidate display-pane "Display panes") (__new_candidate clock-mode "Clock mode") (__new_candidate set-option "Set option") (__new_candidate list-keys "List keys") (__new_candidate send-keys "Send keys") (__new_candidate bind-key "Bind key") (__new_candidate unbind-key "Unbind key") ] complete -c gogo:tmux -e complete -c gogo:tmux -d "Terminal multiplexer" complete -c gogo:tmux -s h -l help --description "Display help and exit" -n '__option_not_present -h --help' complete -c gogo:tmux -a 'wi = ($.commandLine wordIndex); if { %(wi==1) } { $__tmux_commands } { [ ] }' __gogo = (new "org.jline.demo.Gogo" $.processor) __remote_ssh_support = (new org.jline.builtins.ssh.Ssh \ ($__gogo shell) \ ($__gogo command) \ { server = ((new org.apache.sshd.server.ServerBuilder) build) $server setPasswordAuthenticator (__get_static_field org.apache.sshd.server.auth.password.AcceptAllPasswordAuthenticator INSTANCE) $server setPublickeyAuthenticator (__get_static_field org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator INSTANCE) $server } \ { (new org.apache.sshd.client.ClientBuilder) build } ) __remote_sshd = { # See FELIX-5465, should be: # a = [ "sshd" ${argv[@]} ] a = [ "sshd" ] each $argv { $a add $it } $__remote_ssh_support sshd (__process_stream "out") (__process_stream "err") ${a[@]} } __remote_ssh = { # See FELIX-5465, should be: # a = [ "ssh" ${argv[@]} ] a = [ "ssh" ] each $argv { $a add $it } $__remote_ssh_support ssh $.terminal $.reader $USER (__process_stream "in") (__process_stream "out") (__process_stream "err") ${a[@]} } $.processor addcommand "remote" $__remote_sshd "sshd" 0 $.processor addcommand "remote" $__remote_ssh "ssh" 0 complete -c remote:sshd -e complete -c remote:sshd -d "SSH daemon" complete -c remote:sshd -s i -l ip --description "Listening IP interface" -n '__option_not_present -i --ip' complete -c remote:sshd -s p -l port --description "Listening IP port" -n '__option_not_present -p --port' complete -c remote:sshd -a '[start stop status]' complete -c remote:ssh -e complete -c remote:ssh -d "SSH client" __remote_telnet_support = (new org.jline.builtins.telnet.Telnet $.terminal ($__gogo telnet)) __remote_telnetd = { a = [ "telnetd" ] each $argv { $a add $it } $__remote_telnet_support telnetd ${a[@]} } $.processor addcommand "remote" $__remote_telnetd "telnetd" 0 complete -c remote:telnetd -e complete -c remote:telnetd -d "Telnet daemon" complete -c remote:telnetd -s i -l ip --description "Listening IP interface" -n '__option_not_present -i --ip' complete -c remote:telnetd -s p -l port --description "Listening IP port" -n '__option_not_present -p --port' complete -c remote:telnetd -a '[start stop status]' } { if { not { (($exception cause) toString) startsWith "java.lang.NoClassDefFoundError: org/apache/sshd/" } } { $exception printStackTrace } } } jline3-jline-3.3.1/demo/etc/logging-verbose.properties000066400000000000000000000022131311544710100227020ustar00rootroot00000000000000################################################################################ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ################################################################################ handlers=java.util.logging.ConsoleHandler .level=FINEST java.util.logging.ConsoleHandler.level=FINEST java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter jline3-jline-3.3.1/demo/etc/logging.properties000066400000000000000000000021411311544710100212370ustar00rootroot00000000000000################################################################################ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ################################################################################ # Empty java.util.logging.properties to prevent the log to stderr, so that # all logs will be delegated to pax logging JUL handler only jline3-jline-3.3.1/demo/jline-gogo.bat000077500000000000000000000040661311544710100174550ustar00rootroot00000000000000@echo off set DIRNAME=%~dp0% set ROOTDIR=%DIRNAME%\.. set TARGETDIR=%DIRNAME%target rem initialization if not exist %TARGETDIR%\lib ( echo Build jline with maven before running the demo goto END ) goto :SETUP_CLASSPATH : APPEND_TO_CLASSPATH set filename=%~1 set cp=%cp%;%TARGETDIR%\lib\%filename% goto :EOF :SETUP_CLASSPATH set cp=%TARGETDIR%\classes rem JLINE pushd %TARGETDIR%\lib for %%G in (jline-*.jar) do call:APPEND_TO_CLASSPATH %%G rem Gogo Runtime for %%G in (org.apache.felix.gogo.runtime-*.jar) do call:APPEND_TO_CLASSPATH %%G rem Gogo JLine for %%G in (org.apache.felix.gogo.jline-*.jar) do call:APPEND_TO_CLASSPATH %%G set "opts=%JLINE_OPTS%" set "logconf=%DIRNAME%etc\logging.properties" :RUN_LOOP if "%1" == "jansi" goto :EXECUTE_JANSI if "%1" == "jna" goto :EXECUTE_JNA if "%1" == "ssh" goto :EXECUTE_SSH if "%1" == "debug" goto :EXECUTE_DEBUG if "%1" == "debugs" goto :EXECUTE_DEBUGS if "%1" == "verbose" goto :EXECUTE_VERBOSE if "%1" == "" goto :EXECUTE set "opts=%opts% %~1" shift goto :RUN_LOOP :EXECUTE_JANSI for %%G in (jansi-*.jar) do call:APPEND_TO_CLASSPATH %%G shift goto :RUN_LOOP :EXECUTE_JNA for %%G in (jna-*.jar) do call:APPEND_TO_CLASSPATH %%G shift goto :RUN_LOOP :EXECUTE_SSH for %%G in (sshd-core-*.jar) do call:APPEND_TO_CLASSPATH %%G for %%G in (slf4j-api-*.jar) do call:APPEND_TO_CLASSPATH %%G for %%G in (slf4j-jdk14-*.jar) do call:APPEND_TO_CLASSPATH %%G shift goto :RUN_LOOP :EXECUTE_DEBUG set "opts=%opts% -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" shift goto :RUN_LOOP :EXECUTE_DEBUGS set "opts=%opts% -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" shift goto :RUN_LOOP :EXECUTE_VERBOSE set "logconf=%DIRNAME%etc\logging-verbose.properties" shift goto :RUN_LOOP :EXECUTE popd rem Launch gogo shell echo Launching Gogo JLine... echo Classpath: %cp% java -cp %cp% %opts% -Dgosh.home=%DIRNAME% -Djava.util.logging.config.file=%logconf% org.apache.felix.gogo.jline.Main :ENDjline3-jline-3.3.1/demo/jline-gogo.sh000077500000000000000000000051071311544710100173160ustar00rootroot00000000000000#!/bin/sh realpath() { OURPWD=${PWD} cd "$(dirname "${1}")" LINK=$(readlink "$(basename "${1}")") while [ "${LINK}" ]; do cd "$(dirname "${LINK}")" LINK=$(readlink "$(basename "${1}")") done REALPATH="${PWD}/$(basename "${1}")" cd "${OURPWD}" echo "${REALPATH}" } REALNAME=$(realpath "$0") DIRNAME=$(dirname "${REALNAME}") PROGNAME=$(basename "${REALNAME}") ROOTDIR=${DIRNAME}/.. TARGETDIR=${DIRNAME}/target if [ ! -e ${TARGETDIR}/lib ] ; then echo "Build jline with maven before running the demo" exit fi; cp=${TARGETDIR}/classes # JLINE cp=${cp}$(find ${TARGETDIR}/lib -name "jline-*.jar" -exec printf :{} ';') # Gogo Runtime cp=${cp}$(find ${TARGETDIR}/lib -name "org.apache.felix.gogo.runtime-*.jar" -exec printf :{} ';') # Gogo JLine cp=${cp}$(find ${TARGETDIR}/lib -name "org.apache.felix.gogo.jline-*.jar" -exec printf :{} ';') opts="${JLINE_OPTS}" logconf="${DIRNAME}/etc/logging.properties" while [ "${1}" != "" ]; do case ${1} in 'debug') opts="${opts} -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" shift ;; 'debugs') opts="${opts} -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" shift ;; 'jansi') cp=${cp}$(find ${TARGETDIR}/lib -name "jansi-*.jar" -exec printf :{} ';') shift ;; 'jna') cp=${cp}$(find ${TARGETDIR}/lib -name "jna-*.jar" -exec printf :{} ';') shift ;; 'ssh' | 'telnet' | 'remote') cp=${cp}$(find ${TARGETDIR}/lib -name "sshd-core-*.jar" -exec printf :{} ';') cp=${cp}$(find ${TARGETDIR}/lib -name "slf4j-api-*.jar" -exec printf :{} ';') cp=${cp}$(find ${TARGETDIR}/lib -name "slf4j-jdk14-*.jar" -exec printf :{} ';') shift ;; 'verbose') logconf="${DIRNAME}/etc/logging-verbose.properties" shift ;; *) opts="${opts} ${1}" shift ;; esac done cygwin=false mingw=false case "$(uname)" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true ;; esac if ${cygwin}; then cp=$(cygpath --path --windows "${cp}") DIRNAME=$(cygpath --path --windows "${DIRNAME}") fi nothing() { # nothing to do here a=a } trap 'nothing' TSTP # Launch gogo shell echo "Launching Gogo JLine..." echo "Classpath: $cp" set mouse=a java -cp $cp \ $opts \ -Dgosh.home="${DIRNAME}" \ -Djava.util.logging.config.file="${logconf}" \ org.apache.felix.gogo.jline.Main jline3-jline-3.3.1/demo/pom.xml000066400000000000000000000050121311544710100162350ustar00rootroot00000000000000 4.0.0 org.jline jline-parent 3.3.1 jline-demo JLine Demo org.jline jline org.apache.felix org.apache.felix.gogo.runtime org.apache.felix org.apache.felix.gogo.jline net.java.dev.jna jna org.fusesource.jansi jansi org.apache.sshd sshd-core org.slf4j slf4j-api org.slf4j slf4j-jdk14 maven-dependency-plugin copy copy-dependencies ${project.build.directory}/lib jline3-jline-3.3.1/demo/src/000077500000000000000000000000001311544710100155115ustar00rootroot00000000000000jline3-jline-3.3.1/demo/src/main/000077500000000000000000000000001311544710100164355ustar00rootroot00000000000000jline3-jline-3.3.1/demo/src/main/java/000077500000000000000000000000001311544710100173565ustar00rootroot00000000000000jline3-jline-3.3.1/demo/src/main/java/org/000077500000000000000000000000001311544710100201455ustar00rootroot00000000000000jline3-jline-3.3.1/demo/src/main/java/org/jline/000077500000000000000000000000001311544710100212465ustar00rootroot00000000000000jline3-jline-3.3.1/demo/src/main/java/org/jline/demo/000077500000000000000000000000001311544710100221725ustar00rootroot00000000000000jline3-jline-3.3.1/demo/src/main/java/org/jline/demo/FunctionConverter.java000066400000000000000000000071521311544710100265170ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.demo; import org.apache.felix.service.command.Converter; import org.apache.felix.service.command.Function; import java.lang.invoke.MethodHandles; import java.lang.reflect.*; import java.util.Arrays; import java.util.Collections; public class FunctionConverter implements Converter { @Override public Object convert(Class desiredType, Object in) throws Exception { if (in instanceof Function && desiredType.isInterface() && isFunctional(desiredType)) { return Proxy.newProxyInstance(desiredType.getClassLoader(), new Class[]{desiredType}, new InvocationHandler() { Function command = ((Function) in); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (isObjectMethod(method)) { return method.invoke(command, args); } else if (method.isDefault()) { final Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); field.setAccessible(true); final MethodHandles.Lookup lookup = (MethodHandles.Lookup) field.get(null); return lookup .unreflectSpecial(method, method.getDeclaringClass()) .bindTo(proxy) .invokeWithArguments(args); } else { return command.execute(null, args != null ? Arrays.asList(args) : Collections.emptyList()); } } }); } return null; } @Override public CharSequence format(Object target, int level, Converter escape) throws Exception { return null; } public static boolean isFunctional(Class clazz) { if (!clazz.isInterface()) { return false; } int nb = 0; for (Method method : clazz.getMethods()) { if (method.isDefault() || isObjectMethod(method) || isStatic(method)) { continue; } nb++; } return nb == 1; } public static boolean isStatic(Method method) { return (method.getModifiers() & Modifier.STATIC) == Modifier.STATIC; } public static boolean isObjectMethod(Method method) { switch (method.getName()) { case "toString": if (method.getParameterCount() == 0 && method.getReturnType() == String.class) { return true; } break; case "equals": if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == Object.class && method.getReturnType() == boolean.class) { return true; } break; case "hashCode": if (method.getParameterCount() == 0 && method.getReturnType() == int.class) { return true; } break; } return false; } }jline3-jline-3.3.1/demo/src/main/java/org/jline/demo/Gogo.java000066400000000000000000000057531311544710100237420ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.demo; import org.apache.felix.gogo.jline.Shell; import org.apache.felix.service.command.CommandProcessor; import org.apache.felix.service.command.CommandSession; import org.jline.builtins.ssh.Ssh; import org.jline.builtins.telnet.Telnet; import org.jline.terminal.Terminal; import java.util.Map; import java.util.function.Consumer; public class Gogo { private final CommandProcessor processor; public Gogo(CommandProcessor processor) { this.processor = processor; } public Consumer shell() { return this::shell; } public Consumer command() { return this::command; } public Telnet.ShellProvider telnet() { return this::telnet; } private void shell(Ssh.ShellParams shell) { Terminal terminal = shell.getTerminal(); CommandSession session = processor.createSession(terminal.input(), terminal.output(), terminal.output()); session.put(Shell.VAR_TERMINAL, terminal); shell.getEnv().forEach(session::put); try { new Shell(context(shell.getCloser()::run), processor).gosh(session, new String[]{"--login"}); } catch (Exception e) { throw new RuntimeException(e); } } private void command(Ssh.ExecuteParams exec) { CommandSession session = processor.createSession(exec.getIn(), exec.getOut(), exec.getErr()); exec.getEnv().forEach(session::put); try { new Shell(context(null), processor).gosh(session, new String[]{"--login", "--nointeractive", "--noshutdown", "--command", exec.getCommand()}); } catch (Exception e) { throw new RuntimeException(e); } } private void telnet(Terminal terminal, Map environment) { CommandSession session = processor.createSession(terminal.input(), terminal.output(), terminal.output()); session.put(Shell.VAR_TERMINAL, terminal); environment.forEach(session::put); try { new Shell(context(terminal::close), processor).gosh(session, new String[]{"--login"}); } catch (Exception e) { throw new RuntimeException(e); } } interface Closer { void close() throws Exception; } private Shell.Context context(Closer closer) { return new Shell.Context() { @Override public String getProperty(String name) { return System.getProperty(name); } @Override public void exit() throws Exception { if (closer != null) { closer.close(); } } }; } } jline3-jline-3.3.1/header.txt000066400000000000000000000003661311544710100157740ustar00rootroot00000000000000Copyright (c) 2002-2016, the original author or authors. This software is distributable under the BSD license. See the terms of the BSD license in the documentation provided with this software. http://www.opensource.org/licenses/bsd-license.phpjline3-jline-3.3.1/jline/000077500000000000000000000000001311544710100150775ustar00rootroot00000000000000jline3-jline-3.3.1/jline/pom.xml000066400000000000000000000327301311544710100164210ustar00rootroot00000000000000 4.0.0 org.jline jline-parent 3.3.1 jline JLine Bundle org.fusesource.jansi jansi true net.java.dev.jna jna true com.googlecode.juniversalchardet juniversalchardet true org.apache.sshd sshd-core true maven-dependency-plugin process-sources unpack org.jline jline-terminal sources jar false ${project.build.directory}/generated-sources org.jline jline-terminal-jansi sources jar false ${project.build.directory}/generated-sources org.jline jline-terminal-jna sources jar false ${project.build.directory}/generated-sources org.jline jline-reader sources jar false ${project.build.directory}/generated-sources org.jline jline-builtins sources jar false ${project.build.directory}/generated-sources org.jline jline-remote-ssh sources jar false ${project.build.directory}/generated-sources org.jline jline-remote-telnet sources jar false ${project.build.directory}/generated-sources org.jline jline-terminal jar false ${project.build.directory}/generated-resources **/*.class org.jline jline-terminal-jansi jar false ${project.build.directory}/generated-resources **/*.class org.jline jline-terminal-jna jar false ${project.build.directory}/generated-resources **/*.class org.jline jline-reader jar false ${project.build.directory}/generated-resources **/*.class org.jline jline-builtins jar false ${project.build.directory}/generated-resources **/*.class org.jline jline-remote-ssh jar false ${project.build.directory}/generated-resources **/*.class org.jline jline-remote-telnet jar false ${project.build.directory}/generated-resources **/*.class org.codehaus.mojo build-helper-maven-plugin 1.12 add-source generate-sources add-source ${project.build.directory}/generated-sources add-resource generate-resources add-resource ${project.build.directory}/generated-resources org.apache.maven.plugins maven-compiler-plugin true true default-compile **/TTop.java -Xlint:all,-options -Werror -profile compact1 noncompact compile **/TTop.java -Xlint:all,-options -Werror -profile compact3 org.apache.felix maven-bundle-plugin *;-noimport:=true com.sun.jna*, org.apache.sshd*, org.fusesource.jansi;version="[1.12,2)", org.fusesource.jansi.internal;version="[1.6,2)", * jline3-jline-3.3.1/mvnw000077500000000000000000000160441311544710100147200ustar00rootroot00000000000000#!/bin/sh # ---------------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # Maven2 Start Up Batch script # # Required ENV vars: # ------------------ # JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- # M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- if [ -z "$MAVEN_SKIP_RC" ] ; then if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi if [ -f "$HOME/.mavenrc" ] ; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. cygwin=false; darwin=false; mingw=false case "`uname`" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true # # Look for the Apple JDKs first to preserve the existing behaviour, and then look # for the new JDKs provided by Oracle. # if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then # # Apple JDKs # export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home fi if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then # # Apple JDKs # export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home fi if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then # # Oracle JDKs # export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home fi if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then # # Apple JDKs # export JAVA_HOME=`/usr/libexec/java_home` fi ;; esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then JAVA_HOME=`java-config --jre-home` fi fi if [ -z "$M2_HOME" ] ; then ## resolve links - $0 may be a link to maven's home PRG="$0" # need this for relative symlinks while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG="`dirname "$PRG"`/$link" fi done saveddir=`pwd` M2_HOME=`dirname "$PRG"`/.. # make it fully qualified M2_HOME=`cd "$M2_HOME" && pwd` cd "$saveddir" # echo Using m2 at $M2_HOME fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi # For Migwn, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" fi if [ -z "$JAVA_HOME" ]; then javaExecutable="`which javac`" if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=`which readlink` if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then if $darwin ; then javaHome="`dirname \"$javaExecutable\"`" javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" else javaExecutable="`readlink -f \"$javaExecutable\"`" fi javaHome="`dirname \"$javaExecutable\"`" javaHome=`expr "$javaHome" : '\(.*\)/bin'` JAVA_HOME="$javaHome" export JAVA_HOME fi fi fi if [ -z "$JAVACMD" ] ; then if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi else JAVACMD="`which java`" fi fi if [ ! -x "$JAVACMD" ] ; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { local basedir=$(pwd) local wdir=$(pwd) while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi wdir=$(cd "$wdir/.."; pwd) done echo "${basedir}" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then echo "$(tr -s '\n' ' ' < "$1")" fi } export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"` [ -n "$MAVEN_PROJECTBASEDIR" ] && MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi # Provide a "standardized" way to retrieve the CLI args that will # work with both Windows and non-Windows executions. MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" export MAVEN_CMD_LINE_ARGS WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS jline3-jline-3.3.1/mvnw.cmd000066400000000000000000000116631311544710100154610ustar00rootroot00000000000000@REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Maven2 Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @REM ==== END VALIDATION ==== :init set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%" == "on" pause if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% exit /B %ERROR_CODE% jline3-jline-3.3.1/pom.xml000066400000000000000000000374331311544710100153250ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 9 org.jline jline-parent JLine Parent JLine 3.3.1 pom The BSD License http://www.opensource.org/licenses/bsd-license.php repo scm:git:git://github.com/jline/jline3.git scm:git:ssh://git@github.com/jline/jline3.git http://github.com/jline/jline3 github https://github.com/jline/jline3/issues travis https://travis-ci.org/jline/jline3 jline-users mailto:jline-users@googlegroups.com mailto:jline-users+unsubscribe@googlegroups.com https://groups.google.com/group/jline-users https://groups.google.com/group/jline-users gnodet Guillaume Nodet gnodet@gmail.com Developer UTF-8 1.8 1.8 4.2.2 1.16 1.0.3 1.4.0 3.3.1 4.12 1.0.6 1.0.6 1.7.21 2.2.1 apache-snapshots Apache Snapshots https://repository.apache.org/content/groups/snapshots false true org.jline jline-terminal ${project.version} org.jline jline-terminal-jansi ${project.version} org.jline jline-terminal-jna ${project.version} org.jline jline-reader ${project.version} org.jline jline-builtins ${project.version} org.jline jline-remote-ssh ${project.version} org.jline jline-remote-telnet ${project.version} org.jline jline ${project.version} org.fusesource.jansi jansi ${jansi.version} net.java.dev.jna jna ${jna.version} com.googlecode.juniversalchardet juniversalchardet ${juniversalchardet.version} org.apache.sshd sshd-core ${sshd.version} org.apache.felix org.apache.felix.gogo.runtime ${gogo.runtime.version} org.apache.felix org.apache.felix.gogo.jline ${gogo.jline.version} org.slf4j slf4j-api ${slf4j.version} org.slf4j slf4j-jdk14 ${slf4j.version} org.easymock easymock ${easymock.version} junit junit ${junit.version} install org.apache.maven.plugins maven-deploy-plugin 2.8.2 true com.mycila license-maven-plugin 2.11 UTF-8 true false
${project.basedir}/header.txt
true **/pom.xml **/*.xml **/*.xsd **/*.xjb **/*.properties **/*.ini **/*.java **/*.groovy **/*.scala **/*.aj **/*.js **/*.css **/*.help **/*.proto **/*.sm **/*.bat **/*.xsl **/*.html **/*.vm **/*.md **/*.sh **/*.bash **/*.rb **/target/** **/.*/** **/dependency-reduced-pom.xml **/nbactions*.xml **/nb-configuration.xml **/atlassian-ide-plugin.xml **/release.properties **/META-INF/services/** JAVADOC_STYLE SLASHSTAR_STYLE SCRIPT_STYLE SCRIPT_STYLE SLASHSTAR_STYLE XML_STYLE DOUBLESLASH_STYLE SLASHSTAR_STYLE DOUBLESLASH_STYLE VELOCITY_STYLE XML_STYLE
org.apache.maven.plugins maven-compiler-plugin 3.6.0 true -Xlint:all,-options -Werror -profile compact1 true org.apache.felix maven-bundle-plugin 3.2.0 *;-noimport:=true process-classes manifest org.apache.maven.plugins maven-jar-plugin 3.0.2 ${project.build.outputDirectory}/META-INF/MANIFEST.MF org.apache.maven.plugins maven-surefire-plugin 2.19.1 true ${surefire.argLine} false org.apache.maven.plugins maven-source-plugin 3.0.1 attach-sources jar-no-fork org.apache.maven.plugins maven-javadoc-plugin 2.10.4 -Xdoclint:none false javadoc jar
java9 --add-opens java.base/java.io=ALL-UNNAMED 9 org.apache.maven.plugins maven-compiler-plugin 3.6.0 8 license-check com.mycila:license-maven-plugin:check license-format com.mycila:license-maven-plugin:format terminal terminal-jna terminal-jansi reader builtins remote-ssh remote-telnet jline demo
jline3-jline-3.3.1/reader/000077500000000000000000000000001311544710100152405ustar00rootroot00000000000000jline3-jline-3.3.1/reader/pom.xml000066400000000000000000000021341311544710100165550ustar00rootroot00000000000000 4.0.0 org.jline jline-parent 3.3.1 jline-reader JLine Reader org.jline jline-terminal junit junit test jline3-jline-3.3.1/reader/src/000077500000000000000000000000001311544710100160275ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/000077500000000000000000000000001311544710100167535ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/java/000077500000000000000000000000001311544710100176745ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/java/org/000077500000000000000000000000001311544710100204635ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/java/org/jline/000077500000000000000000000000001311544710100215645ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/java/org/jline/keymap/000077500000000000000000000000001311544710100230525ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/java/org/jline/keymap/BindingReader.java000066400000000000000000000132211311544710100264110ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.keymap; import java.io.IOError; import java.io.IOException; import java.util.ArrayDeque; import java.util.Deque; import org.jline.reader.EndOfFileException; import org.jline.utils.ClosedException; import org.jline.utils.NonBlockingReader; /** * The BindingReader will transform incoming chars into * key bindings * * @author Guillaume Nodet */ public class BindingReader { protected final NonBlockingReader reader; protected final StringBuilder opBuffer = new StringBuilder(); protected final Deque pushBackChar = new ArrayDeque<>(); protected String lastBinding; public BindingReader(NonBlockingReader reader) { this.reader = reader; } /** * Read from the input stream and decode an operation from the key map. * * The input stream will be read character by character until a matching * binding can be found. Characters that can't possibly be matched to * any binding will be send with the {@link KeyMap#getNomatch()} binding. * Unicode (>= 128) characters will be matched to {@link KeyMap#getUnicode()}. * If the current key sequence is ambiguous, i.e. the sequence is bound but * it's also a prefix to other sequences, then the {@link KeyMap#getAmbiguousTimeout()} * timeout will be used to wait for another incoming character. * If a character comes, the disambiguation will be done. If the timeout elapses * and no character came in, or if the timeout is <= 0, the current bound operation * will be returned. * * @param keys the KeyMap to use for decoding the input stream * @return the decoded binding or null if the end of * stream has been reached */ public T readBinding(KeyMap keys) { return readBinding(keys, null, true); } public T readBinding(KeyMap keys, KeyMap local) { return readBinding(keys, local, true); } public T readBinding(KeyMap keys, KeyMap local, boolean block) { lastBinding = null; T o = null; int[] remaining = new int[1]; boolean hasRead = false; for (;;) { if (local != null) { o = local.getBound(opBuffer, remaining); } if (o == null && (local == null || remaining[0] >= 0)) { o = keys.getBound(opBuffer, remaining); } // We have a binding and additional chars if (o != null) { if (remaining[0] >= 0) { runMacro(opBuffer.substring(opBuffer.length() - remaining[0])); opBuffer.setLength(opBuffer.length() - remaining[0]); } else { long ambiguousTimeout = keys.getAmbiguousTimeout(); if (ambiguousTimeout > 0 && peekCharacter(ambiguousTimeout) != NonBlockingReader.READ_EXPIRED) { o = null; } } if (o != null) { lastBinding = opBuffer.toString(); opBuffer.setLength(0); return o; } // We don't match anything } else if (remaining[0] > 0) { int cp = opBuffer.codePointAt(0); String rem = opBuffer.substring(Character.charCount(cp)); lastBinding = opBuffer.substring(0, Character.charCount(cp)); // Unicode character o = (cp >= KeyMap.KEYMAP_LENGTH) ? keys.getUnicode() : keys.getNomatch(); opBuffer.setLength(0); opBuffer.append(rem); if (o != null) { return o; } } if (!block && hasRead) { break; } int c = readCharacter(); if (c == -1) { return null; } opBuffer.appendCodePoint(c); hasRead = true; } return null; } /** * Read a codepoint from the terminal. * * @return the character, or -1 if an EOF is received. */ public int readCharacter() { if (!pushBackChar.isEmpty()) { return pushBackChar.pop(); } try { int c = NonBlockingReader.READ_EXPIRED; int s = 0; while (c == NonBlockingReader.READ_EXPIRED) { c = reader.read(100L); if (c >= 0 && Character.isHighSurrogate((char) c)) { s = c; c = NonBlockingReader.READ_EXPIRED; } } return s != 0 ? Character.toCodePoint((char) s, (char) c) : c; } catch (ClosedException e) { throw new EndOfFileException(e); } catch (IOException e) { throw new IOError(e); } } public int peekCharacter(long timeout) { if (!pushBackChar.isEmpty()) { return pushBackChar.peek(); } try { return reader.peek(timeout); } catch (IOException e) { throw new IOError(e); } } public void runMacro(String macro) { macro.codePoints().forEachOrdered(pushBackChar::addLast); } public String getCurrentBuffer() { return opBuffer.toString(); } public String getLastBinding() { return lastBinding; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/keymap/KeyMap.java000066400000000000000000000350021311544710100251030ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.keymap; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Map; import java.util.TreeMap; import org.jline.terminal.Terminal; import org.jline.utils.Curses; import org.jline.utils.InfoCmp.Capability; /** * The KeyMap class contains all bindings from keys to operations. * * @author Guillaume Nodet * @since 2.6 */ public class KeyMap { public static final int KEYMAP_LENGTH = 128; public static final long DEFAULT_AMBIGUOUS_TIMEOUT = 1000L; private Object[] mapping = new Object[KEYMAP_LENGTH]; private T anotherKey = null; private T unicode; private T nomatch; private long ambiguousTimeout = DEFAULT_AMBIGUOUS_TIMEOUT; public static String display(String key) { StringBuilder sb = new StringBuilder(); sb.append("\""); for (int i = 0; i < key.length(); i++) { char c = key.charAt(i); if (c < 32) { sb.append('^'); sb.append((char) (c + 'A' - 1)); } else if (c == 127) { sb.append("^?"); } else if (c == '^' || c == '\\') { sb.append('\\').append(c); } else if (c >= 128) { sb.append(String.format("\\u%04x", (int) c)); } else { sb.append(c); } } sb.append("\""); return sb.toString(); } public static String translate(String str) { int i; if (!str.isEmpty()) { char c = str.charAt(0); if ((c == '\'' || c == '"') && str.charAt(str.length() - 1) == c) { str = str.substring(1, str.length() - 1); } } StringBuilder keySeq = new StringBuilder(); for (i = 0; i < str.length(); i++) { char c = str.charAt(i); if (c == '\\') { if (++i >= str.length()) { break; } c = str.charAt(i); switch (c) { case 'a': c = 0x07; break; case 'b': c = '\b'; break; case 'd': c = 0x7f; break; case 'e': case 'E': c = 0x1b; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = 0x0b; break; case '\\': c = '\\'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = 0; for (int j = 0; j < 3; j++, i++) { if (i >= str.length()) { break; } int k = Character.digit(str.charAt(i), 8); if (k < 0) { break; } c = (char) (c * 8 + k); } i--; c &= 0xFF; break; case 'x': i++; c = 0; for (int j = 0; j < 2; j++, i++) { if (i >= str.length()) { break; } int k = Character.digit(str.charAt(i), 16); if (k < 0) { break; } c = (char) (c * 16 + k); } i--; c &= 0xFF; break; case 'u': i++; c = 0; for (int j = 0; j < 4; j++, i++) { if (i >= str.length()) { break; } int k = Character.digit(str.charAt(i), 16); if (k < 0) { break; } c = (char) (c * 16 + k); } break; case 'C': if (++i >= str.length()) { break; } c = str.charAt(i); if (c == '-') { if (++i >= str.length()) { break; } c = str.charAt(i); } c = c == '?' ? 0x7f : (char) (Character.toUpperCase(c) & 0x1f); break; } } else if (c == '^') { if (++i >= str.length()) { break; } c = str.charAt(i); if (c != '^') { c = c == '?' ? 0x7f : (char) (Character.toUpperCase(c) & 0x1f); } } keySeq.append(c); } return keySeq.toString(); } public static Collection range(String range) { String[] keys = range.split("-"); if (keys.length != 2) { return null; } keys[0] = translate(keys[0]); keys[1] = translate(keys[1]); if (keys[0].length() != keys[1].length()) { return null; } String pfx; if (keys[0].length() > 1) { pfx = keys[0].substring(0, keys[0].length() - 1); if (!keys[1].startsWith(pfx)) { return null; } } else { pfx = ""; } char c0 = keys[0].charAt(keys[0].length() - 1); char c1 = keys[1].charAt(keys[1].length() - 1); if (c0 > c1) { return null; } Collection seqs = new ArrayList<>(); for (char c = c0; c <= c1; c++) { seqs.add(pfx + c); } return seqs; } public static String esc() { return "\033"; } public static String alt(char c) { return "\033" + c; } public static String alt(String c) { return "\033" + c; } public static String del() { return "\177"; } public static String ctrl(char key) { return key == '?' ? del() : Character.toString((char) (Character.toUpperCase(key) & 0x1f)); } public static String key(Terminal terminal, Capability capability) { try { String str = terminal.getStringCapability(capability); if (str != null) { StringWriter sw = new StringWriter(); Curses.tputs(sw, str); return sw.toString(); } } catch (IOException e) { // Ignore } return null; } public static final Comparator KEYSEQ_COMPARATOR = (s1, s2) -> { int len1 = s1.length(); int len2 = s2.length(); int lim = Math.min(len1, len2); int k = 0; while (k < lim) { char c1 = s1.charAt(k); char c2 = s2.charAt(k); if (c1 != c2) { int l = len1 - len2; return l != 0 ? l : c1 - c2; } k++; } return len1 - len2; }; // // Methods // public T getUnicode() { return unicode; } public void setUnicode(T unicode) { this.unicode = unicode; } public T getNomatch() { return nomatch; } public void setNomatch(T nomatch) { this.nomatch = nomatch; } public long getAmbiguousTimeout() { return ambiguousTimeout; } public void setAmbiguousTimeout(long ambiguousTimeout) { this.ambiguousTimeout = ambiguousTimeout; } public T getAnotherKey() { return anotherKey; } public Map getBoundKeys() { Map bound = new TreeMap<>(KEYSEQ_COMPARATOR); doGetBoundKeys(this, "", bound); return bound; } @SuppressWarnings("unchecked") private static void doGetBoundKeys(KeyMap keyMap, String prefix, Map bound) { if (keyMap.anotherKey != null) { bound.put(prefix, keyMap.anotherKey); } for (int c = 0; c < keyMap.mapping.length; c++) { if (keyMap.mapping[c] instanceof KeyMap) { doGetBoundKeys((KeyMap) keyMap.mapping[c], prefix + (char) (c), bound); } else if (keyMap.mapping[c] != null) { bound.put(prefix + (char) (c), (T) keyMap.mapping[c]); } } } @SuppressWarnings("unchecked") public T getBound(CharSequence keySeq, int[] remaining) { remaining[0] = -1; if (keySeq != null && keySeq.length() > 0) { char c = keySeq.charAt(0); if (c >= mapping.length) { remaining[0] = Character.codePointCount(keySeq, 0, keySeq.length()); return null; } else { if (mapping[c] instanceof KeyMap) { CharSequence sub = keySeq.subSequence(1, keySeq.length()); return ((KeyMap) mapping[c]).getBound(sub, remaining); } else if (mapping[c] != null) { remaining[0] = keySeq.length() - 1; return (T) mapping[c]; } else { remaining[0] = keySeq.length(); return anotherKey; } } } else { return anotherKey; } } public T getBound(CharSequence keySeq) { int[] remaining = new int[1]; T res = getBound(keySeq, remaining); return remaining[0] <= 0 ? res : null; } public void bindIfNotBound(T function, CharSequence keySeq) { if (function != null && keySeq != null) { bind(this, keySeq, function, true); } } public void bind(T function, CharSequence... keySeqs) { for (CharSequence keySeq : keySeqs) { bind(function, keySeq); } } public void bind(T function, Iterable keySeqs) { for (CharSequence keySeq : keySeqs) { bind(function, keySeq); } } public void bind(T function, CharSequence keySeq) { if (keySeq != null) { if (function == null) { unbind(keySeq); } else { bind(this, keySeq, function, false); } } } public void unbind(CharSequence... keySeqs) { for (CharSequence keySeq : keySeqs) { unbind(keySeq); } } public void unbind(CharSequence keySeq) { if (keySeq != null) { unbind(this, keySeq); } } @SuppressWarnings("unchecked") private static T unbind(KeyMap map, CharSequence keySeq) { KeyMap prev = null; if (keySeq != null && keySeq.length() > 0) { for (int i = 0; i < keySeq.length() - 1; i++) { char c = keySeq.charAt(i); if (c > map.mapping.length) { return null; } if (!(map.mapping[c] instanceof KeyMap)) { return null; } prev = map; map = (KeyMap) map.mapping[c]; } char c = keySeq.charAt(keySeq.length() - 1); if (c > map.mapping.length) { return null; } if (map.mapping[c] instanceof KeyMap) { KeyMap sub = (KeyMap) map.mapping[c]; Object res = sub.anotherKey; sub.anotherKey = null; return (T) res; } else { Object res = map.mapping[c]; map.mapping[c] = null; int nb = 0; for (int i = 0; i < map.mapping.length; i++) { if (map.mapping[i] != null) { nb++; } } if (nb == 0 && prev != null) { prev.mapping[keySeq.charAt(keySeq.length() - 2)] = map.anotherKey; } return (T) res; } } return null; } @SuppressWarnings("unchecked") private static void bind(KeyMap map, CharSequence keySeq, T function, boolean onlyIfNotBound) { if (keySeq != null && keySeq.length() > 0) { for (int i = 0; i < keySeq.length(); i++) { char c = keySeq.charAt(i); if (c >= map.mapping.length) { return; } if (i < keySeq.length() - 1) { if (!(map.mapping[c] instanceof KeyMap)) { KeyMap m = new KeyMap<>(); m.anotherKey = (T) map.mapping[c]; map.mapping[c] = m; } map = (KeyMap) map.mapping[c]; } else { if (map.mapping[c] instanceof KeyMap) { ((KeyMap) map.mapping[c]).anotherKey = function; } else { Object op = map.mapping[c]; if (!onlyIfNotBound || op == null) { map.mapping[c] = function; } } } } } } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/000077500000000000000000000000001311544710100230265ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Binding.java000066400000000000000000000010341311544710100252410ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; /** * Marker interface for objects bound to key sequences. * * @see Macro * @see Reference * @see Widget * @see org.jline.keymap.KeyMap * * @author Guillaume Nodet */ public interface Binding { } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Buffer.java000066400000000000000000000023341311544710100251040ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; public interface Buffer { /* * Read access */ int cursor(); int atChar(int i); int length(); int currChar(); int prevChar(); int nextChar(); /* * Movement */ boolean cursor(int position); int move(int num); boolean up(); boolean down(); boolean moveXY(int dx, int dy); /* * Modification */ boolean clear(); boolean currChar(int c); void write(int c); void write(int c, boolean overTyping); void write(CharSequence str); void write(CharSequence str, boolean overTyping); boolean backspace(); int backspace(int num); boolean delete(); int delete(int num); /* * String */ String substring(int start); String substring(int start, int end); String upToCursor(); String toString(); /* * Copy */ Buffer copy(); void copyFrom(Buffer buffer); } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Candidate.java000066400000000000000000000065711311544710100255560ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; import java.util.Objects; /** * A completion candidate. * * @author Guillaume Nodet */ public class Candidate implements Comparable { private final String value; private final String displ; private final String group; private final String descr; private final String suffix; private final String key; private final boolean complete; /** * Simple constructor with only a single String as an argument. * * @param value the candidate */ public Candidate(String value) { this(value, value, null, null, null, null, true); } /** * Constructs a new Candidate. */ public Candidate(String value, String displ, String group, String descr, String suffix, String key, boolean complete) { Objects.requireNonNull(value); this.value = value; this.displ = displ; this.group = group; this.descr = descr; this.suffix = suffix; this.key = key; this.complete = complete; } /** * The value that will be used for the actual completion. * This string should not contain ANSI sequences. */ public String value() { return value; } /** * The string that will be displayed to the user. * This string may contain ANSI sequences. */ public String displ() { return displ; } /** * The group name for this candidate. * Candidates can be grouped together and this string is used * as a key for the group and displayed to the user. * * @see LineReader.Option#GROUP * @see LineReader.Option#AUTO_GROUP */ public String group() { return group; } /** * Description of this candidate, usually a small help message * to understand the meaning of this candidate. * This string may contain ANSI sequences. */ public String descr() { return descr; } /** * The suffix is added when this candidate is displayed. * However, if the next character entered does not match, * the suffix will be automatically removed. * This string should not contain ANSI sequences. * * @see LineReader.Option#AUTO_REMOVE_SLASH * @see LineReader#REMOVE_SUFFIX_CHARS */ public String suffix() { return suffix; } /** * Candidates which have the same key will be merged together. * For example, if a command has multiple aliases, they can be merged * if they are using the same key. */ public String key() { return key; } /** * Boolean indicating whether this candidate is complete or * if the completer may further expand the candidate value * after this candidate has been selected. * This can be the case when completing folders for example. * If the candidate is complete and is selected, a space * separator will be added. */ public boolean complete() { return complete; } @Override public int compareTo(Candidate o) { return value.compareTo(o.value); } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Completer.java000066400000000000000000000026361311544710100256320ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; import java.util.List; /** * A completer is the mechanism by which tab-completion candidates will be resolved. * * @author Marc Prud'hommeaux * @author Jason Dillon * @author Guillaume Nodet * @since 2.3 */ public interface Completer { /** * Populates candidates with a list of possible completions for the buffer. * * The list of candidates will be sorted and filtered by the LineReader, so that * the list of candidates displayed to the user will usually be smaller than * the list given by the completer. Thus it is not necessary for the completer * to do any matching based on the current buffer. On the contrary, in order * for the typo matcher to work, all possible candidates for the word being * completed should be returned. * * @param line The parsed command line * @param candidates The {@link List} of candidates to populate */ void complete(LineReader reader, ParsedLine line, List candidates); } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/EOFError.java000066400000000000000000000024211311544710100253130ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jline.reader; public class EOFError extends SyntaxError { private static final long serialVersionUID = 1L; private final String missing; public EOFError(int line, int column, String message) { this(line, column, message, null); } public EOFError(int line, int column, String message, String missing) { super(line, column, message); this.missing = missing; } public String getMissing() { return missing; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/EndOfFileException.java000066400000000000000000000020071311544710100273420ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; /** * This exception is thrown by {@link LineReader#readLine} when * user the user types ctrl-D). */ public class EndOfFileException extends RuntimeException { private static final long serialVersionUID = 528485360925144689L; public EndOfFileException() { } public EndOfFileException(String message) { super(message); } public EndOfFileException(String message, Throwable cause) { super(message, cause); } public EndOfFileException(Throwable cause) { super(cause); } public EndOfFileException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Expander.java000066400000000000000000000006451311544710100254440ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; public interface Expander { String expandHistory(History history, String line); String expandVar(String word); } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Highlighter.java000066400000000000000000000006671311544710100261400ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; import org.jline.utils.AttributedString; public interface Highlighter { AttributedString highlight(LineReader reader, String buffer); } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/History.java000066400000000000000000000052561311544710100253420ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; import java.io.IOException; import java.time.Instant; import java.util.ListIterator; /** * Console history. * * @author Marc Prud'hommeaux * @author Jason Dillon * @since 2.3 */ public interface History extends Iterable { /** * Initialize the history for the given reader. */ void attach(LineReader reader); /** * Load history. */ void load() throws IOException; /** * Save history. */ void save() throws IOException; /** * Purge history. */ void purge() throws IOException; int size(); default boolean isEmpty() { return size() == 0; } int index(); int first(); int last(); String get(int index); default void add(String line) { add(Instant.now(), line); } void add(Instant time, String line); // // Entries // interface Entry { int index(); Instant time(); String line(); } ListIterator iterator(int index); default ListIterator iterator() { return iterator(first()); } // // Navigation // /** * Return the content of the current buffer. */ String current(); /** * Move the pointer to the previous element in the buffer. * * @return true if we successfully went to the previous element */ boolean previous(); /** * Move the pointer to the next element in the buffer. * * @return true if we successfully went to the next element */ boolean next(); /** * Moves the history index to the first entry. * * @return Return false if there are no iterator in the history or if the * history is already at the beginning. */ boolean moveToFirst(); /** * This moves the history to the last entry. This entry is one position * before the moveToEnd() position. * * @return Returns false if there were no history iterator or the history * index was already at the last entry. */ boolean moveToLast(); /** * Move to the specified index in the history */ boolean moveTo(int index); /** * Move to the end of the history buffer. This will be a blank entry, after * all of the other iterator. */ void moveToEnd(); } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/LineReader.java000066400000000000000000000471741311544710100257200ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; import java.io.InputStream; import java.util.Map; import java.util.function.IntConsumer; import org.jline.keymap.KeyMap; import org.jline.terminal.MouseEvent; import org.jline.terminal.Terminal; /** Read lines from the console, with input editing. * *

Prompt strings

*

* It is traditional for an interactive console-based program * to print a short prompt string to signal that the user is expected * to type a command. JLine supports 3 kinds of prompt string: *

    *
  • The normal prompt at the start (left) of the initial line of a command. *
  • An optional right prompt at the right border of the initial line. *
  • A start (left) prompt for continuation lines. I.e. the lines * after the first line of a multi-line command. *
*

* All of these are specified with prompt templates, * which are similar to {@code printf} format strings, * using the character {@code '%'} to indicate special functionality. *

* The pattern may include ANSI escapes. * It may include these template markers: *

*
{@code %N}
*
A line number. This is the sum of {@code getLineNumber()} * and a counter starting with 1 for the first continuation line. *
{@code %M}
*
A short word explaining what is "missing". This is supplied from * the {@link EOFError#getMissing()} method, if provided. * Defaults to an empty string. *
*
{@code %}n{@code P}c
*
Insert padding at this possion, repeating the following * character c as needed to bring the total prompt * column width as specified by the digits n. *
*
{@code %P}c
*
As before, but use width from the initial prompt. *
*
{@code %%}
*
A literal {@code '%'}. *
*
%{
*
%}
* Text between a %{...%} pair is printed as * part of a prompt, but not interpreted by JLine * (except that {@code '%'}-escapes are processed). The text is assumed * to take zero columns (not move the cursor). If it changes the style, * you're responsible for changing it back. Standard ANSI escape sequences * do not need to be within a %{...%} pair * (though can be) since JLine knows how to deal with them. However, * these delimiters are needed for unusual non-standard escape sequences. *
*
*
*/ public interface LineReader { // // Widget names // String CALLBACK_INIT = "callback-init"; String CALLBACK_FINISH = "callback-finish"; String CALLBACK_KEYMAP = "callback-keymap"; String ACCEPT_LINE = "accept-line"; String ARGUMENT_BASE = "argument-base"; String BACKWARD_CHAR = "backward-char"; String BACKWARD_DELETE_CHAR = "backward-delete-char"; String BACKWARD_DELETE_WORD = "backward-delete-word"; String BACKWARD_KILL_LINE = "backward-kill-line"; String BACKWARD_KILL_WORD = "backward-kill-word"; String BACKWARD_WORD = "backward-word"; String BEEP = "beep"; String BEGINNING_OF_BUFFER_OR_HISTORY = "beginning-of-buffer-or-history"; String BEGINNING_OF_HISTORY = "beginning-of-history"; String BEGINNING_OF_LINE = "beginning-of-line"; String BEGINNING_OF_LINE_HIST = "beginning-of-line-hist"; String CAPITALIZE_WORD = "capitalize-word"; String CHARACTER_SEARCH = "character-search"; String CHARACTER_SEARCH_BACKWARD = "character-search-backward"; String CLEAR = "clear"; String CLEAR_SCREEN = "clear-screen"; String COMPLETE_PREFIX = "complete-prefix"; String COMPLETE_WORD = "complete-word"; String COPY_PREV_WORD = "copy-prev-word"; String COPY_REGION_AS_KILL = "copy-region-as-kill"; String DELETE_CHAR = "delete-char"; String DELETE_CHAR_OR_LIST = "delete-char-or-list"; String DELETE_WORD = "delete-word"; String DIGIT_ARGUMENT = "digit-argument"; String DO_LOWERCASE_VERSION = "do-lowercase-version"; String DOWN_CASE_WORD = "down-case-word"; String DOWN_HISTORY = "down-history"; String DOWN_LINE = "down-line"; String DOWN_LINE_OR_HISTORY = "down-line-or-history"; String DOWN_LINE_OR_SEARCH = "down-line-or-search"; String EMACS_BACKWARD_WORD = "emacs-backward-word"; String EMACS_EDITING_MODE = "emacs-editing-mode"; String EMACS_FORWARD_WORD = "emacs-forward-word"; String END_OF_BUFFER_OR_HISTORY = "end-of-buffer-or-history"; String END_OF_HISTORY = "end-of-history"; String END_OF_LINE = "end-of-line"; String END_OF_LINE_HIST = "end-of-line-hist"; String EXCHANGE_POINT_AND_MARK = "exchange-point-and-mark"; String EXECUTE_NAMED_CMD = "execute-named-cmd"; String EXPAND_HISTORY = "expand-history"; String EXPAND_OR_COMPLETE = "expand-or-complete"; String EXPAND_OR_COMPLETE_PREFIX = "expand-or-complete-prefix"; String EXPAND_WORD = "expand-word"; String FRESH_LINE = "fresh-line"; String FORWARD_CHAR = "forward-char"; String FORWARD_WORD = "forward-word"; String HISTORY_BEGINNING_SEARCH_BACKWARD = "history-beginning-search-backward"; String HISTORY_BEGINNING_SEARCH_FORWARD = "history-beginning-search-forward"; String HISTORY_INCREMENTAL_PATTERN_SEARCH_BACKWARD = "history-incremental-pattern-search-backward"; String HISTORY_INCREMENTAL_PATTERN_SEARCH_FORWARD = "history-incremental-pattern-search-forward"; String HISTORY_INCREMENTAL_SEARCH_BACKWARD = "history-incremental-search-backward"; String HISTORY_INCREMENTAL_SEARCH_FORWARD = "history-incremental-search-forward"; String HISTORY_SEARCH_BACKWARD = "history-search-backward"; String HISTORY_SEARCH_FORWARD = "history-search-forward"; String INSERT_CLOSE_CURLY = "insert-close-curly"; String INSERT_CLOSE_PAREN = "insert-close-paren"; String INSERT_CLOSE_SQUARE = "insert-close-square"; String INFER_NEXT_HISTORY = "infer-next-history"; String INSERT_COMMENT = "insert-comment"; String INSERT_LAST_WORD = "insert-last-word"; String KILL_BUFFER = "kill-buffer"; String KILL_LINE = "kill-line"; String KILL_REGION = "kill-region"; String KILL_WHOLE_LINE = "kill-whole-line"; String KILL_WORD = "kill-word"; String LIST_CHOICES = "list-choices"; String LIST_EXPAND = "list-expand"; String MAGIC_SPACE = "magic-space"; String MENU_EXPAND_OR_COMPLETE = "menu-expand-or-complete"; String MENU_COMPLETE = "menu-complete"; String MENU_SELECT = "menu-select"; String NEG_ARGUMENT = "neg-argument"; String OVERWRITE_MODE = "overwrite-mode"; String PUT_REPLACE_SELECTION = "put-replace-selection"; String QUOTED_INSERT = "quoted-insert"; String READ_COMMAND = "read-command"; String RECURSIVE_EDIT = "recursive-edit"; String REDISPLAY = "redisplay"; String REDRAW_LINE = "redraw-line"; String REDO = "redo"; String REVERSE_MENU_COMPLETE = "reverse-menu-complete"; String SELF_INSERT = "self-insert"; String SELF_INSERT_UNMETA = "self-insert-unmeta"; String SEND_BREAK = "abort"; String SET_LOCAL_HISTORY = "set-local-history"; String SET_MARK_COMMAND = "set-mark-command"; String SPELL_WORD = "spell-word"; String SPLIT_UNDO = "split-undo"; String TRANSPOSE_CHARS = "transpose-chars"; String TRANSPOSE_WORDS = "transpose-words"; String UNDEFINED_KEY = "undefined-key"; String UNDO = "undo"; String UNIVERSAL_ARGUMENT = "universal-argument"; String UP_CASE_WORD = "up-case-word"; String UP_HISTORY = "up-history"; String UP_LINE = "up-line"; String UP_LINE_OR_HISTORY = "up-line-or-history"; String UP_LINE_OR_SEARCH = "up-line-or-search"; String VI_ADD_EOL = "vi-add-eol"; String VI_ADD_NEXT = "vi-add-next"; String VI_BACKWARD_BLANK_WORD = "vi-backward-blank-word"; String VI_BACKWARD_BLANK_WORD_END = "vi-backward-blank-word-end"; String VI_BACKWARD_CHAR = "vi-backward-char"; String VI_BACKWARD_DELETE_CHAR = "vi-backward-delete-char"; String VI_BACKWARD_KILL_WORD = "vi-backward-kill-word"; String VI_BACKWARD_WORD = "vi-backward-word"; String VI_BACKWARD_WORD_END = "vi-backward-word-end"; String VI_BEGINNING_OF_LINE = "vi-beginning-of-line"; String VI_CHANGE = "vi-change-to"; String VI_CHANGE_EOL = "vi-change-eol"; String VI_CHANGE_WHOLE_LINE = "vi-change-whole-line"; String VI_CMD_MODE = "vi-cmd-mode"; String VI_DELETE = "vi-delete"; String VI_DELETE_CHAR = "vi-delete-char"; String VI_DIGIT_OR_BEGINNING_OF_LINE = "vi-digit-or-beginning-of-line"; String VI_DOWN_LINE_OR_HISTORY = "vi-down-line-or-history"; String VI_END_OF_LINE = "vi-end-of-line"; String VI_FETCH_HISTORY = "vi-fetch-history"; String VI_FIND_NEXT_CHAR = "vi-find-next-char"; String VI_FIND_NEXT_CHAR_SKIP = "vi-find-next-char-skip"; String VI_FIND_PREV_CHAR = "vi-find-prev-char"; String VI_FIND_PREV_CHAR_SKIP = "vi-find-prev-char-skip"; String VI_FIRST_NON_BLANK = "vi-first-non-blank"; String VI_FORWARD_BLANK_WORD = "vi-forward-blank-word"; String VI_FORWARD_BLANK_WORD_END = "vi-forward-blank-word-end"; String VI_FORWARD_CHAR = "vi-forward-char"; String VI_FORWARD_WORD = "vi-forward-word"; String VI_FORWARD_WORD_END = "vi-forward-word-end"; String VI_GOTO_COLUMN = "vi-goto-column"; String VI_HISTORY_SEARCH_BACKWARD = "vi-history-search-backward"; String VI_HISTORY_SEARCH_FORWARD = "vi-history-search-forward"; String VI_INSERT = "vi-insert"; String VI_INSERT_BOL = "vi-insert-bol"; String VI_INSERT_COMMENT = "vi-insert-comment"; String VI_JOIN = "vi-join"; String VI_KILL_EOL = "vi-kill-eol"; String VI_KILL_LINE = "vi-kill-line"; String VI_MATCH_BRACKET = "vi-match-bracket"; String VI_OPEN_LINE_ABOVE = "vi-open-line-above"; String VI_OPEN_LINE_BELOW = "vi-open-line-below"; String VI_OPER_SWAP_CASE = "vi-oper-swap-case"; String VI_PUT_AFTER = "vi-put-after"; String VI_PUT_BEFORE = "vi-put-before"; String VI_QUOTED_INSERT = "vi-quoted-insert"; String VI_REPEAT_CHANGE = "vi-repeat-change"; String VI_REPEAT_FIND = "vi-repeat-find"; String VI_REPEAT_SEARCH = "vi-repeat-search"; String VI_REPLACE = "vi-replace"; String VI_REPLACE_CHARS = "vi-replace-chars"; String VI_REV_REPEAT_FIND = "vi-rev-repeat-find"; String VI_REV_REPEAT_SEARCH = "vi-rev-repeat-search"; String VI_SET_BUFFER = "vi-set-buffer"; String VI_SUBSTITUTE = "vi-substitute"; String VI_SWAP_CASE = "vi-swap-case"; String VI_UNDO_CHANGE = "vi-undo-change"; String VI_UP_LINE_OR_HISTORY = "vi-up-line-or-history"; String VI_YANK = "vi-yank"; String VI_YANK_EOL = "vi-yank-eol"; String VI_YANK_WHOLE_LINE = "vi-yank-whole-line"; String VISUAL_LINE_MODE = "visual-line-mode"; String VISUAL_MODE = "visual-mode"; String WHAT_CURSOR_POSITION = "what-cursor-position"; String YANK = "yank"; String YANK_POP = "yank-pop"; String MOUSE = "mouse"; // // KeyMap names // String VICMD = "vicmd"; String VIINS = "viins"; String VIOPP = "viopp"; String VISUAL = "visual"; String MAIN = "main"; String EMACS = "emacs"; String SAFE = ".safe"; String MENU = "menu"; // // Variable names // String BIND_TTY_SPECIAL_CHARS = "bind-tty-special-chars"; String COMMENT_BEGIN = "comment-begin"; String BELL_STYLE = "bell-style"; String PREFER_VISIBLE_BELL = "prefer-visible-bell"; String LIST_MAX = "list-max"; String DISABLE_HISTORY = "disable-history"; String DISABLE_COMPLETION = "disable-completion"; String EDITING_MODE = "editing-mode"; String KEYMAP = "keymap"; String BLINK_MATCHING_PAREN = "blink-matching-paren"; String WORDCHARS = "WORDCHARS"; String REMOVE_SUFFIX_CHARS = "REMOVE_SUFFIX_CHARS"; String SEARCH_TERMINATORS = "search-terminators"; String ERRORS = "errors"; /** * Set the template for prompts for secondary (continuation) lines. * This is a prompt template as described in the class header. */ String SECONDARY_PROMPT_PATTERN = "secondary-prompt-pattern"; /** * When in multiline edit mode, this variable can be used * to offset the line number displayed. */ String LINE_OFFSET = "line-offset"; /** * Timeout for ambiguous key sequences. * If the key sequence is ambiguous, i.e. there is a matching * sequence but the sequence is also a prefix for other bindings, * the next key press will be waited for a specified amount of * time. If the timeout elapses, the matched sequence will be * used. */ String AMBIGUOUS_BINDING = "ambiguous-binding"; /** * Columns separated list of patterns that will not be saved in history. */ String HISTORY_IGNORE = "history-ignore"; /** * File system history path. */ String HISTORY_FILE = "history-file"; /** * Number of history items to keep in memory. */ String HISTORY_SIZE = "history-size"; /** * Number of history items to keep in the history file. */ String HISTORY_FILE_SIZE = "history-file-size"; Map> defaultKeyMaps(); enum Option { COMPLETE_IN_WORD, DISABLE_EVENT_EXPANSION, HISTORY_VERIFY, HISTORY_IGNORE_SPACE(true), HISTORY_IGNORE_DUPS(true), HISTORY_REDUCE_BLANKS(true), HISTORY_BEEP(true), HISTORY_INCREMENTAL(true), AUTO_GROUP(true), AUTO_MENU(true), AUTO_LIST(true), RECOGNIZE_EXACT, GROUP, CASE_INSENSITIVE, LIST_AMBIGUOUS, LIST_PACKED, LIST_ROWS_FIRST, GLOB_COMPLETE, MENU_COMPLETE, /** if set and not at start of line before prompt, move to new line */ AUTO_FRESH_LINE, /** After writing into the rightmost column, do we immediately * move to the next line (the default)? Or do we wait until * the next character. * If set, an input line that is exactly {@code N*columns} wide will * use {@code N} screen lines; otherwise it will use {@code N+1} lines. * When the cursor position is the right margin of the last line * (i.e. after {@code N*columns} normal characters), if this option * it set, the cursor will be remain on the last line (line {@code N-1}, * zero-origin); if unset the cursor will be on the empty next line. * Regardless, for all except the last screen line if the cursor is at * the right margin, it will be shown at the start of the next line. */ DELAY_LINE_WRAP, AUTO_PARAM_SLASH(true), AUTO_REMOVE_SLASH(true), INSERT_TAB(true), MOUSE, DISABLE_HIGHLIGHTER; private final boolean def; Option() { this(false); } Option(boolean def) { this.def = def; } public boolean isDef() { return def; } } enum RegionType { NONE, CHAR, LINE } /** * Read the next line and return the contents of the buffer. * * Equivalent to readLine(null, null, null) */ String readLine() throws UserInterruptException, EndOfFileException; /** * Read the next line with the specified character mask. If null, then * characters will be echoed. If 0, then no characters will be echoed. * * Equivalent to readLine(null, mask, null) */ String readLine(Character mask) throws UserInterruptException, EndOfFileException; /** * Read the next line with the specified prompt. * If null, then the default prompt will be used. * * Equivalent to readLine(prompt, null, null) */ String readLine(String prompt) throws UserInterruptException, EndOfFileException; /** * Read a line from the in {@link InputStream}, and return the line * (without any trailing newlines). * * Equivalent to readLine(prompt, mask, null) */ String readLine(String prompt, Character mask) throws UserInterruptException, EndOfFileException; /** * Read a line from the in {@link InputStream}, and return the line * (without any trailing newlines). * * @param prompt The prompt to issue to the terminal, may be null. * This is a template, with optional {@code '%'} escapes, as * described in the class header. * @param mask The character mask, may be null. * @param buffer The default value presented to the user to edit, may be null. * @return A line that is read from the terminal, can never be null. * * Equivalent to readLine(prompt, null, mask, buffer) */ String readLine(String prompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException; /** * Read a line from the in {@link InputStream}, and return the line * (without any trailing newlines). * * @param prompt The prompt to issue to the terminal, may be null. * This is a template, with optional {@code '%'} escapes, as * described in the class header. * @param rightPrompt The right prompt * This is a template, with optional {@code '%'} escapes, as * described in the class header. * @param mask The character mask, may be null. * @param buffer The default value presented to the user to edit, may be null. * @return A line that is read from the terminal, can never be null. * * @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example) * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example) * @throws java.io.IOError in case of other i/o errors */ String readLine(String prompt, String rightPrompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException; void callWidget(String name); Map getVariables(); Object getVariable(String name); void setVariable(String name, Object value); boolean isSet(Option option); void setOpt(Option option); void unsetOpt(Option option); Terminal getTerminal(); Map getWidgets(); Map getBuiltinWidgets(); Buffer getBuffer(); /** * Push back a key sequence that will be later consumed by the line reader. * This method can be used after reading the cursor position using * {@link Terminal#getCursorPosition(IntConsumer)}. * * @param macro the key sequence to push back * @see Terminal#getCursorPosition(IntConsumer) * @see #readMouseEvent() */ void runMacro(String macro); /** * Read a mouse event when the {@link org.jline.utils.InfoCmp.Capability#key_mouse} sequence * has just been read on the input stream. * Compared to {@link Terminal#readMouseEvent()}, this method takes into account keys * that have been pushed back using {@link #runMacro(String)}. * * @return the mouse event * @see #runMacro(String) * @see Terminal#getCursorPosition(IntConsumer) */ MouseEvent readMouseEvent(); History getHistory(); Parser getParser(); Highlighter getHighlighter(); Expander getExpander(); Map> getKeyMaps(); String getKeyMap(); boolean setKeyMap(String name); KeyMap getKeys(); ParsedLine getParsedLine(); String getSearchTerm(); RegionType getRegionActive(); int getRegionMark(); } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/LineReaderBuilder.java000066400000000000000000000063351311544710100272210ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; import java.io.IOError; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.jline.reader.impl.LineReaderImpl; import org.jline.reader.impl.history.DefaultHistory; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; public final class LineReaderBuilder { public static LineReaderBuilder builder() { return new LineReaderBuilder(); } Terminal terminal; String appName; Map variables; History history; Completer completer; History memoryHistory; Highlighter highlighter; Parser parser; Expander expander; private LineReaderBuilder() { } public LineReaderBuilder terminal(Terminal terminal) { this.terminal = terminal; return this; } public LineReaderBuilder appName(String appName) { this.appName = appName; return this; } public LineReaderBuilder variables(Map variables) { Map old = this.variables; this.variables = variables; if (old != null) { this.variables.putAll(old); } return this; } public LineReaderBuilder variable(String name, Object value) { if (variables == null) { variables = new HashMap<>(); } this.variables.put(name, value); return this; } public LineReaderBuilder history(History history) { this.history = history; return this; } public LineReaderBuilder completer(Completer completer) { this.completer = completer; return this; } public LineReaderBuilder highlighter(Highlighter highlighter) { this.highlighter = highlighter; return this; } public LineReaderBuilder parser(Parser parser) { this.parser = parser; return this; } public LineReaderBuilder expander(Expander expander) { this.expander = expander; return this; } public LineReader build() { Terminal terminal = this.terminal; if (terminal == null) { try { terminal = TerminalBuilder.terminal(); } catch (IOException e) { throw new IOError(e); } } LineReaderImpl reader = new LineReaderImpl(terminal, appName, variables); if (history != null) { reader.setHistory(history); } else { if (memoryHistory == null) { memoryHistory = new DefaultHistory(); } reader.setHistory(memoryHistory); } if (completer != null) { reader.setCompleter(completer); } if (highlighter != null) { reader.setHighlighter(highlighter); } if (parser != null) { reader.setParser(parser); } if (expander != null) { reader.setExpander(expander); } return reader; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Macro.java000066400000000000000000000016741311544710100247420ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; public class Macro implements Binding { private final String sequence; public Macro(String sequence) { this.sequence = sequence; } public String getSequence() { return sequence; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Macro macro = (Macro) o; return sequence.equals(macro.sequence); } @Override public int hashCode() { return sequence.hashCode(); } @Override public String toString() { return "Macro[" + sequence + ']'; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/ParsedLine.java000066400000000000000000000017041311544710100257210ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; import java.util.List; public interface ParsedLine { /** * The current word being completed. * If the cursor is after the last word, an empty string is returned. * * @return the word being completed or an empty string */ String word(); /** * The cursor position within the current word */ int wordCursor(); /** * The index of the current word in the list of words */ int wordIndex(); /** * The list of words */ List words(); /** * The unparsed line */ String line(); /** * The cursor position within the line */ int cursor(); } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Parser.java000066400000000000000000000021471311544710100251310ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; public interface Parser { ParsedLine parse(String line, int cursor, ParseContext context) throws SyntaxError; default ParsedLine parse(String line, int cursor) throws SyntaxError { return parse(line, cursor, ParseContext.UNSPECIFIED); } enum ParseContext { UNSPECIFIED, /** Try a real "final" parse. * May throw EOFError in which case we have incomplete input. */ ACCEPT_LINE, /** Parse to find completions (typically after a Tab). * We should tolerate and ignore errors. */ COMPLETE, /** Called when we need to update the secondary prompts. * Specifically, when we need the 'missing' field from EOFError, * which is used by a "%M" in a prompt pattern. */ SECONDARY_PROMPT } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Reference.java000066400000000000000000000017171311544710100255750ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; /** * A reference to a {@link Widget}. */ public class Reference implements Binding { private final String name; public Reference(String name) { this.name = name; } public String name() { return name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Reference func = (Reference) o; return name.equals(func.name); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return "Reference[" + name + ']'; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/SyntaxError.java000066400000000000000000000023541311544710100261750ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jline.reader; public class SyntaxError extends RuntimeException { private static final long serialVersionUID = 1L; private final int line; private final int column; public SyntaxError(int line, int column, String message) { super(message); this.line = line; this.column = column; } public int column() { return column; } public int line() { return line; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/UserInterruptException.java000066400000000000000000000017401311544710100304050ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; /** * This exception is thrown by {@link LineReader#readLine} when * user interrupt handling is enabled and the user types the * interrupt character (ctrl-C). The partially entered line is * available via the {@link #getPartialLine()} method. */ public class UserInterruptException extends RuntimeException { private static final long serialVersionUID = 6172232572140736750L; private final String partialLine; public UserInterruptException(String partialLine) { this.partialLine = partialLine; } /** * @return the partially entered line when ctrl-C was pressed */ public String getPartialLine() { return partialLine; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/Widget.java000066400000000000000000000006141311544710100251150ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader; /** * */ @FunctionalInterface public interface Widget extends Binding { boolean apply(); } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/000077500000000000000000000000001311544710100237675ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/BufferImpl.java000066400000000000000000000214061311544710100266700ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.util.Objects; import org.jline.reader.Buffer; /** * A holder for a {@link StringBuilder} that also contains the current cursor position. * * @author Marc Prud'hommeaux * @author Jason Dillon * @since 2.0 */ public class BufferImpl implements Buffer { private int cursor = 0; private int cursorCol = -1; private int[] buffer; private int g0; private int g1; public BufferImpl() { this(64); } public BufferImpl(int size) { buffer = new int[size]; g0 = 0; g1 = buffer.length; } public BufferImpl copy () { BufferImpl that = new BufferImpl(); that.copyFrom(this); return that; } public int cursor() { return cursor; } public int length() { return buffer.length - (g1 - g0); } public boolean currChar(int ch) { if (cursor == length()) { return false; } else { buffer[adjust(cursor)] = ch; return true; } } public int currChar() { if (cursor == length()) { return 0; } else { return atChar(cursor); } } public int prevChar() { if (cursor <= 0) { return 0; } return atChar(cursor - 1); } public int nextChar() { if (cursor >= length() - 1) { return 0; } return atChar(cursor + 1); } public int atChar(int i) { if (i < 0 || i >= length()) { return 0; } return buffer[adjust(i)]; } private int adjust(int i) { return (i >= g0) ? i + g1 - g0 : i; } /** * Write the specific character into the buffer, setting the cursor position * ahead one. * * @param c the character to insert */ public void write(int c) { write(new int[] { c }); } /** * Write the specific character into the buffer, setting the cursor position * ahead one. The text may overwrite or insert based on the current setting * of {@code overTyping}. * * @param c the character to insert */ public void write(int c, boolean overTyping) { if (overTyping) { delete(1); } write(new int[] { c }); } /** * Insert the specified chars into the buffer, setting the cursor to the end of the insertion point. */ public void write(CharSequence str) { Objects.requireNonNull(str); write(str.codePoints().toArray()); } public void write(CharSequence str, boolean overTyping) { Objects.requireNonNull(str); int[] ucps = str.codePoints().toArray(); if (overTyping) { delete(ucps.length); } write(ucps); } private void write(int[] ucps) { moveGapToCursor(); int len = length() + ucps.length; int sz = buffer.length; if (sz < len) { while (sz < len) { sz *= 2; } int[] nb = new int[sz]; System.arraycopy(buffer, 0, nb, 0, g0); System.arraycopy(buffer, g1, nb, g1 + sz - buffer.length, buffer.length - g1); g1 += sz - buffer.length; buffer = nb; } System.arraycopy(ucps, 0, buffer, cursor, ucps.length); g0 += ucps.length; cursor += ucps.length; cursorCol = -1; } public boolean clear() { if (length() == 0) { return false; } g0 = 0; g1 = buffer.length; cursor = 0; cursorCol = -1; return true; } public String substring(int start) { return substring(start, length()); } public String substring(int start, int end) { if (start >= end || start < 0 || end > length()) { return ""; } if (end <= g0) { return new String(buffer, start, end - start); } else if (start > g0) { return new String(buffer, g1 - g0 + start, end - start); } else { int[] b = buffer.clone(); System.arraycopy(b, g1, b, g0, b.length - g1); return new String(b, start, end - start); } } public String upToCursor() { return substring(0, cursor); } /** * Move the cursor position to the specified absolute index. */ public boolean cursor(int position) { if (position == cursor) { return true; } return move(position - cursor) != 0; } /** * Move the cursor where characters. * * @param num If less than 0, move abs(where) to the left, otherwise move where to the right. * @return The number of spaces we moved */ public int move(final int num) { int where = num; if ((cursor == 0) && (where <= 0)) { return 0; } if ((cursor == length()) && (where >= 0)) { return 0; } if ((cursor + where) < 0) { where = -cursor; } else if ((cursor + where) > length()) { where = length() - cursor; } cursor += where; cursorCol = -1; return where; } public boolean up() { int col = getCursorCol(); int pnl = cursor - 1; while (pnl >= 0 && atChar(pnl) != '\n') { pnl--; } if (pnl < 0) { return false; } int ppnl = pnl - 1; while (ppnl >= 0 && atChar(ppnl) != '\n') { ppnl--; } cursor = Math.min(ppnl + col + 1, pnl); return true; } public boolean down() { int col = getCursorCol(); int nnl = cursor; while (nnl < length() && atChar(nnl) != '\n') { nnl++; } if (nnl >= length()) { return false; } int nnnl = nnl + 1; while (nnnl < length() && atChar(nnnl) != '\n') { nnnl++; } cursor = Math.min(nnl + col + 1, nnnl); return true; } public boolean moveXY(int dx, int dy) { int col = 0; while (prevChar() != '\n' && move(-1) == -1) { col++; } cursorCol = 0; while (dy < 0) { up(); dy++; } while (dy > 0) { down(); dy--; } col = Math.max(col + dx, 0); for (int i = 0; i < col; i++) { if (move(1) != 1 || currChar() == '\n') { break; } } cursorCol = col; return true; } private int getCursorCol() { if (cursorCol < 0) { cursorCol = 0; int pnl = cursor - 1; while (pnl >= 0 && atChar(pnl) != '\n') { pnl--; } cursorCol = cursor - pnl - 1; } return cursorCol; } /** * Issue num backspaces. * * @return the number of characters backed up */ public int backspace(final int num) { int count = Math.max(Math.min(cursor, num), 0); moveGapToCursor(); cursor -= count; g0 -= count; cursorCol = -1; return count; } /** * Issue a backspace. * * @return true if successful */ public boolean backspace() { return backspace(1) == 1; } public int delete(int num) { int count = Math.max(Math.min(length() - cursor, num), 0); moveGapToCursor(); g1 += count; cursorCol = -1; return count; } public boolean delete() { return delete(1) == 1; } @Override public String toString() { return substring(0, length()); } public void copyFrom(Buffer buf) { if (!(buf instanceof BufferImpl)) { throw new IllegalStateException(); } BufferImpl that = (BufferImpl) buf; this.g0 = that.g0; this.g1 = that.g1; this.buffer = that.buffer.clone(); this.cursor = that.cursor; this.cursorCol = that.cursorCol; } private void moveGapToCursor() { if (cursor < g0) { int l = g0 - cursor; System.arraycopy(buffer, cursor, buffer, g1 - l, l); g0 -= l; g1 -= l; } else if (cursor > g0) { int l = cursor - g0; System.arraycopy(buffer, g1, buffer, g0, l); g0 += l; g1 += l; } } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/DefaultExpander.java000066400000000000000000000217601311544710100277130ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.util.ListIterator; import org.jline.reader.Expander; import org.jline.reader.History; import org.jline.reader.History.Entry; public class DefaultExpander implements Expander { /** * Expand event designator such as !!, !#, !3, etc... * See http://www.gnu.org/software/bash/manual/html_node/Event-Designators.html */ @SuppressWarnings("fallthrough") @Override public String expandHistory(History history, String line) { boolean inQuote = false; StringBuilder sb = new StringBuilder(); boolean escaped = false; int unicode = 0; for (int i = 0; i < line.length(); i++) { char c = line.charAt(i); if (unicode > 0) { escaped = (--unicode >= 0); sb.append(c); } else if (escaped) { if (c == 'u') { unicode = 4; } else { escaped = false; } sb.append(c); } else if (c == '\'') { inQuote = !inQuote; sb.append(c); } else if (inQuote) { sb.append(c); } else { switch (c) { case '\\': // any '\!' should be considered an expansion escape, so skip expansion and strip the escape character // a leading '\^' should be considered an expansion escape, so skip expansion and strip the escape character // otherwise, add the escape escaped = true; sb.append(c); break; case '!': if (i + 1 < line.length()) { c = line.charAt(++i); boolean neg = false; String rep = null; int i1, idx; switch (c) { case '!': if (history.size() == 0) { throw new IllegalArgumentException("!!: event not found"); } rep = history.get(history.index() - 1); break; case '#': sb.append(sb.toString()); break; case '?': i1 = line.indexOf('?', i + 1); if (i1 < 0) { i1 = line.length(); } String sc = line.substring(i + 1, i1); i = i1; idx = searchBackwards(history, sc, history.index(), false); if (idx < 0) { throw new IllegalArgumentException("!?" + sc + ": event not found"); } else { rep = history.get(idx); } break; case '$': if (history.size() == 0) { throw new IllegalArgumentException("!$: event not found"); } String previous = history.get(history.index() - 1).trim(); int lastSpace = previous.lastIndexOf(' '); if (lastSpace != -1) { rep = previous.substring(lastSpace + 1); } else { rep = previous; } break; case ' ': case '\t': sb.append('!'); sb.append(c); break; case '-': neg = true; i++; // fall through case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i1 = i; for (; i < line.length(); i++) { c = line.charAt(i); if (c < '0' || c > '9') { break; } } try { idx = Integer.parseInt(line.substring(i1, i)); } catch (NumberFormatException e) { throw new IllegalArgumentException((neg ? "!-" : "!") + line.substring(i1, i) + ": event not found"); } if (neg && idx > 0 && idx <= history.size()) { rep = history.get(history.index() - idx); } else if (!neg && idx > history.index() - history.size() && idx <= history.index()) { rep = history.get(idx - 1); } else { throw new IllegalArgumentException((neg ? "!-" : "!") + line.substring(i1, i) + ": event not found"); } break; default: String ss = line.substring(i); i = line.length(); idx = searchBackwards(history, ss, history.index(), true); if (idx < 0) { throw new IllegalArgumentException("!" + ss + ": event not found"); } else { rep = history.get(idx); } break; } if (rep != null) { sb.append(rep); } } else { sb.append(c); } break; case '^': if (i == 0) { int i1 = line.indexOf('^', i + 1); int i2 = line.indexOf('^', i1 + 1); if (i2 < 0) { i2 = line.length(); } if (i1 > 0 && i2 > 0) { String s1 = line.substring(i + 1, i1); String s2 = line.substring(i1 + 1, i2); String s = history.get(history.index() - 1).replace(s1, s2); sb.append(s); i = i2 + 1; break; } } sb.append(c); break; default: sb.append(c); break; } } } return sb.toString(); } @Override public String expandVar(String word) { return word; } protected int searchBackwards(History history, String searchTerm, int startIndex, boolean startsWith) { ListIterator it = history.iterator(startIndex); while (it.hasPrevious()) { History.Entry e = it.previous(); if (startsWith) { if (e.line().startsWith(searchTerm)) { return e.index(); } } else { if (e.line().contains(searchTerm)) { return e.index(); } } } return -1; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/DefaultHighlighter.java000066400000000000000000000057111311544710100304010ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import org.jline.reader.LineReader; import org.jline.reader.LineReader.RegionType; import org.jline.reader.Highlighter; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; import org.jline.utils.WCWidth; public class DefaultHighlighter implements Highlighter { @Override public AttributedString highlight(LineReader reader, String buffer) { int underlineStart = -1; int underlineEnd = -1; int negativeStart = -1; int negativeEnd = -1; String search = reader.getSearchTerm(); if (search != null && search.length() > 0) { underlineStart = buffer.indexOf(search); if (underlineStart >= 0) { underlineEnd = underlineStart + search.length() - 1; } } if (reader.getRegionActive() != RegionType.NONE) { negativeStart = reader.getRegionMark(); negativeEnd = reader.getBuffer().cursor(); if (negativeStart > negativeEnd) { int x = negativeEnd; negativeEnd = negativeStart; negativeStart = x; } if (reader.getRegionActive() == RegionType.LINE) { while (negativeStart > 0 && reader.getBuffer().atChar(negativeStart - 1) != '\n') { negativeStart--; } while (negativeEnd < reader.getBuffer().length() - 1 && reader.getBuffer().atChar(negativeEnd + 1) != '\n') { negativeEnd++; } } } AttributedStringBuilder sb = new AttributedStringBuilder(); for (int i = 0; i < buffer.length(); i++) { if (i == underlineStart) { sb.style(AttributedStyle::underline); } if (i == negativeStart) { sb.style(AttributedStyle::inverse); } char c = buffer.charAt(i); if (c == '\t' || c == '\n') { sb.append(c); } else if (c < 32) { sb.style(AttributedStyle::inverseNeg) .append('^') .append((char) (c + '@')) .style(AttributedStyle::inverseNeg); } else { int w = WCWidth.wcwidth(c); if (w > 0) { sb.append(c); } } if (i == underlineEnd) { sb.style(AttributedStyle::underlineOff); } if (i == negativeEnd) { sb.style(AttributedStyle::inverseOff); } } return sb.toAttributedString(); } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/DefaultParser.java000066400000000000000000000200351311544710100273730ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Objects; import org.jline.reader.EOFError; import org.jline.reader.ParsedLine; import org.jline.reader.Parser; import org.jline.reader.Parser.ParseContext; public class DefaultParser implements Parser { private char[] quoteChars = {'\'', '"'}; private char[] escapeChars = {'\\'}; private boolean eofOnUnclosedQuote; private boolean eofOnEscapedNewLine; public void setQuoteChars(final char[] chars) { this.quoteChars = chars; } public char[] getQuoteChars() { return this.quoteChars; } public void setEscapeChars(final char[] chars) { this.escapeChars = chars; } public char[] getEscapeChars() { return this.escapeChars; } public void setEofOnUnclosedQuote(boolean eofOnUnclosedQuote) { this.eofOnUnclosedQuote = eofOnUnclosedQuote; } public boolean isEofOnUnclosedQuote() { return eofOnUnclosedQuote; } public void setEofOnEscapedNewLine(boolean eofOnEscapedNewLine) { this.eofOnEscapedNewLine = eofOnEscapedNewLine; } public boolean isEofOnEscapedNewLine() { return eofOnEscapedNewLine; } public ParsedLine parse(final String line, final int cursor, ParseContext context) { List words = new LinkedList<>(); StringBuilder current = new StringBuilder(); int wordCursor = -1; int wordIndex = -1; int quoteStart = -1; for (int i = 0; (line != null) && (i < line.length()); i++) { // once we reach the cursor, set the // position of the selected index if (i == cursor) { wordIndex = words.size(); // the position in the current argument is just the // length of the current argument wordCursor = current.length(); } if (quoteStart < 0 && isQuoteChar(line, i)) { // Start a quote block quoteStart = i; } else if (quoteStart >= 0) { // In a quote block if (line.charAt(quoteStart) == line.charAt(i) && !isEscaped(line, i)) { // End the block; arg could be empty, but that's fine words.add(current.toString()); current.setLength(0); quoteStart = -1; } else if (!isEscapeChar(line, i)) { // Take the next character current.append(line.charAt(i)); } } else { // Not in a quote block if (isDelimiter(line, i)) { if (current.length() > 0) { words.add(current.toString()); current.setLength(0); // reset the arg } } else if (!isEscapeChar(line, i)) { current.append(line.charAt(i)); } } } if (current.length() > 0 || cursor == line.length()) { words.add(current.toString()); } if (cursor == line.length()) { wordIndex = words.size() - 1; wordCursor = words.get(words.size() - 1).length(); } if (eofOnEscapedNewLine && isEscapeChar(line, line.length() - 1)) { throw new EOFError(-1, -1, "Escaped new line", "newline"); } if (eofOnUnclosedQuote && quoteStart >= 0) { throw new EOFError(-1, -1, "Missing closing quote", line.charAt(quoteStart) == '\'' ? "quote" : "dquote"); } return new ArgumentList(line, words, wordIndex, wordCursor, 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 CharSequence buffer, final int pos) { return !isQuoted(buffer, pos) && !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos); } public boolean isQuoted(final CharSequence buffer, final int pos) { return false; } public boolean isQuoteChar(final CharSequence buffer, final int pos) { if (pos < 0) { return false; } for (int i = 0; (quoteChars != null) && (i < quoteChars.length); i++) { if (buffer.charAt(pos) == quoteChars[i]) { return !isEscaped(buffer, pos); } } return false; } /** * Check if this character is a valid escape char (i.e. one that has not been escaped) */ public boolean isEscapeChar(final CharSequence 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); // escape escape } } return false; } /** * Check if a character is escaped (i.e. if the previous character is an escape) * * @param buffer * the buffer to check in * @param pos * the position of the character to check * @return true if the character at the specified position in the given buffer is an escape character and the character immediately preceding it is not an * escape character. */ public boolean isEscaped(final CharSequence buffer, final int pos) { if (pos <= 0) { return false; } return isEscapeChar(buffer, pos - 1); } /** * 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 boolean isDelimiterChar(CharSequence buffer, int pos) { return Character.isWhitespace(buffer.charAt(pos)); } /** * The result of a delimited buffer. * * @author Marc Prud'hommeaux */ public static class ArgumentList implements ParsedLine { private final String line; private final List words; private final int wordIndex; private final int wordCursor; private final int cursor; public ArgumentList(final String line, final List words, final int wordIndex, final int wordCursor, final int cursor) { this.line = line; this.words = Collections.unmodifiableList(Objects.requireNonNull(words)); this.wordIndex = wordIndex; this.wordCursor = wordCursor; this.cursor = cursor; } public int wordIndex() { return this.wordIndex; } public String word() { // TODO: word() should always be contained in words() if ((wordIndex < 0) || (wordIndex >= words.size())) { return ""; } return words.get(wordIndex); } public int wordCursor() { return this.wordCursor; } public List words() { return this.words; } public int cursor() { return this.cursor; } public String line() { return line; } } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/KillRing.java000066400000000000000000000100331311544710100263420ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; /** * The kill ring class keeps killed text in a fixed size ring. In this * class we also keep record of whether or not the last command was a * kill or a yank. Depending on this, the class may behave * different. For instance, two consecutive kill-word commands fill * the same slot such that the next yank will return the two * previously killed words instead that only the last one. Likewise * yank pop requires that the previous command was either a yank or a * yank-pop. */ public final class KillRing { /** * Default size is 60, like in emacs. */ private static final int DEFAULT_SIZE = 60; private final String[] slots; private int head = 0; private boolean lastKill = false; private boolean lastYank = false; /** * Creates a new kill ring of the given size. */ public KillRing(int size) { slots = new String[size]; } /** * Creates a new kill ring of the default size. See {@link #DEFAULT_SIZE}. */ public KillRing() { this(DEFAULT_SIZE); } /** * Resets the last-yank state. */ public void resetLastYank() { lastYank = false; } /** * Resets the last-kill state. */ public void resetLastKill() { lastKill = false; } /** * Returns {@code true} if the last command was a yank. */ public boolean lastYank() { return lastYank; } /** * Adds the string to the kill-ring. Also sets lastYank to false * and lastKill to true. */ public void add(String str) { lastYank = false; if (lastKill) { if (slots[head] != null) { slots[head] += str; return; } } lastKill = true; next(); slots[head] = str; } /** * Adds the string to the kill-ring product of killing * backwards. If the previous command was a kill text one then * adds the text at the beginning of the previous kill to avoid * that two consecutive backwards kills followed by a yank leaves * things reversed. */ public void addBackwards(String str) { lastYank = false; if (lastKill) { if (slots[head] != null) { slots[head] = str + slots[head]; return; } } lastKill = true; next(); slots[head] = str; } /** * Yanks a previously killed text. Returns {@code null} if the * ring is empty. */ public String yank() { lastKill = false; lastYank = true; return slots[head]; } /** * Moves the pointer to the current slot back and returns the text * in that position. If the previous command was not yank returns * null. */ public String yankPop() { lastKill = false; if (lastYank) { prev(); return slots[head]; } return null; } /** * Moves the pointer to the current slot forward. If the end of * the slots is reached then points back to the beginning. */ private void next() { if (head == 0 && slots[0] == null) { return; } head++; if (head == slots.length) { head = 0; } } /** * Moves the pointer to the current slot backwards. If the * beginning of the slots is reached then traverses the slot * backwards until one with not null content is found. */ private void prev() { head--; if (head == -1) { int x = (slots.length - 1); for (; x >= 0; x--) { if (slots[x] != null) { break; } } head = x; } } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java000066400000000000000000005550651311544710100275060ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.io.Flushable; import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.StringWriter; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.function.Function; import java.util.function.IntBinaryOperator; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.jline.keymap.BindingReader; import org.jline.keymap.KeyMap; import org.jline.reader.*; import org.jline.reader.Parser.ParseContext; import org.jline.reader.impl.history.DefaultHistory; import org.jline.terminal.*; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Terminal.Signal; import org.jline.terminal.Terminal.SignalHandler; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; import org.jline.utils.Curses; import org.jline.utils.Display; import org.jline.utils.InfoCmp.Capability; import org.jline.utils.Levenshtein; import org.jline.utils.Log; import org.jline.utils.WCWidth; import static org.jline.keymap.KeyMap.alt; import static org.jline.keymap.KeyMap.ctrl; import static org.jline.keymap.KeyMap.del; import static org.jline.keymap.KeyMap.esc; import static org.jline.keymap.KeyMap.range; import static org.jline.keymap.KeyMap.translate; /** * A reader for terminal applications. It supports custom tab-completion, * saveable command history, and command line editing. * * @author Marc Prud'hommeaux * @author Jason Dillon * @author Guillaume Nodet */ @SuppressWarnings("StatementWithEmptyBody") public class LineReaderImpl implements LineReader, Flushable { public static final char NULL_MASK = 0; public static final int TAB_WIDTH = 4; public static final String DEFAULT_WORDCHARS = "*?_-.[]~=/&;!#$%^(){}<>"; public static final String DEFAULT_REMOVE_SUFFIX_CHARS = " \t\n;&|"; public static final String DEFAULT_COMMENT_BEGIN = "#"; public static final String DEFAULT_SEARCH_TERMINATORS = "\033\012"; public static final String DEFAULT_BELL_STYLE = ""; public static final int DEFAULT_LIST_MAX = 100; public static final int DEFAULT_ERRORS = 2; public static final long DEFAULT_BLINK_MATCHING_PAREN = 500L; public static final long DEFAULT_AMBIGUOUS_BINDING = 1000L; public static final String DEFAULT_SECONDARY_PROMPT_PATTERN = "%M> "; private static final int MIN_ROWS = 3; /** * Possible states in which the current readline operation may be in. */ protected enum State { /** * The user is just typing away */ NORMAL, /** * readLine should exit and return the buffer content */ DONE, /** * readLine should exit and throw an EOFException */ EOF, /** * readLine should exit and throw an UserInterruptException */ INTERRUPT } protected enum ViMoveMode { NORMAL, YANK, DELETE, CHANGE } protected enum BellType { NONE, AUDIBLE, VISIBLE } // // Constructor variables // /** The terminal to use */ protected final Terminal terminal; /** The application name */ protected final String appName; /** The terminal keys mapping */ protected final Map> keyMaps; // // Configuration // protected final Map variables; protected History history = new DefaultHistory(); protected Completer completer = null; protected Highlighter highlighter = new DefaultHighlighter(); protected Parser parser = new DefaultParser(); protected Expander expander = new DefaultExpander(); // // State variables // protected final Map options = new HashMap<>(); protected final Buffer buf = new BufferImpl(); protected final Size size = new Size(); protected AttributedString prompt; protected AttributedString rightPrompt; protected Character mask; protected Map modifiedHistory = new HashMap<>(); protected Buffer historyBuffer = null; protected CharSequence searchBuffer; protected StringBuffer searchTerm = null; protected int searchIndex = -1; // Reading buffers protected final BindingReader bindingReader; /** * VI character find */ protected int findChar; protected int findDir; protected int findTailAdd; /** * VI history string search */ private int searchDir; private String searchString; /** * Region state */ protected int regionMark; protected RegionType regionActive; private boolean forceChar; private boolean forceLine; /** * The vi yank buffer */ protected String yankBuffer = ""; protected ViMoveMode viMoveMode = ViMoveMode.NORMAL; protected KillRing killRing = new KillRing(); protected UndoTree undo = new UndoTree<>(this::setBuffer); protected boolean isUndo; /* * Current internal state of the line reader */ protected State state = State.DONE; protected boolean reading; protected Supplier post; protected Map builtinWidgets; protected Map widgets; protected int count; protected int mult; protected int universal = 4; protected int repeatCount; protected boolean isArgDigit; protected ParsedLine parsedLine; protected boolean skipRedisplay; protected Display display; protected boolean overTyping = false; protected String keyMap; protected int smallTerminalOffset = 0; public LineReaderImpl(Terminal terminal) throws IOException { this(terminal, null, null); } public LineReaderImpl(Terminal terminal, String appName) throws IOException { this(terminal, appName, null); } public LineReaderImpl(Terminal terminal, String appName, Map variables) { Objects.requireNonNull(terminal, "terminal can not be null"); this.terminal = terminal; if (appName == null) { appName = "JLine"; } this.appName = appName; if (variables != null) { this.variables = variables; } else { this.variables = new HashMap<>(); } this.keyMaps = defaultKeyMaps(); builtinWidgets = builtinWidgets(); widgets = new HashMap<>(builtinWidgets); bindingReader = new BindingReader(terminal.reader()); } public Terminal getTerminal() { return terminal; } public String getAppName() { return appName; } public Map> getKeyMaps() { return keyMaps; } public KeyMap getKeys() { return keyMaps.get(keyMap); } @Override public Map getWidgets() { return widgets; } @Override public Map getBuiltinWidgets() { return Collections.unmodifiableMap(builtinWidgets); } @Override public Buffer getBuffer() { return buf; } @Override public void runMacro(String macro) { bindingReader.runMacro(macro); } @Override public MouseEvent readMouseEvent() { return terminal.readMouseEvent(bindingReader::readCharacter); } /** * Set the completer. */ public void setCompleter(Completer completer) { this.completer = completer; } /** * Returns the completer. */ public Completer getCompleter() { return completer; } // // History // public void setHistory(final History history) { Objects.requireNonNull(history); this.history = history; } public History getHistory() { return history; } // // Highlighter // public void setHighlighter(Highlighter highlighter) { this.highlighter = highlighter; } public Highlighter getHighlighter() { return highlighter; } public Parser getParser() { return parser; } public void setParser(Parser parser) { this.parser = parser; } @Override public Expander getExpander() { return expander; } public void setExpander(Expander expander) { this.expander = expander; } // // Line Reading // /** * Read the next line and return the contents of the buffer. */ public String readLine() throws UserInterruptException, EndOfFileException { return readLine(null, null, null, null); } /** * Read the next line with the specified character mask. If null, then * characters will be echoed. If 0, then no characters will be echoed. */ public String readLine(Character mask) throws UserInterruptException, EndOfFileException { return readLine(null, null, mask, null); } public String readLine(String prompt) throws UserInterruptException, EndOfFileException { return readLine(prompt, null, null, null); } /** * Read a line from the in {@link InputStream}, and return the line * (without any trailing newlines). * * @param prompt The prompt to issue to the terminal, may be null. * @return A line that is read from the terminal, or null if there was null input (e.g., CTRL-D * was pressed). */ public String readLine(String prompt, Character mask) throws UserInterruptException, EndOfFileException { return readLine(prompt, null, mask, null); } /** * Read a line from the in {@link InputStream}, and return the line * (without any trailing newlines). * * @param prompt The prompt to issue to the terminal, may be null. * @return A line that is read from the terminal, or null if there was null input (e.g., CTRL-D * was pressed). */ public String readLine(String prompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException { return readLine(prompt, null, mask, buffer); } /** * Read a line from the in {@link InputStream}, and return the line * (without any trailing newlines). * * @param prompt The prompt to issue to the terminal, may be null. * @return A line that is read from the terminal, or null if there was null input (e.g., CTRL-D * was pressed). */ public String readLine(String prompt, String rightPrompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException { // prompt may be null // mask may be null // buffer may be null Thread readLineThread = Thread.currentThread(); SignalHandler previousIntrHandler = null; SignalHandler previousWinchHandler = null; SignalHandler previousContHandler = null; Attributes originalAttributes = null; boolean dumb = Terminal.TYPE_DUMB.equals(terminal.getType()); try { if (reading) { throw new IllegalStateException(); } reading = true; this.mask = mask; /* * This is the accumulator for VI-mode repeat count. That is, while in * move mode, if you type 30x it will delete 30 characters. This is * where the "30" is accumulated until the command is struck. */ repeatCount = 0; mult = 1; regionActive = RegionType.NONE; regionMark = -1; smallTerminalOffset = 0; state = State.NORMAL; modifiedHistory.clear(); setPrompt(prompt); setRightPrompt(rightPrompt); buf.clear(); if (buffer != null) { buf.write(buffer); } undo.clear(); parsedLine = null; keyMap = MAIN; if (history != null) { history.attach(this); } previousIntrHandler = terminal.handle(Signal.INT, signal -> readLineThread.interrupt()); previousWinchHandler = terminal.handle(Signal.WINCH, this::handleSignal); previousContHandler = terminal.handle(Signal.CONT, this::handleSignal); originalAttributes = terminal.enterRawMode(); // Cache terminal size for the duration of the call to readLine() // It will eventually be updated with WINCH signals size.copy(terminal.getSize()); display = new Display(terminal, false); if (size.getRows() == 0 || size.getColumns() == 0) { display.resize(1, Integer.MAX_VALUE); } else { display.resize(size.getRows(), size.getColumns()); } if (isSet(Option.DELAY_LINE_WRAP)) display.setDelayLineWrap(true); // Move into application mode if (!dumb) { terminal.puts(Capability.keypad_xmit); if (isSet(Option.AUTO_FRESH_LINE)) callWidget(FRESH_LINE); if (isSet(Option.MOUSE)) terminal.trackMouse(Terminal.MouseTracking.Normal); } else { // For dumb terminals, we need to make sure that CR are ignored Attributes attr = new Attributes(originalAttributes); attr.setInputFlag(Attributes.InputFlag.IGNCR, true); terminal.setAttributes(attr); } callWidget(CALLBACK_INIT); undo.newState(buf.copy()); // Draw initial prompt redrawLine(); redisplay(); while (true) { KeyMap local = null; if (isInViCmdMode() && regionActive != RegionType.NONE) { local = keyMaps.get(VISUAL); } Binding o = readBinding(getKeys(), local); if (o == null) { return null; } Log.trace("Binding: ", o); if (buf.length() == 0 && getLastBinding().charAt(0) == originalAttributes.getControlChar(ControlChar.VEOF)) { throw new EndOfFileException(); } // If this is still false after handling the binding, then // we reset our repeatCount to 0. isArgDigit = false; // Every command that can be repeated a specified number // of times, needs to know how many times to repeat, so // we figure that out here. count = ((repeatCount == 0) ? 1 : repeatCount) * mult; // Reset undo/redo flag isUndo = false; // Get executable widget Buffer copy = buf.copy(); Widget w = getWidget(o); if (!w.apply()) { beep(); } if (!isUndo && !copy.toString().equals(buf.toString())) { undo.newState(buf.copy()); } switch (state) { case DONE: return finishBuffer(); case EOF: throw new EndOfFileException(); case INTERRUPT: throw new UserInterruptException(buf.toString()); } if (!isArgDigit) { /* * If the operation performed wasn't a vi argument * digit, then clear out the current repeatCount; */ repeatCount = 0; mult = 1; } if (!dumb) { redisplay(); } } } catch (IOError e) { if (e.getCause() instanceof InterruptedIOException) { throw new UserInterruptException(buf.toString()); } else { throw e; } } finally { cleanup(); reading = false; if (originalAttributes != null) { terminal.setAttributes(originalAttributes); } if (previousIntrHandler != null) { terminal.handle(Signal.INT, previousIntrHandler); } if (previousWinchHandler != null) { terminal.handle(Signal.WINCH, previousWinchHandler); } if (previousContHandler != null) { terminal.handle(Signal.CONT, previousContHandler); } } } /** Make sure we position the cursor on column 0 */ protected boolean freshLine() { boolean wrapAtEol = terminal.getBooleanCapability(Capability.auto_right_margin); boolean delayedWrapAtEol = wrapAtEol && terminal.getBooleanCapability(Capability.eat_newline_glitch); AttributedStringBuilder sb = new AttributedStringBuilder(); sb.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.BLACK + AttributedStyle.BRIGHT)); sb.append("~"); sb.style(AttributedStyle.DEFAULT); if (!wrapAtEol || delayedWrapAtEol) { for (int i = 0; i < size.getColumns() - 1; i++) { sb.append(" "); } sb.append(KeyMap.key(terminal, Capability.carriage_return)); sb.append(" "); sb.append(KeyMap.key(terminal, Capability.carriage_return)); } else { // Given the terminal will wrap automatically, // we need to print one less than needed. // This means that the last character will not // be overwritten, and that's why we're using // a clr_eol first if possible. String el = terminal.getStringCapability(Capability.clr_eol); if (el != null) { StringWriter sw = new StringWriter(); try { Curses.tputs(sw, el); } catch (IOException e) { // nothing } sb.append(sw.toString()); } for (int i = 0; i < size.getColumns() - 2; i++) { sb.append(" "); } sb.append(KeyMap.key(terminal, Capability.carriage_return)); sb.append(" "); sb.append(KeyMap.key(terminal, Capability.carriage_return)); } print(sb.toAnsi(terminal)); return true; } @Override public void callWidget(String name) { if (!reading) { throw new IllegalStateException(); } try { Widget w; if (name.startsWith(".")) { w = builtinWidgets.get(name.substring(1)); } else { w = widgets.get(name); } if (w != null) { w.apply(); } } catch (Throwable t) { Log.debug("Error executing widget '", name, "'", t); } } /** * Clear the line and redraw it. */ public boolean redrawLine() { display.reset(); return true; } /** * Write out the specified string to the buffer and the output stream. */ public void putString(final CharSequence str) { buf.write(str, overTyping); } /** * Flush the terminal output stream. This is important for printout out single characters (like a buf.backspace or * keyboard) that we want the terminal to handle immediately. */ public void flush() { terminal.flush(); } public boolean isKeyMap(String name) { return keyMap.equals(name); } /** * Read a character from the terminal. * * @return the character, or -1 if an EOF is received. */ public int readCharacter() { return bindingReader.readCharacter(); } public int peekCharacter(long timeout) { return bindingReader.peekCharacter(timeout); } /** * Read from the input stream and decode an operation from the key map. * * The input stream will be read character by character until a matching * binding can be found. Characters that can't possibly be matched to * any binding will be discarded. * * @param keys the KeyMap to use for decoding the input stream * @return the decoded binding or null if the end of * stream has been reached */ public Binding readBinding(KeyMap keys) { return readBinding(keys, null); } public Binding readBinding(KeyMap keys, KeyMap local) { Binding o = bindingReader.readBinding(keys, local); /* * The kill ring keeps record of whether or not the * previous command was a yank or a kill. We reset * that state here if needed. */ if (o instanceof Reference) { String ref = ((Reference) o).name(); if (!YANK_POP.equals(ref) && !YANK.equals(ref)) { killRing.resetLastYank(); } if (!KILL_LINE.equals(ref) && !KILL_WHOLE_LINE.equals(ref) && !BACKWARD_KILL_WORD.equals(ref) && !KILL_WORD.equals(ref)) { killRing.resetLastKill(); } } return o; } @Override public ParsedLine getParsedLine() { return parsedLine; } public String getLastBinding() { return bindingReader.getLastBinding(); } public String getSearchTerm() { return searchTerm != null ? searchTerm.toString() : null; } @Override public RegionType getRegionActive() { return regionActive; } @Override public int getRegionMark() { return regionMark; } // // Key Bindings // /** * Sets the current keymap by name. Supported keymaps are "emacs", * "viins", "vicmd". * @param name The name of the keymap to switch to * @return true if the keymap was set, or false if the keymap is * not recognized. */ public boolean setKeyMap(String name) { KeyMap map = keyMaps.get(name); if (map == null) { return false; } this.keyMap = name; if (reading) { callWidget(CALLBACK_KEYMAP); } return true; } /** * Returns the name of the current key mapping. * @return the name of the key mapping. This will be the canonical name * of the current mode of the key map and may not reflect the name that * was used with {@link #setKeyMap(String)}. */ public String getKeyMap() { return keyMap; } @Override public Map getVariables() { return variables; } @Override public Object getVariable(String name) { return variables.get(name); } @Override public void setVariable(String name, Object value) { variables.put(name, value); } @Override public boolean isSet(Option option) { Boolean b = options.get(option); return b != null ? b : option.isDef(); } @Override public void setOpt(Option option) { options.put(option, Boolean.TRUE); } @Override public void unsetOpt(Option option) { options.put(option, Boolean.FALSE); } // // Widget implementation // /** * Clear the buffer and add its contents to the history. * * @return the former contents of the buffer. */ protected String finishBuffer() { String str = buf.toString(); String historyLine = str; if (!isSet(Option.DISABLE_EVENT_EXPANSION)) { StringBuilder sb = new StringBuilder(); boolean escaped = false; for (int i = 0; i < str.length(); i++) { char ch = str.charAt(i); if (escaped) { escaped = false; if (ch != '\n') { sb.append(ch); } } else if (ch == '\\') { escaped = true; } else { sb.append(ch); } } str = sb.toString(); } // we only add it to the history if the buffer is not empty // and if mask is null, since having a mask typically means // the string was a password. We clear the mask after this call if (str.length() > 0 && mask == null) { history.add(Instant.now(), historyLine); } return str; } protected void handleSignal(Signal signal) { if (signal == Signal.WINCH) { size.copy(terminal.getSize()); display.resize(size.getRows(), size.getColumns()); redisplay(); } else if (signal == Signal.CONT) { terminal.enterRawMode(); size.copy(terminal.getSize()); display.resize(size.getRows(), size.getColumns()); terminal.puts(Capability.keypad_xmit); redrawLine(); redisplay(); } } @SuppressWarnings("unchecked") protected Widget getWidget(Object binding) { Widget w; if (binding instanceof Widget) { w = (Widget) binding; } else if (binding instanceof Macro) { String macro = ((Macro) binding).getSequence(); w = () -> { bindingReader.runMacro(macro); return true; }; } else if (binding instanceof Reference) { String name = ((Reference) binding).name(); w = widgets.get(name); if (w == null) { w = () -> { post = () -> new AttributedString("No such widget `" + name + "'"); return false; }; } } else { w = () -> { post = () -> new AttributedString("Unsupported widget"); return false; }; } return w; } // // Helper methods // protected void setPrompt(final String prompt) { this.prompt = (prompt == null ? AttributedString.EMPTY : expandPromptPattern(prompt, 0, "", 0)); } protected void setRightPrompt(final String rightPrompt) { this.rightPrompt = (rightPrompt == null ? AttributedString.EMPTY : expandPromptPattern(rightPrompt, 0, "", 0)); } protected void setBuffer(Buffer buffer) { setBuffer(buffer.toString()); buf.cursor(buffer.cursor()); } /** * Set the current buffer's content to the specified {@link String}. The * visual terminal will be modified to show the current buffer. * * @param buffer the new contents of the buffer. */ protected void setBuffer(final String buffer) { buf.clear(); buf.write(buffer); } /** * This method is calling while doing a delete-to ("d"), change-to ("c"), * or yank-to ("y") and it filters out only those movement operations * that are allowable during those operations. Any operation that isn't * allow drops you back into movement mode. * * @param op The incoming operation to remap * @return The remaped operation */ protected String viDeleteChangeYankToRemap (String op) { switch (op) { case SEND_BREAK: case BACKWARD_CHAR: case FORWARD_CHAR: case END_OF_LINE: case VI_MATCH_BRACKET: case VI_DIGIT_OR_BEGINNING_OF_LINE: case NEG_ARGUMENT: case DIGIT_ARGUMENT: case VI_BACKWARD_CHAR: case VI_BACKWARD_WORD: case VI_FORWARD_CHAR: case VI_FORWARD_WORD: case VI_FORWARD_WORD_END: case VI_FIRST_NON_BLANK: case VI_GOTO_COLUMN: case VI_DELETE: case VI_YANK: case VI_CHANGE: case VI_FIND_NEXT_CHAR: case VI_FIND_NEXT_CHAR_SKIP: case VI_FIND_PREV_CHAR: case VI_FIND_PREV_CHAR_SKIP: case VI_REPEAT_FIND: case VI_REV_REPEAT_FIND: return op; default: return VI_CMD_MODE; } } protected int switchCase(int ch) { if (Character.isUpperCase(ch)) { return Character.toLowerCase(ch); } else if (Character.isLowerCase(ch)) { return Character.toUpperCase(ch); } else { return ch; } } /** * @return true if line reader is in the middle of doing a change-to * delete-to or yank-to. */ protected boolean isInViMoveOperation() { return viMoveMode != ViMoveMode.NORMAL; } protected boolean isInViChangeOperation() { return viMoveMode == ViMoveMode.CHANGE; } protected boolean isInViCmdMode() { return VICMD.equals(keyMap); } // // Movement // protected boolean viForwardChar() { if (count < 0) { return callNeg(this::viBackwardChar); } int lim = findeol(); if (isInViCmdMode() && !isInViMoveOperation()) { lim--; } if (buf.cursor() >= lim) { return false; } while (count-- > 0 && buf.cursor() < lim) { buf.move(1); } return true; } protected boolean viBackwardChar() { if (count < 0) { return callNeg(this::viForwardChar); } int lim = findbol(); if (buf.cursor() == lim) { return false; } while (count-- > 0 && buf.cursor() > 0) { buf.move(-1); if (buf.currChar() == '\n') { buf.move(1); break; } } return true; } // // Word movement // protected boolean forwardWord() { if (count < 0) { return callNeg(this::backwardWord); } while (count-- > 0) { while (buf.cursor() < buf.length() && isWord(buf.currChar())) { buf.move(1); } if (isInViChangeOperation() && count == 0) { break; } while (buf.cursor() < buf.length() && !isWord(buf.currChar())) { buf.move(1); } } return true; } protected boolean viForwardWord() { if (count < 0) { return callNeg(this::backwardWord); } while (count-- > 0) { if (isViAlphaNum(buf.currChar())) { while (buf.cursor() < buf.length() && isViAlphaNum(buf.currChar())) { buf.move(1); } } else { while (buf.cursor() < buf.length() && !isViAlphaNum(buf.currChar()) && !isWhitespace(buf.currChar())) { buf.move(1); } } if (isInViChangeOperation() && count == 0) { return true; } int nl = buf.currChar() == '\n' ? 1 : 0; while (buf.cursor() < buf.length() && nl < 2 && isWhitespace(buf.currChar())) { buf.move(1); nl += buf.currChar() == '\n' ? 1 : 0; } } return true; } protected boolean viForwardBlankWord() { if (count < 0) { return callNeg(this::viBackwardBlankWord); } while (count-- > 0) { while (buf.cursor() < buf.length() && !isWhitespace(buf.currChar())) { buf.move(1); } if (isInViChangeOperation() && count == 0) { return true; } int nl = buf.currChar() == '\n' ? 1 : 0; while (buf.cursor() < buf.length() && nl < 2 && isWhitespace(buf.currChar())) { buf.move(1); nl += buf.currChar() == '\n' ? 1 : 0; } } return true; } protected boolean emacsForwardWord() { if (count < 0) { return callNeg(this::emacsBackwardWord); } while (count-- > 0) { while (buf.cursor() < buf.length() && !isWord(buf.currChar())) { buf.move(1); } if (isInViChangeOperation() && count == 0) { return true; } while (buf.cursor() < buf.length() && isWord(buf.currChar())) { buf.move(1); } } return true; } protected boolean viForwardBlankWordEnd() { if (count < 0) { return false; } while (count-- > 0) { while (buf.cursor() < buf.length()) { buf.move(1); if (!isWhitespace(buf.currChar())) { break; } } while (buf.cursor() < buf.length()) { buf.move(1); if (isWhitespace(buf.currChar())) { break; } } } return true; } protected boolean viForwardWordEnd() { if (count < 0) { return callNeg(this::backwardWord); } while (count-- > 0) { while (buf.cursor() < buf.length()) { if (!isWhitespace(buf.nextChar())) { break; } buf.move(1); } if (buf.cursor() < buf.length()) { if (isViAlphaNum(buf.nextChar())) { buf.move(1); while (buf.cursor() < buf.length() && isViAlphaNum(buf.nextChar())) { buf.move(1); } } else { buf.move(1); while (buf.cursor() < buf.length() && !isViAlphaNum(buf.nextChar()) && !isWhitespace(buf.nextChar())) { buf.move(1); } } } } if (buf.cursor() < buf.length() && isInViMoveOperation()) { buf.move(1); } return true; } protected boolean backwardWord() { if (count < 0) { return callNeg(this::forwardWord); } while (count-- > 0) { while (buf.cursor() > 0 && !isWord(buf.atChar(buf.cursor() - 1))) { buf.move(-1); } while (buf.cursor() > 0 && isWord(buf.atChar(buf.cursor() - 1))) { buf.move(-1); } } return true; } protected boolean viBackwardWord() { if (count < 0) { return callNeg(this::backwardWord); } while (count-- > 0) { int nl = 0; while (buf.cursor() > 0) { buf.move(-1); if (!isWhitespace(buf.currChar())) { break; } nl += buf.currChar() == '\n' ? 1 : 0; if (nl == 2) { buf.move(1); break; } } if (buf.cursor() > 0) { if (isViAlphaNum(buf.currChar())) { while (buf.cursor() > 0) { if (!isViAlphaNum(buf.prevChar())) { break; } buf.move(-1); } } else { while (buf.cursor() > 0) { if (isViAlphaNum(buf.prevChar()) || isWhitespace(buf.prevChar())) { break; } buf.move(-1); } } } } return true; } protected boolean viBackwardBlankWord() { if (count < 0) { return callNeg(this::viForwardBlankWord); } while (count-- > 0) { while (buf.cursor() > 0) { buf.move(-1); if (!isWhitespace(buf.currChar())) { break; } } while (buf.cursor() > 0) { buf.move(-1); if (isWhitespace(buf.currChar())) { break; } } } return true; } protected boolean viBackwardWordEnd() { if (count < 0) { return callNeg(this::viForwardWordEnd); } while (count-- > 0 && buf.cursor() > 1) { int start; if (isViAlphaNum(buf.currChar())) { start = 1; } else if (!isWhitespace(buf.currChar())) { start = 2; } else { start = 0; } while (buf.cursor() > 0) { boolean same = (start != 1) && isWhitespace(buf.currChar()); if (start != 0) { same |= isViAlphaNum(buf.currChar()); } if (same == (start == 2)) { break; } buf.move(-1); } while (buf.cursor() > 0 && isWhitespace(buf.currChar())) { buf.move(-1); } } return true; } protected boolean viBackwardBlankWordEnd() { if (count < 0) { return callNeg(this::viForwardBlankWordEnd); } while (count-- > 0) { while (buf.cursor() > 0 && !isWhitespace(buf.currChar())) { buf.move(-1); } while (buf.cursor() > 0 && isWhitespace(buf.currChar())) { buf.move(-1); } } return true; } protected boolean emacsBackwardWord() { if (count < 0) { return callNeg(this::emacsForwardWord); } while (count-- > 0) { while (buf.cursor() > 0) { buf.move(-1); if (isWord(buf.currChar())) { break; } } while (buf.cursor() > 0) { buf.move(-1); if (!isWord(buf.currChar())) { break; } } } return true; } protected boolean backwardDeleteWord() { if (count < 0) { return callNeg(this::deleteWord); } int cursor = buf.cursor(); while (count-- > 0) { while (cursor > 0 && !isWord(buf.atChar(cursor - 1))) { cursor--; } while (cursor > 0 && isWord(buf.atChar(cursor - 1))) { cursor--; } } buf.backspace(buf.cursor() - cursor); return true; } protected boolean viBackwardKillWord() { if (count < 0) { return false; } int lim = findbol(); int x = buf.cursor(); while (count-- > 0) { while (x > lim && isWhitespace(buf.atChar(x - 1))) { x--; } if (x > lim) { if (isViAlphaNum(buf.atChar(x - 1))) { while (x > lim && isViAlphaNum(buf.atChar(x - 1))) { x--; } } else { while (x > lim && !isViAlphaNum(buf.atChar(x - 1)) && !isWhitespace(buf.atChar(x - 1))) { x--; } } } } killRing.addBackwards(buf.substring(x, buf.cursor())); buf.backspace(buf.cursor() - x); return true; } protected boolean backwardKillWord() { if (count < 0) { return callNeg(this::killWord); } int x = buf.cursor(); while (count-- > 0) { while (x > 0 && !isWord(buf.atChar(x - 1))) { x--; } while (x > 0 && isWord(buf.atChar(x - 1))) { x--; } } killRing.addBackwards(buf.substring(x, buf.cursor())); buf.backspace(buf.cursor() - x); return true; } protected boolean copyPrevWord() { if (count <= 0) { return false; } int t1, t0 = buf.cursor(); while (true) { t1 = t0; while (t0 > 0 && !isWord(buf.atChar(t0 - 1))) { t0--; } while (t0 > 0 && isWord(buf.atChar(t0 - 1))) { t0--; } if (--count == 0) { break; } if (t0 == 0) { return false; } } buf.write(buf.substring(t0, t1)); return true; } protected boolean upCaseWord() { int count = Math.abs(this.count); int cursor = buf.cursor(); while (count-- > 0) { while (buf.cursor() < buf.length() && !isWord(buf.currChar())) { buf.move(1); } while (buf.cursor() < buf.length() && isWord(buf.currChar())) { buf.currChar(Character.toUpperCase(buf.currChar())); buf.move(1); } } if (this.count < 0) { buf.cursor(cursor); } return true; } protected boolean downCaseWord() { int count = Math.abs(this.count); int cursor = buf.cursor(); while (count-- > 0) { while (buf.cursor() < buf.length() && !isWord(buf.currChar())) { buf.move(1); } while (buf.cursor() < buf.length() && isWord(buf.currChar())) { buf.currChar(Character.toLowerCase(buf.currChar())); buf.move(1); } } if (this.count < 0) { buf.cursor(cursor); } return true; } protected boolean capitalizeWord() { int count = Math.abs(this.count); int cursor = buf.cursor(); while (count-- > 0) { boolean first = true; while (buf.cursor() < buf.length() && !isWord(buf.currChar())) { buf.move(1); } while (buf.cursor() < buf.length() && isWord(buf.currChar()) && !isAlpha(buf.currChar())) { buf.move(1); } while (buf.cursor() < buf.length() && isWord(buf.currChar())) { buf.currChar(first ? Character.toUpperCase(buf.currChar()) : Character.toLowerCase(buf.currChar())); buf.move(1); first = false; } } if (this.count < 0) { buf.cursor(cursor); } return true; } protected boolean deleteWord() { if (count < 0) { return callNeg(this::backwardDeleteWord); } int x = buf.cursor(); while (count-- > 0) { while (x < buf.length() && !isWord(buf.atChar(x))) { x++; } while (x < buf.length() && isWord(buf.atChar(x))) { x++; } } buf.delete(x - buf.cursor()); return true; } protected boolean killWord() { if (count < 0) { return callNeg(this::backwardKillWord); } int x = buf.cursor(); while (count-- > 0) { while (x < buf.length() && !isWord(buf.atChar(x))) { x++; } while (x < buf.length() && isWord(buf.atChar(x))) { x++; } } killRing.add(buf.substring(buf.cursor(), x)); buf.delete(x - buf.cursor()); return true; } protected boolean transposeWords() { int lstart = buf.cursor() - 1; int lend = buf.cursor(); while (buf.atChar(lstart) != 0 && buf.atChar(lstart) != '\n') { lstart--; } lstart++; while (buf.atChar(lend) != 0 && buf.atChar(lend) != '\n') { lend++; } if (lend - lstart < 2) { return false; } int words = 0; boolean inWord = false; if (!isDelimiter(buf.atChar(lstart))) { words++; inWord = true; } for (int i = lstart; i < lend; i++) { if (isDelimiter(buf.atChar(i))) { inWord = false; } else { if (!inWord) { words++; } inWord = true; } } if (words < 2) { return false; } // TODO: use isWord instead of isDelimiter boolean neg = this.count < 0; for (int count = Math.max(this.count, -this.count); count > 0; --count) { int sta1, end1, sta2, end2; // Compute current word boundaries sta1 = buf.cursor(); while (sta1 > lstart && !isDelimiter(buf.atChar(sta1 - 1))) { sta1--; } end1 = sta1; while (end1 < lend && !isDelimiter(buf.atChar(++end1))); if (neg) { end2 = sta1 - 1; while (end2 > lstart && isDelimiter(buf.atChar(end2 - 1))) { end2--; } if (end2 < lstart) { // No word before, use the word after sta2 = end1; while (isDelimiter(buf.atChar(++sta2))); end2 = sta2; while (end2 < lend && !isDelimiter(buf.atChar(++end2))); } else { sta2 = end2; while (sta2 > lstart && !isDelimiter(buf.atChar(sta2 - 1))) { sta2--; } } } else { sta2 = end1; while (sta2 < lend && isDelimiter(buf.atChar(++sta2))); if (sta2 == lend) { // No word after, use the word before end2 = sta1; while (isDelimiter(buf.atChar(end2 - 1))) { end2--; } sta2 = end2; while (sta2 > lstart && !isDelimiter(buf.atChar(sta2 - 1))) { sta2--; } } else { end2 = sta2; while (end2 < lend && !isDelimiter(buf.atChar(++end2))) ; } } if (sta1 < sta2) { String res = buf.substring(0, sta1) + buf.substring(sta2, end2) + buf.substring(end1, sta2) + buf.substring(sta1, end1) + buf.substring(end2); buf.clear(); buf.write(res); buf.cursor(neg ? end1 : end2); } else { String res = buf.substring(0, sta2) + buf.substring(sta1, end1) + buf.substring(end2, sta1) + buf.substring(sta2, end2) + buf.substring(end1); buf.clear(); buf.write(res); buf.cursor(neg ? end2 : end1); } } return true; } private int findbol() { int x = buf.cursor(); while (x > 0 && buf.atChar(x - 1) != '\n') { x--; } return x; } private int findeol() { int x = buf.cursor(); while (x < buf.length() && buf.atChar(x) != '\n') { x++; } return x; } protected boolean insertComment() { return doInsertComment(false); } protected boolean viInsertComment() { return doInsertComment(true); } protected boolean doInsertComment(boolean isViMode) { String comment = getString(COMMENT_BEGIN, DEFAULT_COMMENT_BEGIN); beginningOfLine(); putString(comment); if (isViMode) { setKeyMap(VIINS); } return acceptLine(); } protected boolean viFindNextChar() { if ((findChar = vigetkey()) > 0) { findDir = 1; findTailAdd = 0; return vifindchar(false); } return false; } protected boolean viFindPrevChar() { if ((findChar = vigetkey()) > 0) { findDir = -1; findTailAdd = 0; return vifindchar(false); } return false; } protected boolean viFindNextCharSkip() { if ((findChar = vigetkey()) > 0) { findDir = 1; findTailAdd = -1; return vifindchar(false); } return false; } protected boolean viFindPrevCharSkip() { if ((findChar = vigetkey()) > 0) { findDir = -1; findTailAdd = 1; return vifindchar(false); } return false; } protected boolean viRepeatFind() { return vifindchar(true); } protected boolean viRevRepeatFind() { if (count < 0) { return callNeg(() -> vifindchar(true)); } findTailAdd = -findTailAdd; findDir = -findDir; boolean ret = vifindchar(true); findTailAdd = -findTailAdd; findDir = -findDir; return ret; } private int vigetkey() { int ch = readCharacter(); KeyMap km = keyMaps.get(MAIN); if (km != null) { Binding b = km.getBound(new String(Character.toChars(ch))); if (b instanceof Reference) { String func = ((Reference) b).name(); if (SEND_BREAK.equals(func)) { return -1; } } } return ch; } private boolean vifindchar(boolean repeat) { if (findDir == 0) { return false; } if (count < 0) { return callNeg(this::viRevRepeatFind); } if (repeat && findTailAdd != 0) { if (findDir > 0) { if (buf.cursor() < buf.length() && buf.nextChar() == findChar) { buf.move(1); } } else { if (buf.cursor() > 0 && buf.prevChar() == findChar) { buf.move(-1); } } } int cursor = buf.cursor(); while (count-- > 0) { do { buf.move(findDir); } while (buf.cursor() > 0 && buf.cursor() < buf.length() && buf.currChar() != findChar && buf.currChar() != '\n'); if (buf.cursor() <= 0 || buf.cursor() >= buf.length() || buf.currChar() == '\n') { buf.cursor(cursor); return false; } } if (findTailAdd != 0) { buf.move(findTailAdd); } if (findDir == 1 && isInViMoveOperation()) { buf.move(1); } return true; } private boolean callNeg(Widget widget) { this.count = -this.count; boolean ret = widget.apply(); this.count = -this.count; return ret; } /** * Implements vi search ("/" or "?"). */ protected boolean viHistorySearchForward() { searchDir = 1; searchIndex = 0; return getViSearchString() && viRepeatSearch(); } protected boolean viHistorySearchBackward() { searchDir = -1; searchIndex = history.size() - 1; return getViSearchString() && viRepeatSearch(); } protected boolean viRepeatSearch() { if (searchDir == 0) { return false; } int si = searchDir < 0 ? searchBackwards(searchString, searchIndex, false) : searchForwards(searchString, searchIndex, false); if (si == -1 || si == history.index()) { return false; } searchIndex = si; /* * Show the match. */ buf.clear(); history.moveTo(searchIndex); buf.write(history.get(searchIndex)); if (VICMD.equals(keyMap)) { buf.move(-1); } return true; } protected boolean viRevRepeatSearch() { boolean ret; searchDir = -searchDir; ret = viRepeatSearch(); searchDir = -searchDir; return ret; } private boolean getViSearchString() { if (searchDir == 0) { return false; } String searchPrompt = searchDir < 0 ? "?" : "/"; Buffer searchBuffer = new BufferImpl(); KeyMap keyMap = keyMaps.get(MAIN); if (keyMap == null) { keyMap = keyMaps.get(SAFE); } while (true) { post = () -> new AttributedString(searchPrompt + searchBuffer.toString() + "_"); redisplay(); Binding b = bindingReader.readBinding(keyMap); if (b instanceof Reference) { String func = ((Reference) b).name(); switch (func) { case SEND_BREAK: post = null; return false; case ACCEPT_LINE: case VI_CMD_MODE: searchString = searchBuffer.toString(); post = null; return true; case MAGIC_SPACE: searchBuffer.write(' '); break; case REDISPLAY: redisplay(); break; case CLEAR_SCREEN: clearScreen(); break; case SELF_INSERT: searchBuffer.write(getLastBinding()); break; case SELF_INSERT_UNMETA: if (getLastBinding().charAt(0) == '\u001b') { String s = getLastBinding().substring(1); if ("\r".equals(s)) { s = "\n"; } searchBuffer.write(s); } break; case BACKWARD_DELETE_CHAR: case VI_BACKWARD_DELETE_CHAR: if (searchBuffer.length() > 0) { searchBuffer.backspace(); } break; case BACKWARD_KILL_WORD: case VI_BACKWARD_KILL_WORD: if (searchBuffer.length() > 0 && !isWhitespace(searchBuffer.prevChar())) { searchBuffer.backspace(); } if (searchBuffer.length() > 0 && isWhitespace(searchBuffer.prevChar())) { searchBuffer.backspace(); } break; case QUOTED_INSERT: case VI_QUOTED_INSERT: int c = readCharacter(); if (c >= 0) { searchBuffer.write(c); } else { beep(); } break; default: beep(); break; } } } } protected boolean insertCloseCurly() { return insertClose("}"); } protected boolean insertCloseParen() { return insertClose(")"); } protected boolean insertCloseSquare() { return insertClose("]"); } protected boolean insertClose(String s) { putString(s); int closePosition = buf.cursor(); buf.move(-1); doViMatchBracket(); redisplay(); peekCharacter(getLong(BLINK_MATCHING_PAREN, DEFAULT_BLINK_MATCHING_PAREN)); buf.cursor(closePosition); return true; } protected boolean viMatchBracket() { return doViMatchBracket(); } protected boolean undefinedKey() { return false; } /** * Implements vi style bracket matching ("%" command). The matching * bracket for the current bracket type that you are sitting on is matched. * The logic works like so: * @return true if it worked, false if the cursor was not on a bracket * character or if there was no matching bracket. */ protected boolean doViMatchBracket() { int pos = buf.cursor(); if (pos == buf.length()) { return false; } int type = getBracketType(buf.atChar(pos)); int move = (type < 0) ? -1 : 1; int count = 1; if (type == 0) return false; while (count > 0) { pos += move; // Fell off the start or end. if (pos < 0 || pos >= buf.length()) { return false; } int curType = getBracketType(buf.atChar(pos)); if (curType == type) { ++count; } else if (curType == -type) { --count; } } /* * Slight adjustment for delete-to, yank-to, change-to to ensure * that the matching paren is consumed */ if (move > 0 && isInViMoveOperation()) ++pos; buf.cursor(pos); return true; } /** * Given a character determines what type of bracket it is (paren, * square, curly, or none). * @param ch The character to check * @return 1 is square, 2 curly, 3 parent, or zero for none. The value * will be negated if it is the closing form of the bracket. */ protected int getBracketType (int ch) { switch (ch) { case '[': return 1; case ']': return -1; case '{': return 2; case '}': return -2; case '(': return 3; case ')': return -3; default: return 0; } } /** * Performs character transpose. The character prior to the cursor and the * character under the cursor are swapped and the cursor is advanced one. * Do not cross line breaks. */ protected boolean transposeChars() { int lstart = buf.cursor() - 1; int lend = buf.cursor(); while (buf.atChar(lstart) != 0 && buf.atChar(lstart) != '\n') { lstart--; } lstart++; while (buf.atChar(lend) != 0 && buf.atChar(lend) != '\n') { lend++; } if (lend - lstart < 2) { return false; } boolean neg = this.count < 0; for (int count = Math.max(this.count, -this.count); count > 0; --count) { while (buf.cursor() <= lstart) { buf.move(1); } while (buf.cursor() >= lend) { buf.move(-1); } int c = buf.currChar(); buf.currChar(buf.prevChar()); buf.move(-1); buf.currChar(c); buf.move(neg ? 0 : 2); } return true; } protected boolean undo() { isUndo = true; if (undo.canUndo()) { undo.undo(); return true; } return false; } protected boolean redo() { isUndo = true; if (undo.canRedo()) { undo.redo(); return true; } return false; } protected boolean sendBreak() { if (searchTerm == null) { buf.clear(); println(); redrawLine(); // state = State.INTERRUPT; return false; } return true; } protected boolean backwardChar() { return buf.move(-count) != 0; } protected boolean forwardChar() { return buf.move(count) != 0; } protected boolean viDigitOrBeginningOfLine() { if (repeatCount > 0) { return digitArgument(); } else { return beginningOfLine(); } } protected boolean universalArgument() { mult *= universal; isArgDigit = true; return true; } protected boolean argumentBase() { if (repeatCount > 0 && repeatCount < 32) { universal = repeatCount; isArgDigit = true; return true; } else { return false; } } protected boolean negArgument() { mult *= -1; isArgDigit = true; return true; } protected boolean digitArgument() { String s = getLastBinding(); repeatCount = (repeatCount * 10) + s.charAt(s.length() - 1) - '0'; isArgDigit = true; return true; } protected boolean viDelete() { int cursorStart = buf.cursor(); Binding o = readBinding(getKeys()); if (o instanceof Reference) { // TODO: be smarter on how to get the vi range String op = viDeleteChangeYankToRemap(((Reference) o).name()); // This is a weird special case. In vi // "dd" deletes the current line. So if we // get a delete-to, followed by a delete-to, // we delete the line. if (VI_DELETE.equals(op)) { killWholeLine(); } else { viMoveMode = ViMoveMode.DELETE; Widget widget = widgets.get(op); if (widget != null && !widget.apply()) { viMoveMode = ViMoveMode.NORMAL; return false; } viMoveMode = ViMoveMode.NORMAL; } return viDeleteTo(cursorStart, buf.cursor()); } else { pushBackBinding(); return false; } } protected boolean viYankTo() { int cursorStart = buf.cursor(); Binding o = readBinding(getKeys()); if (o instanceof Reference) { // TODO: be smarter on how to get the vi range String op = viDeleteChangeYankToRemap(((Reference) o).name()); // Similar to delete-to, a "yy" yanks the whole line. if (VI_YANK.equals(op)) { yankBuffer = buf.toString(); return true; } else { viMoveMode = ViMoveMode.YANK; Widget widget = widgets.get(op); if (widget != null && !widget.apply()) { return false; } viMoveMode = ViMoveMode.NORMAL; } return viYankTo(cursorStart, buf.cursor()); } else { pushBackBinding(); return false; } } protected boolean viYankWholeLine() { int s, e; int p = buf.cursor(); while (buf.move(-1) == -1 && buf.prevChar() != '\n') ; s = buf.cursor(); for (int i = 0; i < repeatCount; i++) { while (buf.move(1) == 1 && buf.prevChar() != '\n') ; } e = buf.cursor(); yankBuffer = buf.substring(s, e); if (!yankBuffer.endsWith("\n")) { yankBuffer += "\n"; } buf.cursor(p); return true; } protected boolean viChange() { int cursorStart = buf.cursor(); Binding o = readBinding(getKeys()); if (o instanceof Reference) { // TODO: be smarter on how to get the vi range String op = viDeleteChangeYankToRemap(((Reference) o).name()); // change whole line if (VI_CHANGE.equals(op)) { killWholeLine(); } else { viMoveMode = ViMoveMode.CHANGE; Widget widget = widgets.get(op); if (widget != null && !widget.apply()) { viMoveMode = ViMoveMode.NORMAL; return false; } viMoveMode = ViMoveMode.NORMAL; } boolean res = viChange(cursorStart, buf.cursor()); setKeyMap(VIINS); return res; } else { pushBackBinding(); return false; } } /* protected int getViRange(Reference cmd, ViMoveMode mode) { Buffer buffer = buf.copy(); int oldMark = mark; int pos = buf.cursor(); String bind = getLastBinding(); if (visual != 0) { if (buf.length() == 0) { return -1; } pos = mark; v } else { viMoveMode = mode; mark = -1; Binding b = bindingReader.readBinding(getKeys(), keyMaps.get(VIOPP)); if (b == null || new Reference(SEND_BREAK).equals(b)) { viMoveMode = ViMoveMode.NORMAL; mark = oldMark; return -1; } if (cmd.equals(b)) { doViLineRange(); } Widget w = getWidget(b); if (w ) if (b instanceof Reference) { } } } */ protected void cleanup() { buf.cursor(buf.length()); post = null; if (size.getColumns() > 0 || size.getRows() > 0) { redisplay(false); println(); terminal.puts(Capability.keypad_local); terminal.trackMouse(Terminal.MouseTracking.Off); flush(); } history.moveToEnd(); } protected boolean historyIncrementalSearchForward() { return doSearchHistory(false); } protected boolean historyIncrementalSearchBackward() { return doSearchHistory(true); } protected boolean doSearchHistory(boolean backward) { if (history.isEmpty()) { return false; } Buffer originalBuffer = buf.copy(); String previousSearchTerm = (searchTerm != null) ? searchTerm.toString() : ""; searchTerm = new StringBuffer(buf.toString()); if (searchTerm.length() > 0) { searchIndex = backward ? searchBackwards(searchTerm.toString(), history.index(), false) : searchForwards(searchTerm.toString(), history.index(), false); if (searchIndex == -1) { beep(); } printSearchStatus(searchTerm.toString(), searchIndex > -1 ? history.get(searchIndex) : "", backward); } else { searchIndex = -1; printSearchStatus("", "", backward); } redisplay(); KeyMap terminators = new KeyMap<>(); getString(SEARCH_TERMINATORS, DEFAULT_SEARCH_TERMINATORS) .codePoints().forEach(c -> bind(terminators, ACCEPT_LINE, new String(Character.toChars(c)))); try { while (true) { Binding o = readBinding(getKeys(), terminators); if (new Reference(SEND_BREAK).equals(o)) { buf.copyFrom(originalBuffer); return true; } else if (new Reference(HISTORY_INCREMENTAL_SEARCH_BACKWARD).equals(o)) { backward = true; if (searchTerm.length() == 0) { searchTerm.append(previousSearchTerm); } if (searchIndex > 0) { searchIndex = searchBackwards(searchTerm.toString(), searchIndex, false); } } else if (new Reference(HISTORY_INCREMENTAL_SEARCH_FORWARD).equals(o)) { backward = false; if (searchTerm.length() == 0) { searchTerm.append(previousSearchTerm); } if (searchIndex > -1 && searchIndex < history.size() - 1) { searchIndex = searchForwards(searchTerm.toString(), searchIndex, false); } } else if (new Reference(BACKWARD_DELETE_CHAR).equals(o)) { if (searchTerm.length() > 0) { searchTerm.deleteCharAt(searchTerm.length() - 1); if (backward) { searchIndex = searchBackwards(searchTerm.toString(), history.index(), false); } else { searchIndex = searchForwards(searchTerm.toString(), history.index(), false); } } } else if (new Reference(SELF_INSERT).equals(o)) { searchTerm.append(getLastBinding()); if (backward) { searchIndex = searchBackwards(searchTerm.toString(), history.index(), false); } else { searchIndex = searchForwards(searchTerm.toString(), history.index(), false); } } else { // Set buffer and cursor position to the found string. if (searchIndex != -1) { history.moveTo(searchIndex); } pushBackBinding(); return true; } // print the search status if (searchTerm.length() == 0) { printSearchStatus("", "", backward); searchIndex = -1; } else { if (searchIndex == -1) { beep(); printSearchStatus(searchTerm.toString(), "", backward); } else { printSearchStatus(searchTerm.toString(), history.get(searchIndex), backward); } } redisplay(); } } finally { searchTerm = null; searchIndex = -1; post = null; } } private void pushBackBinding() { pushBackBinding(false); } private void pushBackBinding(boolean skip) { String s = getLastBinding(); if (s != null) { bindingReader.runMacro(s); skipRedisplay = skip; } } protected boolean historySearchForward() { if (historyBuffer == null || buf.length() == 0 || !buf.toString().equals(history.current())) { historyBuffer = buf.copy(); searchBuffer = getFirstWord(); } int index = history.index() + 1; if (index < history.last() + 1) { int searchIndex = searchForwards(searchBuffer.toString(), index, true); if (searchIndex == -1) { history.moveToEnd(); if (!buf.toString().equals(historyBuffer.toString())) { setBuffer(historyBuffer.toString()); historyBuffer = null; } else { return false; } } else { // Maintain cursor position while searching. if (history.moveTo(searchIndex)) { setBuffer(history.current()); } else { history.moveToEnd(); setBuffer(historyBuffer.toString()); return false; } } } else { history.moveToEnd(); if (!buf.toString().equals(historyBuffer.toString())) { setBuffer(historyBuffer.toString()); historyBuffer = null; } else { return false; } } return true; } private CharSequence getFirstWord() { String s = buf.toString(); int i = 0; while (i < s.length() && !Character.isWhitespace(s.charAt(i))) { i++; } return s.substring(0, i); } protected boolean historySearchBackward() { if (historyBuffer == null || buf.length() == 0 || !buf.toString().equals(history.current())) { historyBuffer = buf.copy(); searchBuffer = getFirstWord(); } int searchIndex = searchBackwards(searchBuffer.toString(), history.index(), true); if (searchIndex == -1) { return false; } else { // Maintain cursor position while searching. if (history.moveTo(searchIndex)) { setBuffer(history.current()); } else { return false; } } return true; } // // History search // /** * Search backward in history from a given position. * * @param searchTerm substring to search for. * @param startIndex the index from which on to search * @return index where this substring has been found, or -1 else. */ public int searchBackwards(String searchTerm, int startIndex) { return searchBackwards(searchTerm, startIndex, false); } /** * Search backwards in history from the current position. * * @param searchTerm substring to search for. * @return index where the substring has been found, or -1 else. */ public int searchBackwards(String searchTerm) { return searchBackwards(searchTerm, history.index(), false); } public int searchBackwards(String searchTerm, int startIndex, boolean startsWith) { ListIterator it = history.iterator(startIndex); while (it.hasPrevious()) { History.Entry e = it.previous(); if (startsWith) { if (e.line().startsWith(searchTerm)) { return e.index(); } } else { if (e.line().contains(searchTerm)) { return e.index(); } } } return -1; } public int searchForwards(String searchTerm, int startIndex, boolean startsWith) { if (startIndex > history.last()) { startIndex = history.last(); } ListIterator it = history.iterator(startIndex); if (searchIndex != -1 && it.hasNext()) { it.next(); } while (it.hasNext()) { History.Entry e = it.next(); if (startsWith) { if (e.line().startsWith(searchTerm)) { return e.index(); } } else { if (e.line().contains(searchTerm)) { return e.index(); } } } return -1; } /** * Search forward in history from a given position. * * @param searchTerm substring to search for. * @param startIndex the index from which on to search * @return index where this substring has been found, or -1 else. */ public int searchForwards(String searchTerm, int startIndex) { return searchForwards(searchTerm, startIndex, false); } /** * Search forwards in history from the current position. * * @param searchTerm substring to search for. * @return index where the substring has been found, or -1 else. */ public int searchForwards(String searchTerm) { return searchForwards(searchTerm, history.index()); } public void printSearchStatus(String searchTerm, String match, boolean backward) { String searchLabel = backward ? "bck-i-search" : "i-search"; post = () -> new AttributedString(searchLabel + ": " + searchTerm + "_"); setBuffer(match); buf.move(match.indexOf(searchTerm) - buf.cursor()); } protected boolean quit() { getBuffer().clear(); return acceptLine(); } protected boolean acceptLine() { parsedLine = null; if (!isSet(Option.DISABLE_EVENT_EXPANSION)) { try { String str = buf.toString(); String exp = expander.expandHistory(history, str); if (!exp.equals(str)) { buf.clear(); buf.write(exp); if (isSet(Option.HISTORY_VERIFY)) { return true; } } } catch (IllegalArgumentException e) { // Ignore } } try { parsedLine = parser.parse(buf.toString(), buf.cursor(), ParseContext.ACCEPT_LINE); } catch (EOFError e) { buf.write("\n"); return true; } catch (SyntaxError e) { // do nothing } callWidget(CALLBACK_FINISH); state = State.DONE; return true; } protected boolean selfInsert() { for (int count = this.count; count > 0; count--) { putString(getLastBinding()); } return true; } protected boolean selfInsertUnmeta() { if (getLastBinding().charAt(0) == '\u001b') { String s = getLastBinding().substring(1); if ("\r".equals(s)) { s = "\n"; } for (int count = this.count; count > 0; count--) { putString(s); } return true; } else { return false; } } protected boolean overwriteMode() { overTyping = !overTyping; return true; } // // History Control // protected boolean beginningOfBufferOrHistory() { if (findbol() != 0) { buf.cursor(0); return true; } else { return beginningOfHistory(); } } protected boolean beginningOfHistory() { if (history.moveToFirst()) { setBuffer(history.current()); return true; } else { return false; } } protected boolean endOfBufferOrHistory() { if (findeol() != buf.length()) { buf.cursor(buf.length()); return true; } else { return endOfHistory(); } } protected boolean endOfHistory() { if (history.moveToLast()) { setBuffer(history.current()); return true; } else { return false; } } protected boolean beginningOfLineHist() { if (count < 0) { return callNeg(this::endOfLineHist); } while (count-- > 0) { int bol = findbol(); if (bol != buf.cursor()) { buf.cursor(bol); } else { moveHistory(false); buf.cursor(0); } } return true; } protected boolean endOfLineHist() { if (count < 0) { return callNeg(this::beginningOfLineHist); } while (count-- > 0) { int eol = findeol(); if (eol != buf.cursor()) { buf.cursor(eol); } else { moveHistory(true); } } return true; } protected boolean upHistory() { while (count-- > 0) { if (!moveHistory(false)) { return !isSet(Option.HISTORY_BEEP); } } return true; } protected boolean downHistory() { while (count-- > 0) { if (!moveHistory(true)) { return !isSet(Option.HISTORY_BEEP); } } return true; } protected boolean viUpLineOrHistory() { return upLine() || upHistory() && viFirstNonBlank(); } protected boolean viDownLineOrHistory() { return downLine() || downHistory() && viFirstNonBlank(); } protected boolean upLine() { return buf.up(); } protected boolean downLine() { return buf.down(); } protected boolean upLineOrHistory() { return upLine() || upHistory(); } protected boolean upLineOrSearch() { return upLine() || historySearchBackward(); } protected boolean downLineOrHistory() { return downLine() || downHistory(); } protected boolean downLineOrSearch() { return downLine() || historySearchForward(); } protected boolean viCmdMode() { // If we are re-entering move mode from an // aborted yank-to, delete-to, change-to then // don't move the cursor back. The cursor is // only move on an explicit entry to movement // mode. if (state == State.NORMAL) { buf.move(-1); } return setKeyMap(VICMD); } protected boolean viInsert() { return setKeyMap(VIINS); } protected boolean viAddNext() { buf.move(1); return setKeyMap(VIINS); } protected boolean viAddEol() { return endOfLine() && setKeyMap(VIINS); } protected boolean emacsEditingMode() { return setKeyMap(EMACS); } protected boolean viChangeWholeLine() { return viFirstNonBlank() && viChangeEol(); } protected boolean viChangeEol() { return viChange(buf.cursor(), buf.length()) && setKeyMap(VIINS); } protected boolean viKillEol() { int eol = findeol(); if (buf.cursor() == eol) { return false; } killRing.add(buf.substring(buf.cursor(), eol)); buf.delete(eol - buf.cursor()); return true; } protected boolean quotedInsert() { int c = readCharacter(); while (count-- > 0) { putString(new String(Character.toChars(c))); } return true; } protected boolean viJoin() { if (buf.down()) { while (buf.move(-1) == -1 && buf.prevChar() != '\n') ; buf.backspace(); buf.write(' '); buf.move(-1); return true; } return false; } protected boolean viKillWholeLine() { return killWholeLine() && setKeyMap(VIINS); } protected boolean viInsertBol() { return beginningOfLine() && setKeyMap(VIINS); } protected boolean backwardDeleteChar() { if (count < 0) { return callNeg(this::deleteChar); } if (buf.cursor() == 0) { return false; } buf.backspace(count); return true; } protected boolean viFirstNonBlank() { beginningOfLine(); while (buf.cursor() < buf.length() && isWhitespace(buf.currChar())) { buf.move(1); } return true; } protected boolean viBeginningOfLine() { buf.cursor(findbol()); return true; } protected boolean viEndOfLine() { if (count < 0) { return false; } while (count-- > 0) { buf.cursor(findeol() + 1); } buf.move(-1); return true; } protected boolean beginningOfLine() { while (count-- > 0) { while (buf.move(-1) == -1 && buf.prevChar() != '\n') ; } return true; } protected boolean endOfLine() { while (count-- > 0) { while (buf.move(1) == 1 && buf.currChar() != '\n') ; } return true; } protected boolean deleteChar() { if (count < 0) { return callNeg(this::backwardDeleteChar); } if (buf.cursor() == buf.length()) { return false; } buf.delete(count); return true; } /** * Deletes the previous character from the cursor position */ protected boolean viBackwardDeleteChar() { for (int i = 0; i < count; i++) { if (!buf.backspace()) { return false; } } return true; } /** * Deletes the character you are sitting on and sucks the rest of * the line in from the right. */ protected boolean viDeleteChar() { for (int i = 0; i < count; i++) { if (!buf.delete()) { return false; } } return true; } /** * Switches the case of the current character from upper to lower * or lower to upper as necessary and advances the cursor one * position to the right. */ protected boolean viSwapCase() { for (int i = 0; i < count; i++) { if (buf.cursor() < buf.length()) { int ch = buf.atChar(buf.cursor()); ch = switchCase(ch); buf.currChar(ch); buf.move(1); } else { return false; } } return true; } /** * Implements the vi change character command (in move-mode "r" * followed by the character to change to). */ protected boolean viReplaceChars() { int c = readCharacter(); // EOF, ESC, or CTRL-C aborts. if (c < 0 || c == '\033' || c == '\003') { return true; } for (int i = 0; i < count; i++) { if (buf.currChar((char) c)) { if (i < count - 1) { buf.move(1); } } else { return false; } } return true; } protected boolean viChange(int startPos, int endPos) { return doViDeleteOrChange(startPos, endPos, true); } protected boolean viDeleteTo(int startPos, int endPos) { return doViDeleteOrChange(startPos, endPos, false); } /** * Performs the vi "delete-to" action, deleting characters between a given * span of the input line. * @param startPos The start position * @param endPos The end position. * @param isChange If true, then the delete is part of a change operationg * (e.g. "c$" is change-to-end-of line, so we first must delete to end * of line to start the change * @return true if it succeeded, false otherwise */ protected boolean doViDeleteOrChange(int startPos, int endPos, boolean isChange) { if (startPos == endPos) { return true; } if (endPos < startPos) { int tmp = endPos; endPos = startPos; startPos = tmp; } buf.cursor(startPos); buf.delete(endPos - startPos); // If we are doing a delete operation (e.g. "d$") then don't leave the // cursor dangling off the end. In reality the "isChange" flag is silly // what is really happening is that if we are in "move-mode" then the // cursor can't be moved off the end of the line, but in "edit-mode" it // is ok, but I have no easy way of knowing which mode we are in. if (! isChange && startPos > 0 && startPos == buf.length()) { buf.move(-1); } return true; } /** * Implement the "vi" yank-to operation. This operation allows you * to yank the contents of the current line based upon a move operation, * for example "yw" yanks the current word, "3yw" yanks 3 words, etc. * * @param startPos The starting position from which to yank * @param endPos The ending position to which to yank * @return true if the yank succeeded */ protected boolean viYankTo(int startPos, int endPos) { int cursorPos = startPos; if (endPos < startPos) { int tmp = endPos; endPos = startPos; startPos = tmp; } if (startPos == endPos) { yankBuffer = ""; return true; } yankBuffer = buf.substring(startPos, endPos); /* * It was a movement command that moved the cursor to find the * end position, so put the cursor back where it started. */ buf.cursor(cursorPos); return true; } protected boolean viOpenLineAbove() { while (buf.move(-1) == -1 && buf.prevChar() != '\n') ; buf.write('\n'); buf.move(-1); return setKeyMap(VIINS); } protected boolean viOpenLineBelow() { while (buf.move(1) == 1 && buf.currChar() != '\n') ; buf.write('\n'); return setKeyMap(VIINS); } /** * Pasts the yank buffer to the right of the current cursor position * and moves the cursor to the end of the pasted region. */ protected boolean viPutAfter() { if (yankBuffer.indexOf('\n') >= 0) { while (buf.move(1) == 1 && buf.currChar() != '\n'); buf.move(1); putString(yankBuffer); buf.move(- yankBuffer.length()); } else if (yankBuffer.length () != 0) { if (buf.cursor() < buf.length()) { buf.move(1); } for (int i = 0; i < count; i++) { putString(yankBuffer); } buf.move(-1); } return true; } protected boolean viPutBefore() { if (yankBuffer.indexOf('\n') >= 0) { while (buf.move(-1) == -1 && buf.prevChar() != '\n'); putString(yankBuffer); buf.move(- yankBuffer.length()); } else if (yankBuffer.length () != 0) { if (buf.cursor() > 0) { buf.move(-1); } for (int i = 0; i < count; i++) { putString(yankBuffer); } buf.move(-1); } return true; } protected boolean doLowercaseVersion() { bindingReader.runMacro(getLastBinding().toLowerCase()); return true; } protected boolean setMarkCommand() { if (count < 0) { regionActive = RegionType.NONE; return true; } regionMark = buf.cursor(); regionActive = RegionType.CHAR; return true; } protected boolean exchangePointAndMark() { if (count == 0) { regionActive = RegionType.CHAR; return true; } int x = regionMark; regionMark = buf.cursor(); buf.cursor(x); if (buf.cursor() > buf.length()) { buf.cursor(buf.length()); } if (count > 0) { regionActive = RegionType.CHAR; } return true; } protected boolean visualMode() { if (isInViMoveOperation()) { isArgDigit = true; forceLine = false; forceChar = true; return true; } if (regionActive == RegionType.NONE) { regionMark = buf.cursor(); regionActive = RegionType.CHAR; } else if (regionActive == RegionType.CHAR) { regionActive = RegionType.NONE; } else if (regionActive == RegionType.LINE) { regionActive = RegionType.CHAR; } return true; } protected boolean visualLineMode() { if (isInViMoveOperation()) { isArgDigit = true; forceLine = true; forceChar = false; return true; } if (regionActive == RegionType.NONE) { regionMark = buf.cursor(); regionActive = RegionType.LINE; } else if (regionActive == RegionType.CHAR) { regionActive = RegionType.LINE; } else if (regionActive == RegionType.LINE) { regionActive = RegionType.NONE; } return true; } protected boolean deactivateRegion() { regionActive = RegionType.NONE; return true; } protected boolean whatCursorPosition() { post = () -> { AttributedStringBuilder sb = new AttributedStringBuilder(); if (buf.cursor() < buf.length()) { int c = buf.currChar(); sb.append("Char: "); if (c == ' ') { sb.append("SPC"); } else if (c == '\n') { sb.append("LFD"); } else if (c < 32) { sb.append('^'); sb.append((char) (c + 'A' - 1)); } else if (c == 127) { sb.append("^?"); } else { sb.append((char) c); } sb.append(" ("); sb.append("0").append(Integer.toOctalString(c)).append(" "); sb.append(Integer.toString(c)).append(" "); sb.append("0x").append(Integer.toHexString(c)).append(" "); sb.append(")"); } else { sb.append("EOF"); } sb.append(" "); sb.append("point "); sb.append(Integer.toString(buf.cursor() + 1)); sb.append(" of "); sb.append(Integer.toString(buf.length() + 1)); sb.append(" ("); sb.append(Integer.toString(buf.length() == 0 ? 100 : ((100 * buf.cursor()) / buf.length()))); sb.append("%)"); sb.append(" "); sb.append("column "); sb.append(Integer.toString(buf.cursor() - findbol())); return sb.toAttributedString(); }; return true; } protected Map builtinWidgets() { Map widgets = new HashMap<>(); widgets.put(ACCEPT_LINE, this::acceptLine); widgets.put(ARGUMENT_BASE, this::argumentBase); widgets.put(BACKWARD_CHAR, this::backwardChar); widgets.put(BACKWARD_DELETE_CHAR, this::backwardDeleteChar); widgets.put(BACKWARD_DELETE_WORD, this::backwardDeleteWord); widgets.put(BACKWARD_KILL_LINE, this::backwardKillLine); widgets.put(BACKWARD_KILL_WORD, this::backwardKillWord); widgets.put(BACKWARD_WORD, this::backwardWord); widgets.put(BEEP, this::beep); widgets.put(BEGINNING_OF_BUFFER_OR_HISTORY, this::beginningOfBufferOrHistory); widgets.put(BEGINNING_OF_HISTORY, this::beginningOfHistory); widgets.put(BEGINNING_OF_LINE, this::beginningOfLine); widgets.put(BEGINNING_OF_LINE_HIST, this::beginningOfLineHist); widgets.put(CAPITALIZE_WORD, this::capitalizeWord); widgets.put(CLEAR, this::clear); widgets.put(CLEAR_SCREEN, this::clearScreen); widgets.put(COMPLETE_PREFIX, this::completePrefix); widgets.put(COMPLETE_WORD, this::completeWord); widgets.put(COPY_PREV_WORD, this::copyPrevWord); widgets.put(COPY_REGION_AS_KILL, this::copyRegionAsKill); widgets.put(DELETE_CHAR, this::deleteChar); widgets.put(DELETE_CHAR_OR_LIST, this::deleteCharOrList); widgets.put(DELETE_WORD, this::deleteWord); widgets.put(DIGIT_ARGUMENT, this::digitArgument); widgets.put(DO_LOWERCASE_VERSION, this::doLowercaseVersion); widgets.put(DOWN_CASE_WORD, this::downCaseWord); widgets.put(DOWN_LINE, this::downLine); widgets.put(DOWN_LINE_OR_HISTORY, this::downLineOrHistory); widgets.put(DOWN_LINE_OR_SEARCH, this::downLineOrSearch); widgets.put(DOWN_HISTORY, this::downHistory); widgets.put(EMACS_EDITING_MODE, this::emacsEditingMode); widgets.put(EMACS_BACKWARD_WORD, this::emacsBackwardWord); widgets.put(EMACS_FORWARD_WORD, this::emacsForwardWord); widgets.put(END_OF_BUFFER_OR_HISTORY, this::endOfBufferOrHistory); widgets.put(END_OF_HISTORY, this::endOfHistory); widgets.put(END_OF_LINE, this::endOfLine); widgets.put(END_OF_LINE_HIST, this::endOfLineHist); widgets.put(EXCHANGE_POINT_AND_MARK, this::exchangePointAndMark); widgets.put(EXPAND_HISTORY, this::expandHistory); widgets.put(EXPAND_OR_COMPLETE, this::expandOrComplete); widgets.put(EXPAND_OR_COMPLETE_PREFIX, this::expandOrCompletePrefix); widgets.put(EXPAND_WORD, this::expandWord); widgets.put(FRESH_LINE, this::freshLine); widgets.put(FORWARD_CHAR, this::forwardChar); widgets.put(FORWARD_WORD, this::forwardWord); widgets.put(HISTORY_INCREMENTAL_SEARCH_BACKWARD, this::historyIncrementalSearchBackward); widgets.put(HISTORY_INCREMENTAL_SEARCH_FORWARD, this::historyIncrementalSearchForward); widgets.put(HISTORY_SEARCH_BACKWARD, this::historySearchBackward); widgets.put(HISTORY_SEARCH_FORWARD, this::historySearchForward); widgets.put(INSERT_CLOSE_CURLY, this::insertCloseCurly); widgets.put(INSERT_CLOSE_PAREN, this::insertCloseParen); widgets.put(INSERT_CLOSE_SQUARE, this::insertCloseSquare); widgets.put(INSERT_COMMENT, this::insertComment); widgets.put(KILL_BUFFER, this::killBuffer); widgets.put(KILL_LINE, this::killLine); widgets.put(KILL_REGION, this::killRegion); widgets.put(KILL_WHOLE_LINE, this::killWholeLine); widgets.put(KILL_WORD, this::killWord); widgets.put(LIST_CHOICES, this::listChoices); widgets.put(MENU_COMPLETE, this::menuComplete); widgets.put(MENU_EXPAND_OR_COMPLETE, this::menuExpandOrComplete); widgets.put(NEG_ARGUMENT, this::negArgument); widgets.put(OVERWRITE_MODE, this::overwriteMode); // widgets.put(QUIT, this::quit); widgets.put(QUOTED_INSERT, this::quotedInsert); widgets.put(REDISPLAY, this::redisplay); widgets.put(REDRAW_LINE, this::redrawLine); widgets.put(REDO, this::redo); widgets.put(SELF_INSERT, this::selfInsert); widgets.put(SELF_INSERT_UNMETA, this::selfInsertUnmeta); widgets.put(SEND_BREAK, this::sendBreak); widgets.put(SET_MARK_COMMAND, this::setMarkCommand); widgets.put(TRANSPOSE_CHARS, this::transposeChars); widgets.put(TRANSPOSE_WORDS, this::transposeWords); widgets.put(UNDEFINED_KEY, this::undefinedKey); widgets.put(UNIVERSAL_ARGUMENT, this::universalArgument); widgets.put(UNDO, this::undo); widgets.put(UP_CASE_WORD, this::upCaseWord); widgets.put(UP_HISTORY, this::upHistory); widgets.put(UP_LINE, this::upLine); widgets.put(UP_LINE_OR_HISTORY, this::upLineOrHistory); widgets.put(UP_LINE_OR_SEARCH, this::upLineOrSearch); widgets.put(VI_ADD_EOL, this::viAddEol); widgets.put(VI_ADD_NEXT, this::viAddNext); widgets.put(VI_BACKWARD_CHAR, this::viBackwardChar); widgets.put(VI_BACKWARD_DELETE_CHAR, this::viBackwardDeleteChar); widgets.put(VI_BACKWARD_BLANK_WORD, this::viBackwardBlankWord); widgets.put(VI_BACKWARD_BLANK_WORD_END, this::viBackwardBlankWordEnd); widgets.put(VI_BACKWARD_KILL_WORD, this::viBackwardKillWord); widgets.put(VI_BACKWARD_WORD, this::viBackwardWord); widgets.put(VI_BACKWARD_WORD_END, this::viBackwardWordEnd); widgets.put(VI_BEGINNING_OF_LINE, this::viBeginningOfLine); widgets.put(VI_CMD_MODE, this::viCmdMode); widgets.put(VI_DIGIT_OR_BEGINNING_OF_LINE, this::viDigitOrBeginningOfLine); widgets.put(VI_DOWN_LINE_OR_HISTORY, this::viDownLineOrHistory); widgets.put(VI_CHANGE, this::viChange); widgets.put(VI_CHANGE_EOL, this::viChangeEol); widgets.put(VI_CHANGE_WHOLE_LINE, this::viChangeWholeLine); widgets.put(VI_DELETE_CHAR, this::viDeleteChar); widgets.put(VI_DELETE, this::viDelete); widgets.put(VI_END_OF_LINE, this::viEndOfLine); widgets.put(VI_KILL_EOL, this::viKillEol); widgets.put(VI_FIRST_NON_BLANK, this::viFirstNonBlank); widgets.put(VI_FIND_NEXT_CHAR, this::viFindNextChar); widgets.put(VI_FIND_NEXT_CHAR_SKIP, this::viFindNextCharSkip); widgets.put(VI_FIND_PREV_CHAR, this::viFindPrevChar); widgets.put(VI_FIND_PREV_CHAR_SKIP, this::viFindPrevCharSkip); widgets.put(VI_FORWARD_BLANK_WORD, this::viForwardBlankWord); widgets.put(VI_FORWARD_BLANK_WORD_END, this::viForwardBlankWordEnd); widgets.put(VI_FORWARD_CHAR, this::viForwardChar); widgets.put(VI_FORWARD_WORD, this::viForwardWord); widgets.put(VI_FORWARD_WORD, this::viForwardWord); widgets.put(VI_FORWARD_WORD_END, this::viForwardWordEnd); widgets.put(VI_HISTORY_SEARCH_BACKWARD, this::viHistorySearchBackward); widgets.put(VI_HISTORY_SEARCH_FORWARD, this::viHistorySearchForward); widgets.put(VI_INSERT, this::viInsert); widgets.put(VI_INSERT_BOL, this::viInsertBol); widgets.put(VI_INSERT_COMMENT, this::viInsertComment); widgets.put(VI_JOIN, this::viJoin); widgets.put(VI_KILL_LINE, this::viKillWholeLine); widgets.put(VI_MATCH_BRACKET, this::viMatchBracket); widgets.put(VI_OPEN_LINE_ABOVE, this::viOpenLineAbove); widgets.put(VI_OPEN_LINE_BELOW, this::viOpenLineBelow); widgets.put(VI_PUT_AFTER, this::viPutAfter); widgets.put(VI_PUT_BEFORE, this::viPutBefore); widgets.put(VI_REPEAT_FIND, this::viRepeatFind); widgets.put(VI_REPEAT_SEARCH, this::viRepeatSearch); widgets.put(VI_REPLACE_CHARS, this::viReplaceChars); widgets.put(VI_REV_REPEAT_FIND, this::viRevRepeatFind); widgets.put(VI_REV_REPEAT_SEARCH, this::viRevRepeatSearch); widgets.put(VI_SWAP_CASE, this::viSwapCase); widgets.put(VI_UP_LINE_OR_HISTORY, this::viUpLineOrHistory); widgets.put(VI_YANK, this::viYankTo); widgets.put(VI_YANK_WHOLE_LINE, this::viYankWholeLine); widgets.put(VISUAL_LINE_MODE, this::visualLineMode); widgets.put(VISUAL_MODE, this::visualMode); widgets.put(WHAT_CURSOR_POSITION, this::whatCursorPosition); widgets.put(YANK, this::yank); widgets.put(YANK_POP, this::yankPop); widgets.put(MOUSE, this::mouse); return widgets; } public boolean redisplay() { redisplay(true); return true; } protected void redisplay(boolean flush) { if (skipRedisplay) { skipRedisplay = false; return; } if (size.getRows() > 0 && size.getRows() < MIN_ROWS) { AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH); sb.append(prompt); concat(getHighlightedBuffer(buf.toString()).columnSplitLength(Integer.MAX_VALUE), sb); AttributedString full = sb.toAttributedString(); sb.setLength(0); sb.append(prompt); concat(getMaskedBuffer(buf.upToCursor()).columnSplitLength(Integer.MAX_VALUE), sb); AttributedString toCursor = sb.toAttributedString(); int w = WCWidth.wcwidth('…'); int width = size.getColumns(); int cursor = toCursor.columnLength(); int inc = width /2 + 1; while (cursor <= smallTerminalOffset + w) { smallTerminalOffset -= inc; } while (cursor >= smallTerminalOffset + width - w) { smallTerminalOffset += inc; } if (smallTerminalOffset > 0) { sb.setLength(0); sb.append("…"); sb.append(full.columnSubSequence(smallTerminalOffset + w, Integer.MAX_VALUE)); full = sb.toAttributedString(); } int length = full.columnLength(); if (length >= smallTerminalOffset + width) { sb.setLength(0); sb.append(full.columnSubSequence(0, width - w)); sb.append("…"); full = sb.toAttributedString(); } display.update(Collections.singletonList(full), cursor - smallTerminalOffset, flush); return; } List secondaryPrompts = new ArrayList<>(); AttributedString full = getDisplayedBufferWithPrompts(secondaryPrompts); List newLines; if (size.getColumns() <= 0) { newLines = new ArrayList<>(); newLines.add(full); } else { newLines = full.columnSplitLength(size.getColumns(), true, display.delayLineWrap()); } List rightPromptLines; if (rightPrompt.length() == 0 || size.getColumns() <= 0) { rightPromptLines = new ArrayList<>(); } else { rightPromptLines = rightPrompt.columnSplitLength(size.getColumns()); } while (newLines.size() < rightPromptLines.size()) { newLines.add(new AttributedString("")); } for (int i = 0; i < rightPromptLines.size(); i++) { AttributedString line = rightPromptLines.get(i); newLines.set(i, addRightPrompt(line, newLines.get(i))); } int cursorPos = -1; if (size.getColumns() > 0) { AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH); sb.append(prompt); String buffer = buf.upToCursor(); if (mask != null) { if (mask == NULL_MASK) { buffer = ""; } else { StringBuilder nsb = new StringBuilder(); for (int i = buffer.length(); i-- > 0; ) { nsb.append((char) mask); } buffer = nsb.toString(); } } sb.append(insertSecondaryPrompts(new AttributedString(buffer), secondaryPrompts, false)); List promptLines = sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); if (!promptLines.isEmpty()) { cursorPos = size.cursorPos(promptLines.size() - 1, promptLines.get(promptLines.size() - 1).columnLength()); } } display.update(newLines, cursorPos, flush); } private void concat(List lines, AttributedStringBuilder sb) { if (lines.size() > 1) { for (int i = 0; i < lines.size() - 1; i++) { sb.append(lines.get(i)); sb.style(sb.style().inverse()); sb.append("\\n"); sb.style(sb.style().inverseOff()); } } sb.append(lines.get(lines.size() - 1)); } private AttributedString getDisplayedBufferWithPrompts(List secondaryPrompts) { AttributedString attBuf = getHighlightedBuffer(buf.toString()); AttributedString tNewBuf = insertSecondaryPrompts(attBuf, secondaryPrompts); AttributedStringBuilder full = new AttributedStringBuilder().tabs(TAB_WIDTH); full.append(prompt); full.append(tNewBuf); if (post != null) { full.append("\n"); full.append(post.get()); } return full.toAttributedString(); } private AttributedString getMaskedBuffer(String buffer) { if (mask != null) { if (mask == NULL_MASK) { buffer = ""; } else { StringBuilder sb = new StringBuilder(); for (int i = buffer.length(); i-- > 0;) { sb.append((char) mask); } buffer = sb.toString(); } } return new AttributedString(buffer); } private AttributedString getHighlightedBuffer(String buffer) { if (mask != null) { return getMaskedBuffer(buffer); } else if (highlighter != null && !isSet(Option.DISABLE_HIGHLIGHTER)) { return highlighter.highlight(this, buffer); } else { return new AttributedString(buffer); } } private AttributedString expandPromptPattern(String pattern, int padToWidth, String message, int line) { ArrayList parts = new ArrayList<>(); boolean isHidden = false; int padPartIndex = -1; StringBuilder padPartString = null; StringBuilder sb = new StringBuilder(); // Add "%{" to avoid special case for end of string. pattern = pattern + "%{"; int plen = pattern.length(); int padChar = -1; int padPos = -1; int cols = 0; for (int i = 0; i < plen; ) { char ch = pattern.charAt(i++); if (ch == '%' && i < plen) { int count = 0; boolean countSeen = false; decode: while (true) { ch = pattern.charAt(i++); switch (ch) { case '{': case '}': String str = sb.toString(); AttributedString astr; if (!isHidden) { astr = AttributedString.fromAnsi(str); cols += astr.columnLength(); } else { astr = new AttributedString(str, AttributedStyle.HIDDEN); } if (padPartIndex == parts.size()) { padPartString = sb; if (i < plen) { sb = new StringBuilder(); } } else { sb.setLength(0); } parts.add(astr); isHidden = ch == '{'; break decode; case '%': sb.append(ch); break decode; case 'N': sb.append(getInt(LINE_OFFSET, 0) + line); break decode; case 'M': if (message != null) sb.append(message); break decode; case 'P': if (countSeen && count >= 0) padToWidth = count; if (i < plen) { padChar = pattern.charAt(i++); // FIXME check surrogate } padPos = sb.length(); padPartIndex = parts.size(); break decode; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': boolean neg = false; if (ch == '-') { neg = true; ch = pattern.charAt(i++); } countSeen = true; count = 0; while (ch >= '0' && ch <= '9') { count = (count < 0 ? 0 : 10 * count) + (ch - '0'); ch = pattern.charAt(i++); } if (neg) { count = -count; } i--; break; default: break decode; } } } else sb.append(ch); } if (padToWidth > cols) { int padCharCols = WCWidth.wcwidth(padChar); int padCount = (padToWidth - cols) / padCharCols; sb = padPartString; while (--padCount >= 0) sb.insert(padPos, (char) padChar); // FIXME if wide parts.set(padPartIndex, AttributedString.fromAnsi(sb.toString())); } return AttributedString.join(null, parts); } private AttributedString insertSecondaryPrompts(AttributedString str, List prompts) { return insertSecondaryPrompts(str, prompts, true); } private AttributedString insertSecondaryPrompts(AttributedString strAtt, List prompts, boolean computePrompts) { Objects.requireNonNull(prompts); List lines = strAtt.columnSplitLength(Integer.MAX_VALUE); AttributedStringBuilder sb = new AttributedStringBuilder(); String secondaryPromptPattern = getString(SECONDARY_PROMPT_PATTERN, DEFAULT_SECONDARY_PROMPT_PATTERN); boolean needsMessage = secondaryPromptPattern.contains("%M"); AttributedStringBuilder buf = new AttributedStringBuilder(); int width = 0; List missings = new ArrayList<>(); if (computePrompts && secondaryPromptPattern.contains("%P")) { width = prompt.columnLength(); for (int line = 0; line < lines.size() - 1; line++) { AttributedString prompt; buf.append(lines.get(line)).append("\n"); String missing = ""; if (needsMessage) { try { parser.parse(buf.toString(), buf.length(), ParseContext.SECONDARY_PROMPT); } catch (EOFError e) { missing = e.getMissing(); } catch (SyntaxError e) { // Ignore } } missings.add(missing); prompt = expandPromptPattern(secondaryPromptPattern, 0, missing, line + 1); width = Math.max(width, prompt.columnLength()); } buf.setLength(0); } int line = 0; while (line < lines.size() - 1) { sb.append(lines.get(line)).append("\n"); buf.append(lines.get(line)).append("\n"); AttributedString prompt; if (computePrompts) { String missing = ""; if (needsMessage) { if (missings.isEmpty()) { try { parser.parse(buf.toString(), buf.length(), ParseContext.SECONDARY_PROMPT); } catch (EOFError e) { missing = e.getMissing(); } catch (SyntaxError e) { // Ignore } } else { missing = missings.get(line); } } prompt = expandPromptPattern(secondaryPromptPattern, width, missing, line + 1); } else { prompt = prompts.get(line); } prompts.add(prompt); sb.append(prompt); line++; } sb.append(lines.get(line)); buf.append(lines.get(line)); return sb.toAttributedString(); } private AttributedString addRightPrompt(AttributedString prompt, AttributedString line) { int width = prompt.columnLength(); boolean endsWithNl = line.length() > 0 && line.charAt(line.length() - 1) == '\n'; // columnLength counts -1 for the final newline; adjust for that int nb = size.getColumns() - width - (line.columnLength() + (endsWithNl ? 1 : 0)); if (nb >= 3) { AttributedStringBuilder sb = new AttributedStringBuilder(size.getColumns()); sb.append(line, 0, endsWithNl ? line.length() - 1 : line.length()); for (int j = 0; j < nb; j++) { sb.append(' '); } sb.append(prompt); if (endsWithNl) { sb.append('\n'); } line = sb.toAttributedString(); } return line; } // // Completion // protected boolean insertTab() { return isSet(Option.INSERT_TAB) && getLastBinding().equals("\t") && buf.toString().matches("(^|[\\s\\S]*\n)[\r\n\t ]*"); } protected boolean expandHistory() { String str = buf.toString(); String exp = expander.expandHistory(history, str); if (!exp.equals(str)) { buf.clear(); buf.write(exp); return true; } else { return false; } } protected enum CompletionType { Expand, ExpandComplete, Complete, List, } protected boolean expandWord() { if (insertTab()) { return selfInsert(); } else { return doComplete(CompletionType.Expand, isSet(Option.MENU_COMPLETE), false); } } protected boolean expandOrComplete() { if (insertTab()) { return selfInsert(); } else { return doComplete(CompletionType.ExpandComplete, isSet(Option.MENU_COMPLETE), false); } } protected boolean expandOrCompletePrefix() { if (insertTab()) { return selfInsert(); } else { return doComplete(CompletionType.ExpandComplete, isSet(Option.MENU_COMPLETE), true); } } protected boolean completeWord() { if (insertTab()) { return selfInsert(); } else { return doComplete(CompletionType.Complete, isSet(Option.MENU_COMPLETE), false); } } protected boolean menuComplete() { if (insertTab()) { return selfInsert(); } else { return doComplete(CompletionType.Complete, true, false); } } protected boolean menuExpandOrComplete() { if (insertTab()) { return selfInsert(); } else { return doComplete(CompletionType.ExpandComplete, true, false); } } protected boolean completePrefix() { if (insertTab()) { return selfInsert(); } else { return doComplete(CompletionType.Complete, isSet(Option.MENU_COMPLETE), true); } } protected boolean listChoices() { return doComplete(CompletionType.List, isSet(Option.MENU_COMPLETE), false); } protected boolean deleteCharOrList() { if (buf.cursor() != buf.length() || buf.length() == 0) { return deleteChar(); } else { return doComplete(CompletionType.List, isSet(Option.MENU_COMPLETE), false); } } protected boolean doComplete(CompletionType lst, boolean useMenu, boolean prefix) { // Try to expand history first // If there is actually an expansion, bail out now try { if (expandHistory()) { return true; } } catch (Exception e) { Log.info("Error while expanding history", e); return false; } // Parse the command line ParsedLine line; try { line = parser.parse(buf.toString(), buf.cursor(), ParseContext.COMPLETE); } catch (Exception e) { Log.info("Error while parsing line", e); return false; } // Find completion candidates List candidates = new ArrayList<>(); try { if (completer != null) { completer.complete(this, line, candidates); } } catch (Exception e) { Log.info("Error while finding completion candidates", e); return false; } if (lst == CompletionType.ExpandComplete || lst == CompletionType.Expand) { String w = expander.expandVar(line.word()); if (!line.word().equals(w)) { if (prefix) { buf.backspace(line.wordCursor()); } else { buf.move(line.word().length() - line.wordCursor()); buf.backspace(line.word().length()); } buf.write(w); return true; } if (lst == CompletionType.Expand) { return false; } else { lst = CompletionType.Complete; } } boolean caseInsensitive = isSet(Option.CASE_INSENSITIVE); int errors = getInt(ERRORS, DEFAULT_ERRORS); // Build a list of sorted candidates NavigableMap> sortedCandidates = new TreeMap<>(caseInsensitive ? String.CASE_INSENSITIVE_ORDER : null); for (Candidate cand : candidates) { sortedCandidates .computeIfAbsent(AttributedString.fromAnsi(cand.value()).toString(), s -> new ArrayList<>()) .add(cand); } // Find matchers // TODO: glob completion List>, Map>>> matchers; Predicate exact; if (prefix) { String wp = line.word().substring(0, line.wordCursor()); matchers = Arrays.asList( simpleMatcher(s -> s.startsWith(wp)), simpleMatcher(s -> s.contains(wp)), typoMatcher(wp, errors) ); exact = s -> s.equals(wp); } else if (isSet(Option.COMPLETE_IN_WORD)) { String wd = line.word(); String wp = wd.substring(0, line.wordCursor()); String ws = wd.substring(line.wordCursor()); Pattern p1 = Pattern.compile(Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*"); Pattern p2 = Pattern.compile(".*" + Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*"); matchers = Arrays.asList( simpleMatcher(s -> p1.matcher(s).matches()), simpleMatcher(s -> p2.matcher(s).matches()), typoMatcher(wd, errors) ); exact = s -> s.equals(wd); } else { String wd = line.word(); matchers = Arrays.asList( simpleMatcher(s -> s.startsWith(wd)), simpleMatcher(s -> s.contains(wd)), typoMatcher(wd, errors) ); exact = s -> s.equals(wd); } // Find matching candidates Map> matching = Collections.emptyMap(); for (Function>, Map>> matcher : matchers) { matching = matcher.apply(sortedCandidates); if (!matching.isEmpty()) { break; } } // If we have no matches, bail out if (matching.isEmpty()) { return false; } // If we only need to display the list, do it now if (lst == CompletionType.List) { List possible = matching.entrySet().stream() .flatMap(e -> e.getValue().stream()) .collect(Collectors.toList()); doList(possible, line.word(), false); return !possible.isEmpty(); } // Check if there's a single possible match Candidate completion = null; // If there's a single possible completion if (matching.size() == 1) { completion = matching.values().stream().flatMap(Collection::stream) .findFirst().orElse(null); } // Or if RECOGNIZE_EXACT is set, try to find an exact match else if (isSet(Option.RECOGNIZE_EXACT)) { completion = matching.values().stream().flatMap(Collection::stream) .filter(Candidate::complete) .filter(c -> exact.test(c.value())) .findFirst().orElse(null); } // Complete and exit if (completion != null && !completion.value().isEmpty()) { if (prefix) { buf.backspace(line.wordCursor()); } else { buf.move(line.word().length() - line.wordCursor()); buf.backspace(line.word().length()); } buf.write(completion.value()); if (completion.complete() && buf.currChar() != ' ') { buf.write(" "); } if (completion.suffix() != null) { redisplay(); Binding op = readBinding(getKeys()); if (op != null) { String chars = getString(REMOVE_SUFFIX_CHARS, DEFAULT_REMOVE_SUFFIX_CHARS); String ref = op instanceof Reference ? ((Reference) op).name() : null; if (SELF_INSERT.equals(ref) && chars.indexOf(getLastBinding().charAt(0)) >= 0 || ACCEPT_LINE.equals(ref)) { buf.backspace(completion.suffix().length()); if (getLastBinding().charAt(0) != ' ') { buf.write(' '); } } pushBackBinding(true); } } return true; } List possible = matching.entrySet().stream() .flatMap(e -> e.getValue().stream()) .collect(Collectors.toList()); if (useMenu) { buf.move(line.word().length() - line.wordCursor()); buf.backspace(line.word().length()); doMenu(possible, line.word()); return true; } // Find current word and move to end String current; if (prefix) { current = line.word().substring(0, line.wordCursor()); } else { current = line.word(); buf.move(current.length() - line.wordCursor()); } // Now, we need to find the unambiguous completion // TODO: need to find common suffix String commonPrefix = null; for (String key : matching.keySet()) { commonPrefix = commonPrefix == null ? key : getCommonStart(commonPrefix, key, caseInsensitive); } boolean hasUnambiguous = commonPrefix.startsWith(current) && !commonPrefix.equals(current); if (hasUnambiguous) { buf.backspace(current.length()); buf.write(commonPrefix); current = commonPrefix; if ((!isSet(Option.AUTO_LIST) && isSet(Option.AUTO_MENU)) || (isSet(Option.AUTO_LIST) && isSet(Option.LIST_AMBIGUOUS))) { if (!nextBindingIsComplete()) { return true; } } } if (isSet(Option.AUTO_LIST)) { if (!doList(possible, current, true)) { return true; } } if (isSet(Option.AUTO_MENU)) { buf.backspace(current.length()); doMenu(possible, line.word()); } return true; } private void mergeCandidates(List possible) { // Merge candidates if the have the same key Map> keyedCandidates = new HashMap<>(); for (Candidate candidate : possible) { if (candidate.key() != null) { List cands = keyedCandidates.computeIfAbsent(candidate.key(), s -> new ArrayList<>()); cands.add(candidate); } } if (!keyedCandidates.isEmpty()) { for (List candidates : keyedCandidates.values()) { if (candidates.size() >= 1) { possible.removeAll(candidates); // Candidates with the same key are supposed to have // the same description candidates.sort(Comparator.comparing(Candidate::value)); Candidate first = candidates.get(0); String disp = candidates.stream() .map(Candidate::displ) .collect(Collectors.joining(" ")); possible.add(new Candidate(first.value(), disp, first.group(), first.descr(), first.suffix(), null, first.complete())); } } } } private Function>, Map>> simpleMatcher(Predicate pred) { return m -> m.entrySet().stream() .filter(e -> pred.test(e.getKey())) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } private Function>, Map>> typoMatcher(String word, int errors) { return m -> { Map> map = m.entrySet().stream() .filter(e -> distance(word, e.getKey()) < errors) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); if (map.size() > 1) { map.computeIfAbsent(word, w -> new ArrayList<>()) .add(new Candidate(word, word, "original", null, null, null, false)); } return map; }; } private int distance(String word, String cand) { if (word.length() < cand.length()) { int d1 = Levenshtein.distance(word, cand.substring(0, Math.min(cand.length(), word.length()))); int d2 = Levenshtein.distance(word, cand); return Math.min(d1, d2); } else { return Levenshtein.distance(word, cand); } } protected boolean nextBindingIsComplete() { redisplay(); KeyMap keyMap = keyMaps.get(MENU); Binding operation = readBinding(getKeys(), keyMap); if (operation instanceof Reference && MENU_COMPLETE.equals(((Reference) operation).name())) { return true; } else { pushBackBinding(); return false; } } private class MenuSupport implements Supplier { final List possible; int selection; int topLine; String word; AttributedString computed; int lines; int columns; String completed; public MenuSupport(List original, String completed) { this.possible = new ArrayList<>(); this.selection = -1; this.topLine = 0; this.word = ""; this.completed = completed; computePost(original, null, possible, completed); next(); } public Candidate completion() { return possible.get(selection); } public void next() { selection = (selection + 1) % possible.size(); update(); } public void previous() { selection = (selection + possible.size() - 1) % possible.size(); update(); } public void down() { if (isSet(Option.LIST_ROWS_FIRST)) { int r = selection / columns; int c = selection % columns; if ((r + 1) * columns + c < possible.size()) { r++; } else if (c + 1 < columns) { c++; r = 0; } else { r = 0; c = 0; } selection = r * columns + c; update(); } else { next(); } } public void left() { if (isSet(Option.LIST_ROWS_FIRST)) { previous(); } else { int c = selection / lines; int r = selection % lines; if (c - 1 >= 0) { c--; } else { c = columns - 1; r--; } selection = c * lines + r; if (selection < 0) { selection = possible.size() - 1; } update(); } } public void right() { if (isSet(Option.LIST_ROWS_FIRST)) { next(); } else { int c = selection / lines; int r = selection % lines; if (c + 1 < columns) { c++; } else { c = 0; r++; } selection = c * lines + r; if (selection >= possible.size()) { selection = 0; } update(); } } public void up() { if (isSet(Option.LIST_ROWS_FIRST)) { int r = selection / columns; int c = selection % columns; if (r > 0) { r--; } else { c = (c + columns - 1) % columns; r = lines - 1; if (r * columns + c >= possible.size()) { r--; } } selection = r * columns + c; update(); } else { previous(); } } private void update() { buf.backspace(word.length()); word = completion().value(); buf.write(word); // Compute displayed prompt PostResult pr = computePost(possible, completion(), null, completed); AttributedString text = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); int promptLines = text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size(); if (pr.lines > size.getRows() - promptLines) { int displayed = size.getRows() - promptLines - 1; if (pr.selectedLine >= 0) { if (pr.selectedLine < topLine) { topLine = pr.selectedLine; } else if (pr.selectedLine >= topLine + displayed) { topLine = pr.selectedLine - displayed + 1; } } AttributedString post = pr.post; if (post.length() > 0 && post.charAt(post.length() - 1) != '\n') { post = new AttributedStringBuilder(post.length() + 1) .append(post).append("\n").toAttributedString(); } List lines = post.columnSplitLength(size.getColumns(), true, display.delayLineWrap()); List sub = new ArrayList<>(lines.subList(topLine, topLine + displayed)); sub.add(new AttributedStringBuilder() .style(AttributedStyle.DEFAULT.foreground(AttributedStyle.CYAN)) .append("rows ") .append(Integer.toString(topLine + 1)) .append(" to ") .append(Integer.toString(topLine + displayed)) .append(" of ") .append(Integer.toString(lines.size())) .append("\n") .style(AttributedStyle.DEFAULT).toAttributedString()); computed = AttributedString.join(AttributedString.EMPTY, sub); } else { computed = pr.post; } lines = pr.lines; columns = (possible.size() + lines - 1) / lines; } @Override public AttributedString get() { return computed; } } protected boolean doMenu(List original, String completed) { // Reorder candidates according to display order final List possible = new ArrayList<>(); mergeCandidates(original); computePost(original, null, possible, completed); // Build menu support MenuSupport menuSupport = new MenuSupport(original, completed); post = menuSupport; redisplay(); // Loop KeyMap keyMap = keyMaps.get(MENU); Binding operation; while ((operation = readBinding(getKeys(), keyMap)) != null) { String ref = (operation instanceof Reference) ? ((Reference) operation).name() : ""; switch (ref) { case MENU_COMPLETE: menuSupport.next(); break; case REVERSE_MENU_COMPLETE: menuSupport.previous(); break; case UP_LINE_OR_HISTORY: menuSupport.up(); break; case DOWN_LINE_OR_HISTORY: menuSupport.down(); break; case FORWARD_CHAR: menuSupport.right(); break; case BACKWARD_CHAR: menuSupport.left(); break; case CLEAR_SCREEN: clearScreen(); break; default: { Candidate completion = menuSupport.completion(); if (completion.suffix() != null) { String chars = getString(REMOVE_SUFFIX_CHARS, DEFAULT_REMOVE_SUFFIX_CHARS); if (SELF_INSERT.equals(ref) && chars.indexOf(getLastBinding().charAt(0)) >= 0 || BACKWARD_DELETE_CHAR.equals(ref)) { buf.backspace(completion.suffix().length()); } } if (completion.complete() && getLastBinding().charAt(0) != ' ' && (SELF_INSERT.equals(ref) || getLastBinding().charAt(0) != ' ')) { buf.write(' '); } if (!ACCEPT_LINE.equals(ref) && !(SELF_INSERT.equals(ref) && completion.suffix() != null && completion.suffix().startsWith(getLastBinding()))) { pushBackBinding(true); } post = null; return true; } } redisplay(); } return false; } protected boolean doList(List possible, String completed, boolean runLoop) { // If we list only and if there's a big // number of items, we should ask the user // for confirmation, display the list // and redraw the line at the bottom mergeCandidates(possible); AttributedString text = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); int promptLines = text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size(); PostResult postResult = computePost(possible, null, null, completed); int lines = postResult.lines; int listMax = getInt(LIST_MAX, DEFAULT_LIST_MAX); if (listMax > 0 && possible.size() >= listMax || lines >= size.getRows() - promptLines) { // prompt post = () -> new AttributedString(getAppName() + ": do you wish to see to see all " + possible.size() + " possibilities (" + lines + " lines)?"); redisplay(true); int c = readCharacter(); if (c != 'y' && c != 'Y' && c != '\t') { post = null; return false; } } StringBuilder sb = new StringBuilder(); while (true) { String current = completed + sb.toString(); List cands; if (sb.length() > 0) { cands = possible.stream() .filter(c -> c.value().startsWith(current)) .collect(Collectors.toList()); } else { cands = possible; } post = () -> { AttributedString t = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); int pl = t.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size(); PostResult pr = computePost(cands, null, null, current); if (pr.lines >= size.getRows() - pl) { post = null; int oldCursor = buf.cursor(); buf.cursor(buf.length()); redisplay(false); buf.cursor(oldCursor); println(); List ls = postResult.post.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); Display d = new Display(terminal, false); d.resize(size.getRows(), size.getColumns()); d.update(ls, -1); redrawLine(); return new AttributedString(""); } return pr.post; }; if (!runLoop) { return false; } redisplay(); // TODO: use a different keyMap ? Binding b = bindingReader.readBinding(getKeys()); if (b instanceof Reference) { String name = ((Reference) b).name(); if (BACKWARD_DELETE_CHAR.equals(name) || VI_BACKWARD_DELETE_CHAR.equals(name)) { if (sb.length() == 0) { pushBackBinding(); post = null; return false; } else { sb.setLength(sb.length() - 1); buf.backspace(); } } else if (SELF_INSERT.equals(name)) { sb.append(getLastBinding()); buf.write(getLastBinding()); if (cands.isEmpty()) { post = null; return false; } } else if ("\t".equals(getLastBinding())) { if (cands.size() == 1 || sb.length() > 0) { post = null; pushBackBinding(); } else if (isSet(Option.AUTO_MENU)) { buf.backspace(current.length()); doMenu(cands, current); } return false; } else { pushBackBinding(); post = null; return false; } } else if (b == null) { post = null; return false; } } } protected static class PostResult { final AttributedString post; final int lines; final int selectedLine; public PostResult(AttributedString post, int lines, int selectedLine) { this.post = post; this.lines = lines; this.selectedLine = selectedLine; } } protected PostResult computePost(List possible, Candidate selection, List ordered, String completed) { List strings = new ArrayList<>(); boolean groupName = isSet(Option.GROUP); if (groupName) { LinkedHashMap> sorted = new LinkedHashMap<>(); for (Candidate cand : possible) { String group = cand.group(); sorted.computeIfAbsent(group != null ? group : "", s -> new TreeMap<>()) .put(cand.value(), cand); } for (Map.Entry> entry : sorted.entrySet()) { String group = entry.getKey(); if (group.isEmpty() && sorted.size() > 1) { group = "others"; } if (!group.isEmpty() && isSet(Option.AUTO_GROUP)) { strings.add(group); } strings.add(new ArrayList<>(entry.getValue().values())); if (ordered != null) { ordered.addAll(entry.getValue().values()); } } } else { Set groups = new LinkedHashSet<>(); TreeMap sorted = new TreeMap<>(); for (Candidate cand : possible) { String group = cand.group(); if (group != null) { groups.add(group); } sorted.put(cand.value(), cand); } if (isSet(Option.AUTO_GROUP)) { for (String group : groups) { strings.add(group); } } strings.add(new ArrayList<>(sorted.values())); if (ordered != null) { ordered.addAll(sorted.values()); } } return toColumns(strings, selection, completed); } private static final String DESC_PREFIX = "("; private static final String DESC_SUFFIX = ")"; private static final int MARGIN_BETWEEN_DISPLAY_AND_DESC = 1; private static final int MARGIN_BETWEEN_COLUMNS = 3; @SuppressWarnings("unchecked") protected PostResult toColumns(List items, Candidate selection, String completed) { int[] out = new int[2]; int width = size.getColumns(); // TODO: support Option.LIST_PACKED // Compute column width int maxWidth = 0; for (Object item : items) { if (item instanceof String) { int len = display.wcwidth((String) item); maxWidth = Math.max(maxWidth, len); } else if (item instanceof List) { for (Candidate cand : (List) item) { int len = display.wcwidth(cand.displ()); if (cand.descr() != null) { len += MARGIN_BETWEEN_DISPLAY_AND_DESC; len += DESC_PREFIX.length(); len += display.wcwidth(cand.descr()); len += DESC_SUFFIX.length(); } maxWidth = Math.max(maxWidth, len); } } } // Build columns AttributedStringBuilder sb = new AttributedStringBuilder(); for (Object list : items) { toColumns(list, width, maxWidth, sb, selection, completed, out); } if (sb.length() > 0 && sb.charAt(sb.length() - 1) == '\n') { sb.setLength(sb.length() - 1); } return new PostResult(sb.toAttributedString(), out[0], out[1]); } @SuppressWarnings("unchecked") protected void toColumns(Object items, int width, int maxWidth, AttributedStringBuilder sb, Candidate selection, String completed, int[] out) { if (maxWidth <= 0) { return; } // This is a group if (items instanceof String) { sb.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.CYAN)) .append((String) items) .style(AttributedStyle.DEFAULT) .append("\n"); out[0]++; } // This is a Candidate list else if (items instanceof List) { List candidates = (List) items; maxWidth = Math.min(width, maxWidth); int c = width / maxWidth; while (c > 1 && c * maxWidth + (c - 1) * MARGIN_BETWEEN_COLUMNS >= width) { c--; } int columns = c; int lines = (candidates.size() + columns - 1) / columns; IntBinaryOperator index; if (isSet(Option.LIST_ROWS_FIRST)) { index = (i, j) -> i * columns + j; } else { index = (i, j) -> j * lines + i; } for (int i = 0; i < lines; i++) { for (int j = 0; j < columns; j++) { int idx = index.applyAsInt(i, j); if (idx < candidates.size()) { Candidate cand = candidates.get(idx); boolean hasRightItem = j < columns - 1 && index.applyAsInt(i, j + 1) < candidates.size(); AttributedString left = AttributedString.fromAnsi(cand.displ()); AttributedString right = AttributedString.fromAnsi(cand.descr()); int lw = left.columnLength(); int rw = 0; if (right != null) { int rem = maxWidth - (lw + MARGIN_BETWEEN_DISPLAY_AND_DESC + DESC_PREFIX.length() + DESC_SUFFIX.length()); rw = right.columnLength(); if (rw > rem) { right = AttributedStringBuilder.append( right.columnSubSequence(0, rem - WCWidth.wcwidth('…')), "…"); rw = right.columnLength(); } right = AttributedStringBuilder.append(DESC_PREFIX, right, DESC_SUFFIX); rw += DESC_PREFIX.length() + DESC_SUFFIX.length(); } if (cand == selection) { out[1] = i; sb.style(AttributedStyle.INVERSE); if (left.toString().startsWith(completed)) { sb.append(left.toString(), 0, completed.length()); sb.append(left.toString(), completed.length(), left.length()); } else { sb.append(left.toString()); } for (int k = 0; k < maxWidth - lw - rw; k++) { sb.append(' '); } if (right != null) { sb.append(right); } sb.style(AttributedStyle.DEFAULT); } else { if (left.toString().startsWith(completed)) { sb.style(sb.style().foreground(AttributedStyle.CYAN)); sb.append(left, 0, completed.length()); sb.style(AttributedStyle.DEFAULT); sb.append(left, completed.length(), left.length()); } else { sb.append(left); } if (right != null || hasRightItem) { for (int k = 0; k < maxWidth - lw - rw; k++) { sb.append(' '); } } if (right != null) { sb.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.BLACK + AttributedStyle.BRIGHT)); sb.append(right); sb.style(AttributedStyle.DEFAULT); } } if (hasRightItem) { for (int k = 0; k < MARGIN_BETWEEN_COLUMNS; k++) { sb.append(' '); } } } } sb.append('\n'); } out[0] += lines; } } private String getCommonStart(String str1, String str2, boolean caseInsensitive) { int[] s1 = str1.codePoints().toArray(); int[] s2 = str2.codePoints().toArray(); int len = 0; while (len < Math.min(s1.length, s2.length)) { int ch1 = s1[len]; int ch2 = s2[len]; if (ch1 != ch2 && caseInsensitive) { ch1 = Character.toUpperCase(ch1); ch2 = Character.toUpperCase(ch2); if (ch1 != ch2) { ch1 = Character.toLowerCase(ch1); ch2 = Character.toLowerCase(ch2); } } if (ch1 != ch2) { break; } len++; } return new String(s1, 0, len); } /** * Used in "vi" mode for argumented history move, to move a specific * number of history entries forward or back. * * @param next If true, move forward * @param count The number of entries to move * @return true if the move was successful */ protected boolean moveHistory(final boolean next, int count) { boolean ok = true; for (int i = 0; i < count && (ok = moveHistory(next)); i++) { /* empty */ } return ok; } /** * Move up or down the history tree. */ protected boolean moveHistory(final boolean next) { if (!buf.toString().equals(history.current())) { modifiedHistory.put(history.index(), buf.toString()); } if (next && !history.next()) { return false; } else if (!next && !history.previous()) { return false; } setBuffer(modifiedHistory.containsKey(history.index()) ? modifiedHistory.get(history.index()) : history.current()); return true; } // // Printing // /** * Raw output printing */ void print(String str) { terminal.writer().write(str); } void println(String s) { print(s); println(); } /** * Output a platform-dependant newline. */ void println() { terminal.puts(Capability.carriage_return); print("\n"); redrawLine(); } // // Actions // protected boolean killBuffer() { killRing.add(buf.toString()); buf.clear(); return true; } protected boolean killWholeLine() { if (buf.length() == 0) { return false; } int start; int end; if (count < 0) { end = buf.cursor(); while (buf.atChar(end) != 0 && buf.atChar(end) != '\n') { end++; } start = end; for (int count = -this.count; count > 0; --count) { while (start > 0 && buf.atChar(start - 1) != '\n') { start--; } start--; } } else { start = buf.cursor(); while (start > 0 && buf.atChar(start - 1) != '\n') { start--; } end = start; while (count-- > 0) { while (end < buf.length() && buf.atChar(end) != '\n') { end++; } end++; } } String killed = buf.substring(start, end); buf.cursor(start); buf.delete(end - start); killRing.add(killed); return true; } /** * Kill the buffer ahead of the current cursor position. * * @return true if successful */ public boolean killLine() { if (count < 0) { return callNeg(this::backwardKillLine); } if (buf.cursor() == buf.length()) { return false; } int cp = buf.cursor(); int len = cp; while (count-- > 0) { if (buf.atChar(len) == '\n') { len++; } else { while (buf.atChar(len) != 0 && buf.atChar(len) != '\n') { len++; } } } int num = len - cp; String killed = buf.substring(cp, cp + num); buf.delete(num); killRing.add(killed); return true; } public boolean backwardKillLine() { if (count < 0) { return callNeg(this::killLine); } if (buf.cursor() == 0) { return false; } int cp = buf.cursor(); int beg = cp; while (count-- > 0) { if (beg == 0) { break; } if (buf.atChar(beg - 1) == '\n') { beg--; } else { while (beg > 0 && buf.atChar(beg - 1) != 0 && buf.atChar(beg - 1) != '\n') { beg--; } } } int num = cp - beg; String killed = buf.substring(cp - beg, cp); buf.cursor(beg); buf.delete(num); killRing.add(killed); return true; } public boolean killRegion() { return doCopyKillRegion(true); } public boolean copyRegionAsKill() { return doCopyKillRegion(false); } private boolean doCopyKillRegion(boolean kill) { if (regionMark > buf.length()) { regionMark = buf.length(); } if (regionActive == RegionType.LINE) { int start = regionMark; int end = buf.cursor(); if (start < end) { while (start > 0 && buf.atChar(start - 1) != '\n') { start--; } while (end < buf.length() - 1 && buf.atChar(end + 1) != '\n') { end++; } if (isInViCmdMode()) { end++; } killRing.add(buf.substring(start, end)); if (kill) { buf.backspace(end - start); } } else { while (end > 0 && buf.atChar(end - 1) != '\n') { end--; } while (start < buf.length() && buf.atChar(start) != '\n') { start++; } if (isInViCmdMode()) { start++; } killRing.addBackwards(buf.substring(end, start)); if (kill) { buf.cursor(end); buf.delete(start - end); } } } else if (regionMark > buf.cursor()) { if (isInViCmdMode()) { regionMark++; } killRing.add(buf.substring(buf.cursor(), regionMark)); if (kill) { buf.delete(regionMark - buf.cursor()); } } else { if (isInViCmdMode()) { buf.move(1); } killRing.add(buf.substring(regionMark, buf.cursor())); if (kill) { buf.backspace(buf.cursor() - regionMark); } } if (kill) { regionActive = RegionType.NONE; } return true; } public boolean yank() { String yanked = killRing.yank(); if (yanked == null) { return false; } else { putString(yanked); return true; } } public boolean yankPop() { if (!killRing.lastYank()) { return false; } String current = killRing.yank(); if (current == null) { // This shouldn't happen. return false; } buf.backspace(current.length()); String yanked = killRing.yankPop(); if (yanked == null) { // This shouldn't happen. return false; } putString(yanked); return true; } public boolean mouse() { MouseEvent event = readMouseEvent(); if (event.getType() == MouseEvent.Type.Released && event.getButton() == MouseEvent.Button.Button1) { StringBuilder tsb = new StringBuilder(); Cursor cursor = terminal.getCursorPosition(c -> tsb.append((char) c)); bindingReader.runMacro(tsb.toString()); List secondaryPrompts = new ArrayList<>(); getDisplayedBufferWithPrompts(secondaryPrompts); AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH); sb.append(prompt); sb.append(insertSecondaryPrompts(new AttributedString(buf.upToCursor()), secondaryPrompts, false)); List promptLines = sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); int currentLine = promptLines.size() - 1; int wantedLine = Math.max(0, Math.min(currentLine + event.getY() - cursor.getY(), secondaryPrompts.size())); int pl0 = currentLine == 0 ? prompt.columnLength() : secondaryPrompts.get(currentLine - 1).columnLength(); int pl1 = wantedLine == 0 ? prompt.columnLength() : secondaryPrompts.get(wantedLine - 1).columnLength(); int adjust = pl1 - pl0; buf.moveXY(event.getX() - cursor.getX() - adjust, event.getY() - cursor.getY()); } return true; } /** * Clean the used display */ public boolean clear() { display.update(Collections.emptyList(), 0); return true; } /** * Clear the screen by issuing the ANSI "clear screen" code. */ public boolean clearScreen() { if (terminal.puts(Capability.clear_screen)) { redrawLine(); } else { println(); } return true; } /** * Issue an audible keyboard bell. */ public boolean beep() { BellType bell_preference = BellType.AUDIBLE; switch (getString(BELL_STYLE, DEFAULT_BELL_STYLE).toLowerCase()) { case "none": case "off": bell_preference = BellType.NONE; break; case "audible": bell_preference = BellType.AUDIBLE; break; case "visible": bell_preference = BellType.VISIBLE; break; case "on": bell_preference = getBoolean(PREFER_VISIBLE_BELL, false) ? BellType.VISIBLE : BellType.AUDIBLE; break; } if (bell_preference == BellType.VISIBLE) { if (terminal.puts(Capability.flash_screen) || terminal.puts(Capability.bell)) { flush(); } } else if (bell_preference == BellType.AUDIBLE) { if (terminal.puts(Capability.bell)) { flush(); } } return true; } // // Helpers // /** * 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 */ protected boolean isDelimiter(int c) { return !Character.isLetterOrDigit(c); } /** * Checks to see if a character is a whitespace character. Currently * this delegates to {@link Character#isWhitespace(char)}, however * eventually it should be hooked up so that the definition of whitespace * can be configured, as readline does. * * @param c The character to check * @return true if the character is a whitespace */ protected boolean isWhitespace(int c) { return Character.isWhitespace(c); } protected boolean isViAlphaNum(int c) { return c == '_' || Character.isLetterOrDigit(c); } protected boolean isAlpha(int c) { return Character.isLetter(c); } protected boolean isWord(int c) { String wordchars = getString(WORDCHARS, DEFAULT_WORDCHARS); return Character.isLetterOrDigit(c) || (c < 128 && wordchars.indexOf((char) c) >= 0); } String getString(String name, String def) { return ReaderUtils.getString(this, name, def); } boolean getBoolean(String name, boolean def) { return ReaderUtils.getBoolean(this, name, def); } int getInt(String name, int def) { return ReaderUtils.getInt(this, name, def); } long getLong(String name, long def) { return ReaderUtils.getLong(this, name, def); } @Override public Map> defaultKeyMaps() { Map> keyMaps = new HashMap<>(); keyMaps.put(EMACS, emacs()); keyMaps.put(VICMD, viCmd()); keyMaps.put(VIINS, viInsertion()); keyMaps.put(MENU, menu()); keyMaps.put(VIOPP, viOpp()); keyMaps.put(VISUAL, visual()); keyMaps.put(SAFE, safe()); if (getBoolean(BIND_TTY_SPECIAL_CHARS, true)) { Attributes attr = terminal.getAttributes(); bindConsoleChars(keyMaps.get(EMACS), attr); bindConsoleChars(keyMaps.get(VIINS), attr); } // Put default for (KeyMap keyMap : keyMaps.values()) { keyMap.setUnicode(new Reference(SELF_INSERT)); keyMap.setAmbiguousTimeout(getLong(AMBIGUOUS_BINDING, DEFAULT_AMBIGUOUS_BINDING)); } // By default, link main to emacs keyMaps.put(MAIN, keyMaps.get(EMACS)); return keyMaps; } public KeyMap emacs() { KeyMap emacs = new KeyMap<>(); bind(emacs, SET_MARK_COMMAND, ctrl('@')); bind(emacs, BEGINNING_OF_LINE, ctrl('A')); bind(emacs, BACKWARD_CHAR, ctrl('B')); bind(emacs, DELETE_CHAR_OR_LIST, ctrl('D')); bind(emacs, END_OF_LINE, ctrl('E')); bind(emacs, FORWARD_CHAR, ctrl('F')); bind(emacs, SEND_BREAK, ctrl('G')); bind(emacs, BACKWARD_DELETE_CHAR, ctrl('H')); bind(emacs, EXPAND_OR_COMPLETE, ctrl('I')); bind(emacs, ACCEPT_LINE, ctrl('J')); bind(emacs, KILL_LINE, ctrl('K')); bind(emacs, CLEAR_SCREEN, ctrl('L')); bind(emacs, ACCEPT_LINE, ctrl('M')); bind(emacs, DOWN_LINE_OR_HISTORY, ctrl('N')); bind(emacs, UP_LINE_OR_HISTORY, ctrl('P')); bind(emacs, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('R')); bind(emacs, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('S')); bind(emacs, TRANSPOSE_CHARS, ctrl('T')); bind(emacs, KILL_WHOLE_LINE, ctrl('U')); bind(emacs, QUOTED_INSERT, ctrl('V')); bind(emacs, BACKWARD_KILL_WORD, ctrl('W')); bind(emacs, YANK, ctrl('Y')); bind(emacs, CHARACTER_SEARCH, ctrl(']')); bind(emacs, UNDO, ctrl('_')); bind(emacs, SELF_INSERT, range(" -~")); bind(emacs, INSERT_CLOSE_PAREN, ")"); bind(emacs, INSERT_CLOSE_SQUARE, "]"); bind(emacs, INSERT_CLOSE_CURLY, "}"); bind(emacs, BACKWARD_DELETE_CHAR, del()); bind(emacs, VI_MATCH_BRACKET, translate("^X^B")); bind(emacs, SEND_BREAK, translate("^X^G")); bind(emacs, VI_FIND_NEXT_CHAR, translate("^X^F")); bind(emacs, VI_JOIN, translate("^X^J")); bind(emacs, KILL_BUFFER, translate("^X^K")); bind(emacs, INFER_NEXT_HISTORY, translate("^X^N")); bind(emacs, OVERWRITE_MODE, translate("^X^O")); bind(emacs, REDO, translate("^X^R")); bind(emacs, UNDO, translate("^X^U")); bind(emacs, VI_CMD_MODE, translate("^X^V")); bind(emacs, EXCHANGE_POINT_AND_MARK, translate("^X^X")); bind(emacs, DO_LOWERCASE_VERSION, translate("^XA-^XZ")); bind(emacs, WHAT_CURSOR_POSITION, translate("^X=")); bind(emacs, KILL_LINE, translate("^X^?")); bind(emacs, SEND_BREAK, alt(ctrl('G'))); bind(emacs, BACKWARD_KILL_WORD, alt(ctrl('H'))); bind(emacs, SELF_INSERT_UNMETA, alt(ctrl('M'))); bind(emacs, COMPLETE_WORD, alt(esc())); bind(emacs, CHARACTER_SEARCH_BACKWARD, alt(ctrl(']'))); bind(emacs, COPY_PREV_WORD, alt(ctrl('_'))); bind(emacs, SET_MARK_COMMAND, alt(' ')); bind(emacs, NEG_ARGUMENT, alt('-')); bind(emacs, DIGIT_ARGUMENT, range("\\E0-\\E9")); bind(emacs, BEGINNING_OF_HISTORY, alt('<')); bind(emacs, LIST_CHOICES, alt('=')); bind(emacs, END_OF_HISTORY, alt('>')); bind(emacs, LIST_CHOICES, alt('?')); bind(emacs, DO_LOWERCASE_VERSION, range("^[A-^[Z")); bind(emacs, BACKWARD_WORD, alt('b')); bind(emacs, CAPITALIZE_WORD, alt('c')); bind(emacs, KILL_WORD, alt('d')); bind(emacs, KILL_WORD, translate("^[[3;5~")); // ctrl-delete bind(emacs, FORWARD_WORD, alt('f')); bind(emacs, DOWN_CASE_WORD, alt('l')); bind(emacs, HISTORY_SEARCH_FORWARD, alt('n')); bind(emacs, HISTORY_SEARCH_BACKWARD, alt('p')); bind(emacs, TRANSPOSE_WORDS, alt('t')); bind(emacs, UP_CASE_WORD, alt('u')); bind(emacs, YANK_POP, alt('y')); bind(emacs, BACKWARD_KILL_WORD, alt(del())); bindArrowKeys(emacs); bind(emacs, FORWARD_WORD, translate("^[[1;5C")); // ctrl-left bind(emacs, BACKWARD_WORD, translate("^[[1;5D")); // ctrl-right bind(emacs, FORWARD_WORD, alt(key(Capability.key_right))); bind(emacs, BACKWARD_WORD, alt(key(Capability.key_left))); bind(emacs, FORWARD_WORD, alt(translate("^[[C"))); bind(emacs, BACKWARD_WORD, alt(translate("^[[D"))); return emacs; } public KeyMap viInsertion() { KeyMap viins = new KeyMap<>(); bind(viins, SELF_INSERT, range("^@-^_")); bind(viins, LIST_CHOICES, ctrl('D')); bind(viins, SEND_BREAK, ctrl('G')); bind(viins, BACKWARD_DELETE_CHAR, ctrl('H')); bind(viins, EXPAND_OR_COMPLETE, ctrl('I')); bind(viins, ACCEPT_LINE, ctrl('J')); bind(viins, CLEAR_SCREEN, ctrl('L')); bind(viins, ACCEPT_LINE, ctrl('M')); bind(viins, MENU_COMPLETE, ctrl('N')); bind(viins, REVERSE_MENU_COMPLETE, ctrl('P')); bind(viins, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('R')); bind(viins, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('S')); bind(viins, TRANSPOSE_CHARS, ctrl('T')); bind(viins, KILL_WHOLE_LINE, ctrl('U')); bind(viins, QUOTED_INSERT, ctrl('V')); bind(viins, BACKWARD_KILL_WORD, ctrl('W')); bind(viins, YANK, ctrl('Y')); bind(viins, VI_CMD_MODE, ctrl('[')); bind(viins, UNDO, ctrl('_')); bind(viins, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('X') + "r"); bind(viins, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('X') + "s"); bind(viins, SELF_INSERT, range(" -~")); bind(viins, INSERT_CLOSE_PAREN, ")"); bind(viins, INSERT_CLOSE_SQUARE, "]"); bind(viins, INSERT_CLOSE_CURLY, "}"); bind(viins, BACKWARD_DELETE_CHAR, del()); bindArrowKeys(viins); return viins; } public KeyMap viCmd() { KeyMap vicmd = new KeyMap<>(); bind(vicmd, LIST_CHOICES, ctrl('D')); bind(vicmd, EMACS_EDITING_MODE, ctrl('E')); bind(vicmd, SEND_BREAK, ctrl('G')); bind(vicmd, VI_BACKWARD_CHAR, ctrl('H')); bind(vicmd, ACCEPT_LINE, ctrl('J')); bind(vicmd, KILL_LINE, ctrl('K')); bind(vicmd, CLEAR_SCREEN, ctrl('L')); bind(vicmd, ACCEPT_LINE, ctrl('M')); bind(vicmd, VI_DOWN_LINE_OR_HISTORY, ctrl('N')); bind(vicmd, VI_UP_LINE_OR_HISTORY, ctrl('P')); bind(vicmd, QUOTED_INSERT, ctrl('Q')); bind(vicmd, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('R')); bind(vicmd, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('S')); bind(vicmd, TRANSPOSE_CHARS, ctrl('T')); bind(vicmd, KILL_WHOLE_LINE, ctrl('U')); bind(vicmd, QUOTED_INSERT, ctrl('V')); bind(vicmd, BACKWARD_KILL_WORD, ctrl('W')); bind(vicmd, YANK, ctrl('Y')); bind(vicmd, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('X') + "r"); bind(vicmd, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('X') + "s"); bind(vicmd, SEND_BREAK, alt(ctrl('G'))); bind(vicmd, BACKWARD_KILL_WORD, alt(ctrl('H'))); bind(vicmd, SELF_INSERT_UNMETA, alt(ctrl('M'))); bind(vicmd, COMPLETE_WORD, alt(esc())); bind(vicmd, CHARACTER_SEARCH_BACKWARD, alt(ctrl(']'))); bind(vicmd, SET_MARK_COMMAND, alt(' ')); // bind(vicmd, INSERT_COMMENT, alt('#')); // bind(vicmd, INSERT_COMPLETIONS, alt('*')); bind(vicmd, DIGIT_ARGUMENT, alt('-')); bind(vicmd, BEGINNING_OF_HISTORY, alt('<')); bind(vicmd, LIST_CHOICES, alt('=')); bind(vicmd, END_OF_HISTORY, alt('>')); bind(vicmd, LIST_CHOICES, alt('?')); bind(vicmd, DO_LOWERCASE_VERSION, range("^[A-^[Z")); bind(vicmd, BACKWARD_WORD, alt('b')); bind(vicmd, CAPITALIZE_WORD, alt('c')); bind(vicmd, KILL_WORD, alt('d')); bind(vicmd, FORWARD_WORD, alt('f')); bind(vicmd, DOWN_CASE_WORD, alt('l')); bind(vicmd, HISTORY_SEARCH_FORWARD, alt('n')); bind(vicmd, HISTORY_SEARCH_BACKWARD, alt('p')); bind(vicmd, TRANSPOSE_WORDS, alt('t')); bind(vicmd, UP_CASE_WORD, alt('u')); bind(vicmd, YANK_POP, alt('y')); bind(vicmd, BACKWARD_KILL_WORD, alt(del())); bind(vicmd, FORWARD_CHAR, " "); bind(vicmd, VI_INSERT_COMMENT, "#"); bind(vicmd, END_OF_LINE, "$"); bind(vicmd, VI_MATCH_BRACKET, "%"); bind(vicmd, VI_DOWN_LINE_OR_HISTORY, "+"); bind(vicmd, VI_REV_REPEAT_FIND, ","); bind(vicmd, VI_UP_LINE_OR_HISTORY, "-"); bind(vicmd, VI_REPEAT_CHANGE, "."); bind(vicmd, VI_HISTORY_SEARCH_BACKWARD, "/"); bind(vicmd, VI_DIGIT_OR_BEGINNING_OF_LINE, "0"); bind(vicmd, DIGIT_ARGUMENT, range("1-9")); bind(vicmd, VI_REPEAT_FIND, ";"); bind(vicmd, LIST_CHOICES, "="); bind(vicmd, VI_HISTORY_SEARCH_FORWARD, "?"); bind(vicmd, VI_ADD_EOL, "A"); bind(vicmd, VI_BACKWARD_BLANK_WORD, "B"); bind(vicmd, VI_CHANGE_EOL, "C"); bind(vicmd, VI_KILL_EOL, "D"); bind(vicmd, VI_FORWARD_BLANK_WORD_END, "E"); bind(vicmd, VI_FIND_PREV_CHAR, "F"); bind(vicmd, VI_FETCH_HISTORY, "G"); bind(vicmd, VI_INSERT_BOL, "I"); bind(vicmd, VI_JOIN, "J"); bind(vicmd, VI_REV_REPEAT_SEARCH, "N"); bind(vicmd, VI_OPEN_LINE_ABOVE, "O"); bind(vicmd, VI_PUT_BEFORE, "P"); bind(vicmd, VI_REPLACE, "R"); bind(vicmd, VI_KILL_LINE, "S"); bind(vicmd, VI_FIND_PREV_CHAR_SKIP, "T"); bind(vicmd, REDO, "U"); bind(vicmd, VISUAL_LINE_MODE, "V"); bind(vicmd, VI_FORWARD_BLANK_WORD, "W"); bind(vicmd, VI_BACKWARD_DELETE_CHAR, "X"); bind(vicmd, VI_YANK_WHOLE_LINE, "Y"); bind(vicmd, VI_FIRST_NON_BLANK, "^"); bind(vicmd, VI_ADD_NEXT, "a"); bind(vicmd, VI_BACKWARD_WORD, "b"); bind(vicmd, VI_CHANGE, "c"); bind(vicmd, VI_DELETE, "d"); bind(vicmd, VI_FORWARD_WORD_END, "e"); bind(vicmd, VI_FIND_NEXT_CHAR, "f"); bind(vicmd, WHAT_CURSOR_POSITION, "ga"); bind(vicmd, VI_BACKWARD_BLANK_WORD_END, "gE"); bind(vicmd, VI_BACKWARD_WORD_END, "ge"); bind(vicmd, VI_BACKWARD_CHAR, "h"); bind(vicmd, VI_INSERT, "i"); bind(vicmd, DOWN_LINE_OR_HISTORY, "j"); bind(vicmd, UP_LINE_OR_HISTORY, "k"); bind(vicmd, VI_FORWARD_CHAR, "l"); bind(vicmd, VI_REPEAT_SEARCH, "n"); bind(vicmd, VI_OPEN_LINE_BELOW, "o"); bind(vicmd, VI_PUT_AFTER, "p"); bind(vicmd, VI_REPLACE_CHARS, "r"); bind(vicmd, VI_SUBSTITUTE, "s"); bind(vicmd, VI_FIND_NEXT_CHAR_SKIP, "t"); bind(vicmd, UNDO, "u"); bind(vicmd, VISUAL_MODE, "v"); bind(vicmd, VI_FORWARD_WORD, "w"); bind(vicmd, VI_DELETE_CHAR, "x"); bind(vicmd, VI_YANK, "y"); bind(vicmd, VI_GOTO_COLUMN, "|"); bind(vicmd, VI_SWAP_CASE, "~"); bind(vicmd, VI_BACKWARD_CHAR, del()); bindArrowKeys(vicmd); return vicmd; } public KeyMap menu() { KeyMap menu = new KeyMap<>(); bind(menu, MENU_COMPLETE, "\t"); bind(menu, REVERSE_MENU_COMPLETE, key(Capability.back_tab)); bind(menu, ACCEPT_LINE, "\r", "\n"); bindArrowKeys(menu); return menu; } public KeyMap safe() { KeyMap safe = new KeyMap<>(); bind(safe, SELF_INSERT, range("^@-^?")); bind(safe, ACCEPT_LINE, "\r", "\n"); bind(safe, SEND_BREAK, ctrl('G')); return safe; } public KeyMap visual() { KeyMap visual = new KeyMap<>(); bind(visual, UP_LINE, key(Capability.key_up), "k"); bind(visual, DOWN_LINE, key(Capability.key_down), "j"); bind(visual, this::deactivateRegion, esc()); bind(visual, EXCHANGE_POINT_AND_MARK, "o"); bind(visual, PUT_REPLACE_SELECTION, "p"); bind(visual, VI_DELETE, "x"); bind(visual, VI_OPER_SWAP_CASE, "~"); return visual; } public KeyMap viOpp() { KeyMap viOpp = new KeyMap<>(); bind(viOpp, UP_LINE, key(Capability.key_up), "k"); bind(viOpp, DOWN_LINE, key(Capability.key_down), "j"); bind(viOpp, VI_CMD_MODE, esc()); return viOpp; } private void bind(KeyMap map, String widget, Iterable keySeqs) { map.bind(new Reference(widget), keySeqs); } private void bind(KeyMap map, String widget, CharSequence... keySeqs) { map.bind(new Reference(widget), keySeqs); } private void bind(KeyMap map, Widget widget, CharSequence... keySeqs) { map.bind(widget, keySeqs); } private String key(Capability capability) { return KeyMap.key(terminal, capability); } private void bindArrowKeys(KeyMap map) { bind(map, UP_LINE_OR_SEARCH, key(Capability.key_up)); bind(map, DOWN_LINE_OR_SEARCH, key(Capability.key_down)); bind(map, BACKWARD_CHAR, key(Capability.key_left)); bind(map, FORWARD_CHAR, key(Capability.key_right)); bind(map, BEGINNING_OF_LINE, key(Capability.key_home)); bind(map, END_OF_LINE, key(Capability.key_end)); bind(map, DELETE_CHAR, key(Capability.key_dc)); bind(map, KILL_WHOLE_LINE, key(Capability.key_dl)); bind(map, OVERWRITE_MODE, key(Capability.key_ic)); bind(map, MOUSE, key(Capability.key_mouse)); } /** * Bind special chars defined by the terminal instead of * the default bindings */ private void bindConsoleChars(KeyMap keyMap, Attributes attr) { if (attr != null) { rebind(keyMap, BACKWARD_DELETE_CHAR, del(), (char) attr.getControlChar(ControlChar.VERASE)); rebind(keyMap, BACKWARD_KILL_WORD, ctrl('W'), (char) attr.getControlChar(ControlChar.VWERASE)); rebind(keyMap, KILL_WHOLE_LINE, ctrl('U'), (char) attr.getControlChar(ControlChar.VKILL)); rebind(keyMap, QUOTED_INSERT, ctrl('V'), (char) attr.getControlChar(ControlChar.VLNEXT)); } } private void rebind(KeyMap keyMap, String operation, String prevBinding, char newBinding) { if (newBinding > 0 && newBinding < 128) { Reference ref = new Reference(operation); bind(keyMap, SELF_INSERT, prevBinding); keyMap.bind(ref, Character.toString(newBinding)); } } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/ReaderUtils.java000066400000000000000000000042161311544710100270600ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import org.jline.reader.LineReader; public class ReaderUtils { private ReaderUtils() { } public static boolean isSet(LineReader reader, LineReader.Option option) { return reader != null && reader.isSet(option); } public static String getString(LineReader reader, String name, String def) { Object v = reader != null ? reader.getVariable(name) : null; return v != null ? v.toString() : def; } public static boolean getBoolean(LineReader reader, String name, boolean def) { Object v = reader != null ? reader.getVariable(name) : null; if (v instanceof Boolean) { return (Boolean) v; } else if (v != null) { String s = v.toString(); return s.isEmpty() || s.equalsIgnoreCase("on") || s.equalsIgnoreCase("1") || s.equalsIgnoreCase("true"); } return def; } public static int getInt(LineReader reader, String name, int def) { int nb = def; Object v = reader != null ? reader.getVariable(name) : null; if (v instanceof Number) { return ((Number) v).intValue(); } else if (v != null) { nb = 0; try { nb = Integer.parseInt(v.toString()); } catch (NumberFormatException e) { // Ignore } } return nb; } public static long getLong(LineReader reader, String name, long def) { long nb = def; Object v = reader != null ? reader.getVariable(name) : null; if (v instanceof Number) { return ((Number) v).longValue(); } else if (v != null) { nb = 0; try { nb = Long.parseLong(v.toString()); } catch (NumberFormatException e) { // Ignore } } return nb; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/UndoTree.java000066400000000000000000000031641311544710100263630ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.util.function.Consumer; /** * Simple undo tree. * Note that the first added state can't be undone */ public class UndoTree { private final Consumer state; private final Node parent; private Node current; public UndoTree(Consumer s) { state = s; parent = new Node(null); parent.left = parent; clear(); } public void clear() { current = parent; } public void newState(T state) { Node node = new Node(state); current.right = node; node.left = current; current = node; } public boolean canUndo() { return current.left != parent; } public boolean canRedo() { return current.right != null; } public void undo() { if (!canUndo()) { throw new IllegalStateException("Cannot undo."); } current = current.left; state.accept(current.state); } public void redo() { if (!canRedo()) { throw new IllegalStateException("Cannot redo."); } current = current.right; state.accept(current.state); } private class Node { private final T state; private Node left = null; private Node right = null; public Node(T s) { state = s; } } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/completer/000077500000000000000000000000001311544710100257615ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/completer/AggregateCompleter.java000066400000000000000000000045301311544710100323670ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.completer; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Objects; import org.jline.reader.Candidate; import org.jline.reader.Completer; import org.jline.reader.LineReader; import org.jline.reader.ParsedLine; /** * Completer which contains multiple completers and aggregates them together. * * @author Jason Dillon * @since 2.3 */ public class AggregateCompleter implements Completer { private final Collection completers; /** * Construct an AggregateCompleter with the given completers. * The completers will be used in the order given. * * @param completers the completers */ public AggregateCompleter(final Completer... completers) { this(Arrays.asList(completers)); } /** * Construct an AggregateCompleter with the given completers. * The completers will be used in the order given. * * @param completers the completers */ public AggregateCompleter(Collection completers) { assert completers != null; this.completers = completers; } /** * Retrieve the collection of completers currently being aggregated. * * @return the aggregated completers */ public Collection getCompleters() { return completers; } /** * Perform a completion operation across all aggregated completers. * * @see Completer#complete(LineReader, ParsedLine, List) */ public void complete(LineReader reader, final ParsedLine line, final List candidates) { Objects.requireNonNull(line); Objects.requireNonNull(candidates); for (Completer completer : completers) { completer.complete(reader, line, candidates); } } /** * @return a string representing the aggregated completers */ @Override public String toString() { return getClass().getSimpleName() + "{" + "completers=" + completers + '}'; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/completer/ArgumentCompleter.java000066400000000000000000000111111311544710100322540ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.completer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Objects; import org.jline.reader.Candidate; import org.jline.reader.Completer; import org.jline.reader.LineReader; import org.jline.reader.ParsedLine; /** * A {@link Completer} implementation that invokes a child completer using the appropriate separator argument. * This can be used instead of the individual completers having to know about argument parsing semantics. * * @author Marc Prud'hommeaux * @author Jason Dillon * @since 2.3 */ public class ArgumentCompleter implements Completer { private final List completers = new ArrayList<>(); private boolean strict = true; /** * Create a new completer. * * @param completers The embedded completers */ public ArgumentCompleter(final Collection completers) { Objects.requireNonNull(completers); this.completers.addAll(completers); } /** * Create a new completer. * * @param completers The embedded completers */ public ArgumentCompleter(final Completer... completers) { this(Arrays.asList(completers)); } /** * 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 success * if all the completions from arguments 0-(N-1) also succeed. * * @return True if strict. * @since 2.3 */ public boolean isStrict() { return this.strict; } /** * @since 2.3 */ public List getCompleters() { return completers; } public void complete(LineReader reader, ParsedLine line, final List candidates) { Objects.requireNonNull(line); Objects.requireNonNull(candidates); if (line.wordIndex() < 0) { return; } List completers = getCompleters(); Completer completer; // if we are beyond the end of the completers, just use the last one if (line.wordIndex() >= completers.size()) { completer = completers.get(completers.size() - 1); } else { completer = completers.get(line.wordIndex()); } // ensure that all the previous completers are successful before allowing this completer to pass (only if strict). for (int i = 0; isStrict() && (i < line.wordIndex()); i++) { Completer sub = completers.get(i >= completers.size() ? (completers.size() - 1) : i); List args = line.words(); String arg = (args == null || i >= args.size()) ? "" : args.get(i).toString(); List subCandidates = new LinkedList<>(); sub.complete(reader, new ArgumentLine(arg, arg.length()), subCandidates); boolean found = false; for (Candidate cand : subCandidates) { if (cand.value().equals(arg)) { found = true; break; } } if (!found) { return; } } completer.complete(reader, line, candidates); } public static class ArgumentLine implements ParsedLine { private final String word; private final int cursor; public ArgumentLine(String word, int cursor) { this.word = word; this.cursor = cursor; } @Override public String word() { return word; } @Override public int wordCursor() { return cursor; } @Override public int wordIndex() { return 0; } @Override public List words() { return Collections.singletonList(word); } @Override public String line() { return word; } @Override public int cursor() { return cursor; } } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/completer/EnumCompleter.java000066400000000000000000000015051311544710100314040ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.completer; import java.util.Objects; import org.jline.reader.Candidate; import org.jline.reader.Completer; /** * {@link Completer} for {@link Enum} names. * * @author Jason Dillon * @since 2.3 */ public class EnumCompleter extends StringsCompleter { public EnumCompleter(Class> source) { Objects.requireNonNull(source); for (Enum n : source.getEnumConstants()) { candidates.add(new Candidate(n.name().toLowerCase())); } } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/completer/FileNameCompleter.java000066400000000000000000000106001311544710100321540ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.completer; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import org.jline.reader.Candidate; import org.jline.reader.Completer; import org.jline.reader.LineReader; import org.jline.reader.LineReader.Option; import org.jline.reader.ParsedLine; import org.jline.terminal.Terminal; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; /** * A file name completer takes the buffer and issues a list of * potential completions. *

* This completer tries to behave as similar as possible to * bash's file name completion (using GNU readline) * with the following exceptions: *

*

    *
  • Candidates that are directories will end with "/"
  • *
  • Wildcard regular expressions are not evaluated or replaced
  • *
  • The "~" character can be used to represent the user's home, * but it cannot complete to other users' homes, since java does * not provide any way of determining that easily
  • *
* * @author Marc Prud'hommeaux * @author Jason Dillon * @since 2.3 */ public class FileNameCompleter implements Completer { public void complete(LineReader reader, ParsedLine commandLine, final List candidates) { assert commandLine != null; assert candidates != null; String buffer = commandLine.word().substring(0, commandLine.wordCursor()); Path current; String curBuf; int lastSep = buffer.lastIndexOf(File.separator); if (lastSep >= 0) { curBuf = buffer.substring(0, lastSep + 1); if (curBuf.startsWith("~")) { if (curBuf.startsWith("~/")) { current = getUserHome().resolve(curBuf.substring(2)); } else { current = getUserHome().getParent().resolve(curBuf.substring(1)); } } else { current = getUserDir().resolve(curBuf); } } else { curBuf = ""; current = getUserDir(); } try { Files.newDirectoryStream(current, this::accept).forEach(p -> { String value = curBuf + p.getFileName().toString(); if (Files.isDirectory(p)) { candidates.add(new Candidate( value + (reader.isSet(Option.AUTO_PARAM_SLASH) ? "/" : ""), getDisplay(reader.getTerminal(), p), null, null, reader.isSet(Option.AUTO_REMOVE_SLASH) ? "/" : null, null, false)); } else { candidates.add(new Candidate(value, getDisplay(reader.getTerminal(), p), null, null, null, null, true)); } }); } catch (IOException e) { // Ignore } } protected boolean accept(Path path) { try { return !Files.isHidden(path); } catch (IOException e) { return false; } } protected Path getUserDir() { return Paths.get(System.getProperty("user.dir")); } protected Path getUserHome() { return Paths.get(System.getProperty("user.home")); } protected String getDisplay(Terminal terminal, Path p) { // TODO: use $LS_COLORS for output String name = p.getFileName().toString(); if (Files.isDirectory(p)) { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.styled(AttributedStyle.BOLD.foreground(AttributedStyle.RED), name); sb.append("/"); name = sb.toAnsi(terminal); } else if (Files.isSymbolicLink(p)) { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.styled(AttributedStyle.BOLD.foreground(AttributedStyle.RED), name); sb.append("@"); name = sb.toAnsi(terminal); } return name; } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/completer/NullCompleter.java000066400000000000000000000016011311544710100314070ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.completer; import java.util.List; import org.jline.reader.Candidate; import org.jline.reader.Completer; import org.jline.reader.LineReader; import org.jline.reader.ParsedLine; /** * Null completer. * * @author Marc Prud'hommeaux * @author Jason Dillon * @since 2.3 */ public final class NullCompleter implements Completer { public static final NullCompleter INSTANCE = new NullCompleter(); public void complete(LineReader reader, final ParsedLine line, final List candidates) { } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/completer/StringsCompleter.java000066400000000000000000000027131311544710100321330ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.completer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.jline.reader.Candidate; import org.jline.reader.Completer; import org.jline.reader.LineReader; import org.jline.reader.ParsedLine; import org.jline.utils.AttributedString; /** * Completer for a set of strings. * * @author Jason Dillon * @since 2.3 */ public class StringsCompleter implements Completer { protected final Collection candidates = new ArrayList<>(); public StringsCompleter() { } public StringsCompleter(String... strings) { this(Arrays.asList(strings)); } public StringsCompleter(Iterable strings) { assert strings != null; for (String string : strings) { candidates.add(new Candidate(AttributedString.stripAnsi(string), string, null, null, null, null, true)); } } public void complete(LineReader reader, final ParsedLine commandLine, final List candidates) { assert commandLine != null; assert candidates != null; candidates.addAll(this.candidates); } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/completer/package-info.java000066400000000000000000000005341311544710100311520ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /** * JLine 3. * * @since 3.0 */ package org.jline.reader.impl.completer; jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/history/000077500000000000000000000000001311544710100254705ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/history/DefaultHistory.java000066400000000000000000000316431311544710100313100ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.history; import java.io.*; import java.nio.file.*; import java.time.Instant; import java.util.*; import org.jline.reader.History; import org.jline.reader.LineReader; import org.jline.utils.Log; import static org.jline.reader.LineReader.HISTORY_IGNORE; import static org.jline.reader.impl.ReaderUtils.*; /** * {@link History} using a file for persistent backing. *

* Implementers should install shutdown hook to call {@link DefaultHistory#save} * to save history to disk. */ public class DefaultHistory implements History { public static final int DEFAULT_HISTORY_SIZE = 500; public static final int DEFAULT_HISTORY_FILE_SIZE = 10000; private final LinkedList items = new LinkedList<>(); private LineReader reader; private int loaded = 0; private int offset = 0; private int index = 0; public DefaultHistory() { } public DefaultHistory(LineReader reader) { attach(reader); } private Path getPath() { Object obj = reader != null ? reader.getVariables().get(LineReader.HISTORY_FILE) : null; if (obj instanceof Path) { return (Path) obj; } else if (obj instanceof File) { return ((File) obj).toPath(); } else if (obj != null) { return Paths.get(obj.toString()); } else { return null; } } @Override public void attach(LineReader reader) { if (this.reader != reader) { this.reader = reader; try { load(); } catch (IOException e) { Log.warn("Failed to load history", e); } } } @Override public void load() throws IOException { Path path = getPath(); if (path != null) { try { if (Files.exists(path)) { Log.trace("Loading history from: ", path); try (BufferedReader reader = Files.newBufferedReader(path)) { internalClear(); reader.lines().forEach(l -> { int idx = l.indexOf(':'); Instant time = Instant.ofEpochMilli(Long.parseLong(l.substring(0, idx))); String line = unescape(l.substring(idx + 1)); internalAdd(time, line); }); loaded = items.size(); maybeResize(); } } } catch (IOException e) { Log.debug("Failed to load history; clearing", e); internalClear(); throw e; } } } @Override public void purge() throws IOException { internalClear(); Path path = getPath(); if (path != null) { Log.trace("Purging history from: ", path); Files.deleteIfExists(path); } } @Override public void save() throws IOException { Path path = getPath(); if (path != null) { Log.trace("Saving history to: ", path); Files.createDirectories(path.toAbsolutePath().getParent()); // Append new items to the history file try (BufferedWriter writer = Files.newBufferedWriter(path.toAbsolutePath(), StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { for (Entry entry : items.subList(loaded, items.size())) { writer.append(format(entry)); } } // If we are over 25% max size, trim history file int max = getInt(reader, LineReader.HISTORY_FILE_SIZE, DEFAULT_HISTORY_FILE_SIZE); if (last() > max + max / 4) { trimHistory(path, max); } } loaded = items.size(); } protected void trimHistory(Path path, int max) throws IOException { Log.trace("Trimming history path: ", path); // Load all history entries LinkedList allItems = new LinkedList<>(); try (BufferedReader reader = Files.newBufferedReader(path)) { reader.lines().forEach(l -> { int idx = l.indexOf(':'); Instant time = Instant.ofEpochMilli(Long.parseLong(l.substring(0, idx))); String line = unescape(l.substring(idx + 1)); allItems.add(new EntryImpl(allItems.size(), time, line)); }); } // Remove duplicates doTrimHistory(allItems, max); // Write history Path temp = Files.createTempFile(path.toAbsolutePath().getParent(), path.getFileName().toString(), ".tmp"); try (BufferedWriter writer = Files.newBufferedWriter(temp, StandardOpenOption.WRITE)) { for (Entry entry : allItems) { writer.append(format(entry)); } } Files.move(temp, path, StandardCopyOption.REPLACE_EXISTING); // Keep items in memory internalClear(); items.addAll(allItems); loaded = items.size(); maybeResize(); } private void internalClear() { offset = 0; index = 0; loaded = 0; items.clear(); } static void doTrimHistory(List allItems, int max) { int idx = 0; while (idx < allItems.size()) { int ridx = allItems.size() - idx - 1; String line = allItems.get(ridx).line().trim(); ListIterator iterator = allItems.listIterator(ridx); while (iterator.hasPrevious()) { String l = iterator.previous().line(); if (line.equals(l.trim())) { iterator.remove(); } } idx++; } while (allItems.size() > max) { allItems.remove(0); } } public int size() { return items.size(); } public boolean isEmpty() { return items.isEmpty(); } public int index() { return offset + index; } public int first() { return offset; } public int last() { return offset + items.size() - 1; } private String format(Entry entry) { return Long.toString(entry.time().toEpochMilli()) + ":" + escape(entry.line()) + "\n"; } public String get(final int index) { return items.get(index - offset).line(); } @Override public void add(Instant time, String line) { Objects.requireNonNull(time); Objects.requireNonNull(line); if (getBoolean(reader, LineReader.DISABLE_HISTORY, false)) { return; } if (isSet(reader, LineReader.Option.HISTORY_IGNORE_SPACE) && line.startsWith(" ")) { return; } if (isSet(reader, LineReader.Option.HISTORY_REDUCE_BLANKS)) { line = line.trim(); } if (isSet(reader, LineReader.Option.HISTORY_IGNORE_DUPS)) { if (!items.isEmpty() && line.equals(items.getLast().line())) { return; } } if (matchPatterns(getString(reader, HISTORY_IGNORE, ""), line)) { return; } internalAdd(time, line); if (isSet(reader, LineReader.Option.HISTORY_INCREMENTAL)) { try { save(); } catch (IOException e) { Log.warn("Failed to save history", e); } } } protected boolean matchPatterns(String patterns, String line) { if (patterns == null || patterns.isEmpty()) { return false; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < patterns.length(); i++) { char ch = patterns.charAt(i); if (ch == '\\') { ch = patterns.charAt(++i); sb.append(ch); } else if (ch == ':') { sb.append('|'); } else if (ch == '*') { sb.append('.').append('*'); } } return line.matches(sb.toString()); } protected void internalAdd(Instant time, String line) { Entry entry = new EntryImpl(offset + items.size(), time, line); items.add(entry); maybeResize(); } private void maybeResize() { while (size() > getInt(reader, LineReader.HISTORY_SIZE, DEFAULT_HISTORY_SIZE)) { items.removeFirst(); loaded--; offset++; } index = size(); } public ListIterator iterator(int index) { return items.listIterator(index - offset); } static class EntryImpl implements Entry { private final int index; private final Instant time; private final String line; public EntryImpl(int index, Instant time, String line) { this.index = index; this.time = time; this.line = line; } public int index() { return index; } public Instant time() { return time; } public String line() { return line; } @Override public String toString() { return String.format("%d: %s", index, line); } } // // Navigation // /** * This moves the history to the last entry. This entry is one position * before the moveToEnd() position. * * @return Returns false if there were no history iterator or the history * index was already at the last entry. */ public boolean moveToLast() { int lastEntry = size() - 1; if (lastEntry >= 0 && lastEntry != index) { index = size() - 1; return true; } return false; } /** * Move to the specified index in the history */ public boolean moveTo(int index) { index -= offset; if (index >= 0 && index < size()) { this.index = index; return true; } return false; } /** * Moves the history index to the first entry. * * @return Return false if there are no iterator in the history or if the * history is already at the beginning. */ public boolean moveToFirst() { if (size() > 0 && index != 0) { index = 0; return true; } return false; } /** * Move to the end of the history buffer. This will be a blank entry, after * all of the other iterator. */ public void moveToEnd() { index = size(); } /** * Return the content of the current buffer. */ public String current() { if (index >= size()) { return ""; } return items.get(index).line(); } /** * Move the pointer to the previous element in the buffer. * * @return true if we successfully went to the previous element */ public boolean previous() { if (index <= 0) { return false; } index--; return true; } /** * Move the pointer to the next element in the buffer. * * @return true if we successfully went to the next element */ public boolean next() { if (index >= size()) { return false; } index++; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Entry e : this) { sb.append(e.toString()).append("\n"); } return sb.toString(); } private static String escape(String s) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); switch (ch) { case '\n': sb.append('\\'); sb.append('n'); break; case '\\': sb.append('\\'); sb.append('\\'); break; default: sb.append(ch); break; } } return sb.toString(); } private static String unescape(String s) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); switch (ch) { case '\\': ch = s.charAt(++i); if (ch == 'n') { sb.append('\n'); } else { sb.append(ch); } break; default: sb.append(ch); break; } } return sb.toString(); } } jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/impl/history/package-info.java000066400000000000000000000005321311544710100306570ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /** * JLine 3. * * @since 3.0 */ package org.jline.reader.impl.history; jline3-jline-3.3.1/reader/src/main/java/org/jline/reader/package-info.java000066400000000000000000000005141311544710100262150ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /** * JLine 3. * * @since 3.0 */ package org.jline.reader;jline3-jline-3.3.1/reader/src/test/000077500000000000000000000000001311544710100170065ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/000077500000000000000000000000001311544710100177275ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/000077500000000000000000000000001311544710100205165ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/jline/000077500000000000000000000000001311544710100216175ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/jline/keymap/000077500000000000000000000000001311544710100231055ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/jline/keymap/BindingReaderTest.java000066400000000000000000000053211311544710100273060ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.keymap; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.Charset; import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import org.jline.reader.Binding; import org.jline.reader.Reference; import org.jline.terminal.Size; import org.jline.terminal.impl.DumbTerminal; import org.jline.reader.impl.ReaderTestSupport.EofPipedInputStream; import org.jline.terminal.Terminal; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; public class BindingReaderTest { protected Terminal terminal; protected EofPipedInputStream in; protected ByteArrayOutputStream out; @Before public void setUp() throws Exception { Handler ch = new ConsoleHandler(); ch.setLevel(Level.FINEST); Logger logger = Logger.getLogger("org.jline"); logger.addHandler(ch); // Set the handler log level logger.setLevel(Level.INFO); in = new EofPipedInputStream(); out = new ByteArrayOutputStream(); terminal = new DumbTerminal("dumb", "dumb", in, out, "UTF-8"); terminal.setSize(new Size(160, 80)); } @Test public void testBindingReaderNoUnicode() { in.setIn(new ByteArrayInputStream("\uD834\uDD21abc".getBytes(Charset.forName("UTF-8")))); BindingReader reader = new BindingReader(terminal.reader()); KeyMap keyMap = new KeyMap<>(); keyMap.bind(new Reference("foo"), "b"); assertEquals(new Reference("foo"), reader.readBinding(keyMap)); assertEquals("b", reader.getLastBinding()); assertNull(reader.readBinding(keyMap)); } @Test public void testBindingReaderUnicode() { in.setIn(new ByteArrayInputStream("\uD834\uDD21abc".getBytes(Charset.forName("UTF-8")))); BindingReader reader = new BindingReader(terminal.reader()); KeyMap keyMap = new KeyMap<>(); keyMap.setUnicode(new Reference("insert")); keyMap.bind(new Reference("foo"), "b"); assertEquals(new Reference("insert"), reader.readBinding(keyMap)); assertEquals("\uD834\uDD21", reader.getLastBinding()); assertEquals(new Reference("foo"), reader.readBinding(keyMap)); assertEquals("b", reader.getLastBinding()); assertNull(reader.readBinding(keyMap)); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/keymap/KeyMapTest.java000066400000000000000000000140441311544710100260010ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.keymap; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import org.jline.reader.Binding; import org.jline.reader.Reference; import org.jline.reader.impl.LineReaderImpl; import org.jline.terminal.Size; import org.jline.terminal.impl.DumbTerminal; import org.jline.reader.impl.ReaderTestSupport.EofPipedInputStream; import org.jline.terminal.Terminal; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import static org.jline.keymap.KeyMap.alt; import static org.jline.keymap.KeyMap.display; import static org.jline.keymap.KeyMap.range; import static org.jline.keymap.KeyMap.translate; import static org.jline.reader.LineReader.ACCEPT_LINE; import static org.jline.reader.LineReader.BACKWARD_WORD; import static org.jline.reader.LineReader.COMPLETE_WORD; import static org.jline.reader.LineReader.DOWN_HISTORY; import static org.jline.reader.LineReader.KILL_WHOLE_LINE; import static org.jline.reader.LineReader.SEND_BREAK; import static org.jline.reader.LineReader.UP_HISTORY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; public class KeyMapTest { protected Terminal terminal; protected EofPipedInputStream in; protected ByteArrayOutputStream out; @Before public void setUp() throws Exception { Handler ch = new ConsoleHandler(); ch.setLevel(Level.FINEST); Logger logger = Logger.getLogger("org.jline"); logger.addHandler(ch); // Set the handler log level logger.setLevel(Level.INFO); in = new EofPipedInputStream(); out = new ByteArrayOutputStream(); terminal = new DumbTerminal(in, out); terminal.setSize(new Size(160, 80)); } @Test public void testBound() throws Exception { KeyMap map = new LineReaderImpl(terminal).emacs(); Assert.assertEquals(new Reference(COMPLETE_WORD), map.getBound("\u001B\u001B")); assertEquals(new Reference(BACKWARD_WORD), map.getBound(alt("b"))); map.bindIfNotBound(new Reference(UP_HISTORY), "\033[0A"); assertEquals(new Reference(UP_HISTORY), map.getBound("\033[0A")); map.bind(new Reference(DOWN_HISTORY), "\033[0AB"); assertEquals(new Reference(UP_HISTORY), map.getBound("\033[0A")); assertEquals(new Reference(DOWN_HISTORY), map.getBound("\033[0AB")); int[] remaining = new int[1]; assertEquals(new Reference(COMPLETE_WORD), map.getBound("\u001B\u001Ba", remaining)); assertEquals(1, remaining[0]); map.bind(new Reference("anotherkey"), translate("^Uc")); assertEquals(new Reference("anotherkey"), map.getBound(translate("^Uc"), remaining)); assertEquals(0, remaining[0]); assertEquals(new Reference(KILL_WHOLE_LINE), map.getBound(translate("^Ua"), remaining)); assertEquals(1, remaining[0]); } @Test public void testRemaining() throws Exception { KeyMap map = new KeyMap<>(); int[] remaining = new int[1]; assertNull(map.getBound("ab", remaining)); map.bind(new Reference(SEND_BREAK), "ab"); assertNull(map.getBound("a", remaining)); assertEquals(-1, remaining[0]); assertEquals(new Reference(SEND_BREAK), map.getBound("ab", remaining)); assertEquals(0, remaining[0]); assertEquals(new Reference(SEND_BREAK), map.getBound("abc", remaining)); assertEquals(1, remaining[0]); map.bind(new Reference(ACCEPT_LINE), "abc"); assertNull(map.getBound("a", remaining)); assertEquals(-1, remaining[0]); assertEquals(new Reference(SEND_BREAK), map.getBound("ab", remaining)); assertEquals(-1, remaining[0]); assertEquals(new Reference(SEND_BREAK), map.getBound("abd", remaining)); assertEquals(1, remaining[0]); assertEquals(new Reference(ACCEPT_LINE), map.getBound("abc", remaining)); assertEquals(0, remaining[0]); map.unbind("abc"); assertNull(map.getBound("a", remaining)); assertEquals(-1, remaining[0]); assertEquals(new Reference(SEND_BREAK), map.getBound("ab", remaining)); assertEquals(0, remaining[0]); assertEquals(new Reference(SEND_BREAK), map.getBound("abc", remaining)); assertEquals(1, remaining[0]); } @Test public void testSort() { List strings = new ArrayList<>(); strings.add("abc"); strings.add("ab"); strings.add("ad"); Collections.sort(strings, KeyMap.KEYSEQ_COMPARATOR); assertEquals("ab", strings.get(0)); assertEquals("ad", strings.get(1)); assertEquals("abc", strings.get(2)); } @Test public void testTranslate() { assertEquals("\\\u0007\b\u001b\u001b\f\n\r\t\u000b\u0053\u0045\u2345", translate("\\\\\\a\\b\\e\\E\\f\\n\\r\\t\\v\\123\\x45\\u2345")); assertEquals("\u0001\u0001\u0002\u0002\u0003\u0003\u007f^", translate("\\Ca\\CA\\C-B\\C-b^c^C^?^^")); assertEquals("\u001b3", translate("'\\e3'")); assertEquals("\u001b3", translate("\"\\e3\"")); } @Test public void testDisplay() { assertEquals("\"\\\\^G^H^[^L^J^M^I\\u0098\\u2345\"", display("\\\u0007\b\u001b\f\n\r\t\u0098\u2345")); assertEquals("\"^A^B^C^?\\^\\\\\"", display("\u0001\u0002\u0003\u007f^\\")); } @Test public void testRange() { Collection range = range("a^A-a^D"); assertEquals(Arrays.asList(translate("a^A"), translate("a^B"), translate("a^C"), translate("a^D")), range); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/000077500000000000000000000000001311544710100230615ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/completer/000077500000000000000000000000001311544710100250535ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/completer/ArgumentCompleterTest.java000066400000000000000000000052021311544710100322120ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.completer; import org.jline.reader.impl.ReaderTestSupport; import org.jline.reader.impl.completer.ArgumentCompleter; import org.jline.reader.impl.completer.StringsCompleter; import org.junit.Test; /** * Tests for {@link ArgumentCompleter}. * * @author Marc Prud'hommeaux */ public class ArgumentCompleterTest extends ReaderTestSupport { @Test public void test1() throws Exception { reader.setCompleter(new ArgumentCompleter(new StringsCompleter("foo", "bar", "baz"))); assertBuffer("foo foo ", new TestBuffer("foo f").tab()); assertBuffer("foo ba", new TestBuffer("foo b").tab()); assertBuffer("foo ba", new TestBuffer("foo ba").tab()); assertBuffer("foo baz ", new TestBuffer("foo baz").tab()); // test completion in the mid range assertBuffer("foo baz", new TestBuffer("f baz").left().left().left().left().tab()); assertBuffer("ba foo", new TestBuffer("b foo").left().left().left().left().tab()); assertBuffer("foo ba baz", new TestBuffer("foo b baz").left().left().left().left().tab()); assertBuffer("foo foo baz", new TestBuffer("foo f baz").left().left().left().left().tab()); } @Test public void testMultiple() throws Exception { ArgumentCompleter argCompleter = new ArgumentCompleter( new StringsCompleter("bar", "baz"), new StringsCompleter("foo"), new StringsCompleter("ree")); reader.setCompleter(argCompleter); assertBuffer("bar foo ", new TestBuffer("bar f").tab()); assertBuffer("baz foo ", new TestBuffer("baz f").tab()); // co completion of 2nd arg in strict mode when 1st argument is not matched exactly assertBuffer("ba f", new TestBuffer("ba f").tab()); assertBuffer("bar fo r", new TestBuffer("bar fo r").tab()); argCompleter.setStrict(false); assertBuffer("ba foo ", new TestBuffer("ba f").tab()); assertBuffer("ba fo ree ", new TestBuffer("ba fo r").tab()); } @Test public void test2() throws Exception { reader.setCompleter( new ArgumentCompleter( new StringsCompleter("some", "any"), new StringsCompleter("foo", "bar", "baz"))); assertBuffer("some foo ", new TestBuffer("some fo").tab()); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/completer/DefaultParserTest.java000066400000000000000000000047131311544710100313240ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.completer; import java.util.Arrays; import org.jline.reader.ParsedLine; import org.jline.reader.impl.DefaultParser; import org.jline.reader.impl.ReaderTestSupport; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; /** * Tests for {@link DefaultParser}. * * @author Mike Drob */ public class DefaultParserTest extends ReaderTestSupport { ParsedLine delimited; DefaultParser parser; @Before public void setUp() { parser = new DefaultParser(); } @Test public void testDelimit() { // These all passed before adding quoting and escaping delimited = parser.parse("1 2 3", 0); assertEquals(Arrays.asList("1", "2", "3"), delimited.words()); delimited = parser.parse("1 2 3", 0); assertEquals(Arrays.asList("1", "2", "3"), delimited.words()); } @Test public void testQuotedDelimit() { delimited = parser.parse("\"1 2\" 3", 0); assertEquals(Arrays.asList("1 2", "3"), delimited.words()); delimited = parser.parse("'1 2' 3", 0); assertEquals(Arrays.asList("1 2", "3"), delimited.words()); delimited = parser.parse("1 '2 3'", 0); assertEquals(Arrays.asList("1", "2 3"), delimited.words()); } @Test public void testMixedQuotes() { delimited = parser.parse("\"1' '2\" 3", 0); assertEquals(Arrays.asList("1' '2", "3"), delimited.words()); delimited = parser.parse("'1\" 2' 3\"", 0); assertEquals(Arrays.asList("1\" 2", "3"), delimited.words()); } @Test public void testEscapedSpace() { delimited = parser.parse("1\\ 2 3", 0); assertEquals(Arrays.asList("1 2", "3"), delimited.words()); } @Test public void testEscapedQuotes() { delimited = parser.parse("'1 \\'2' 3", 0); assertEquals(Arrays.asList("1 '2", "3"), delimited.words()); delimited = parser.parse("\\'1 '2' 3", 0); assertEquals(Arrays.asList("'1", "2", "3"), delimited.words()); delimited = parser.parse("'1 '2\\' 3", 0); assertEquals(Arrays.asList("1 ", "2'", "3"), delimited.words()); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/completer/NullCompleterTest.java000066400000000000000000000015721311544710100313500ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.completer; import org.jline.reader.impl.ReaderTestSupport; import org.jline.reader.impl.completer.NullCompleter; import org.junit.Test; /** * Tests for {@link NullCompleter}. * * @author Jason Dillon */ public class NullCompleterTest extends ReaderTestSupport { @Test public void test1() throws Exception { reader.setCompleter(NullCompleter.INSTANCE); assertBuffer("f", new TestBuffer("f").tab()); assertBuffer("ba", new TestBuffer("ba").tab()); assertBuffer("baz", new TestBuffer("baz").tab()); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/completer/StringsCompleterTest.java000066400000000000000000000020111311544710100320540ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.completer; import org.jline.reader.impl.ReaderTestSupport; import org.jline.reader.impl.completer.StringsCompleter; import org.junit.Test; /** * Tests for {@link StringsCompleter}. * * @author Marc Prud'hommeaux */ public class StringsCompleterTest extends ReaderTestSupport { @Test public void test1() throws Exception { reader.setCompleter(new StringsCompleter("foo", "bar", "baz")); assertBuffer("foo ", new TestBuffer("f").tab()); // single tab completes to unambiguous "ba" assertBuffer("ba", new TestBuffer("b").tab()); assertBuffer("ba", new TestBuffer("ba").tab()); assertBuffer("baz ", new TestBuffer("baz").tab()); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/000077500000000000000000000000001311544710100240225ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/BufferTest.java000066400000000000000000000031331311544710100267360ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class BufferTest { @Test public void testUpDown() { BufferImpl buffer = new BufferImpl(); buffer.write("a\ncd\nefg\nhijk\nlmn\nop\nq"); buffer.cursor(13); // after k assertTrue(buffer.up()); // after g assertEquals(8, buffer.cursor()); buffer.move(-1); // on g assertEquals(7, buffer.cursor()); assertTrue(buffer.up()); // after d assertEquals(4, buffer.cursor()); assertTrue(buffer.up()); assertEquals(1, buffer.cursor()); assertFalse(buffer.up()); assertEquals(1, buffer.cursor()); assertTrue(buffer.down()); assertEquals(4, buffer.cursor()); assertTrue(buffer.down()); assertEquals(7, buffer.cursor()); assertTrue(buffer.down()); assertEquals(11, buffer.cursor()); assertTrue(buffer.down()); assertEquals(16, buffer.cursor()); assertTrue(buffer.down()); assertEquals(20, buffer.cursor()); assertTrue(buffer.down()); assertEquals(22, buffer.cursor()); assertFalse(buffer.down()); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/CompletionTest.java000066400000000000000000000100011311544710100276260ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.io.IOException; import org.jline.reader.Completer; import org.jline.reader.LineReader.Option; import org.jline.reader.Reference; import org.jline.reader.impl.completer.AggregateCompleter; import org.jline.reader.impl.completer.ArgumentCompleter; import org.jline.reader.impl.completer.NullCompleter; import org.jline.reader.impl.completer.StringsCompleter; import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class CompletionTest extends ReaderTestSupport { @Test public void testListAndMenu() throws IOException { reader.setCompleter(new StringsCompleter("foo", "foobar")); reader.unsetOpt(Option.MENU_COMPLETE); reader.unsetOpt(Option.AUTO_LIST); reader.unsetOpt(Option.AUTO_MENU); reader.unsetOpt(Option.LIST_AMBIGUOUS); assertBuffer("foo", new TestBuffer("fo\t")); assertFalse(reader.list); assertFalse(reader.menu); assertBuffer("foo", new TestBuffer("fo\t\t")); assertFalse(reader.list); assertFalse(reader.menu); reader.setOpt(Option.AUTO_LIST); reader.unsetOpt(Option.AUTO_MENU); reader.unsetOpt(Option.LIST_AMBIGUOUS); assertBuffer("foo", new TestBuffer("fo\t")); assertTrue(reader.list); assertFalse(reader.menu); reader.setOpt(Option.AUTO_LIST); reader.unsetOpt(Option.AUTO_MENU); reader.setOpt(Option.LIST_AMBIGUOUS); assertBuffer("foo", new TestBuffer("fo\t")); assertFalse(reader.list); assertFalse(reader.menu); assertBuffer("foo", new TestBuffer("fo\t\t")); assertTrue(reader.list); assertFalse(reader.menu); reader.unsetOpt(Option.AUTO_LIST); reader.setOpt(Option.AUTO_MENU); reader.unsetOpt(Option.LIST_AMBIGUOUS); assertBuffer("foo", new TestBuffer("fo\t")); assertFalse(reader.list); assertFalse(reader.menu); assertBuffer("foo", new TestBuffer("fo\t\t")); assertFalse(reader.list); assertTrue(reader.menu); reader.setOpt(Option.AUTO_LIST); reader.setOpt(Option.AUTO_MENU); reader.unsetOpt(Option.LIST_AMBIGUOUS); assertBuffer("foo", new TestBuffer("fo\t")); assertTrue(reader.list); assertFalse(reader.menu); assertBuffer("foo", new TestBuffer("fo\t\t")); assertTrue(reader.list); assertTrue(reader.menu); reader.setOpt(Option.AUTO_LIST); reader.setOpt(Option.AUTO_MENU); reader.setOpt(Option.LIST_AMBIGUOUS); assertBuffer("foo", new TestBuffer("fo\t")); assertFalse(reader.list); assertFalse(reader.menu); assertBuffer("foo", new TestBuffer("fo\t\t")); assertTrue(reader.list); assertFalse(reader.menu); assertBuffer("foo", new TestBuffer("fo\t\t\t")); assertTrue(reader.list); assertTrue(reader.menu); } @Test public void testCompletePrefix() throws Exception { Completer nil = new NullCompleter(); Completer read = new StringsCompleter("read"); Completer and = new StringsCompleter("and"); Completer save = new StringsCompleter("save"); Completer aggregator = new AggregateCompleter( new ArgumentCompleter(read, and, save, nil) ); reader.setCompleter(aggregator); reader.getKeys().bind(new Reference("complete-word"), "\t"); assertLine("read and ", new TestBuffer("read an\t\n")); assertLine("read and ", new TestBuffer("read an\033[D\t\n")); reader.getKeys().bind(new Reference("complete-prefix"), "\t"); assertLine("read and nd", new TestBuffer("read and\033[D\033[D\t\n")); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/DigitArgumentTest.java000066400000000000000000000327271311544710100303030ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import org.jline.reader.Reference; import org.junit.Test; import static org.jline.keymap.KeyMap.alt; import static org.jline.keymap.KeyMap.ctrl; import static org.jline.keymap.KeyMap.translate; import static org.jline.reader.impl.LineReaderImpl.EMACS; public class DigitArgumentTest extends ReaderTestSupport { @Test public void testMoveChar() throws Exception { reader.setKeyMap(EMACS); TestBuffer b = (new TestBuffer("0123456789")) .append(alt('8')) .append(ctrl('B')) .append(alt('2')) .append(ctrl('F')) .append(ctrl('K')) .enter(); assertLine("0123", b, true); } @Test public void testSelfInsert() throws Exception { reader.setKeyMap(EMACS); TestBuffer b = (new TestBuffer()) .append(alt('4')) .append("0") .append(alt('2')) .append(alt('\r')) .enter(); assertLine("0000\n\n", b, true); } @Test public void testMoveWord() throws Exception { reader.setKeyMap(EMACS); TestBuffer b = (new TestBuffer("abc def ghi klm nop")) .append(alt('2')) .append(alt('b')) .append(alt('-')) .append(alt('2')) .append(alt('f')) .append(ctrl('K')) .enter(); assertLine("abc ", b, true); } @Test public void testCaseTransform() throws Exception { reader.setKeyMap(EMACS); TestBuffer b = (new TestBuffer("abc def ghi klm nop")) .append(ctrl('A')) .append(alt('3')) .append(alt('u')) .append(alt('b')) .append(alt('3')) .append(alt('c')) .append(alt('b')) .append(alt('l')) .enter(); assertLine("ABC DEF Ghi Klm nop", b, true); } @Test public void testTransposeChars() throws Exception { reader.setKeyMap(EMACS); TestBuffer b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('2')) .append(alt('b')) .append(ctrl('T')) .enter(); assertLine("bacd\nefgh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('2')) .append(alt('b')) .append(alt('2')) .append(ctrl('T')) .enter(); assertLine("bcad\nefgh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('2')) .append(alt('b')) .append(alt('3')) .append(ctrl('T')) .enter(); assertLine("bcda\nefgh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('2')) .append(alt('b')) .append(alt('4')) .append(ctrl('T')) .enter(); assertLine("bcad\nefgh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('b')) .append(ctrl('T')) .enter(); assertLine("abcd\nfegh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('b')) .append(alt('2')) .append(ctrl('T')) .enter(); assertLine("abcd\nfgeh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('b')) .append(alt('3')) .append(ctrl('T')) .enter(); assertLine("abcd\nfghe", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('b')) .append(alt('4')) .append(ctrl('T')) .enter(); assertLine("abcd\nfgeh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('b')) .append(ctrl('B')) .append(alt('-')) .append(ctrl('T')) .enter(); assertLine("abdc\nefgh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('b')) .append(ctrl('B')) .append(alt('-')) .append(alt('2')) .append(ctrl('T')) .enter(); assertLine("adbc\nefgh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('b')) .append(ctrl('B')) .append(alt('-')) .append(alt('3')) .append(ctrl('T')) .enter(); assertLine("dabc\nefgh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('b')) .append(ctrl('B')) .append(alt('-')) .append(alt('4')) .append(ctrl('T')) .enter(); assertLine("adbc\nefgh", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('-')) .append(ctrl('T')) .enter(); assertLine("abcd\nefhg", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('-')) .append(alt('2')) .append(ctrl('T')) .enter(); assertLine("abcd\nehfg", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('-')) .append(alt('3')) .append(ctrl('T')) .enter(); assertLine("abcd\nhefg", b, true); b = (new TestBuffer(translate("abcd\\E\refgh"))) .append(alt('-')) .append(alt('4')) .append(ctrl('T')) .enter(); assertLine("abcd\nehfg", b, true); } @Test public void testTransposeWords() throws Exception { reader.setKeyMap(EMACS); TestBuffer b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('t')) .enter(); assertLine("def abc ghi\nklm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('2')) .append(alt('t')) .enter(); assertLine("def ghi abc\nklm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('3')) .append(alt('t')) .enter(); assertLine("def abc ghi\nklm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(ctrl('A')) .append(alt('t')) .enter(); assertLine("abc def ghi\nnop klm qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(ctrl('A')) .append(alt('2')) .append(alt('t')) .enter(); assertLine("abc def ghi\nnop qrs klm", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(ctrl('A')) .append(alt('3')) .append(alt('t')) .enter(); assertLine("abc def ghi\nnop klm qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(ctrl('A')) .append(ctrl('B')) .append(alt('-')) .append(alt('t')) .enter(); assertLine("abc ghi def\nklm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(ctrl('A')) .append(ctrl('B')) .append(alt('-')) .append(alt('2')) .append(alt('t')) .enter(); assertLine("ghi abc def\nklm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(ctrl('A')) .append(ctrl('B')) .append(alt('-')) .append(alt('3')) .append(alt('t')) .enter(); assertLine("abc ghi def\nklm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('-')) .append(alt('t')) .enter(); assertLine("abc def ghi\nklm qrs nop", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('-')) .append(alt('2')) .append(alt('t')) .enter(); assertLine("abc def ghi\nqrs klm nop", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('-')) .append(alt('3')) .append(alt('t')) .enter(); assertLine("abc def ghi\nklm qrs nop", b, true); } @Test public void testKillLine() { reader.setKeyMap(EMACS); reader.getKeys().bind(new Reference("backward-kill-line"), ctrl('U')); TestBuffer b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('1')) .append(ctrl('U')) .enter(); assertLine("abc def ghi\n", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('U')) .enter(); assertLine("abc def ghi", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('3')) .append(ctrl('U')) .enter(); assertLine("", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('1')) .append(ctrl('K')) .enter(); assertLine("\nklm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('2')) .append(ctrl('K')) .enter(); assertLine("klm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('3')) .append(ctrl('K')) .enter(); assertLine("", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('-')) .append(alt('1')) .append(ctrl('K')) .enter(); assertLine("abc def ghi\n", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('-')) .append(alt('2')) .append(ctrl('K')) .enter(); assertLine("abc def ghi", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('-')) .append(alt('3')) .append(ctrl('K')) .enter(); assertLine("", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('-')) .append(alt('1')) .append(ctrl('U')) .enter(); assertLine("\nklm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('-')) .append(alt('2')) .append(ctrl('U')) .enter(); assertLine("klm nop qrs", b, true); b = (new TestBuffer(translate("abc def ghi\\E\rklm nop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('-')) .append(alt('3')) .append(ctrl('U')) .enter(); assertLine("", b, true); } @Test public void testKillWholeLine() { reader.setKeyMap(EMACS); TestBuffer b = (new TestBuffer(translate("abc def\\E\rghi klm\\E\rnop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('2')) .append(ctrl('U')) .append('X') .enter(); assertLine("abc def\nX", b, true); b = (new TestBuffer(translate("abc def\\E\rghi klm\\E\rnop qrs"))) .append(alt('2')) .append(ctrl('A')) .append(alt('-')) .append(alt('2')) .append(ctrl('U')) .append('X') .enter(); assertLine("Xnop qrs", b, true); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/EditLineTest.java000066400000000000000000000204321311544710100272230ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import org.jline.reader.LineReader; import org.jline.reader.Reference; import org.junit.Test; import static org.jline.keymap.KeyMap.ctrl; import static org.jline.reader.LineReader.BACKWARD_KILL_LINE; import static org.jline.reader.LineReader.BACKWARD_KILL_WORD; import static org.jline.reader.LineReader.BACKWARD_WORD; import static org.jline.reader.LineReader.END_OF_LINE; import static org.jline.reader.LineReader.FORWARD_WORD; import static org.jline.reader.LineReader.KILL_WORD; /** * Tests various features of editing lines. * * @author Marc Prud'hommeaux */ public class EditLineTest extends ReaderTestSupport { @Test public void testIssue101() throws Exception { TestBuffer b = new TestBuffer("config:property-set --pid org.ops4j.pax.url.mvn org.ops4j.pax.url.mvn.globalChecksumPolicy crash") .op(BACKWARD_WORD) .op(BACKWARD_WORD) .append("odsa odsa ") .op(BACKWARD_WORD) .op(BACKWARD_KILL_WORD) .op(FORWARD_WORD) .op(FORWARD_WORD) .op(BACKWARD_KILL_WORD); assertBuffer("config:property-set --pid org.ops4j.pax.url.mvn odsa crash", b); } @Test public void testDeletePreviousWord() throws Exception { TestBuffer b = new TestBuffer("This is a test"); assertBuffer("This is a ", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("This is ", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("This ", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("", b = b.op(BACKWARD_KILL_WORD)); } @Test public void testDeleteNextWord() throws Exception { TestBuffer b = new TestBuffer("This is a test").op(END_OF_LINE); assertBuffer("This is a test", b = b.op(KILL_WORD)); assertBuffer("This is a ", b = b.op(BACKWARD_WORD).op(KILL_WORD)); } @Test public void testMoveToEnd() throws Exception { assertBuffer("This is a XtestX", new TestBuffer("This is a test").op(BACKWARD_WORD) .append('X') .op(END_OF_LINE) .append('X')); assertBuffer("This is Xa testX", new TestBuffer("This is a test").op(BACKWARD_WORD) .op(BACKWARD_WORD) .append('X') .op(END_OF_LINE) .append('X')); assertBuffer("This Xis a testX", new TestBuffer("This is a test").op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .append('X') .op(END_OF_LINE) .append('X')); } @Test public void testPreviousWord() throws Exception { assertBuffer("This is a Xtest", new TestBuffer("This is a test").op(BACKWARD_WORD) .append('X')); assertBuffer("This is Xa test", new TestBuffer("This is a test").op(BACKWARD_WORD) .op(BACKWARD_WORD) .append('X')); assertBuffer("This Xis a test", new TestBuffer("This is a test").op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .append('X')); assertBuffer("XThis is a test", new TestBuffer("This is a test").op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .append('X')); assertBuffer("XThis is a test", new TestBuffer("This is a test").op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .append('X')); assertBuffer("XThis is a test", new TestBuffer("This is a test").op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .op(BACKWARD_WORD) .append('X')); } @Test public void testLineStart() throws Exception { assertBuffer("XThis is a test", new TestBuffer("This is a test").ctrlA().append('X')); assertBuffer("TXhis is a test", new TestBuffer("This is a test").ctrlA().right().append('X')); } @Test public void testClearLine() throws Exception { reader.getKeys().bind(new Reference(BACKWARD_KILL_LINE), ctrl('U')); assertBuffer("", new TestBuffer("This is a test").ctrlU()); assertBuffer("t", new TestBuffer("This is a test").left().ctrlU()); assertBuffer("st", new TestBuffer("This is a test").left().left().ctrlU()); } @Test public void testRight() throws Exception { TestBuffer b = new TestBuffer("This is a test"); b = b.left().right().back(); assertBuffer("This is a tes", b); b = b.left().left().left().right().left().back(); assertBuffer("This is ates", b); b.append('X'); assertBuffer("This is aXtes", b); } @Test public void testLeft() throws Exception { TestBuffer b = new TestBuffer("This is a test"); b = b.left().left().left(); assertBuffer("This is a est", b = b.back()); assertBuffer("This is aest", b = b.back()); assertBuffer("This is est", b = b.back()); assertBuffer("This isest", b = b.back()); assertBuffer("This iest", b = b.back()); assertBuffer("This est", b = b.back()); assertBuffer("Thisest", b = b.back()); assertBuffer("Thiest", b = b.back()); assertBuffer("Thest", b = b.back()); assertBuffer("Test", b = b.back()); assertBuffer("est", b = b.back()); assertBuffer("est", b = b.back()); assertBuffer("est", b = b.back()); assertBuffer("est", b = b.back()); assertBuffer("est", b = b.back()); } @Test public void testBackspace() throws Exception { TestBuffer b = new TestBuffer("This is a test"); assertBuffer("This is a tes", b = b.back()); assertBuffer("This is a te", b = b.back()); assertBuffer("This is a t", b = b.back()); assertBuffer("This is a ", b = b.back()); assertBuffer("This is a", b = b.back()); assertBuffer("This is ", b = b.back()); assertBuffer("This is", b = b.back()); assertBuffer("This i", b = b.back()); assertBuffer("This ", b = b.back()); assertBuffer("This", b = b.back()); assertBuffer("Thi", b = b.back()); assertBuffer("Th", b = b.back()); assertBuffer("T", b = b.back()); assertBuffer("", b = b.back()); assertBuffer("", b = b.back()); assertBuffer("", b = b.back()); assertBuffer("", b = b.back()); assertBuffer("", b = b.back()); } @Test public void testBuffer() throws Exception { assertBuffer("This is a test", new TestBuffer("This is a test")); } @Test public void testAbortPartialBuffer() throws Exception { reader.setVariable(LineReader.BELL_STYLE, "audible"); assertBuffer("", new TestBuffer("This is a test").ctrl('G')); assertConsoleOutputContains("\n"); assertBeeped(); out.reset(); assertBuffer("", new TestBuffer("This is a test").op(BACKWARD_WORD) .op(BACKWARD_WORD) .ctrl('G')); assertConsoleOutputContains("\n"); assertBeeped(); } @Test public void testEscapeNewLine() throws Exception { boolean prev = ((DefaultParser) reader.getParser()).isEofOnEscapedNewLine(); ((DefaultParser) reader.getParser()).setEofOnEscapedNewLine(true); try { assertLine("echo foobar", new TestBuffer("echo foo\\").enter().append("bar").enter()); } finally { ((DefaultParser) reader.getParser()).setEofOnEscapedNewLine(prev); } } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/HistorySearchTest.java000066400000000000000000000107761311544710100303270ustar00rootroot00000000000000package org.jline.reader.impl; import java.io.ByteArrayInputStream; import org.jline.reader.LineReader; import org.jline.reader.impl.history.DefaultHistory; import org.junit.Test; import static org.jline.keymap.KeyMap.translate; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class HistorySearchTest extends ReaderTestSupport { private DefaultHistory setupHistory() { DefaultHistory history = new DefaultHistory(); reader.setVariable(LineReader.HISTORY_SIZE, 10); reader.setHistory(history); history.add("foo"); history.add("fiddle"); history.add("faddle"); return history; } @Test public void testReverseHistorySearch() throws Exception { DefaultHistory history = setupHistory(); // TODO: use assertBuffer String readLineResult; in.setIn(new ByteArrayInputStream(translate("^Rf\n").getBytes())); readLineResult = reader.readLine(); assertEquals("faddle", readLineResult); assertEquals(3, history.size()); in.setIn(new ByteArrayInputStream(translate("^Rf^R^R^R^R^R\n").getBytes())); readLineResult = reader.readLine(); assertEquals("foo", readLineResult); assertEquals(4, history.size()); in.setIn(new ByteArrayInputStream(translate("^Rf^R^R\n").getBytes())); readLineResult = reader.readLine(); assertEquals("fiddle", readLineResult); assertEquals(5, history.size()); } @Test public void testForwardHistorySearch() throws Exception { DefaultHistory history = setupHistory(); String readLineResult; in.setIn(new ByteArrayInputStream(translate("^Rf^R^R^S\n").getBytes())); readLineResult = reader.readLine(); assertEquals("fiddle", readLineResult); assertEquals(4, history.size()); in.setIn(new ByteArrayInputStream(translate("^Rf^R^R^R^S^S\n").getBytes())); readLineResult = reader.readLine(); assertEquals("faddle", readLineResult); assertEquals(5, history.size()); in.setIn(new ByteArrayInputStream(translate("^Rf^R^R^R^R^S\n").getBytes())); readLineResult = reader.readLine(); assertEquals("fiddle", readLineResult); assertEquals(6, history.size()); } @Test public void testSearchHistoryAfterHittingEnd() throws Exception { DefaultHistory history = setupHistory(); String readLineResult; in.setIn(new ByteArrayInputStream(translate("^Rf^R^R^R^S\n").getBytes())); readLineResult = reader.readLine(); assertEquals("fiddle", readLineResult); assertEquals(4, history.size()); } @Test public void testSearchHistoryWithNoMatches() throws Exception { DefaultHistory history = setupHistory(); String readLineResult; in.setIn(new ByteArrayInputStream(translate("x^S^S\n").getBytes())); readLineResult = reader.readLine(); assertEquals("", readLineResult); assertEquals(3, history.size()); } @Test public void testAbortingSearchRetainsCurrentBufferAndPrintsDetails() throws Exception { DefaultHistory history = setupHistory(); String readLineResult; in.setIn(new ByteArrayInputStream(translate("f^Rf^G").getBytes())); readLineResult = reader.readLine(); assertEquals(null, readLineResult); assertTrue(out.toString().contains("bck-i-search: f_")); assertFalse(out.toString().contains("bck-i-search: ff_")); assertEquals("f", reader.getBuffer().toString()); assertEquals(3, history.size()); } @Test public void testAbortingAfterSearchingPreviousLinesGivesBlank() throws Exception { DefaultHistory history = setupHistory(); String readLineResult; in.setIn(new ByteArrayInputStream(translate("f^Rf\nfoo^G").getBytes())); readLineResult = reader.readLine(); assertEquals("", readLineResult); readLineResult = reader.readLine(); assertEquals(null, readLineResult); assertEquals("", reader.getBuffer().toString()); assertEquals(3, history.size()); } @Test public void testSearchOnEmptyHistory() throws Exception { DefaultHistory history = setupHistory(); history.purge(); String readLineResult; in.setIn(new ByteArrayInputStream(translate("^Sa").getBytes())); readLineResult = reader.readLine(); assertEquals(null, readLineResult); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/KillRingTest.java000066400000000000000000000127361311544710100272510ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import org.junit.Test; import static org.jline.reader.LineReader.BACKWARD_KILL_WORD; import static org.jline.reader.LineReader.BACKWARD_WORD; import static org.jline.reader.LineReader.KILL_WORD; import static org.jline.reader.LineReader.YANK; import static org.jline.reader.LineReader.YANK_POP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; /** * Tests for the {@link KillRing}. */ public class KillRingTest extends ReaderTestSupport { @Test public void testEmptyKillRing() { KillRing killRing = new KillRing(); assertNull(killRing.yank()); } @Test public void testOneElementKillRing() { KillRing killRing = new KillRing(); killRing.add("foo"); String yanked = killRing.yank(); assertNotNull(yanked); assertEquals(yanked, "foo"); } @Test public void testKillKill() { // A kill followed by another kill will be saved in the same // slot. KillRing killRing = new KillRing(); killRing.add("foo"); killRing.add(" bar"); String yanked = killRing.yank(); assertNotNull(yanked); assertEquals(yanked, "foo bar"); } @Test public void testYankTwice() { // A yank followed by another yank should yield the same // string. KillRing killRing = new KillRing(); killRing.add("foo"); killRing.resetLastKill(); killRing.add("bar"); String yanked = killRing.yank(); assertNotNull(yanked); assertEquals(yanked, "bar"); yanked = killRing.yank(); assertNotNull(yanked); assertEquals(yanked, "bar"); } @Test public void testYankPopNoPreviousYank() { // A yank-pop without a previous yank should return null. KillRing killRing = new KillRing(); killRing.add("foo"); String yanked = killRing.yankPop(); assertNull(yanked); } @Test public void testYankPopWithOneSlot() { // Verifies that the ring works fine with one element. KillRing killRing = new KillRing(); killRing.add("foo"); String yanked = killRing.yank(); assertNotNull(yanked); assertEquals(yanked, "foo"); // yanked = killRing.yankPop(); assertNotNull(yanked); assertEquals(yanked, "foo"); // yanked = killRing.yankPop(); assertNotNull(yanked); assertEquals(yanked, "foo"); } @Test public void testYankPop() { // Verifies that the ring actually works like that, ie, a // series of yank-pop commands should eventually start // repeating. KillRing killRing = new KillRing(); killRing.add("foo"); killRing.resetLastKill(); killRing.add("bar"); killRing.resetLastKill(); killRing.add("baz"); String yanked = killRing.yank(); assertNotNull(yanked); assertEquals(yanked, "baz"); // yanked = killRing.yankPop(); assertNotNull(yanked); assertEquals(yanked, "bar"); // yanked = killRing.yankPop(); assertNotNull(yanked); assertEquals(yanked, "foo"); // Back to the beginning. yanked = killRing.yankPop(); assertNotNull(yanked); assertEquals(yanked, "baz"); } // Those tests are run using a buffer. @Test public void testBufferEmptyRing() throws Exception { TestBuffer b = new TestBuffer("This is a test"); assertBuffer("This is a test", b = b.op(BACKWARD_WORD)); assertBuffer("This is a test", b = b.op(YANK)); } @Test public void testBufferWordRuboutOnce() throws Exception { TestBuffer b = new TestBuffer("This is a test"); assertBuffer("This is a ", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("This is a test", b = b.op(YANK)); } @Test public void testBufferWordRuboutTwice() throws Exception { TestBuffer b = new TestBuffer("This is a test"); assertBuffer("This is a ", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("This is ", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("This is a test", b = b.op(YANK)); } @Test public void testBufferYankPop() throws Exception { TestBuffer b = new TestBuffer("This is a test"); b = b.op(BACKWARD_WORD); b = b.op(BACKWARD_WORD); assertBuffer("This a test", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("This a test", b = b.op(BACKWARD_WORD)); assertBuffer(" a test", b = b.op(KILL_WORD)); assertBuffer("This a test", b = b.op(YANK)); assertBuffer("is a test", b = b.op(YANK_POP)); } @Test public void testBufferMixedKillsAndYank() throws Exception { TestBuffer b = new TestBuffer("This is a test"); b = b.op(BACKWARD_WORD); b = b.op(BACKWARD_WORD); assertBuffer("This is test", b = b.op(KILL_WORD)); assertBuffer("This test", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("This ", b = b.op(KILL_WORD)); assertBuffer("", b = b.op(BACKWARD_KILL_WORD)); assertBuffer("This is a test", b = b.op(YANK)); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/LineReaderTest.java000066400000000000000000000037761311544710100275540ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.io.ByteArrayInputStream; import java.io.InputStream; import org.jline.reader.EndOfFileException; import org.jline.reader.LineReader; import org.jline.reader.LineReaderBuilder; import org.jline.terminal.TerminalBuilder; import org.junit.Test; import static org.junit.Assert.fail; public class LineReaderTest { @Test(expected = EndOfFileException.class) public void emptyStringGivesEOFWithJna() throws Exception { String inputString = ""; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); LineReaderBuilder builder = LineReaderBuilder.builder() .terminal(TerminalBuilder.builder() .streams(inputStream, System.out) .jna(true) .build()); LineReader reader = builder.build(); // this gets trapped in an infinite loop reader.readLine(); fail("Should have thrown an EndOfFileException"); } @Test(expected = EndOfFileException.class) public void emptyStringGivesEOFNoJna() throws Exception { String inputString = ""; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); LineReaderBuilder builder = LineReaderBuilder.builder() .terminal(TerminalBuilder.builder() .streams(inputStream, System.out) .jna(false) .build()); LineReader reader = builder.build(); // this gets trapped in an infinite loop reader.readLine(); fail("Should have thrown an EndOfFileException"); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/MultiByteCharTest.java000066400000000000000000000030331311544710100302400ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.Reader; import java.nio.charset.Charset; import org.jline.utils.InputStreamReader; import org.junit.Test; import static org.junit.Assert.assertEquals; public class MultiByteCharTest extends ReaderTestSupport { @Test public void testInputStreamReader() throws IOException { String str = "e\uD834\uDD21"; PipedOutputStream pos = new PipedOutputStream(); PipedInputStream pis = new PipedInputStream(pos); pos.write(str.getBytes(Charset.forName("UTF-8"))); Reader r = new InputStreamReader(pis, Charset.forName("UTF-8")); int c0 = r.read(); int c1 = r.read(); int c2 = r.read(); assertEquals(c0, str.charAt(0)); assertEquals(c1, str.charAt(1)); assertEquals(c2, str.charAt(2)); } @Test public void testMbs() throws IOException { TestBuffer b = new TestBuffer("\uD834\uDD21").enter(); assertLine("\uD834\uDD21", b, true); b = new TestBuffer("\uD834\uDD21").back().enter(); assertLine("", b, true); b = new TestBuffer("\uD834\uDD21").left().ctrlD().enter(); assertLine("", b, true); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/ReaderTestSupport.java000066400000000000000000000236051311544710100303320ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.io.*; import java.nio.charset.Charset; import java.util.List; import java.util.Map; import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import org.jline.keymap.KeyMap; import org.jline.reader.Candidate; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.impl.DumbTerminal; import org.jline.utils.Curses; import org.jline.utils.InfoCmp.Capability; import org.junit.Before; import static org.jline.reader.LineReader.ACCEPT_LINE; import static org.jline.reader.LineReader.BACKWARD_CHAR; import static org.jline.reader.LineReader.BACKWARD_DELETE_CHAR; import static org.jline.reader.LineReader.BACKWARD_KILL_WORD; import static org.jline.reader.LineReader.BACKWARD_WORD; import static org.jline.reader.LineReader.BEGINNING_OF_LINE; import static org.jline.reader.LineReader.COMPLETE_WORD; import static org.jline.reader.LineReader.DOWN_HISTORY; import static org.jline.reader.LineReader.END_OF_LINE; import static org.jline.reader.LineReader.FORWARD_WORD; import static org.jline.reader.LineReader.KILL_WORD; import static org.jline.reader.LineReader.UP_HISTORY; import static org.jline.reader.LineReader.YANK; import static org.jline.reader.LineReader.YANK_POP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * Provides support for reader tests. */ public abstract class ReaderTestSupport { protected Terminal terminal; protected TestLineReader reader; protected EofPipedInputStream in; protected ByteArrayOutputStream out; protected Character mask; @Before public void setUp() throws Exception { Handler ch = new ConsoleHandler(); ch.setLevel(Level.FINEST); Logger logger = Logger.getLogger("org.jline"); logger.addHandler(ch); // Set the handler log level logger.setLevel(Level.INFO); in = new EofPipedInputStream(); out = new ByteArrayOutputStream(); terminal = new DumbTerminal("terminal", "ansi", in, out, "UTF-8"); terminal.setSize(new Size(160, 80)); reader = new TestLineReader(terminal, "JLine", null); reader.setKeyMap(LineReaderImpl.EMACS); mask = null; } protected void assertConsoleOutputContains(String s) { String output = out.toString(); assertTrue(output.contains(s)); } protected void assertBeeped() throws IOException { String bellCap = terminal.getStringCapability(Capability.bell); StringWriter sw = new StringWriter(); Curses.tputs(sw, bellCap); assertConsoleOutputContains(sw.toString()); } protected void assertBuffer(final String expected, final TestBuffer buffer) throws IOException { assertBuffer(expected, buffer, true); } protected void assertBuffer(final String expected, final TestBuffer buffer, final boolean clear) throws IOException { // clear current buffer, if any if (clear) { reader.getHistory().purge(); } reader.list = false; reader.menu = false; in.setIn(new ByteArrayInputStream(buffer.getBytes())); // run it through the reader //String line; //while ((line = reader.readLine((String) null)) != null) { //System.err.println("Read line: " + line); while ((reader.readLine(null, null, mask, null)) != null) { // noop } assertEquals(expected, reader.getBuffer().toString()); } protected void assertLine(final String expected, final TestBuffer buffer) { assertLine(expected, buffer, true); } /** * This is used to check the contents of the last completed * line of input in the input buffer. * * @param expected The expected contents of the line. * @param buffer The buffer * @param clear If true, the current buffer of the reader * is cleared. */ protected void assertLine(final String expected, final TestBuffer buffer, final boolean clear) { // clear current buffer, if any if (clear) { try { reader.getHistory().purge(); } catch (Exception e) { e.printStackTrace(); } } in.setIn(new ByteArrayInputStream(buffer.getBytes())); String line; String prevLine = null; while ((line = reader.readLine(null, null, mask, null)) != null) { prevLine = line; } assertEquals(expected, prevLine); } private String getKeyForAction(final String key) { switch (key) { case BACKWARD_WORD: return "\u001Bb"; case FORWARD_WORD: return "\u001Bf"; case BEGINNING_OF_LINE: return "\033[H"; case END_OF_LINE: return "\u0005"; case KILL_WORD: return "\u001Bd"; case BACKWARD_KILL_WORD: return "\u0017"; case ACCEPT_LINE: return "\n"; case UP_HISTORY: return "\033[A"; case DOWN_HISTORY: return "\033[B"; case BACKWARD_CHAR: return "\u0002"; case COMPLETE_WORD: return "\011"; case BACKWARD_DELETE_CHAR: return "\010"; case YANK: return "\u0019"; case YANK_POP: return new String(new char[]{27, 121}); default: throw new IllegalArgumentException(key); } } protected class TestBuffer { private final ByteArrayOutputStream out = new ByteArrayOutputStream(); public TestBuffer() { // nothing } public TestBuffer(String str) { append(str); } public TestBuffer(char[] chars) { append(new String(chars)); } @Override public String toString() { try { return out.toString("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } public byte[] getBytes() { return out.toByteArray(); } public TestBuffer op(final String op) { return append(getKeyForAction(op)); } public TestBuffer ctrlA() { return ctrl('A'); } public TestBuffer ctrlD() { return ctrl('D'); } /** * Generate a CTRL-X sequence where 'X' is the control character * you wish to generate. * @param let The letter of the control character. Valid values are * 'A' through 'Z'. * @return The modified buffer. */ public TestBuffer ctrl(char let) { return append(KeyMap.ctrl(let)); } public TestBuffer alt(char let) { return append(KeyMap.alt(let)); } public TestBuffer enter() { return ctrl('J'); } public TestBuffer CR() { return ctrl('M'); } public TestBuffer ctrlU() { return ctrl('U'); } public TestBuffer tab() { return op(COMPLETE_WORD); } public TestBuffer escape() { return append("\033"); } public TestBuffer back() { return op(BACKWARD_DELETE_CHAR); } public TestBuffer back(int n) { for (int i = 0; i < n; i++) op(BACKWARD_DELETE_CHAR); return this; } public TestBuffer left() { return append("\033[D"); } public TestBuffer left(int n) { for (int i = 0; i < n; i++) left(); return this; } public TestBuffer right() { return append("\033[C"); } public TestBuffer right(int n) { for (int i = 0; i < n; i++) right(); return this; } public TestBuffer up() { return append(getKeyForAction(UP_HISTORY)); } public TestBuffer down() { return append("\033[B"); } public TestBuffer append(final String str) { for (byte b : str.getBytes(Charset.forName("UTF-8"))) { append(b); } return this; } public TestBuffer append(final int i) { out.write((byte) i); return this; } } public static class EofPipedInputStream extends InputStream { private InputStream in; public InputStream getIn() { return in; } public void setIn(InputStream in) { this.in = in; } @Override public int read() throws IOException { return in != null ? in.read() : -1; } @Override public int available() throws IOException { return in != null ? in.available() : 0; } } public static class TestLineReader extends LineReaderImpl { boolean list = false; boolean menu = false; public TestLineReader(Terminal terminal, String appName, Map variables) { super(terminal, appName, variables); } @Override protected boolean doList(List possible, String completed, boolean runLoop) { list = true; return super.doList(possible, completed, runLoop); } @Override protected boolean doMenu(List possible, String completed) { menu = true; return super.doMenu(possible, completed); } } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/TerminalReaderTest.java000066400000000000000000000344231311544710100304310ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import java.io.ByteArrayInputStream; import java.io.StringWriter; import org.jline.keymap.KeyMap; import org.jline.reader.Binding; import org.jline.reader.Expander; import org.jline.reader.LineReader; import org.jline.reader.LineReader.Option; import org.jline.reader.History; import org.jline.reader.Reference; import org.jline.reader.Widget; import org.jline.reader.impl.history.DefaultHistory; import org.jline.utils.Curses; import org.jline.utils.InfoCmp.Capability; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Tests for the {@link LineReaderImpl}. */ public class TerminalReaderTest extends ReaderTestSupport { @Before public void setUp() throws Exception { super.setUp(); reader.setHistory(createSeededHistory()); } @Test public void testReadline() throws Exception { assertLine("Sample String", new TestBuffer("Sample String\n")); } @Test public void testReadlineWithUnicode() throws Exception { System.setProperty("input.encoding", "UTF-8"); assertLine("\u6771\u00E9\u00E8", new TestBuffer("\u6771\u00E9\u00E8\n")); } @Test public void testReadlineWithMask() throws Exception { mask = '*'; assertLine("Sample String", new TestBuffer("Sample String\n")); assertTrue(this.out.toString().contains("*************")); } @Test public void testExpansion() throws Exception { DefaultHistory history = new DefaultHistory(reader); reader.setVariable(LineReader.HISTORY_SIZE, 3); history.add("foo"); history.add("dir"); history.add("cd c:\\"); history.add("mkdir monkey"); Expander expander = new DefaultExpander(); assertEquals("echo a!", expander.expandHistory(history, "echo a!")); assertEquals("mkdir monkey ; echo a!", expander.expandHistory(history, "!! ; echo a!")); assertEquals("echo ! a", expander.expandHistory(history, "echo ! a")); assertEquals("echo !\ta", expander.expandHistory(history, "echo !\ta")); assertEquals("mkdir barey", expander.expandHistory(history, "^monk^bar^")); assertEquals("mkdir barey", expander.expandHistory(history, "^monk^bar")); assertEquals("a^monk^bar", expander.expandHistory(history, "a^monk^bar")); assertEquals("mkdir monkey", expander.expandHistory(history, "!!")); assertEquals("echo echo a", expander.expandHistory(history, "echo !#a")); assertEquals("mkdir monkey", expander.expandHistory(history, "!mk")); try { expander.expandHistory(history, "!mz"); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertEquals("!mz: event not found", e.getMessage()); } assertEquals("mkdir monkey", expander.expandHistory(history, "!?mo")); assertEquals("mkdir monkey", expander.expandHistory(history, "!?mo?")); assertEquals("mkdir monkey", expander.expandHistory(history, "!-1")); assertEquals("cd c:\\", expander.expandHistory(history, "!-2")); assertEquals("cd c:\\", expander.expandHistory(history, "!3")); assertEquals("mkdir monkey", expander.expandHistory(history, "!4")); try { expander.expandHistory(history, "!20"); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertEquals("!20: event not found", e.getMessage()); } try { expander.expandHistory(history, "!-20"); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertEquals("!-20: event not found", e.getMessage()); } } @Test public void testNumericExpansions() throws Exception { DefaultHistory history = new DefaultHistory(reader); reader.setVariable(LineReader.HISTORY_SIZE, 3); // Seed history with three iterator: // 1 history1 // 2 history2 // 3 history3 history.add("history1"); history.add("history2"); history.add("history3"); Expander expander = new DefaultExpander(); // Validate !n assertExpansionIllegalArgumentException(expander, history, "!0"); assertEquals("history1", expander.expandHistory(history, "!1")); assertEquals("history2", expander.expandHistory(history, "!2")); assertEquals("history3", expander.expandHistory(history, "!3")); assertExpansionIllegalArgumentException(expander, history, "!4"); // Validate !-n assertExpansionIllegalArgumentException(expander, history, "!-0"); assertEquals("history3", expander.expandHistory(history, "!-1")); assertEquals("history2", expander.expandHistory(history, "!-2")); assertEquals("history1", expander.expandHistory(history, "!-3")); assertExpansionIllegalArgumentException(expander, history, "!-4"); // Validate !! assertEquals("history3", expander.expandHistory(history, "!!")); // Add two new iterator. Because maxSize=3, history is: // 3 history3 // 4 history4 // 5 history5 history.add("history4"); history.add("history5"); // Validate !n assertExpansionIllegalArgumentException(expander, history, "!0"); assertExpansionIllegalArgumentException(expander, history, "!1"); assertExpansionIllegalArgumentException(expander, history, "!2"); assertEquals("history3", expander.expandHistory(history, "!3")); assertEquals("history4", expander.expandHistory(history, "!4")); assertEquals("history5", expander.expandHistory(history, "!5")); assertExpansionIllegalArgumentException(expander, history, "!6"); // Validate !-n assertExpansionIllegalArgumentException(expander, history, "!-0"); assertEquals("history5", expander.expandHistory(history, "!-1")); assertEquals("history4", expander.expandHistory(history, "!-2")); assertEquals("history3", expander.expandHistory(history, "!-3")); assertExpansionIllegalArgumentException(expander, history, "!-4"); // Validate !! assertEquals("history5", expander.expandHistory(history, "!!")); } @Test public void testArgsExpansion() throws Exception { DefaultHistory history = new DefaultHistory(reader); reader.setVariable(LineReader.HISTORY_SIZE, 3); Expander expander = new DefaultExpander(); // we can't go back to previous arguments if there are none try { expander.expandHistory(history, "!$"); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertEquals("!$: event not found", e.getMessage()); } // if no arguments were given, it should expand to the command itself history.add("ls"); assertEquals("ls", expander.expandHistory(history, "!$")); // now we can expand to the last argument history.add("ls /home"); assertEquals("/home", expander.expandHistory(history, "!$")); //we always take the last argument history.add("ls /home /etc"); assertEquals("/etc", expander.expandHistory(history, "!$")); //make sure we don't add spaces accidentally history.add("ls /home /foo "); assertEquals("/foo", expander.expandHistory(history, "!$")); } @Test public void testIllegalExpansionDoesntCrashReadLine() throws Exception { DefaultHistory history = new DefaultHistory(); reader.setHistory(history); reader.setVariable(LineReader.BELL_STYLE, "audible"); assertLine("!f", new TestBuffer("!f\n")); assertEquals(1, history.size()); } @Test public void testStoringHistory() throws Exception { DefaultHistory history = new DefaultHistory(); reader.setHistory(history); assertLine("foo ! bar", new TestBuffer("foo ! bar\n")); history.previous(); assertEquals("foo ! bar", history.current()); history = new DefaultHistory(); reader.setHistory(history); assertLine("cd c:\\docs", new TestBuffer("cd c:\\\\docs\n")); history.previous(); assertEquals("cd c:\\\\docs", history.current()); } @Test public void testExpansionAndHistoryWithEscapes() throws Exception { /* * Tests the results of the ReaderImpl.readLine() call and the line * stored in history. For each input, it tests the with-expansion and * without-expansion case. */ // \! (escaped expansion v1) assertLineAndHistory( "echo ab!ef", "echo ab\\!ef", new TestBuffer("echo ab\\!ef\n"), true, "cd"); assertLineAndHistory( "echo ab\\!ef", "echo ab\\!ef", new TestBuffer("echo ab\\!ef\n"), false, "cd"); // \!\! (escaped expansion v2) assertLineAndHistory( "echo ab!!ef", "echo ab\\!\\!ef", new TestBuffer("echo ab\\!\\!ef\n"), true, "cd"); assertLineAndHistory( "echo ab\\!\\!ef", "echo ab\\!\\!ef", new TestBuffer("echo ab\\!\\!ef\n"), false, "cd"); // !! (expansion) assertLineAndHistory( "echo abcdef", "echo abcdef", new TestBuffer("echo ab!!ef\n"), true, "cd"); assertLineAndHistory( "echo ab!!ef", "echo ab!!ef", new TestBuffer("echo ab!!ef\n"), false, "cd"); // \G (backslash no expansion) assertLineAndHistory( "echo abcGdef", "echo abc\\Gdef", new TestBuffer("echo abc\\Gdef\n"), true, "cd"); assertLineAndHistory( "echo abc\\Gdef", "echo abc\\Gdef", new TestBuffer("echo abc\\Gdef\n"), false, "cd"); // \^ (escaped expansion) assertLineAndHistory( "^abc^def", "\\^abc^def", new TestBuffer("\\^abc^def\n"), true, "echo abc"); assertLineAndHistory( "\\^abc^def", "\\^abc^def", new TestBuffer("\\^abc^def\n"), false, "echo abc"); // ^^ (expansion) assertLineAndHistory( "echo def", "echo def", new TestBuffer("^abc^def\n"), true, "echo abc"); assertLineAndHistory( "^abc^def", "^abc^def", new TestBuffer("^abc^def\n"), false, "echo abc"); } @Test public void testStoringHistoryWithExpandEventsOff() throws Exception { assertLineAndHistory( "foo ! bar", "foo ! bar", new TestBuffer("foo ! bar\n"), false ); } @Test public void testBell() throws Exception { reader.setVariable(LineReader.BELL_STYLE, "off"); reader.beep(); assertEquals("out should not have received bell", 0, out.size()); reader.setVariable(LineReader.BELL_STYLE, "audible"); reader.beep(); String bellCap = terminal.getStringCapability(Capability.bell); StringWriter sw = new StringWriter(); Curses.tputs(sw, bellCap); assertEquals("out should have received bell", sw.toString(), out.toString()); } @Test public void testCallbacks() throws Exception { reader.getKeys().bind((Widget) () -> reader.getBuffer().clear(), "x"); assertLine("", new TestBuffer("sample stringx\n")); } @Test public void testDefaultBuffer() throws Exception { in.setIn(new ByteArrayInputStream(new TestBuffer().enter().getBytes())); String line = reader.readLine(null, null, "foo"); assertEquals("foo", line); } @Test public void testReadBinding() throws Exception { in.setIn(new ByteArrayInputStream(new TestBuffer("abcde").getBytes())); KeyMap map = new KeyMap<>(); map.bind(new Reference("foo"), "bc"); map.bind(new Reference("bar"), "e"); Binding b = reader.readBinding(map); assertEquals(new Reference("foo"), b); assertEquals("bc", reader.getLastBinding()); b = reader.readBinding(map); assertEquals(new Reference("bar"), b); assertEquals("e", reader.getLastBinding()); b = reader.readBinding(map); assertNull(b); } private History createSeededHistory() { History history = new DefaultHistory(); history.add("dir"); history.add("cd c:\\"); history.add("mkdir monkey"); return history; } private void assertLineAndHistory(String expectedLine, String expectedHistory, TestBuffer input, boolean expandEvents, String... historyItems) { DefaultHistory history = new DefaultHistory(); reader.setHistory(history); if (historyItems != null) { for (String historyItem : historyItems) { history.add(historyItem); } } if (expandEvents) { reader.unsetOpt(Option.DISABLE_EVENT_EXPANSION); } else { reader.setOpt(Option.DISABLE_EVENT_EXPANSION); } assertLine(expectedLine, input, false); history.previous(); assertEquals(expectedHistory, history.current()); } /** * Validates that an 'event not found' IllegalArgumentException is thrown * for the expansion event. */ protected void assertExpansionIllegalArgumentException(Expander expander, History history, String event) throws Exception { try { expander.expandHistory(history, event); fail("Expected IllegalArgumentException for " + event); } catch (IllegalArgumentException e) { assertEquals(event + ": event not found", e.getMessage()); } } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/ViMoveModeTest.java000066400000000000000000001135231311544710100275440ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import org.jline.reader.History; import org.jline.reader.Reference; import org.junit.Before; import org.junit.Test; import static org.jline.keymap.KeyMap.ctrl; import static org.jline.reader.LineReader.BACKWARD_KILL_LINE; import static org.jline.reader.impl.LineReaderImpl.MAIN; import static org.jline.reader.impl.LineReaderImpl.VICMD; import static org.jline.reader.impl.LineReaderImpl.VIINS; import static org.jline.reader.impl.LineReaderImpl.VISUAL; /** * Unit tests for the greatest keymap binding in the world! Vi!!!! * These tests are primarily intended to test "move-mode" in VI, but * as a necessary by-product they use quite a bit of insert mode as well. */ public class ViMoveModeTest extends ReaderTestSupport { /** * For all tests we will start out in insert/edit mode. */ @Before public void setUp() throws Exception { super.setUp(); reader.setVariable("WORDCHARS", ""); reader.getKeyMaps().put(MAIN, reader.getKeyMaps().get(VIINS)); } @Test public void testMoveLeft() throws Exception { /* * There are various keys that will move you left. */ testMoveLeft("\033[D"); /* Left arrow */ testMoveLeft("h"); /* h key */ testMoveLeft("\010"); /* CTRL-H */ } public void testMoveLeft(String left) throws Exception { /* * Move left */ TestBuffer b = (new TestBuffer("0123456789")) .escape() .append(left) .append(left) .append(left) .append("iX") .enter(); assertLine("012345X6789", b, true); /* * Move left - use digit arguments. */ b = (new TestBuffer("0123456789")) .escape() .append('3') .append(left) .append("iX") .enter(); assertLine("012345X6789", b, true); /* * Move left - use multi-digit arguments. */ b = (new TestBuffer("0123456789ABCDEFHIJLMNOPQRSTUVWXYZ")) .escape() .append("13") .append(left) .append("iX") .enter(); assertLine("0123456789ABCDEFHIJLXMNOPQRSTUVWXYZ", b, true); /* * Delete move left. */ b = (new TestBuffer("0123456789ABCDEFHIJLMNOPQRSTUVWXYZ")) .escape() .append("13d") .append(left) .enter(); assertLine("0123456789ABCDEFHIJLZ", b, true); b = (new TestBuffer("0123456789ABCDEFHIJLMNOPQRSTUVWXYZ")) .escape() .append("d") .append(left) .append("d") .append(left) .enter(); assertLine("0123456789ABCDEFHIJLMNOPQRSTUVWZ", b, true); /* * Change move left */ b = (new TestBuffer("0123456789ABCDEFHIJLMNOPQRSTUVWXYZ")) .escape() .append("13c") .append(left) .append("_HI") .enter(); assertLine("0123456789ABCDEFHIJL_HIZ", b, true); b = (new TestBuffer("word")) .escape() .append("c") .append(left) .append("X") .enter(); assertLine("woXd", b, true); /* * Yank left */ b = (new TestBuffer("word")) .escape() .append("3y") .append(left) .append("p") .enter(); assertLine("wordwor", b, true); } @Test public void testMoveRight() throws Exception { testMoveRight("\033[C"); /* right arrow */ testMoveRight("l"); /* "l" key */ testMoveRight(" "); /* space */ } public void testMoveRight(String right) throws Exception { /* * Move right */ TestBuffer b = (new TestBuffer("0123456789")) .escape() .append('0') // beginning of line .append(right) .append(right) .append(right) .append("iX") .enter(); assertLine("012X3456789", b, true); /* * Move right use digit arguments. */ b = (new TestBuffer("0123456789ABCDEFHIJK")) .escape() .append("012") .append(right) .append("iX") .enter(); assertLine("0123456789ABXCDEFHIJK", b, true); /* * Delete move right */ b = (new TestBuffer("a bunch of words")) .escape() .append("05d") .append(right) .enter(); assertLine("ch of words", b, true); b = (new TestBuffer("a bunch of words")) .escape() .append("0d") .append(right) .append("d") .append(right) .enter(); assertLine("bunch of words", b, true); /* * Change move right */ b = (new TestBuffer("a bunch of words")) .escape() .append("010c") .append(right) .append("XXX") .enter(); assertLine("XXX words", b, true); /* * Yank move right */ b = (new TestBuffer("a bunch of words")) .escape() .append("010y") .append(right) .append("$p") .enter(); assertLine("a bunch of wordsa bunch of", b, true); } @Test public void testCtrlJ() throws Exception { /* * ENTER is CTRL-J. */ testEnter('J'); } @Test public void testCtrlK() throws Exception { /* * Ctrl-K should delete to end-of-line */ TestBuffer b = (new TestBuffer("This is a test")) .escape() .left().left().left().left() .ctrl('K') .enter(); assertLine("This is a", b, true); b = (new TestBuffer("hello")) .escape() .ctrl('K') .enter(); assertLine("hell", b, true); } @Test public void testCtrlL() throws Exception { /* * CTRL-L clears the screen. I can't test much but to make sure * that the cursor is where it is supposed to be. * * IMPORTANT NOTE: The CTRL-K is commented out below. Technically * it is a bug. What is supposed to happen is that the escape() * backs the cursor up one, so the CTRL-K is supposed to delete * the "o". With the CTRL-L involved, it doesn't. I suspect that * it has to do with the parsing of the stream of escape's coming * in and I'm not entirely sure it is easy to fix. Since this is * really an edge case I'm commenting it out, but I'm leaving this * comment here because it may be a sign of something lurking down the * road. */ TestBuffer b = (new TestBuffer("hello")) .escape() .ctrl ('L') // .ctrl ('K') .enter(); assertLine("hello", b, true); } @Test public void testCtrlM() throws Exception { testEnter('M'); } @Test public void testCtrlP_CtrlN() throws Exception { TestBuffer b = (new TestBuffer("line1")).enter() .append("line2").enter() .append("li") .escape() .ctrl('P') .ctrl('P') .enter(); assertLine("line1", b, false); reader.getHistory ().purge(); b = (new TestBuffer("line1")).enter() .append("line2").enter() .append("li") .escape() .ctrl('P') .ctrl('P') .ctrl('N') .enter(); assertLine("line2", b, false); /* * One last test. Make sure that when we move through history * that the cursor is moved to the front of the line. */ reader.getHistory ().purge(); b = (new TestBuffer("aline")).enter() .append("bline").enter() .append("cli") .escape() .ctrl('P') .ctrl('P') .ctrl('N') .append ("iX") .enter(); assertLine("blineX", b, false); } @Test public void testCtrlT() throws Exception { /* * Transpose every character exactly. */ TestBuffer b = (new TestBuffer("abcdef")) .escape() // Move mode .append('0') // Beginning of line .right() // Right one .ctrl('T') // Transpose .ctrl('T') .ctrl('T') .ctrl('T') .ctrl('T') .enter(); assertLine("bcdefa", b, false); /* * Cannot transpose the first character or the last character */ b = (new TestBuffer("abcdef")) .escape() // Move mode .append('0') // Beginning of line .ctrl('T') .ctrl('T') .append('$') // End of line .ctrl('T') .enter(); assertLine("bcadfe", b, false); } @Test public void testCtrlU() throws Exception { reader.getKeyMaps().get(VICMD).bind(new Reference(BACKWARD_KILL_LINE), ctrl('U')); /* * CTRL-U is "backward-kill-line", it deletes everything prior to the * current cursor position. */ TestBuffer b = (new TestBuffer("all work and no play")) .escape() // Move mode .left(3) // Left to the "p" in play .ctrl('U') // Line discard .enter(); assertLine("play", b, false); /* * Nothing happens at the beginning of the line */ b = (new TestBuffer("donkey punch")) .escape() // Move mode .append('0') // Beginning of the line .ctrl('U') // Line discard .enter(); assertLine("donkey punch", b, false); /* * End of the line leaves an empty buffer */ b = (new TestBuffer("rabid hamster")) .escape() // Move mode .right() // End of line .ctrl('U') // Line discard .enter(); assertLine("", b, false); } @Test public void testCtrlW() throws Exception { /* * CTRL-W is word rubout. It deletes to the beginning of the word * you are currently sitting in, or if you are one a break character * it deletes up to the beginning of the previous word. */ TestBuffer b = (new TestBuffer("oily rancid badgers")) .escape() .ctrl('W') .ctrl('W') .enter(); assertLine("oily s", b, false); /* * Test behavior with non-word characters. */ b = (new TestBuffer("pasty bulimic rats !!!!!")) .escape() .ctrl('W') .ctrl('W') .enter(); assertLine("pasty !", b, false); b = (new TestBuffer("pasty bulimic rats !!!!!")) .escape() .append("2") .ctrl('W') .enter(); assertLine("pasty !", b, false); } @Test public void testInsertComment() throws Exception { /* * The # key causes a comment to get inserted. */ TestBuffer b = (new TestBuffer("putrified whales")) .escape() .append("#"); assertLine("#putrified whales", b, false); } @Test public void testD() throws Exception { // D is a vim extension for delete-to-end-of-line TestBuffer b = (new TestBuffer("banana")) .escape() .left(2) .append("Dadaid") .enter(); assertLine("bandaid", b, false); } @Test public void testC() throws Exception { // C is a vim extension for change-to-end-of-line TestBuffer b = (new TestBuffer("yogurt")) .escape() .left(3) .append("Cyo") .enter(); assertLine("yoyo", b, false); } @Test public void testS() throws Exception { // S is a vim extension that is a synonum for 'cc' (clear whole line) TestBuffer b = (new TestBuffer("great lakes brewery")) .escape() .left(3) .append("Sdogfishhead") .enter(); assertLine("dogfishhead", b, false); } @Test public void testO() throws Exception { // O insert a line TestBuffer b = (new TestBuffer("great lakes brewery")) .escape() .left(6) .append("Odog ") .enter(); assertLine("dog \ngreat lakes brewery", b, false); } @Test public void testo() throws Exception { // O insert a line TestBuffer b = (new TestBuffer("great lakes brewery")) .escape() .left(6) .append("odog ") .enter(); assertLine("great lakes brewery\ndog ", b, false); } @Test public void testJ() throws Exception { // J joins the current line with the following one TestBuffer b = (new TestBuffer("bar")) .escape() .append("ofoo") .escape() .up() .append("J") .append("ii") .enter(); assertLine("bari foo", b, false); } @Test public void testEndOfLine() throws Exception { /* * The $ key causes the cursor to move to the end of the line */ TestBuffer b = (new TestBuffer("chicken sushimi")) .escape() .left(10) .append("$a is tasty!") .enter(); assertLine("chicken sushimi is tasty!", b, false); /* * Delete to EOL */ b = (new TestBuffer("chicken sushimi")) .escape() .append("0lld$") .enter(); assertLine("ch", b, false); /* * Change to EOL */ b = (new TestBuffer("chicken sushimi")) .escape() .append("0llc$opsticks") .enter(); assertLine("chopsticks", b, false); /* * Yank to EOL */ b = (new TestBuffer("chicken sushimi")) .escape() .append("0lly$$p") .enter(); assertLine("chicken sushimiicken sushimi", b, false); } @Test public void testYankLines() { /* * Yank whole line and put after */ TestBuffer b = (new TestBuffer("chicken")) .escape() .append("o") .append("sushi") .escape() .append("o") .append("pork") .escape() .up() .append("2Ypiz") .enter(); assertLine("chicken\nsushi\nzsushi\npork\npork", b, false); /* * Yank whole line and put before */ b = (new TestBuffer("chicken")) .escape() .append("o") .append("sushi") .escape() .append("o") .append("pork") .escape() .up() .append("2YPiz") .enter(); assertLine("chicken\nzsushi\npork\nsushi\npork", b, false); } @Test public void firstPrintable() throws Exception { TestBuffer b = (new TestBuffer(" foo bar")) .escape() .append("^dw") .enter(); assertLine(" bar", b, false); } @Test public void testMatch() throws Exception { /* * The % character matches brackets (square, parens, or curly). * First, test close paren w/nesting */ TestBuffer b = (new TestBuffer("ab((cdef[[))")) .escape() // Move us back one character (on last close) .append("%aX") // Find match, add an X after it .enter(); assertLine("ab(X(cdef[[))", b, false); /* * Open paren, w/nesting */ b = (new TestBuffer("ab((cdef[[))")) .escape() // Move us back one character (on last close) .append('0') // Beginning of line .right(2) // Right to first open paren .append("%aX") // Match closing, add an X after it .enter(); assertLine("ab((cdef[[))X", b, false); /* * No match leaves the cursor in place */ b = (new TestBuffer("abcd))")) .escape() .append("%aX") .enter(); assertLine("abcd))X", b, false); b = (new TestBuffer("(abcd(d")) .escape() .append("0%aX") // Beginning of line, match, append X .enter(); assertLine("(Xabcd(d", b, false); /* * Delete match */ b = (new TestBuffer("ab(def)hij")) .escape() .append("0lld%") .enter(); assertLine("abhij", b, false); b = (new TestBuffer("ab(def)")) .escape() .append("0lld%") .enter(); assertLine("ab", b, false); /* * Yank match */ b = (new TestBuffer("ab(def)hij")) .escape() .append("0lly%$p") .enter(); assertLine("ab(def)hij(def)", b, false); /* * Change match */ b = (new TestBuffer("ab(def)hij")) .escape() .append("0llc%X") .enter(); assertLine("abXhij", b, false); } @Test public void testSearch() throws Exception { /* * Tests the "/" forward search */ History history = reader.getHistory(); history.purge(); history.add("aaadef"); history.add("bbbdef"); history.add("cccdef"); /* * An aborted search should leave you exactly on the * character you were on of the original term. First, I will * test aborting by deleting back over the search expression. */ TestBuffer b = (new TestBuffer("I like frogs")) .escape() .left(4) // Cursor is on the "f" .append("/def") // Start a search .ctrl('G') // Abort .append("ibig ") // Insert mode, type "big " .enter(); // Done assertLine("I like big frogs", b, false); /* * Next, hit escape to abort. This technically isn't readline * behavior, but I added it because I find it useful. */ b = (new TestBuffer("I like frogs")) .escape() .left(4) // Cursor is on the "f" .append("/def") // Start a search .ctrl('G') // Abort the search .append("ibig ") // Insert mode, type "big " .enter(); // Done assertLine("I like big frogs", b, false); /* * Test a failed history match. This is like an abort, but it * should leave the cursor at the start of the line. */ b = (new TestBuffer("I like frogs")) .escape() .left(4) // Cursor is on the "f" .append("/III") // Search (no match) .enter() // Kick it off. .append("iX") // With cursor at start, insert an X .enter(); assertLine("I like Xfrogs", b, false); /* * Test a valid search. */ b = (new TestBuffer("I like frogs")) .escape() .left(4) // Cursor is on the "f" .append("/def") // Search (no match) .enter() // Kick it off. .append("nniX") // Move forward two, insert an X. Note I use // use "n" and "N" to move. .enter(); assertLine("aaadeXf", b, false); /* * The previous test messed with history. */ history.purge(); history.add("aaadef"); history.add("bbbdef"); history.add("cccdef"); /* * Search backwards */ b = (new TestBuffer("I like frogs")) .escape() .left(4) // Cursor is on the "f" .append("?def") // Search (no match) .enter() // Kick it off. .append("nniX") // Move forward two, insert an X. // use "n" and "N" to move. .enter(); assertLine("cccdeXf", b, false); /* * Test bug fix: use CR to terminate seach instead of newline */ b = (new TestBuffer("abc")) .enter() .append("def") .enter() .append("hij") .enter() .escape() .append("/bc") .CR() .append("iX") .enter(); assertLine("abXc", b, false); } @Test public void testWordRight() throws Exception { reader.getKeyMaps().get(VICMD).bind(new Reference(BACKWARD_KILL_LINE), ctrl('U')); TestBuffer b = (new TestBuffer("buttery frog necks")) .escape() .append("0ww") // Beginning of line, nxt word, nxt word .ctrl('U') // Kill to beginning of line .enter(); // Kick it off. assertLine("necks", b, false); b = (new TestBuffer("buttery frog foo")) .escape() .left(5) .append('w') .ctrl('K') // Kill to end of line .enter(); // Kick it off. assertLine("buttery frog ", b, false); b = (new TestBuffer("a big batch of buttery frog livers")) .escape() .append("05w") // Beg of line, 5 words right .ctrl('U') // Kill to beginning of line .enter(); // Kick it off. assertLine("frog livers", b, false); /* * Delete word right */ b = (new TestBuffer("a big batch of buttery frog livers")) .escape() .append("05dw") .enter(); assertLine("frog livers", b, false); b = (new TestBuffer("another big batch of buttery frog livers")) .escape() .append("0ldw") .enter(); assertLine("abig batch of buttery frog livers", b, false); /* * Yank word right */ b = (new TestBuffer("big brown pickles")) .escape() .append("02yw$piz") .enter(); assertLine("big brown picklesbig brownz ", b, false); /* * Put before */ b = (new TestBuffer("big brown pickles")) .escape() .append("02yw$Piz") .enter(); assertLine("big brown picklebig brownz s", b, false); /* * Change word right */ b = (new TestBuffer("big brown pickles")) .escape() .append("0wcwgreen") .enter(); assertLine("big green pickles", b, false); b = (new TestBuffer("big brown pickles")) .escape() .append("02cwlittle bitty") .enter(); assertLine("little bitty pickles", b, false); } @Test public void testWordLeft() throws Exception { reader.getKeyMaps().get(VICMD).bind(new Reference(BACKWARD_KILL_LINE), ctrl('U')); TestBuffer b = (new TestBuffer("lucious lark liquid ")) .escape() .append("bb") // Beginning of line, prv word, prv word .ctrl('K') // Kill to end of line .enter(); // Kick it off. assertLine("lucious ", b, false); b = (new TestBuffer("lucious lark liquid")) .escape() .left(2) .append('b') .ctrl('U') .enter(); assertLine("liquid", b, false); b = (new TestBuffer("lively lolling lark liquid")) .escape() .append("3b") .ctrl('K') // Kill to beginning of line .enter(); assertLine("lively ", b, false); } @Test public void testEndWord() throws Exception { reader.getKeyMaps().get(VICMD).bind(new Reference(BACKWARD_KILL_LINE), ctrl('U')); TestBuffer b = (new TestBuffer("putrid pidgen porridge")) .escape() .append("0e") .append('D') // Kill to end of line .enter(); // Kick it off. assertLine("putri", b, false); b = (new TestBuffer(" putrid pidgen porridge")) .escape() .append("0e") .append('D') // Kill to end of line .enter(); // Kick it off. assertLine(" putri", b, false); b = (new TestBuffer("putrid pidgen porridge and mash")) .escape() .append("05l") // Beg of line, 5 right .append("3e") // 3 end-of-word .ctrl('U') // Kill to beg of line .enter(); // Kick it off. assertLine("d mash", b, false); } @Test public void testInsertBeginningOfLine() throws Exception { TestBuffer b = (new TestBuffer("dessicated dog droppings")) .escape() .append("Itasty ") .enter(); assertLine("tasty dessicated dog droppings", b, false); } @Test public void testRubout() throws Exception { TestBuffer b = (new TestBuffer("gross animal stuff")) .escape() .left() .append("XXX") .enter(); assertLine("gross animal ff", b, false); b = (new TestBuffer("gross animal stuff")) .escape() .left() .append("50X") .enter(); assertLine("ff", b, false); } @Test public void testDelete() throws Exception { TestBuffer b = (new TestBuffer("thing to delete")) .escape() .append("bbxxx") .enter(); assertLine("thing delete", b, false); b = (new TestBuffer("thing to delete")) .escape() .append("bb99x") .enter(); assertLine("thing ", b, false); } @Test public void testChangeCase() throws Exception { TestBuffer b = (new TestBuffer("big.LITTLE")) .escape() .append("0~~~~~~~~~~") .enter(); assertLine("BIG.little", b, false); b = (new TestBuffer("big.LITTLE")) .escape() .append("020~") .enter(); assertLine("BIG.little", b, false); } @Test public void testChangeChar() throws Exception { TestBuffer b = (new TestBuffer("abcdefhij")) .escape() .append("0rXiY") .enter(); assertLine("YXbcdefhij", b, false); b = (new TestBuffer("abcdefhij")) .escape() .append("04rXiY") .enter(); assertLine("XXXYXefhij", b, false); b = (new TestBuffer("abcdefhij")) .escape() .append("099rZ") .enter(); assertLine("ZZZZZZZZZ", b, false); /* * Aborted replace. */ b = (new TestBuffer("abcdefhij")) .escape() .append("0r") .escape() .append("iX") .enter(); assertLine("Xabcdefhij", b, false); } @Test public void testCharSearch_f() throws Exception { /* * f = search forward for character */ TestBuffer b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("03ffiX") // start, find the third f, insert X .enter(); assertLine("aaaafaaaafaaaaXfaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("0ffffffiX") // start, find the third f, insert X .enter(); assertLine("aaaafaaaafaaaaXfaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("0ff;;iX") // start, find f, repeat fwd, repeat fwd .enter(); assertLine("aaaafaaaafaaaaXfaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("0ff;,iX") // start, find f, repeat fwd, repeat back .enter(); assertLine("aaaaXfaaaafaaaafaaaaf", b, true); b = (new TestBuffer("aaaaXaaaaXaaaaXaaaaX")) .escape() .append("0fX3;iY") // start, find X, repeat fwd x 3, ins Y .enter(); assertLine("aaaaXaaaaXaaaaXaaaaYX", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("03dff") // start, delete to third f .enter(); assertLine("aaaaf", b, true); b = (new TestBuffer("aaaaXaaaaXaaaaXaaaaX")) .escape() .append("0fX2d;") // start, find X, 2 x delete repeat last search .enter(); assertLine("aaaaaaaaX", b, true); } @Test public void testCharSearch_F() throws Exception { /* * f = search forward for character */ TestBuffer b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("3FfiX") // go 3 f's back, insert X .enter(); assertLine("aaaaXfaaaafaaaafaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("FfFfFfiX") // start, find the third f back, insert X .enter(); assertLine("aaaaXfaaaafaaaafaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("Ff;iX") // start, find f, repeat fwd, repeat fwd .enter(); assertLine("aaaafaaaaXfaaaafaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("Ff;,iX") // start, rev find f, repeat, reverse .enter(); assertLine("aaaafaaaafaaaaXfaaaaf", b, true); b = (new TestBuffer("aaaaXaaaaXaaaaXaaaaX")) .escape() .append("FX2;iY") // start, rev find X, repeat x 2, ins Y .enter(); assertLine("aaaaYXaaaaXaaaaXaaaaX", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("3dFf") // start, delete back to third f .enter(); assertLine("aaaaf", b, true); b = (new TestBuffer("aaaaXaaaaXaaaaXaaaaX")) .escape() .append("FX2d;") // start, find X, 2 x delete repeat last search .enter(); assertLine("aaaaXaaaaX", b, true); } @Test public void testCharSearch_t() throws Exception { /* * r = search forward for character, stopping before */ TestBuffer b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("03tfiX") .enter(); assertLine("aaaafaaaafaaaXafaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("0tftftfiX") .enter(); assertLine("aaaXafaaaafaaaafaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("0tf;;iX") .enter(); assertLine("aaaafaaaafaaaXafaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("02tf;,iX") .enter(); assertLine("aaaafaaaafXaaaafaaaaf", b, true); b = (new TestBuffer("aaaaXaaaaXaaaaXaaaaX")) .escape() .append("0tX3;iY") .enter(); assertLine("aaaaXaaaaXaaaaXaaaYaX", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("03dtf") .enter(); assertLine("faaaaf", b, true); b = (new TestBuffer("aaaaXaaaaXaaaaXaaaaX")) .escape() .append("0tX2d;iY") .enter(); assertLine("aaaYXaaaaX", b, true); } @Test public void testCharSearch_T() throws Exception { /* * r = search backward for character, stopping after */ TestBuffer b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("3TfiX") .enter(); assertLine("aaaafXaaaafaaaafaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("TfTfTfiX") .enter(); assertLine("aaaafaaaafaaaafXaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("Tf;;iX") .enter(); assertLine("aaaafXaaaafaaaafaaaaf", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("2Tf;,iX") .enter(); assertLine("aaaafaaaXafaaaafaaaaf", b, true); b = (new TestBuffer("aaaaXaaaaXaaaaXaaaaX")) .escape() .append("TX2;iY") .enter(); assertLine("aaaaXYaaaaXaaaaXaaaaX", b, true); b = (new TestBuffer("aaaafaaaafaaaafaaaaf")) .escape() .append("3dTf") .enter(); assertLine("aaaaff", b, true); b = (new TestBuffer("aaaaXaaaaXaaaaXaaaaX")) .escape() .append("TX2d;iY") .enter(); assertLine("aaaaXYaaaaX", b, true); } @Test public void test_dd() throws Exception { /* * This tests "dd" or delete-to + delete-to, which should kill the * current line. */ TestBuffer b = (new TestBuffer("abcdef")) .escape() .append("dd") .enter(); assertLine("", b, true); /* * I found a bug here dd didn't work at position 0. This tests the fix. */ b = (new TestBuffer("abcdef")) .escape() .append("0dd") .enter(); assertLine("", b, true); } @Test public void test_yy() throws Exception { /* * This tests "yy" or yank-to + yank-to, which should yank the whole line */ TestBuffer b = (new TestBuffer("abcdef")) .escape() .append("yyp") .enter(); assertLine("abcdefabcdef", b, true); } @Test public void test_cc() throws Exception { /* * This tests "cc" or change-to + change-to, which changes the whole line */ TestBuffer b = (new TestBuffer("abcdef")) .escape() .append("ccsuck") .enter(); assertLine("suck", b, true); } @Test public void testRegion() throws Exception { reader.getKeyMaps().get(VISUAL).bind(new Reference("kill-region"), "p"); TestBuffer b = new TestBuffer("abc def ghi") .escape() .append("bvbp") .append("iX") .enter(); assertLine("abc Xhi", b, true); b = new TestBuffer("abc def ghi") .escape() .append("bvbop") .append("iX") .enter(); assertLine("abc Xhi", b, true); b = new TestBuffer() .append("abc def ghi") .ctrl('V').append('\n') .append("foo bar baz") .ctrl('V').append('\n') .append("klm nop") .escape() .append("4bV3bp") .append("iX") .enter(); assertLine("Xklm nop", b, true); b = new TestBuffer() .append("abc def ghi") .ctrl('V').append('\n') .append("foo bar baz") .ctrl('V').append('\n') .append("klm nop") .escape() .append("4bV3bop") .append("iX") .enter(); assertLine("abc Xar baz\nklm nop", b, true); } /** * Used to test various forms of hitting "enter" (return). This can be * CTRL-J or CTRL-M...maybe others. * * @param enterChar The escape character that acts as enter. */ private void testEnter(char enterChar) throws Exception { /* * I want to test to make sure that I am re-entering insert mode * when enter is hit. */ TestBuffer b = (new TestBuffer("abc")).escape().ctrl(enterChar); assertLine("abc", b, true); /* * This sort of tests the same thing by actually enter * characters after the first enter. */ b = (new TestBuffer("abc")).escape().ctrl(enterChar) .append("def").enter(); assertLine("def", b, true); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/WidgetTest.java000066400000000000000000000037521311544710100267570ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl; import org.jline.keymap.KeyMap; import org.jline.reader.LineReader; import org.jline.reader.Reference; import org.jline.reader.Widget; import org.jline.utils.InputStreamReader; import org.junit.Test; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.Reader; import java.nio.charset.Charset; import static org.junit.Assert.assertEquals; public class WidgetTest extends ReaderTestSupport { @Test public void testCustomWidget() throws IOException { reader.getKeyMaps().get(reader.getKeyMap()) .bind(new Reference("custom-widget"), "\n"); reader.getWidgets().put("custom-widget", () -> { if (reader.getBuffer().cursor() == reader.getBuffer().length()) { reader.callWidget(LineReader.ACCEPT_LINE); } else { reader.getBuffer().write('\n'); } return true; }); TestBuffer b = new TestBuffer() .append("foo bar") .left(3) .enter() .right(3) .enter(); assertLine("foo \nbar", b, false); } @Test public void testCustomWidget2() throws IOException { reader.getKeyMaps().get(reader.getKeyMap()) .bind(new Reference(LineReader.SELF_INSERT), "\n"); reader.getKeyMaps().get(reader.getKeyMap()) .bind(new Reference(LineReader.ACCEPT_LINE), KeyMap.alt('\n')); TestBuffer b = new TestBuffer() .append("foo bar") .left(3) .enter() .right(3) .alt('\n'); assertLine("foo \nbar", b, false); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/history/000077500000000000000000000000001311544710100255235ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/history/HistoryPersistenceTest.java000066400000000000000000000064051311544710100331010ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.history; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.stream.IntStream; import org.jline.reader.LineReader; import org.jline.reader.impl.ReaderTestSupport; import org.junit.After; import org.junit.Before; import org.junit.Test; import static java.util.stream.Collectors.toList; import static org.junit.Assert.assertEquals; /** * Tests file history. * * @author Guillaume Nodet */ public class HistoryPersistenceTest extends ReaderTestSupport { @Before public void setUp() throws Exception { super.setUp(); Files.deleteIfExists(Paths.get("test")); } @After public void tearDown() throws IOException { Files.deleteIfExists(Paths.get("test")); } private void doTestFileHistory(int count, CyclicBarrier barrier) { DefaultHistory history = new DefaultHistory(reader); try { barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { throw new RuntimeException(e); } assertEquals(count, history.size()); IntStream.range(0, count) .forEach(i -> history.add("cmd" + i)); // we need to synchronize here // if we don't, multiple writes can occur at the same time and some // history items may be lost, we'd have to use a file lock to fix that // but that's not testable // what we're testing here is the fact that only *new* items are // written to the file incrementally and that we're not rewriting the // whole file synchronized (reader) { try { history.save(); } catch (IOException e) { e.printStackTrace(); } } } @Test public void testFileHistory() throws Exception { reader.setVariable(LineReader.HISTORY_FILE, Paths.get("test")); reader.unsetOpt(LineReader.Option.HISTORY_INCREMENTAL); int cmdsPerThread = 3; int nbThreads = 3; DefaultHistory history = new DefaultHistory(reader); IntStream.range(0, cmdsPerThread) .forEach(i -> history.add("cmd" + i)); history.save(); List lines = Files.readAllLines(Paths.get("test")); assertEquals(cmdsPerThread, lines.size()); final CyclicBarrier barrier = new CyclicBarrier(nbThreads); List ts = IntStream.range(0, nbThreads) .mapToObj(i -> new Thread(() -> { doTestFileHistory(cmdsPerThread, barrier); })) .collect(toList()); ts.forEach(Thread::start); for (Thread t : ts) { t.join(); } lines = Files.readAllLines(Paths.get("test")); assertEquals(cmdsPerThread * (nbThreads + 1), lines.size()); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/history/HistoryReaderTest.java000066400000000000000000000141321311544710100320130ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.history; import org.jline.keymap.KeyMap; import org.jline.reader.Binding; import org.jline.reader.Reference; import org.jline.reader.impl.ReaderTestSupport; import org.junit.Test; import static org.jline.reader.LineReader.ACCEPT_LINE; import static org.jline.reader.LineReader.BACKWARD_CHAR; import static org.jline.reader.LineReader.BEGINNING_OF_LINE; import static org.jline.reader.LineReader.DOWN_HISTORY; import static org.jline.reader.LineReader.DOWN_LINE_OR_HISTORY; import static org.jline.reader.LineReader.HISTORY_SEARCH_BACKWARD; import static org.jline.reader.LineReader.HISTORY_SEARCH_FORWARD; import static org.jline.reader.LineReader.UP_HISTORY; import static org.jline.reader.LineReader.UP_LINE_OR_HISTORY; /** * Tests command history. * * @author Marc Prud'hommeaux */ public class HistoryReaderTest extends ReaderTestSupport { @Test public void testSingleHistory() throws Exception { KeyMap map = reader.getKeys(); // Map in HISTORY_SEARCH_BACKWARD. map.bind(new Reference(UP_LINE_OR_HISTORY), "\033[A"); map.bind(new Reference(DOWN_LINE_OR_HISTORY), "\033[B"); TestBuffer b = new TestBuffer(). append("test line 1").op(ACCEPT_LINE). append("test line 2").op(ACCEPT_LINE). append("test line 3").op(ACCEPT_LINE). append("test line 4").op(ACCEPT_LINE). append("test line 5").op(ACCEPT_LINE). append("ab"); assertBuffer("ab", b); assertBuffer("test line 5", b = b.op(UP_HISTORY)); assertBuffer("test line 5", b = b.op(BACKWARD_CHAR)); assertBuffer("test line 4", b = b.op(UP_HISTORY)); assertBuffer("test line 5", b = b.op(DOWN_HISTORY)); assertBuffer("test line 4", b = b.op(UP_HISTORY)); assertBuffer("test line 3", b = b.op(UP_HISTORY)); assertBuffer("test line 2", b = b.op(UP_HISTORY)); assertBuffer("test line 1", b = b.op(UP_HISTORY)); // beginning of history assertBuffer("test line 1", b = b.op(UP_HISTORY)); assertBuffer("test line 1", b = b.op(UP_HISTORY)); assertBuffer("test line 1", b = b.op(UP_HISTORY)); assertBuffer("test line 1", b = b.op(UP_HISTORY)); assertBuffer("test line 2", b = b.op(DOWN_HISTORY)); assertBuffer("test line 3", b = b.op(DOWN_HISTORY)); assertBuffer("test line 4", b = b.op(DOWN_HISTORY)); assertBuffer("test line 5", b = b.op(DOWN_HISTORY)); // end of history assertBuffer("ab", b = b.op(DOWN_HISTORY)); assertBuffer("ab", b = b.op(DOWN_HISTORY)); assertBuffer("ab", b = b.op(DOWN_HISTORY)); assertBuffer("test line 5", b = b.op(UP_HISTORY)); assertBuffer("test line 4", b = b.op(UP_HISTORY)); b = b.op(BEGINNING_OF_LINE).append("XXX").op(ACCEPT_LINE); assertBuffer("XXXtest line 4", b = b.op(UP_HISTORY)); assertBuffer("test line 5", b = b.op(UP_HISTORY)); assertBuffer("test line 4", b = b.op(UP_HISTORY)); assertBuffer("test line 5", b = b.op(DOWN_HISTORY)); assertBuffer("XXXtest line 4", b = b.op(DOWN_HISTORY)); assertBuffer("", b = b.op(DOWN_HISTORY)); assertBuffer("XXXtest line 4", b = b.op(UP_HISTORY)); assertBuffer("XXXtest line 4", b = b.op(ACCEPT_LINE).op(UP_HISTORY)); assertBuffer("XXXtest line 4", b = b.op(ACCEPT_LINE).op(UP_HISTORY)); assertBuffer("XXXtest line 4", b = b.op(ACCEPT_LINE).op(UP_HISTORY)); assertBuffer("XXXtest line 4", b = b.op(ACCEPT_LINE).op(UP_HISTORY)); } @Test public void testHistorySearchBackwardAndForward() throws Exception { KeyMap map = reader.getKeys(); // Map in HISTORY_SEARCH_BACKWARD. map.bind(new Reference(HISTORY_SEARCH_BACKWARD), "\033[0A"); map.bind(new Reference(HISTORY_SEARCH_FORWARD), "\033[0B"); TestBuffer b = new TestBuffer(). append("toes").op(ACCEPT_LINE). append("the quick brown").op(ACCEPT_LINE). append("fox jumps").op(ACCEPT_LINE). append("over the").op(ACCEPT_LINE). append("lazy dog").op(ACCEPT_LINE). append(""); assertBuffer("", b); // Using history-search-backward behaves like previous-history when // no input has been provided. assertBuffer("lazy dog", b = b.append("\033[0A")); assertBuffer("over the", b = b.append("\033[0A")); assertBuffer("fox jumps", b = b.append("\033[0A")); // history-search-forward should behave line next-history when no // input has been provided. assertBuffer("over the", b = b.append("\033[0B")); assertBuffer("lazy dog", b = b.append("\033[0B")); assertBuffer("", b = b.append("\033[0B")); // Make sure we go back correctly. assertBuffer("lazy dog", b = b.append("\033[0A")); assertBuffer("over the", b = b.append("\033[0A")); assertBuffer("fox jumps", b = b.append("\033[0A")); // Search forward on 'l'. b = b.append("l"); assertBuffer("fox jumpsl", b = b.append("\033[0B")); // Try moving again. assertBuffer("fox jumpsl", b.append("\033[0B")); assertBuffer("fox jumps", b.append("\033[0A")); // Now we should have more context and history-search-backward should // take us to "the quick brown" line. b = b.back(100).append("t"); assertBuffer("the quick brown", b = b.append("\033[0A")); // Try moving backward again. assertBuffer("toes", b = b.append("\033[0A")); assertBuffer("the quick brown", b = b.append("\033[0B")); b = b.back(100); assertBuffer("fox jumps", b = b.append("\033[0B")); b = b.back(100); b = b.append("to"); assertBuffer("toes", b = b.append("\033[0A")); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/reader/impl/history/HistoryTest.java000066400000000000000000000065621311544710100307000ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.reader.impl.history; import org.jline.reader.History; import org.jline.reader.LineReader; import org.jline.reader.impl.ReaderTestSupport; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.time.Instant; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertEquals; /** * Tests for {@link DefaultHistory}. * * @author Marc Prud'hommeaux */ public class HistoryTest extends ReaderTestSupport { private DefaultHistory history; @Before public void setUp() throws Exception { super.setUp(); history = new DefaultHistory(reader); } @After public void tearDown() { history = null; } @Test public void testAdd() { assertEquals(0, history.size()); history.add("test"); assertEquals(1, history.size()); assertEquals("test", history.get(0)); assertEquals(1, history.index()); } private void assertHistoryContains(final int offset, final String... items) { assertEquals(items.length, history.size()); int i=0; for (History.Entry entry : history) { assertEquals(offset + i, entry.index()); assertEquals(items[i++], entry.line()); } } @Test public void testOffset() { reader.setVariable(LineReader.HISTORY_SIZE, 5); assertEquals(0, history.size()); assertEquals(0, history.index()); history.add("a"); history.add("b"); history.add("c"); history.add("d"); history.add("e"); assertEquals(5, history.size()); assertEquals(5, history.index()); assertHistoryContains(0, "a", "b", "c", "d", "e"); history.add("f"); assertEquals(5, history.size()); assertEquals(6, history.index()); assertHistoryContains(1, "b", "c", "d", "e", "f"); assertEquals("f", history.get(5)); } @Test public void testTrim() { List entries = new ArrayList<>(); entries.add(new DefaultHistory.EntryImpl(0, Instant.now(), "a")); entries.add(new DefaultHistory.EntryImpl(1, Instant.now(), "b")); entries.add(new DefaultHistory.EntryImpl(2, Instant.now(), "c")); entries.add(new DefaultHistory.EntryImpl(3, Instant.now(), "d")); entries.add(new DefaultHistory.EntryImpl(4, Instant.now(), "e")); entries.add(new DefaultHistory.EntryImpl(5, Instant.now(), "d")); entries.add(new DefaultHistory.EntryImpl(6, Instant.now(), "c")); entries.add(new DefaultHistory.EntryImpl(7, Instant.now(), "b")); entries.add(new DefaultHistory.EntryImpl(8, Instant.now(), "a")); List trimmed = new ArrayList<>(entries); DefaultHistory.doTrimHistory(trimmed, 6); assertEquals(5, trimmed.size()); DefaultHistory.doTrimHistory(trimmed, 3); assertEquals(3, trimmed.size()); assertEquals("c", trimmed.get(0).line()); assertEquals("b", trimmed.get(1).line()); assertEquals("a", trimmed.get(2).line()); } } jline3-jline-3.3.1/reader/src/test/java/org/jline/terminal/000077500000000000000000000000001311544710100234325ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/jline/terminal/impl/000077500000000000000000000000001311544710100243735ustar00rootroot00000000000000jline3-jline-3.3.1/reader/src/test/java/org/jline/terminal/impl/ExternalTerminalTest.java000066400000000000000000000113451311544710100313600ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.EnumSet; import org.jline.reader.LineReader; import org.jline.reader.LineReaderBuilder; import org.jline.reader.UserInterruptException; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Attributes.OutputFlag; import org.jline.terminal.Cursor; import org.jline.terminal.Terminal; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; public class ExternalTerminalTest { @Test public void testInput() throws IOException, InterruptedException { PipedInputStream in = new PipedInputStream(); PipedOutputStream outIn = new PipedOutputStream(in); ByteArrayOutputStream out = new ByteArrayOutputStream(); ExternalTerminal console = new ExternalTerminal("foo", "ansi", in, out, "UTF-8"); testConsole(outIn, out, console); } /* SANDBOX JANSI @Test public void testPosix() throws IOException, InterruptedException { PipedInputStream in = new PipedInputStream(); PipedOutputStream outIn = new PipedOutputStream(in); ByteArrayOutputStream out = new ByteArrayOutputStream(); Console terminal = new PosixPtyConsole("ansi", new ConsoleReaderBuilder(), NativePty.open(null, null), in, out, "UTF-8"); testConsole(outIn, out, terminal); } */ private void testConsole(PipedOutputStream outIn, ByteArrayOutputStream out, Terminal terminal) throws IOException, InterruptedException { Attributes attributes = terminal.getAttributes(); attributes.setLocalFlag(LocalFlag.ECHO, true); attributes.setInputFlag(InputFlag.IGNCR, true); attributes.setOutputFlags(EnumSet.of(OutputFlag.OPOST)); terminal.setAttributes(attributes); outIn.write("a\r\nb".getBytes()); while (out.size() < 3) { Thread.sleep(100); } String output = out.toString(); assertEquals("a\nb", output); } @Test public void testInterrupt() throws Exception { PipedInputStream in = new PipedInputStream(); final PipedOutputStream outIn = new PipedOutputStream(in); ByteArrayOutputStream out = new ByteArrayOutputStream(); ExternalTerminal console = new ExternalTerminal("foo", "ansi", in, out, "UTF-8"); Attributes attributes = console.getAttributes(); attributes.setLocalFlag(LocalFlag.ISIG, true); attributes.setControlChar(ControlChar.VINTR, 3); console.setAttributes(attributes); LineReader lineReader = LineReaderBuilder.builder() .terminal(console).build(); assertNotNull(lineReader); Thread th = new Thread() { public void run() { try { outIn.write('a'); outIn.write('b'); outIn.flush(); Thread.sleep(50); outIn.write(3); outIn.write('c'); outIn.flush(); Thread.sleep(50); } catch (Exception e) { e.printStackTrace(); } } }; th.start(); try { lineReader.readLine(); fail("Expected UserInterruptException"); } catch (UserInterruptException e) { assertEquals("ab", e.getPartialLine()); } th.join(); } @Test public void testCursorPosition() throws IOException { PipedInputStream in = new PipedInputStream(); final PipedOutputStream outIn = new PipedOutputStream(in); ByteArrayOutputStream out = new ByteArrayOutputStream(); ExternalTerminal console = new ExternalTerminal("foo", "ansi", in, out, "UTF-8"); outIn.write(new byte[] { 'a', '\033', 'b', '\033', '[', '2', ';', '3', 'R', 'f'}); outIn.flush(); StringBuilder sb = new StringBuilder(); Cursor cursor = console.getCursorPosition(c -> sb.append((char) c)); assertNotNull(cursor); assertEquals(2, cursor.getX()); assertEquals(1, cursor.getY()); assertEquals("a\033b", sb.toString()); assertEquals('f', console.reader().read()); } } jline3-jline-3.3.1/remote-ssh/000077500000000000000000000000001311544710100160645ustar00rootroot00000000000000jline3-jline-3.3.1/remote-ssh/pom.xml000066400000000000000000000024241311544710100174030ustar00rootroot00000000000000 4.0.0 org.jline jline-parent 3.3.1 jline-remote-ssh JLine Remote SSH org.jline jline-builtins org.apache.sshd sshd-core true junit junit test jline3-jline-3.3.1/remote-ssh/src/000077500000000000000000000000001311544710100166535ustar00rootroot00000000000000jline3-jline-3.3.1/remote-ssh/src/main/000077500000000000000000000000001311544710100175775ustar00rootroot00000000000000jline3-jline-3.3.1/remote-ssh/src/main/java/000077500000000000000000000000001311544710100205205ustar00rootroot00000000000000jline3-jline-3.3.1/remote-ssh/src/main/java/org/000077500000000000000000000000001311544710100213075ustar00rootroot00000000000000jline3-jline-3.3.1/remote-ssh/src/main/java/org/jline/000077500000000000000000000000001311544710100224105ustar00rootroot00000000000000jline3-jline-3.3.1/remote-ssh/src/main/java/org/jline/builtins/000077500000000000000000000000001311544710100242415ustar00rootroot00000000000000jline3-jline-3.3.1/remote-ssh/src/main/java/org/jline/builtins/ssh/000077500000000000000000000000001311544710100250365ustar00rootroot00000000000000jline3-jline-3.3.1/remote-ssh/src/main/java/org/jline/builtins/ssh/ShellCommand.java000066400000000000000000000050031311544710100302450ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins.ssh; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; import org.apache.sshd.server.ExitCallback; import org.apache.sshd.server.SessionAware; import org.apache.sshd.server.session.ServerSession; public class ShellCommand implements Command, SessionAware { private static final Logger LOGGER = Logger.getLogger(ShellCommand.class.getName()); private final Consumer execute; private final String command; private InputStream in; private OutputStream out; private OutputStream err; private ExitCallback callback; private ServerSession session; private Environment env; public ShellCommand(Consumer execute, String command) { this.execute = execute; this.command = command; } public void setInputStream(InputStream in) { this.in = in; } public void setOutputStream(OutputStream out) { this.out = out; } public void setErrorStream(OutputStream err) { this.err = err; } public void setExitCallback(ExitCallback callback) { this.callback = callback; } public void setSession(ServerSession session) { this.session = session; } public void start(final Environment env) throws IOException { this.env = env; new Thread(this::run).start(); } private void run() { int exitStatus = 0; try { execute.accept(new Ssh.ExecuteParams(command, env.getEnv(), in, out, err)); } catch (RuntimeException e) { exitStatus = 1; LOGGER.log(Level.SEVERE, "Unable to start shell", e); try { Throwable t = (e.getCause() != e) ? e.getCause() : e; err.write(t.toString().getBytes()); err.flush(); } catch (IOException e2) { // Ignore } } finally { ShellFactoryImpl.close(in, out, err); callback.onExit(exitStatus); } } public void destroy() { } } jline3-jline-3.3.1/remote-ssh/src/main/java/org/jline/builtins/ssh/ShellFactoryImpl.java000066400000000000000000000217341311544710100311310ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins.ssh; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; import java.util.function.Consumer; import org.apache.sshd.common.Factory; import org.apache.sshd.common.channel.PtyMode; import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; import org.apache.sshd.server.ExitCallback; import org.apache.sshd.server.SessionAware; import org.apache.sshd.server.Signal; import org.apache.sshd.server.session.ServerSession; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Attributes.OutputFlag; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; /** * SSHD {@link org.apache.sshd.server.Command} factory which provides access to * Shell. */ public class ShellFactoryImpl implements Factory { private final Consumer shell; public ShellFactoryImpl(Consumer shell) { this.shell = shell; } private static void flush(OutputStream... streams) { for (OutputStream s : streams) { try { s.flush(); } catch (IOException e) { // Ignore } } } static void close(Closeable... closeables) { for (Closeable c : closeables) { try { c.close(); } catch (IOException e) { // Ignore } } } public Command create() { return new ShellImpl(); } public class ShellImpl implements Command, SessionAware { private InputStream in; private OutputStream out; private OutputStream err; private ExitCallback callback; private ServerSession session; private boolean closed; public void setInputStream(final InputStream in) { this.in = in; } public void setOutputStream(final OutputStream out) { this.out = out; } public void setErrorStream(final OutputStream err) { this.err = err; } public void setExitCallback(ExitCallback callback) { this.callback = callback; } public void setSession(ServerSession session) { this.session = session; } public void start(final Environment env) throws IOException { try { new Thread(() -> { try { ShellImpl.this.run(env); } catch (Throwable t) { t.printStackTrace(); } }).start(); } catch (Exception e) { throw new IOException("Unable to start shell", e); } } public void run(Environment env) throws Exception { try { Terminal terminal = TerminalBuilder.builder() .name("JLine SSH") .type(env.getEnv().get("TERM")) .system(false) .streams(in, out) .build(); terminal.setSize(new Size(Integer.parseInt(env.getEnv().get("COLUMNS")), Integer.parseInt(env.getEnv().get("LINES")))); Attributes attr = terminal.getAttributes(); for (Map.Entry e : env.getPtyModes().entrySet()) { switch (e.getKey()) { case VINTR: attr.setControlChar(ControlChar.VINTR, e.getValue()); break; case VQUIT: attr.setControlChar(ControlChar.VQUIT, e.getValue()); break; case VERASE: attr.setControlChar(ControlChar.VERASE, e.getValue()); break; case VKILL: attr.setControlChar(ControlChar.VKILL, e.getValue()); break; case VEOF: attr.setControlChar(ControlChar.VEOF, e.getValue()); break; case VEOL: attr.setControlChar(ControlChar.VEOL, e.getValue()); break; case VEOL2: attr.setControlChar(ControlChar.VEOL2, e.getValue()); break; case VSTART: attr.setControlChar(ControlChar.VSTART, e.getValue()); break; case VSTOP: attr.setControlChar(ControlChar.VSTOP, e.getValue()); break; case VSUSP: attr.setControlChar(ControlChar.VSUSP, e.getValue()); break; case VDSUSP: attr.setControlChar(ControlChar.VDSUSP, e.getValue()); break; case VREPRINT: attr.setControlChar(ControlChar.VREPRINT, e.getValue()); break; case VWERASE: attr.setControlChar(ControlChar.VWERASE, e.getValue()); break; case VLNEXT: attr.setControlChar(ControlChar.VLNEXT, e.getValue()); break; /* case VFLUSH: attr.setControlChar(ControlChar.VMIN, e.getValue()); break; case VSWTCH: attr.setControlChar(ControlChar.VTIME, e.getValue()); break; */ case VSTATUS: attr.setControlChar(ControlChar.VSTATUS, e.getValue()); break; case VDISCARD: attr.setControlChar(ControlChar.VDISCARD, e.getValue()); break; case ECHO: attr.setLocalFlag(LocalFlag.ECHO, e.getValue() != 0); break; case ICANON: attr.setLocalFlag(LocalFlag.ICANON, e.getValue() != 0); break; case ISIG: attr.setLocalFlag(LocalFlag.ISIG, e.getValue() != 0); break; case ICRNL: attr.setInputFlag(InputFlag.ICRNL, e.getValue() != 0); break; case INLCR: attr.setInputFlag(InputFlag.INLCR, e.getValue() != 0); break; case IGNCR: attr.setInputFlag(InputFlag.IGNCR, e.getValue() != 0); break; case OCRNL: attr.setOutputFlag(OutputFlag.OCRNL, e.getValue() != 0); break; case ONLCR: attr.setOutputFlag(OutputFlag.ONLCR, e.getValue() != 0); break; case ONLRET: attr.setOutputFlag(OutputFlag.ONLRET, e.getValue() != 0); break; case OPOST: attr.setOutputFlag(OutputFlag.OPOST, e.getValue() != 0); break; } } terminal.setAttributes(attr); env.addSignalListener(signals -> { terminal.setSize(new Size(Integer.parseInt(env.getEnv().get("COLUMNS")), Integer.parseInt(env.getEnv().get("LINES")))); terminal.raise(Terminal.Signal.WINCH); }, Signal.WINCH); shell.accept(new Ssh.ShellParams(env.getEnv(), terminal, this::destroy)); } catch (Throwable t) { t.printStackTrace(); } } public void destroy() { if (!closed) { closed = true; flush(out, err); close(in, out, err); callback.onExit(0); } } } } jline3-jline-3.3.1/remote-ssh/src/main/java/org/jline/builtins/ssh/Ssh.java000066400000000000000000000460271311544710100264470ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins.ssh; import java.io.*; import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.auth.keyboard.UserInteraction; import org.apache.sshd.client.channel.ChannelShell; import org.apache.sshd.client.channel.ClientChannel; import org.apache.sshd.client.channel.ClientChannelEvent; import org.apache.sshd.client.future.ConnectFuture; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.channel.PtyMode; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.util.io.NoCloseInputStream; import org.apache.sshd.common.util.io.NoCloseOutputStream; import org.apache.sshd.server.SshServer; import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; import org.apache.sshd.server.scp.ScpCommandFactory; import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; import org.jline.builtins.Options; import org.jline.reader.LineReader; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.Terminal; public class Ssh { public static final String[] functions = {"ssh", "sshd"}; public static class ShellParams { private final Map env; private final Terminal terminal; private final Runnable closer; public ShellParams(Map env, Terminal terminal, Runnable closer) { this.env = env; this.terminal = terminal; this.closer = closer; } public Map getEnv() { return env; } public Terminal getTerminal() { return terminal; } public Runnable getCloser() { return closer; } } public static class ExecuteParams { private final String command; private final Map env; private final InputStream in; private final OutputStream out; private final OutputStream err; public ExecuteParams(String command, Map env, InputStream in, OutputStream out, OutputStream err) { this.command = command; this.env = env; this.in = in; this.out = out; this.err = err; } public String getCommand() { return command; } public Map getEnv() { return env; } public InputStream getIn() { return in; } public OutputStream getOut() { return out; } public OutputStream getErr() { return err; } } private static final int defaultPort = 2022; private final Consumer shell; private final Consumer execute; private final Supplier serverBuilder; private final Supplier clientBuilder; private SshServer server; private int port; private String ip; public Ssh(Consumer shell, Consumer execute, Supplier serverBuilder, Supplier clientBuilder) { this.shell = shell; this.execute = execute; this.serverBuilder = serverBuilder; this.clientBuilder = clientBuilder; } public void ssh(Terminal terminal, LineReader reader, String user, InputStream stdin, PrintStream stdout, PrintStream stderr, String[] argv) throws Exception { final String[] usage = {"ssh - connect to a server using ssh", "Usage: ssh [user@]hostname [command]", " -? --help show help"}; Options opt = Options.compile(usage).parse(argv, true); List args = opt.args(); if (opt.isSet("help") || args.isEmpty()) { opt.usage(stderr); return; } String username = user; String hostname = args.remove(0); int port = this.port; String command = null; int idx = hostname.indexOf('@'); if (idx >= 0) { username = hostname.substring(0, idx); hostname = hostname.substring(idx + 1); } idx = hostname.indexOf(':'); if (idx >= 0) { port = Integer.parseInt(hostname.substring(idx + 1)); hostname = hostname.substring(0, idx); } if (!args.isEmpty()) { command = String.join(" ", args); } try (SshClient client = clientBuilder.get()) { JLineUserInteraction ui = new JLineUserInteraction(terminal, reader, stderr); client.setFilePasswordProvider(ui); client.setUserInteraction(ui); client.start(); try (ClientSession sshSession = connectWithRetries(terminal.writer(), client, username, hostname, port, 3)) { sshSession.auth().verify(); if (command != null) { ClientChannel channel = sshSession.createChannel("exec", command + "\n"); channel.setIn(new ByteArrayInputStream(new byte[0])); channel.setOut(new NoCloseOutputStream(stdout)); channel.setErr(new NoCloseOutputStream(stderr)); channel.open().verify(); channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0); } else { final ChannelShell channel = sshSession.createShellChannel(); Attributes attributes = terminal.enterRawMode(); try { Map modes = new HashMap<>(); // Control chars modes.put(PtyMode.VINTR, attributes.getControlChar(Attributes.ControlChar.VINTR)); modes.put(PtyMode.VQUIT, attributes.getControlChar(Attributes.ControlChar.VQUIT)); modes.put(PtyMode.VERASE, attributes.getControlChar(Attributes.ControlChar.VERASE)); modes.put(PtyMode.VKILL, attributes.getControlChar(Attributes.ControlChar.VKILL)); modes.put(PtyMode.VEOF, attributes.getControlChar(Attributes.ControlChar.VEOF)); modes.put(PtyMode.VEOL, attributes.getControlChar(Attributes.ControlChar.VEOL)); modes.put(PtyMode.VEOL2, attributes.getControlChar(Attributes.ControlChar.VEOL2)); modes.put(PtyMode.VSTART, attributes.getControlChar(Attributes.ControlChar.VSTART)); modes.put(PtyMode.VSTOP, attributes.getControlChar(Attributes.ControlChar.VSTOP)); modes.put(PtyMode.VSUSP, attributes.getControlChar(Attributes.ControlChar.VSUSP)); modes.put(PtyMode.VDSUSP, attributes.getControlChar(Attributes.ControlChar.VDSUSP)); modes.put(PtyMode.VREPRINT, attributes.getControlChar(Attributes.ControlChar.VREPRINT)); modes.put(PtyMode.VWERASE, attributes.getControlChar(Attributes.ControlChar.VWERASE)); modes.put(PtyMode.VLNEXT, attributes.getControlChar(Attributes.ControlChar.VLNEXT)); modes.put(PtyMode.VSTATUS, attributes.getControlChar(Attributes.ControlChar.VSTATUS)); modes.put(PtyMode.VDISCARD, attributes.getControlChar(Attributes.ControlChar.VDISCARD)); // Input flags modes.put(PtyMode.IGNPAR, getFlag(attributes, Attributes.InputFlag.IGNPAR)); modes.put(PtyMode.PARMRK, getFlag(attributes, Attributes.InputFlag.PARMRK)); modes.put(PtyMode.INPCK, getFlag(attributes, Attributes.InputFlag.INPCK)); modes.put(PtyMode.ISTRIP, getFlag(attributes, Attributes.InputFlag.ISTRIP)); modes.put(PtyMode.INLCR, getFlag(attributes, Attributes.InputFlag.INLCR)); modes.put(PtyMode.IGNCR, getFlag(attributes, Attributes.InputFlag.IGNCR)); modes.put(PtyMode.ICRNL, getFlag(attributes, Attributes.InputFlag.ICRNL)); modes.put(PtyMode.IXON, getFlag(attributes, Attributes.InputFlag.IXON)); modes.put(PtyMode.IXANY, getFlag(attributes, Attributes.InputFlag.IXANY)); modes.put(PtyMode.IXOFF, getFlag(attributes, Attributes.InputFlag.IXOFF)); // Local flags modes.put(PtyMode.ISIG, getFlag(attributes, Attributes.LocalFlag.ISIG)); modes.put(PtyMode.ICANON, getFlag(attributes, Attributes.LocalFlag.ICANON)); modes.put(PtyMode.ECHO, getFlag(attributes, Attributes.LocalFlag.ECHO)); modes.put(PtyMode.ECHOE, getFlag(attributes, Attributes.LocalFlag.ECHOE)); modes.put(PtyMode.ECHOK, getFlag(attributes, Attributes.LocalFlag.ECHOK)); modes.put(PtyMode.ECHONL, getFlag(attributes, Attributes.LocalFlag.ECHONL)); modes.put(PtyMode.NOFLSH, getFlag(attributes, Attributes.LocalFlag.NOFLSH)); modes.put(PtyMode.TOSTOP, getFlag(attributes, Attributes.LocalFlag.TOSTOP)); modes.put(PtyMode.IEXTEN, getFlag(attributes, Attributes.LocalFlag.IEXTEN)); // Output flags modes.put(PtyMode.OPOST, getFlag(attributes, Attributes.OutputFlag.OPOST)); modes.put(PtyMode.ONLCR, getFlag(attributes, Attributes.OutputFlag.ONLCR)); modes.put(PtyMode.OCRNL, getFlag(attributes, Attributes.OutputFlag.OCRNL)); modes.put(PtyMode.ONOCR, getFlag(attributes, Attributes.OutputFlag.ONOCR)); modes.put(PtyMode.ONLRET, getFlag(attributes, Attributes.OutputFlag.ONLRET)); channel.setPtyModes(modes); channel.setPtyColumns(terminal.getWidth()); channel.setPtyLines(terminal.getHeight()); channel.setAgentForwarding(true); channel.setEnv("TERM", terminal.getType()); // TODO: channel.setEnv("LC_CTYPE", terminal.encoding().toString()); channel.setIn(new NoCloseInputStream(stdin)); channel.setOut(new NoCloseOutputStream(stdout)); channel.setErr(new NoCloseOutputStream(stderr)); channel.open().verify(); Terminal.SignalHandler prevWinchHandler = terminal.handle(Terminal.Signal.WINCH, signal -> { try { Size size = terminal.getSize(); channel.sendWindowChange(size.getColumns(), size.getRows()); } catch (IOException e) { // Ignore } }); Terminal.SignalHandler prevQuitHandler = terminal.handle(Terminal.Signal.QUIT, signal -> { try { channel.getInvertedIn().write(attributes.getControlChar(Attributes.ControlChar.VQUIT)); channel.getInvertedIn().flush(); } catch (IOException e) { // Ignore } }); Terminal.SignalHandler prevIntHandler = terminal.handle(Terminal.Signal.INT, signal -> { try { channel.getInvertedIn().write(attributes.getControlChar(Attributes.ControlChar.VINTR)); channel.getInvertedIn().flush(); } catch (IOException e) { // Ignore } }); Terminal.SignalHandler prevStopHandler = terminal.handle(Terminal.Signal.TSTP, signal -> { try { channel.getInvertedIn().write(attributes.getControlChar(Attributes.ControlChar.VDSUSP)); channel.getInvertedIn().flush(); } catch (IOException e) { // Ignore } }); try { channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0); } finally { terminal.handle(Terminal.Signal.WINCH, prevWinchHandler); terminal.handle(Terminal.Signal.INT, prevIntHandler); terminal.handle(Terminal.Signal.TSTP, prevStopHandler); terminal.handle(Terminal.Signal.QUIT, prevQuitHandler); } } finally { terminal.setAttributes(attributes); } } } } } private static int getFlag(Attributes attributes, Attributes.InputFlag flag) { return attributes.getInputFlag(flag) ? 1 : 0; } private static int getFlag(Attributes attributes, Attributes.OutputFlag flag) { return attributes.getOutputFlag(flag) ? 1 : 0; } private static int getFlag(Attributes attributes, Attributes.LocalFlag flag) { return attributes.getLocalFlag(flag) ? 1 : 0; } private ClientSession connectWithRetries(PrintWriter stdout, SshClient client, String username, String host, int port, int maxAttempts) throws Exception { ClientSession session = null; int retries = 0; do { ConnectFuture future = client.connect(username, host, port); future.await(); try { session = future.getSession(); } catch (Exception ex) { if (retries++ < maxAttempts) { Thread.sleep(2 * 1000); stdout.println("retrying (attempt " + retries + ") ..."); } else { throw ex; } } } while (session == null); return session; } public void sshd(PrintStream stdout, PrintStream stderr, String[] argv) throws IOException { final String[] usage = {"sshd - start an ssh server", "Usage: sshd [-i ip] [-p port] start | stop | status", " -i --ip=INTERFACE listen interface (default=127.0.0.1)", " -p --port=PORT listen port (default=" + defaultPort + ")", " -? --help show help"}; Options opt = Options.compile(usage).parse(argv, true); List args = opt.args(); if (opt.isSet("help") || args.isEmpty()) { opt.usage(stderr); return; } String command = args.get(0); if ("start".equals(command)) { if (server != null) { throw new IllegalStateException("sshd is already running on port " + port); } ip = opt.get("ip"); port = opt.getNumber("port"); start(); status(stdout); } else if ("stop".equals(command)) { if (server == null) { throw new IllegalStateException("sshd is not running."); } stop(); } else if ("status".equals(command)) { status(stdout); } else { throw opt.usageError("bad command: " + command); } } private void status(PrintStream stdout) { if (server != null) { stdout.println("sshd is running on " + ip + ":" + port); } else { stdout.println("sshd is not running."); } } private void start() throws IOException { server = serverBuilder.get(); server.setPort(port); server.setHost(ip); server.setShellFactory(new ShellFactoryImpl(shell)); server.setCommandFactory(new ScpCommandFactory.Builder() .withDelegate(command -> new ShellCommand(execute, command)).build()); server.setSubsystemFactories(Collections.singletonList( new SftpSubsystemFactory.Builder().build() )); server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider()); server.start(); } private void stop() throws IOException { try { server.stop(); } finally { server = null; } } private static class JLineUserInteraction implements UserInteraction, FilePasswordProvider { private final Terminal terminal; private final LineReader reader; private final PrintStream stderr; public JLineUserInteraction(Terminal terminal, LineReader reader, PrintStream stderr) { this.terminal = terminal; this.reader = reader; this.stderr = stderr; } @Override public String getPassword(String resourceKey) throws IOException { return readLine("Enter password for " + resourceKey + ":", false); } @Override public void welcome(ClientSession session, String banner, String lang) { terminal.writer().println(banner); } @Override public String[] interactive(ClientSession s, String name, String instruction, String lang, String[] prompt, boolean[] echo) { String[] answers = new String[prompt.length]; try { for (int i = 0; i < prompt.length; i++) { answers[i] = readLine(prompt[i], echo[i]); } } catch (Exception e) { stderr.append(e.getClass().getSimpleName()).append(" while read prompts: ").println(e.getMessage()); } return answers; } @Override public boolean isInteractionAllowed(ClientSession session) { return true; } @Override public void serverVersionInfo(ClientSession session, List lines) { for (String l : lines) { terminal.writer().append('\t').println(l); } } @Override public String getUpdatedPassword(ClientSession session, String prompt, String lang) { try { return readLine(prompt, false); } catch (Exception e) { stderr.append(e.getClass().getSimpleName()).append(" while reading password: ").println(e.getMessage()); } return null; } private String readLine(String prompt, boolean echo) { return reader.readLine(prompt + " ", echo ? null : '\0'); } } } jline3-jline-3.3.1/remote-telnet/000077500000000000000000000000001311544710100165625ustar00rootroot00000000000000jline3-jline-3.3.1/remote-telnet/pom.xml000066400000000000000000000024321311544710100201000ustar00rootroot00000000000000 4.0.0 org.jline jline-parent 3.3.1 jline-remote-telnet JLine Remote Telnet org.jline jline-builtins org.apache.sshd sshd-core true junit junit test jline3-jline-3.3.1/remote-telnet/src/000077500000000000000000000000001311544710100173515ustar00rootroot00000000000000jline3-jline-3.3.1/remote-telnet/src/main/000077500000000000000000000000001311544710100202755ustar00rootroot00000000000000jline3-jline-3.3.1/remote-telnet/src/main/java/000077500000000000000000000000001311544710100212165ustar00rootroot00000000000000jline3-jline-3.3.1/remote-telnet/src/main/java/org/000077500000000000000000000000001311544710100220055ustar00rootroot00000000000000jline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/000077500000000000000000000000001311544710100231065ustar00rootroot00000000000000jline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/000077500000000000000000000000001311544710100247375ustar00rootroot00000000000000jline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/000077500000000000000000000000001311544710100262325ustar00rootroot00000000000000jline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/Connection.java000066400000000000000000000207201311544710100311750ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Java TelnetD library (embeddable telnet daemon) * Copyright (c) 2000-2005 Dieter Wimberger * All rights reserved. *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. *

* Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ***/ package org.jline.builtins.telnet; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; /** * Class that implements a connection with this telnet daemon.
* It is derived from java.lang.Thread, which reflects the architecture * constraint of one thread per connection. This might seem a waste of * resources, but as a matter of fact sharing threads would require a * far more complex imlementation, due to the fact that telnet is not a * stateless protocol (i.e. alive throughout a session of multiple requests * and responses).
* Each Connection instance is created by the listeners ConnectionManager * instance, making it part of a threadgroup and passing in an associated * ConnectionData instance, that holds vital information about the connection. * Be sure to take a look at their documention.
*

* Once the thread has started and is running, it will get a login * shell instance from the ShellManager and run passing its own reference. * * @author Dieter Wimberger * @version 2.0 (16/07/2006) * @see ConnectionManager * @see ConnectionData */ public abstract class Connection extends Thread { private static final Logger LOG = Logger.getLogger(Connection.class.getName()); private static int number; //unique number for a thread in the thread group private boolean dead; private List listeners; //Associations private ConnectionData connectionData; //associated information /** * Constructs a TelnetConnection by invoking its parent constructor * and setting of various members.
* Subsequently instantiates the whole i/o subsystem, negotiating * telnet protocol level options etc.
* * @param tcg ThreadGroup that this instance is running in. * @param cd ConnectionData instance containing all vital information * of this connection. * @see ConnectionData */ public Connection(ThreadGroup tcg, ConnectionData cd) { super(tcg, ("Connection" + (++number))); connectionData = cd; //init the connection listeners for events //(there should actually be only one or two) listeners = new CopyOnWriteArrayList(); dead = false; }//constructor /** * Method overloaded to implement following behaviour: *

    *
  1. On first entry, retrieve an instance of the configured * login shell from the ShellManager and run it. *
  2. Handle a shell switch or close down disgracefully when * problems (i.e. unhandled unchecked exceptions) occur in the * running shell. *
*/ public void run() { try { doRun(); } catch (Exception ex) { LOG.log(Level.SEVERE, "run()", ex); //Handle properly } finally { //call close if not dead already if (!dead) { close(); } } LOG.log(Level.FINE, "run():: Returning from " + this.toString()); }//run protected abstract void doRun() throws Exception; protected abstract void doClose() throws Exception; /** * Method to access the associated connection data. * * @return ConnectionData associated with the Connection instance. * @see ConnectionData */ public ConnectionData getConnectionData() { return connectionData; }//getConnectionData /** * Closes the connection and its underlying i/o and network * resources.
*/ public synchronized void close() { if (dead) { return; } else { try { //connection dead dead = true; //close i/o doClose(); } catch (Exception ex) { LOG.log(Level.SEVERE, "close()", ex); //handle } try { //close socket connectionData.getSocket().close(); } catch (Exception ex) { LOG.log(Level.SEVERE, "close()", ex); //handle } try { //register closed connection in ConnectionManager connectionData.getManager().registerClosedConnection(this); } catch (Exception ex) { LOG.log(Level.SEVERE, "close()", ex); //handle } try { //try to interrupt it interrupt(); } catch (Exception ex) { LOG.log(Level.SEVERE, "close()", ex); //handle } LOG.log(Level.FINE, "Closed " + this.toString() + " and inactive."); } }//close /** * Returns if a connection has been closed.
* * @return the state of the connection. */ public boolean isActive() { return !dead; }//isClosed /****** Event handling ****************/ /** * Method that registers a ConnectionListener with the * Connection instance. * * @param cl ConnectionListener to be registered. * @see ConnectionListener */ public void addConnectionListener(ConnectionListener cl) { listeners.add(cl); }//addConnectionListener /** * Method that removes a ConnectionListener from the * Connection instance. * * @param cl ConnectionListener to be removed. * @see ConnectionListener */ public void removeConnectionListener(ConnectionListener cl) { listeners.remove(cl); }//removeConnectionListener /** * Method called by the io subsystem to pass on a * "low-level" event. It will be properly delegated to * all registered listeners. * * @param ce ConnectionEvent to be processed. * @see ConnectionEvent */ public void processConnectionEvent(ConnectionEvent ce) { for (ConnectionListener cl : listeners) { switch (ce.getType()) { case CONNECTION_IDLE: cl.connectionIdle(ce); break; case CONNECTION_TIMEDOUT: cl.connectionTimedOut(ce); break; case CONNECTION_LOGOUTREQUEST: cl.connectionLogoutRequest(ce); break; case CONNECTION_BREAK: cl.connectionSentBreak(ce); break; case CONNECTION_TERMINAL_GEOMETRY_CHANGED: cl.connectionTerminalGeometryChanged(ce); } } }//processConnectionEvent }//class Connection jline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/ConnectionData.java000066400000000000000000000347171311544710100320020ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Java TelnetD library (embeddable telnet daemon) * Copyright (c) 2000-2005 Dieter Wimberger * All rights reserved. *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. *

* Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ***/ package org.jline.builtins.telnet; import java.net.InetAddress; import java.net.Socket; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * An utility class that is used to store and allow retrieval * of all data associated with a connection. * * @author Dieter Wimberger * @version 2.0 (16/07/2006) * @see Connection */ public class ConnectionData { //Associations private ConnectionManager connectionManager; //the connection's ConnectionManager private Socket socket; //the connection's socket private InetAddress address; //the connection's IP Address Object private Map environment; //the environment //Members private String hostName; //cache for the hostname private String hostAddress; //cache for the host ip private int port; //port of the connection private Locale locale; //locale of the connection private long lastActivity; //timestamp for the last activity private boolean warned; //warned flag private String negotiatedTerminalType; //negotiated TerminalType as String private int[] terminalGeometry; //negotiated terminal geometry private boolean terminalGeometryChanged = true; //flag for changes in the terminal geometry private String loginShell; //the login shell private boolean lineMode = false; /** * Constructs a ConnectionData instance storing vital * information about a connection. * * @param sock Socket of the inbound connection. */ public ConnectionData(Socket sock, ConnectionManager cm) { socket = sock; connectionManager = cm; address = sock.getInetAddress(); setHostName(); setHostAddress(); setLocale(); port = sock.getPort(); //this will set a default geometry and terminal type for the terminal terminalGeometry = new int[2]; terminalGeometry[0] = 80; //width terminalGeometry[1] = 25; //height negotiatedTerminalType = "default"; environment = new HashMap(20); //this will stamp the first activity for validity :) activity(); }//ConnectionData /** * Returns a reference to the ConnectionManager the * connection is associated with. * * @return Reference to the associated ConnectionManager. * @see ConnectionManager */ public ConnectionManager getManager() { return connectionManager; }//getManager /** * Returns a reference to the socket the Connection * is associated with. * * @return Reference to the associated Socket. * @see java.net.Socket */ public Socket getSocket() { return socket; }//getSocket /** * Returns the remote port to which the socket is connected. * * @return String that contains the remote port number to which the socket is connected. */ public int getPort() { return port; }//getPort /** * Returns the fully qualified host name for the connection's IP address.
* The name is cached on creation for performance reasons. Subsequent calls * will not result in resolve queries. * * @return String that contains the fully qualified host name for this address. */ public String getHostName() { return hostName; }//getHostName /** * Returns the IP address of the connection. * * @return String that contains the connection's IP address.
* The format "%d.%d.%d.%d" is well known, where %d goes from zero to 255. */ public String getHostAddress() { return hostAddress; }//getHostAddress /** * Returns the InetAddress object associated with the connection. * * @return InetAddress associated with the connection. */ public InetAddress getInetAddress() { return address; }//getInetAddress /** * Returns the Locale object associated with the connection * by carrying out a simple domain match.
* This can either be effective, if your users are really * home in the country they are connecting from, * or ineffective if they are on the move getting connected * from anywhere in the world.
*
* Yet this gives the chance of capturing a default locale * and starting from some point. On application context * this can be by far better handled, so be aware that * it makes sense to spend some thoughts on that thing when you * build your application. * * @return the Locale object "guessed" for the connection based * on its host name. */ public Locale getLocale() { return locale; }//getLocale /** * Returns a timestamp of the last activity that happened on * the associated connection. * * @return the timestamp as a long representing the difference, * measured in milliseconds, between the current time and * midnight, January 1, 1970 UTC. */ public long getLastActivity() { return lastActivity; }//getLastActivity /** * Sets a new timestamp to the actual time in millis * retrieved from the System. This will remove an idle warning * flag if it has been set. Note that you can use this behaviour * to implement your own complex idle timespan policies within * the context of your application.
* The check frequency of the ConnectionManager should just be set * according to the lowest time to warning and time to disconnect * requirements. */ public void activity() { warned = false; lastActivity = System.currentTimeMillis(); }//setLastActivity /** * Returns the state of the idle warning flag, which * will be true if a warning has been issued, and false * if not. * * @return the state of the idle warning flag. */ public boolean isWarned() { return warned; }//isWarned /** * Sets the state of the idle warning flag.
* Note that this method will also update the * the timestamp if the idle warning flag is removed, * which means its kind of a second way to achieve the * same thing as with the activity method. * * @param bool true if a warning is to be issued, * false if to be removed. * @see #activity() */ public void setWarned(boolean bool) { warned = bool; if (!bool) { lastActivity = System.currentTimeMillis(); } }//setWarned /** * Sets the terminal geometry data.
* This method should not be called explicitly * by the application (i.e. the its here for the io subsystem).
* A call will set the terminal geometry changed flag. * * @param width of the terminal in columns. * @param height of the terminal in rows. */ public void setTerminalGeometry(int width, int height) { terminalGeometry[0] = width; terminalGeometry[1] = height; terminalGeometryChanged = true; }//setTerminalGeometry /** * Returns the terminal geometry in an array of two integers. *

    *
  • index 0: Width in columns. *
  • index 1: Height in rows. *
* A call will reset the terminal geometry changed flag. * * @return integer array containing width and height. */ public int[] getTerminalGeometry() { //we toggle the flag because the change should now be known if (terminalGeometryChanged) terminalGeometryChanged = false; return terminalGeometry; }//getTerminalGeometry /** * Returns the width of the terminal in columns for convenience. * * @return the number of columns. */ public int getTerminalColumns() { return terminalGeometry[0]; }//getTerminalColumns /** * Returns the height of the terminal in rows for convenience. * * @return the number of rows. */ public int getTerminalRows() { return terminalGeometry[1]; }//getTerminalRows /** * Returns the state of the terminal geometry changed flag, * which will be true if it has been set, and false * if not. * * @return the state of the terminal geometry changed flag. */ public boolean isTerminalGeometryChanged() { return terminalGeometryChanged; }//isTerminalGeometryChanged /** * Returns the terminal type that has been negotiated * between the telnet client and the telnet server, in * of a String.
* * @return the negotiated terminal type as String. */ public String getNegotiatedTerminalType() { return negotiatedTerminalType; }//getNegotiatedTerminalType /** * Sets the terminal type that has been negotiated * between telnet client and telnet server, in form of * a String.
*

* This method should not be called explicitly * by the application (i.e. the its here for the io subsystem).
* * @param termtype the negotiated terminal type as String. */ public void setNegotiatedTerminalType(String termtype) { negotiatedTerminalType = termtype; }//setNegotiatedTerminalType /** * Returns the hashmap for storing and * retrieving environment variables to be passed * between shells. * * @return a HashMap instance. */ public Map getEnvironment() { return environment; }//getEnvironment /** * Returns the login shell name. * * @return the shell name as string. */ public String getLoginShell() { return loginShell; }//getLoginShell /** * Sets the login shell name. * * @param s the shell name as string. */ public void setLoginShell(String s) { loginShell = s; }//setLoginShell /** * Tests if in line mode. * * @return true if in line mode, false otherwise */ public boolean isLineMode() { return lineMode; }//isLineMode /** * Sets the line mode flag for the connection. * Note that the setting will only be used at * startup at the moment. * * @param b true if to be initialized in linemode, * false otherwise. */ public void setLineMode(boolean b) { lineMode = b; }//setLineMode /** * Mutator for HostName cache */ private void setHostName() { hostName = address.getHostName(); }//setHostName /** * Mutator for HostAddress cache */ private void setHostAddress() { hostAddress = address.getHostAddress(); }//setHostAddress /** * Mutator for Locale * Sets a Locale derived from the hostname, * or the default which is Locale.ENGLISH if something * goes wrong. * The localhost represents a problem for example :) */ private void setLocale() { String country = getHostName(); try { country = country.substring(country.lastIndexOf(".") + 1); if (country.equals("at")) { locale = new Locale("de", "AT"); } else if (country.equals("de")) { locale = new Locale("de", "DE"); } else if (country.equals("mx")) { locale = new Locale("es", "MX"); } else if (country.equals("es")) { locale = new Locale("es", "ES"); } else if (country.equals("it")) { locale = Locale.ITALY; } else if (country.equals("fr")) { locale = Locale.FRANCE; } else if (country.equals("uk")) { locale = new Locale("en", "GB"); } else if (country.equals("arpa")) { locale = Locale.US; } else if (country.equals("com")) { locale = Locale.US; } else if (country.equals("edu")) { locale = Locale.US; } else if (country.equals("gov")) { locale = Locale.US; } else if (country.equals("org")) { locale = Locale.US; } else if (country.equals("mil")) { locale = Locale.US; } else { //default to english locale = Locale.ENGLISH; } } catch (Exception ex) { //default to english locale = Locale.ENGLISH; } }//setLocale }//class ConnectionData jline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/ConnectionEvent.java000066400000000000000000000103461311544710100322020ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Java TelnetD library (embeddable telnet daemon) * Copyright (c) 2000-2005 Dieter Wimberger * All rights reserved. *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. *

* Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ***/ package org.jline.builtins.telnet; /** * Class implementing a ConnectionEvent.
* These events are used to communicate things that are * supposed to be handled within the application context. * These events are processed by the Connection instance * calling upon its registered listeners. * * @author Dieter Wimberger * @version 2.0 (16/07/2006) * @see Connection * @see ConnectionListener */ public class ConnectionEvent { private final Connection source; private final Type type; /** * Constructs a new instance of a ConnectionEvent * with a given source (Connection) and a given type. * * @param source Connection that represents the source of this event. * @param type int that contains one of the defined event types. */ public ConnectionEvent(Connection source, Type type) { this.type = type; this.source = source; }//constructor /** * Accessor method returning the source of the * ConnectionEvent instance. * * @return Connection representing the source. */ public Connection getSource() { return source; }//getSource /** * Method that helps identifying the type. * * @return Event type. */ public Type getType() { return type; }//getType public enum Type { /** * Defines the connection idle event type.
* It occurs if a connection has been idle exceeding * the configured time to warning. */ CONNECTION_IDLE, /** * Defines the connection timed out event type.
* It occurs if a connection has been idle exceeding * the configured time to warning and the configured time * to timedout. */ CONNECTION_TIMEDOUT, /** * Defines the connection requested logout event type.
* It occurs if a connection requested disgraceful logout by * sending a - key combination. */ CONNECTION_LOGOUTREQUEST, /** * Defines the connection sent break event type.
* It occurs when the connection sent a NVT BREAK. */ CONNECTION_BREAK, /** * Defines the connection geometry event type. * It occurs when the connection sent a NAWS. */ CONNECTION_TERMINAL_GEOMETRY_CHANGED; } }//class ConnectionEventjline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/ConnectionFilter.java000066400000000000000000000052151311544710100323450ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Java TelnetD library (embeddable telnet daemon) * Copyright (c) 2000-2005 Dieter Wimberger * All rights reserved. *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. *

* Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ***/ package org.jline.builtins.telnet; import java.net.InetAddress; /** * Interface defining a generic IP level connection * filter.
* Due to the fact that this task depends heavily on * application context, I chose a very generic way * of applying IP level connection filtering. *

* Implementations should consider following issues: *

    *
  • performance *
  • administration (maybe via an admin shell) *
  • logging denials *
* * @author Dieter Wimberger * @version 2.0 (16/07/2006) */ public interface ConnectionFilter { /** * Tests if a given ip address is allowed to connect. * * @param ip the address to be tested. * @return true if allowed to connect, false otherwise. */ boolean isAllowed(InetAddress ip); }//interface ConnectionFilterjline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/ConnectionListener.java000066400000000000000000000066531311544710100327140ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Java TelnetD library (embeddable telnet daemon) * Copyright (c) 2000-2005 Dieter Wimberger * All rights reserved. *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. *

* Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ***/ package org.jline.builtins.telnet; /** * Interface to be implemented if a class wants to * qualify as a ConnectionListener.
* Note that a Shell is per contract also forced to * implement this interface. * * @author Dieter Wimberger * @version 2.0 (16/07/2006) * @see ConnectionEvent */ public interface ConnectionListener { /** * Called when a CONNECTION_IDLE event occured. * * @param ce ConnectionEvent instance. * @see ConnectionEvent.Type#CONNECTION_IDLE */ default void connectionIdle(ConnectionEvent ce) { } /** * Called when a CONNECTION_TIMEDOUT event occured. * * @param ce ConnectionEvent instance. * @see ConnectionEvent.Type#CONNECTION_TIMEDOUT */ default void connectionTimedOut(ConnectionEvent ce) { } /** * Called when a CONNECTION_LOGOUTREQUEST occured. * * @param ce ConnectionEvent instance. * @see ConnectionEvent.Type#CONNECTION_LOGOUTREQUEST */ default void connectionLogoutRequest(ConnectionEvent ce) { } /** * Called when a CONNECTION_BREAK event occured. * * @param ce ConnectionEvent instance. * @see ConnectionEvent.Type#CONNECTION_BREAK */ default void connectionSentBreak(ConnectionEvent ce) { } /** * Called when a CONNECTION_TERMINAL_GEOMETRY_CHANGED event occured. * * @param ce ConnectionEvent instance. * @see ConnectionEvent.Type#CONNECTION_TERMINAL_GEOMETRY_CHANGED */ default void connectionTerminalGeometryChanged(ConnectionEvent ce) { } }//interface ConnectionListenerjline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/ConnectionManager.java000066400000000000000000000330361311544710100324740ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Java TelnetD library (embeddable telnet daemon) * Copyright (c) 2000-2005 Dieter Wimberger * All rights reserved. *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. *

* Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ***/ package org.jline.builtins.telnet; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Stack; import java.util.logging.Level; import java.util.logging.Logger; /** * Class that takes care for active and queued connection. * Housekeeping is done also for connections that were just broken * off, or exceeded their timeout. * * @author Dieter Wimberger * @version 2.0 (16/07/2006) */ public abstract class ConnectionManager implements Runnable { private static Logger LOG = Logger.getLogger(ConnectionManager.class.getName()); private final List openConnections; private Thread thread; private ThreadGroup threadGroup; //ThreadGroup all connections run in private Stack closedConnections; private ConnectionFilter connectionFilter; //reference to the connection filter private int maxConnections; //maximum allowed connections stored from the properties private int warningTimeout; //time to idle warning private int disconnectTimeout; //time to idle diconnection private int housekeepingInterval; //interval for managing cleanups private String loginShell; private boolean lineMode = false; private boolean stopping = false; public ConnectionManager() { threadGroup = new ThreadGroup(toString() + "Connections"); closedConnections = new Stack(); openConnections = Collections.synchronizedList(new ArrayList(100)); } public ConnectionManager(int con, int timew, int timedis, int hoke, ConnectionFilter filter, String lsh, boolean lm) { this(); connectionFilter = filter; loginShell = lsh; lineMode = lm; maxConnections = con; warningTimeout = timew; disconnectTimeout = timedis; housekeepingInterval = hoke; }//constructor /** * Gets the active ConnectionFilter instance or * returns null if no filter is set. * * @return the managers ConnectionFilter. */ public ConnectionFilter getConnectionFilter() { return connectionFilter; }//getConnectionFilter /** * Set a connection filter for this * ConnectionManager instance. The filter is used to handle * IP level allow/deny of incoming connections. * * @param filter ConnectionFilter instance. */ public void setConnectionFilter(ConnectionFilter filter) { connectionFilter = filter; }//setConnectionFilter /** * Returns the number of open connections. * @return the number of open connections as int. */ public int openConnectionCount() { return openConnections.size(); }//openConnectionCount /** * Returns the {@link Connection} at the given index. * @param idx * @return */ public Connection getConnection(int idx) { synchronized (openConnections) { return openConnections.get(idx); } }//getConnection /** * Get all {@link Connection} instances with the given * InetAddress. * * @return all {@link Connection} instances with the given * InetAddress. */ public Connection[] getConnectionsByAdddress(InetAddress addr) { ArrayList l = new ArrayList(); synchronized (openConnections) { for (Connection connection : openConnections) { if (connection.getConnectionData().getInetAddress().equals(addr)) { l.add(connection); } } } Connection[] conns = new Connection[l.size()]; return l.toArray(conns); }//getConnectionsByAddress /** * Starts this ConnectionManager. */ public void start() { thread = new Thread(this); thread.start(); }//start /** * Stops this ConnectionManager. */ public void stop() { LOG.log(Level.FINE, "stop()::" + this.toString()); stopping = true; //wait for thread to die try { if (thread != null) { thread.join(); } } catch (InterruptedException iex) { LOG.log(Level.SEVERE, "stop()", iex); } synchronized (openConnections) { for (Connection tc : openConnections) { try { //maybe write a disgrace to the socket? tc.close(); } catch (Exception exc) { LOG.log(Level.SEVERE, "stop()", exc); } } openConnections.clear(); } LOG.log(Level.FINE, "stop():: Stopped " + this.toString()); }//stop /** * Method that that tries to connect an incoming request. * Properly queueing. * * @param insock Socket thats representing the incoming connection. */ public void makeConnection(Socket insock) { LOG.log(Level.FINE, "makeConnection()::" + insock.toString()); if (connectionFilter == null || connectionFilter.isAllowed(insock.getInetAddress())) { //we create the connection data object at this point to //store certain information there. ConnectionData newCD = new ConnectionData(insock, this); newCD.setLoginShell(loginShell); newCD.setLineMode(lineMode); if (openConnections.size() < maxConnections) { //create a new Connection instance Connection con = createConnection(threadGroup, newCD); //log the newly created connection Object[] args = {openConnections.size() + 1}; LOG.info(MessageFormat.format("connection #{0,number,integer} made.", args)); //register it for being managed synchronized (openConnections) { openConnections.add(con); } //start it con.start(); } } else { LOG.info("makeConnection():: Active Filter blocked incoming connection."); try { insock.close(); } catch (IOException ex) { //do nothing or log. } } }//makeConnection protected abstract Connection createConnection(ThreadGroup threadGroup, ConnectionData newCD); /** * Periodically does following work: *

    *
  • cleaning up died connections. *
  • checking managed connections if they are working properly. *
  • checking the open connections. *
*/ public void run() { //housekeep connections try { do { //clean up and close all broken connections //cleanupBroken(); //clean up closed connections cleanupClosed(); //check all active connections checkOpenConnections(); //sleep interval Thread.sleep(housekeepingInterval); } while (!stopping); } catch (Exception e) { LOG.log(Level.SEVERE, "run()", e); } LOG.log(Level.FINE, "run():: Ran out " + this.toString()); }//run /* private void cleanupBroken() { //cleanup loop while (!m_BrokenConnections.isEmpty()) { Connection nextOne = (Connection) m_BrokenConnections.pop(); log.info("cleanupBroken():: Closing broken connection " + nextOne.toString()); //fire logoff event for shell site cleanup , beware could hog the daemon thread nextOne.processConnectionEvent(new ConnectionEvent(nextOne, ConnectionEvent.CONNECTION_BROKEN)); //close the connection, will be automatically registered as closed nextOne.close(); } }//cleanupBroken */ private void cleanupClosed() { if (stopping) { return; } //cleanup loop while (!closedConnections.isEmpty()) { Connection nextOne = closedConnections.pop(); LOG.info("cleanupClosed():: Removing closed connection " + nextOne.toString()); synchronized (openConnections) { openConnections.remove(nextOne); } } }//cleanupBroken private void checkOpenConnections() { if (stopping) { return; } //do routine checks on active connections synchronized (openConnections) { for (Connection conn : openConnections) { ConnectionData cd = conn.getConnectionData(); //check if it is dead and remove it. if (!conn.isActive()) { registerClosedConnection(conn); continue; } /* Timeouts check */ //first we caculate the inactivity time long inactivity = System.currentTimeMillis() - cd.getLastActivity(); //now we check for warning and disconnection if (inactivity > warningTimeout) { //..and for disconnect if (inactivity > (disconnectTimeout + warningTimeout)) { //this connection needs to be disconnected :) LOG.log(Level.FINE, "checkOpenConnections():" + conn.toString() + " exceeded total timeout."); //fire logoff event for shell site cleanup , beware could hog the daemon thread conn.processConnectionEvent(new ConnectionEvent(conn, ConnectionEvent.Type.CONNECTION_TIMEDOUT)); //conn.close(); } else { //this connection needs to be warned :) if (!cd.isWarned()) { LOG.log(Level.FINE, "checkOpenConnections():" + conn.toString() + " exceeded warning timeout."); cd.setWarned(true); //warning event is fired but beware this could hog the daemon thread!! conn.processConnectionEvent(new ConnectionEvent(conn, ConnectionEvent.Type.CONNECTION_IDLE)); } } } } /* end Timeouts check */ } }//checkConnections public void registerClosedConnection(Connection con) { if (stopping) { return; } if (!closedConnections.contains(con)) { LOG.log(Level.FINE, "registerClosedConnection()::" + con.toString()); closedConnections.push(con); } }//unregister public int getDisconnectTimeout() { return disconnectTimeout; } public void setDisconnectTimeout(int disconnectTimeout) { this.disconnectTimeout = disconnectTimeout; } public int getHousekeepingInterval() { return housekeepingInterval; } public void setHousekeepingInterval(int housekeepingInterval) { this.housekeepingInterval = housekeepingInterval; } public boolean isLineMode() { return lineMode; } public void setLineMode(boolean lineMode) { this.lineMode = lineMode; } public String getLoginShell() { return loginShell; } public void setLoginShell(String loginShell) { this.loginShell = loginShell; } public int getMaxConnections() { return maxConnections; } public void setMaxConnections(int maxConnections) { this.maxConnections = maxConnections; } public int getWarningTimeout() { return warningTimeout; } public void setWarningTimeout(int warningTimeout) { this.warningTimeout = warningTimeout; } }//class ConnectionManager jline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/PortListener.java000066400000000000000000000161741311544710100315400ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Java TelnetD library (embeddable telnet daemon) * Copyright (c) 2000-2005 Dieter Wimberger * All rights reserved. *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. *

* Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ***/ package org.jline.builtins.telnet; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.text.MessageFormat; import java.util.logging.Level; import java.util.logging.Logger; /** * Class that implements a PortListener.
* If available, it accepts incoming connections and passes them * to an associated ConnectionManager. * * @author Dieter Wimberger * @version 2.0 (16/07/2006) * @see ConnectionManager */ public class PortListener implements Runnable { private static final Logger LOG = Logger.getLogger(PortListener.class.getName()); private static final String logmsg = "Listening to Port {0,number,integer} with a connectivity queue size of {1,number,integer}."; private String name; private int port; //port number running on private int floodProtection; //flooding protection private ServerSocket serverSocket = null; //server socket private Thread thread; private ConnectionManager connectionManager; //connection management thread private boolean stopping = false; private boolean available; //Flag for availability /** * Constructs a PortListener instance.
* * @param port int that specifies the port number of the server socket. * @param floodprot that specifies the server socket queue size. */ public PortListener(String name, int port, int floodprot) { this.name = name; available = false; this.port = port; floodProtection = floodprot; }//constructor /** * Returns the name of this PortListener. * * @return the name as String. */ public String getName() { return name; }//getName /** * Tests if this PortListener is available. * * @return true if available, false otherwise. */ public boolean isAvailable() { return available; }//isAvailable /** * Sets the availability flag of this PortListener. * * @param b true if to be available, false otherwise. */ public void setAvailable(boolean b) { available = b; }//setAvailable /** * Starts this PortListener. */ public void start() { LOG.log(Level.FINE, "start()"); thread = new Thread(this); thread.start(); available = true; }//start /** * Stops this PortListener, and returns * when everything was stopped successfully. */ public void stop() { LOG.log(Level.FINE, "stop()::" + this.toString()); //flag stop stopping = true; available = false; //take down all connections connectionManager.stop(); //close server socket try { serverSocket.close(); } catch (IOException ex) { LOG.log(Level.SEVERE, "stop()", ex); } //wait for thread to die try { thread.join(); } catch (InterruptedException iex) { LOG.log(Level.SEVERE, "stop()", iex); } LOG.info("stop()::Stopped " + this.toString()); }//stop /** * Listen constantly to a server socket and handles incoming connections * through the associated {a:link ConnectionManager}. * * @see ConnectionManager */ public void run() { try { /* A server socket is opened with a connectivity queue of a size specified in int floodProtection. Concurrent login handling under normal circumstances should be handled properly, but denial of service attacks via massive parallel program logins should be prevented with this. */ serverSocket = new ServerSocket(port, floodProtection); //log entry LOG.info(MessageFormat.format(logmsg, port, floodProtection)); do { try { Socket s = serverSocket.accept(); if (available) { connectionManager.makeConnection(s); } else { //just shut down the socket s.close(); } } catch (SocketException ex) { if (stopping) { //server socket was closed blocked in accept LOG.log(Level.FINE, "run(): ServerSocket closed by stop()"); } else { LOG.log(Level.SEVERE, "run()", ex); } } } while (!stopping); } catch (IOException e) { LOG.log(Level.SEVERE, "run()", e); } LOG.log(Level.FINE, "run(): returning."); }//run /** * Returns reference to ConnectionManager instance associated * with the PortListener. * * @return the associated ConnectionManager. */ public ConnectionManager getConnectionManager() { return connectionManager; }//getConnectionManager public void setConnectionManager(ConnectionManager connectionManager) { this.connectionManager = connectionManager; } }//class PortListener jline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/Telnet.java000066400000000000000000000146661311544710100303450ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.builtins.telnet; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.util.List; import java.util.Map; import org.jline.builtins.Options; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.Terminal.Signal; import org.jline.terminal.TerminalBuilder; /* * a very simple Telnet server. * real remote access should be via ssh. */ public class Telnet { public static final String[] functions = {"telnetd"}; public interface ShellProvider { void shell(Terminal terminal, Map environment); } private static final int defaultPort = 2019; private final Terminal terminal; private final ShellProvider provider; private PortListener portListener; private int port; private String ip; public Telnet(Terminal terminal, ShellProvider provider) { this.terminal = terminal; this.provider = provider; } public void telnetd(String[] argv) throws IOException { final String[] usage = {"telnetd - start simple telnet server", "Usage: telnetd [-i ip] [-p port] start | stop | status", " -i --ip=INTERFACE listen interface (default=127.0.0.1)", " -p --port=PORT listen port (default=" + defaultPort + ")", " -? --help show help"}; Options opt = Options.compile(usage).parse(argv, true); List args = opt.args(); if (opt.isSet("help") || args.isEmpty()) { opt.usage(System.err); return; } String command = args.get(0); if ("start".equals(command)) { if (portListener != null) { throw new IllegalStateException("telnetd is already running on port " + port); } ip = opt.get("ip"); port = opt.getNumber("port"); start(); status(); } else if ("stop".equals(command)) { if (portListener == null) { throw new IllegalStateException("telnetd is not running."); } stop(); } else if ("status".equals(command)) { status(); } else { throw opt.usageError("bad command: " + command); } } private void status() { if (portListener != null) { System.out.println("telnetd is running on " + ip + ":" + port); } else { System.out.println("telnetd is not running."); } } private void start() throws IOException { ConnectionManager connectionManager = new ConnectionManager(1000, 5 * 60 * 1000, 5 * 60 * 1000, 60 * 1000, null, null, false) { @Override protected Connection createConnection(ThreadGroup threadGroup, ConnectionData newCD) { return new Connection(threadGroup, newCD) { TelnetIO telnetIO; @Override protected void doRun() throws Exception { telnetIO = new TelnetIO(); telnetIO.setConnection(this); telnetIO.initIO(); InputStream in = new InputStream() { @Override public int read() throws IOException { return telnetIO.read(); } @Override public int read(byte[] b, int off, int len) throws IOException { int r = read(); if (r >= 0) { b[off] = (byte) r; return 1; } else { return -1; } } }; PrintStream out = new PrintStream(new OutputStream() { @Override public void write(int b) throws IOException { telnetIO.write(b); } @Override public void flush() throws IOException { telnetIO.flush(); } }); Terminal terminal = TerminalBuilder.builder() .type(getConnectionData().getNegotiatedTerminalType().toLowerCase()) .streams(in, out) .system(false) .name("telnet") .build(); terminal.setSize(new Size(getConnectionData().getTerminalColumns(), getConnectionData().getTerminalRows())); terminal.setAttributes(Telnet.this.terminal.getAttributes()); addConnectionListener(new ConnectionListener() { @Override public void connectionTerminalGeometryChanged(ConnectionEvent ce) { terminal.setSize(new Size(getConnectionData().getTerminalColumns(), getConnectionData().getTerminalRows())); terminal.raise(Signal.WINCH); } }); try { provider.shell(terminal, getConnectionData().getEnvironment()); } finally { close(); } } @Override protected void doClose() throws Exception { telnetIO.closeOutput(); telnetIO.closeInput(); } }; } }; portListener = new PortListener("gogo", port, 10); portListener.setConnectionManager(connectionManager); portListener.start(); } private void stop() throws IOException { portListener.stop(); portListener = null; } } jline3-jline-3.3.1/remote-telnet/src/main/java/org/jline/builtins/telnet/TelnetIO.java000066400000000000000000001522031311544710100305630ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /* * Java TelnetD library (embeddable telnet daemon) * Copyright (c) 2000-2005 Dieter Wimberger * All rights reserved. *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. *

* Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ***/ package org.jline.builtins.telnet; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.util.logging.Level; import java.util.logging.Logger; /** * Class that represents the TelnetIO implementation. It contains * an inner IACHandler class to handle the telnet protocol level * communication. *

* Although supposed to work full-duplex, we only process the telnet protocol * layer communication in case of reading requests from the higher levels. * This is the only way to meet the one thread per connection requirement. *

*

* The output is done via byte-oriented streams, definately suitable for the * telnet protocol. The format of the output is UTF-8 (Unicode), which is a * standard and supported by any telnet client, including the ones included * in Microsoft OS's. *

* Notes: *
    *
  • The underlying output is buffered, to ensure that all bytes written * are send, the flush() method has to be called. *
  • This low-level routines ensure nice multithreading behaviour on I/O. * Neither large outputs, nor input sequences excuted by the connection thread * can hog the system. *
* * @author Dieter Wimberger * @version 2.0 (16/07/2006) */ public class TelnetIO { /** * Interpret As Command */ protected static final int IAC = 255; /** * Go Ahead
Newer Telnets do not make use of this option * that allows a specific communication mode. */ protected static final int GA = 249; /** * Negotiation: Will do option */ protected static final int WILL = 251; /** * Negotiation: Wont do option */ protected static final int WONT = 252; /** * Negotiation: Do option */ protected static final int DO = 253; /** * Negotiation: Dont do option */ protected static final int DONT = 254; /** * Marks start of a subnegotiation. */ protected static final int SB = 250; /** * Marks end of subnegotiation. */ protected static final int SE = 240; /** * No operation */ protected static final int NOP = 241; /** * Data mark its the data part of a SYNCH which helps to clean up the buffers between * Telnet Server <-> Telnet Client.
* It should work like this we send a TCP urgent package and <IAC> <DM> the receiver * should get the urgent package (SYNCH) and just discard everything until he receives * our <IAC> <DM>.
* Remark: *
    *
  1. can we send a TCP urgent package? *
  2. can we make use of the thing at all? *
*/ protected static final int DM = 242; /** * Break */ protected static final int BRK = 243; /** * Interrupt Process */ protected static final int IP = 244; /** * Abort Output */ protected static final int AO = 245; /**** Implementation of OutputStream ****************************************************/ /** * Are You There */ protected static final int AYT = 246; /** * Erase Char */ protected static final int EC = 247; /** * Erase Line */ protected static final int EL = 248; /** * Telnet Option: ECHO */ protected static final int ECHO = 1; /** * Telnet Option: SUPress Go Ahead
* This will be negotiated, all new telnet protocol implementations are * recommended to do this. */ protected static final int SUPGA = 3; /** * Telnet Option: Negotiate About Window Size
*
    *
  • Server request is IAC DO NAWS *
  • Client response contains subnegotiation with data (columns, rows). *
*/ protected static final int NAWS = 31; /** * Telnet Option: Terminal TYPE
*
    *
  • Server request contains subnegotiation SEND *
  • Client response contains subnegotiation with data IS,terminal type string *
*/ protected static final int TTYPE = 24; /** * TTYPE subnegotiation: IS */ protected static final int IS = 0; /** * TTYPE subnegotiation: SEND */ protected static final int SEND = 1; /**** End implementation of OutputStream ***********************************************/ /**** Implementation of InputStream ****************************************************/ /** * Telnet Option: Logout
* This allows nice goodbye to time-outed or unwanted clients. */ protected static final int LOGOUT = 18; /** * Telnet Option: Linemode *

* The infamous line mode option. */ protected static final int LINEMODE = 34; protected static final int LM_MODE = 1; protected static final int LM_EDIT = 1; protected static final int LM_TRAPSIG = 2; /**** Implementation of InputStream ****************************************************/ /**** * Following methods implement init/request/answer procedures for telnet * protocol level communication. */ protected static final int LM_MODEACK = 4; protected static final int LM_FORWARDMASK = 2; protected static final int LM_SLC = 3; protected static final int LM_SLC_NOSUPPORT = 0; protected static final int LM_SLC_DEFAULT = 3; /**** End telnet protocol level communication methods *******************************/ protected static final int LM_SLC_VALUE = 2; /** Constants declaration ***********************************************/ //Telnet Protocoll Constants protected static final int LM_SLC_CANTCHANGE = 1; protected static final int LM_SLC_LEVELBITS = 3; protected static final int LM_SLC_ACK = 128; protected static final int LM_SLC_FLUSHIN = 64; protected static final int LM_SLC_FLUSHOUT = 32; protected static final int LM_SLC_SYNCH = 1; protected static final int LM_SLC_BRK = 2; protected static final int LM_SLC_IP = 3; protected static final int LM_SLC_AO = 4; protected static final int LM_SLC_AYT = 5; protected static final int LM_SLC_EOR = 6; /** * The following implement the NVT (network virtual terminal) which offers the concept * of a simple "printer". They are the basical meanings of control possibilities * on a standard telnet implementation. */ protected static final int LM_SLC_ABORT = 7; protected static final int LM_SLC_EOF = 8; protected static final int LM_SLC_SUSP = 9; /** * Telnet Option: Environment */ protected static final int NEWENV = 39; protected static final int NE_INFO = 2; /** * The following are constants for supported options, * which can be negotiated based upon the telnet protocol * specification. */ protected static final int NE_VAR = 0; protected static final int NE_VALUE = 1; /** * The following options are options for which we also support subnegotiation * based upon the telnet protocol specification. */ protected static final int NE_ESC = 2; protected static final int NE_USERVAR = 3; protected static final int NE_VAR_OK = 2; protected static final int NE_VAR_DEFINED = 1; protected static final int NE_VAR_DEFINED_EMPTY = 0; protected static final int NE_VAR_UNDEFINED = -1; protected static final int NE_IN_ERROR = -2; protected static final int NE_IN_END = -3; protected static final int NE_VAR_NAME_MAXLENGTH = 50; protected static final int NE_VAR_VALUE_MAXLENGTH = 1000; /** * Unused */ protected static final int EXT_ASCII = 17; //Defines Extended ASCII protected static final int SEND_LOC = 23; //Defines Send Location protected static final int AUTHENTICATION = 37; //Defines Authentication protected static final int ENCRYPT = 38; //Defines Encryption private static final Logger LOG = Logger.getLogger(TelnetIO.class.getName()); /** * Window Size Constants */ private static final int SMALLEST_BELIEVABLE_WIDTH = 20; private static final int SMALLEST_BELIEVABLE_HEIGHT = 6; private static final int DEFAULT_WIDTH = 80; private static final int DEFAULT_HEIGHT = 25; private Connection connection; //a reference to the connection this instance works for private ConnectionData connectionData; //holds all important information of the connection private DataOutputStream out; //the byte oriented outputstream private DataInputStream in; //the byte oriented input stream //Aggregations private IACHandler iacHandler; //holds a reference to the aggregated IACHandler //Members private InetAddress localAddress; //address of the host the telnetd is running on private boolean noIac = false; //describes if IAC was found and if its just processed private boolean initializing; private boolean crFlag; /** * Creates a TelnetIO object for the given connection.
* Input- and OutputStreams are properly set and the primary telnet * protocol initialization is carried out by the inner IACHandler class.
*/ public TelnetIO() { }//constructor public void initIO() throws IOException { //we make an instance of our inner class iacHandler = new IACHandler(); //we setup underlying byte oriented streams in = new DataInputStream(connectionData.getSocket().getInputStream()); out = new DataOutputStream(new BufferedOutputStream(connectionData.getSocket().getOutputStream())); //we save the local address (necessary?) localAddress = connectionData.getSocket().getLocalAddress(); crFlag = false; //bootstrap telnet communication initTelnetCommunication(); }//initIO public void setConnection(Connection con) { connection = con; connectionData = connection.getConnectionData(); }//setConnection /** * Method to output a byte. Ensures that CR(\r) is never send * alone,but CRLF(\r\n), which is a rule of the telnet protocol. * * @param b Byte to be written. */ public void write(byte b) throws IOException { //ensure CRLF(\r\n) is written for LF(\n) to adhere //to the telnet protocol. if (!crFlag && b == 10) { out.write(13); } out.write(b); if (b == 13) { crFlag = true; } else { crFlag = false; } }//write(byte) /** * Method to output an int. * * @param i Integer to be written. */ public void write(int i) throws IOException { write((byte) i); }//write(int) /** * Method to write an array of bytes. * * @param sequence byte[] to be written. */ public void write(byte[] sequence) throws IOException { for (byte b : sequence) { write(b); } }//write(byte[]) /** * Method to output an array of int' s. * * @param sequence int [] to write */ public void write(int[] sequence) throws IOException { for (int i : sequence) { write((byte) i); } }//write(int[]) /** * Method to write a char. * * @param ch char to be written. */ public void write(char ch) throws IOException { write((byte) ch); }//write(char) /** * Method to output a string. * * @param str String to be written. */ public void write(String str) throws IOException { write(str.getBytes()); }//write(String) /** * Method to flush all buffered output. */ public void flush() throws IOException { out.flush(); }//flush /** * Method to close the underlying output stream to free system resources.
* Most likely only to be called by the ConnectionManager upon clean up of * connections that ended or died. */ public void closeOutput() { try { //sends telnetprotocol logout acknowledgement write(IAC); write(DO); write(LOGOUT); //and now close underlying outputstream out.close(); } catch (IOException ex) { LOG.log(Level.SEVERE, "closeOutput()", ex); //handle? } }//close private void rawWrite(int i) throws IOException { out.write(i); }//rawWrite /** * Method to read a byte from the InputStream. * Invokes the IACHandler upon IAC (Byte=255). * * @return int read from stream. */ public int read() throws IOException { int c = rawread(); //if (c == 255) { noIac = false; while ((c == 255) && (!noIac)) { /** * Read next, and invoke * the IACHandler he is taking care of the rest. Or at least he should :) */ c = rawread(); if (c != 255) { iacHandler.handleC(c); c = rawread(); } else { noIac = true; } } return stripCRSeq(c); }//read /** * Method to close the underlying inputstream to free system resources.
* Most likely only to be called by the ConnectionManager upon clean up of * connections that ended or died. */ public void closeInput() { try { in.close(); } catch (IOException e) { //handle? } }//closeInput /** * This method reads an unsigned 16bit Integer from the stream, * its here for getting the NAWS Data Values for height and width. */ private int read16int() throws IOException { int c = in.readUnsignedShort(); return c; }//read16int /** * The following options are options which might be of interest, but are not * yet implemented or in use. */ /** * Method to read a raw byte from the InputStream.
* Telnet protocol layer communication is filtered and processed here. * * @return int read from stream. */ private int rawread() throws IOException { int b = 0; //try { b = in.readUnsignedByte(); connectionData.activity(); return b; }//rawread /** * Checks for the telnet protocol specified CR followed by NULL or LF
* Subsequently reads for the next byte and forwards * only a ENTER represented by LF internally. */ private int stripCRSeq(int input) throws IOException { if (input == 13) { rawread(); return 10; } return input; }//stripCRSeq /** * Method that initializes the telnet communication layer. */ private void initTelnetCommunication() { initializing = true; try { //start out, some clients just wait if (connectionData.isLineMode()) { iacHandler.doLineModeInit(); LOG.log(Level.FINE, "Line mode initialized."); } else { iacHandler.doCharacterModeInit(); LOG.log(Level.FINE, "Character mode initialized."); } //open for a defined timeout so we read incoming negotiation connectionData.getSocket().setSoTimeout(1000); read(); } catch (Exception e) { //handle properly //log.error("initTelnetCommunication()",e); } finally { //this is important, dont ask me why :) try { connectionData.getSocket().setSoTimeout(0); } catch (Exception ex) { LOG.log(Level.SEVERE, "initTelnetCommunication()", ex); } } initializing = false; }//initTelnetCommunication /** * Method that represents the answer to the * AreYouThere question of the telnet protocol specification *

* Output of the String [HostAdress:Yes] */ private void IamHere() { try { write("[" + localAddress.toString() + ":Yes]"); flush(); } catch (Exception ex) { LOG.log(Level.SEVERE, "IamHere()", ex); } }//IamHere /** * Network virtual terminal break. */ private void nvtBreak() { connection.processConnectionEvent(new ConnectionEvent(connection, ConnectionEvent.Type.CONNECTION_BREAK)); }//nvtBreak /** * Method that checks reported terminal sizes and sets the * asserted values in the ConnectionData instance associated with * the connection. * * @param width Integer that represents the Window width in chars * @param height Integer that represents the Window height in chars */ private void setTerminalGeometry(int width, int height) { if (width < SMALLEST_BELIEVABLE_WIDTH) { width = DEFAULT_WIDTH; } if (height < SMALLEST_BELIEVABLE_HEIGHT) { height = DEFAULT_HEIGHT; } //DEBUG: write("[New Window Size " + window_width + "x" + window_height + "]"); connectionData.setTerminalGeometry(width, height); connection.processConnectionEvent(new ConnectionEvent(connection, ConnectionEvent.Type.CONNECTION_TERMINAL_GEOMETRY_CHANGED)); }//setTerminalGeometry public void setEcho(boolean b) { }//setEcho /** * An inner class for handling incoming option negotiations implementing the telnet protocol * specification based upon following Standards and RFCs: *

    *
  1. 854 Telnet Protocol Specification *
  2. 855 Telnet Option Specifications *
  3. 857 Telnet Echo Option *
  4. 858 Telnet Supress Go Ahead Option *
  5. 727 Telnet Logout Option *
  6. 1073 Telnet Window Size Option *
  7. 1091 Telnet Terminal-Type Option *
*

* Furthermore there are some more, which helped to solve problems, or might be important * for future enhancements:
* 1143 The Q Method of Implementing Option Negotiation
* 1416 Telnet Authentication Option
*

* After an intense study of the available material (mainly cryptical written RFCs, * a telnet client implementation for the macintosh based upon NCSA telnet, and a server side * implementation called key, a mud-like system completely written in Java) I realized * the problems we are facing regarding to the telnet protocol: *

    *
  1. a minimal spread of invented options, which means there are a lot of invented options, * but rarely they made it through to become a standard. *
  2. Dependency on a special type of implementation is dangerous in our case. * We are no kind of host that offers the user to run several processes at once, * a BBS is intended to be a single process the user is interacting with. *
  3. The LAMER has to be expected to log in with the standard Microsoft telnet * implementation. This means forget every nice feature and most of the almost-standards. *

    *

*
* * @author Dieter Wimberger * @version 1.1 16/06/1998 *

*

* To-Do:

    *
  • UNIX conform new style TTYPE negotiation. Setting a list and selecting from it... *
*/ class IACHandler { /** * Telnet readin buffer * Here its implemented guys. Open your eyes upon this solution. * The others take a one byte solution :) */ private int[] buffer = new int[2]; /** * DO_ECHO or not */ private boolean DO_ECHO = false; /** * DO_SUPGA or not */ private boolean DO_SUPGA = false; /** * DO_NAWS or not */ private boolean DO_NAWS = false; /** * DO_TTYPE or not */ private boolean DO_TTYPE = false; /** * DO_LINEMODE or not */ private boolean DO_LINEMODE = false; /** * DO_NEWENV or not */ private boolean DO_NEWENV = false; /** * Are we waiting for a DO reply? */ private boolean WAIT_DO_REPLY_SUPGA = false; private boolean WAIT_DO_REPLY_ECHO = false; private boolean WAIT_DO_REPLY_NAWS = false; private boolean WAIT_DO_REPLY_TTYPE = false; private boolean WAIT_DO_REPLY_LINEMODE = false; private boolean WAIT_LM_MODE_ACK = false; private boolean WAIT_LM_DO_REPLY_FORWARDMASK = false; private boolean WAIT_DO_REPLY_NEWENV = false; private boolean WAIT_NE_SEND_REPLY = false; /** * Are we waiting for a WILL reply? */ private boolean WAIT_WILL_REPLY_SUPGA = false; private boolean WAIT_WILL_REPLY_ECHO = false; private boolean WAIT_WILL_REPLY_NAWS = false; private boolean WAIT_WILL_REPLY_TTYPE = false; public void doCharacterModeInit() throws IOException { sendCommand(WILL, ECHO, true); sendCommand(DONT, ECHO, true); //necessary for some clients sendCommand(DO, NAWS, true); sendCommand(WILL, SUPGA, true); sendCommand(DO, SUPGA, true); sendCommand(DO, TTYPE, true); sendCommand(DO, NEWENV, true); //environment variables }//doCharacterModeInit public void doLineModeInit() throws IOException { sendCommand(DO, NAWS, true); sendCommand(WILL, SUPGA, true); sendCommand(DO, SUPGA, true); sendCommand(DO, TTYPE, true); sendCommand(DO, LINEMODE, true); sendCommand(DO, NEWENV, true); }//doLineModeInit /** * Method to handle a IAC that came in over the line. * * @param i (int)ed byte that followed the IAC */ public void handleC(int i) throws IOException { buffer[0] = i; if (!parseTWO(buffer)) { buffer[1] = rawread(); parse(buffer); } buffer[0] = 0; buffer[1] = 0; }//handleC /** * Method that parses for options with two characters. * * @param buf int [] that represents the first byte that followed the IAC first. * @return true when it was a two byte command (IAC OPTIONBYTE) */ private boolean parseTWO(int[] buf) { switch (buf[0]) { case IAC: //doubled IAC to escape 255 is handled within the //read method. break; case AYT: IamHere(); break; case AO: case IP: case EL: case EC: case NOP: break; case BRK: nvtBreak(); break; default: return false; } return true; }//parseTWO /** * Method that parses further on for options. * * @param buf that represents the first two bytes that followed the IAC. */ private void parse(int[] buf) throws IOException { switch (buf[0]) { /* First switch on the Negotiation Option */ case WILL: if (supported(buf[1]) && isEnabled(buf[1])) { ;// do nothing } else { if (waitDOreply(buf[1]) && supported(buf[1])) { enable(buf[1]); setWait(DO, buf[1], false); } else { if (supported(buf[1])) { sendCommand(DO, buf[1], false); enable(buf[1]); } else { sendCommand(DONT, buf[1], false); } } } break; case WONT: if (waitDOreply(buf[1]) && supported(buf[1])) { setWait(DO, buf[1], false); } else { if (supported(buf[1]) && isEnabled(buf[1])) { // eanable() Method disables an Option that is already enabled enable(buf[1]); } } break; case DO: if (supported(buf[1]) && isEnabled(buf[1])) { ; // do nothing } else { if (waitWILLreply(buf[1]) && supported(buf[1])) { enable(buf[1]); setWait(WILL, buf[1], false); } else { if (supported(buf[1])) { sendCommand(WILL, buf[1], false); enable(buf[1]); } else { sendCommand(WONT, buf[1], false); } } } break; case DONT: if (waitWILLreply(buf[1]) && supported(buf[1])) { setWait(WILL, buf[1], false); } else { if (supported(buf[1]) && isEnabled(buf[1])) { // enable() Method disables an Option that is already enabled enable(buf[1]); } } break; /* Now about other two byte IACs */ case DM: //How do I implement a SYNCH signal? break; case SB: //handle subnegotiations if ((supported(buf[1])) && (isEnabled(buf[1]))) { switch (buf[1]) { case NAWS: handleNAWS(); break; case TTYPE: handleTTYPE(); break; case LINEMODE: handleLINEMODE(); break; case NEWENV: handleNEWENV(); break; default: ; } } else { //do nothing } break; default: ; }//switch }//parse /** * Method that reads a NawsSubnegotiation that ends up with a IAC SE * If the measurements are unbelieveable it switches to the defaults. */ private void handleNAWS() throws IOException { int width = read16int(); if (width == 255) { width = read16int(); //handle doubled 255 value; } int height = read16int(); if (height == 255) { height = read16int(); //handle doubled 255 value; } skipToSE(); setTerminalGeometry(width, height); }//handleNAWS /** * Method that reads a TTYPE Subnegotiation String that ends up with a IAC SE * If no Terminal is valid, we switch to the dumb "none" terminal. */ private void handleTTYPE() throws IOException { String tmpstr = ""; // The next read should be 0 which is IS by the protocol // specs. hmmm? rawread(); //that should be the is :) tmpstr = readIACSETerminatedString(40); LOG.log(Level.FINE, "Reported terminal name " + tmpstr); connectionData.setNegotiatedTerminalType(tmpstr); }//handleTTYPE /** * Method that handles LINEMODE subnegotiation. */ public void handleLINEMODE() throws IOException { int c = rawread(); switch (c) { case LM_MODE: handleLMMode(); break; case LM_SLC: handleLMSLC(); break; case WONT: case WILL: handleLMForwardMask(c); break; default: //skip to (including) SE skipToSE(); } }//handleLINEMODE public void handleLMMode() throws IOException { //we sent the default which no client might deny //so we only wait the ACK if (WAIT_LM_MODE_ACK) { int mask = rawread(); if (mask != (LM_EDIT | LM_TRAPSIG | LM_MODEACK)) { LOG.log(Level.FINE, "Client violates linemodeack sent: " + mask); } WAIT_LM_MODE_ACK = false; } skipToSE(); }//handleLMMode public void handleLMSLC() throws IOException { int[] triple = new int[3]; if (!readTriple(triple)) return; //SLC will be initiated by the client //case 1. client requests set //LINEMODE SLC 0 SLC_DEFAULT 0 if ((triple[0] == 0) && (triple[1] == LM_SLC_DEFAULT) && (triple[2] == 0)) { skipToSE(); //reply with SLC xxx SLC_DEFAULT 0 rawWrite(IAC); rawWrite(SB); rawWrite(LINEMODE); rawWrite(LM_SLC); //triples defaults for all for (int i = 1; i < 12; i++) { rawWrite(i); rawWrite(LM_SLC_DEFAULT); rawWrite(0); } rawWrite(IAC); rawWrite(SE); flush(); } else { //case 2: just acknowledge anything we get from the client rawWrite(IAC); rawWrite(SB); rawWrite(LINEMODE); rawWrite(LM_SLC); rawWrite(triple[0]); rawWrite(triple[1] | LM_SLC_ACK); rawWrite(triple[2]); while (readTriple(triple)) { rawWrite(triple[0]); rawWrite(triple[1] | LM_SLC_ACK); rawWrite(triple[2]); } rawWrite(IAC); rawWrite(SE); flush(); } }//handleLMSLC public void handleLMForwardMask(int WHAT) throws IOException { switch (WHAT) { case WONT: if (WAIT_LM_DO_REPLY_FORWARDMASK) { WAIT_LM_DO_REPLY_FORWARDMASK = false; } break; } skipToSE(); }//handleLMForward public void handleNEWENV() throws IOException { LOG.log(Level.FINE, "handleNEWENV()"); int c = rawread(); switch (c) { case IS: handleNEIs(); break; case NE_INFO: handleNEInfo(); break; default: //skip to (including) SE skipToSE(); } }//handleNEWENV /* The characters following a "type" up to the next "type" or VALUE specify the variable name. If a "type" is not followed by a VALUE (e.g., by another VAR, USERVAR, or IAC SE) then that variable is undefined. */ private int readNEVariableName(StringBuffer sbuf) throws IOException { LOG.log(Level.FINE, "readNEVariableName()"); int i = -1; do { i = rawread(); if (i == -1) { return NE_IN_ERROR; } else if (i == IAC) { i = rawread(); if (i == IAC) { //duplicated IAC sbuf.append((char) i); } else if (i == SE) { return NE_IN_END; } else { //Error should have been duplicated return NE_IN_ERROR; } } else if (i == NE_ESC) { i = rawread(); if (i == NE_ESC || i == NE_VAR || i == NE_USERVAR || i == NE_VALUE) { sbuf.append((char) i); } else { return NE_IN_ERROR; } } else if (i == NE_VAR || i == NE_USERVAR) { return NE_VAR_UNDEFINED; } else if (i == NE_VALUE) { return NE_VAR_DEFINED; } else { //check maximum length to prevent overflow if (sbuf.length() >= NE_VAR_NAME_MAXLENGTH) { //TODO: Log Overflow return NE_IN_ERROR; } else { sbuf.append((char) i); } } } while (true); }//readNEVariableName /* The characters following a VALUE up to the next "type" specify the value of the variable. If a VALUE is immediately followed by a "type" or IAC, then the variable is defined, but has no value. If an IAC is contained between the IS and the IAC SE, it must be sent as IAC IAC. */ private int readNEVariableValue(StringBuffer sbuf) throws IOException { LOG.log(Level.FINE, "readNEVariableValue()"); //check conditions for first character after VALUE int i = rawread(); if (i == -1) { return NE_IN_ERROR; } else if (i == IAC) { i = rawread(); if (i == IAC) { //Double IAC return NE_VAR_DEFINED_EMPTY; } else if (i == SE) { return NE_IN_END; } else { //according to rule IAC has to be duplicated return NE_IN_ERROR; } } else if (i == NE_VAR || i == NE_USERVAR) { return NE_VAR_DEFINED_EMPTY; } else if (i == NE_ESC) { //escaped value i = rawread(); if (i == NE_ESC || i == NE_VAR || i == NE_USERVAR || i == NE_VALUE) { sbuf.append((char) i); } else { return NE_IN_ERROR; } } else { //character sbuf.append((char) i); } //loop until end of value (IAC SE or TYPE) do { i = rawread(); if (i == -1) { return NE_IN_ERROR; } else if (i == IAC) { i = rawread(); if (i == IAC) { //duplicated IAC sbuf.append((char) i); } else if (i == SE) { return NE_IN_END; } else { //Error should have been duplicated return NE_IN_ERROR; } } else if (i == NE_ESC) { i = rawread(); if (i == NE_ESC || i == NE_VAR || i == NE_USERVAR || i == NE_VALUE) { sbuf.append((char) i); } else { return NE_IN_ERROR; } } else if (i == NE_VAR || i == NE_USERVAR) { return NE_VAR_OK; } else { //check maximum length to prevent overflow if (sbuf.length() > NE_VAR_VALUE_MAXLENGTH) { //TODO: LOG Overflow return NE_IN_ERROR; } else { sbuf.append((char) i); } } } while (true); }//readNEVariableValue public void readNEVariables() throws IOException { LOG.log(Level.FINE, "readNEVariables()"); StringBuffer sbuf = new StringBuffer(50); int i = rawread(); if (i == IAC) { //invalid or empty response skipToSE(); LOG.log(Level.FINE, "readNEVariables()::INVALID VARIABLE"); return; } boolean cont = true; if (i == NE_VAR || i == NE_USERVAR) { do { switch (readNEVariableName(sbuf)) { case NE_IN_ERROR: LOG.log(Level.FINE, "readNEVariables()::NE_IN_ERROR"); return; case NE_IN_END: LOG.log(Level.FINE, "readNEVariables()::NE_IN_END"); return; case NE_VAR_DEFINED: LOG.log(Level.FINE, "readNEVariables()::NE_VAR_DEFINED"); String str = sbuf.toString(); sbuf.delete(0, sbuf.length()); switch (readNEVariableValue(sbuf)) { case NE_IN_ERROR: LOG.log(Level.FINE, "readNEVariables()::NE_IN_ERROR"); return; case NE_IN_END: LOG.log(Level.FINE, "readNEVariables()::NE_IN_END"); return; case NE_VAR_DEFINED_EMPTY: LOG.log(Level.FINE, "readNEVariables()::NE_VAR_DEFINED_EMPTY"); break; case NE_VAR_OK: //add variable LOG.log(Level.FINE, "readNEVariables()::NE_VAR_OK:VAR=" + str + " VAL=" + sbuf.toString()); TelnetIO.this.connectionData.getEnvironment().put(str, sbuf.toString()); sbuf.delete(0, sbuf.length()); break; } break; case NE_VAR_UNDEFINED: LOG.log(Level.FINE, "readNEVariables()::NE_VAR_UNDEFINED"); break; } } while (cont); } }//readVariables public void handleNEIs() throws IOException { LOG.log(Level.FINE, "handleNEIs()"); if (isEnabled(NEWENV)) { readNEVariables(); } }//handleNEIs public void handleNEInfo() throws IOException { LOG.log(Level.FINE, "handleNEInfo()"); if (isEnabled(NEWENV)) { readNEVariables(); } }//handleNEInfo /** * Method that sends a TTYPE Subnegotiation Request. * IAC SB TERMINAL-TYPE SEND */ public void getTTYPE() throws IOException { if (isEnabled(TTYPE)) { rawWrite(IAC); rawWrite(SB); rawWrite(TTYPE); rawWrite(SEND); rawWrite(IAC); rawWrite(SE); flush(); } }//getTTYPE /** * Method that sends a LINEMODE MODE Subnegotiation request. * IAC LINEMODE MODE MASK SE */ public void negotiateLineMode() throws IOException { if (isEnabled(LINEMODE)) { rawWrite(IAC); rawWrite(SB); rawWrite(LINEMODE); rawWrite(LM_MODE); rawWrite(LM_EDIT | LM_TRAPSIG); rawWrite(IAC); rawWrite(SE); WAIT_LM_MODE_ACK = true; //dont forwardmask rawWrite(IAC); rawWrite(SB); rawWrite(LINEMODE); rawWrite(DONT); rawWrite(LM_FORWARDMASK); rawWrite(IAC); rawWrite(SE); WAIT_LM_DO_REPLY_FORWARDMASK = true; flush(); } }//negotiateLineMode /** * Method that sends a NEW-ENVIRON SEND subnegotiation request * for default variables and user variables. * IAC SB NEW-ENVIRON SEND VAR USERVAR IAC SE */ private void negotiateEnvironment() throws IOException { //log.debug("negotiateEnvironment()"); if (isEnabled(NEWENV)) { rawWrite(IAC); rawWrite(SB); rawWrite(NEWENV); rawWrite(SEND); rawWrite(NE_VAR); rawWrite(NE_USERVAR); rawWrite(IAC); rawWrite(SE); WAIT_NE_SEND_REPLY = true; flush(); } }//negotiateEnvironment /** * Method that skips a subnegotiation response. */ private void skipToSE() throws IOException { while (rawread() != SE) ; }//skipSubnegotiation private boolean readTriple(int[] triple) throws IOException { triple[0] = rawread(); triple[1] = rawread(); if ((triple[0] == IAC) && (triple[1] == SE)) { return false; } else { triple[2] = rawread(); return true; } }//readTriple /** * Method that reads a subnegotiation String, * one of those that end with a IAC SE combination. * A maximum length is observed to prevent overflow. * * @return IAC SE terminated String */ private String readIACSETerminatedString(int maxlength) throws IOException { int where = 0; char[] cbuf = new char[maxlength]; char b = ' '; boolean cont = true; do { int i; i = rawread(); switch (i) { case IAC: i = rawread(); if (i == SE) { cont = false; } break; case -1: return (new String("default")); default: } if (cont) { b = (char) i; //Fix for overflow wimpi (10/06/2004) if (b == '\n' || b == '\r' || where == maxlength) { cont = false; } else { cbuf[where++] = b; } } } while (cont); return (new String(cbuf, 0, where)); }//readIACSETerminatedString /** * Method that informs internally about the supported Negotiation Options * * @param i int that represents requested the Option * @return Boolean that represents support status */ private boolean supported(int i) { switch (i) { case SUPGA: case ECHO: case NAWS: case TTYPE: case NEWENV: return true; case LINEMODE: return connectionData.isLineMode(); default: return false; } }//supported /** * Method that sends a Telnet IAC String with TelnetIO.write(byte b) method. * * @param i int that represents requested Command Type (DO,DONT,WILL,WONT) * @param j int that represents the Option itself (e.g. ECHO, NAWS) */ private void sendCommand(int i, int j, boolean westarted) throws IOException { rawWrite(IAC); rawWrite(i); rawWrite(j); // we started with DO OPTION and now wait for reply if ((i == DO) && westarted) setWait(DO, j, true); // we started with WILL OPTION and now wait for reply if ((i == WILL) && westarted) setWait(WILL, j, true); flush(); }//sendCommand /** * Method enables or disables a supported Option * * @param i int that represents the Option */ private void enable(int i) throws IOException { switch (i) { case SUPGA: if (DO_SUPGA) { DO_SUPGA = false; } else { DO_SUPGA = true; } break; case ECHO: if (DO_ECHO) { DO_ECHO = false; } else { DO_ECHO = true; } break; case NAWS: if (DO_NAWS) { DO_NAWS = false; } else { DO_NAWS = true; } break; case TTYPE: if (DO_TTYPE) { DO_TTYPE = false; } else { DO_TTYPE = true; getTTYPE(); } break; case LINEMODE: if (DO_LINEMODE) { DO_LINEMODE = false; //set false in connection data, so the application knows. connectionData.setLineMode(false); } else { DO_LINEMODE = true; negotiateLineMode(); } break; case NEWENV: if (DO_NEWENV) { DO_NEWENV = false; } else { DO_NEWENV = true; negotiateEnvironment(); } break; } }//enable /** * Method that informs internally about the status of the supported * Negotiation Options. * * @param i int that represents requested the Option * @return Boolean that represents the enabled status */ private boolean isEnabled(int i) { switch (i) { case SUPGA: return DO_SUPGA; case ECHO: return DO_ECHO; case NAWS: return DO_NAWS; case TTYPE: return DO_TTYPE; case LINEMODE: return DO_LINEMODE; case NEWENV: return DO_NEWENV; default: return false; } }//isEnabled /** * Method that informs internally about the WILL wait status * of an option. * * @param i that represents requested the Option * @return Boolean that represents WILL wait status of the Option */ private boolean waitWILLreply(int i) { switch (i) { case SUPGA: return WAIT_WILL_REPLY_SUPGA; case ECHO: return WAIT_WILL_REPLY_ECHO; case NAWS: return WAIT_WILL_REPLY_NAWS; case TTYPE: return WAIT_WILL_REPLY_TTYPE; default: return false; } }//waitWILLreply /** * Method that informs internally about the DO wait status * of an option. * * @param i Integer that represents requested the Option * @return Boolean that represents DO wait status of the Option */ private boolean waitDOreply(int i) { switch (i) { case SUPGA: return WAIT_DO_REPLY_SUPGA; case ECHO: return WAIT_DO_REPLY_ECHO; case NAWS: return WAIT_DO_REPLY_NAWS; case TTYPE: return WAIT_DO_REPLY_TTYPE; case LINEMODE: return WAIT_DO_REPLY_LINEMODE; case NEWENV: return WAIT_DO_REPLY_NEWENV; default: return false; } }//waitDOreply /** * Method that mutates the wait status of an option in * negotiation. We need the wait status to keep track of * negotiation in process. So we cant miss if we started out * or the other and so on. * * @param WHAT Integer values of DO or WILL * @param OPTION Integer that represents the Option * @param WAIT Boolean that represents the status of wait that should be set */ private void setWait(int WHAT, int OPTION, boolean WAIT) { switch (WHAT) { case DO: switch (OPTION) { case SUPGA: WAIT_DO_REPLY_SUPGA = WAIT; break; case ECHO: WAIT_DO_REPLY_ECHO = WAIT; break; case NAWS: WAIT_DO_REPLY_NAWS = WAIT; break; case TTYPE: WAIT_DO_REPLY_TTYPE = WAIT; break; case LINEMODE: WAIT_DO_REPLY_LINEMODE = WAIT; break; case NEWENV: WAIT_DO_REPLY_NEWENV = WAIT; break; } break; case WILL: switch (OPTION) { case SUPGA: WAIT_WILL_REPLY_SUPGA = WAIT; break; case ECHO: WAIT_WILL_REPLY_ECHO = WAIT; break; case NAWS: WAIT_WILL_REPLY_NAWS = WAIT; break; case TTYPE: WAIT_WILL_REPLY_TTYPE = WAIT; break; } break; } }//setWait }//inner class IACHandler /** end Constants declaration **************************************************/ }//class TelnetIO jline3-jline-3.3.1/terminal-jansi/000077500000000000000000000000001311544710100167135ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/pom.xml000066400000000000000000000037231311544710100202350ustar00rootroot00000000000000 4.0.0 org.jline jline-parent 3.3.1 jline-terminal-jansi JLine JANSI Terminal org.fusesource.jansi jansi org.jline jline-terminal junit junit test org.apache.felix maven-bundle-plugin *;-noimport:=true org.fusesource.jansi;version="[1.12,2)", org.fusesource.jansi.internal;version="[1.6,2)" org.jline.terminal jline3-jline-3.3.1/terminal-jansi/src/000077500000000000000000000000001311544710100175025ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/000077500000000000000000000000001311544710100204265ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/000077500000000000000000000000001311544710100213475ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/000077500000000000000000000000001311544710100221365ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/000077500000000000000000000000001311544710100232375ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/000077500000000000000000000000001311544710100250525ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/000077500000000000000000000000001311544710100260135ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/000077500000000000000000000000001311544710100271175ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiNativePty.java000066400000000000000000000106541311544710100327000ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jansi; import org.fusesource.jansi.internal.CLibrary; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.spi.Pty; import org.jline.utils.OSUtils; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Constructor; import static org.fusesource.jansi.internal.CLibrary.TCSANOW; import static org.jline.terminal.impl.jansi.JansiSupportImpl.JANSI_MAJOR_VERSION; import static org.jline.terminal.impl.jansi.JansiSupportImpl.JANSI_MINOR_VERSION; import static org.jline.utils.ExecHelper.exec; public abstract class JansiNativePty implements Pty { private final int master; private final int slave; private final String name; private final FileDescriptor masterFD; private final FileDescriptor slaveFD; public JansiNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { this.master = master; this.slave = slave; this.name = name; this.masterFD = masterFD; this.slaveFD = slaveFD; } protected static String ttyname() throws IOException { String name; if (JANSI_MAJOR_VERSION > 1 || JANSI_MAJOR_VERSION == 1 && JANSI_MINOR_VERSION >= 16) { name = CLibrary.ttyname(0); } else { try { name = exec(true, OSUtils.TTY_COMMAND); } catch (IOException e) { throw new IOException("Not a tty", e); } } if (name != null) { name = name.trim(); } if (name == null || name.isEmpty()) { throw new IOException("Not a tty"); } return name; } @Override public void close() throws IOException { if (master > 0) { getMasterInput().close(); } if (slave > 0) { getSlaveInput().close(); } } public int getMaster() { return master; } public int getSlave() { return slave; } public String getName() { return name; } public FileDescriptor getMasterFD() { return masterFD; } public FileDescriptor getSlaveFD() { return slaveFD; } public InputStream getMasterInput() { return new FileInputStream(getMasterFD()); } public OutputStream getMasterOutput() { return new FileOutputStream(getMasterFD()); } public InputStream getSlaveInput() { return new FileInputStream(getSlaveFD()); } public OutputStream getSlaveOutput() { return new FileOutputStream(getSlaveFD()); } @Override public Attributes getAttr() throws IOException { CLibrary.Termios tios = new CLibrary.Termios(); CLibrary.tcgetattr(slave, tios); return toAttributes(tios); } @Override public void setAttr(Attributes attr) throws IOException { CLibrary.Termios tios = toTermios(attr); CLibrary.tcsetattr(slave, TCSANOW, tios); } @Override public Size getSize() throws IOException { CLibrary.WinSize sz = new CLibrary.WinSize(); CLibrary.ioctl(slave, CLibrary.TIOCGWINSZ, sz); return new Size(sz.ws_col, sz.ws_row); } @Override public void setSize(Size size) throws IOException { CLibrary.WinSize sz = new CLibrary.WinSize((short) size.getRows(), (short) size.getColumns()); CLibrary.ioctl(slave, CLibrary.TIOCSWINSZ, sz); } protected abstract CLibrary.Termios toTermios(Attributes t); protected abstract Attributes toAttributes(CLibrary.Termios tios); @Override public String toString() { return "JansiNativePty[" + getName() + "]"; } protected static FileDescriptor newDescriptor(int fd) { try { Constructor cns = FileDescriptor.class.getDeclaredConstructor(int.class); cns.setAccessible(true); return cns.newInstance(fd); } catch (Throwable e) { throw new RuntimeException("Unable to create FileDescriptor", e); } } } jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiSupportImpl.java000066400000000000000000000100441311544710100332440ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jansi; import org.fusesource.jansi.Ansi; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.impl.jansi.freebsd.FreeBsdNativePty; import org.jline.terminal.impl.jansi.linux.LinuxNativePty; import org.jline.terminal.impl.jansi.osx.OsXNativePty; import org.jline.terminal.impl.jansi.win.JansiWinSysTerminal; import org.jline.terminal.spi.JansiSupport; import org.jline.terminal.spi.Pty; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JansiSupportImpl implements JansiSupport { static final int JANSI_MAJOR_VERSION; static final int JANSI_MINOR_VERSION; static { int major = 0, minor = 0; try { String v = Ansi.class.getPackage().getImplementationVersion(); if (v != null) { Matcher m = Pattern.compile("([0-9]+)\\.([0-9]+)([\\.-]\\S+)?").matcher(v); if (m.matches()) { major = Integer.parseInt(m.group(1)); minor = Integer.parseInt(m.group(2)); } } } catch (Throwable t) { // Ignore } JANSI_MAJOR_VERSION = major; JANSI_MINOR_VERSION = minor; } @Override public Pty current() throws IOException { String osName = System.getProperty("os.name"); if (osName.startsWith("Linux")) { if (JANSI_MAJOR_VERSION > 1 || JANSI_MAJOR_VERSION == 1 && JANSI_MINOR_VERSION >= 16) { return LinuxNativePty.current(); } } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { if (JANSI_MAJOR_VERSION > 1 || JANSI_MAJOR_VERSION == 1 && JANSI_MINOR_VERSION >= 12) { return OsXNativePty.current(); } } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { // Solaris is not supported by jansi // return SolarisNativePty.current(); } else if (osName.startsWith("FreeBSD")) { if (JANSI_MAJOR_VERSION > 1 || JANSI_MAJOR_VERSION == 1 && JANSI_MINOR_VERSION >= 16) { return FreeBsdNativePty.current(); } } throw new UnsupportedOperationException(); } @Override public Pty open(Attributes attributes, Size size) throws IOException { if (JANSI_MAJOR_VERSION > 1 || JANSI_MAJOR_VERSION == 1 && JANSI_MINOR_VERSION >= 16) { String osName = System.getProperty("os.name"); if (osName.startsWith("Linux")) { return LinuxNativePty.open(attributes, size); } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { return OsXNativePty.open(attributes, size); } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { // Solaris is not supported by jansi // return SolarisNativePty.current(); } else if (osName.startsWith("FreeBSD")) { return FreeBsdNativePty.open(attributes, size); } } throw new UnsupportedOperationException(); } @Override public Terminal winSysTerminal(String name, boolean nativeSignals, Terminal.SignalHandler signalHandler) throws IOException { if (JANSI_MAJOR_VERSION > 1 || JANSI_MAJOR_VERSION == 1 && JANSI_MINOR_VERSION >= 12) { JansiWinSysTerminal terminal = new JansiWinSysTerminal(name, nativeSignals, signalHandler); if (JANSI_MAJOR_VERSION == 1 && JANSI_MINOR_VERSION < 16) { terminal.disableScrolling(); } return terminal; } throw new UnsupportedOperationException(); } } jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/freebsd/000077500000000000000000000000001311544710100305315ustar00rootroot00000000000000FreeBsdNativePty.java000066400000000000000000000470371311544710100345060ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/freebsd/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jansi.freebsd; import org.fusesource.jansi.internal.CLibrary; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.impl.jansi.JansiNativePty; import org.jline.terminal.impl.jansi.osx.OsXNativePty; import java.io.FileDescriptor; import java.io.IOException; import java.util.EnumMap; import java.util.EnumSet; public class FreeBsdNativePty extends JansiNativePty { public static FreeBsdNativePty current() throws IOException { try { String name = ttyname(); return new FreeBsdNativePty(-1, null, 0, FileDescriptor.in, name); } catch (IOException e) { throw new IOException("Not a tty", e); } } public static FreeBsdNativePty open(Attributes attr, Size size) throws IOException { int[] master = new int[1]; int[] slave = new int[1]; byte[] buf = new byte[64]; CLibrary.openpty(master, slave, buf, attr != null ? termios(attr) : null, size != null ? new CLibrary.WinSize((short) size.getRows(), (short) size.getColumns()) : null); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new FreeBsdNativePty(master[0], newDescriptor(master[0]), slave[0], newDescriptor(slave[0]), name); } public FreeBsdNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { super(master, masterFD, slave, slaveFD, name); } // CONSTANTS private static final int VEOF = 0; private static final int VEOL = 1; private static final int VEOL2 = 2; private static final int VERASE = 3; private static final int VWERASE = 4; private static final int VKILL = 5; private static final int VREPRINT = 6; private static final int VERASE2 = 7; private static final int VINTR = 8; private static final int VQUIT = 9; private static final int VSUSP = 10; private static final int VDSUSP = 11; private static final int VSTART = 12; private static final int VSTOP = 13; private static final int VLNEXT = 14; private static final int VDISCARD = 15; private static final int VMIN = 16; private static final int VTIME = 17; private static final int VSTATUS = 18; private static final int IGNBRK = 0x0000001; private static final int BRKINT = 0x0000002; private static final int IGNPAR = 0x0000004; private static final int PARMRK = 0x0000008; private static final int INPCK = 0x0000010; private static final int ISTRIP = 0x0000020; private static final int INLCR = 0x0000040; private static final int IGNCR = 0x0000080; private static final int ICRNL = 0x0000100; private static final int IXON = 0x0000200; private static final int IXOFF = 0x0000400; private static final int IXANY = 0x0000800; private static final int IMAXBEL = 0x0002000; private static final int OPOST = 0x0000001; private static final int ONLCR = 0x0000002; private static final int TABDLY = 0x0000004; private static final int TAB0 = 0x0000000; private static final int TAB3 = 0x0000004; private static final int ONOEOT = 0x0000008; private static final int OCRNL = 0x0000010; private static final int ONLRET = 0x0000040; private static final int CIGNORE = 0x0000001; private static final int CSIZE = 0x0000300; private static final int CS5 = 0x0000000; private static final int CS6 = 0x0000100; private static final int CS7 = 0x0000200; private static final int CS8 = 0x0000300; private static final int CSTOPB = 0x0000400; private static final int CREAD = 0x0000800; private static final int PARENB = 0x0001000; private static final int PARODD = 0x0002000; private static final int HUPCL = 0x0004000; private static final int CLOCAL = 0x0008000; private static final int ECHOKE = 0x0000001; private static final int ECHOE = 0x0000002; private static final int ECHOK = 0x0000004; private static final int ECHO = 0x0000008; private static final int ECHONL = 0x0000010; private static final int ECHOPRT = 0x0000020; private static final int ECHOCTL = 0x0000040; private static final int ISIG = 0x0000080; private static final int ICANON = 0x0000100; private static final int ALTWERASE = 0x000200; private static final int IEXTEN = 0x0000400; private static final int EXTPROC = 0x0000800; private static final int TOSTOP = 0x0400000; private static final int FLUSHO = 0x0800000; private static final int PENDIN = 0x2000000; private static final int NOFLSH = 0x8000000; protected CLibrary.Termios toTermios(Attributes t) { return termios(t); } static CLibrary.Termios termios(Attributes t) { CLibrary.Termios tio = new CLibrary.Termios(); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNBRK), IGNBRK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.BRKINT), BRKINT, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNPAR), IGNPAR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.PARMRK), PARMRK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INPCK), INPCK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ISTRIP), ISTRIP, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INLCR), INLCR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNCR), IGNCR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ICRNL), ICRNL, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXON), IXON, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXOFF), IXOFF, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXANY), IXANY, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IMAXBEL), IMAXBEL, tio.c_iflag); // tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IUTF8), IUTF8, tio.c_iflag); // Output flags tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OPOST), OPOST, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLCR), ONLCR, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OXTABS), OXTABS, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOEOT), ONOEOT, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OCRNL), OCRNL, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOCR), ONOCR, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLRET), ONLRET, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFILL), OFILL, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.NLDLY), NLDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.TABDLY), TABDLY, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.CRDLY), CRDLY, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.FFDLY), FFDLY, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.BSDLY), BSDLY, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.VTDLY), VTDLY, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFDEL), OFDEL, tio.c_oflag); // Control flags tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CIGNORE), CIGNORE, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS5), CS5, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS6), CS6, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS7), CS7, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS8), CS8, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CSTOPB), CSTOPB, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CREAD), CREAD, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARENB), PARENB, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARODD), PARODD, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.HUPCL), HUPCL, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CLOCAL), CLOCAL, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCTS_OFLOW), CCTS_OFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CRTS_IFLOW), CRTS_IFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDTR_IFLOW), CDTR_IFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDSR_OFLOW), CDSR_OFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCAR_OFLOW), CCAR_OFLOW, tio.c_cflag); // Local flags tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOKE), ECHOKE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOE), ECHOE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOK), ECHOK, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHO), ECHO, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHONL), ECHONL, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOPRT), ECHOPRT, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOCTL), ECHOCTL, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ISIG), ISIG, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ICANON), ICANON, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ALTWERASE), ALTWERASE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.IEXTEN), IEXTEN, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.EXTPROC), EXTPROC, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.TOSTOP), TOSTOP, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.FLUSHO), FLUSHO, tio.c_lflag); // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOKERNINFO), NOKERNINFO, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.PENDIN), PENDIN, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOFLSH), NOFLSH, tio.c_lflag); // Control chars tio.c_cc[VEOF] = (byte) t.getControlChar(Attributes.ControlChar.VEOF); tio.c_cc[VEOL] = (byte) t.getControlChar(Attributes.ControlChar.VEOL); tio.c_cc[VEOL2] = (byte) t.getControlChar(Attributes.ControlChar.VEOL2); tio.c_cc[VERASE] = (byte) t.getControlChar(Attributes.ControlChar.VERASE); tio.c_cc[VWERASE] = (byte) t.getControlChar(Attributes.ControlChar.VWERASE); tio.c_cc[VKILL] = (byte) t.getControlChar(Attributes.ControlChar.VKILL); tio.c_cc[VREPRINT] = (byte) t.getControlChar(Attributes.ControlChar.VREPRINT); tio.c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR); tio.c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT); tio.c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP); // tio.c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP); tio.c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART); tio.c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP); tio.c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT); tio.c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD); tio.c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN); tio.c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME); // tio.c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS); return tio; } protected Attributes toAttributes(CLibrary.Termios tio) { Attributes attr = new Attributes(); // Input flags EnumSet iflag = attr.getInputFlags(); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXON, IXON); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL); // addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8); // Output flags EnumSet oflag = attr.getOutputFlags(); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL); // Control flags EnumSet cflag = attr.getControlFlags(); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS5, CS5); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS6, CS6); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS7, CS7); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS8, CS8); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW); // Local flags EnumSet lflag = attr.getLocalFlags(); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO); // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH); // Control chars EnumMap cc = attr.getControlChars(); cc.put(Attributes.ControlChar.VEOF, (int) tio.c_cc[VEOF]); cc.put(Attributes.ControlChar.VEOL, (int) tio.c_cc[VEOL]); cc.put(Attributes.ControlChar.VEOL2, (int) tio.c_cc[VEOL2]); cc.put(Attributes.ControlChar.VERASE, (int) tio.c_cc[VERASE]); cc.put(Attributes.ControlChar.VWERASE, (int) tio.c_cc[VWERASE]); cc.put(Attributes.ControlChar.VKILL, (int) tio.c_cc[VKILL]); cc.put(Attributes.ControlChar.VREPRINT, (int) tio.c_cc[VREPRINT]); cc.put(Attributes.ControlChar.VINTR, (int) tio.c_cc[VINTR]); cc.put(Attributes.ControlChar.VQUIT, (int) tio.c_cc[VQUIT]); cc.put(Attributes.ControlChar.VSUSP, (int) tio.c_cc[VSUSP]); // cc.put(Attributes.ControlChar.VDSUSP, (int) tio.c_cc[VDSUSP]); cc.put(Attributes.ControlChar.VSTART, (int) tio.c_cc[VSTART]); cc.put(Attributes.ControlChar.VSTOP, (int) tio.c_cc[VSTOP]); cc.put(Attributes.ControlChar.VLNEXT, (int) tio.c_cc[VLNEXT]); cc.put(Attributes.ControlChar.VDISCARD, (int) tio.c_cc[VDISCARD]); cc.put(Attributes.ControlChar.VMIN, (int) tio.c_cc[VMIN]); cc.put(Attributes.ControlChar.VTIME, (int) tio.c_cc[VTIME]); // cc.put(Attributes.ControlChar.VSTATUS, (int) tio.c_cc[VSTATUS]); // Return return attr; } private static long setFlag(boolean flag, long value, long org) { return flag ? org | value : org; } private static > void addFlag(long value, EnumSet flags, T flag, int v) { if ((value & v) != 0) { flags.add(flag); } } } jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/linux/000077500000000000000000000000001311544710100302565ustar00rootroot00000000000000LinuxNativePty.java000066400000000000000000000525241311544710100340150ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/linux/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jansi.linux; import org.fusesource.jansi.internal.CLibrary; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.impl.jansi.JansiNativePty; import org.jline.terminal.impl.jansi.osx.OsXNativePty; import java.io.FileDescriptor; import java.io.IOException; import java.util.EnumMap; import java.util.EnumSet; public class LinuxNativePty extends JansiNativePty { public static LinuxNativePty current() throws IOException { try { String name = ttyname(); return new LinuxNativePty(-1, null, 0, FileDescriptor.in, name); } catch (IOException e) { throw new IOException("Not a tty", e); } } public static LinuxNativePty open(Attributes attr, Size size) throws IOException { int[] master = new int[1]; int[] slave = new int[1]; byte[] buf = new byte[64]; CLibrary.openpty(master, slave, buf, attr != null ? termios(attr) : null, size != null ? new CLibrary.WinSize((short) size.getRows(), (short) size.getColumns()) : null); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new LinuxNativePty(master[0], newDescriptor(master[0]), slave[0], newDescriptor(slave[0]), name); } public LinuxNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { super(master, masterFD, slave, slaveFD, name); } // CONSTANTS private static final int VINTR = 0; private static final int VQUIT = 1; private static final int VERASE = 2; private static final int VKILL = 3; private static final int VEOF = 4; private static final int VTIME = 5; private static final int VMIN = 6; private static final int VSWTC = 7; private static final int VSTART = 8; private static final int VSTOP = 9; private static final int VSUSP = 10; private static final int VEOL = 11; private static final int VREPRINT = 12; private static final int VDISCARD = 13; private static final int VWERASE = 14; private static final int VLNEXT = 15; private static final int VEOL2 = 16; private static final int IGNBRK = 0x0000001; private static final int BRKINT = 0x0000002; private static final int IGNPAR = 0x0000004; private static final int PARMRK = 0x0000008; private static final int INPCK = 0x0000010; private static final int ISTRIP = 0x0000020; private static final int INLCR = 0x0000040; private static final int IGNCR = 0x0000080; private static final int ICRNL = 0x0000100; private static final int IUCLC = 0x0000200; private static final int IXON = 0x0000400; private static final int IXANY = 0x0000800; private static final int IXOFF = 0x0001000; private static final int IMAXBEL = 0x0002000; private static final int IUTF8 = 0x0004000; private static final int OPOST = 0x0000001; private static final int OLCUC = 0x0000002; private static final int ONLCR = 0x0000004; private static final int OCRNL = 0x0000008; private static final int ONOCR = 0x0000010; private static final int ONLRET = 0x0000020; private static final int OFILL = 0x0000040; private static final int OFDEL = 0x0000080; private static final int NLDLY = 0x0000100; private static final int NL0 = 0x0000000; private static final int NL1 = 0x0000100; private static final int CRDLY = 0x0000600; private static final int CR0 = 0x0000000; private static final int CR1 = 0x0000200; private static final int CR2 = 0x0000400; private static final int CR3 = 0x0000600; private static final int TABDLY = 0x0001800; private static final int TAB0 = 0x0000000; private static final int TAB1 = 0x0000800; private static final int TAB2 = 0x0001000; private static final int TAB3 = 0x0001800; private static final int XTABS = 0x0001800; private static final int BSDLY = 0x0002000; private static final int BS0 = 0x0000000; private static final int BS1 = 0x0002000; private static final int VTDLY = 0x0004000; private static final int VT0 = 0x0000000; private static final int VT1 = 0x0004000; private static final int FFDLY = 0x0008000; private static final int FF0 = 0x0000000; private static final int FF1 = 0x0008000; private static final int CBAUD = 0x000100f; private static final int B0 = 0x0000000; private static final int B50 = 0x0000001; private static final int B75 = 0x0000002; private static final int B110 = 0x0000003; private static final int B134 = 0x0000004; private static final int B150 = 0x0000005; private static final int B200 = 0x0000006; private static final int B300 = 0x0000007; private static final int B600 = 0x0000008; private static final int B1200 = 0x0000009; private static final int B1800 = 0x000000a; private static final int B2400 = 0x000000b; private static final int B4800 = 0x000000c; private static final int B9600 = 0x000000d; private static final int B19200 = 0x000000e; private static final int B38400 = 0x000000f; private static final int EXTA = B19200; private static final int EXTB = B38400; private static final int CSIZE = 0x0000030; private static final int CS5 = 0x0000000; private static final int CS6 = 0x0000010; private static final int CS7 = 0x0000020; private static final int CS8 = 0x0000030; private static final int CSTOPB = 0x0000040; private static final int CREAD = 0x0000080; private static final int PARENB = 0x0000100; private static final int PARODD = 0x0000200; private static final int HUPCL = 0x0000400; private static final int CLOCAL = 0x0000800; private static final int ISIG = 0x0000001; private static final int ICANON = 0x0000002; private static final int XCASE = 0x0000004; private static final int ECHO = 0x0000008; private static final int ECHOE = 0x0000010; private static final int ECHOK = 0x0000020; private static final int ECHONL = 0x0000040; private static final int NOFLSH = 0x0000080; private static final int TOSTOP = 0x0000100; private static final int ECHOCTL = 0x0000200; private static final int ECHOPRT = 0x0000400; private static final int ECHOKE = 0x0000800; private static final int FLUSHO = 0x0001000; private static final int PENDIN = 0x0002000; private static final int IEXTEN = 0x0008000; private static final int EXTPROC = 0x0010000; protected CLibrary.Termios toTermios(Attributes t) { return termios(t); } static CLibrary.Termios termios(Attributes t) { CLibrary.Termios tio = new CLibrary.Termios(); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNBRK), IGNBRK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.BRKINT), BRKINT, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNPAR), IGNPAR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.PARMRK), PARMRK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INPCK), INPCK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ISTRIP), ISTRIP, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INLCR), INLCR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNCR), IGNCR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ICRNL), ICRNL, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXON), IXON, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXOFF), IXOFF, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXANY), IXANY, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IMAXBEL), IMAXBEL, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IUTF8), IUTF8, tio.c_iflag); // Output flags tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OPOST), OPOST, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLCR), ONLCR, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OXTABS), OXTABS, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOEOT), ONOEOT, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OCRNL), OCRNL, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOCR), ONOCR, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLRET), ONLRET, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFILL), OFILL, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.NLDLY), NLDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.TABDLY), TABDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.CRDLY), CRDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.FFDLY), FFDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.BSDLY), BSDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.VTDLY), VTDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFDEL), OFDEL, tio.c_oflag); // Control flags // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CIGNORE), CIGNORE, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS5), CS5, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS6), CS6, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS7), CS7, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS8), CS8, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CSTOPB), CSTOPB, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CREAD), CREAD, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARENB), PARENB, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARODD), PARODD, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.HUPCL), HUPCL, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CLOCAL), CLOCAL, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCTS_OFLOW), CCTS_OFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CRTS_IFLOW), CRTS_IFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDTR_IFLOW), CDTR_IFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDSR_OFLOW), CDSR_OFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCAR_OFLOW), CCAR_OFLOW, tio.c_cflag); // Local flags tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOKE), ECHOKE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOE), ECHOE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOK), ECHOK, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHO), ECHO, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHONL), ECHONL, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOPRT), ECHOPRT, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOCTL), ECHOCTL, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ISIG), ISIG, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ICANON), ICANON, tio.c_lflag); // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ALTWERASE), ALTWERASE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.IEXTEN), IEXTEN, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.EXTPROC), EXTPROC, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.TOSTOP), TOSTOP, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.FLUSHO), FLUSHO, tio.c_lflag); // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOKERNINFO), NOKERNINFO, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.PENDIN), PENDIN, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOFLSH), NOFLSH, tio.c_lflag); // Control chars tio.c_cc[VEOF] = (byte) t.getControlChar(Attributes.ControlChar.VEOF); tio.c_cc[VEOL] = (byte) t.getControlChar(Attributes.ControlChar.VEOL); tio.c_cc[VEOL2] = (byte) t.getControlChar(Attributes.ControlChar.VEOL2); tio.c_cc[VERASE] = (byte) t.getControlChar(Attributes.ControlChar.VERASE); tio.c_cc[VWERASE] = (byte) t.getControlChar(Attributes.ControlChar.VWERASE); tio.c_cc[VKILL] = (byte) t.getControlChar(Attributes.ControlChar.VKILL); tio.c_cc[VREPRINT] = (byte) t.getControlChar(Attributes.ControlChar.VREPRINT); tio.c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR); tio.c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT); tio.c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP); tio.c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART); tio.c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP); tio.c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT); tio.c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD); tio.c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN); tio.c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME); // tio.c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS); return tio; } protected Attributes toAttributes(CLibrary.Termios tio) { Attributes attr = new Attributes(); // Input flags EnumSet iflag = attr.getInputFlags(); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXON, IXON); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8); // Output flags EnumSet oflag = attr.getOutputFlags(); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL); // Control flags EnumSet cflag = attr.getControlFlags(); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS5, CS5); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS6, CS6); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS7, CS7); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS8, CS8); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW); // Local flags EnumSet lflag = attr.getLocalFlags(); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON); // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO); // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH); // Control chars EnumMap cc = attr.getControlChars(); cc.put(Attributes.ControlChar.VEOF, (int) tio.c_cc[VEOF]); cc.put(Attributes.ControlChar.VEOL, (int) tio.c_cc[VEOL]); cc.put(Attributes.ControlChar.VEOL2, (int) tio.c_cc[VEOL2]); cc.put(Attributes.ControlChar.VERASE, (int) tio.c_cc[VERASE]); cc.put(Attributes.ControlChar.VWERASE, (int) tio.c_cc[VWERASE]); cc.put(Attributes.ControlChar.VKILL, (int) tio.c_cc[VKILL]); cc.put(Attributes.ControlChar.VREPRINT, (int) tio.c_cc[VREPRINT]); cc.put(Attributes.ControlChar.VINTR, (int) tio.c_cc[VINTR]); cc.put(Attributes.ControlChar.VQUIT, (int) tio.c_cc[VQUIT]); cc.put(Attributes.ControlChar.VSUSP, (int) tio.c_cc[VSUSP]); cc.put(Attributes.ControlChar.VSTART, (int) tio.c_cc[VSTART]); cc.put(Attributes.ControlChar.VSTOP, (int) tio.c_cc[VSTOP]); cc.put(Attributes.ControlChar.VLNEXT, (int) tio.c_cc[VLNEXT]); cc.put(Attributes.ControlChar.VDISCARD, (int) tio.c_cc[VDISCARD]); cc.put(Attributes.ControlChar.VMIN, (int) tio.c_cc[VMIN]); cc.put(Attributes.ControlChar.VTIME, (int) tio.c_cc[VTIME]); // cc.put(Attributes.ControlChar.VSTATUS, (int) tio.c_cc[VSTATUS]); // Return return attr; } private static long setFlag(boolean flag, long value, long org) { return flag ? org | value : org; } private static > void addFlag(long value, EnumSet flags, T flag, int v) { if ((value & v) != 0) { flags.add(flag); } } } jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/osx/000077500000000000000000000000001311544710100277305ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/osx/OsXNativePty.java000066400000000000000000000503631311544710100331570ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jansi.osx; import org.fusesource.jansi.internal.CLibrary; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.impl.jansi.JansiNativePty; import java.io.FileDescriptor; import java.io.IOException; import java.util.EnumMap; import java.util.EnumSet; public class OsXNativePty extends JansiNativePty { public static OsXNativePty current() throws IOException { try { String name = ttyname(); return new OsXNativePty(-1, null, 0, FileDescriptor.in, name); } catch (IOException e) { throw new IOException("Not a tty", e); } } public static OsXNativePty open(Attributes attr, Size size) throws IOException { int[] master = new int[1]; int[] slave = new int[1]; byte[] buf = new byte[64]; CLibrary.openpty(master, slave, buf, attr != null ? termios(attr) : null, size != null ? new CLibrary.WinSize((short) size.getRows(), (short) size.getColumns()) : null); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new OsXNativePty(master[0], newDescriptor(master[0]), slave[0], newDescriptor(slave[0]), name); } public OsXNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { super(master, masterFD, slave, slaveFD, name); } // CONSTANTS private static final int VEOF = 0; private static final int VEOL = 1; private static final int VEOL2 = 2; private static final int VERASE = 3; private static final int VWERASE = 4; private static final int VKILL = 5; private static final int VREPRINT = 6; private static final int VINTR = 8; private static final int VQUIT = 9; private static final int VSUSP = 10; private static final int VDSUSP = 11; private static final int VSTART = 12; private static final int VSTOP = 13; private static final int VLNEXT = 14; private static final int VDISCARD = 15; private static final int VMIN = 16; private static final int VTIME = 17; private static final int VSTATUS = 18; private static final int IGNBRK = 0x00000001; private static final int BRKINT = 0x00000002; private static final int IGNPAR = 0x00000004; private static final int PARMRK = 0x00000008; private static final int INPCK = 0x00000010; private static final int ISTRIP = 0x00000020; private static final int INLCR = 0x00000040; private static final int IGNCR = 0x00000080; private static final int ICRNL = 0x00000100; private static final int IXON = 0x00000200; private static final int IXOFF = 0x00000400; private static final int IXANY = 0x00000800; private static final int IMAXBEL = 0x00002000; private static final int IUTF8 = 0x00004000; private static final int OPOST = 0x00000001; private static final int ONLCR = 0x00000002; private static final int OXTABS = 0x00000004; private static final int ONOEOT = 0x00000008; private static final int OCRNL = 0x00000010; private static final int ONOCR = 0x00000020; private static final int ONLRET = 0x00000040; private static final int OFILL = 0x00000080; private static final int NLDLY = 0x00000300; private static final int TABDLY = 0x00000c04; private static final int CRDLY = 0x00003000; private static final int FFDLY = 0x00004000; private static final int BSDLY = 0x00008000; private static final int VTDLY = 0x00010000; private static final int OFDEL = 0x00020000; private static final int CIGNORE = 0x00000001; private static final int CS5 = 0x00000000; private static final int CS6 = 0x00000100; private static final int CS7 = 0x00000200; private static final int CS8 = 0x00000300; private static final int CSTOPB = 0x00000400; private static final int CREAD = 0x00000800; private static final int PARENB = 0x00001000; private static final int PARODD = 0x00002000; private static final int HUPCL = 0x00004000; private static final int CLOCAL = 0x00008000; private static final int CCTS_OFLOW = 0x00010000; private static final int CRTS_IFLOW = 0x00020000; private static final int CDTR_IFLOW = 0x00040000; private static final int CDSR_OFLOW = 0x00080000; private static final int CCAR_OFLOW = 0x00100000; private static final int ECHOKE = 0x00000001; private static final int ECHOE = 0x00000002; private static final int ECHOK = 0x00000004; private static final int ECHO = 0x00000008; private static final int ECHONL = 0x00000010; private static final int ECHOPRT = 0x00000020; private static final int ECHOCTL = 0x00000040; private static final int ISIG = 0x00000080; private static final int ICANON = 0x00000100; private static final int ALTWERASE = 0x00000200; private static final int IEXTEN = 0x00000400; private static final int EXTPROC = 0x00000800; private static final int TOSTOP = 0x00400000; private static final int FLUSHO = 0x00800000; private static final int NOKERNINFO = 0x02000000; private static final int PENDIN = 0x20000000; private static final int NOFLSH = 0x80000000; protected CLibrary.Termios toTermios(Attributes t) { return termios(t); } static CLibrary.Termios termios(Attributes t) { CLibrary.Termios tio = new CLibrary.Termios(); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNBRK), IGNBRK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.BRKINT), BRKINT, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNPAR), IGNPAR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.PARMRK), PARMRK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INPCK), INPCK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ISTRIP), ISTRIP, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INLCR), INLCR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNCR), IGNCR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ICRNL), ICRNL, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXON), IXON, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXOFF), IXOFF, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXANY), IXANY, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IMAXBEL), IMAXBEL, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IUTF8), IUTF8, tio.c_iflag); // Output flags tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OPOST), OPOST, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLCR), ONLCR, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OXTABS), OXTABS, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOEOT), ONOEOT, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OCRNL), OCRNL, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOCR), ONOCR, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLRET), ONLRET, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFILL), OFILL, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.NLDLY), NLDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.TABDLY), TABDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.CRDLY), CRDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.FFDLY), FFDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.BSDLY), BSDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.VTDLY), VTDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFDEL), OFDEL, tio.c_oflag); // Control flags tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CIGNORE), CIGNORE, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS5), CS5, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS6), CS6, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS7), CS7, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS8), CS8, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CSTOPB), CSTOPB, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CREAD), CREAD, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARENB), PARENB, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARODD), PARODD, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.HUPCL), HUPCL, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CLOCAL), CLOCAL, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCTS_OFLOW), CCTS_OFLOW, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CRTS_IFLOW), CRTS_IFLOW, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDTR_IFLOW), CDTR_IFLOW, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDSR_OFLOW), CDSR_OFLOW, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCAR_OFLOW), CCAR_OFLOW, tio.c_cflag); // Local flags tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOKE), ECHOKE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOE), ECHOE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOK), ECHOK, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHO), ECHO, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHONL), ECHONL, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOPRT), ECHOPRT, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOCTL), ECHOCTL, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ISIG), ISIG, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ICANON), ICANON, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ALTWERASE), ALTWERASE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.IEXTEN), IEXTEN, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.EXTPROC), EXTPROC, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.TOSTOP), TOSTOP, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.FLUSHO), FLUSHO, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOKERNINFO), NOKERNINFO, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.PENDIN), PENDIN, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOFLSH), NOFLSH, tio.c_lflag); // Control chars tio.c_cc[VEOF] = (byte) t.getControlChar(Attributes.ControlChar.VEOF); tio.c_cc[VEOL] = (byte) t.getControlChar(Attributes.ControlChar.VEOL); tio.c_cc[VEOL2] = (byte) t.getControlChar(Attributes.ControlChar.VEOL2); tio.c_cc[VERASE] = (byte) t.getControlChar(Attributes.ControlChar.VERASE); tio.c_cc[VWERASE] = (byte) t.getControlChar(Attributes.ControlChar.VWERASE); tio.c_cc[VKILL] = (byte) t.getControlChar(Attributes.ControlChar.VKILL); tio.c_cc[VREPRINT] = (byte) t.getControlChar(Attributes.ControlChar.VREPRINT); tio.c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR); tio.c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT); tio.c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP); tio.c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP); tio.c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART); tio.c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP); tio.c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT); tio.c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD); tio.c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN); tio.c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME); tio.c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS); return tio; } protected Attributes toAttributes(CLibrary.Termios tio) { Attributes attr = new Attributes(); // Input flags EnumSet iflag = attr.getInputFlags(); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXON, IXON); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8); // Output flags EnumSet oflag = attr.getOutputFlags(); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL); // Control flags EnumSet cflag = attr.getControlFlags(); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS5, CS5); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS6, CS6); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS7, CS7); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS8, CS8); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW); // Local flags EnumSet lflag = attr.getLocalFlags(); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH); // Control chars EnumMap cc = attr.getControlChars(); cc.put(Attributes.ControlChar.VEOF, (int) tio.c_cc[VEOF]); cc.put(Attributes.ControlChar.VEOL, (int) tio.c_cc[VEOL]); cc.put(Attributes.ControlChar.VEOL2, (int) tio.c_cc[VEOL2]); cc.put(Attributes.ControlChar.VERASE, (int) tio.c_cc[VERASE]); cc.put(Attributes.ControlChar.VWERASE, (int) tio.c_cc[VWERASE]); cc.put(Attributes.ControlChar.VKILL, (int) tio.c_cc[VKILL]); cc.put(Attributes.ControlChar.VREPRINT, (int) tio.c_cc[VREPRINT]); cc.put(Attributes.ControlChar.VINTR, (int) tio.c_cc[VINTR]); cc.put(Attributes.ControlChar.VQUIT, (int) tio.c_cc[VQUIT]); cc.put(Attributes.ControlChar.VSUSP, (int) tio.c_cc[VSUSP]); cc.put(Attributes.ControlChar.VDSUSP, (int) tio.c_cc[VDSUSP]); cc.put(Attributes.ControlChar.VSTART, (int) tio.c_cc[VSTART]); cc.put(Attributes.ControlChar.VSTOP, (int) tio.c_cc[VSTOP]); cc.put(Attributes.ControlChar.VLNEXT, (int) tio.c_cc[VLNEXT]); cc.put(Attributes.ControlChar.VDISCARD, (int) tio.c_cc[VDISCARD]); cc.put(Attributes.ControlChar.VMIN, (int) tio.c_cc[VMIN]); cc.put(Attributes.ControlChar.VTIME, (int) tio.c_cc[VTIME]); cc.put(Attributes.ControlChar.VSTATUS, (int) tio.c_cc[VSTATUS]); // Return return attr; } private static long setFlag(boolean flag, long value, long org) { return flag ? org | value : org; } private static > void addFlag(long value, EnumSet flags, T flag, int v) { if ((value & v) != 0) { flags.add(flag); } } } jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/solaris/000077500000000000000000000000001311544710100305735ustar00rootroot00000000000000SolarisNativePty.java000066400000000000000000000513711311544710100346460ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/solaris/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jansi.solaris; import org.fusesource.jansi.internal.CLibrary; import org.jline.terminal.Attributes; import org.jline.terminal.impl.jansi.JansiNativePty; import java.io.FileDescriptor; import java.io.IOException; import java.util.EnumMap; import java.util.EnumSet; import static org.jline.utils.ExecHelper.exec; public class SolarisNativePty extends JansiNativePty { public static SolarisNativePty current() throws IOException { try { String name = ttyname(); return new SolarisNativePty(-1, null, 0, FileDescriptor.in, name); } catch (IOException e) { throw new IOException("Not a tty", e); } } public SolarisNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { super(master, masterFD, slave, slaveFD, name); } // CONSTANTS private static final int VINTR = 0; private static final int VQUIT = 1; private static final int VERASE = 2; private static final int VKILL = 3; private static final int VEOF = 4; private static final int VTIME = 5; private static final int VMIN = 6; private static final int VSWTC = 7; private static final int VSTART = 8; private static final int VSTOP = 9; private static final int VSUSP = 10; private static final int VEOL = 11; private static final int VREPRINT = 12; private static final int VDISCARD = 13; private static final int VWERASE = 14; private static final int VLNEXT = 15; private static final int VEOL2 = 16; private static final int IGNBRK = 0x0000001; private static final int BRKINT = 0x0000002; private static final int IGNPAR = 0x0000004; private static final int PARMRK = 0x0000010; private static final int INPCK = 0x0000020; private static final int ISTRIP = 0x0000040; private static final int INLCR = 0x0000100; private static final int IGNCR = 0x0000200; private static final int ICRNL = 0x0000400; private static final int IUCLC = 0x0001000; private static final int IXON = 0x0002000; private static final int IXANY = 0x0004000; private static final int IXOFF = 0x0010000; private static final int IMAXBEL = 0x0020000; private static final int IUTF8 = 0x0040000; private static final int OPOST = 0x0000001; private static final int OLCUC = 0x0000002; private static final int ONLCR = 0x0000004; private static final int OCRNL = 0x0000010; private static final int ONOCR = 0x0000020; private static final int ONLRET = 0x0000040; private static final int OFILL = 0x0000100; private static final int OFDEL = 0x0000200; private static final int NLDLY = 0x0000400; private static final int NL0 = 0x0000000; private static final int NL1 = 0x0000400; private static final int CRDLY = 0x0003000; private static final int CR0 = 0x0000000; private static final int CR1 = 0x0001000; private static final int CR2 = 0x0002000; private static final int CR3 = 0x0003000; private static final int TABDLY = 0x0014000; private static final int TAB0 = 0x0000000; private static final int TAB1 = 0x0004000; private static final int TAB2 = 0x0010000; private static final int TAB3 = 0x0014000; private static final int XTABS = 0x0014000; private static final int BSDLY = 0x0020000; private static final int BS0 = 0x0000000; private static final int BS1 = 0x0020000; private static final int VTDLY = 0x0040000; private static final int VT0 = 0x0000000; private static final int VT1 = 0x0040000; private static final int FFDLY = 0x0100000; private static final int FF0 = 0x0000000; private static final int FF1 = 0x0100000; private static final int CBAUD = 0x0010017; private static final int B0 = 0x0000000; private static final int B50 = 0x0000001; private static final int B75 = 0x0000002; private static final int B110 = 0x0000003; private static final int B134 = 0x0000004; private static final int B150 = 0x0000005; private static final int B200 = 0x0000006; private static final int B300 = 0x0000007; private static final int B600 = 0x0000010; private static final int B1200 = 0x0000011; private static final int B1800 = 0x0000012; private static final int B2400 = 0x0000013; private static final int B4800 = 0x0000014; private static final int B9600 = 0x0000015; private static final int B19200 = 0x0000016; private static final int B38400 = 0x0000017; private static final int EXTA = 0xB19200; private static final int EXTB = 0xB38400; private static final int CSIZE = 0x0000060; private static final int CS5 = 0x0000000; private static final int CS6 = 0x0000020; private static final int CS7 = 0x0000040; private static final int CS8 = 0x0000060; private static final int CSTOPB = 0x0000100; private static final int CREAD = 0x0000200; private static final int PARENB = 0x0000400; private static final int PARODD = 0x0001000; private static final int HUPCL = 0x0002000; private static final int CLOCAL = 0x0004000; private static final int ISIG = 0x0000001; private static final int ICANON = 0x0000002; private static final int XCASE = 0x0000004; private static final int ECHO = 0x0000010; private static final int ECHOE = 0x0000020; private static final int ECHOK = 0x0000040; private static final int ECHONL = 0x0000100; private static final int NOFLSH = 0x0000200; private static final int TOSTOP = 0x0000400; private static final int ECHOCTL = 0x0001000; private static final int ECHOPRT = 0x0002000; private static final int ECHOKE = 0x0004000; private static final int FLUSHO = 0x0010000; private static final int PENDIN = 0x0040000; private static final int IEXTEN = 0x0100000; private static final int EXTPROC = 0x0200000; protected CLibrary.Termios toTermios(Attributes t) { CLibrary.Termios tio = new CLibrary.Termios(); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNBRK), IGNBRK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.BRKINT), BRKINT, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNPAR), IGNPAR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.PARMRK), PARMRK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INPCK), INPCK, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ISTRIP), ISTRIP, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INLCR), INLCR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNCR), IGNCR, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ICRNL), ICRNL, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXON), IXON, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXOFF), IXOFF, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXANY), IXANY, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IMAXBEL), IMAXBEL, tio.c_iflag); tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IUTF8), IUTF8, tio.c_iflag); // Output flags tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OPOST), OPOST, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLCR), ONLCR, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OXTABS), OXTABS, tio.c_oflag); // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOEOT), ONOEOT, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OCRNL), OCRNL, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOCR), ONOCR, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLRET), ONLRET, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFILL), OFILL, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.NLDLY), NLDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.TABDLY), TABDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.CRDLY), CRDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.FFDLY), FFDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.BSDLY), BSDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.VTDLY), VTDLY, tio.c_oflag); tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFDEL), OFDEL, tio.c_oflag); // Control flags // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CIGNORE), CIGNORE, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS5), CS5, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS6), CS6, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS7), CS7, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS8), CS8, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CSTOPB), CSTOPB, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CREAD), CREAD, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARENB), PARENB, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARODD), PARODD, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.HUPCL), HUPCL, tio.c_cflag); tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CLOCAL), CLOCAL, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCTS_OFLOW), CCTS_OFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CRTS_IFLOW), CRTS_IFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDTR_IFLOW), CDTR_IFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDSR_OFLOW), CDSR_OFLOW, tio.c_cflag); // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCAR_OFLOW), CCAR_OFLOW, tio.c_cflag); // Local flags tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOKE), ECHOKE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOE), ECHOE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOK), ECHOK, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHO), ECHO, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHONL), ECHONL, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOPRT), ECHOPRT, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOCTL), ECHOCTL, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ISIG), ISIG, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ICANON), ICANON, tio.c_lflag); // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ALTWERASE), ALTWERASE, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.IEXTEN), IEXTEN, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.EXTPROC), EXTPROC, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.TOSTOP), TOSTOP, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.FLUSHO), FLUSHO, tio.c_lflag); // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOKERNINFO), NOKERNINFO, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.PENDIN), PENDIN, tio.c_lflag); tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOFLSH), NOFLSH, tio.c_lflag); // Control chars tio.c_cc[VEOF] = (byte) t.getControlChar(Attributes.ControlChar.VEOF); tio.c_cc[VEOL] = (byte) t.getControlChar(Attributes.ControlChar.VEOL); tio.c_cc[VEOL2] = (byte) t.getControlChar(Attributes.ControlChar.VEOL2); tio.c_cc[VERASE] = (byte) t.getControlChar(Attributes.ControlChar.VERASE); tio.c_cc[VWERASE] = (byte) t.getControlChar(Attributes.ControlChar.VWERASE); tio.c_cc[VKILL] = (byte) t.getControlChar(Attributes.ControlChar.VKILL); tio.c_cc[VREPRINT] = (byte) t.getControlChar(Attributes.ControlChar.VREPRINT); tio.c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR); tio.c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT); tio.c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP); // tio.c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP); tio.c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART); tio.c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP); tio.c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT); tio.c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD); tio.c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN); tio.c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME); // tio.c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS); return tio; } protected Attributes toAttributes(CLibrary.Termios tio) { Attributes attr = new Attributes(); // Input flags EnumSet iflag = attr.getInputFlags(); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXON, IXON); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL); addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8); // Output flags EnumSet oflag = attr.getOutputFlags(); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS); // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY); addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL); // Control flags EnumSet cflag = attr.getControlFlags(); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS5, CS5); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS6, CS6); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS7, CS7); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS8, CS8); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL); addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW); // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW); // Local flags EnumSet lflag = attr.getLocalFlags(); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON); // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO); // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN); addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH); // Control chars EnumMap cc = attr.getControlChars(); cc.put(Attributes.ControlChar.VEOF, (int) tio.c_cc[VEOF]); cc.put(Attributes.ControlChar.VEOL, (int) tio.c_cc[VEOL]); cc.put(Attributes.ControlChar.VEOL2, (int) tio.c_cc[VEOL2]); cc.put(Attributes.ControlChar.VERASE, (int) tio.c_cc[VERASE]); cc.put(Attributes.ControlChar.VWERASE, (int) tio.c_cc[VWERASE]); cc.put(Attributes.ControlChar.VKILL, (int) tio.c_cc[VKILL]); cc.put(Attributes.ControlChar.VREPRINT, (int) tio.c_cc[VREPRINT]); cc.put(Attributes.ControlChar.VINTR, (int) tio.c_cc[VINTR]); cc.put(Attributes.ControlChar.VQUIT, (int) tio.c_cc[VQUIT]); cc.put(Attributes.ControlChar.VSUSP, (int) tio.c_cc[VSUSP]); // cc.put(Attributes.ControlChar.VDSUSP, (int) tio.c_cc[VDSUSP]); cc.put(Attributes.ControlChar.VSTART, (int) tio.c_cc[VSTART]); cc.put(Attributes.ControlChar.VSTOP, (int) tio.c_cc[VSTOP]); cc.put(Attributes.ControlChar.VLNEXT, (int) tio.c_cc[VLNEXT]); cc.put(Attributes.ControlChar.VDISCARD, (int) tio.c_cc[VDISCARD]); cc.put(Attributes.ControlChar.VMIN, (int) tio.c_cc[VMIN]); cc.put(Attributes.ControlChar.VTIME, (int) tio.c_cc[VTIME]); // cc.put(Attributes.ControlChar.VSTATUS, (int) tio.c_cc[VSTATUS]); // Return return attr; } private static long setFlag(boolean flag, long value, long org) { return flag ? org | value : org; } private static > void addFlag(long value, EnumSet flags, T flag, int v) { if ((value & v) != 0) { flags.add(flag); } } } jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/000077500000000000000000000000001311544710100277145ustar00rootroot00000000000000JansiWinSysTerminal.java000066400000000000000000000131511311544710100344160ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jansi.win; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOError; import java.io.IOException; import java.util.function.IntConsumer; import org.fusesource.jansi.WindowsAnsiOutputStream; import org.fusesource.jansi.internal.Kernel32; import org.fusesource.jansi.internal.Kernel32.CONSOLE_SCREEN_BUFFER_INFO; import org.fusesource.jansi.internal.Kernel32.INPUT_RECORD; import org.fusesource.jansi.internal.Kernel32.KEY_EVENT_RECORD; import org.fusesource.jansi.internal.WindowsSupport; import org.jline.terminal.Cursor; import org.jline.terminal.Size; import org.jline.terminal.impl.AbstractWindowsTerminal; import org.jline.utils.InfoCmp; import static org.fusesource.jansi.internal.Kernel32.GetConsoleScreenBufferInfo; import static org.fusesource.jansi.internal.Kernel32.GetStdHandle; import static org.fusesource.jansi.internal.Kernel32.STD_OUTPUT_HANDLE; public class JansiWinSysTerminal extends AbstractWindowsTerminal { public JansiWinSysTerminal(String name, boolean nativeSignals) throws IOException { this(name, nativeSignals, SignalHandler.SIG_DFL); } public JansiWinSysTerminal(String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException { super(new WindowsAnsiOutputStream(new FileOutputStream(FileDescriptor.out)), name, nativeSignals, signalHandler); } protected int getConsoleOutputCP() { return Kernel32.GetConsoleOutputCP(); } @Override protected int getConsoleMode() { return WindowsSupport.getConsoleMode(); } @Override protected void setConsoleMode(int mode) { WindowsSupport.setConsoleMode(mode); } public Size getSize() { Size size = new Size(); size.setColumns(WindowsSupport.getWindowsTerminalWidth()); size.setRows(WindowsSupport.getWindowsTerminalHeight()); return size; } protected byte[] readConsoleInput() throws IOException { INPUT_RECORD[] events = WindowsSupport.readConsoleInput(1); if (events == null) { return new byte[0]; } StringBuilder sb = new StringBuilder(); for (INPUT_RECORD event : events) { KEY_EVENT_RECORD keyEvent = event.keyEvent; // support some C1 control sequences: ALT + [@-_] (and [a-z]?) => ESC // http://en.wikipedia.org/wiki/C0_and_C1_control_codes#C1_set final int altState = KEY_EVENT_RECORD.LEFT_ALT_PRESSED | KEY_EVENT_RECORD.RIGHT_ALT_PRESSED; // Pressing "Alt Gr" is translated to Alt-Ctrl, hence it has to be checked that Ctrl is _not_ pressed, // otherwise inserting of "Alt Gr" codes on non-US keyboards would yield errors final int ctrlState = KEY_EVENT_RECORD.LEFT_CTRL_PRESSED | KEY_EVENT_RECORD.RIGHT_CTRL_PRESSED; // Compute the overall alt state boolean isAlt = ((keyEvent.controlKeyState & altState) != 0) && ((keyEvent.controlKeyState & ctrlState) == 0); //Log.trace(keyEvent.keyDown? "KEY_DOWN" : "KEY_UP", "key code:", keyEvent.keyCode, "char:", (long)keyEvent.uchar); if (keyEvent.keyDown) { if (keyEvent.uchar > 0) { boolean shiftPressed = (keyEvent.controlKeyState & KEY_EVENT_RECORD.SHIFT_PRESSED) != 0; if (keyEvent.uchar == '\t' && shiftPressed) { sb.append(getSequence(InfoCmp.Capability.key_btab)); } else { if (isAlt) { sb.append('\033'); } sb.append(keyEvent.uchar); } } else { // virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx // TODO: numpad keys, modifiers String escapeSequence = getEscapeSequence(keyEvent.keyCode); if (escapeSequence != null) { for (int k = 0; k < keyEvent.repeatCount; k++) { if (isAlt) { sb.append('\033'); } sb.append(escapeSequence); } } } } else { // key up event // support ALT+NumPad input method if (keyEvent.keyCode == 0x12/*VK_MENU ALT key*/ && keyEvent.uchar > 0) { sb.append(keyEvent.uchar); } } } return sb.toString().getBytes(); } @Override public Cursor getCursorPosition(IntConsumer discarded) { CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO(); long console = GetStdHandle(STD_OUTPUT_HANDLE); if (GetConsoleScreenBufferInfo(console, info) == 0) { throw new IOError(new IOException("Could not get the cursor position: " + WindowsSupport.getLastErrorMessage())); } return new Cursor(info.cursorPosition.x, info.cursorPosition.y); } public void disableScrolling() { strings.remove(InfoCmp.Capability.insert_line); strings.remove(InfoCmp.Capability.parm_insert_line); strings.remove(InfoCmp.Capability.delete_line); strings.remove(InfoCmp.Capability.parm_delete_line); } } jline3-jline-3.3.1/terminal-jansi/src/main/resources/000077500000000000000000000000001311544710100224405ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/resources/META-INF/000077500000000000000000000000001311544710100236005ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/resources/META-INF/services/000077500000000000000000000000001311544710100254235ustar00rootroot00000000000000org.jline.terminal.spi.JansiSupport000066400000000000000000000000571311544710100342240ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jansi/src/main/resources/META-INF/servicesorg.jline.terminal.impl.jansi.JansiSupportImpl jline3-jline-3.3.1/terminal-jna/000077500000000000000000000000001311544710100163575ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/pom.xml000066400000000000000000000033451311544710100177010ustar00rootroot00000000000000 4.0.0 org.jline jline-parent 3.3.1 jline-terminal-jna JLine JNA Terminal net.java.dev.jna jna org.jline jline-terminal junit junit test org.apache.felix maven-bundle-plugin *;-noimport:=true org.jline.terminal jline3-jline-3.3.1/terminal-jna/src/000077500000000000000000000000001311544710100171465ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/000077500000000000000000000000001311544710100200725ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/000077500000000000000000000000001311544710100210135ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/000077500000000000000000000000001311544710100216025ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/000077500000000000000000000000001311544710100227035ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/000077500000000000000000000000001311544710100245165ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/000077500000000000000000000000001311544710100254575ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/000077500000000000000000000000001311544710100262275ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaNativePty.java000066400000000000000000000074101311544710100314500ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Constructor; import com.sun.jna.Platform; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.spi.Pty; import org.jline.terminal.impl.jna.freebsd.FreeBsdNativePty; import org.jline.terminal.impl.jna.linux.LinuxNativePty; import org.jline.terminal.impl.jna.osx.OsXNativePty; import org.jline.terminal.impl.jna.solaris.SolarisNativePty; public abstract class JnaNativePty implements Pty { private final int master; private final int slave; private final String name; private final FileDescriptor masterFD; private final FileDescriptor slaveFD; public static JnaNativePty current() throws IOException { if (Platform.isMac()) { return OsXNativePty.current(); } else if (Platform.isLinux()) { return LinuxNativePty.current(); } else if (Platform.isSolaris()) { return SolarisNativePty.current(); } else if (Platform.isFreeBSD()) { return FreeBsdNativePty.current(); } else { throw new UnsupportedOperationException(); } } public static JnaNativePty open(Attributes attr, Size size) throws IOException { if (Platform.isMac()) { return OsXNativePty.open(attr, size); } else if (Platform.isLinux()) { return LinuxNativePty.open(attr, size); } else if (Platform.isSolaris()) { return SolarisNativePty.open(attr, size); } else if (Platform.isFreeBSD()) { return FreeBsdNativePty.open(attr, size); } else { throw new UnsupportedOperationException(); } } protected JnaNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { this.master = master; this.slave = slave; this.name = name; this.masterFD = masterFD; this.slaveFD = slaveFD; } @Override public void close() throws IOException { if (master > 0) { getMasterInput().close(); } if (slave > 0) { getSlaveInput().close(); } } public int getMaster() { return master; } public int getSlave() { return slave; } public String getName() { return name; } public FileDescriptor getMasterFD() { return masterFD; } public FileDescriptor getSlaveFD() { return slaveFD; } public InputStream getMasterInput() { return new FileInputStream(getMasterFD()); } public OutputStream getMasterOutput() { return new FileOutputStream(getMasterFD()); } public InputStream getSlaveInput() { return new FileInputStream(getSlaveFD()); } public OutputStream getSlaveOutput() { return new FileOutputStream(getSlaveFD()); } protected static FileDescriptor newDescriptor(int fd) { try { Constructor cns = FileDescriptor.class.getDeclaredConstructor(int.class); cns.setAccessible(true); return cns.newInstance(fd); } catch (Throwable e) { throw new RuntimeException("Unable to create FileDescriptor", e); } } @Override public String toString() { return "JnaNativePty[" + getName() + "]"; } } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaSupportImpl.java000066400000000000000000000015111311544710100320170ustar00rootroot00000000000000package org.jline.terminal.impl.jna; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.impl.jna.win.JnaWinSysTerminal; import org.jline.terminal.spi.JnaSupport; import org.jline.terminal.spi.Pty; import java.io.IOException; public class JnaSupportImpl implements JnaSupport { @Override public Pty current() throws IOException { return JnaNativePty.current(); } @Override public Pty open(Attributes attributes, Size size) throws IOException { return JnaNativePty.open(attributes, size); } @Override public Terminal winSysTerminal(String name, boolean nativeSignals, Terminal.SignalHandler signalHandler) throws IOException { return new JnaWinSysTerminal(name, nativeSignals, signalHandler); } } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/freebsd/000077500000000000000000000000001311544710100276415ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/freebsd/CLibrary.java000066400000000000000000000347731311544710100322310ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.freebsd; import java.util.Arrays; import java.util.EnumMap; import java.util.EnumSet; import java.util.List; import com.sun.jna.LastErrorException; import com.sun.jna.Structure; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.ControlFlag; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Attributes.OutputFlag; import org.jline.terminal.Size; public interface CLibrary extends com.sun.jna.Library { void tcgetattr(int fd, termios termios) throws LastErrorException; void tcsetattr(int fd, int cmd, termios termios) throws LastErrorException; void ioctl(int fd, long cmd, winsize data) throws LastErrorException; void ttyname_r(int fd, byte[] buf, int len) throws LastErrorException; class winsize extends Structure { public short ws_row; public short ws_col; public short ws_xpixel; public short ws_ypixel; public winsize() { } public winsize(Size ws) { ws_row = (short) ws.getRows(); ws_col = (short) ws.getColumns(); } public Size toSize() { return new Size(ws_col, ws_row); } @Override protected List getFieldOrder() { return Arrays.asList(// "ws_row",// "ws_col",// "ws_xpixel",// "ws_ypixel"// ); } } class termios extends Structure { public int c_iflag; public int c_oflag; public int c_cflag; public int c_lflag; public byte[] c_cc = new byte[20]; public int c_ispeed; public int c_ospeed; @Override protected List getFieldOrder() { return Arrays.asList(// "c_iflag",// "c_oflag",// "c_cflag",// "c_lflag",// "c_cc",// "c_ispeed",// "c_ospeed"// ); } public termios() { } public termios(Attributes t) { // Input flags c_iflag = setFlag(t.getInputFlag(InputFlag.IGNBRK), IGNBRK, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.BRKINT), BRKINT, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IGNPAR), IGNPAR, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.PARMRK), PARMRK, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.INPCK), INPCK, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.ISTRIP), ISTRIP, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.INLCR), INLCR, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IGNCR), IGNCR, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.ICRNL), ICRNL, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IXON), IXON, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IXOFF), IXOFF, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IXANY), IXANY, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IMAXBEL), IMAXBEL, c_iflag); // Output flags c_oflag = setFlag(t.getOutputFlag(OutputFlag.OPOST), OPOST, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.ONLCR), ONLCR, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.OCRNL), OCRNL, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.ONLRET), ONLRET, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.TABDLY), TABDLY, c_oflag); // Control flags c_cflag = setFlag(t.getControlFlag(ControlFlag.CS5), CS5, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CS6), CS6, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CS7), CS7, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CS8), CS8, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CSTOPB), CSTOPB, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CREAD), CREAD, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.PARENB), PARENB, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.PARODD), PARODD, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.HUPCL), HUPCL, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CLOCAL), CLOCAL, c_cflag); // Local flags c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOKE), ECHOKE, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOE), ECHOE, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOK), ECHOK, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHO), ECHO, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHONL), ECHONL, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOPRT), ECHOPRT, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOCTL), ECHOCTL, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ISIG), ISIG, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ICANON), ICANON, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.IEXTEN), IEXTEN, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.EXTPROC), EXTPROC, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.TOSTOP), TOSTOP, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.FLUSHO), FLUSHO, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.PENDIN), PENDIN, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.NOFLSH), NOFLSH, c_lflag); // Control chars c_cc[VEOF] = (byte) t.getControlChar(ControlChar.VEOF); c_cc[VEOL] = (byte) t.getControlChar(ControlChar.VEOL); c_cc[VEOL2] = (byte) t.getControlChar(ControlChar.VEOL2); c_cc[VERASE] = (byte) t.getControlChar(ControlChar.VERASE); c_cc[VWERASE] = (byte) t.getControlChar(ControlChar.VWERASE); c_cc[VKILL] = (byte) t.getControlChar(ControlChar.VKILL); c_cc[VREPRINT] = (byte) t.getControlChar(ControlChar.VREPRINT); c_cc[VINTR] = (byte) t.getControlChar(ControlChar.VINTR); c_cc[VQUIT] = (byte) t.getControlChar(ControlChar.VQUIT); c_cc[VSUSP] = (byte) t.getControlChar(ControlChar.VSUSP); c_cc[VSTART] = (byte) t.getControlChar(ControlChar.VSTART); c_cc[VSTOP] = (byte) t.getControlChar(ControlChar.VSTOP); c_cc[VLNEXT] = (byte) t.getControlChar(ControlChar.VLNEXT); c_cc[VDISCARD] = (byte) t.getControlChar(ControlChar.VDISCARD); c_cc[VMIN] = (byte) t.getControlChar(ControlChar.VMIN); c_cc[VTIME] = (byte) t.getControlChar(ControlChar.VTIME); } private int setFlag(boolean flag, int value, int org) { return flag ? (org | value) : org; } public Attributes toAttributes() { Attributes attr = new Attributes(); // Input flags EnumSet iflag = attr.getInputFlags(); addFlag(c_iflag, iflag, InputFlag.IGNBRK, IGNBRK); addFlag(c_iflag, iflag, InputFlag.IGNBRK, IGNBRK); addFlag(c_iflag, iflag, InputFlag.BRKINT, BRKINT); addFlag(c_iflag, iflag, InputFlag.IGNPAR, IGNPAR); addFlag(c_iflag, iflag, InputFlag.PARMRK, PARMRK); addFlag(c_iflag, iflag, InputFlag.INPCK, INPCK); addFlag(c_iflag, iflag, InputFlag.ISTRIP, ISTRIP); addFlag(c_iflag, iflag, InputFlag.INLCR, INLCR); addFlag(c_iflag, iflag, InputFlag.IGNCR, IGNCR); addFlag(c_iflag, iflag, InputFlag.ICRNL, ICRNL); addFlag(c_iflag, iflag, InputFlag.IXON, IXON); addFlag(c_iflag, iflag, InputFlag.IXOFF, IXOFF); addFlag(c_iflag, iflag, InputFlag.IXANY, IXANY); addFlag(c_iflag, iflag, InputFlag.IMAXBEL, IMAXBEL); // Output flags EnumSet oflag = attr.getOutputFlags(); addFlag(c_oflag, oflag, OutputFlag.OPOST, OPOST); addFlag(c_oflag, oflag, OutputFlag.ONLCR, ONLCR); addFlag(c_oflag, oflag, OutputFlag.OCRNL, OCRNL); addFlag(c_oflag, oflag, OutputFlag.ONLRET, ONLRET); addFlag(c_oflag, oflag, OutputFlag.TABDLY, TABDLY); // Control flags EnumSet cflag = attr.getControlFlags(); addFlag(c_cflag, cflag, ControlFlag.CS5, CS5); addFlag(c_cflag, cflag, ControlFlag.CS6, CS6); addFlag(c_cflag, cflag, ControlFlag.CS7, CS7); addFlag(c_cflag, cflag, ControlFlag.CS8, CS8); addFlag(c_cflag, cflag, ControlFlag.CSTOPB, CSTOPB); addFlag(c_cflag, cflag, ControlFlag.CREAD, CREAD); addFlag(c_cflag, cflag, ControlFlag.PARENB, PARENB); addFlag(c_cflag, cflag, ControlFlag.PARODD, PARODD); addFlag(c_cflag, cflag, ControlFlag.HUPCL, HUPCL); addFlag(c_cflag, cflag, ControlFlag.CLOCAL, CLOCAL); // Local flags EnumSet lflag = attr.getLocalFlags(); addFlag(c_lflag, lflag, LocalFlag.ECHOKE, ECHOKE); addFlag(c_lflag, lflag, LocalFlag.ECHOE, ECHOE); addFlag(c_lflag, lflag, LocalFlag.ECHOK, ECHOK); addFlag(c_lflag, lflag, LocalFlag.ECHO, ECHO); addFlag(c_lflag, lflag, LocalFlag.ECHONL, ECHONL); addFlag(c_lflag, lflag, LocalFlag.ECHOPRT, ECHOPRT); addFlag(c_lflag, lflag, LocalFlag.ECHOCTL, ECHOCTL); addFlag(c_lflag, lflag, LocalFlag.ISIG, ISIG); addFlag(c_lflag, lflag, LocalFlag.ICANON, ICANON); addFlag(c_lflag, lflag, LocalFlag.IEXTEN, IEXTEN); addFlag(c_lflag, lflag, LocalFlag.EXTPROC, EXTPROC); addFlag(c_lflag, lflag, LocalFlag.TOSTOP, TOSTOP); addFlag(c_lflag, lflag, LocalFlag.FLUSHO, FLUSHO); addFlag(c_lflag, lflag, LocalFlag.PENDIN, PENDIN); addFlag(c_lflag, lflag, LocalFlag.NOFLSH, NOFLSH); // Control chars EnumMap cc = attr.getControlChars(); cc.put(ControlChar.VEOF, (int) c_cc[VEOF]); cc.put(ControlChar.VEOL, (int) c_cc[VEOL]); cc.put(ControlChar.VEOL2, (int) c_cc[VEOL2]); cc.put(ControlChar.VERASE, (int) c_cc[VERASE]); cc.put(ControlChar.VWERASE, (int) c_cc[VWERASE]); cc.put(ControlChar.VKILL, (int) c_cc[VKILL]); cc.put(ControlChar.VREPRINT, (int) c_cc[VREPRINT]); cc.put(ControlChar.VINTR, (int) c_cc[VINTR]); cc.put(ControlChar.VQUIT, (int) c_cc[VQUIT]); cc.put(ControlChar.VSUSP, (int) c_cc[VSUSP]); cc.put(ControlChar.VSTART, (int) c_cc[VSTART]); cc.put(ControlChar.VSTOP, (int) c_cc[VSTOP]); cc.put(ControlChar.VLNEXT, (int) c_cc[VLNEXT]); cc.put(ControlChar.VDISCARD, (int) c_cc[VDISCARD]); cc.put(ControlChar.VMIN, (int) c_cc[VMIN]); cc.put(ControlChar.VTIME, (int) c_cc[VTIME]); // Return return attr; } private > void addFlag(int value, EnumSet flags, T flag, int v) { if ((value & v) != 0) { flags.add(flag); } } } // CONSTANTS int TIOCGWINSZ = 0x40087468; int TIOCSWINSZ = 0x80087467; int VEOF = 0; int VEOL = 1; int VEOL2 = 2; int VERASE = 3; int VWERASE = 4; int VKILL = 5; int VREPRINT = 6; int VERASE2 = 7; int VINTR = 8; int VQUIT = 9; int VSUSP = 10; int VDSUSP = 11; int VSTART = 12; int VSTOP = 13; int VLNEXT = 14; int VDISCARD = 15; int VMIN = 16; int VTIME = 17; int VSTATUS = 18; int IGNBRK = 0x0000001; int BRKINT = 0x0000002; int IGNPAR = 0x0000004; int PARMRK = 0x0000008; int INPCK = 0x0000010; int ISTRIP = 0x0000020; int INLCR = 0x0000040; int IGNCR = 0x0000080; int ICRNL = 0x0000100; int IXON = 0x0000200; int IXOFF = 0x0000400; int IXANY = 0x0000800; int IMAXBEL = 0x0002000; int OPOST = 0x0000001; int ONLCR = 0x0000002; int TABDLY = 0x0000004; int TAB0 = 0x0000000; int TAB3 = 0x0000004; int ONOEOT = 0x0000008; int OCRNL = 0x0000010; int ONLRET = 0x0000040; int CIGNORE = 0x0000001; int CSIZE = 0x0000300; int CS5 = 0x0000000; int CS6 = 0x0000100; int CS7 = 0x0000200; int CS8 = 0x0000300; int CSTOPB = 0x0000400; int CREAD = 0x0000800; int PARENB = 0x0001000; int PARODD = 0x0002000; int HUPCL = 0x0004000; int CLOCAL = 0x0008000; int ECHOKE = 0x0000001; int ECHOE = 0x0000002; int ECHOK = 0x0000004; int ECHO = 0x0000008; int ECHONL = 0x0000010; int ECHOPRT = 0x0000020; int ECHOCTL = 0x0000040; int ISIG = 0x0000080; int ICANON = 0x0000100; int ALTWERASE = 0x000200; int IEXTEN = 0x0000400; int EXTPROC = 0x0000800; int TOSTOP = 0x0400000; int FLUSHO = 0x0800000; int PENDIN = 0x2000000; int NOFLSH = 0x8000000; int TCSANOW = 0x0; int TCSADRAIN = 0x1; int TCSAFLUSH = 0x2; } FreeBsdNativePty.java000066400000000000000000000064101311544710100336040ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/freebsd/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.freebsd; import java.io.FileDescriptor; import java.io.IOException; import com.sun.jna.LastErrorException; import com.sun.jna.Native; import com.sun.jna.Platform; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.impl.jna.JnaNativePty; import static org.jline.terminal.impl.jna.freebsd.CLibrary.TCSANOW; import static org.jline.terminal.impl.jna.freebsd.CLibrary.TIOCGWINSZ; import static org.jline.terminal.impl.jna.freebsd.CLibrary.TIOCSWINSZ; import static org.jline.terminal.impl.jna.freebsd.CLibrary.termios; import static org.jline.terminal.impl.jna.freebsd.CLibrary.winsize; public class FreeBsdNativePty extends JnaNativePty { private static final CLibrary C_LIBRARY = (CLibrary) Native.loadLibrary(Platform.C_LIBRARY_NAME, CLibrary.class); public interface UtilLibrary extends com.sun.jna.Library { void openpty(int[] master, int[] slave, byte[] name, CLibrary.termios t, CLibrary.winsize s) throws LastErrorException; UtilLibrary INSTANCE = (UtilLibrary) Native.loadLibrary("util", UtilLibrary.class); } public static FreeBsdNativePty current() throws IOException { int slave = 0; byte[] buf = new byte[64]; C_LIBRARY.ttyname_r(slave, buf, buf.length); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new FreeBsdNativePty(-1, null, slave, FileDescriptor.in, name); } public static FreeBsdNativePty open(Attributes attr, Size size) throws IOException { int[] master = new int[1]; int[] slave = new int[1]; byte[] buf = new byte[64]; UtilLibrary.INSTANCE.openpty(master, slave, buf, attr != null ? new termios(attr) : null, size != null ? new winsize(size) : null); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new FreeBsdNativePty(master[0], newDescriptor(master[0]), slave[0], newDescriptor(slave[0]), name); } public FreeBsdNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { super(master, masterFD, slave, slaveFD, name); } @Override public Attributes getAttr() throws IOException { termios termios = new termios(); C_LIBRARY.tcgetattr(getSlave(), termios); return termios.toAttributes(); } @Override public void setAttr(Attributes attr) throws IOException { termios termios = new termios(attr); C_LIBRARY.tcsetattr(getSlave(), TCSANOW, termios); } @Override public Size getSize() throws IOException { winsize sz = new winsize(); C_LIBRARY.ioctl(getSlave(), TIOCGWINSZ, sz); return sz.toSize(); } @Override public void setSize(Size size) throws IOException { winsize sz = new winsize(size); C_LIBRARY.ioctl(getSlave(), TIOCSWINSZ, sz); } } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/linux/000077500000000000000000000000001311544710100273665ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/linux/CLibrary.java000066400000000000000000000415151311544710100317460ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.linux; import java.util.Arrays; import java.util.EnumMap; import java.util.EnumSet; import java.util.List; import com.sun.jna.LastErrorException; import com.sun.jna.Structure; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.ControlFlag; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Attributes.OutputFlag; import org.jline.terminal.Size; public interface CLibrary extends com.sun.jna.Library { void tcgetattr(int fd, termios termios) throws LastErrorException; void tcsetattr(int fd, int cmd, termios termios) throws LastErrorException; void ioctl(int fd, int cmd, winsize data) throws LastErrorException; void ttyname_r(int fd, byte[] buf, int len) throws LastErrorException; class winsize extends Structure { public short ws_row; public short ws_col; public short ws_xpixel; public short ws_ypixel; public winsize() { } public winsize(Size ws) { ws_row = (short) ws.getRows(); ws_col = (short) ws.getColumns(); } public Size toSize() { return new Size(ws_col, ws_row); } @Override protected List getFieldOrder() { return Arrays.asList(// "ws_row",// "ws_col",// "ws_xpixel",// "ws_ypixel"// ); } } class termios extends Structure { public int c_iflag; public int c_oflag; public int c_cflag; public int c_lflag; public byte c_line; public byte[] c_cc = new byte[32]; public int c_ispeed; public int c_ospeed; @Override protected List getFieldOrder() { return Arrays.asList(// "c_iflag",// "c_oflag",// "c_cflag",// "c_lflag",// "c_line",// "c_cc",// "c_ispeed",// "c_ospeed"// ); } public termios() { } public termios(Attributes t) { // Input flags c_iflag = setFlag(t.getInputFlag(InputFlag.IGNBRK), IGNBRK, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.BRKINT), BRKINT, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IGNPAR), IGNPAR, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.PARMRK), PARMRK, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.INPCK), INPCK, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.ISTRIP), ISTRIP, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.INLCR), INLCR, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IGNCR), IGNCR, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.ICRNL), ICRNL, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IXON), IXON, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IXOFF), IXOFF, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IXANY), IXANY, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IMAXBEL), IMAXBEL, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IUTF8), IUTF8, c_iflag); // Output flags c_oflag = setFlag(t.getOutputFlag(OutputFlag.OPOST), OPOST, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.ONLCR), ONLCR, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.OCRNL), OCRNL, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.ONOCR), ONOCR, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.ONLRET), ONLRET, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.OFILL), OFILL, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.NLDLY), NLDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.TABDLY), TABDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.CRDLY), CRDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.FFDLY), FFDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.BSDLY), BSDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.VTDLY), VTDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.OFDEL), OFDEL, c_oflag); // Control flags c_cflag = setFlag(t.getControlFlag(ControlFlag.CS5), CS5, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CS6), CS6, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CS7), CS7, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CS8), CS8, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CSTOPB), CSTOPB, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CREAD), CREAD, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.PARENB), PARENB, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.PARODD), PARODD, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.HUPCL), HUPCL, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CLOCAL), CLOCAL, c_cflag); // Local flags c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOKE), ECHOKE, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOE), ECHOE, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOK), ECHOK, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHO), ECHO, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHONL), ECHONL, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOPRT), ECHOPRT, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOCTL), ECHOCTL, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ISIG), ISIG, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ICANON), ICANON, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.EXTPROC), EXTPROC, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.TOSTOP), TOSTOP, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.FLUSHO), FLUSHO, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.NOFLSH), NOFLSH, c_lflag); // Control chars c_cc[VEOF] = (byte) t.getControlChar(ControlChar.VEOF); c_cc[VEOL] = (byte) t.getControlChar(ControlChar.VEOL); c_cc[VEOL2] = (byte) t.getControlChar(ControlChar.VEOL2); c_cc[VERASE] = (byte) t.getControlChar(ControlChar.VERASE); c_cc[VWERASE] = (byte) t.getControlChar(ControlChar.VWERASE); c_cc[VKILL] = (byte) t.getControlChar(ControlChar.VKILL); c_cc[VREPRINT] = (byte) t.getControlChar(ControlChar.VREPRINT); c_cc[VINTR] = (byte) t.getControlChar(ControlChar.VINTR); c_cc[VQUIT] = (byte) t.getControlChar(ControlChar.VQUIT); c_cc[VSUSP] = (byte) t.getControlChar(ControlChar.VSUSP); c_cc[VSTART] = (byte) t.getControlChar(ControlChar.VSTART); c_cc[VSTOP] = (byte) t.getControlChar(ControlChar.VSTOP); c_cc[VLNEXT] = (byte) t.getControlChar(ControlChar.VLNEXT); c_cc[VDISCARD] = (byte) t.getControlChar(ControlChar.VDISCARD); c_cc[VMIN] = (byte) t.getControlChar(ControlChar.VMIN); c_cc[VTIME] = (byte) t.getControlChar(ControlChar.VTIME); } private int setFlag(boolean flag, int value, int org) { return flag ? (org | value) : org; } public Attributes toAttributes() { Attributes attr = new Attributes(); // Input flags EnumSet iflag = attr.getInputFlags(); addFlag(c_iflag, iflag, InputFlag.IGNBRK, IGNBRK); addFlag(c_iflag, iflag, InputFlag.IGNBRK, IGNBRK); addFlag(c_iflag, iflag, InputFlag.BRKINT, BRKINT); addFlag(c_iflag, iflag, InputFlag.IGNPAR, IGNPAR); addFlag(c_iflag, iflag, InputFlag.PARMRK, PARMRK); addFlag(c_iflag, iflag, InputFlag.INPCK, INPCK); addFlag(c_iflag, iflag, InputFlag.ISTRIP, ISTRIP); addFlag(c_iflag, iflag, InputFlag.INLCR, INLCR); addFlag(c_iflag, iflag, InputFlag.IGNCR, IGNCR); addFlag(c_iflag, iflag, InputFlag.ICRNL, ICRNL); addFlag(c_iflag, iflag, InputFlag.IXON, IXON); addFlag(c_iflag, iflag, InputFlag.IXOFF, IXOFF); addFlag(c_iflag, iflag, InputFlag.IXANY, IXANY); addFlag(c_iflag, iflag, InputFlag.IMAXBEL, IMAXBEL); addFlag(c_iflag, iflag, InputFlag.IUTF8, IUTF8); // Output flags EnumSet oflag = attr.getOutputFlags(); addFlag(c_oflag, oflag, OutputFlag.OPOST, OPOST); addFlag(c_oflag, oflag, OutputFlag.ONLCR, ONLCR); addFlag(c_oflag, oflag, OutputFlag.OCRNL, OCRNL); addFlag(c_oflag, oflag, OutputFlag.ONOCR, ONOCR); addFlag(c_oflag, oflag, OutputFlag.ONLRET, ONLRET); addFlag(c_oflag, oflag, OutputFlag.OFILL, OFILL); addFlag(c_oflag, oflag, OutputFlag.NLDLY, NLDLY); addFlag(c_oflag, oflag, OutputFlag.TABDLY, TABDLY); addFlag(c_oflag, oflag, OutputFlag.CRDLY, CRDLY); addFlag(c_oflag, oflag, OutputFlag.FFDLY, FFDLY); addFlag(c_oflag, oflag, OutputFlag.BSDLY, BSDLY); addFlag(c_oflag, oflag, OutputFlag.VTDLY, VTDLY); addFlag(c_oflag, oflag, OutputFlag.OFDEL, OFDEL); // Control flags EnumSet cflag = attr.getControlFlags(); addFlag(c_cflag, cflag, ControlFlag.CS5, CS5); addFlag(c_cflag, cflag, ControlFlag.CS6, CS6); addFlag(c_cflag, cflag, ControlFlag.CS7, CS7); addFlag(c_cflag, cflag, ControlFlag.CS8, CS8); addFlag(c_cflag, cflag, ControlFlag.CSTOPB, CSTOPB); addFlag(c_cflag, cflag, ControlFlag.CREAD, CREAD); addFlag(c_cflag, cflag, ControlFlag.PARENB, PARENB); addFlag(c_cflag, cflag, ControlFlag.PARODD, PARODD); addFlag(c_cflag, cflag, ControlFlag.HUPCL, HUPCL); addFlag(c_cflag, cflag, ControlFlag.CLOCAL, CLOCAL); // Local flags EnumSet lflag = attr.getLocalFlags(); addFlag(c_lflag, lflag, LocalFlag.ECHOKE, ECHOKE); addFlag(c_lflag, lflag, LocalFlag.ECHOE, ECHOE); addFlag(c_lflag, lflag, LocalFlag.ECHOK, ECHOK); addFlag(c_lflag, lflag, LocalFlag.ECHO, ECHO); addFlag(c_lflag, lflag, LocalFlag.ECHONL, ECHONL); addFlag(c_lflag, lflag, LocalFlag.ECHOPRT, ECHOPRT); addFlag(c_lflag, lflag, LocalFlag.ECHOCTL, ECHOCTL); addFlag(c_lflag, lflag, LocalFlag.ISIG, ISIG); addFlag(c_lflag, lflag, LocalFlag.ICANON, ICANON); addFlag(c_lflag, lflag, LocalFlag.EXTPROC, EXTPROC); addFlag(c_lflag, lflag, LocalFlag.TOSTOP, TOSTOP); addFlag(c_lflag, lflag, LocalFlag.FLUSHO, FLUSHO); addFlag(c_lflag, lflag, LocalFlag.NOFLSH, NOFLSH); // Control chars EnumMap cc = attr.getControlChars(); cc.put(ControlChar.VEOF, (int) c_cc[VEOF]); cc.put(ControlChar.VEOL, (int) c_cc[VEOL]); cc.put(ControlChar.VEOL2, (int) c_cc[VEOL2]); cc.put(ControlChar.VERASE, (int) c_cc[VERASE]); cc.put(ControlChar.VWERASE, (int) c_cc[VWERASE]); cc.put(ControlChar.VKILL, (int) c_cc[VKILL]); cc.put(ControlChar.VREPRINT, (int) c_cc[VREPRINT]); cc.put(ControlChar.VINTR, (int) c_cc[VINTR]); cc.put(ControlChar.VQUIT, (int) c_cc[VQUIT]); cc.put(ControlChar.VSUSP, (int) c_cc[VSUSP]); cc.put(ControlChar.VSTART, (int) c_cc[VSTART]); cc.put(ControlChar.VSTOP, (int) c_cc[VSTOP]); cc.put(ControlChar.VLNEXT, (int) c_cc[VLNEXT]); cc.put(ControlChar.VDISCARD, (int) c_cc[VDISCARD]); cc.put(ControlChar.VMIN, (int) c_cc[VMIN]); cc.put(ControlChar.VTIME, (int) c_cc[VTIME]); // Return return attr; } private > void addFlag(int value, EnumSet flags, T flag, int v) { if ((value & v) != 0) { flags.add(flag); } } } // CONSTANTS int TIOCGWINSZ = 0x00005413; int TIOCSWINSZ = 0x00005414; int VINTR = 0; int VQUIT = 1; int VERASE = 2; int VKILL = 3; int VEOF = 4; int VTIME = 5; int VMIN = 6; int VSWTC = 7; int VSTART = 8; int VSTOP = 9; int VSUSP = 10; int VEOL = 11; int VREPRINT = 12; int VDISCARD = 13; int VWERASE = 14; int VLNEXT = 15; int VEOL2 = 16; int IGNBRK = 0x0000001; int BRKINT = 0x0000002; int IGNPAR = 0x0000004; int PARMRK = 0x0000008; int INPCK = 0x0000010; int ISTRIP = 0x0000020; int INLCR = 0x0000040; int IGNCR = 0x0000080; int ICRNL = 0x0000100; int IUCLC = 0x0000200; int IXON = 0x0000400; int IXANY = 0x0000800; int IXOFF = 0x0001000; int IMAXBEL = 0x0002000; int IUTF8 = 0x0004000; int OPOST = 0x0000001; int OLCUC = 0x0000002; int ONLCR = 0x0000004; int OCRNL = 0x0000008; int ONOCR = 0x0000010; int ONLRET = 0x0000020; int OFILL = 0x0000040; int OFDEL = 0x0000080; int NLDLY = 0x0000100; int NL0 = 0x0000000; int NL1 = 0x0000100; int CRDLY = 0x0000600; int CR0 = 0x0000000; int CR1 = 0x0000200; int CR2 = 0x0000400; int CR3 = 0x0000600; int TABDLY = 0x0001800; int TAB0 = 0x0000000; int TAB1 = 0x0000800; int TAB2 = 0x0001000; int TAB3 = 0x0001800; int XTABS = 0x0001800; int BSDLY = 0x0002000; int BS0 = 0x0000000; int BS1 = 0x0002000; int VTDLY = 0x0004000; int VT0 = 0x0000000; int VT1 = 0x0004000; int FFDLY = 0x0008000; int FF0 = 0x0000000; int FF1 = 0x0008000; int CBAUD = 0x000100f; int B0 = 0x0000000; int B50 = 0x0000001; int B75 = 0x0000002; int B110 = 0x0000003; int B134 = 0x0000004; int B150 = 0x0000005; int B200 = 0x0000006; int B300 = 0x0000007; int B600 = 0x0000008; int B1200 = 0x0000009; int B1800 = 0x000000a; int B2400 = 0x000000b; int B4800 = 0x000000c; int B9600 = 0x000000d; int B19200 = 0x000000e; int B38400 = 0x000000f; int EXTA = B19200; int EXTB = B38400; int CSIZE = 0x0000030; int CS5 = 0x0000000; int CS6 = 0x0000010; int CS7 = 0x0000020; int CS8 = 0x0000030; int CSTOPB = 0x0000040; int CREAD = 0x0000080; int PARENB = 0x0000100; int PARODD = 0x0000200; int HUPCL = 0x0000400; int CLOCAL = 0x0000800; int ISIG = 0x0000001; int ICANON = 0x0000002; int XCASE = 0x0000004; int ECHO = 0x0000008; int ECHOE = 0x0000010; int ECHOK = 0x0000020; int ECHONL = 0x0000040; int NOFLSH = 0x0000080; int TOSTOP = 0x0000100; int ECHOCTL = 0x0000200; int ECHOPRT = 0x0000400; int ECHOKE = 0x0000800; int FLUSHO = 0x0001000; int PENDIN = 0x0002000; int IEXTEN = 0x0008000; int EXTPROC = 0x0010000; int TCSANOW = 0x0; int TCSADRAIN = 0x1; int TCSAFLUSH = 0x2; } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/linux/LinuxNativePty.java000066400000000000000000000070051311544710100331760ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.linux; import java.io.FileDescriptor; import java.io.IOException; import com.sun.jna.LastErrorException; import com.sun.jna.Native; import com.sun.jna.Platform; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.impl.jna.JnaNativePty; import static org.jline.terminal.impl.jna.linux.CLibrary.TCSADRAIN; import static org.jline.terminal.impl.jna.linux.CLibrary.TIOCGWINSZ; import static org.jline.terminal.impl.jna.linux.CLibrary.TIOCSWINSZ; import static org.jline.terminal.impl.jna.linux.CLibrary.termios; import static org.jline.terminal.impl.jna.linux.CLibrary.winsize; public class LinuxNativePty extends JnaNativePty { private static final CLibrary C_LIBRARY = (CLibrary) Native.loadLibrary(Platform.C_LIBRARY_NAME, CLibrary.class); public interface UtilLibrary extends com.sun.jna.Library { void openpty(int[] master, int[] slave, byte[] name, CLibrary.termios t, CLibrary.winsize s) throws LastErrorException; UtilLibrary INSTANCE = (UtilLibrary) Native.loadLibrary("util", UtilLibrary.class); } public static LinuxNativePty current() throws IOException { int slave = 0; byte[] buf = new byte[64]; C_LIBRARY.ttyname_r(slave, buf, buf.length); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new LinuxNativePty(-1, null, slave, FileDescriptor.in, name); } public static LinuxNativePty open(Attributes attr, Size size) throws IOException { int[] master = new int[1]; int[] slave = new int[1]; byte[] buf = new byte[64]; UtilLibrary.INSTANCE.openpty(master, slave, buf, attr != null ? new termios(attr) : null, size != null ? new winsize(size) : null); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new LinuxNativePty(master[0], newDescriptor(master[0]), slave[0], newDescriptor(slave[0]), name); } public LinuxNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { super(master, masterFD, slave, slaveFD, name); } @Override public Attributes getAttr() throws IOException { termios termios = new termios(); C_LIBRARY.tcgetattr(getSlave(), termios); return termios.toAttributes(); } @Override public void setAttr(Attributes attr) throws IOException { termios termios = new termios(attr); termios org = new termios(); C_LIBRARY.tcgetattr(getSlave(), org); org.c_iflag = termios.c_iflag; org.c_oflag = termios.c_oflag; org.c_lflag = termios.c_lflag; System.arraycopy(termios.c_cc, 0, org.c_cc, 0, termios.c_cc.length); C_LIBRARY.tcsetattr(getSlave(), TCSADRAIN, org); } @Override public Size getSize() throws IOException { winsize sz = new winsize(); C_LIBRARY.ioctl(getSlave(), TIOCGWINSZ, sz); return sz.toSize(); } @Override public void setSize(Size size) throws IOException { winsize sz = new winsize(size); C_LIBRARY.ioctl(getSlave(), TIOCSWINSZ, sz); } } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/osx/000077500000000000000000000000001311544710100270405ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/osx/CLibrary.java000066400000000000000000000452511311544710100314210ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.osx; import java.util.Arrays; import java.util.EnumMap; import java.util.EnumSet; import java.util.List; import com.sun.jna.LastErrorException; import com.sun.jna.NativeLong; import com.sun.jna.Structure; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.ControlFlag; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Attributes.OutputFlag; import org.jline.terminal.Size; public interface CLibrary extends com.sun.jna.Library { void tcgetattr(int fd, termios termios) throws LastErrorException; void tcsetattr(int fd, int cmd, termios termios) throws LastErrorException; void ioctl(int fd, NativeLong cmd, winsize data) throws LastErrorException; void ttyname_r(int fd, byte[] buf, int len) throws LastErrorException; void openpty(int[] master, int[] slave, byte[] name, termios t, winsize s) throws LastErrorException; class winsize extends Structure { public short ws_row; public short ws_col; public short ws_xpixel; public short ws_ypixel; public winsize() { } public winsize(Size ws) { ws_row = (short) ws.getRows(); ws_col = (short) ws.getColumns(); } public Size toSize() { return new Size(ws_col, ws_row); } @Override protected List getFieldOrder() { return Arrays.asList(// "ws_row",// "ws_col",// "ws_xpixel",// "ws_ypixel"// ); } } class termios extends Structure { public NativeLong c_iflag; public NativeLong c_oflag; public NativeLong c_cflag; public NativeLong c_lflag; public byte[] c_cc = new byte[20]; public NativeLong c_ispeed; public NativeLong c_ospeed; @Override protected List getFieldOrder() { return Arrays.asList(// "c_iflag",// "c_oflag",// "c_cflag",// "c_lflag",// "c_cc",// "c_ispeed",// "c_ospeed"// ); } public termios() { } public termios(Attributes t) { // Input flags setFlag(t.getInputFlag(InputFlag.IGNBRK), IGNBRK, c_iflag); setFlag(t.getInputFlag(InputFlag.BRKINT), BRKINT, c_iflag); setFlag(t.getInputFlag(InputFlag.IGNPAR), IGNPAR, c_iflag); setFlag(t.getInputFlag(InputFlag.PARMRK), PARMRK, c_iflag); setFlag(t.getInputFlag(InputFlag.INPCK), INPCK, c_iflag); setFlag(t.getInputFlag(InputFlag.ISTRIP), ISTRIP, c_iflag); setFlag(t.getInputFlag(InputFlag.INLCR), INLCR, c_iflag); setFlag(t.getInputFlag(InputFlag.IGNCR), IGNCR, c_iflag); setFlag(t.getInputFlag(InputFlag.ICRNL), ICRNL, c_iflag); setFlag(t.getInputFlag(InputFlag.IXON), IXON, c_iflag); setFlag(t.getInputFlag(InputFlag.IXOFF), IXOFF, c_iflag); setFlag(t.getInputFlag(InputFlag.IXANY), IXANY, c_iflag); setFlag(t.getInputFlag(InputFlag.IMAXBEL), IMAXBEL, c_iflag); setFlag(t.getInputFlag(InputFlag.IUTF8), IUTF8, c_iflag); // Output flags setFlag(t.getOutputFlag(OutputFlag.OPOST), OPOST, c_oflag); setFlag(t.getOutputFlag(OutputFlag.ONLCR), ONLCR, c_oflag); setFlag(t.getOutputFlag(OutputFlag.OXTABS), OXTABS, c_oflag); setFlag(t.getOutputFlag(OutputFlag.ONOEOT), ONOEOT, c_oflag); setFlag(t.getOutputFlag(OutputFlag.OCRNL), OCRNL, c_oflag); setFlag(t.getOutputFlag(OutputFlag.ONOCR), ONOCR, c_oflag); setFlag(t.getOutputFlag(OutputFlag.ONLRET), ONLRET, c_oflag); setFlag(t.getOutputFlag(OutputFlag.OFILL), OFILL, c_oflag); setFlag(t.getOutputFlag(OutputFlag.NLDLY), NLDLY, c_oflag); setFlag(t.getOutputFlag(OutputFlag.TABDLY), TABDLY, c_oflag); setFlag(t.getOutputFlag(OutputFlag.CRDLY), CRDLY, c_oflag); setFlag(t.getOutputFlag(OutputFlag.FFDLY), FFDLY, c_oflag); setFlag(t.getOutputFlag(OutputFlag.BSDLY), BSDLY, c_oflag); setFlag(t.getOutputFlag(OutputFlag.VTDLY), VTDLY, c_oflag); setFlag(t.getOutputFlag(OutputFlag.OFDEL), OFDEL, c_oflag); // Control flags setFlag(t.getControlFlag(ControlFlag.CIGNORE), CIGNORE, c_cflag); setFlag(t.getControlFlag(ControlFlag.CS5), CS5, c_cflag); setFlag(t.getControlFlag(ControlFlag.CS6), CS6, c_cflag); setFlag(t.getControlFlag(ControlFlag.CS7), CS7, c_cflag); setFlag(t.getControlFlag(ControlFlag.CS8), CS8, c_cflag); setFlag(t.getControlFlag(ControlFlag.CSTOPB), CSTOPB, c_cflag); setFlag(t.getControlFlag(ControlFlag.CREAD), CREAD, c_cflag); setFlag(t.getControlFlag(ControlFlag.PARENB), PARENB, c_cflag); setFlag(t.getControlFlag(ControlFlag.PARODD), PARODD, c_cflag); setFlag(t.getControlFlag(ControlFlag.HUPCL), HUPCL, c_cflag); setFlag(t.getControlFlag(ControlFlag.CLOCAL), CLOCAL, c_cflag); setFlag(t.getControlFlag(ControlFlag.CCTS_OFLOW), CCTS_OFLOW, c_cflag); setFlag(t.getControlFlag(ControlFlag.CRTS_IFLOW), CRTS_IFLOW, c_cflag); setFlag(t.getControlFlag(ControlFlag.CDTR_IFLOW), CDTR_IFLOW, c_cflag); setFlag(t.getControlFlag(ControlFlag.CDSR_OFLOW), CDSR_OFLOW, c_cflag); setFlag(t.getControlFlag(ControlFlag.CCAR_OFLOW), CCAR_OFLOW, c_cflag); // Local flags setFlag(t.getLocalFlag(LocalFlag.ECHOKE), ECHOKE, c_lflag); setFlag(t.getLocalFlag(LocalFlag.ECHOE), ECHOE, c_lflag); setFlag(t.getLocalFlag(LocalFlag.ECHOK), ECHOK, c_lflag); setFlag(t.getLocalFlag(LocalFlag.ECHO), ECHO, c_lflag); setFlag(t.getLocalFlag(LocalFlag.ECHONL), ECHONL, c_lflag); setFlag(t.getLocalFlag(LocalFlag.ECHOPRT), ECHOPRT, c_lflag); setFlag(t.getLocalFlag(LocalFlag.ECHOCTL), ECHOCTL, c_lflag); setFlag(t.getLocalFlag(LocalFlag.ISIG), ISIG, c_lflag); setFlag(t.getLocalFlag(LocalFlag.ICANON), ICANON, c_lflag); setFlag(t.getLocalFlag(LocalFlag.ALTWERASE), ALTWERASE, c_lflag); setFlag(t.getLocalFlag(LocalFlag.IEXTEN), IEXTEN, c_lflag); setFlag(t.getLocalFlag(LocalFlag.EXTPROC), EXTPROC, c_lflag); setFlag(t.getLocalFlag(LocalFlag.TOSTOP), TOSTOP, c_lflag); setFlag(t.getLocalFlag(LocalFlag.FLUSHO), FLUSHO, c_lflag); setFlag(t.getLocalFlag(LocalFlag.NOKERNINFO), NOKERNINFO, c_lflag); setFlag(t.getLocalFlag(LocalFlag.PENDIN), PENDIN, c_lflag); setFlag(t.getLocalFlag(LocalFlag.NOFLSH), NOFLSH, c_lflag); // Control chars c_cc[VEOF] = (byte) t.getControlChar(ControlChar.VEOF); c_cc[VEOL] = (byte) t.getControlChar(ControlChar.VEOL); c_cc[VEOL2] = (byte) t.getControlChar(ControlChar.VEOL2); c_cc[VERASE] = (byte) t.getControlChar(ControlChar.VERASE); c_cc[VWERASE] = (byte) t.getControlChar(ControlChar.VWERASE); c_cc[VKILL] = (byte) t.getControlChar(ControlChar.VKILL); c_cc[VREPRINT] = (byte) t.getControlChar(ControlChar.VREPRINT); c_cc[VINTR] = (byte) t.getControlChar(ControlChar.VINTR); c_cc[VQUIT] = (byte) t.getControlChar(ControlChar.VQUIT); c_cc[VSUSP] = (byte) t.getControlChar(ControlChar.VSUSP); c_cc[VDSUSP] = (byte) t.getControlChar(ControlChar.VDSUSP); c_cc[VSTART] = (byte) t.getControlChar(ControlChar.VSTART); c_cc[VSTOP] = (byte) t.getControlChar(ControlChar.VSTOP); c_cc[VLNEXT] = (byte) t.getControlChar(ControlChar.VLNEXT); c_cc[VDISCARD] = (byte) t.getControlChar(ControlChar.VDISCARD); c_cc[VMIN] = (byte) t.getControlChar(ControlChar.VMIN); c_cc[VTIME] = (byte) t.getControlChar(ControlChar.VTIME); c_cc[VSTATUS] = (byte) t.getControlChar(ControlChar.VSTATUS); } private void setFlag(boolean flag, long value, NativeLong org) { org.setValue(flag ? org.longValue() | value : org.longValue()); } public Attributes toAttributes() { Attributes attr = new Attributes(); // Input flags EnumSet iflag = attr.getInputFlags(); addFlag(c_iflag.longValue(), iflag, InputFlag.IGNBRK, IGNBRK); addFlag(c_iflag.longValue(), iflag, InputFlag.IGNBRK, IGNBRK); addFlag(c_iflag.longValue(), iflag, InputFlag.BRKINT, BRKINT); addFlag(c_iflag.longValue(), iflag, InputFlag.IGNPAR, IGNPAR); addFlag(c_iflag.longValue(), iflag, InputFlag.PARMRK, PARMRK); addFlag(c_iflag.longValue(), iflag, InputFlag.INPCK, INPCK); addFlag(c_iflag.longValue(), iflag, InputFlag.ISTRIP, ISTRIP); addFlag(c_iflag.longValue(), iflag, InputFlag.INLCR, INLCR); addFlag(c_iflag.longValue(), iflag, InputFlag.IGNCR, IGNCR); addFlag(c_iflag.longValue(), iflag, InputFlag.ICRNL, ICRNL); addFlag(c_iflag.longValue(), iflag, InputFlag.IXON, IXON); addFlag(c_iflag.longValue(), iflag, InputFlag.IXOFF, IXOFF); addFlag(c_iflag.longValue(), iflag, InputFlag.IXANY, IXANY); addFlag(c_iflag.longValue(), iflag, InputFlag.IMAXBEL, IMAXBEL); addFlag(c_iflag.longValue(), iflag, InputFlag.IUTF8, IUTF8); // Output flags EnumSet oflag = attr.getOutputFlags(); addFlag(c_oflag.longValue(), oflag, OutputFlag.OPOST, OPOST); addFlag(c_oflag.longValue(), oflag, OutputFlag.ONLCR, ONLCR); addFlag(c_oflag.longValue(), oflag, OutputFlag.OXTABS, OXTABS); addFlag(c_oflag.longValue(), oflag, OutputFlag.ONOEOT, ONOEOT); addFlag(c_oflag.longValue(), oflag, OutputFlag.OCRNL, OCRNL); addFlag(c_oflag.longValue(), oflag, OutputFlag.ONOCR, ONOCR); addFlag(c_oflag.longValue(), oflag, OutputFlag.ONLRET, ONLRET); addFlag(c_oflag.longValue(), oflag, OutputFlag.OFILL, OFILL); addFlag(c_oflag.longValue(), oflag, OutputFlag.NLDLY, NLDLY); addFlag(c_oflag.longValue(), oflag, OutputFlag.TABDLY, TABDLY); addFlag(c_oflag.longValue(), oflag, OutputFlag.CRDLY, CRDLY); addFlag(c_oflag.longValue(), oflag, OutputFlag.FFDLY, FFDLY); addFlag(c_oflag.longValue(), oflag, OutputFlag.BSDLY, BSDLY); addFlag(c_oflag.longValue(), oflag, OutputFlag.VTDLY, VTDLY); addFlag(c_oflag.longValue(), oflag, OutputFlag.OFDEL, OFDEL); // Control flags EnumSet cflag = attr.getControlFlags(); addFlag(c_cflag.longValue(), cflag, ControlFlag.CIGNORE, CIGNORE); addFlag(c_cflag.longValue(), cflag, ControlFlag.CS5, CS5); addFlag(c_cflag.longValue(), cflag, ControlFlag.CS6, CS6); addFlag(c_cflag.longValue(), cflag, ControlFlag.CS7, CS7); addFlag(c_cflag.longValue(), cflag, ControlFlag.CS8, CS8); addFlag(c_cflag.longValue(), cflag, ControlFlag.CSTOPB, CSTOPB); addFlag(c_cflag.longValue(), cflag, ControlFlag.CREAD, CREAD); addFlag(c_cflag.longValue(), cflag, ControlFlag.PARENB, PARENB); addFlag(c_cflag.longValue(), cflag, ControlFlag.PARODD, PARODD); addFlag(c_cflag.longValue(), cflag, ControlFlag.HUPCL, HUPCL); addFlag(c_cflag.longValue(), cflag, ControlFlag.CLOCAL, CLOCAL); addFlag(c_cflag.longValue(), cflag, ControlFlag.CCTS_OFLOW, CCTS_OFLOW); addFlag(c_cflag.longValue(), cflag, ControlFlag.CRTS_IFLOW, CRTS_IFLOW); addFlag(c_cflag.longValue(), cflag, ControlFlag.CDSR_OFLOW, CDSR_OFLOW); addFlag(c_cflag.longValue(), cflag, ControlFlag.CCAR_OFLOW, CCAR_OFLOW); // Local flags EnumSet lflag = attr.getLocalFlags(); addFlag(c_lflag.longValue(), lflag, LocalFlag.ECHOKE, ECHOKE); addFlag(c_lflag.longValue(), lflag, LocalFlag.ECHOE, ECHOE); addFlag(c_lflag.longValue(), lflag, LocalFlag.ECHOK, ECHOK); addFlag(c_lflag.longValue(), lflag, LocalFlag.ECHO, ECHO); addFlag(c_lflag.longValue(), lflag, LocalFlag.ECHONL, ECHONL); addFlag(c_lflag.longValue(), lflag, LocalFlag.ECHOPRT, ECHOPRT); addFlag(c_lflag.longValue(), lflag, LocalFlag.ECHOCTL, ECHOCTL); addFlag(c_lflag.longValue(), lflag, LocalFlag.ISIG, ISIG); addFlag(c_lflag.longValue(), lflag, LocalFlag.ICANON, ICANON); addFlag(c_lflag.longValue(), lflag, LocalFlag.ALTWERASE, ALTWERASE); addFlag(c_lflag.longValue(), lflag, LocalFlag.IEXTEN, IEXTEN); addFlag(c_lflag.longValue(), lflag, LocalFlag.EXTPROC, EXTPROC); addFlag(c_lflag.longValue(), lflag, LocalFlag.TOSTOP, TOSTOP); addFlag(c_lflag.longValue(), lflag, LocalFlag.FLUSHO, FLUSHO); addFlag(c_lflag.longValue(), lflag, LocalFlag.NOKERNINFO, NOKERNINFO); addFlag(c_lflag.longValue(), lflag, LocalFlag.PENDIN, PENDIN); addFlag(c_lflag.longValue(), lflag, LocalFlag.NOFLSH, NOFLSH); // Control chars EnumMap cc = attr.getControlChars(); cc.put(ControlChar.VEOF, (int) c_cc[VEOF]); cc.put(ControlChar.VEOL, (int) c_cc[VEOL]); cc.put(ControlChar.VEOL2, (int) c_cc[VEOL2]); cc.put(ControlChar.VERASE, (int) c_cc[VERASE]); cc.put(ControlChar.VWERASE, (int) c_cc[VWERASE]); cc.put(ControlChar.VKILL, (int) c_cc[VKILL]); cc.put(ControlChar.VREPRINT, (int) c_cc[VREPRINT]); cc.put(ControlChar.VINTR, (int) c_cc[VINTR]); cc.put(ControlChar.VQUIT, (int) c_cc[VQUIT]); cc.put(ControlChar.VSUSP, (int) c_cc[VSUSP]); cc.put(ControlChar.VDSUSP, (int) c_cc[VDSUSP]); cc.put(ControlChar.VSTART, (int) c_cc[VSTART]); cc.put(ControlChar.VSTOP, (int) c_cc[VSTOP]); cc.put(ControlChar.VLNEXT, (int) c_cc[VLNEXT]); cc.put(ControlChar.VDISCARD, (int) c_cc[VDISCARD]); cc.put(ControlChar.VMIN, (int) c_cc[VMIN]); cc.put(ControlChar.VTIME, (int) c_cc[VTIME]); cc.put(ControlChar.VSTATUS, (int) c_cc[VSTATUS]); // Return return attr; } private > void addFlag(long value, EnumSet flags, T flag, int v) { if ((value & v) != 0) { flags.add(flag); } } } // CONSTANTS long TIOCGWINSZ = 0x40087468L; long TIOCSWINSZ = 0x80087467L; int TCSANOW = 0x00000000; int VEOF = 0; int VEOL = 1; int VEOL2 = 2; int VERASE = 3; int VWERASE = 4; int VKILL = 5; int VREPRINT = 6; int VINTR = 8; int VQUIT = 9; int VSUSP = 10; int VDSUSP = 11; int VSTART = 12; int VSTOP = 13; int VLNEXT = 14; int VDISCARD = 15; int VMIN = 16; int VTIME = 17; int VSTATUS = 18; int IGNBRK = 0x00000001; int BRKINT = 0x00000002; int IGNPAR = 0x00000004; int PARMRK = 0x00000008; int INPCK = 0x00000010; int ISTRIP = 0x00000020; int INLCR = 0x00000040; int IGNCR = 0x00000080; int ICRNL = 0x00000100; int IXON = 0x00000200; int IXOFF = 0x00000400; int IXANY = 0x00000800; int IMAXBEL = 0x00002000; int IUTF8 = 0x00004000; int OPOST = 0x00000001; int ONLCR = 0x00000002; int OXTABS = 0x00000004; int ONOEOT = 0x00000008; int OCRNL = 0x00000010; int ONOCR = 0x00000020; int ONLRET = 0x00000040; int OFILL = 0x00000080; int NLDLY = 0x00000300; int TABDLY = 0x00000c04; int CRDLY = 0x00003000; int FFDLY = 0x00004000; int BSDLY = 0x00008000; int VTDLY = 0x00010000; int OFDEL = 0x00020000; int CIGNORE = 0x00000001; int CS5 = 0x00000000; int CS6 = 0x00000100; int CS7 = 0x00000200; int CS8 = 0x00000300; int CSTOPB = 0x00000400; int CREAD = 0x00000800; int PARENB = 0x00001000; int PARODD = 0x00002000; int HUPCL = 0x00004000; int CLOCAL = 0x00008000; int CCTS_OFLOW = 0x00010000; int CRTS_IFLOW = 0x00020000; int CDTR_IFLOW = 0x00040000; int CDSR_OFLOW = 0x00080000; int CCAR_OFLOW = 0x00100000; int ECHOKE = 0x00000001; int ECHOE = 0x00000002; int ECHOK = 0x00000004; int ECHO = 0x00000008; int ECHONL = 0x00000010; int ECHOPRT = 0x00000020; int ECHOCTL = 0x00000040; int ISIG = 0x00000080; int ICANON = 0x00000100; int ALTWERASE = 0x00000200; int IEXTEN = 0x00000400; int EXTPROC = 0x00000800; int TOSTOP = 0x00400000; int FLUSHO = 0x00800000; int NOKERNINFO = 0x02000000; int PENDIN = 0x20000000; int NOFLSH = 0x80000000; } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/osx/OsXNativePty.java000066400000000000000000000057011311544710100322630ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.osx; import java.io.FileDescriptor; import java.io.IOException; import com.sun.jna.Native; import com.sun.jna.NativeLong; import com.sun.jna.Platform; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.impl.jna.JnaNativePty; import static org.jline.terminal.impl.jna.osx.CLibrary.TCSANOW; import static org.jline.terminal.impl.jna.osx.CLibrary.TIOCGWINSZ; import static org.jline.terminal.impl.jna.osx.CLibrary.TIOCSWINSZ; import static org.jline.terminal.impl.jna.osx.CLibrary.termios; import static org.jline.terminal.impl.jna.osx.CLibrary.winsize; public class OsXNativePty extends JnaNativePty { private static final CLibrary C_LIBRARY = (CLibrary) Native.loadLibrary(Platform.C_LIBRARY_NAME, CLibrary.class); public static OsXNativePty current() throws IOException { int slave = 0; byte[] buf = new byte[64]; C_LIBRARY.ttyname_r(slave, buf, buf.length); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new OsXNativePty(-1, null, slave, FileDescriptor.in, name); } public static OsXNativePty open(Attributes attr, Size size) throws IOException { int[] master = new int[1]; int[] slave = new int[1]; byte[] buf = new byte[64]; C_LIBRARY.openpty(master, slave, buf, attr != null ? new termios(attr) : null, size != null ? new winsize(size) : null); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new OsXNativePty(master[0], newDescriptor(master[0]), slave[0], newDescriptor(slave[0]), name); } public OsXNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { super(master, masterFD, slave, slaveFD, name); } @Override public Attributes getAttr() throws IOException { termios termios = new termios(); C_LIBRARY.tcgetattr(getSlave(), termios); return termios.toAttributes(); } @Override public void setAttr(Attributes attr) throws IOException { termios termios = new termios(attr); C_LIBRARY.tcsetattr(getSlave(), TCSANOW, termios); } @Override public Size getSize() throws IOException { winsize sz = new winsize(); C_LIBRARY.ioctl(getSlave(), new NativeLong(TIOCGWINSZ), sz); return sz.toSize(); } @Override public void setSize(Size size) throws IOException { winsize sz = new winsize(size); C_LIBRARY.ioctl(getSlave(), new NativeLong(TIOCSWINSZ), sz); } } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/solaris/000077500000000000000000000000001311544710100277035ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/solaris/CLibrary.java000066400000000000000000000422061311544710100322610ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.solaris; import java.util.Arrays; import java.util.EnumMap; import java.util.EnumSet; import java.util.List; import com.sun.jna.LastErrorException; import com.sun.jna.Structure; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.ControlFlag; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Attributes.OutputFlag; import org.jline.terminal.Size; public interface CLibrary extends com.sun.jna.Library { void tcgetattr(int fd, termios termios) throws LastErrorException; void tcsetattr(int fd, int cmd, termios termios) throws LastErrorException; void ioctl(int fd, long cmd, winsize data) throws LastErrorException; // int isatty(int fd); void ttyname_r(int fd, byte[] buf, int len) throws LastErrorException; void openpty(int[] master, int[] slave, byte[] name, termios t, winsize s) throws LastErrorException; class winsize extends Structure { public short ws_row; public short ws_col; public short ws_xpixel; public short ws_ypixel; public winsize() { } public winsize(Size ws) { ws_row = (short) ws.getRows(); ws_col = (short) ws.getColumns(); } public Size toSize() { return new Size(ws_col, ws_row); } @Override protected List getFieldOrder() { return Arrays.asList(// "ws_row",// "ws_col",// "ws_xpixel",// "ws_ypixel"// ); } } class termios extends Structure { public int c_iflag; public int c_oflag; public int c_cflag; public int c_lflag; public byte[] c_cc = new byte[32]; @Override protected List getFieldOrder() { return Arrays.asList(// "c_iflag",// "c_oflag",// "c_cflag",// "c_lflag",// "c_cc"// ); } public termios() { } public termios(Attributes t) { // Input flags c_iflag = setFlag(t.getInputFlag(InputFlag.IGNBRK), IGNBRK, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.BRKINT), BRKINT, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IGNPAR), IGNPAR, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.PARMRK), PARMRK, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.INPCK), INPCK, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.ISTRIP), ISTRIP, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.INLCR), INLCR, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IGNCR), IGNCR, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.ICRNL), ICRNL, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IXON), IXON, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IXOFF), IXOFF, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IXANY), IXANY, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IMAXBEL), IMAXBEL, c_iflag); c_iflag = setFlag(t.getInputFlag(InputFlag.IUTF8), IUTF8, c_iflag); // Output flags c_oflag = setFlag(t.getOutputFlag(OutputFlag.OPOST), OPOST, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.ONLCR), ONLCR, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.OCRNL), OCRNL, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.ONOCR), ONOCR, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.ONLRET), ONLRET, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.OFILL), OFILL, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.NLDLY), NLDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.TABDLY), TABDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.CRDLY), CRDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.FFDLY), FFDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.BSDLY), BSDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.VTDLY), VTDLY, c_oflag); c_oflag = setFlag(t.getOutputFlag(OutputFlag.OFDEL), OFDEL, c_oflag); // Control flags c_cflag = setFlag(t.getControlFlag(ControlFlag.CS5), CS5, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CS6), CS6, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CS7), CS7, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CS8), CS8, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CSTOPB), CSTOPB, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CREAD), CREAD, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.PARENB), PARENB, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.PARODD), PARODD, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.HUPCL), HUPCL, c_cflag); c_cflag = setFlag(t.getControlFlag(ControlFlag.CLOCAL), CLOCAL, c_cflag); // Local flags c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOKE), ECHOKE, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOE), ECHOE, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOK), ECHOK, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHO), ECHO, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHONL), ECHONL, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOPRT), ECHOPRT, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ECHOCTL), ECHOCTL, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ISIG), ISIG, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.ICANON), ICANON, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.IEXTEN), IEXTEN, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.EXTPROC), EXTPROC, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.TOSTOP), TOSTOP, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.FLUSHO), FLUSHO, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.PENDIN), PENDIN, c_lflag); c_lflag = setFlag(t.getLocalFlag(LocalFlag.NOFLSH), NOFLSH, c_lflag); // Control chars c_cc[VEOF] = (byte) t.getControlChar(ControlChar.VEOF); c_cc[VEOL] = (byte) t.getControlChar(ControlChar.VEOL); c_cc[VEOL2] = (byte) t.getControlChar(ControlChar.VEOL2); c_cc[VERASE] = (byte) t.getControlChar(ControlChar.VERASE); c_cc[VWERASE] = (byte) t.getControlChar(ControlChar.VWERASE); c_cc[VKILL] = (byte) t.getControlChar(ControlChar.VKILL); c_cc[VREPRINT] = (byte) t.getControlChar(ControlChar.VREPRINT); c_cc[VINTR] = (byte) t.getControlChar(ControlChar.VINTR); c_cc[VQUIT] = (byte) t.getControlChar(ControlChar.VQUIT); c_cc[VSUSP] = (byte) t.getControlChar(ControlChar.VSUSP); c_cc[VSTART] = (byte) t.getControlChar(ControlChar.VSTART); c_cc[VSTOP] = (byte) t.getControlChar(ControlChar.VSTOP); c_cc[VLNEXT] = (byte) t.getControlChar(ControlChar.VLNEXT); c_cc[VDISCARD] = (byte) t.getControlChar(ControlChar.VDISCARD); c_cc[VMIN] = (byte) t.getControlChar(ControlChar.VMIN); c_cc[VTIME] = (byte) t.getControlChar(ControlChar.VTIME); } private int setFlag(boolean flag, int value, int org) { return flag ? (org | value) : org; } public Attributes toAttributes() { Attributes attr = new Attributes(); // Input flags EnumSet iflag = attr.getInputFlags(); addFlag(c_iflag, iflag, InputFlag.IGNBRK, IGNBRK); addFlag(c_iflag, iflag, InputFlag.IGNBRK, IGNBRK); addFlag(c_iflag, iflag, InputFlag.BRKINT, BRKINT); addFlag(c_iflag, iflag, InputFlag.IGNPAR, IGNPAR); addFlag(c_iflag, iflag, InputFlag.PARMRK, PARMRK); addFlag(c_iflag, iflag, InputFlag.INPCK, INPCK); addFlag(c_iflag, iflag, InputFlag.ISTRIP, ISTRIP); addFlag(c_iflag, iflag, InputFlag.INLCR, INLCR); addFlag(c_iflag, iflag, InputFlag.IGNCR, IGNCR); addFlag(c_iflag, iflag, InputFlag.ICRNL, ICRNL); addFlag(c_iflag, iflag, InputFlag.IXON, IXON); addFlag(c_iflag, iflag, InputFlag.IXOFF, IXOFF); addFlag(c_iflag, iflag, InputFlag.IXANY, IXANY); addFlag(c_iflag, iflag, InputFlag.IMAXBEL, IMAXBEL); addFlag(c_iflag, iflag, InputFlag.IUTF8, IUTF8); // Output flags EnumSet oflag = attr.getOutputFlags(); addFlag(c_oflag, oflag, OutputFlag.OPOST, OPOST); addFlag(c_oflag, oflag, OutputFlag.ONLCR, ONLCR); addFlag(c_oflag, oflag, OutputFlag.OCRNL, OCRNL); addFlag(c_oflag, oflag, OutputFlag.ONOCR, ONOCR); addFlag(c_oflag, oflag, OutputFlag.ONLRET, ONLRET); addFlag(c_oflag, oflag, OutputFlag.OFILL, OFILL); addFlag(c_oflag, oflag, OutputFlag.NLDLY, NLDLY); addFlag(c_oflag, oflag, OutputFlag.TABDLY, TABDLY); addFlag(c_oflag, oflag, OutputFlag.CRDLY, CRDLY); addFlag(c_oflag, oflag, OutputFlag.FFDLY, FFDLY); addFlag(c_oflag, oflag, OutputFlag.BSDLY, BSDLY); addFlag(c_oflag, oflag, OutputFlag.VTDLY, VTDLY); addFlag(c_oflag, oflag, OutputFlag.OFDEL, OFDEL); // Control flags EnumSet cflag = attr.getControlFlags(); addFlag(c_cflag, cflag, ControlFlag.CS5, CS5); addFlag(c_cflag, cflag, ControlFlag.CS6, CS6); addFlag(c_cflag, cflag, ControlFlag.CS7, CS7); addFlag(c_cflag, cflag, ControlFlag.CS8, CS8); addFlag(c_cflag, cflag, ControlFlag.CSTOPB, CSTOPB); addFlag(c_cflag, cflag, ControlFlag.CREAD, CREAD); addFlag(c_cflag, cflag, ControlFlag.PARENB, PARENB); addFlag(c_cflag, cflag, ControlFlag.PARODD, PARODD); addFlag(c_cflag, cflag, ControlFlag.HUPCL, HUPCL); addFlag(c_cflag, cflag, ControlFlag.CLOCAL, CLOCAL); // Local flags EnumSet lflag = attr.getLocalFlags(); addFlag(c_lflag, lflag, LocalFlag.ECHOKE, ECHOKE); addFlag(c_lflag, lflag, LocalFlag.ECHOE, ECHOE); addFlag(c_lflag, lflag, LocalFlag.ECHOK, ECHOK); addFlag(c_lflag, lflag, LocalFlag.ECHO, ECHO); addFlag(c_lflag, lflag, LocalFlag.ECHONL, ECHONL); addFlag(c_lflag, lflag, LocalFlag.ECHOPRT, ECHOPRT); addFlag(c_lflag, lflag, LocalFlag.ECHOCTL, ECHOCTL); addFlag(c_lflag, lflag, LocalFlag.ISIG, ISIG); addFlag(c_lflag, lflag, LocalFlag.ICANON, ICANON); addFlag(c_lflag, lflag, LocalFlag.IEXTEN, IEXTEN); addFlag(c_lflag, lflag, LocalFlag.EXTPROC, EXTPROC); addFlag(c_lflag, lflag, LocalFlag.TOSTOP, TOSTOP); addFlag(c_lflag, lflag, LocalFlag.FLUSHO, FLUSHO); addFlag(c_lflag, lflag, LocalFlag.PENDIN, PENDIN); addFlag(c_lflag, lflag, LocalFlag.NOFLSH, NOFLSH); // Control chars EnumMap cc = attr.getControlChars(); cc.put(ControlChar.VEOF, (int) c_cc[VEOF]); cc.put(ControlChar.VEOL, (int) c_cc[VEOL]); cc.put(ControlChar.VEOL2, (int) c_cc[VEOL2]); cc.put(ControlChar.VERASE, (int) c_cc[VERASE]); cc.put(ControlChar.VWERASE, (int) c_cc[VWERASE]); cc.put(ControlChar.VKILL, (int) c_cc[VKILL]); cc.put(ControlChar.VREPRINT, (int) c_cc[VREPRINT]); cc.put(ControlChar.VINTR, (int) c_cc[VINTR]); cc.put(ControlChar.VQUIT, (int) c_cc[VQUIT]); cc.put(ControlChar.VSUSP, (int) c_cc[VSUSP]); cc.put(ControlChar.VSTART, (int) c_cc[VSTART]); cc.put(ControlChar.VSTOP, (int) c_cc[VSTOP]); cc.put(ControlChar.VLNEXT, (int) c_cc[VLNEXT]); cc.put(ControlChar.VDISCARD, (int) c_cc[VDISCARD]); cc.put(ControlChar.VMIN, (int) c_cc[VMIN]); cc.put(ControlChar.VTIME, (int) c_cc[VTIME]); // Return return attr; } private > void addFlag(int value, EnumSet flags, T flag, int v) { if ((value & v) != 0) { flags.add(flag); } } } // CONSTANTS int _TIOC = ( 'T' << 8 ); int TIOCGWINSZ = ( _TIOC | 104 ); int TIOCSWINSZ = ( _TIOC | 103 ); int VINTR = 0; int VQUIT = 1; int VERASE = 2; int VKILL = 3; int VEOF = 4; int VTIME = 5; int VMIN = 6; int VSWTC = 7; int VSTART = 8; int VSTOP = 9; int VSUSP = 10; int VEOL = 11; int VREPRINT = 12; int VDISCARD = 13; int VWERASE = 14; int VLNEXT = 15; int VEOL2 = 16; int IGNBRK = 0x0000001; int BRKINT = 0x0000002; int IGNPAR = 0x0000004; int PARMRK = 0x0000010; int INPCK = 0x0000020; int ISTRIP = 0x0000040; int INLCR = 0x0000100; int IGNCR = 0x0000200; int ICRNL = 0x0000400; int IUCLC = 0x0001000; int IXON = 0x0002000; int IXANY = 0x0004000; int IXOFF = 0x0010000; int IMAXBEL = 0x0020000; int IUTF8 = 0x0040000; int OPOST = 0x0000001; int OLCUC = 0x0000002; int ONLCR = 0x0000004; int OCRNL = 0x0000010; int ONOCR = 0x0000020; int ONLRET = 0x0000040; int OFILL = 0x0000100; int OFDEL = 0x0000200; int NLDLY = 0x0000400; int NL0 = 0x0000000; int NL1 = 0x0000400; int CRDLY = 0x0003000; int CR0 = 0x0000000; int CR1 = 0x0001000; int CR2 = 0x0002000; int CR3 = 0x0003000; int TABDLY = 0x0014000; int TAB0 = 0x0000000; int TAB1 = 0x0004000; int TAB2 = 0x0010000; int TAB3 = 0x0014000; int XTABS = 0x0014000; int BSDLY = 0x0020000; int BS0 = 0x0000000; int BS1 = 0x0020000; int VTDLY = 0x0040000; int VT0 = 0x0000000; int VT1 = 0x0040000; int FFDLY = 0x0100000; int FF0 = 0x0000000; int FF1 = 0x0100000; int CBAUD = 0x0010017; int B0 = 0x0000000; int B50 = 0x0000001; int B75 = 0x0000002; int B110 = 0x0000003; int B134 = 0x0000004; int B150 = 0x0000005; int B200 = 0x0000006; int B300 = 0x0000007; int B600 = 0x0000010; int B1200 = 0x0000011; int B1800 = 0x0000012; int B2400 = 0x0000013; int B4800 = 0x0000014; int B9600 = 0x0000015; int B19200 = 0x0000016; int B38400 = 0x0000017; int EXTA = 0xB19200; int EXTB = 0xB38400; int CSIZE = 0x0000060; int CS5 = 0x0000000; int CS6 = 0x0000020; int CS7 = 0x0000040; int CS8 = 0x0000060; int CSTOPB = 0x0000100; int CREAD = 0x0000200; int PARENB = 0x0000400; int PARODD = 0x0001000; int HUPCL = 0x0002000; int CLOCAL = 0x0004000; int ISIG = 0x0000001; int ICANON = 0x0000002; int XCASE = 0x0000004; int ECHO = 0x0000010; int ECHOE = 0x0000020; int ECHOK = 0x0000040; int ECHONL = 0x0000100; int NOFLSH = 0x0000200; int TOSTOP = 0x0000400; int ECHOCTL = 0x0001000; int ECHOPRT = 0x0002000; int ECHOKE = 0x0004000; int FLUSHO = 0x0010000; int PENDIN = 0x0040000; int IEXTEN = 0x0100000; int EXTPROC = 0x0200000; int TCSANOW = 0x0; int TCSADRAIN = 0x1; int TCSAFLUSH = 0x2; } SolarisNativePty.java000066400000000000000000000056621311544710100337600ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/solaris/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.solaris; import java.io.FileDescriptor; import java.io.IOException; import com.sun.jna.Native; import com.sun.jna.Platform; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.impl.jna.JnaNativePty; import static org.jline.terminal.impl.jna.solaris.CLibrary.TCSANOW; import static org.jline.terminal.impl.jna.solaris.CLibrary.TIOCGWINSZ; import static org.jline.terminal.impl.jna.solaris.CLibrary.TIOCSWINSZ; import static org.jline.terminal.impl.jna.solaris.CLibrary.termios; import static org.jline.terminal.impl.jna.solaris.CLibrary.winsize; public class SolarisNativePty extends JnaNativePty { private static final CLibrary C_LIBRARY = (CLibrary) Native.loadLibrary(Platform.C_LIBRARY_NAME, CLibrary.class); public static SolarisNativePty current() throws IOException { int slave = 0; byte[] buf = new byte[64]; C_LIBRARY.ttyname_r(slave, buf, buf.length); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new SolarisNativePty(-1, null, slave, FileDescriptor.in, name); } public static SolarisNativePty open(Attributes attr, Size size) throws IOException { int[] master = new int[1]; int[] slave = new int[1]; byte[] buf = new byte[64]; C_LIBRARY.openpty(master, slave, buf, attr != null ? new termios(attr) : null, size != null ? new winsize(size) : null); int len = 0; while (buf[len] != 0) { len++; } String name = new String(buf, 0, len); return new SolarisNativePty(master[0], newDescriptor(master[0]), slave[0], newDescriptor(slave[0]), name); } public SolarisNativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) { super(master, masterFD, slave, slaveFD, name); } @Override public Attributes getAttr() throws IOException { termios termios = new termios(); C_LIBRARY.tcgetattr(getSlave(), termios); return termios.toAttributes(); } @Override public void setAttr(Attributes attr) throws IOException { termios termios = new termios(attr); C_LIBRARY.tcsetattr(getSlave(), TCSANOW, termios); } @Override public Size getSize() throws IOException { winsize sz = new winsize(); C_LIBRARY.ioctl(getSlave(), TIOCGWINSZ, sz); return sz.toSize(); } @Override public void setSize(Size size) throws IOException { winsize sz = new winsize(size); C_LIBRARY.ioctl(getSlave(), TIOCSWINSZ, sz); } } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/win/000077500000000000000000000000001311544710100270245ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/win/AnsiOutputStream.java000066400000000000000000000710331311544710100331620ustar00rootroot00000000000000/* * Copyright (C) 2009-2017 the original author(s). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jline.terminal.impl.jna.win; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Iterator; /** * A ANSI output stream extracts ANSI escape codes written to * an output stream and calls corresponding process* methods. * * For more information about ANSI escape codes, see: * http://en.wikipedia.org/wiki/ANSI_escape_code * * This class just filters out the escape codes so that they are not * sent out to the underlying OutputStream: process* methods * are empty. Subclasses should actually perform the ANSI escape behaviors * by implementing active code in process* methods. * * @author Hiram Chirino * @author Joris Kuipers * @since 1.0 */ public class AnsiOutputStream extends FilterOutputStream { public static final byte[] RESET_CODE = "\033[0m".getBytes(); @Deprecated public static final byte[] REST_CODE = RESET_CODE; public AnsiOutputStream(OutputStream os) { super(os); } private final static int MAX_ESCAPE_SEQUENCE_LENGTH = 100; private final byte[] buffer = new byte[MAX_ESCAPE_SEQUENCE_LENGTH]; private int pos = 0; private int startOfValue; private final ArrayList options = new ArrayList(); private static final int LOOKING_FOR_FIRST_ESC_CHAR = 0; private static final int LOOKING_FOR_SECOND_ESC_CHAR = 1; private static final int LOOKING_FOR_NEXT_ARG = 2; private static final int LOOKING_FOR_STR_ARG_END = 3; private static final int LOOKING_FOR_INT_ARG_END = 4; private static final int LOOKING_FOR_OSC_COMMAND = 5; private static final int LOOKING_FOR_OSC_COMMAND_END = 6; private static final int LOOKING_FOR_OSC_PARAM = 7; private static final int LOOKING_FOR_ST = 8; int state = LOOKING_FOR_FIRST_ESC_CHAR; private static final int FIRST_ESC_CHAR = 27; private static final int SECOND_ESC_CHAR = '['; private static final int SECOND_OSC_CHAR = ']'; private static final int BEL = 7; private static final int SECOND_ST_CHAR = '\\'; @Override public synchronized void write(int data) throws IOException { switch (state) { case LOOKING_FOR_FIRST_ESC_CHAR: if (data == FIRST_ESC_CHAR) { buffer[pos++] = (byte) data; state = LOOKING_FOR_SECOND_ESC_CHAR; } else { out.write(data); } break; case LOOKING_FOR_SECOND_ESC_CHAR: buffer[pos++] = (byte) data; if (data == SECOND_ESC_CHAR) { state = LOOKING_FOR_NEXT_ARG; } else if (data == SECOND_OSC_CHAR) { state = LOOKING_FOR_OSC_COMMAND; } else { reset(false); } break; case LOOKING_FOR_NEXT_ARG: buffer[pos++] = (byte) data; if ('"' == data) { startOfValue = pos - 1; state = LOOKING_FOR_STR_ARG_END; } else if ('0' <= data && data <= '9') { startOfValue = pos - 1; state = LOOKING_FOR_INT_ARG_END; } else if (';' == data) { options.add(null); } else if ('?' == data) { options.add('?'); } else if ('=' == data) { options.add('='); } else { reset(processEscapeCommand(options, data)); } break; default: break; case LOOKING_FOR_INT_ARG_END: buffer[pos++] = (byte) data; if (!('0' <= data && data <= '9')) { String strValue = new String(buffer, startOfValue, (pos - 1) - startOfValue, Charset.defaultCharset()); Integer value = new Integer(strValue); options.add(value); if (data == ';') { state = LOOKING_FOR_NEXT_ARG; } else { reset(processEscapeCommand(options, data)); } } break; case LOOKING_FOR_STR_ARG_END: buffer[pos++] = (byte) data; if ('"' != data) { String value = new String(buffer, startOfValue, (pos - 1) - startOfValue, Charset.defaultCharset()); options.add(value); if (data == ';') { state = LOOKING_FOR_NEXT_ARG; } else { reset(processEscapeCommand(options, data)); } } break; case LOOKING_FOR_OSC_COMMAND: buffer[pos++] = (byte) data; if ('0' <= data && data <= '9') { startOfValue = pos - 1; state = LOOKING_FOR_OSC_COMMAND_END; } else { reset(false); } break; case LOOKING_FOR_OSC_COMMAND_END: buffer[pos++] = (byte) data; if (';' == data) { String strValue = new String(buffer, startOfValue, (pos - 1) - startOfValue, Charset.defaultCharset()); Integer value = new Integer(strValue); options.add(value); startOfValue = pos; state = LOOKING_FOR_OSC_PARAM; } else if ('0' <= data && data <= '9') { // already pushed digit to buffer, just keep looking } else { // oops, did not expect this reset(false); } break; case LOOKING_FOR_OSC_PARAM: buffer[pos++] = (byte) data; if (BEL == data) { String value = new String(buffer, startOfValue, (pos - 1) - startOfValue, Charset.defaultCharset()); options.add(value); reset(processOperatingSystemCommand(options)); } else if (FIRST_ESC_CHAR == data) { state = LOOKING_FOR_ST; } else { // just keep looking while adding text } break; case LOOKING_FOR_ST: buffer[pos++] = (byte) data; if (SECOND_ST_CHAR == data) { String value = new String(buffer, startOfValue, (pos - 2) - startOfValue, Charset.defaultCharset()); options.add(value); reset(processOperatingSystemCommand(options)); } else { state = LOOKING_FOR_OSC_PARAM; } break; } // Is it just too long? if (pos >= buffer.length) { reset(false); } } /** * Resets all state to continue with regular parsing * @param skipBuffer if current buffer should be skipped or written to out * @throws IOException */ private void reset(boolean skipBuffer) throws IOException { if (!skipBuffer) { out.write(buffer, 0, pos); } pos = 0; startOfValue = 0; options.clear(); state = LOOKING_FOR_FIRST_ESC_CHAR; } /** * Helper for processEscapeCommand() to iterate over integer options * @param optionsIterator the underlying iterator * @throws IOException if no more non-null values left */ private int getNextOptionInt(Iterator optionsIterator) throws IOException { for (;;) { if (!optionsIterator.hasNext()) throw new IllegalArgumentException(); Object arg = optionsIterator.next(); if (arg != null) return (Integer) arg; } } /** * * @param options * @param command * @return true if the escape command was processed. */ private boolean processEscapeCommand(ArrayList options, int command) throws IOException { try { switch (command) { case 'A': processCursorUp(optionInt(options, 0, 1)); return true; case 'B': processCursorDown(optionInt(options, 0, 1)); return true; case 'C': processCursorRight(optionInt(options, 0, 1)); return true; case 'D': processCursorLeft(optionInt(options, 0, 1)); return true; case 'E': processCursorDownLine(optionInt(options, 0, 1)); return true; case 'F': processCursorUpLine(optionInt(options, 0, 1)); return true; case 'G': processCursorToColumn(optionInt(options, 0)); return true; case 'H': case 'f': processCursorTo(optionInt(options, 0, 1), optionInt(options, 1, 1)); return true; case 'J': processEraseScreen(optionInt(options, 0, 0)); return true; case 'K': processEraseLine(optionInt(options, 0, 0)); return true; case 'L': processInsertLine(optionInt(options, 0, 1)); return true; case 'M': processDeleteLine(optionInt(options, 0, 1)); return true; case 'S': processScrollUp(optionInt(options, 0, 1)); return true; case 'T': processScrollDown(optionInt(options, 0, 1)); return true; case 'm': // Validate all options are ints... for (Object next : options) { if (next != null && next.getClass() != Integer.class) { throw new IllegalArgumentException(); } } int count = 0; Iterator optionsIterator = options.iterator(); while (optionsIterator.hasNext()) { Object next = optionsIterator.next(); if (next != null) { count++; int value = (Integer) next; if (30 <= value && value <= 37) { processSetForegroundColor(value - 30); } else if (40 <= value && value <= 47) { processSetBackgroundColor(value - 40); } else if (90 <= value && value <= 97) { processSetForegroundColor(value - 90, true); } else if (100 <= value && value <= 107) { processSetBackgroundColor(value - 100, true); } else if (value == 38 || value == 48) { // extended color like `esc[38;5;m` or `esc[38;2;;;m` int arg2or5 = getNextOptionInt(optionsIterator); if (arg2or5 == 2) { // 24 bit color style like `esc[38;2;;;m` int r = getNextOptionInt(optionsIterator); int g = getNextOptionInt(optionsIterator); int b = getNextOptionInt(optionsIterator); if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) { if (value == 38) processSetForegroundColorExt(r, g, b); else processSetBackgroundColorExt(r, g, b); } else { throw new IllegalArgumentException(); } } else if (arg2or5 == 5) { // 256 color style like `esc[38;5;m` int paletteIndex = getNextOptionInt(optionsIterator); if (paletteIndex >= 0 && paletteIndex <= 255) { if (value == 38) processSetForegroundColorExt(paletteIndex); else processSetBackgroundColorExt(paletteIndex); } else { throw new IllegalArgumentException(); } } else { throw new IllegalArgumentException(); } } else { switch (value) { case 39: processDefaultTextColor(); break; case 49: processDefaultBackgroundColor(); break; case 0: processAttributeRest(); break; default: processSetAttribute(value); } } } } if (count == 0) { processAttributeRest(); } return true; case 's': processSaveCursorPosition(); return true; case 'u': processRestoreCursorPosition(); return true; default: if ('a' <= command && 'z' <= command) { processUnknownExtension(options, command); return true; } if ('A' <= command && 'Z' <= command) { processUnknownExtension(options, command); return true; } return false; } } catch (IllegalArgumentException ignore) { } return false; } /** * * @param options * @return true if the operating system command was processed. */ private boolean processOperatingSystemCommand(ArrayList options) throws IOException { int command = optionInt(options, 0); String label = (String) options.get(1); // for command > 2 label could be composed (i.e. contain ';'), but we'll leave // it to processUnknownOperatingSystemCommand implementations to handle that try { switch (command) { case 0: processChangeIconNameAndWindowTitle(label); return true; case 1: processChangeIconName(label); return true; case 2: processChangeWindowTitle(label); return true; default: // not exactly unknown, but not supported through dedicated process methods: processUnknownOperatingSystemCommand(command, label); return true; } } catch (IllegalArgumentException ignore) { } return false; } /** * Process CSI u ANSI code, corresponding to RCP – Restore Cursor Position * @throws IOException */ protected void processRestoreCursorPosition() throws IOException { } /** * Process CSI s ANSI code, corresponding to SCP – Save Cursor Position * @throws IOException */ protected void processSaveCursorPosition() throws IOException { } /** * Process CSI s ANSI code, corresponding to IL – Insert Line * @throws IOException */ protected void processInsertLine(int optionInt) throws IOException { } /** * Process CSI s ANSI code, corresponding to DL – Delete Line * @throws IOException */ protected void processDeleteLine(int optionInt) throws IOException { } /** * Process CSI n T ANSI code, corresponding to SD – Scroll Down * @throws IOException */ protected void processScrollDown(int optionInt) throws IOException { } /** * Process CSI n U ANSI code, corresponding to SU – Scroll Up * @throws IOException */ protected void processScrollUp(int optionInt) throws IOException { } protected static final int ERASE_SCREEN_TO_END = 0; protected static final int ERASE_SCREEN_TO_BEGINING = 1; protected static final int ERASE_SCREEN = 2; /** * Process CSI n J ANSI code, corresponding to ED – Erase in Display * @throws IOException */ protected void processEraseScreen(int eraseOption) throws IOException { } protected static final int ERASE_LINE_TO_END = 0; protected static final int ERASE_LINE_TO_BEGINING = 1; protected static final int ERASE_LINE = 2; /** * Process CSI n K ANSI code, corresponding to ED – Erase in Line * @throws IOException */ protected void processEraseLine(int eraseOption) throws IOException { } protected static final int ATTRIBUTE_INTENSITY_BOLD = 1; // Intensity: Bold protected static final int ATTRIBUTE_INTENSITY_FAINT = 2; // Intensity; Faint not widely supported protected static final int ATTRIBUTE_ITALIC = 3; // Italic; on not widely supported. Sometimes treated as inverse. protected static final int ATTRIBUTE_UNDERLINE = 4; // Underline; Single protected static final int ATTRIBUTE_BLINK_SLOW = 5; // Blink; Slow less than 150 per minute protected static final int ATTRIBUTE_BLINK_FAST = 6; // Blink; Rapid MS-DOS ANSI.SYS; 150 per minute or more protected static final int ATTRIBUTE_NEGATIVE_ON = 7; // Image; Negative inverse or reverse; swap foreground and background protected static final int ATTRIBUTE_CONCEAL_ON = 8; // Conceal on protected static final int ATTRIBUTE_UNDERLINE_DOUBLE = 21; // Underline; Double not widely supported protected static final int ATTRIBUTE_INTENSITY_NORMAL = 22; // Intensity; Normal not bold and not faint protected static final int ATTRIBUTE_UNDERLINE_OFF = 24; // Underline; None protected static final int ATTRIBUTE_BLINK_OFF = 25; // Blink; off @Deprecated protected static final int ATTRIBUTE_NEGATIVE_Off = 27; // Image; Positive protected static final int ATTRIBUTE_NEGATIVE_OFF = 27; // Image; Positive protected static final int ATTRIBUTE_CONCEAL_OFF = 28; // Reveal conceal off /** * process SGR other than 0 (reset), 30-39 (foreground), * 40-49 (background), 90-97 (foreground high intensity) or * 100-107 (background high intensity) * @param attribute * @throws IOException * @see #processAttributeRest() * @see #processSetForegroundColor(int) * @see #processSetForegroundColor(int, boolean) * @see #processSetForegroundColorExt(int) * @see #processSetForegroundColorExt(int, int, int) * @see #processDefaultTextColor() * @see #processDefaultBackgroundColor() */ protected void processSetAttribute(int attribute) throws IOException { } protected static final int BLACK = 0; protected static final int RED = 1; protected static final int GREEN = 2; protected static final int YELLOW = 3; protected static final int BLUE = 4; protected static final int MAGENTA = 5; protected static final int CYAN = 6; protected static final int WHITE = 7; /** * process SGR 30-37 corresponding to Set text color (foreground). * @param color the text color * @throws IOException */ protected void processSetForegroundColor(int color) throws IOException { processSetForegroundColor(color, false); } /** * process SGR 30-37 or SGR 90-97 corresponding to * Set text color (foreground) either in normal mode or high intensity. * @param color the text color * @param bright is high intensity? * @throws IOException */ protected void processSetForegroundColor(int color, boolean bright) throws IOException { } /** * process SGR 38 corresponding to extended set text color (foreground) * with a palette of 255 colors. * @param paletteIndex the text color in the palette * @throws IOException */ protected void processSetForegroundColorExt(int paletteIndex) throws IOException { } /** * process SGR 38 corresponding to extended set text color (foreground) * with a 24 bits RGB definition of the color. * @param r red * @param g green * @param b blue * @throws IOException */ protected void processSetForegroundColorExt(int r, int g, int b) throws IOException { } /** * process SGR 40-47 corresponding to Set background color. * @param color the background color * @throws IOException */ protected void processSetBackgroundColor(int color) throws IOException { processSetBackgroundColor(color, false); } /** * process SGR 40-47 or SGR 100-107 corresponding to * Set background color either in normal mode or high intensity. * @param color the background color * @param bright is high intensity? * @throws IOException */ protected void processSetBackgroundColor(int color, boolean bright) throws IOException { } /** * process SGR 48 corresponding to extended set background color * with a palette of 255 colors. * @param paletteIndex the background color in the palette * @throws IOException */ protected void processSetBackgroundColorExt(int paletteIndex) throws IOException { } /** * process SGR 48 corresponding to extended set background color * with a 24 bits RGB definition of the color. * @param r red * @param g green * @param b blue * @throws IOException */ protected void processSetBackgroundColorExt(int r, int g, int b) throws IOException { } /** * process SGR 39 corresponding to Default text color (foreground) * @throws IOException */ protected void processDefaultTextColor() throws IOException { } /** * process SGR 49 corresponding to Default background color * @throws IOException */ protected void processDefaultBackgroundColor() throws IOException { } /** * process SGR 0 corresponding to Reset / Normal * @throws IOException */ protected void processAttributeRest() throws IOException { } /** * process CSI n ; m H corresponding to CUP – Cursor Position or * CSI n ; m f corresponding to HVP – Horizontal and Vertical Position * @param row * @param col * @throws IOException */ protected void processCursorTo(int row, int col) throws IOException { } /** * process CSI n G corresponding to CHA – Cursor Horizontal Absolute * @param x the column * @throws IOException */ protected void processCursorToColumn(int x) throws IOException { } /** * process CSI n F corresponding to CPL – Cursor Previous Line * @param count line count * @throws IOException */ protected void processCursorUpLine(int count) throws IOException { } /** * process CSI n E corresponding to CNL – Cursor Next Line * @param count line count * @throws IOException */ protected void processCursorDownLine(int count) throws IOException { // Poor mans impl.. for (int i = 0; i < count; i++) { out.write('\n'); } } /** * process CSI n D corresponding to CUB – Cursor Back * @param count * @throws IOException */ protected void processCursorLeft(int count) throws IOException { } /** * process CSI n C corresponding to CUF – Cursor Forward * @param count * @throws IOException */ protected void processCursorRight(int count) throws IOException { // Poor mans impl.. for (int i = 0; i < count; i++) { out.write(' '); } } /** * process CSI n B corresponding to CUD – Cursor Down * @param count * @throws IOException */ protected void processCursorDown(int count) throws IOException { } /** * process CSI n A corresponding to CUU – Cursor Up * @param count * @throws IOException */ protected void processCursorUp(int count) throws IOException { } protected void processUnknownExtension(ArrayList options, int command) { } /** * process OSC 0;text BEL corresponding to Change Window and Icon label * @param label * @throws IOException */ protected void processChangeIconNameAndWindowTitle(String label) { processChangeIconName(label); processChangeWindowTitle(label); } /** * process OSC 1;text BEL corresponding to Change Icon label * @param label * @throws IOException */ protected void processChangeIconName(String label) { } /** * process OSC 2;text BEL corresponding to Change Window title * @param label * @throws IOException */ protected void processChangeWindowTitle(String label) { } /** * Process unknown OSC command. * @param command * @param param */ protected void processUnknownOperatingSystemCommand(int command, String param) { } private int optionInt(ArrayList options, int index) { if (options.size() <= index) throw new IllegalArgumentException(); Object value = options.get(index); if (value == null) throw new IllegalArgumentException(); if (!value.getClass().equals(Integer.class)) throw new IllegalArgumentException(); return (Integer) value; } private int optionInt(ArrayList options, int index, int defaultValue) { if (options.size() > index) { Object value = options.get(index); if (value == null) { return defaultValue; } return (Integer) value; } return defaultValue; } @Override public void close() throws IOException { write(RESET_CODE); flush(); super.close(); } } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java000066400000000000000000000175141311544710100332600ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.win; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.util.function.IntConsumer; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import org.jline.terminal.Cursor; import org.jline.terminal.Size; import org.jline.terminal.impl.AbstractWindowsTerminal; import org.jline.utils.InfoCmp; import org.jline.utils.Log; public class JnaWinSysTerminal extends AbstractWindowsTerminal { private static final Pointer consoleIn = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_INPUT_HANDLE); private static final Pointer consoleOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE); private int prevButtonState; public JnaWinSysTerminal(String name, boolean nativeSignals) throws IOException { this(name, nativeSignals, SignalHandler.SIG_DFL); } public JnaWinSysTerminal(String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException { super(new WindowsAnsiOutputStream(new FileOutputStream(FileDescriptor.out), consoleOut), name, nativeSignals, signalHandler); strings.put(InfoCmp.Capability.key_mouse, "\\E[M"); } protected int getConsoleOutputCP() { return Kernel32.INSTANCE.GetConsoleOutputCP(); } @Override protected int getConsoleMode() { IntByReference mode = new IntByReference(); Kernel32.INSTANCE.GetConsoleMode(consoleIn, mode); return mode.getValue(); } @Override protected void setConsoleMode(int mode) { Kernel32.INSTANCE.SetConsoleMode(consoleIn, mode); } public Size getSize() { Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO(); Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info); return new Size(info.windowWidth(), info.windowHeight()); } private char[] mouse = new char[] { '\033', '[', 'M', ' ', ' ', ' ' }; protected byte[] readConsoleInput() throws IOException { Kernel32.INPUT_RECORD[] events = doReadConsoleInput(); if (events == null) { return new byte[0]; } StringBuilder sb = new StringBuilder(); for (Kernel32.INPUT_RECORD event : events) { if (event.EventType == Kernel32.INPUT_RECORD.KEY_EVENT) { Kernel32.KEY_EVENT_RECORD keyEvent = event.Event.KeyEvent; // support some C1 control sequences: ALT + [@-_] (and [a-z]?) => ESC // http://en.wikipedia.org/wiki/C0_and_C1_control_codes#C1_set final int altState = Kernel32.LEFT_ALT_PRESSED | Kernel32.RIGHT_ALT_PRESSED; // Pressing "Alt Gr" is translated to Alt-Ctrl, hence it has to be checked that Ctrl is _not_ pressed, // otherwise inserting of "Alt Gr" codes on non-US keyboards would yield errors final int ctrlState = Kernel32.LEFT_CTRL_PRESSED | Kernel32.RIGHT_CTRL_PRESSED; // Compute the overall alt state boolean isAlt = ((keyEvent.dwControlKeyState & altState) != 0) && ((keyEvent.dwControlKeyState & ctrlState) == 0); //Log.trace(keyEvent.keyDown? "KEY_DOWN" : "KEY_UP", "key code:", keyEvent.keyCode, "char:", (long)keyEvent.uchar); if (keyEvent.bKeyDown) { if (keyEvent.uChar.UnicodeChar > 0) { boolean shiftPressed = (keyEvent.dwControlKeyState & Kernel32.SHIFT_PRESSED) != 0; if (keyEvent.uChar.UnicodeChar == '\t' && shiftPressed) { sb.append(getSequence(InfoCmp.Capability.key_btab)); } else { if (isAlt) { sb.append('\033'); } sb.append(keyEvent.uChar.UnicodeChar); } } else { String escapeSequence = getEscapeSequence(keyEvent.wVirtualKeyCode); if (escapeSequence != null) { for (int k = 0; k < keyEvent.wRepeatCount; k++) { if (isAlt) { sb.append('\033'); } sb.append(escapeSequence); } } } } else { // key up event // support ALT+NumPad input method if (keyEvent.wVirtualKeyCode == 0x12/*VK_MENU ALT key*/ && keyEvent.uChar.UnicodeChar > 0) { sb.append(keyEvent.uChar.UnicodeChar); } } } else if (event.EventType == Kernel32.INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT) { raise(Signal.WINCH); } else if (event.EventType == Kernel32.INPUT_RECORD.MOUSE_EVENT) { Kernel32.MOUSE_EVENT_RECORD mouseEvent = event.Event.MouseEvent; int dwEventFlags = mouseEvent.dwEventFlags; int dwButtonState = mouseEvent.dwButtonState; if (tracking == MouseTracking.Off || tracking == MouseTracking.Normal && dwEventFlags == Kernel32.MOUSE_MOVED || tracking == MouseTracking.Button && dwEventFlags == Kernel32.MOUSE_MOVED && dwButtonState == 0) { continue; } int cb = 0; dwEventFlags &= ~ Kernel32.DOUBLE_CLICK; // Treat double-clicks as normal if (dwEventFlags == Kernel32.MOUSE_WHEELED) { cb |= 64; if ((dwButtonState >> 16) < 0) { cb |= 1; } } else if (dwEventFlags == Kernel32.MOUSE_HWHEELED) { continue; } else if ((dwButtonState & Kernel32.FROM_LEFT_1ST_BUTTON_PRESSED) != 0) { cb |= 0x00; } else if ((dwButtonState & Kernel32.RIGHTMOST_BUTTON_PRESSED) != 0) { cb |= 0x01; } else if ((dwButtonState & Kernel32.FROM_LEFT_2ND_BUTTON_PRESSED) != 0) { cb |= 0x02; } else { cb |= 0x03; } int cx = mouseEvent.dwMousePosition.X; int cy = mouseEvent.dwMousePosition.Y; mouse[3] = (char) (' ' + cb); mouse[4] = (char) (' ' + cx + 1); mouse[5] = (char) (' ' + cy + 1); sb.append(mouse); prevButtonState = dwButtonState; } } return sb.toString().getBytes(); } private Kernel32.INPUT_RECORD[] doReadConsoleInput() throws IOException { Kernel32.INPUT_RECORD[] ir = new Kernel32.INPUT_RECORD[1]; IntByReference r = new IntByReference(); Kernel32.INSTANCE.ReadConsoleInput(consoleIn, ir, ir.length, r); for (int i = 0; i < r.getValue(); ++i) { switch (ir[i].EventType) { case Kernel32.INPUT_RECORD.KEY_EVENT: case Kernel32.INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT: case Kernel32.INPUT_RECORD.MOUSE_EVENT: return ir; } } return null; } @Override public Cursor getCursorPosition(IntConsumer discarded) { Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO(); Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info); return new Cursor(info.dwCursorPosition.X, info.dwCursorPosition.Y); } } jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/win/Kernel32.java000066400000000000000000000464361311544710100312710ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.win; import com.sun.jna.LastErrorException; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.Union; import com.sun.jna.ptr.IntByReference; import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.W32APIOptions; interface Kernel32 extends StdCallLibrary { Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class, W32APIOptions.UNICODE_OPTIONS); Pointer INVALID_HANDLE_VALUE = Pointer.createConstant(-1L); int STD_INPUT_HANDLE = -10; int STD_OUTPUT_HANDLE = -11; int STD_ERROR_HANDLE = -12; int ENABLE_PROCESSED_INPUT = 0x0001; int ENABLE_LINE_INPUT = 0x0002; int ENABLE_ECHO_INPUT = 0x0004; int ENABLE_WINDOW_INPUT = 0x0008; int ENABLE_MOUSE_INPUT = 0x0010; int ENABLE_INSERT_MODE = 0x0020; int ENABLE_QUICK_EDIT_MODE = 0x0040; int ENABLE_EXTENDED_FLAGS = 0x0080; int RIGHT_ALT_PRESSED = 0x0001; int LEFT_ALT_PRESSED = 0x0002; int RIGHT_CTRL_PRESSED = 0x0004; int LEFT_CTRL_PRESSED = 0x0008; int SHIFT_PRESSED = 0x0010; int FOREGROUND_BLUE = 0x0001; int FOREGROUND_GREEN = 0x0002; int FOREGROUND_RED = 0x0004; int FOREGROUND_INTENSITY = 0x0008; int BACKGROUND_BLUE = 0x0010; int BACKGROUND_GREEN = 0x0020; int BACKGROUND_RED = 0x0040; int BACKGROUND_INTENSITY = 0x0080; // Button state int FROM_LEFT_1ST_BUTTON_PRESSED = 0x0001; int RIGHTMOST_BUTTON_PRESSED = 0x0002; int FROM_LEFT_2ND_BUTTON_PRESSED = 0x0004; int FROM_LEFT_3RD_BUTTON_PRESSED = 0x0008; int FROM_LEFT_4TH_BUTTON_PRESSED = 0x0010; // Event flags int MOUSE_MOVED = 0x0001; int DOUBLE_CLICK = 0x0002; int MOUSE_WHEELED = 0x0004; int MOUSE_HWHEELED = 0x0008; // HANDLE WINAPI GetStdHandle( // __in DWORD nStdHandle // ); Pointer GetStdHandle(int nStdHandle); // BOOL WINAPI AllocConsole(void); void AllocConsole() throws LastErrorException; // BOOL WINAPI FreeConsole(void); void FreeConsole() throws LastErrorException; // HWND WINAPI GetConsoleWindow(void); Pointer GetConsoleWindow(); // UINT WINAPI GetConsoleOutputCP(void) int GetConsoleOutputCP(); // BOOL WINAPI FillConsoleOutputCharacter( // _In_ HANDLE hConsoleOutput, // _In_ TCHAR cCharacter, // _In_ DWORD nLength, // _In_ COORD dwWriteCoord, // _Out_ LPDWORD lpNumberOfCharsWritten); void FillConsoleOutputCharacter(Pointer in_hConsoleOutput, char in_cCharacter, int in_nLength, COORD in_dwWriteCoord, IntByReference out_lpNumberOfCharsWritten) throws LastErrorException; // BOOL WINAPI FillConsoleOutputAttribute( // _In_ HANDLE hConsoleOutput, // _In_ WORD wAttribute, // _In_ DWORD nLength, // _In_ COORD dwWriteCoord, // _Out_ LPDWORD lpNumberOfAttrsWritten); void FillConsoleOutputAttribute(Pointer in_hConsoleOutput, short in_wAttribute, int in_nLength, COORD in_dwWriteCoord, IntByReference out_lpNumberOfAttrsWritten) throws LastErrorException; // BOOL WINAPI GetConsoleCursorInfo( // _In_ HANDLE hConsoleOutput, // _Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo); void GetConsoleCursorInfo(Pointer in_hConsoleOutput, CONSOLE_CURSOR_INFO.ByReference out_lpConsoleCursorInfo) throws LastErrorException; // BOOL WINAPI GetConsoleMode( // _In_ HANDLE hConsoleHandle, // _Out_ LPDWORD lpMode); void GetConsoleMode( Pointer in_hConsoleOutput, IntByReference out_lpMode) throws LastErrorException; // BOOL WINAPI GetConsoleScreenBufferInfo( // _In_ HANDLE hConsoleOutput, // _Out_ PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo); void GetConsoleScreenBufferInfo( Pointer in_hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFO out_lpConsoleScreenBufferInfo) throws LastErrorException; // BOOL WINAPI GetNumberOfConsoleInputEvents( // _In_ HANDLE hConsoleInput, // _Out_ LPDWORD lpcNumberOfEvents); void GetNumberOfConsoleInputEvents(Pointer in_hConsoleOutput, IntByReference out_lpcNumberOfEvents) throws LastErrorException; // BOOL WINAPI ReadConsoleInput( // _In_ HANDLE hConsoleInput, // _Out_ PINPUT_RECORD lpBuffer, // _In_ DWORD nLength, // _Out_ LPDWORD lpNumberOfEventsRead); void ReadConsoleInput(Pointer in_hConsoleOutput, INPUT_RECORD[] out_lpBuffer, int in_nLength, IntByReference out_lpNumberOfEventsRead) throws LastErrorException; // BOOL WINAPI SetConsoleCtrlHandler( // _In_opt_ PHANDLER_ROUTINE HandlerRoutine, // _In_ BOOL Add); void SetConsoleCtrlHandler( Pointer in_opt_HandlerRoutine, boolean in_Add) throws LastErrorException; // BOOL WINAPI ReadConsoleOutput( // _In_ HANDLE hConsoleOutput, // _Out_ PCHAR_INFO lpBuffer, // _In_ COORD dwBufferSize, // _In_ COORD dwBufferCoord, // _Inout_ PSMALL_RECT lpReadRegion); void ReadConsoleOutput(Pointer in_hConsoleOutput, CHAR_INFO[] out_lpBuffer, COORD in_dwBufferSize, COORD in_dwBufferCoord, SMALL_RECT inout_lpReadRegion) throws LastErrorException; void ReadConsoleOutputA(Pointer in_hConsoleOutput, CHAR_INFO[] out_lpBuffer, COORD in_dwBufferSize, COORD in_dwBufferCoord, SMALL_RECT inout_lpReadRegion) throws LastErrorException; // BOOL WINAPI ReadConsoleOutputCharacter( // _In_ HANDLE hConsoleOutput, // _Out_ LPTSTR lpCharacter, // _In_ DWORD nLength, // _In_ COORD dwReadCoord, // _Out_ LPDWORD lpNumberOfCharsRead); void ReadConsoleOutputCharacter(Pointer in_hConsoleOutput, char[] ouy_lpCharacter, int in_nLength, COORD in_dwReadCoord, IntByReference out_lpNumberOfCharsRead) throws LastErrorException; void ReadConsoleOutputCharacterA(Pointer in_hConsoleOutput, byte[] ouy_lpCharacter, int in_nLength, COORD in_dwReadCoord, IntByReference out_lpNumberOfCharsRead) throws LastErrorException; // BOOL WINAPI SetConsoleCursorInfo( // _In_ HANDLE hConsoleOutput, // _In_ const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo); void SetConsoleCursorInfo(Pointer in_hConsoleOutput, CONSOLE_CURSOR_INFO in_lpConsoleCursorInfo) throws LastErrorException; // BOOL WINAPI SetConsoleCP( // _In_ UINT wCodePageID); void SetConsoleCP(int in_wCodePageID) throws LastErrorException; // BOOL WINAPI SetConsoleCursorPosition( // _In_ HANDLE hConsoleOutput, // _In_ COORD dwCursorPosition); void SetConsoleCursorPosition(Pointer in_hConsoleOutput, COORD in_dwCursorPosition) throws LastErrorException; // BOOL WINAPI SetConsoleMode( // _In_ HANDLE hConsoleHandle, // _In_ DWORD dwMode); void SetConsoleMode( Pointer in_hConsoleOutput, int in_dwMode) throws LastErrorException; // BOOL WINAPI SetConsoleScreenBufferSize( // __in HANDLE hConsoleOutput, // __in COORD dwSize // ); void SetConsoleScreenBufferSize(Pointer in_hConsoleOutput, COORD in_dwSize) throws LastErrorException; // BOOL WINAPI SetConsoleTextAttribute( // _In_ HANDLE hConsoleOutput, // _In_ WORD wAttributes // ); void SetConsoleTextAttribute(Pointer in_hConsoleOutput, short in_wAttributes) throws LastErrorException; // BOOL WINAPI SetConsoleTitle( // _In_ LPCTSTR lpConsoleTitle // ); void SetConsoleTitle(String in_lpConsoleTitle) throws LastErrorException; // BOOL WINAPI SetConsoleWindowInfo( // _In_ HANDLE hConsoleOutput, // _In_ BOOL bAbsolute, // _In_ const SMALL_RECT *lpConsoleWindow); void SetConsoleWindowInfo(Pointer in_hConsoleOutput, boolean in_bAbsolute, SMALL_RECT in_lpConsoleWindow) throws LastErrorException; // BOOL WINAPI WriteConsoleOutput( // _In_ HANDLE hConsoleOutput, // _In_ const CHAR_INFO *lpBuffer, // _In_ COORD dwBufferSize, // _In_ COORD dwBufferCoord, // _Inout_ PSMALL_RECT lpWriteRegion); void WriteConsoleOutput(Pointer in_hConsoleOutput, CHAR_INFO[] in_lpBuffer, COORD in_dwBufferSize, COORD in_dwBufferCoord, SMALL_RECT inout_lpWriteRegion) throws LastErrorException; void WriteConsoleOutputA(Pointer in_hConsoleOutput, CHAR_INFO[] in_lpBuffer, COORD in_dwBufferSize, COORD in_dwBufferCoord, SMALL_RECT inout_lpWriteRegion) throws LastErrorException; // BOOL WINAPI WriteConsoleOutputCharacter( // _In_ HANDLE hConsoleOutput, // _In_ LPCTSTR lpCharacter, // _In_ DWORD nLength, // _In_ COORD dwWriteCoord, // _Out_ LPDWORD lpNumberOfCharsWritten); void WriteConsoleOutputCharacter(Pointer in_hConsoleOutput, char[] in_lpCharacter, int in_nLength, COORD in_dwWriteCoord, IntByReference out_lpNumberOfCharsWritten) throws LastErrorException; void WriteConsoleOutputCharacterA(Pointer in_hConsoleOutput, byte[] in_lpCharacter, int in_nLength, COORD in_dwWriteCoord, IntByReference out_lpNumberOfCharsWritten) throws LastErrorException; // BOOL WINAPI ScrollConsoleScreenBuffer( // _In_ HANDLE hConsoleOutput, // _In_ const SMALL_RECT *lpScrollRectangle, // _In_opt_ const SMALL_RECT *lpClipRectangle, // _In_ COORD dwDestinationOrigin, // _In_ const CHAR_INFO *lpFill); void ScrollConsoleScreenBuffer(Pointer in_hConsoleOutput, SMALL_RECT in_lpScrollRectangle, SMALL_RECT in_lpClipRectangle, COORD in_dwDestinationOrigin, CHAR_INFO in_lpFill) throws LastErrorException; // typedef struct _CHAR_INFO { // union { // WCHAR UnicodeChar; // CHAR AsciiChar; // } Char; // WORD Attributes; // } CHAR_INFO, *PCHAR_INFO; class CHAR_INFO extends Structure { public CHAR_INFO() { } public CHAR_INFO(char c, short attr) { uChar = new UnionChar(c); Attributes = attr; } public CHAR_INFO(byte c, short attr) { uChar = new UnionChar(c); Attributes = attr; } public UnionChar uChar; public short Attributes; public static CHAR_INFO[] createArray(int size) { return (CHAR_INFO[]) new CHAR_INFO().toArray(size); } private static String[] fieldOrder = { "uChar", "Attributes" }; @Override protected java.util.List getFieldOrder() { return java.util.Arrays.asList(fieldOrder); } } // typedef struct _CONSOLE_CURSOR_INFO { // DWORD dwSize; // BOOL bVisible; // } CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO; class CONSOLE_CURSOR_INFO extends Structure { public int dwSize; public boolean bVisible; public static class ByReference extends CONSOLE_CURSOR_INFO implements Structure.ByReference { } private static String[] fieldOrder = { "dwSize", "bVisible" }; @Override protected java.util.List getFieldOrder() { return java.util.Arrays.asList(fieldOrder); } } // typedef struct _CONSOLE_SCREEN_BUFFER_INFO { // COORD dwSize; // COORD dwCursorPosition; // WORD wAttributes; // SMALL_RECT srWindow; // COORD dwMaximumWindowSize; // } CONSOLE_SCREEN_BUFFER_INFO; class CONSOLE_SCREEN_BUFFER_INFO extends Structure { public COORD dwSize; public COORD dwCursorPosition; public short wAttributes; public SMALL_RECT srWindow; public COORD dwMaximumWindowSize; private static String[] fieldOrder = { "dwSize", "dwCursorPosition", "wAttributes", "srWindow", "dwMaximumWindowSize" }; @Override protected java.util.List getFieldOrder() { return java.util.Arrays.asList(fieldOrder); } public int windowWidth() { return this.srWindow.width() + 1; } public int windowHeight() { return this.srWindow.height() + 1; } } // typedef struct _COORD { // SHORT X; // SHORT Y; // } COORD, *PCOORD; class COORD extends Structure implements Structure.ByValue { public COORD() { } public COORD(short X, short Y) { this.X = X; this.Y = Y; } public short X; public short Y; private static String[] fieldOrder = { "X", "Y" }; @Override protected java.util.List getFieldOrder() { return java.util.Arrays.asList(fieldOrder); } } // typedef struct _INPUT_RECORD { // WORD EventType; // union { // KEY_EVENT_RECORD KeyEvent; // MOUSE_EVENT_RECORD MouseEvent; // WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent; // MENU_EVENT_RECORD MenuEvent; // FOCUS_EVENT_RECORD FocusEvent; // } Event; // } INPUT_RECORD; class INPUT_RECORD extends Structure { public static final short KEY_EVENT = 0x0001; public static final short MOUSE_EVENT = 0x0002; public static final short WINDOW_BUFFER_SIZE_EVENT = 0x0004; public static final short MENU_EVENT = 0x0008; public static final short FOCUS_EVENT = 0x0010; public short EventType; public EventUnion Event; public static class EventUnion extends Union { public KEY_EVENT_RECORD KeyEvent; public MOUSE_EVENT_RECORD MouseEvent; public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent; // MENU_EVENT_RECORD MenuEvent; // FOCUS_EVENT_RECORD FocusEvent; } @Override public void read() { readField("EventType"); switch (EventType) { case KEY_EVENT: Event.setType(KEY_EVENT_RECORD.class); break; case MOUSE_EVENT: Event.setType(MOUSE_EVENT_RECORD.class); break; case WINDOW_BUFFER_SIZE_EVENT: Event.setType(WINDOW_BUFFER_SIZE_RECORD.class); break; } super.read(); } private static String[] fieldOrder = {"EventType", "Event"}; @Override protected java.util.List getFieldOrder() { return java.util.Arrays.asList(fieldOrder); } } // typedef struct _KEY_EVENT_RECORD { // BOOL bKeyDown; // WORD wRepeatCount; // WORD wVirtualKeyCode; // WORD wVirtualScanCode; // union { // WCHAR UnicodeChar; // CHAR AsciiChar; // } uChar; // DWORD dwControlKeyState; // } KEY_EVENT_RECORD; class KEY_EVENT_RECORD extends Structure { public boolean bKeyDown; public short wRepeatCount; public short wVirtualKeyCode; public short wVirtualScanCode; public UnionChar uChar; public int dwControlKeyState; private static String[] fieldOrder = {"bKeyDown", "wRepeatCount", "wVirtualKeyCode", "wVirtualScanCode", "uChar", "dwControlKeyState"}; @Override protected java.util.List getFieldOrder() { return java.util.Arrays.asList(fieldOrder); } } // typedef struct _MOUSE_EVENT_RECORD { // COORD dwMousePosition; // DWORD dwButtonState; // DWORD dwControlKeyState; // DWORD dwEventFlags; // } MOUSE_EVENT_RECORD; class MOUSE_EVENT_RECORD extends Structure { public COORD dwMousePosition; public int dwButtonState; public int dwControlKeyState; public int dwEventFlags; private static String[] fieldOrder = { "dwMousePosition", "dwButtonState", "dwControlKeyState", "dwEventFlags"}; @Override protected java.util.List getFieldOrder() { return java.util.Arrays.asList(fieldOrder); } } // typedef struct _WINDOW_BUFFER_SIZE_RECORD { // COORD dwSize; // } WINDOW_BUFFER_SIZE_RECORD; class WINDOW_BUFFER_SIZE_RECORD extends Structure { public COORD dwSize; private static String[] fieldOrder = {"dwSize"}; @Override protected java.util.List getFieldOrder() { return java.util.Arrays.asList(fieldOrder); } } // typedef struct _SMALL_RECT { // SHORT Left; // SHORT Top; // SHORT Right; // SHORT Bottom; // } SMALL_RECT; class SMALL_RECT extends Structure { public SMALL_RECT() { } public SMALL_RECT(SMALL_RECT org) { this(org.Top, org.Left, org.Bottom, org.Right); } public SMALL_RECT(short Top, short Left, short Bottom, short Right) { this.Top = Top; this.Left = Left; this.Bottom = Bottom; this.Right = Right; } public short Left; public short Top; public short Right; public short Bottom; private static String[] fieldOrder = { "Left", "Top", "Right", "Bottom" }; @Override protected java.util.List getFieldOrder() { return java.util.Arrays.asList(fieldOrder); } public short width() { return (short)(this.Right - this.Left); } public short height() { return (short)(this.Bottom - this.Top); } } class UnionChar extends Union { public UnionChar() { } public UnionChar(char c) { setType(char.class); UnicodeChar = c; } public UnionChar(byte c) { setType(byte.class); AsciiChar = c; } public void set(char c) { setType(char.class); UnicodeChar = c; } public void set(byte c) { setType(byte.class); AsciiChar = c; } public char UnicodeChar; public byte AsciiChar; } } WindowsAnsiOutputStream.java000066400000000000000000000331601311544710100344550ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/java/org/jline/terminal/impl/jna/win/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna.win; import java.io.IOException; import java.io.OutputStream; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import static org.jline.terminal.impl.jna.win.Kernel32.BACKGROUND_BLUE; import static org.jline.terminal.impl.jna.win.Kernel32.BACKGROUND_GREEN; import static org.jline.terminal.impl.jna.win.Kernel32.BACKGROUND_INTENSITY; import static org.jline.terminal.impl.jna.win.Kernel32.BACKGROUND_RED; import static org.jline.terminal.impl.jna.win.Kernel32.FOREGROUND_BLUE; import static org.jline.terminal.impl.jna.win.Kernel32.FOREGROUND_GREEN; import static org.jline.terminal.impl.jna.win.Kernel32.FOREGROUND_INTENSITY; import static org.jline.terminal.impl.jna.win.Kernel32.FOREGROUND_RED; /** * A Windows ANSI escape processor, uses JNA to access native platform * API's to change the console attributes. * * @since 1.0 * @author Hiram Chirino * @author Joris Kuipers */ public final class WindowsAnsiOutputStream extends AnsiOutputStream { private static final short FOREGROUND_BLACK = 0; private static final short FOREGROUND_YELLOW = (short) (FOREGROUND_RED|FOREGROUND_GREEN); private static final short FOREGROUND_MAGENTA = (short) (FOREGROUND_BLUE|FOREGROUND_RED); private static final short FOREGROUND_CYAN = (short) (FOREGROUND_BLUE|FOREGROUND_GREEN); private static final short FOREGROUND_WHITE = (short) (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); private static final short BACKGROUND_BLACK = 0; private static final short BACKGROUND_YELLOW = (short) (BACKGROUND_RED|BACKGROUND_GREEN); private static final short BACKGROUND_MAGENTA = (short) (BACKGROUND_BLUE|BACKGROUND_RED); private static final short BACKGROUND_CYAN = (short) (BACKGROUND_BLUE|BACKGROUND_GREEN); private static final short BACKGROUND_WHITE = (short) (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE); private static final short ANSI_FOREGROUND_COLOR_MAP[] = { FOREGROUND_BLACK, FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_YELLOW, FOREGROUND_BLUE, FOREGROUND_MAGENTA, FOREGROUND_CYAN, FOREGROUND_WHITE, }; private static final short ANSI_BACKGROUND_COLOR_MAP[] = { BACKGROUND_BLACK, BACKGROUND_RED, BACKGROUND_GREEN, BACKGROUND_YELLOW, BACKGROUND_BLUE, BACKGROUND_MAGENTA, BACKGROUND_CYAN, BACKGROUND_WHITE, }; private final static int MAX_ESCAPE_SEQUENCE_LENGTH = 100; private final Pointer console; private final Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO(); private final short originalColors; private boolean negative; private short savedX = -1; private short savedY = -1; public WindowsAnsiOutputStream(OutputStream os, Pointer console) throws IOException { super(os); this.console = console; getConsoleInfo(); originalColors = info.wAttributes; } private void getConsoleInfo() throws IOException { out.flush(); Kernel32.INSTANCE.GetConsoleScreenBufferInfo(console, info); if( negative ) { info.wAttributes = invertAttributeColors(info.wAttributes); } } private void applyAttribute() throws IOException { out.flush(); short attributes = info.wAttributes; if( negative ) { attributes = invertAttributeColors(attributes); } Kernel32.INSTANCE.SetConsoleTextAttribute(console, attributes); } private short invertAttributeColors(short attributes) { // Swap the the Foreground and Background bits. int fg = 0x000F & attributes; fg <<= 4; int bg = 0X00F0 & attributes; bg >>= 4; attributes = (short) ((attributes & 0xFF00) | fg | bg); return attributes; } private void applyCursorPosition() throws IOException { Kernel32.INSTANCE.SetConsoleCursorPosition(console, info.dwCursorPosition); } protected void processDefaultTextColor() throws IOException { info.wAttributes = (short)((info.wAttributes & ~0x000F ) | (originalColors & 0x000F)); applyAttribute(); } protected void processDefaultBackgroundColor() throws IOException { info.wAttributes = (short)((info.wAttributes & ~0x00F0 ) | (originalColors & 0x00F0)); applyAttribute(); } protected void processEraseScreen(int eraseOption) throws IOException { getConsoleInfo(); IntByReference written = new IntByReference(); switch(eraseOption) { case ERASE_SCREEN: Kernel32.COORD topLeft = new Kernel32.COORD(); topLeft.X = 0; topLeft.Y = info.srWindow.Top; int screenLength = info.srWindow.height() * info.dwSize.X; Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', screenLength, topLeft, written); Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, screenLength, topLeft, written); break; case ERASE_SCREEN_TO_BEGINING: Kernel32.COORD topLeft2 = new Kernel32.COORD(); topLeft2.X = 0; topLeft2.Y = info.srWindow.Top; int lengthToCursor = (info.dwCursorPosition.Y - info.srWindow.Top) * info.dwSize.X + info.dwCursorPosition.X; Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', lengthToCursor, topLeft2, written); Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, lengthToCursor, topLeft2, written); break; case ERASE_SCREEN_TO_END: int lengthToEnd = (info.srWindow.Bottom - info.dwCursorPosition.Y) * info.dwSize.X + (info.dwSize.X - info.dwCursorPosition.X); Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', lengthToEnd, info.dwCursorPosition, written); Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, lengthToEnd, info.dwCursorPosition, written); } } protected void processEraseLine(int eraseOption) throws IOException { getConsoleInfo(); IntByReference written = new IntByReference(); switch(eraseOption) { case ERASE_LINE: Kernel32.COORD leftColCurrRow = new Kernel32.COORD((short) 0, info.dwCursorPosition.Y); Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', info.dwSize.X, leftColCurrRow, written); Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, info.dwSize.X, leftColCurrRow, written); break; case ERASE_LINE_TO_BEGINING: Kernel32.COORD leftColCurrRow2 = new Kernel32.COORD((short) 0, info.dwCursorPosition.Y); Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', info.dwCursorPosition.X, leftColCurrRow2, written); Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, info.dwCursorPosition.X, leftColCurrRow2, written); break; case ERASE_LINE_TO_END: int lengthToLastCol = info.dwSize.X - info.dwCursorPosition.X; Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', lengthToLastCol, info.dwCursorPosition, written); Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, lengthToLastCol, info.dwCursorPosition, written); } } protected void processCursorUpLine(int count) throws IOException { getConsoleInfo(); info.dwCursorPosition.X = 0; info.dwCursorPosition.Y = (short) Math.max(info.srWindow.Top, info.dwCursorPosition.Y-count); applyCursorPosition(); } protected void processCursorDownLine(int count) throws IOException { getConsoleInfo(); info.dwCursorPosition.X = 0; info.dwCursorPosition.Y = (short) Math.min(info.dwSize.Y, info.dwCursorPosition.Y+count); applyCursorPosition(); } protected void processCursorLeft(int count) throws IOException { getConsoleInfo(); info.dwCursorPosition.X = (short) Math.max(0, info.dwCursorPosition.X-count); applyCursorPosition(); } protected void processCursorRight(int count) throws IOException { getConsoleInfo(); info.dwCursorPosition.X = (short)Math.min(info.srWindow.width(), info.dwCursorPosition.X+count); applyCursorPosition(); } protected void processCursorDown(int count) throws IOException { getConsoleInfo(); info.dwCursorPosition.Y = (short) Math.min(info.dwSize.Y, info.dwCursorPosition.Y+count); applyCursorPosition(); } protected void processCursorUp(int count) throws IOException { getConsoleInfo(); info.dwCursorPosition.Y = (short) Math.max(info.srWindow.Top, info.dwCursorPosition.Y-count); applyCursorPosition(); } protected void processCursorTo(int row, int col) throws IOException { getConsoleInfo(); info.dwCursorPosition.Y = (short) Math.max(info.srWindow.Top, Math.min(info.dwSize.Y, info.srWindow.Top+row-1)); info.dwCursorPosition.X = (short) Math.max(0, Math.min(info.srWindow.width(), col-1)); applyCursorPosition(); } protected void processCursorToColumn(int x) throws IOException { getConsoleInfo(); info.dwCursorPosition.X = (short) Math.max(0, Math.min(info.srWindow.width(), x-1)); applyCursorPosition(); } protected void processSetForegroundColor(int color, boolean bright) throws IOException { info.wAttributes = (short)((info.wAttributes & ~0x0007 ) | ANSI_FOREGROUND_COLOR_MAP[color]); info.wAttributes = (short) ((info.wAttributes & ~FOREGROUND_INTENSITY) | (bright ? FOREGROUND_INTENSITY : 0)); applyAttribute(); } protected void processSetBackgroundColor(int color, boolean bright) throws IOException { info.wAttributes = (short)((info.wAttributes & ~0x0070 ) | ANSI_BACKGROUND_COLOR_MAP[color]); info.wAttributes = (short) ((info.wAttributes & ~BACKGROUND_INTENSITY) | (bright ? BACKGROUND_INTENSITY : 0)); applyAttribute(); } protected void processAttributeRest() throws IOException { info.wAttributes = (short)((info.wAttributes & ~0x00FF ) | originalColors); this.negative = false; applyAttribute(); } protected void processSetAttribute(int attribute) throws IOException { switch(attribute) { case ATTRIBUTE_INTENSITY_BOLD: info.wAttributes = (short)(info.wAttributes | FOREGROUND_INTENSITY ); applyAttribute(); break; case ATTRIBUTE_INTENSITY_NORMAL: info.wAttributes = (short)(info.wAttributes & ~FOREGROUND_INTENSITY ); applyAttribute(); break; // Yeah, setting the background intensity is not underlining.. but it's best we can do // using the Windows console API case ATTRIBUTE_UNDERLINE: info.wAttributes = (short)(info.wAttributes | BACKGROUND_INTENSITY ); applyAttribute(); break; case ATTRIBUTE_UNDERLINE_OFF: info.wAttributes = (short)(info.wAttributes & ~BACKGROUND_INTENSITY ); applyAttribute(); break; case ATTRIBUTE_NEGATIVE_ON: negative = true; applyAttribute(); break; case ATTRIBUTE_NEGATIVE_OFF: negative = false; applyAttribute(); break; } } protected void processSaveCursorPosition() throws IOException { getConsoleInfo(); savedX = info.dwCursorPosition.X; savedY = info.dwCursorPosition.Y; } protected void processRestoreCursorPosition() throws IOException { // restore only if there was a save operation first if (savedX != -1 && savedY != -1) { out.flush(); info.dwCursorPosition.X = savedX; info.dwCursorPosition.Y = savedY; applyCursorPosition(); } } @Override protected void processInsertLine(int optionInt) throws IOException { getConsoleInfo(); Kernel32.SMALL_RECT scroll = new Kernel32.SMALL_RECT(info.srWindow); scroll.Top = info.dwCursorPosition.Y; Kernel32.COORD org = new Kernel32.COORD(); org.X = 0; org.Y = (short)(info.dwCursorPosition.Y + optionInt); Kernel32.CHAR_INFO info = new Kernel32.CHAR_INFO(' ', originalColors); Kernel32.INSTANCE.ScrollConsoleScreenBuffer(console, scroll, scroll, org, info); } @Override protected void processDeleteLine(int optionInt) throws IOException { getConsoleInfo(); Kernel32.SMALL_RECT scroll = new Kernel32.SMALL_RECT(info.srWindow); scroll.Top = info.dwCursorPosition.Y; Kernel32.COORD org = new Kernel32.COORD(); org.X = 0; org.Y = (short)(info.dwCursorPosition.Y - optionInt); Kernel32.CHAR_INFO info = new Kernel32.CHAR_INFO(' ', originalColors); Kernel32.INSTANCE.ScrollConsoleScreenBuffer(console, scroll, scroll, org, info); } protected void processChangeWindowTitle(String label) { Kernel32.INSTANCE.SetConsoleTitle(label); } } jline3-jline-3.3.1/terminal-jna/src/main/resources/000077500000000000000000000000001311544710100221045ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/resources/META-INF/000077500000000000000000000000001311544710100232445ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/resources/META-INF/services/000077500000000000000000000000001311544710100250675ustar00rootroot00000000000000org.jline.terminal.spi.JnaSupport000066400000000000000000000000531311544710100333300ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/main/resources/META-INF/servicesorg.jline.terminal.impl.jna.JnaSupportImpl jline3-jline-3.3.1/terminal-jna/src/test/000077500000000000000000000000001311544710100201255ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/test/java/000077500000000000000000000000001311544710100210465ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/test/java/org/000077500000000000000000000000001311544710100216355ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/test/java/org/jline/000077500000000000000000000000001311544710100227365ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/test/java/org/jline/terminal/000077500000000000000000000000001311544710100245515ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/test/java/org/jline/terminal/impl/000077500000000000000000000000001311544710100255125ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/test/java/org/jline/terminal/impl/jna/000077500000000000000000000000001311544710100262625ustar00rootroot00000000000000jline3-jline-3.3.1/terminal-jna/src/test/java/org/jline/terminal/impl/jna/JnaNativePtyTest.java000066400000000000000000000011771311544710100323470ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl.jna; import org.junit.Test; import static org.junit.Assert.assertNotNull; import static org.junit.Assume.assumeTrue; public class JnaNativePtyTest { @Test public void testDescriptor() { assumeTrue(!System.getProperty( "os.name" ).startsWith( "Windows")); assertNotNull(JnaNativePty.newDescriptor(4)); } } jline3-jline-3.3.1/terminal/000077500000000000000000000000001311544710100156115ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/pom.xml000066400000000000000000000021751311544710100171330ustar00rootroot00000000000000 4.0.0 org.jline jline-parent 3.3.1 jline-terminal JLine Terminal org.easymock easymock test junit junit test jline3-jline-3.3.1/terminal/src/000077500000000000000000000000001311544710100164005ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/000077500000000000000000000000001311544710100173245ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/java/000077500000000000000000000000001311544710100202455ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/java/org/000077500000000000000000000000001311544710100210345ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/java/org/jline/000077500000000000000000000000001311544710100221355ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/000077500000000000000000000000001311544710100237505ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/Attributes.java000066400000000000000000000236221311544710100267460ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal; import java.util.EnumMap; import java.util.EnumSet; import java.util.function.Function; import java.util.stream.Collectors; public class Attributes { /** * Control characters */ public enum ControlChar { VEOF, VEOL, VEOL2, VERASE, VWERASE, VKILL, VREPRINT, VINTR, VQUIT, VSUSP, VDSUSP, VSTART, VSTOP, VLNEXT, VDISCARD, VMIN, VTIME, VSTATUS } /** * Input flags - software input processing */ public enum InputFlag { IGNBRK, /* ignore BREAK condition */ BRKINT, /* map BREAK to SIGINTR */ IGNPAR, /* ignore (discard) parity errors */ PARMRK, /* mark parity and framing errors */ INPCK, /* enable checking of parity errors */ ISTRIP, /* strip 8th bit off chars */ INLCR, /* map NL into CR */ IGNCR, /* ignore CR */ ICRNL, /* map CR to NL (ala CRMOD) */ IXON, /* enable output flow control */ IXOFF, /* enable input flow control */ IXANY, /* any char will restart after stop */ IMAXBEL, /* ring bell on input queue full */ IUTF8 /* maintain state for UTF-8 VERASE */ } /* * Output flags - software output processing */ public enum OutputFlag { OPOST, /* enable following output processing */ ONLCR, /* map NL to CR-NL (ala CRMOD) */ OXTABS, /* expand tabs to spaces */ ONOEOT, /* discard EOT's (^D) on output) */ OCRNL, /* map CR to NL on output */ ONOCR, /* no CR output at column 0 */ ONLRET, /* NL performs CR function */ OFILL, /* use fill characters for delay */ NLDLY, /* \n delay */ TABDLY, /* horizontal tab delay */ CRDLY, /* \r delay */ FFDLY, /* form feed delay */ BSDLY, /* \b delay */ VTDLY, /* vertical tab delay */ OFDEL /* fill is DEL, else NUL */ } /* * Control flags - hardware control of terminal */ public enum ControlFlag { CIGNORE, /* ignore control flags */ CS5, /* 5 bits (pseudo) */ CS6, /* 6 bits */ CS7, /* 7 bits */ CS8, /* 8 bits */ CSTOPB, /* send 2 stop bits */ CREAD, /* enable receiver */ PARENB, /* parity enable */ PARODD, /* odd parity, else even */ HUPCL, /* hang up on last close */ CLOCAL, /* ignore modem status lines */ CCTS_OFLOW, /* CTS flow control of output */ CRTS_IFLOW, /* RTS flow control of input */ CDTR_IFLOW, /* DTR flow control of input */ CDSR_OFLOW, /* DSR flow control of output */ CCAR_OFLOW /* DCD flow control of output */ } /* * "Local" flags - dumping ground for other state * * Warning: some flags in this structure begin with * the letter "I" and look like they belong in the * input flag. */ public enum LocalFlag { ECHOKE, /* visual erase for line kill */ ECHOE, /* visually erase chars */ ECHOK, /* echo NL after line kill */ ECHO, /* enable echoing */ ECHONL, /* echo NL even if ECHO is off */ ECHOPRT, /* visual erase mode for hardcopy */ ECHOCTL, /* echo control chars as ^(Char) */ ISIG, /* enable signals INTR, QUIT, [D]SUSP */ ICANON, /* canonicalize input lines */ ALTWERASE, /* use alternate WERASE algorithm */ IEXTEN, /* enable DISCARD and LNEXT */ EXTPROC, /* external processing */ TOSTOP, /* stop background jobs from output */ FLUSHO, /* output being flushed (state) */ NOKERNINFO, /* no kernel output from VSTATUS */ PENDIN, /* XXX retype pending input (state) */ NOFLSH /* don't flush after interrupt */ } final EnumSet iflag = EnumSet.noneOf(InputFlag.class); final EnumSet oflag = EnumSet.noneOf(OutputFlag.class); final EnumSet cflag = EnumSet.noneOf(ControlFlag.class); final EnumSet lflag = EnumSet.noneOf(LocalFlag.class); final EnumMap cchars = new EnumMap<>(ControlChar.class); public Attributes() { } public Attributes(Attributes attr) { copy(attr); } // // Input flags // public EnumSet getInputFlags() { return iflag; } public void setInputFlags(EnumSet flags) { iflag.clear(); iflag.addAll(flags); } public boolean getInputFlag(InputFlag flag) { return iflag.contains(flag); } public void setInputFlags(EnumSet flags, boolean value) { if (value) { iflag.addAll(flags); } else { iflag.removeAll(flags); } } public void setInputFlag(InputFlag flag, boolean value) { if (value) { iflag.add(flag); } else { iflag.remove(flag); } } // // Output flags // public EnumSet getOutputFlags() { return oflag; } public void setOutputFlags(EnumSet flags) { oflag.clear(); oflag.addAll(flags); } public boolean getOutputFlag(OutputFlag flag) { return oflag.contains(flag); } public void setOutputFlags(EnumSet flags, boolean value) { if (value) { oflag.addAll(flags); } else { oflag.removeAll(flags); } } public void setOutputFlag(OutputFlag flag, boolean value) { if (value) { oflag.add(flag); } else { oflag.remove(flag); } } // // Control flags // public EnumSet getControlFlags() { return cflag; } public void setControlFlags(EnumSet flags) { cflag.clear(); cflag.addAll(flags); } public boolean getControlFlag(ControlFlag flag) { return cflag.contains(flag); } public void setControlFlags(EnumSet flags, boolean value) { if (value) { cflag.addAll(flags); } else { cflag.removeAll(flags); } } public void setControlFlag(ControlFlag flag, boolean value) { if (value) { cflag.add(flag); } else { cflag.remove(flag); } } // // Local flags // public EnumSet getLocalFlags() { return lflag; } public void setLocalFlags(EnumSet flags) { lflag.clear(); lflag.addAll(flags); } public boolean getLocalFlag(LocalFlag flag) { return lflag.contains(flag); } public void setLocalFlags(EnumSet flags, boolean value) { if (value) { lflag.addAll(flags); } else { lflag.removeAll(flags); } } public void setLocalFlag(LocalFlag flag, boolean value) { if (value) { lflag.add(flag); } else { lflag.remove(flag); } } // // Control chars // public EnumMap getControlChars() { return cchars; } public void setControlChars(EnumMap chars) { cchars.clear(); cchars.putAll(chars); } public int getControlChar(ControlChar c) { return cchars.getOrDefault(c, -1); } public void setControlChar(ControlChar c, int value) { cchars.put(c, value); } // // Miscellaneous methods // public void copy(Attributes attributes) { setControlFlags(attributes.getControlFlags()); setInputFlags(attributes.getInputFlags()); setLocalFlags(attributes.getLocalFlags()); setOutputFlags(attributes.getOutputFlags()); setControlChars(attributes.getControlChars()); } @Override public String toString() { return "Attributes[" + "lflags: " + append(lflag) + ", " + "iflags: " + append(iflag) + ", " + "oflags: " + append(oflag) + ", " + "cflags: " + append(cflag) + ", " + "cchars: " + append(EnumSet.allOf(ControlChar.class), this::display) + "]"; } private String display(ControlChar c) { String value; int ch = getControlChar(c); if (c == ControlChar.VMIN || c == ControlChar.VTIME) { value = Integer.toString(ch); } else if (ch < 0) { value = ""; } else if (ch < 32) { value = "^" + (char) (ch + 'A' - 1); } else if (ch == 127) { value = "^?"; } else if (ch >= 128) { value = String.format("\\u%04x", ch); } else { value = String.valueOf((char) ch); } return c.name().toLowerCase().substring(1) + "=" + value; } private > String append(EnumSet set) { return append(set, e -> e.name().toLowerCase()); } private > String append(EnumSet set, Function toString) { return set.stream().map(toString).collect(Collectors.joining(" ")); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/Cursor.java000066400000000000000000000020641311544710100260720ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal; /** * Class holding the cursor position. * * @see Terminal#getCursorPosition(java.util.function.IntConsumer) */ public class Cursor { private final int x; private final int y; public Cursor(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } @Override public boolean equals(Object o) { if (o instanceof Cursor) { Cursor c = (Cursor) o; return x == c.x && y == c.y; } else { return false; } } @Override public int hashCode() { return x * 31 + y; } @Override public String toString() { return "Cursor[" + "x=" + x + ", y=" + y + ']'; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/MouseEvent.java000066400000000000000000000031551311544710100267110ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal; import java.util.EnumSet; public class MouseEvent { public enum Type { Released, Pressed, Wheel, Moved, Dragged } public enum Button { NoButton, Button1, Button2, Button3, WheelUp, WheelDown } public enum Modifier { Shift, Alt, Control } private final Type type; private final Button button; private final EnumSet modifiers; private final int x; private final int y; public MouseEvent(Type type, Button button, EnumSet modifiers, int x, int y) { this.type = type; this.button = button; this.modifiers = modifiers; this.x = x; this.y = y; } public Type getType() { return type; } public Button getButton() { return button; } public EnumSet getModifiers() { return modifiers; } public int getX() { return x; } public int getY() { return y; } @Override public String toString() { return "MouseEvent[" + "type=" + type + ", button=" + button + ", modifiers=" + modifiers + ", x=" + x + ", y=" + y + ']'; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/Size.java000066400000000000000000000031021311544710100255210ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal; public class Size { private int rows; private int cols; public Size() { } public Size(int columns, int rows) { this(); setColumns(columns); setRows(rows); } public int getColumns() { return cols; } public void setColumns(int columns) { cols = (short) columns; } public int getRows() { return rows; } public void setRows(int rows) { this.rows = (short) rows; } /** A cursor position combines a row number with a column position. * Note each row has {@code col+1} different column positions, * including the right margin. */ public int cursorPos(int row, int col) { return row * (cols+1) + col; } public void copy(Size size) { setColumns(size.getColumns()); setRows(size.getRows()); } @Override public boolean equals(Object o) { if (o instanceof Size) { Size size = (Size) o; return rows == size.rows && cols == size.cols; } else { return false; } } @Override public int hashCode() { return rows * 31 + cols; } @Override public String toString() { return "Size[" + "cols=" + cols + ", rows=" + rows + ']'; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/Terminal.java000066400000000000000000000112771311544710100263760ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal; import java.io.Closeable; import java.io.Flushable; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.util.function.IntConsumer; import java.util.function.IntSupplier; import org.jline.terminal.impl.NativeSignalHandler; import org.jline.utils.InfoCmp.Capability; import org.jline.utils.NonBlockingReader; /** * A terminal representing a virtual terminal on the computer. * * Terminals should be closed by calling the {@link #close()} method * in order to restore their original state. */ public interface Terminal extends Closeable, Flushable { /** * Type used for dumb terminals. */ String TYPE_DUMB = "dumb"; String getName(); // // Signal support // enum Signal { INT, QUIT, TSTP, CONT, INFO, WINCH } interface SignalHandler { SignalHandler SIG_DFL = NativeSignalHandler.SIG_DFL; SignalHandler SIG_IGN = NativeSignalHandler.SIG_IGN; void handle(Signal signal); } SignalHandler handle(Signal signal, SignalHandler handler); void raise(Signal signal); // // Input / output // NonBlockingReader reader(); PrintWriter writer(); InputStream input(); OutputStream output(); // // Pty settings // Attributes enterRawMode(); boolean echo(); boolean echo(boolean echo); Attributes getAttributes(); void setAttributes(Attributes attr); Size getSize(); void setSize(Size size); default int getWidth() { return getSize().getColumns(); } default int getHeight() { return getSize().getRows(); } void flush(); // // Infocmp capabilities // String getType(); boolean puts(Capability capability, Object... params); boolean getBooleanCapability(Capability capability); Integer getNumericCapability(Capability capability); String getStringCapability(Capability capability); // // Cursor support // /** * Query the terminal to report the cursor position. * * As the response is read from the input stream, some * characters may be read before the cursor position is actually * read. Those characters can be given back using * {@link org.jline.keymap.BindingReader#runMacro(String)}. * * @param discarded a consumer receiving discarded characters * @return null if cursor position reporting * is not supported or a valid cursor position */ Cursor getCursorPosition(IntConsumer discarded); // // Mouse support // enum MouseTracking { /** * Disable mouse tracking */ Off, /** * Track button press and release. */ Normal, /** * Also report button-motion events. Mouse movements are reported if the mouse pointer * has moved to a different character cell. */ Button, /** * Report all motions events, even if no mouse button is down. */ Any } /** * Returns true if the terminal has support for mouse. * @return whether mouse is supported by the terminal * @see #trackMouse(MouseTracking) */ boolean hasMouseSupport(); /** * Change the mouse tracking mouse. * To start mouse tracking, this method must be called with a valid mouse tracking mode. * Mouse events will be reported by writing the {@link Capability#key_mouse} to the input stream. * When this character sequence is detected, the {@link #readMouseEvent()} method can be * called to actually read the corresponding mouse event. * * @param tracking the mouse tracking mode * @return true if mouse tracking is supported */ boolean trackMouse(MouseTracking tracking); /** * Read a MouseEvent from the terminal input stream. * Such an event must have been detected by scanning the terminal's {@link Capability#key_mouse} * in the stream immediately before reading the event. * * @return the decoded mouse event. * @see #trackMouse(MouseTracking) */ MouseEvent readMouseEvent(); /** * Read a MouseEvent from the given input stream. * * @param reader the input supplier * @return the decoded mouse event */ MouseEvent readMouseEvent(IntSupplier reader); } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java000066400000000000000000000301051311544710100276740ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.ServiceLoader; import org.jline.terminal.impl.AbstractPosixTerminal; import org.jline.terminal.impl.DumbTerminal; import org.jline.terminal.impl.ExecPty; import org.jline.terminal.impl.ExternalTerminal; import org.jline.terminal.impl.PosixPtyTerminal; import org.jline.terminal.impl.PosixSysTerminal; import org.jline.terminal.spi.JansiSupport; import org.jline.terminal.spi.JnaSupport; import org.jline.terminal.spi.Pty; import org.jline.utils.Log; import org.jline.utils.OSUtils; /** * Builder class to create terminals. */ public final class TerminalBuilder { // // System properties // public static final String PROP_ENCODING = "org.jline.terminal.encoding"; public static final String PROP_TYPE = "org.jline.terminal.type"; public static final String PROP_JNA = "org.jline.terminal.jna"; public static final String PROP_JANSI = "org.jline.terminal.jansi"; public static final String PROP_EXEC = "org.jline.terminal.exec"; public static final String PROP_DUMB = "org.jline.terminal.dumb"; /** * Returns the default system terminal. * Terminals should be closed properly using the {@link Terminal#close()} * method in order to restore the original terminal state. * * This call is equivalent to: * builder().build() */ public static Terminal terminal() throws IOException { return builder().build(); } /** * Creates a new terminal builder instance. */ public static TerminalBuilder builder() { return new TerminalBuilder(); } private String name; private InputStream in; private OutputStream out; private String type; private String encoding; private Boolean system; private Boolean jna; private Boolean jansi; private Boolean exec; private Boolean dumb; private Attributes attributes; private Size size; private boolean nativeSignals = false; private Terminal.SignalHandler signalHandler = Terminal.SignalHandler.SIG_DFL; private TerminalBuilder() { } public TerminalBuilder name(String name) { this.name = name; return this; } public TerminalBuilder streams(InputStream in, OutputStream out) { this.in = in; this.out = out; return this; } public TerminalBuilder system(boolean system) { this.system = system; return this; } public TerminalBuilder jna(boolean jna) { this.jna = jna; return this; } public TerminalBuilder jansi(boolean jansi) { this.jansi = jansi; return this; } public TerminalBuilder exec(boolean exec) { this.exec = exec; return this; } public TerminalBuilder dumb(boolean dumb) { this.dumb = dumb; return this; } public TerminalBuilder type(String type) { this.type = type; return this; } public TerminalBuilder encoding(String encoding) { this.encoding = encoding; return this; } /** * Attributes to use when creating a non system terminal, * i.e. when the builder has been given the input and * outut streams using the {@link #streams(InputStream, OutputStream)} method * or when {@link #system(boolean)} has been explicitely called with * false. * * @see #size(Size) * @see #system(boolean) */ public TerminalBuilder attributes(Attributes attributes) { this.attributes = attributes; return this; } /** * Initial size to use when creating a non system terminal, * i.e. when the builder has been given the input and * outut streams using the {@link #streams(InputStream, OutputStream)} method * or when {@link #system(boolean)} has been explicitely called with * false. * * @see #attributes(Attributes) * @see #system(boolean) */ public TerminalBuilder size(Size size) { this.size = size; return this; } public TerminalBuilder nativeSignals(boolean nativeSignals) { this.nativeSignals = nativeSignals; return this; } public TerminalBuilder signalHandler(Terminal.SignalHandler signalHandler) { this.signalHandler = signalHandler; return this; } public Terminal build() throws IOException { Terminal terminal = doBuild(); Log.debug(() -> "Using terminal " + terminal.getClass().getSimpleName()); if (terminal instanceof AbstractPosixTerminal) { Log.debug(() -> "Using pty " + ((AbstractPosixTerminal) terminal).getPty().getClass().getSimpleName()); } return terminal; } private Terminal doBuild() throws IOException { String name = this.name; if (name == null) { name = "JLine terminal"; } String encoding = this.encoding; if (encoding == null) { encoding = System.getProperty(PROP_ENCODING); } if (encoding == null) { encoding = Charset.defaultCharset().name(); } String type = this.type; if (type == null) { type = System.getProperty(PROP_TYPE); } if (type == null) { type = System.getenv("TERM"); } Boolean jna = this.jna; if (jna == null) { jna = getBoolean(PROP_JNA, true); } Boolean jansi = this.jansi; if (jansi == null) { jansi = getBoolean(PROP_JANSI, true); } Boolean exec = this.exec; if (exec == null) { exec = getBoolean(PROP_EXEC, true); } Boolean dumb = this.dumb; if (dumb == null) { dumb = getBoolean(PROP_DUMB, null); } if ((system != null && system) || (system == null && in == null && out == null)) { if (attributes != null || size != null) { Log.warn("Attributes and size fields are ignored when creating a system terminal"); } IllegalStateException exception = new IllegalStateException("Unable to create a system terminal"); // // Cygwin support // if (OSUtils.IS_CYGWIN || OSUtils.IS_MINGW) { if (exec) { try { Pty pty = ExecPty.current(); // Cygwin defaults to XTERM, but actually supports 256 colors, // so if the value comes from the environment, change it to xterm-256color if ("xterm".equals(type) && this.type == null && System.getProperty(PROP_TYPE) == null) { type = "xterm-256color"; } return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler); } catch (IOException e) { // Ignore if not a tty Log.debug("Error creating EXEC based terminal: ", e.getMessage(), e); exception.addSuppressed(e); } } } else if (OSUtils.IS_WINDOWS) { if (jna) { try { return load(JnaSupport.class).winSysTerminal(name, nativeSignals, signalHandler); } catch (Throwable t) { Log.debug("Error creating JNA based terminal: ", t.getMessage(), t); exception.addSuppressed(t); } } if (jansi) { try { return load(JansiSupport.class).winSysTerminal(name, nativeSignals, signalHandler); } catch (Throwable t) { Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t); exception.addSuppressed(t); } } } else { if (jna) { try { Pty pty = load(JnaSupport.class).current(); return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler); } catch (Throwable t) { // ignore Log.debug("Error creating JNA based terminal: ", t.getMessage(), t); exception.addSuppressed(t); } } if (jansi) { try { Pty pty = load(JansiSupport.class).current(); return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler); } catch (Throwable t) { Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t); exception.addSuppressed(t); } } if (exec) { try { Pty pty = ExecPty.current(); return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler); } catch (Throwable t) { // Ignore if not a tty Log.debug("Error creating EXEC based terminal: ", t.getMessage(), t); exception.addSuppressed(t); } } } if (dumb == null || dumb) { if (dumb == null) { if (Log.isDebugEnabled()) { Log.warn("Creating a dumb terminal", exception); } else { Log.warn("Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)"); } } return new DumbTerminal(name, type != null ? type : Terminal.TYPE_DUMB, new FileInputStream(FileDescriptor.in), new FileOutputStream(FileDescriptor.out), encoding, signalHandler); } else { throw exception; } } else { if (jna) { try { Pty pty = load(JnaSupport.class).open(attributes, size); return new PosixPtyTerminal(name, type, pty, in, out, encoding, signalHandler); } catch (Throwable t) { Log.debug("Error creating JNA based terminal: ", t.getMessage(), t); } } if (jansi) { try { Pty pty = load(JansiSupport.class).open(attributes, size); return new PosixPtyTerminal(name, type, pty, in, out, encoding, signalHandler); } catch (Throwable t) { Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t); } } Terminal terminal = new ExternalTerminal(name, type, in, out, encoding, signalHandler); if (attributes != null) { terminal.setAttributes(attributes); } if (size != null) { terminal.setSize(size); } return terminal; } } private static Boolean getBoolean(String name, Boolean def) { try { String str = System.getProperty(name); if (str != null) { return Boolean.parseBoolean(str); } } catch (IllegalArgumentException | NullPointerException e) { } return def; } private S load(Class clazz) { return ServiceLoader.load(clazz, clazz.getClassLoader()).iterator().next(); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/000077500000000000000000000000001311544710100247115ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/AbstractPosixTerminal.java000066400000000000000000000041371311544710100320430ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import java.io.IOError; import java.io.IOException; import java.util.Objects; import java.util.function.IntConsumer; import org.jline.terminal.Attributes; import org.jline.terminal.Cursor; import org.jline.terminal.Size; import org.jline.terminal.spi.Pty; public abstract class AbstractPosixTerminal extends AbstractTerminal { protected final Pty pty; protected final Attributes originalAttributes; public AbstractPosixTerminal(String name, String type, Pty pty) throws IOException { this(name, type, pty, SignalHandler.SIG_DFL); } public AbstractPosixTerminal(String name, String type, Pty pty, SignalHandler signalHandler) throws IOException { super(name, type, signalHandler); Objects.requireNonNull(pty); this.pty = pty; this.originalAttributes = this.pty.getAttr(); } public Pty getPty() { return pty; } public Attributes getAttributes() { try { return pty.getAttr(); } catch (IOException e) { throw new IOError(e); } } public void setAttributes(Attributes attr) { try { pty.setAttr(attr); } catch (IOException e) { throw new IOError(e); } } public Size getSize() { try { return pty.getSize(); } catch (IOException e) { throw new IOError(e); } } public void setSize(Size size) { try { pty.setSize(size); } catch (IOException e) { throw new IOError(e); } } public void close() throws IOException { pty.setAttr(originalAttributes); pty.close(); } @Override public Cursor getCursorPosition(IntConsumer discarded) { return CursorSupport.getCursorPosition(this, discarded); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/AbstractTerminal.java000066400000000000000000000136571311544710100310270ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import java.io.IOError; import java.io.IOException; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.IntConsumer; import java.util.function.IntSupplier; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Cursor; import org.jline.terminal.MouseEvent; import org.jline.terminal.Terminal; import org.jline.utils.Curses; import org.jline.utils.InfoCmp; import org.jline.utils.InfoCmp.Capability; import org.jline.utils.Log; public abstract class AbstractTerminal implements Terminal { protected final String name; protected final String type; protected final Map handlers = new HashMap<>(); protected final Set bools = new HashSet<>(); protected final Map ints = new HashMap<>(); protected final Map strings = new HashMap<>(); public AbstractTerminal(String name, String type) throws IOException { this(name, type, SignalHandler.SIG_DFL); } public AbstractTerminal(String name, String type, SignalHandler signalHandler) throws IOException { this.name = name; this.type = type; for (Signal signal : Signal.values()) { handlers.put(signal, signalHandler); } } public SignalHandler handle(Signal signal, SignalHandler handler) { Objects.requireNonNull(signal); Objects.requireNonNull(handler); return handlers.put(signal, handler); } public void raise(Signal signal) { Objects.requireNonNull(signal); SignalHandler handler = handlers.get(signal); if (handler != SignalHandler.SIG_DFL && handler != SignalHandler.SIG_IGN) { handler.handle(signal); } } protected void echoSignal(Signal signal) { ControlChar cc = null; switch (signal) { case INT: cc = ControlChar.VINTR; break; case QUIT: cc = ControlChar.VQUIT; break; case TSTP: cc = ControlChar.VSUSP; break; } if (cc != null) { int vcc = getAttributes().getControlChar(cc); if (vcc > 0 && vcc < 32) { writer().write(new char[]{'^', (char) (vcc + '@')}, 0, 2); } } } public Attributes enterRawMode() { Attributes prvAttr = getAttributes(); Attributes newAttr = new Attributes(prvAttr); newAttr.setLocalFlags(EnumSet.of(LocalFlag.ICANON, LocalFlag.ECHO, LocalFlag.IEXTEN), false); newAttr.setInputFlags(EnumSet.of(InputFlag.IXON, InputFlag.ICRNL, InputFlag.INLCR), false); newAttr.setControlChar(ControlChar.VMIN, 1); newAttr.setControlChar(ControlChar.VTIME, 0); setAttributes(newAttr); return prvAttr; } public boolean echo() { return getAttributes().getLocalFlag(LocalFlag.ECHO); } public boolean echo(boolean echo) { Attributes attr = getAttributes(); boolean prev = attr.getLocalFlag(LocalFlag.ECHO); if (prev != echo) { attr.setLocalFlag(LocalFlag.ECHO, echo); setAttributes(attr); } return prev; } public String getName() { return name; } public String getType() { return type; } public String getKind() { return getClass().getSimpleName(); } public void flush() { writer().flush(); } public boolean puts(Capability capability, Object... params) { String str = getStringCapability(capability); if (str == null) { return false; } try { Curses.tputs(writer(), str, params); } catch (IOException e) { throw new IOError(e); } return true; } public boolean getBooleanCapability(Capability capability) { return bools.contains(capability); } public Integer getNumericCapability(Capability capability) { return ints.get(capability); } public String getStringCapability(Capability capability) { return strings.get(capability); } protected void parseInfoCmp() { String capabilities = null; if (type != null) { try { capabilities = InfoCmp.getInfoCmp(type); } catch (Exception e) { Log.warn("Unable to retrieve infocmp for type " + type, e); } } if (capabilities == null) { capabilities = InfoCmp.getLoadedInfoCmp("ansi"); } InfoCmp.parseInfoCmp(capabilities, bools, ints, strings); } @Override public Cursor getCursorPosition(IntConsumer discarded) { return null; } private MouseEvent lastMouseEvent = new MouseEvent( MouseEvent.Type.Moved, MouseEvent.Button.NoButton, EnumSet.noneOf(MouseEvent.Modifier.class), 0, 0); @Override public boolean hasMouseSupport() { return MouseSupport.hasMouseSupport(this); } @Override public boolean trackMouse(MouseTracking tracking) { return MouseSupport.trackMouse(this, tracking); } @Override public MouseEvent readMouseEvent() { return lastMouseEvent = MouseSupport.readMouse(this, lastMouseEvent); } @Override public MouseEvent readMouseEvent(IntSupplier reader) { return lastMouseEvent = MouseSupport.readMouse(reader, lastMouseEvent); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/AbstractWindowsTerminal.java000066400000000000000000000300511311544710100323650ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.utils.Curses; import org.jline.utils.InfoCmp; import org.jline.utils.Log; import org.jline.utils.NonBlockingReader; import org.jline.utils.ShutdownHooks; import org.jline.utils.Signals; import java.io.FilterInputStream; import java.io.InputStream; import java.io.IOError; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; public abstract class AbstractWindowsTerminal extends AbstractTerminal { private static final int PIPE_SIZE = 1024; protected static final int ENABLE_PROCESSED_INPUT = 0x0001; protected static final int ENABLE_LINE_INPUT = 0x0002; protected static final int ENABLE_ECHO_INPUT = 0x0004; protected static final int ENABLE_WINDOW_INPUT = 0x0008; protected static final int ENABLE_MOUSE_INPUT = 0x0010; protected static final int ENABLE_INSERT_MODE = 0x0020; protected static final int ENABLE_QUICK_EDIT_MODE = 0x0040; protected final OutputStream slaveInputPipe; protected final InputStream input; protected final OutputStream output; protected final NonBlockingReader reader; protected final PrintWriter writer; protected final Map nativeHandlers = new HashMap<>(); protected final ShutdownHooks.Task closer; protected final Attributes attributes = new Attributes(); protected final Thread pump; protected MouseTracking tracking = MouseTracking.Off; private volatile boolean closing; public AbstractWindowsTerminal(OutputStream output, String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException { super(name, "windows", signalHandler); PipedInputStream input = new PipedInputStream(PIPE_SIZE); this.slaveInputPipe = new PipedOutputStream(input); this.input = new FilterInputStream(input) {}; this.output = output; String encoding = getConsoleEncoding(); if (encoding == null) { encoding = Charset.defaultCharset().name(); } this.reader = new NonBlockingReader(getName(), new org.jline.utils.InputStreamReader(input, encoding)); this.writer = new PrintWriter(new OutputStreamWriter(output, encoding)); parseInfoCmp(); // Attributes attributes.setLocalFlag(Attributes.LocalFlag.ISIG, true); attributes.setControlChar(Attributes.ControlChar.VINTR, ctrl('C')); attributes.setControlChar(Attributes.ControlChar.VEOF, ctrl('D')); attributes.setControlChar(Attributes.ControlChar.VSUSP, ctrl('Z')); // Handle signals if (nativeSignals) { for (final Signal signal : Signal.values()) { if (signalHandler == SignalHandler.SIG_DFL) { nativeHandlers.put(signal, Signals.registerDefault(signal.name())); } else { nativeHandlers.put(signal, Signals.register(signal.name(), () -> raise(signal))); } } } pump = new Thread(this::pump, "WindowsStreamPump"); pump.setDaemon(true); pump.start(); closer = this::close; ShutdownHooks.add(closer); } @Override public SignalHandler handle(Signal signal, SignalHandler handler) { SignalHandler prev = super.handle(signal, handler); if (prev != handler) { if (handler == SignalHandler.SIG_DFL) { Signals.registerDefault(signal.name()); } else { Signals.register(signal.name(), () -> raise(signal)); } } return prev; } protected String getConsoleEncoding() { int codepage = getConsoleOutputCP(); //http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html String charsetMS = "ms" + codepage; if (java.nio.charset.Charset.isSupported(charsetMS)) { return charsetMS; } String charsetCP = "cp" + codepage; if (java.nio.charset.Charset.isSupported(charsetCP)) { return charsetCP; } return null; } protected abstract int getConsoleOutputCP(); public NonBlockingReader reader() { return reader; } public PrintWriter writer() { return writer; } @Override public InputStream input() { return input; } @Override public OutputStream output() { return output; } public Attributes getAttributes() { int mode = getConsoleMode(); if ((mode & ENABLE_ECHO_INPUT) != 0) { attributes.setLocalFlag(Attributes.LocalFlag.ECHO, true); } if ((mode & ENABLE_LINE_INPUT) != 0) { attributes.setLocalFlag(Attributes.LocalFlag.ICANON, true); } return new Attributes(attributes); } public void setAttributes(Attributes attr) { attributes.copy(attr); updateConsoleMode(); } protected void updateConsoleMode() { int mode = ENABLE_WINDOW_INPUT; if (attributes.getLocalFlag(Attributes.LocalFlag.ECHO)) { mode |= ENABLE_ECHO_INPUT; } if (attributes.getLocalFlag(Attributes.LocalFlag.ICANON)) { mode |= ENABLE_LINE_INPUT; } if (tracking != MouseTracking.Off) { mode |= ENABLE_MOUSE_INPUT; } setConsoleMode(mode); } protected int ctrl(char key) { return (Character.toUpperCase(key) & 0x1f); } protected abstract int getConsoleMode(); protected abstract void setConsoleMode(int mode); public void setSize(Size size) { throw new UnsupportedOperationException("Can not resize windows terminal"); } public void close() throws IOException { closing = true; pump.interrupt(); ShutdownHooks.remove(closer); for (Map.Entry entry : nativeHandlers.entrySet()) { Signals.unregister(entry.getKey().name(), entry.getValue()); } reader.close(); writer.close(); } protected abstract byte[] readConsoleInput() throws IOException; protected String getEscapeSequence(short keyCode) { // virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx // TODO: numpad keys, modifiers String escapeSequence = null; switch (keyCode) { case 0x08: // VK_BACK BackSpace escapeSequence = getSequence(InfoCmp.Capability.key_backspace); break; case 0x21: // VK_PRIOR PageUp escapeSequence = getSequence(InfoCmp.Capability.key_ppage); break; case 0x22: // VK_NEXT PageDown escapeSequence = getSequence(InfoCmp.Capability.key_npage); break; case 0x23: // VK_END escapeSequence = getSequence(InfoCmp.Capability.key_end); break; case 0x24: // VK_HOME escapeSequence = getSequence(InfoCmp.Capability.key_home); break; case 0x25: // VK_LEFT escapeSequence = getSequence(InfoCmp.Capability.key_left); break; case 0x26: // VK_UP escapeSequence = getSequence(InfoCmp.Capability.key_up); break; case 0x27: // VK_RIGHT escapeSequence = getSequence(InfoCmp.Capability.key_right); break; case 0x28: // VK_DOWN escapeSequence = getSequence(InfoCmp.Capability.key_down); break; case 0x2D: // VK_INSERT escapeSequence = getSequence(InfoCmp.Capability.key_ic); break; case 0x2E: // VK_DELETE escapeSequence = getSequence(InfoCmp.Capability.key_dc); break; case 0x70: // VK_F1 escapeSequence = getSequence(InfoCmp.Capability.key_f1); break; case 0x71: // VK_F2 escapeSequence = getSequence(InfoCmp.Capability.key_f2); break; case 0x72: // VK_F3 escapeSequence = getSequence(InfoCmp.Capability.key_f3); break; case 0x73: // VK_F4 escapeSequence = getSequence(InfoCmp.Capability.key_f4); break; case 0x74: // VK_F5 escapeSequence = getSequence(InfoCmp.Capability.key_f5); break; case 0x75: // VK_F6 escapeSequence = getSequence(InfoCmp.Capability.key_f6); break; case 0x76: // VK_F7 escapeSequence = getSequence(InfoCmp.Capability.key_f7); break; case 0x77: // VK_F8 escapeSequence = getSequence(InfoCmp.Capability.key_f8); break; case 0x78: // VK_F9 escapeSequence = getSequence(InfoCmp.Capability.key_f9); break; case 0x79: // VK_F10 escapeSequence = getSequence(InfoCmp.Capability.key_f10); break; case 0x7A: // VK_F11 escapeSequence = getSequence(InfoCmp.Capability.key_f11); break; case 0x7B: // VK_F12 escapeSequence = getSequence(InfoCmp.Capability.key_f12); break; default: break; } return escapeSequence; } protected String getSequence(InfoCmp.Capability cap) { String str = strings.get(cap); if (str != null) { StringWriter sw = new StringWriter(); try { Curses.tputs(sw, str); } catch (IOException e) { throw new IOError(e); } return sw.toString(); } return null; } protected void pump() { try { while (!closing) { byte[] buf = readConsoleInput(); for (byte b : buf) { processInputByte(b); } } } catch (IOException e) { if (!closing) { Log.warn("Error in WindowsStreamPump", e); } } } public void processInputByte(int c) throws IOException { if (attributes.getLocalFlag(Attributes.LocalFlag.ISIG)) { if (c == attributes.getControlChar(Attributes.ControlChar.VINTR)) { raise(Signal.INT); return; } else if (c == attributes.getControlChar(Attributes.ControlChar.VQUIT)) { raise(Signal.QUIT); return; } else if (c == attributes.getControlChar(Attributes.ControlChar.VSUSP)) { raise(Signal.TSTP); return; } else if (c == attributes.getControlChar(Attributes.ControlChar.VSTATUS)) { raise(Signal.INFO); } } if (c == '\r') { if (attributes.getInputFlag(Attributes.InputFlag.IGNCR)) { return; } if (attributes.getInputFlag(Attributes.InputFlag.ICRNL)) { c = '\n'; } } else if (c == '\n' && attributes.getInputFlag(Attributes.InputFlag.INLCR)) { c = '\r'; } // if (attributes.getLocalFlag(Attributes.LocalFlag.ECHO)) { // processOutputByte(c); // masterOutput.flush(); // } slaveInputPipe.write(c); slaveInputPipe.flush(); } @Override public boolean trackMouse(MouseTracking tracking) { this.tracking = tracking; updateConsoleMode(); return true; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/CursorSupport.java000066400000000000000000000075051311544710100304350ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import org.jline.terminal.Cursor; import org.jline.terminal.Terminal; import org.jline.utils.Curses; import org.jline.utils.InfoCmp; import java.io.IOError; import java.io.IOException; import java.util.function.IntConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; public class CursorSupport { public static Cursor getCursorPosition(Terminal terminal, IntConsumer discarded) { try { String u6 = terminal.getStringCapability(InfoCmp.Capability.user6); String u7 = terminal.getStringCapability(InfoCmp.Capability.user7); if (u6 == null || u7 == null) { return null; } // Prepare parser boolean inc1 = false; StringBuilder patb = new StringBuilder(); int index = 0; while (index < u6.length()) { char ch; switch (ch = u6.charAt(index++)) { case '\\': switch (u6.charAt(index++)) { case 'e': case 'E': patb.append("\\x1b"); break; default: throw new IllegalArgumentException(); } break; case '%': ch = u6.charAt(index++); switch (ch) { case '%': patb.append('%'); break; case 'i': inc1 = true; break; case 'd': patb.append("([0-9]+)"); break; default: throw new IllegalArgumentException(); } break; default: switch (ch) { case '[': patb.append('\\'); break; } patb.append(ch); break; } } Pattern pattern = Pattern.compile(patb.toString()); // Output cursor position request Curses.tputs(terminal.writer(), u7); terminal.flush(); StringBuilder sb = new StringBuilder(); int start = 0; while (true) { int c = terminal.reader().read(); if (c < 0) { return null; } sb.append((char) c); Matcher matcher = pattern.matcher(sb.substring(start)); if (matcher.matches()) { int y = Integer.parseInt(matcher.group(1)); int x = Integer.parseInt(matcher.group(2)); if (inc1) { x--; y--; } if (discarded != null) { for (int i = 0; i < start; i++) { discarded.accept(sb.charAt(i)); } } return new Cursor(x, y); } else if (!matcher.hitEnd()) { start++; } } } catch (IOException e) { throw new IOError(e); } } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/DumbTerminal.java000066400000000000000000000116361311544710100301460ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.Charset; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Size; import org.jline.utils.NonBlockingReader; public class DumbTerminal extends AbstractTerminal { private final InputStream input; private final OutputStream output; private final NonBlockingReader reader; private final PrintWriter writer; private final Attributes attributes; private final Size size; public DumbTerminal(InputStream in, OutputStream out) throws IOException { this(TYPE_DUMB, TYPE_DUMB, in, out, Charset.defaultCharset().name()); } public DumbTerminal(String name, String type, InputStream in, OutputStream out, String encoding) throws IOException { this(name, type, in, out, encoding, SignalHandler.SIG_DFL); } public DumbTerminal(String name, String type, InputStream in, OutputStream out, String encoding, SignalHandler signalHandler) throws IOException { super(name, type, signalHandler); this.input = new InputStream() { @Override public int read() throws IOException { for (;;) { int c = in.read(); if (attributes.getLocalFlag(Attributes.LocalFlag.ISIG)) { if (c == attributes.getControlChar(ControlChar.VINTR)) { raise(Signal.INT); continue; } else if (c == attributes.getControlChar(ControlChar.VQUIT)) { raise(Signal.QUIT); continue; } else if (c == attributes.getControlChar(ControlChar.VSUSP)) { raise(Signal.TSTP); continue; } else if (c == attributes.getControlChar(ControlChar.VSTATUS)) { raise(Signal.INFO); continue; } } if (c == '\r') { if (attributes.getInputFlag(Attributes.InputFlag.IGNCR)) { continue; } if (attributes.getInputFlag(Attributes.InputFlag.ICRNL)) { c = '\n'; } } else if (c == '\n' && attributes.getInputFlag(Attributes.InputFlag.INLCR)) { c = '\r'; } return c; } } public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; return 1; } }; this.output = out; this.reader = new NonBlockingReader(getName(), new InputStreamReader(input, encoding)); this.writer = new PrintWriter(new OutputStreamWriter(output, encoding)); this.attributes = new Attributes(); this.attributes.setControlChar(ControlChar.VERASE, (char) 127); this.attributes.setControlChar(ControlChar.VWERASE, (char) 23); this.attributes.setControlChar(ControlChar.VKILL, (char) 21); this.attributes.setControlChar(ControlChar.VLNEXT, (char) 22); this.size = new Size(); parseInfoCmp(); } public NonBlockingReader reader() { return reader; } public PrintWriter writer() { return writer; } @Override public InputStream input() { return input; } @Override public OutputStream output() { return output; } public Attributes getAttributes() { Attributes attr = new Attributes(); attr.copy(attributes); return attr; } public void setAttributes(Attributes attr) { attributes.copy(attr); } public Size getSize() { Size sz = new Size(); sz.copy(size); return sz; } public void setSize(Size sz) { size.copy(sz); } public void close() throws IOException { } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/ExecPty.java000066400000000000000000000241511311544710100271400ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.FileDescriptor; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.ControlFlag; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Attributes.OutputFlag; import org.jline.terminal.Size; import org.jline.terminal.spi.Pty; import org.jline.utils.OSUtils; import static org.jline.utils.ExecHelper.exec; public class ExecPty implements Pty { private final String name; private final boolean system; public static Pty current() throws IOException { try { String result = exec(true, OSUtils.TTY_COMMAND); return new ExecPty(result.trim(), true); } catch (IOException e) { throw new IOException("Not a tty", e); } } protected ExecPty(String name, boolean system) { this.name = name; this.system = system; } @Override public void close() throws IOException { } public String getName() { return name; } @Override public InputStream getMasterInput() { throw new UnsupportedOperationException(); } @Override public OutputStream getMasterOutput() { throw new UnsupportedOperationException(); } @Override public InputStream getSlaveInput() throws IOException { return system ? new FileInputStream(FileDescriptor.in) : new FileInputStream(getName()); } @Override public OutputStream getSlaveOutput() throws IOException { return system ? new FileOutputStream(FileDescriptor.out) : new FileOutputStream(getName()); } @Override public Attributes getAttr() throws IOException { String cfg = doGetConfig(); return doGetAttr(cfg); } @Override public void setAttr(Attributes attr) throws IOException { List commands = getFlagsToSet(attr, getAttr()); if (!commands.isEmpty()) { commands.add(0, OSUtils.STTY_COMMAND); if (!system) { commands.add(1, OSUtils.STTY_F_OPTION); commands.add(2, getName()); } try { exec(system, commands.toArray(new String[commands.size()])); } catch (IOException e) { // Handle partial failures with GNU stty, see #97 if (e.toString().contains("unable to perform all requested operations")) { commands = getFlagsToSet(attr, getAttr()); if (!commands.isEmpty()) { throw new IOException("Could not set the following flags: " + String.join(", ", commands), e); } } else { throw e; } } } } protected List getFlagsToSet(Attributes attr, Attributes current) { List commands = new ArrayList<>(); for (InputFlag flag : InputFlag.values()) { if (attr.getInputFlag(flag) != current.getInputFlag(flag)) { commands.add((attr.getInputFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); } } for (OutputFlag flag : OutputFlag.values()) { if (attr.getOutputFlag(flag) != current.getOutputFlag(flag)) { commands.add((attr.getOutputFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); } } for (ControlFlag flag : ControlFlag.values()) { if (attr.getControlFlag(flag) != current.getControlFlag(flag)) { commands.add((attr.getControlFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); } } for (LocalFlag flag : LocalFlag.values()) { if (attr.getLocalFlag(flag) != current.getLocalFlag(flag)) { commands.add((attr.getLocalFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); } } String undef = System.getProperty("os.name").toLowerCase().startsWith("hp") ? "^-" : "undef"; for (ControlChar cchar : ControlChar.values()) { if (attr.getControlChar(cchar) != current.getControlChar(cchar)) { String str = ""; int v = attr.getControlChar(cchar); commands.add(cchar.name().toLowerCase().substring(1)); if (cchar == ControlChar.VMIN || cchar == ControlChar.VTIME) { commands.add(Integer.toBinaryString(v)); } else if (v == 0) { commands.add(undef); } else { if (v >= 128) { v -= 128; str += "M-"; } if (v < 32 || v == 127) { v ^= 0x40; str += "^"; } str += (char) v; commands.add(str); } } } return commands; } @Override public Size getSize() throws IOException { String cfg = doGetConfig(); return doGetSize(cfg); } protected String doGetConfig() throws IOException { return system ? exec(true, OSUtils.STTY_COMMAND, "-a") : exec(false, OSUtils.STTY_COMMAND, OSUtils.STTY_F_OPTION, getName(), "-a"); } static Attributes doGetAttr(String cfg) throws IOException { Attributes attributes = new Attributes(); for (InputFlag flag : InputFlag.values()) { Boolean value = doGetFlag(cfg, flag); if (value != null) { attributes.setInputFlag(flag, value); } } for (OutputFlag flag : OutputFlag.values()) { Boolean value = doGetFlag(cfg, flag); if (value != null) { attributes.setOutputFlag(flag, value); } } for (ControlFlag flag : ControlFlag.values()) { Boolean value = doGetFlag(cfg, flag); if (value != null) { attributes.setControlFlag(flag, value); } } for (LocalFlag flag : LocalFlag.values()) { Boolean value = doGetFlag(cfg, flag); if (value != null) { attributes.setLocalFlag(flag, value); } } for (ControlChar cchar : ControlChar.values()) { String name = cchar.name().toLowerCase().substring(1); if ("reprint".endsWith(name)) { name = "(?:reprint|rprnt)"; } Matcher matcher = Pattern.compile("[\\s;]" + name + "\\s*=\\s*(.+?)[\\s;]").matcher(cfg); if (matcher.find()) { attributes.setControlChar(cchar, parseControlChar(matcher.group(1).toUpperCase())); } } return attributes; } private static Boolean doGetFlag(String cfg, Enum flag) { Matcher matcher = Pattern.compile("(?:^|[\\s;])(\\-?" + flag.name().toLowerCase() + ")(?:[\\s;]|$)").matcher(cfg); return matcher.find() ? !matcher.group(1).startsWith("-") : null; } static int parseControlChar(String str) { // undef if ("".equals(str)) { return -1; } // del if ("DEL".equalsIgnoreCase(str)) { return 127; } // octal if (str.charAt(0) == '0') { return Integer.parseInt(str, 8); } // decimal if (str.charAt(0) >= '1' && str.charAt(0) <= '9') { return Integer.parseInt(str, 10); } // control char if (str.charAt(0) == '^') { if (str.charAt(1) == '?') { return 127; } else { return str.charAt(1) - 64; } } else if (str.charAt(0) == 'M' && str.charAt(1) == '-') { if (str.charAt(2) == '^') { if (str.charAt(3) == '?') { return 127 + 128; } else { return str.charAt(3) - 64 + 128; } } else { return str.charAt(2) + 128; } } else { return str.charAt(0); } } static Size doGetSize(String cfg) throws IOException { return new Size(doGetInt("columns", cfg), doGetInt("rows", cfg)); } static int doGetInt(String name, String cfg) throws IOException { String[] patterns = new String[] { "\\b([0-9]+)\\s+" + name + "\\b", "\\b" + name + "\\s+([0-9]+)\\b", "\\b" + name + "\\s*=\\s*([0-9]+)\\b" }; for (String pattern : patterns) { Matcher matcher = Pattern.compile(pattern).matcher(cfg); if (matcher.find()) { return Integer.parseInt(matcher.group(1)); } } throw new IOException("Unable to parse " + name); } @Override public void setSize(Size size) throws IOException { if (system) { exec(true, OSUtils.STTY_COMMAND, "columns", Integer.toString(size.getColumns()), "rows", Integer.toString(size.getRows())); } else { exec(false, OSUtils.STTY_COMMAND, OSUtils.STTY_F_OPTION, getName(), "columns", Integer.toString(size.getColumns()), "rows", Integer.toString(size.getRows())); } } @Override public String toString() { return "ExecPty[" + getName() + (system ? ", system]" : "]"); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/ExternalTerminal.java000066400000000000000000000051711311544710100310360ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import org.jline.terminal.Cursor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.IntConsumer; /** * Console implementation with embedded line disciplined. * * This terminal is well-suited for supporting incoming external * connections, such as from the network (through telnet, ssh, * or any kind of protocol). * The terminal will start consuming the input in a separate thread * to generate interruption events. * * @see LineDisciplineTerminal */ public class ExternalTerminal extends LineDisciplineTerminal { protected final AtomicBoolean closed = new AtomicBoolean(); protected final Thread pumpThread; protected final InputStream masterInput; public ExternalTerminal(String name, String type, InputStream masterInput, OutputStream masterOutput, String encoding) throws IOException { this(name, type, masterInput, masterOutput, encoding, SignalHandler.SIG_DFL); } public ExternalTerminal(String name, String type, InputStream masterInput, OutputStream masterOutput, String encoding, SignalHandler signalHandler) throws IOException { super(name, type, masterOutput, encoding, signalHandler); this.masterInput = masterInput; this.pumpThread = new Thread(this::pump, toString() + " input pump thread"); this.pumpThread.start(); } public void close() throws IOException { if (closed.compareAndSet(false, true)) { pumpThread.interrupt(); super.close(); } } public void pump() { try { while (true) { int c = masterInput.read(); if (c < 0 || closed.get()) { break; } processInputByte((char) c); } } catch (IOException e) { // Ignore } try { close(); } catch (Throwable t) { // Ignore } } @Override public Cursor getCursorPosition(IntConsumer discarded) { return CursorSupport.getCursorPosition(this, discarded); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/LineDisciplineTerminal.java000066400000000000000000000224271311544710100321520ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.PrintWriter; import java.util.Objects; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Attributes.OutputFlag; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.utils.InputStreamReader; import org.jline.utils.NonBlockingReader; /** * Abstract terminal with support for line discipline. * The {@link Terminal} interface represents the slave * side of a PTY, but implementations derived from this class * will handle both the slave and master side of things. * * In order to correctly handle line discipline, the terminal * needs to read the input in advance in order to raise the * signals as fast as possible. * For example, when the user hits Ctrl+C, we can't wait until * the application consumes all the read events. * The same applies to echoing, when enabled, as the echoing * has to happen as soon as the user hit the keyboard, and not * only when the application running in the terminal processes * the input. */ public class LineDisciplineTerminal extends AbstractTerminal { private static final String DEFAULT_TERMINAL_ATTRIBUTES = "speed 9600 baud; 24 rows; 80 columns;\n" + "lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl\n" + "\t-echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo\n" + "\t-extproc\n" + "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8\n" + "\t-ignbrk brkint -inpck -ignpar -parmrk\n" + "oflags: opost onlcr -oxtabs -onocr -onlret\n" + "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow\n" + "\t-dtrflow -mdmbuf\n" + "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = ;\n" + "\teol2 = ; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;\n" + "\tmin = 1; quit = ^\\; reprint = ^R; start = ^Q; status = ^T;\n" + "\tstop = ^S; susp = ^Z; time = 0; werase = ^W;\n"; private static final int PIPE_SIZE = 1024; /* * Master output stream */ protected final OutputStream masterOutput; /* * Slave input pipe write side */ protected final OutputStream slaveInputPipe; /* * Slave streams */ protected final InputStream slaveInput; protected final NonBlockingReader slaveReader; protected final PrintWriter slaveWriter; protected final OutputStream slaveOutput; /** * Console data */ protected final Attributes attributes; protected final Size size; public LineDisciplineTerminal(String name, String type, OutputStream masterOutput, String encoding) throws IOException { this(name, type, masterOutput, encoding, SignalHandler.SIG_DFL); } public LineDisciplineTerminal(String name, String type, OutputStream masterOutput, String encoding, SignalHandler signalHandler) throws IOException { super(name, type, signalHandler); PipedInputStream input = new PipedInputStream(PIPE_SIZE); this.slaveInputPipe = new PipedOutputStream(input); // This is a hack to fix a problem in gogo where closure closes // streams for commands if they are instances of PipedInputStream. // So we need to get around and make sure it's not an instance of // that class by using a dumb FilterInputStream class to wrap it. this.slaveInput = new FilterInputStream(input) {}; this.slaveReader = new NonBlockingReader(getName(), new InputStreamReader(slaveInput, encoding)); this.slaveOutput = new FilteringOutputStream(); this.slaveWriter = new PrintWriter(new OutputStreamWriter(slaveOutput, encoding)); this.masterOutput = masterOutput; this.attributes = ExecPty.doGetAttr(DEFAULT_TERMINAL_ATTRIBUTES); this.size = new Size(160, 50); parseInfoCmp(); } public NonBlockingReader reader() { return slaveReader; } public PrintWriter writer() { return slaveWriter; } @Override public InputStream input() { return slaveInput; } @Override public OutputStream output() { return slaveOutput; } public Attributes getAttributes() { Attributes attr = new Attributes(); attr.copy(attributes); return attr; } public void setAttributes(Attributes attr) { attributes.copy(attr); } public Size getSize() { Size sz = new Size(); sz.copy(size); return sz; } public void setSize(Size sz) { size.copy(sz); } @Override public void raise(Signal signal) { Objects.requireNonNull(signal); // Do not call clear() atm as this can cause // deadlock between reading / writing threads // TODO: any way to fix that ? /* if (!attributes.getLocalFlag(LocalFlag.NOFLSH)) { try { slaveReader.clear(); } catch (IOException e) { // Ignore } } */ echoSignal(signal); super.raise(signal); } /** * Master input processing. * All data coming to the terminal should be provided * using this method. * * @param c the input byte * @throws IOException */ public void processInputByte(int c) throws IOException { if (attributes.getLocalFlag(LocalFlag.ISIG)) { if (c == attributes.getControlChar(ControlChar.VINTR)) { raise(Signal.INT); return; } else if (c == attributes.getControlChar(ControlChar.VQUIT)) { raise(Signal.QUIT); return; } else if (c == attributes.getControlChar(ControlChar.VSUSP)) { raise(Signal.TSTP); return; } else if (c == attributes.getControlChar(ControlChar.VSTATUS)) { raise(Signal.INFO); } } if (c == '\r') { if (attributes.getInputFlag(InputFlag.IGNCR)) { return; } if (attributes.getInputFlag(InputFlag.ICRNL)) { c = '\n'; } } else if (c == '\n' && attributes.getInputFlag(InputFlag.INLCR)) { c = '\r'; } if (attributes.getLocalFlag(LocalFlag.ECHO)) { processOutputByte(c); masterOutput.flush(); } slaveInputPipe.write(c); slaveInputPipe.flush(); } /** * Master output processing. * All data going to the master should be provided by this method. * * @param c the output byte * @throws IOException */ protected void processOutputByte(int c) throws IOException { if (attributes.getOutputFlag(OutputFlag.OPOST)) { if (c == '\n') { if (attributes.getOutputFlag(OutputFlag.ONLCR)) { masterOutput.write('\r'); masterOutput.write('\n'); return; } } } masterOutput.write(c); } public void close() throws IOException { try { slaveReader.close(); } finally { try { slaveInputPipe.close(); } finally { try { } finally { slaveWriter.close(); } } } } private class FilteringOutputStream extends OutputStream { @Override public void write(int b) throws IOException { processOutputByte(b); flush(); } @Override public void write(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } for (int i = 0 ; i < len ; i++) { processOutputByte(b[off + i]); } flush(); } @Override public void flush() throws IOException { masterOutput.flush(); } @Override public void close() throws IOException { masterOutput.close(); } } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/MouseSupport.java000066400000000000000000000077001311544710100302450ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import org.jline.terminal.MouseEvent; import org.jline.terminal.Terminal; import org.jline.utils.InfoCmp; import java.io.EOFException; import java.io.IOError; import java.io.IOException; import java.util.EnumSet; import java.util.function.IntSupplier; public class MouseSupport { public static boolean hasMouseSupport(Terminal terminal) { return terminal.getStringCapability(InfoCmp.Capability.key_mouse) != null; } public static boolean trackMouse(Terminal terminal, Terminal.MouseTracking tracking) { if (hasMouseSupport(terminal)) { switch (tracking) { case Off: terminal.writer().write("\033[?1000l"); break; case Normal: terminal.writer().write("\033[?1005h\033[?1000h"); break; case Button: terminal.writer().write("\033[?1005h\033[?1002h"); break; case Any: terminal.writer().write("\033[?1005h\033[?1003h"); break; } terminal.flush(); return true; } else { return false; } } public static MouseEvent readMouse(Terminal terminal, MouseEvent last) { return readMouse(() -> readExt(terminal), last); } public static MouseEvent readMouse(IntSupplier reader, MouseEvent last) { int cb = reader.getAsInt() - ' '; int cx = reader.getAsInt() - ' ' - 1; int cy = reader.getAsInt() - ' ' - 1; MouseEvent.Type type; MouseEvent.Button button; EnumSet modifiers = EnumSet.noneOf(MouseEvent.Modifier.class); if ((cb & 4) == 4) { modifiers.add(MouseEvent.Modifier.Shift); } if ((cb & 8) == 8) { modifiers.add(MouseEvent.Modifier.Alt); } if ((cb & 16) == 16) { modifiers.add(MouseEvent.Modifier.Control); } if ((cb & 64) == 64) { type = MouseEvent.Type.Wheel; button = (cb & 1) == 1 ? MouseEvent.Button.WheelDown : MouseEvent.Button.WheelUp; } else { int b = (cb & 3); switch (b) { case 0: button = MouseEvent.Button.Button1; type = last.getButton() == button ? MouseEvent.Type.Dragged : MouseEvent.Type.Pressed; break; case 1: button = MouseEvent.Button.Button2; type = last.getButton() == button ? MouseEvent.Type.Dragged : MouseEvent.Type.Pressed; break; case 2: button = MouseEvent.Button.Button3; type = last.getButton() == button ? MouseEvent.Type.Dragged : MouseEvent.Type.Pressed; break; default: if (last.getType() == MouseEvent.Type.Pressed || last.getType() == MouseEvent.Type.Dragged) { button = last.getButton(); type = MouseEvent.Type.Released; } else { button = MouseEvent.Button.NoButton; type = MouseEvent.Type.Moved; } break; } } return new MouseEvent(type, button, modifiers, cx, cy); } private static int readExt(Terminal terminal) { try { int c = terminal.reader().read(); if (c < 0) { throw new EOFException(); } return c; } catch (IOException e) { throw new IOError(e); } } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/NativeSignalHandler.java000066400000000000000000000014031311544710100314340ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import org.jline.terminal.Terminal.Signal; import org.jline.terminal.Terminal.SignalHandler; public final class NativeSignalHandler implements SignalHandler { public static final NativeSignalHandler SIG_DFL = new NativeSignalHandler(); public static final NativeSignalHandler SIG_IGN = new NativeSignalHandler(); private NativeSignalHandler() { } public void handle(Signal signal) { throw new UnsupportedOperationException(); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/PosixPtyTerminal.java000066400000000000000000000071301311544710100310500ustar00rootroot00000000000000/* * Copyright (c) 2002-2017, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import org.jline.terminal.spi.Pty; import org.jline.utils.ClosedException; import org.jline.utils.InputStreamReader; import org.jline.utils.NonBlockingReader; public class PosixPtyTerminal extends AbstractPosixTerminal { private final InputStreamWrapper input; private final OutputStream output; private final InputStreamReader innerReader; private final NonBlockingReader reader; private final PrintWriter writer; private final Thread inputPumpThread; private final Thread outputPumpThread; public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, String encoding) throws IOException { this(name, type, pty, in, out, encoding, SignalHandler.SIG_DFL); } public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, String encoding, SignalHandler signalHandler) throws IOException { super(name, type, pty, signalHandler); Objects.requireNonNull(in); Objects.requireNonNull(out); this.input = new InputStreamWrapper(pty.getSlaveInput()); this.output = pty.getSlaveOutput(); this.innerReader = new InputStreamReader(input, encoding); this.reader = new NonBlockingReader(name, innerReader); this.writer = new PrintWriter(new OutputStreamWriter(output, encoding)); this.inputPumpThread = new PumpThread(in, getPty().getMasterOutput()); this.outputPumpThread = new PumpThread(getPty().getMasterInput(), out); parseInfoCmp(); this.inputPumpThread.start(); this.outputPumpThread.start(); } public InputStream input() { return input; } public NonBlockingReader reader() { return reader; } public OutputStream output() { return output; } public PrintWriter writer() { return writer; } private class InputStreamWrapper extends InputStream { private final InputStream in; private final AtomicBoolean closed = new AtomicBoolean(); protected InputStreamWrapper(InputStream in) { this.in = in; } @Override public int read() throws IOException { if (closed.get()) { throw new ClosedException(); } return in.read(); } @Override public void close() throws IOException { closed.set(true); } } private class PumpThread extends Thread { private final InputStream in; private final OutputStream out; public PumpThread(InputStream in, OutputStream out) { this.in = in; this.out = out; } @Override public void run() { try { while (true) { int b = in.read(); if (b < 0) { input.close(); break; } out.write(b); out.flush(); } } catch (IOException e) { e.printStackTrace(); } } } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/PosixSysTerminal.java000066400000000000000000000061541311544710100310570ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.jline.terminal.spi.Pty; import org.jline.utils.InputStreamReader; import org.jline.utils.NonBlockingReader; import org.jline.utils.ShutdownHooks; import org.jline.utils.ShutdownHooks.Task; import org.jline.utils.Signals; public class PosixSysTerminal extends AbstractPosixTerminal { protected final InputStream input; protected final OutputStream output; protected final NonBlockingReader reader; protected final PrintWriter writer; protected final Map nativeHandlers = new HashMap<>(); protected final Task closer; public PosixSysTerminal(String name, String type, Pty pty, String encoding, boolean nativeSignals, SignalHandler signalHandler) throws IOException { super(name, type, pty, signalHandler); Objects.requireNonNull(encoding); this.input = pty.getSlaveInput(); this.output = pty.getSlaveOutput(); this.reader = new NonBlockingReader(getName(), new InputStreamReader(input, encoding)); this.writer = new PrintWriter(new OutputStreamWriter(output, encoding)); parseInfoCmp(); if (nativeSignals) { for (final Signal signal : Signal.values()) { if (signalHandler == SignalHandler.SIG_DFL) { nativeHandlers.put(signal, Signals.registerDefault(signal.name())); } else { nativeHandlers.put(signal, Signals.register(signal.name(), () -> raise(signal))); } } } closer = PosixSysTerminal.this::close; ShutdownHooks.add(closer); } @Override public SignalHandler handle(Signal signal, SignalHandler handler) { SignalHandler prev = super.handle(signal, handler); if (prev != handler) { if (handler == SignalHandler.SIG_DFL) { Signals.registerDefault(signal.name()); } else { Signals.register(signal.name(), () -> raise(signal)); } } return prev; } public NonBlockingReader reader() { return reader; } public PrintWriter writer() { return writer; } @Override public InputStream input() { return input; } @Override public OutputStream output() { return output; } @Override public void close() throws IOException { ShutdownHooks.remove(closer); for (Map.Entry entry : nativeHandlers.entrySet()) { Signals.unregister(entry.getKey().name(), entry.getValue()); } super.close(); reader.shutdown(); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/impl/package-info.java000066400000000000000000000005231311544710100301000ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /** * JLine 3. * * @since 3.0 */ package org.jline.terminal.impl;jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/spi/000077500000000000000000000000001311544710100245435ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/spi/JansiSupport.java000066400000000000000000000006621311544710100300530ustar00rootroot00000000000000package org.jline.terminal.spi; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import java.io.IOException; public interface JansiSupport { Pty current() throws IOException; Pty open(Attributes attributes, Size size) throws IOException; Terminal winSysTerminal(String name, boolean nativeSignals, Terminal.SignalHandler signalHandler) throws IOException; } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/spi/JnaSupport.java000066400000000000000000000006601311544710100275150ustar00rootroot00000000000000package org.jline.terminal.spi; import org.jline.terminal.Attributes; import org.jline.terminal.Size; import org.jline.terminal.Terminal; import java.io.IOException; public interface JnaSupport { Pty current() throws IOException; Pty open(Attributes attributes, Size size) throws IOException; Terminal winSysTerminal(String name, boolean nativeSignals, Terminal.SignalHandler signalHandler) throws IOException; } jline3-jline-3.3.1/terminal/src/main/java/org/jline/terminal/spi/Pty.java000066400000000000000000000016521311544710100261660ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.spi; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.jline.terminal.Attributes; import org.jline.terminal.Size; public interface Pty extends Closeable { InputStream getMasterInput() throws IOException; OutputStream getMasterOutput() throws IOException; InputStream getSlaveInput() throws IOException; OutputStream getSlaveOutput() throws IOException; Attributes getAttr() throws IOException; void setAttr(Attributes attr) throws IOException; Size getSize() throws IOException; void setSize(Size size) throws IOException; } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/000077500000000000000000000000001311544710100232755ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/AttributedCharSequence.java000066400000000000000000000340601311544710100305410ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.util.ArrayList; import java.util.List; import org.jline.terminal.Terminal; import org.jline.utils.InfoCmp.Capability; import static org.jline.utils.AttributedStyle.BG_COLOR; import static org.jline.utils.AttributedStyle.BG_COLOR_EXP; import static org.jline.utils.AttributedStyle.FG_COLOR; import static org.jline.utils.AttributedStyle.FG_COLOR_EXP; import static org.jline.utils.AttributedStyle.F_BACKGROUND; import static org.jline.utils.AttributedStyle.F_BLINK; import static org.jline.utils.AttributedStyle.F_BOLD; import static org.jline.utils.AttributedStyle.F_CONCEAL; import static org.jline.utils.AttributedStyle.F_CROSSED_OUT; import static org.jline.utils.AttributedStyle.F_FAINT; import static org.jline.utils.AttributedStyle.F_FOREGROUND; import static org.jline.utils.AttributedStyle.F_INVERSE; import static org.jline.utils.AttributedStyle.F_ITALIC; import static org.jline.utils.AttributedStyle.F_UNDERLINE; import static org.jline.utils.AttributedStyle.F_HIDDEN; import static org.jline.utils.AttributedStyle.MASK; public abstract class AttributedCharSequence implements CharSequence { public String toAnsi() { return toAnsi(null); } public String toAnsi(Terminal terminal) { if (terminal != null && Terminal.TYPE_DUMB.equals(terminal.getType())) { return toString(); } StringBuilder sb = new StringBuilder(); int style = 0; int foreground = -1; int background = -1; int colors = 8; if (terminal != null) { Integer max_colors = terminal.getNumericCapability(Capability.max_colors); if (max_colors != null) { colors = max_colors; } } for (int i = 0; i < length(); i++) { char c = charAt(i); int s = styleCodeAt(i) & ~F_HIDDEN; // The hidden flag does not change the ansi styles if (style != s) { int d = (style ^ s) & MASK; int fg = (s & F_FOREGROUND) != 0 ? (s & FG_COLOR) >>> FG_COLOR_EXP : -1; int bg = (s & F_BACKGROUND) != 0 ? (s & BG_COLOR) >>> BG_COLOR_EXP : -1; if (s == 0) { sb.append("\033[0m"); foreground = background = -1; } else { sb.append("\033["); boolean first = true; if ((d & (F_BOLD | F_FAINT)) != 0) { if ( (d & F_BOLD) != 0 && (s & F_BOLD) == 0 || (d & F_FAINT) != 0 && (s & F_FAINT) == 0) { first = attr(sb, "22", first); } if ((d & F_BOLD) != 0 && (s & F_BOLD) != 0) { first = attr(sb, "1", first); } if ((d & F_FAINT) != 0 && (s & F_FAINT) != 0) { first = attr(sb, "2", first); } } if ((d & F_ITALIC) != 0) { first = attr(sb, (s & F_ITALIC) != 0 ? "3" : "23", first); } if ((d & F_UNDERLINE) != 0) { first = attr(sb, (s & F_UNDERLINE) != 0 ? "4" : "24", first); } if ((d & F_BLINK) != 0) { first = attr(sb, (s & F_BLINK) != 0 ? "5" : "25", first); } if ((d & F_INVERSE) != 0) { first = attr(sb, (s & F_INVERSE) != 0 ? "7" : "27", first); } if ((d & F_CONCEAL) != 0) { first = attr(sb, (s & F_CONCEAL) != 0 ? "8" : "28", first); } if ((d & F_CROSSED_OUT) != 0) { first = attr(sb, (s & F_CROSSED_OUT) != 0 ? "9" : "29", first); } if (foreground != fg) { if (fg >= 0) { int rounded = roundColor(fg, colors); if (rounded < 8) { first = attr(sb, "3" + Integer.toString(rounded), first); } else if (rounded < 16) { first = attr(sb, "9" + Integer.toString(rounded - 8), first); } else { first = attr(sb, "38;5;" + Integer.toString(rounded), first); } } else { first = attr(sb, "39", first); } foreground = fg; } if (background != bg) { if (bg >= 0) { int rounded = roundColor(bg, colors); if (rounded < 8) { first = attr(sb, "4" + Integer.toString(rounded), first); } else if (rounded < 16) { first = attr(sb, "10" + Integer.toString(rounded - 8), first); } else { first = attr(sb, "48;5;" + Integer.toString(rounded), first); } } else { first = attr(sb, "49", first); } background = bg; } sb.append("m"); } style = s; } sb.append(c); } if (style != 0) { sb.append("\033[0m"); } return sb.toString(); } private static final int[] COLORS_256 = { 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0, 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee, }; public static int rgbColor(int col) { return COLORS_256[col]; } public static int roundColor(int col, int max) { if (col >= max) { int c = COLORS_256[col]; int r = (c >> 16) & 0xFF; int g = (c >> 8) & 0xFF; int b = (c >> 0) & 0xFF; col = roundColor(r, g, b, COLORS_256, max); } return col; } public static int roundRgbColor(int r, int g, int b, int max) { return roundColor(r, g, b, COLORS_256, max); } private static int roundColor(int r, int g, int b, int[] colors, int max) { int best_distance = Integer.MAX_VALUE; int best_index = Integer.MAX_VALUE; for (int idx = 0; idx < max; idx++) { int color = colors[idx]; int test_r = (color >> 16) & 0xFF; int test_g = (color >> 8) & 0xFF; int test_b = (color >> 0) & 0xFF; int distance = 2 * sqr(r - test_r) + 4 * sqr(g - test_g) + 3 * sqr(b - test_b); if (distance <= best_distance) { best_index = idx; best_distance = distance; } } return best_index; } static int sqr(int d) { return d * d; } private static boolean attr(StringBuilder sb, String s, boolean first) { if (!first) { sb.append(";"); } sb.append(s); return false; } public abstract AttributedStyle styleAt(int index); int styleCodeAt(int index) { return styleAt(index).getStyle(); } public boolean isHidden(int index) { return (styleCodeAt(index) & F_HIDDEN) != 0; } public int runStart(int index) { AttributedStyle style = styleAt(index); while (index > 0 && styleAt(index - 1).equals(style)) { index--; } return index; } public int runLimit(int index) { AttributedStyle style = styleAt(index); while (index < length() - 1 && styleAt(index + 1).equals(style)) { index++; } return index + 1; } @Override public abstract AttributedString subSequence(int start, int end); public AttributedString substring(int start, int end) { return subSequence(start, end); } protected abstract char[] buffer(); protected abstract int offset(); @Override public char charAt(int index) { return buffer()[offset() + index]; } public int codePointAt(int index) { return Character.codePointAt(buffer(), index + offset()); } public boolean contains(char c) { for (int i = 0; i < length(); i++) { if (charAt(i) == c) { return true; } } return false; } public int codePointBefore(int index) { return Character.codePointBefore(buffer(), index + offset()); } public int codePointCount(int index, int length) { return Character.codePointCount(buffer(), index + offset(), length); } public int columnLength() { int cols = 0; int len = length(); for (int cur = 0; cur < len; ) { int cp = codePointAt(cur); if (!isHidden(cur)) cols += WCWidth.wcwidth(cp); cur += Character.charCount(cp); } return cols; } public AttributedString columnSubSequence(int start, int stop) { int begin = 0; int col = 0; while (begin < this.length()) { int cp = codePointAt(begin); int w = isHidden(begin) ? 0 : WCWidth.wcwidth(cp); if (col + w > start) { break; } begin++; col += w; } int end = begin; while (end < this.length()) { int cp = codePointAt(end); if (cp == '\n') break; int w = isHidden(end) ? 0 : WCWidth.wcwidth(cp); if (col + w > stop) { break; } end++; col += w; } return subSequence(begin, end); } public List columnSplitLength(int columns) { return columnSplitLength(columns, false, true); } public List columnSplitLength(int columns, boolean includeNewlines, boolean delayLineWrap) { List strings = new ArrayList<>(); int cur = 0; int beg = cur; int col = 0; while (cur < length()) { int cp = codePointAt(cur); int w = isHidden(cur) ? 0 : WCWidth.wcwidth(cp); if (cp == '\n') { strings.add(subSequence(beg, includeNewlines ? cur+1 : cur)); beg = cur + 1; col = 0; } else if ((col += w) > columns) { strings.add(subSequence(beg, cur)); beg = cur; col = w; } cur += Character.charCount(cp); } strings.add(subSequence(beg, cur)); return strings; } @Override public String toString() { return new String(buffer(), offset(), length()); } public AttributedString toAttributedString() { return substring(0, length()); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/AttributedString.java000066400000000000000000000153261311544710100274450ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.security.InvalidParameterException; import java.util.Arrays; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Attributed string. * Instances of this class are immutables. * Substrings are created without any memory copy. * * @author Guillaume Nodet */ public class AttributedString extends AttributedCharSequence { final char[] buffer; final int[] style; final int start; final int end; public static final AttributedString EMPTY = new AttributedString(""); public static final AttributedString NEWLINE = new AttributedString("\n"); public AttributedString(CharSequence str) { this(str, 0, str.length(), null); } public AttributedString(CharSequence str, int start, int end) { this(str, start, end, null); } public AttributedString(CharSequence str, AttributedStyle s) { this(str, 0, str.length(), s); } public AttributedString(CharSequence str, int start, int end, AttributedStyle s) { if (end < start) { throw new InvalidParameterException(); } if (str instanceof AttributedString) { AttributedString as = (AttributedString) str; this.buffer = as.buffer; if (s != null) { this.style = as.style.clone(); for (int i = 0; i < style.length; i++) { this.style[i] = (this.style[i] & ~s.getMask()) | s.getStyle(); } } else { this.style = as.style; } this.start = as.start + start; this.end = as.start + end; } else if (str instanceof AttributedStringBuilder) { AttributedStringBuilder asb = (AttributedStringBuilder) str; AttributedString as = asb.subSequence(start, end); this.buffer = as.buffer; this.style = as.style; if (s != null) { for (int i = 0; i < style.length; i++) { this.style[i] = (this.style[i] & ~s.getMask()) | s.getStyle(); } } this.start = as.start; this.end = as.end; } else { int l = end - start; buffer = new char[l]; for (int i = 0; i < l; i++) { buffer[i] = str.charAt(start + i); } style = new int[l]; if (s != null) { Arrays.fill(style, s.getStyle()); } this.start = 0; this.end = l; } } AttributedString(char[] buffer, int[] style, int start, int end) { this.buffer = buffer; this.style = style; this.start = start; this.end = end; } public static AttributedString fromAnsi(String ansi) { return fromAnsi(ansi, 0); } public static AttributedString fromAnsi(String ansi, int tabs) { if (ansi == null) { return null; } AttributedStringBuilder sb = new AttributedStringBuilder(ansi.length()); sb.tabs(tabs); sb.appendAnsi(ansi); return sb.toAttributedString(); } public static String stripAnsi(String ansi) { if (ansi == null) { return null; } AttributedStringBuilder sb = new AttributedStringBuilder(ansi.length()); sb.appendAnsi(ansi); return sb.toString(); } @Override protected char[] buffer() { return buffer; } @Override protected int offset() { return start; } @Override public int length() { return end - start; } @Override public AttributedStyle styleAt(int index) { return new AttributedStyle(style[start + index], style[start + index]); } @Override int styleCodeAt(int index) { return style[start + index]; } @Override public AttributedString subSequence(int start, int end) { return new AttributedString(this, start, end); } public AttributedString styleMatches(Pattern pattern, AttributedStyle style) { Matcher matcher = pattern.matcher(this); boolean result = matcher.find(); if (result) { int[] newstyle = this.style.clone(); do { for (int i = matcher.start(); i < matcher.end(); i++) { newstyle[this.start + i] = (newstyle[this.start + i] & ~style.getMask()) | style.getStyle(); } result = matcher.find(); } while (result); return new AttributedString(buffer, newstyle, start , end); } return this; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AttributedString that = (AttributedString) o; return end - start == that.end - that.start && arrEq(buffer, that.buffer, start, that.start, end - start) && arrEq(style, that.style, start, that.start, end - start); } private boolean arrEq(char[] a1, char[] a2, int s1, int s2, int l) { for (int i = 0; i < l; i++) { if (a1[s1+i] != a2[s2+i]) { return false; } } return true; } private boolean arrEq(int[] a1, int[] a2, int s1, int s2, int l) { for (int i = 0; i < l; i++) { if (a1[s1+i] != a2[s2+i]) { return false; } } return true; } @Override public int hashCode() { int result = Arrays.hashCode(buffer); result = 31 * result + Arrays.hashCode(style); result = 31 * result + start; result = 31 * result + end; return result; } public static AttributedString join(AttributedString delimiter, AttributedString... elements) { Objects.requireNonNull(delimiter); Objects.requireNonNull(elements); return join(delimiter, Arrays.asList(elements)); } public static AttributedString join(AttributedString delimiter, Iterable elements) { Objects.requireNonNull(elements); AttributedStringBuilder sb = new AttributedStringBuilder(); int i = 0; for (AttributedString str : elements) { if (i++ > 0 && delimiter != null) { sb.append(delimiter); } sb.append(str); } return sb.toAttributedString(); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/AttributedStringBuilder.java000066400000000000000000000311041311544710100307440ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.util.Arrays; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Attributed string builder * * @author Guillaume Nodet */ public class AttributedStringBuilder extends AttributedCharSequence implements Appendable { private char[] buffer; private int[] style; private int length; private int tabs = 0; private AttributedStyle current = AttributedStyle.DEFAULT; public static AttributedString append(CharSequence... strings) { AttributedStringBuilder sb = new AttributedStringBuilder(); for (CharSequence s : strings) { sb.append(s); } return sb.toAttributedString(); } public AttributedStringBuilder() { this(64); } public AttributedStringBuilder(int capacity) { buffer = new char[capacity]; style = new int[capacity]; length = 0; } @Override public int length() { return length; } @Override public char charAt(int index) { return buffer[index]; } @Override public AttributedStyle styleAt(int index) { return new AttributedStyle(style[index], style[index]); } @Override int styleCodeAt(int index) { return style[index]; } @Override protected char[] buffer() { return buffer; } @Override protected int offset() { return 0; } @Override public AttributedString subSequence(int start, int end) { return new AttributedString( Arrays.copyOfRange(buffer, start, end), Arrays.copyOfRange(style, start, end), 0, end - start); } @Override public AttributedStringBuilder append(CharSequence csq) { return append(new AttributedString(csq, current)); } @Override public AttributedStringBuilder append(CharSequence csq, int start, int end) { return append(csq.subSequence(start, end)); } @Override public AttributedStringBuilder append(char c) { return append(Character.toString(c)); } public AttributedStringBuilder append(CharSequence csq, AttributedStyle style) { return append(new AttributedString(csq, style)); } public AttributedStringBuilder style(AttributedStyle style) { current = style; return this; } public AttributedStringBuilder style(Function style) { current = style.apply(current); return this; } public AttributedStringBuilder styled(Function style, CharSequence cs) { return styled(style, sb -> sb.append(cs)); } public AttributedStringBuilder styled(AttributedStyle style, CharSequence cs) { return styled(s -> style, sb -> sb.append(cs)); } public AttributedStringBuilder styled(Function style, Consumer consumer) { AttributedStyle prev = current; current = style.apply(prev); consumer.accept(this); current = prev; return this; } public AttributedStyle style() { return current; } public AttributedStringBuilder append(AttributedString str) { return append(str, 0, str.length()); } public AttributedStringBuilder append(AttributedString str, int start, int end) { ensureCapacity(length + end - start); for (int i = start; i < end; i++) { char c = str.charAt(i); int s = str.styleCodeAt(i) & ~current.getMask() | current.getStyle(); if (tabs > 0 && c == '\t') { insertTab(new AttributedStyle(s, 0)); } else { ensureCapacity(length + 1); buffer[length] = c; style[length] = s; length++; } } return this; } protected void ensureCapacity(int nl) { if (nl > buffer.length) { int s = buffer.length; while (s <= nl) { s *= 2; } buffer = Arrays.copyOf(buffer, s); style = Arrays.copyOf(style, s); } } public void appendAnsi(String ansi) { int ansiStart = 0; int ansiState = 0; ensureCapacity(length + ansi.length()); for (int i = 0; i < ansi.length(); i++) { char c = ansi.charAt(i); if (ansiState == 0 && c == 27) { ansiState++; } else if (ansiState == 1 && c == '[') { ansiState++; ansiStart = i + 1; } else if (ansiState == 2) { if (c == 'm') { String[] params = ansi.substring(ansiStart, i).split(";"); int j = 0; while (j < params.length) { int ansiParam = params[j].isEmpty() ? 0 : Integer.parseInt(params[j]); switch (ansiParam) { case 0: current = AttributedStyle.DEFAULT; break; case 1: current = current.bold(); break; case 2: current = current.faint(); break; case 3: current = current.italic(); break; case 4: current = current.underline(); break; case 5: current = current.blink(); break; case 7: current = current.inverse(); break; case 8: current = current.conceal(); break; case 9: current = current.crossedOut(); break; case 22: current = current.boldOff().faintOff(); break; case 23: current = current.italicOff(); break; case 24: current = current.underlineOff(); break; case 25: current = current.blinkOff(); break; case 27: current = current.inverseOff(); break; case 28: current = current.concealOff(); break; case 29: current = current.crossedOutOff(); break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: current = current.foreground(ansiParam - 30); break; case 39: current = current.foregroundOff(); break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: current = current.background(ansiParam - 40); break; case 49: current = current.backgroundOff(); break; case 38: case 48: if (j + 1 < params.length) { int ansiParam2 = Integer.parseInt(params[++j]); if (ansiParam2 == 2) { if (j + 3 < params.length) { int r = Integer.parseInt(params[++j]); int g = Integer.parseInt(params[++j]); int b = Integer.parseInt(params[++j]); // convert to 256 colors int col = 16 + (r >> 3) * 36 + (g >> 3) * 6 + (b >> 3); if (ansiParam == 38) { current = current.foreground(col); } else { current = current.background(col); } } } else if (ansiParam2 == 5) { if (j + 1 < params.length) { int col = Integer.parseInt(params[++j]); if (ansiParam == 38) { current = current.foreground(col); } else { current = current.background(col); } } } } break; case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97: current = current.foreground(ansiParam - 90 + 8); break; case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: current = current.background(ansiParam - 100 + 8); break; } j++; } ansiState = 0; } else if (!(c >= '0' && c <= '9' || c == ';')) { // This is not a SGR code, so ignore ansiState = 0; } } else if (c == '\t' && tabs > 0) { insertTab(current); } else { ensureCapacity(length + 1); buffer[length] = c; style[length] = this.current.getStyle(); length++; } } } protected void insertTab(AttributedStyle s) { int nb = tabs - length % tabs; ensureCapacity(length + nb); for (int i = 0; i < nb; i++) { buffer[length] = ' '; style[length] = s.getStyle(); length++; } } public void setLength(int l) { length = l; } public AttributedStringBuilder tabs(int tabs) { this.tabs = tabs; return this; } public AttributedStringBuilder styleMatches(Pattern pattern, AttributedStyle s) { Matcher matcher = pattern.matcher(this); while (matcher.find()) { for (int i = matcher.start(); i < matcher.end(); i++) { style[i] = (style[i] & ~s.getMask()) | s.getStyle(); } } return this; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/AttributedStyle.java000066400000000000000000000170371311544710100273000ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; /** * Text styling. * * @author Guillaume Nodet */ public class AttributedStyle { public static final int BLACK = 0; public static final int RED = 1; public static final int GREEN = 2; public static final int YELLOW = 3; public static final int BLUE = 4; public static final int MAGENTA = 5; public static final int CYAN = 6; public static final int WHITE = 7; public static final int BRIGHT = 8; static final int F_BOLD = 0x00000001; static final int F_FAINT = 0x00000002; static final int F_ITALIC = 0x00000004; static final int F_UNDERLINE = 0x00000008; static final int F_BLINK = 0x00000010; static final int F_INVERSE = 0x00000020; static final int F_CONCEAL = 0x00000040; static final int F_CROSSED_OUT = 0x00000080; static final int F_FOREGROUND = 0x00000100; static final int F_BACKGROUND = 0x00000200; static final int F_HIDDEN = 0x00000400; static final int MASK = 0x000007FF; static final int FG_COLOR_EXP = 16; static final int BG_COLOR_EXP = 24; static final int FG_COLOR = 0xFF << FG_COLOR_EXP; static final int BG_COLOR = 0xFF << BG_COLOR_EXP; public static final AttributedStyle DEFAULT = new AttributedStyle(); public static final AttributedStyle BOLD = DEFAULT.bold(); public static final AttributedStyle BOLD_OFF = DEFAULT.boldOff(); public static final AttributedStyle INVERSE = DEFAULT.inverse(); public static final AttributedStyle INVERSE_OFF = DEFAULT.inverseOff(); public static final AttributedStyle HIDDEN = DEFAULT.hidden(); public static final AttributedStyle HIDDEN_OFF = DEFAULT.hiddenOff(); final int style; final int mask; AttributedStyle() { this(0, 0); } AttributedStyle(int style, int mask) { this.style = style; this.mask = mask & MASK | ((style & F_FOREGROUND) != 0 ? FG_COLOR : 0) | ((style & F_BACKGROUND) != 0 ? BG_COLOR : 0); } public AttributedStyle bold() { return new AttributedStyle(style | F_BOLD, mask | F_BOLD); } public AttributedStyle boldOff() { return new AttributedStyle(style & ~F_BOLD, mask | F_BOLD); } public AttributedStyle boldDefault() { return new AttributedStyle(style & ~F_BOLD, mask & ~F_BOLD); } public AttributedStyle faint() { return new AttributedStyle(style | F_FAINT, mask | F_FAINT); } public AttributedStyle faintOff() { return new AttributedStyle(style & ~F_FAINT, mask | F_FAINT); } public AttributedStyle faintDefault() { return new AttributedStyle(style & ~F_FAINT, mask & ~F_FAINT); } public AttributedStyle italic() { return new AttributedStyle(style | F_ITALIC, mask | F_ITALIC); } public AttributedStyle italicOff() { return new AttributedStyle(style & ~F_ITALIC, mask | F_ITALIC); } public AttributedStyle italicDefault() { return new AttributedStyle(style & ~F_ITALIC, mask & ~F_ITALIC); } public AttributedStyle underline() { return new AttributedStyle(style | F_UNDERLINE, mask | F_UNDERLINE); } public AttributedStyle underlineOff() { return new AttributedStyle(style & ~F_UNDERLINE, mask | F_UNDERLINE); } public AttributedStyle underlineDefault() { return new AttributedStyle(style & ~F_UNDERLINE, mask & ~F_UNDERLINE); } public AttributedStyle blink() { return new AttributedStyle(style | F_BLINK, mask | F_BLINK); } public AttributedStyle blinkOff() { return new AttributedStyle(style & ~F_BLINK, mask | F_BLINK); } public AttributedStyle blinkDefault() { return new AttributedStyle(style & ~F_BLINK, mask & ~F_BLINK); } public AttributedStyle inverse() { return new AttributedStyle(style | F_INVERSE, mask | F_INVERSE); } public AttributedStyle inverseNeg() { int s = (style & F_INVERSE) != 0 ? style & ~F_INVERSE : style | F_INVERSE; return new AttributedStyle(s, mask | F_INVERSE); } public AttributedStyle inverseOff() { return new AttributedStyle(style & ~F_INVERSE, mask | F_INVERSE); } public AttributedStyle inverseDefault() { return new AttributedStyle(style & ~F_INVERSE, mask & ~F_INVERSE); } public AttributedStyle conceal() { return new AttributedStyle(style | F_CONCEAL, mask | F_CONCEAL); } public AttributedStyle concealOff() { return new AttributedStyle(style & ~F_CONCEAL, mask | F_CONCEAL); } public AttributedStyle concealDefault() { return new AttributedStyle(style & ~F_CONCEAL, mask & ~F_CONCEAL); } public AttributedStyle crossedOut() { return new AttributedStyle(style | F_CROSSED_OUT, mask | F_CROSSED_OUT); } public AttributedStyle crossedOutOff() { return new AttributedStyle(style & ~F_CROSSED_OUT, mask | F_CROSSED_OUT); } public AttributedStyle crossedOutDefault() { return new AttributedStyle(style & ~F_CROSSED_OUT, mask & ~F_CROSSED_OUT); } public AttributedStyle foreground(int color) { return new AttributedStyle(style & ~FG_COLOR | F_FOREGROUND | ((color << FG_COLOR_EXP) & FG_COLOR), mask | F_FOREGROUND); } public AttributedStyle foregroundOff() { return new AttributedStyle(style & ~FG_COLOR & ~F_FOREGROUND, mask | F_FOREGROUND); } public AttributedStyle foregroundDefault() { return new AttributedStyle(style & ~FG_COLOR & ~F_FOREGROUND, mask & ~(F_FOREGROUND | FG_COLOR)); } public AttributedStyle background(int color) { return new AttributedStyle(style & ~BG_COLOR | F_BACKGROUND | ((color << BG_COLOR_EXP) & BG_COLOR), mask | F_BACKGROUND); } public AttributedStyle backgroundOff() { return new AttributedStyle(style & ~BG_COLOR & ~F_BACKGROUND, mask | F_BACKGROUND); } public AttributedStyle backgroundDefault() { return new AttributedStyle(style & ~BG_COLOR & ~F_BACKGROUND, mask & ~(F_BACKGROUND | BG_COLOR)); } /** * The hidden flag can be used to embed custom escape sequences. * The characters are considered being 0-column long and will be printed as-is. * The user is responsible for ensuring that those sequences do not move the cursor. */ public AttributedStyle hidden() { return new AttributedStyle(style | F_HIDDEN, mask | F_HIDDEN); } public AttributedStyle hiddenOff() { return new AttributedStyle(style & ~F_HIDDEN, mask | F_HIDDEN); } public AttributedStyle hiddenDefault() { return new AttributedStyle(style & ~F_HIDDEN, mask & ~F_HIDDEN); } int getStyle() { return style; } int getMask() { return mask; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AttributedStyle that = (AttributedStyle) o; if (style != that.style) return false; return mask == that.mask; } @Override public int hashCode() { int result = style; result = 31 * result + mask; return result; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/ClosedException.java000066400000000000000000000013401311544710100272260ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.IOException; public class ClosedException extends IOException { private static final long serialVersionUID = 3085420657077696L; public ClosedException() { } public ClosedException(String message) { super(message); } public ClosedException(String message, Throwable cause) { super(message, cause); } public ClosedException(Throwable cause) { super(cause); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/Curses.java000066400000000000000000000355431311544710100254160ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.IOException; import java.io.Writer; import java.util.Stack; /** * Curses helper methods. * * @author Guillaume Nodet */ public final class Curses { private static Object[] sv = new Object[26]; private static Object[] dv = new Object[26]; private static final int IFTE_NONE = 0; private static final int IFTE_IF = 1; private static final int IFTE_THEN = 2; private static final int IFTE_ELSE = 3; private Curses() { } /** * Print the given terminal capabilities * * @param out the output stream * @param str the capability to output * @param params optional parameters * @throws IOException if an error occurs */ public static void tputs(Writer out, String str, Object... params) throws IOException { int index = 0; int length = str.length(); int ifte = IFTE_NONE; boolean exec = true; Stack stack = new Stack<>(); while (index < length) { char ch = str.charAt(index++); switch (ch) { case '\\': ch = str.charAt(index++); if (ch >= '0' && ch <= '9') { throw new UnsupportedOperationException(); // todo } else { switch (ch) { case 'e': case 'E': if (exec) { out.write(27); // escape } break; case 'n': out.write('\n'); break; // case 'l': // rawPrint('\l'); // break; case 'r': if (exec) { out.write('\r'); } break; case 't': if (exec) { out.write('\t'); } break; case 'b': if (exec) { out.write('\b'); } break; case 'f': if (exec) { out.write('\f'); } break; case 's': if (exec) { out.write(' '); } break; case ':': case '^': case '\\': if (exec) { out.write(ch); } break; default: throw new IllegalArgumentException(); } } break; case '^': ch = str.charAt(index++); if (exec) { out.write(ch - '@'); } break; case '%': ch = str.charAt(index++); switch (ch) { case '%': if (exec) { out.write('%'); } break; case 'p': ch = str.charAt(index++); if (exec) { stack.push(params[ch - '1']); } break; case 'P': ch = str.charAt(index++); if (ch >= 'a' && ch <= 'z') { if (exec) { dv[ch - 'a'] = stack.pop(); } } else if (ch >= 'A' && ch <= 'Z') { if (exec) { sv[ch - 'A'] = stack.pop(); } } else { throw new IllegalArgumentException(); } break; case 'g': ch = str.charAt(index++); if (ch >= 'a' && ch <= 'z') { if (exec) { stack.push(dv[ch - 'a']); } } else if (ch >= 'A' && ch <= 'Z') { if (exec) { stack.push(sv[ch - 'A']); } } else { throw new IllegalArgumentException(); } break; case '\'': ch = str.charAt(index++); if (exec) { stack.push((int) ch); } ch = str.charAt(index++); if (ch != '\'') { throw new IllegalArgumentException(); } break; case '{': int start = index; while (str.charAt(index++) != '}') ; if (exec) { int v = Integer.valueOf(str.substring(start, index - 1)); stack.push(v); } break; case 'l': if (exec) { stack.push(stack.pop().toString().length()); } break; case '+': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 + v2); } break; case '-': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 - v2); } break; case '*': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 * v2); } break; case '/': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 / v2); } break; case 'm': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 % v2); } break; case '&': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 & v2); } break; case '|': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 | v2); } break; case '^': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 ^ v2); } break; case '=': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 == v2); } break; case '>': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 > v2); } break; case '<': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 < v2); } break; case 'A': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 != 0 && v2 != 0); } break; case '!': if (exec) { int v1 = toInteger(stack.pop()); stack.push(v1 == 0); } break; case '~': if (exec) { int v1 = toInteger(stack.pop()); stack.push(~v1); } break; case 'O': if (exec) { int v2 = toInteger(stack.pop()); int v1 = toInteger(stack.pop()); stack.push(v1 != 0 || v2 != 0); } break; case '?': if (ifte != IFTE_NONE) { throw new IllegalArgumentException(); } else { ifte = IFTE_IF; } break; case 't': if (ifte != IFTE_IF && ifte != IFTE_ELSE) { throw new IllegalArgumentException(); } else { ifte = IFTE_THEN; } exec = toInteger(stack.pop()) != 0; break; case 'e': if (ifte != IFTE_THEN) { throw new IllegalArgumentException(); } else { ifte = IFTE_ELSE; } exec = !exec; break; case ';': if (ifte == IFTE_NONE || ifte == IFTE_IF) { throw new IllegalArgumentException(); } else { ifte = IFTE_NONE; } exec = true; break; case 'i': if (params.length >= 1) { params[0] = toInteger(params[0]) + 1; } if (params.length >= 2) { params[1] = toInteger(params[1]) + 1; } break; case 'd': out.write(Integer.toString(toInteger(stack.pop()))); break; default: throw new UnsupportedOperationException(); } break; case '$': if (str.charAt(index) == '<') { // We don't honour delays, just skip int nb = 0; while ((ch = str.charAt(++index)) != '>') { if (ch >= '0' && ch <= '9') { nb = nb * 10 + (ch - '0'); } else if (ch == '*') { // ignore } else if (ch == '/') { // ignore } else { // illegal, but ... } } index++; try { out.flush(); Thread.sleep(nb); } catch (InterruptedException e) { } } else { if (exec) { out.write(ch); } } break; default: if (exec) { out.write(ch); } break; } } } private static int toInteger(Object pop) { if (pop instanceof Number) { return ((Number) pop).intValue(); } else if (pop instanceof Boolean) { return (Boolean) pop ? 1 : 0; } else { return Integer.valueOf(pop.toString()); } } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/DiffHelper.java000066400000000000000000000113061311544710100261510ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.util.LinkedList; import java.util.List; /** * Class containing the diff method. * This diff is ANSI aware and will correctly handle text attributes * so that any text in a Diff object is a valid ansi string. */ public class DiffHelper { /** * The data structure representing a diff is a Linked list of Diff objects: * {Diff(Operation.DELETE, "Hello"), Diff(Operation.INSERT, "Goodbye"), * Diff(Operation.EQUAL, " world.")} * which means: delete "Hello", add "Goodbye" and keep " world." */ public enum Operation { DELETE, INSERT, EQUAL } /** * Class representing one diff operation. */ public static class Diff { /** * One of: INSERT, DELETE or EQUAL. */ public final Operation operation; /** * The text associated with this diff operation. */ public final AttributedString text; /** * Constructor. Initializes the diff with the provided values. * @param operation One of INSERT, DELETE or EQUAL. * @param text The text being applied. */ public Diff(Operation operation, AttributedString text) { // Construct a diff with the specified operation and text. this.operation = operation; this.text = text; } /** * Display a human-readable version of this Diff. * @return text version. */ public String toString() { return "Diff(" + this.operation + ",\"" + this.text + "\")"; } } /** * Compute a list of difference between two lines. * The result will contain at most 4 Diff objects, as the method * aims to return the common prefix, inserted text, deleted text and * common suffix. * The computation is done on characters and their attributes expressed * as ansi sequences. * * @param text1 the old line * @param text2 the new line * @return a list of Diff */ public static List diff(AttributedString text1, AttributedString text2) { int l1 = text1.length(); int l2 = text2.length(); int n = Math.min(l1, l2); int commonStart = 0; // Given a run of contiguous "hidden" characters (which are // sequences of uninterrupted escape sequences) we always want to // print either the entire run or none of it - never a part of it. int startHiddenRange = -1; while (commonStart < n && text1.charAt(commonStart) == text2.charAt(commonStart) && text1.styleAt(commonStart).equals(text2.styleAt(commonStart))) { if (text1.isHidden(commonStart)) { if (startHiddenRange < 0) startHiddenRange = commonStart; } else startHiddenRange = -1; commonStart++; } if (startHiddenRange >= 0 && (commonStart == l1 || ! text1.isHidden(commonStart)) && (commonStart == l2 || ! text2.isHidden(commonStart))) commonStart = startHiddenRange; startHiddenRange = -1; int commonEnd = 0; while (commonEnd < n - commonStart && text1.charAt(l1 - commonEnd - 1) == text2.charAt(l2 - commonEnd - 1) && text1.styleAt(l1 - commonEnd - 1).equals(text2.styleAt(l2 - commonEnd - 1))) { if (text1.isHidden(l1 - commonEnd - 1)) { if (startHiddenRange < 0) startHiddenRange = commonEnd; } else startHiddenRange = -1; commonEnd++; } if (startHiddenRange >= 0) commonEnd = startHiddenRange; LinkedList diffs = new LinkedList<>(); if (commonStart > 0) { diffs.add(new Diff(DiffHelper.Operation.EQUAL, text1.subSequence(0, commonStart))); } if (l2 > commonStart + commonEnd) { diffs.add(new Diff(DiffHelper.Operation.INSERT, text2.subSequence(commonStart, l2 - commonEnd))); } if (l1 > commonStart + commonEnd) { diffs.add(new Diff(DiffHelper.Operation.DELETE, text1.subSequence(commonStart, l1 - commonEnd))); } if (commonEnd > 0) { diffs.add(new Diff(DiffHelper.Operation.EQUAL, text1.subSequence(l1 - commonEnd, l1))); } return diffs; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/Display.java000066400000000000000000000464551311544710100255630ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.IOError; import java.io.IOException; import java.io.StringWriter; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import org.jline.terminal.Terminal; import org.jline.utils.InfoCmp.Capability; /** * Handle display and visual cursor. * * @author Guillaume Nodet */ public class Display { protected final Terminal terminal; protected final boolean fullScreen; protected List oldLines = Collections.emptyList(); protected int cursorPos; private int columns; private int columns1; // columns+1 protected int rows; protected boolean reset; protected boolean delayLineWrap; protected final Map cost = new HashMap<>(); protected final boolean canScroll; protected final boolean wrapAtEol; protected final boolean delayedWrapAtEol; protected final boolean cursorDownIsNewLine; public Display(Terminal terminal, boolean fullscreen) { this.terminal = terminal; this.fullScreen = fullscreen; this.canScroll = can(Capability.insert_line, Capability.parm_insert_line) && can(Capability.delete_line, Capability.parm_delete_line); this.wrapAtEol = terminal.getBooleanCapability(Capability.auto_right_margin); this.delayedWrapAtEol = this.wrapAtEol && terminal.getBooleanCapability(Capability.eat_newline_glitch); this.cursorDownIsNewLine = "\n".equals(tput(Capability.cursor_down)); } /** If cursor is at right margin, don't wrap immediately. * See {@link org.jline.reader.LineReader.Option#DELAY_LINE_WRAP}. */ public boolean delayLineWrap() { return delayLineWrap; } public void setDelayLineWrap(boolean v) { delayLineWrap = v; } public void resize(int rows, int columns) { if (this.rows != rows || this.columns != columns) { this.rows = rows; this.columns = columns; this.columns1 = columns + 1; oldLines = AttributedString.join(AttributedString.EMPTY, oldLines).columnSplitLength(columns, true, delayLineWrap()); } } public void reset() { oldLines = Collections.emptyList(); } /** * Clears the whole screen. * Use this method only when using full-screen / application mode. */ public void clear() { if (fullScreen) { reset = true; } } public void updateAnsi(List newLines, int targetCursorPos) { update(newLines.stream().map(AttributedString::fromAnsi).collect(Collectors.toList()), targetCursorPos); } /** * Update the display according to the new lines and flushes the output. * @param targetCursorPos desired cursor position - see Size.cursorPos. */ public void update(List newLines, int targetCursorPos) { update(newLines, targetCursorPos, true); } /** * Update the display according to the new lines. * @param targetCursorPos desired cursor position - see Size.cursorPos. * @param flush whether the output should be flushed or not */ public void update(List newLines, int targetCursorPos, boolean flush) { if (reset) { terminal.puts(Capability.clear_screen); oldLines.clear(); cursorPos = 0; reset = false; } // If dumb display, get rid of ansi sequences now Integer cols = terminal.getNumericCapability(Capability.max_colors); if (cols == null || cols < 8) { newLines = newLines.stream().map(s -> new AttributedString(s.toString())) .collect(Collectors.toList()); } // Detect scrolling if ((fullScreen || newLines.size() >= rows) && newLines.size() == oldLines.size() && canScroll) { int nbHeaders = 0; int nbFooters = 0; // Find common headers and footers int l = newLines.size(); while (nbHeaders < l && Objects.equals(newLines.get(nbHeaders), oldLines.get(nbHeaders))) { nbHeaders++; } while (nbFooters < l - nbHeaders - 1 && Objects.equals(newLines.get(newLines.size() - nbFooters - 1), oldLines.get(oldLines.size() - nbFooters - 1))) { nbFooters++; } List o1 = newLines.subList(nbHeaders, newLines.size() - nbFooters); List o2 = oldLines.subList(nbHeaders, oldLines.size() - nbFooters); int[] common = longestCommon(o1, o2); if (common != null) { int s1 = common[0]; int s2 = common[1]; int sl = common[2]; if (sl > 1 && s1 < s2) { moveVisualCursorTo((nbHeaders + s1) * columns1); int nb = s2 - s1; deleteLines(nb); for (int i = 0; i < nb; i++) { oldLines.remove(nbHeaders + s1); } if (nbFooters > 0) { moveVisualCursorTo((nbHeaders + s1 + sl) * columns1); insertLines(nb); for (int i = 0; i < nb; i++) { oldLines.add(nbHeaders + s1 + sl, new AttributedString("")); } } } else if (sl > 1 && s1 > s2) { int nb = s1 - s2; if (nbFooters > 0) { moveVisualCursorTo((nbHeaders + s2 + sl) * columns1); deleteLines(nb); for (int i = 0; i < nb; i++) { oldLines.remove(nbHeaders + s2 + sl); } } moveVisualCursorTo((nbHeaders + s2) * columns1); insertLines(nb); for (int i = 0; i < nb; i++) { oldLines.add(nbHeaders + s2, new AttributedString("")); } } } } int lineIndex = 0; int currentPos = 0; int numLines = Math.max(oldLines.size(), newLines.size()); boolean wrapNeeded = false; while (lineIndex < numLines) { AttributedString oldLine = lineIndex < oldLines.size() ? oldLines.get(lineIndex) : AttributedString.NEWLINE; AttributedString newLine = lineIndex < newLines.size() ? newLines.get(lineIndex) : AttributedString.NEWLINE; currentPos = lineIndex * columns1; int curCol = currentPos; int oldLength = oldLine.length(); int newLength = newLine.length(); boolean oldNL = oldLength > 0 && oldLine.charAt(oldLength-1)=='\n'; boolean newNL = newLength > 0 && newLine.charAt(newLength-1)=='\n'; if (oldNL) { oldLength--; oldLine = oldLine.substring(0, oldLength); } if (newNL) { newLength--; newLine = newLine.substring(0, newLength); } if (wrapNeeded && lineIndex == (cursorPos + 1) / columns1 && lineIndex < newLines.size()) { // move from right margin to next line's left margin cursorPos++; if (newLength == 0 || newLine.isHidden(0)) { // go to next line column zero rawPrint(new AttributedString(" \b")); } else { AttributedString firstChar = newLine.columnSubSequence(0, 1); // go to next line column one rawPrint(firstChar); cursorPos++; int firstLength = firstChar.length(); // normally 1 newLine = newLine.substring(firstLength, newLength); newLength -= firstLength; if (oldLength >= firstLength) { oldLine = oldLine.substring(firstLength, oldLength); oldLength -= firstLength; } currentPos = cursorPos; } } List diffs = DiffHelper.diff(oldLine, newLine); boolean ident = true; boolean cleared = false; for (int i = 0; i < diffs.size(); i++) { DiffHelper.Diff diff = diffs.get(i); int width = diff.text.columnLength(); switch (diff.operation) { case EQUAL: if (!ident) { cursorPos = moveVisualCursorTo(currentPos); rawPrint(diff.text); cursorPos += width; currentPos = cursorPos; } else { currentPos += width; } break; case INSERT: if (i <= diffs.size() - 2 && diffs.get(i + 1).operation == DiffHelper.Operation.EQUAL) { cursorPos = moveVisualCursorTo(currentPos); if (insertChars(width)) { rawPrint(diff.text); cursorPos += width; currentPos = cursorPos; break; } } else if (i <= diffs.size() - 2 && diffs.get(i + 1).operation == DiffHelper.Operation.DELETE && width == diffs.get(i + 1).text.columnLength()) { moveVisualCursorTo(currentPos); rawPrint(diff.text); cursorPos += width; currentPos = cursorPos; i++; // skip delete break; } moveVisualCursorTo(currentPos); rawPrint(diff.text); cursorPos += width; currentPos = cursorPos; ident = false; break; case DELETE: if (cleared) { continue; } if (currentPos - curCol >= columns) { continue; } if (i <= diffs.size() - 2 && diffs.get(i + 1).operation == DiffHelper.Operation.EQUAL) { if (currentPos + diffs.get(i + 1).text.columnLength() < columns) { moveVisualCursorTo(currentPos); if (deleteChars(width)) { break; } } } int oldLen = oldLine.columnLength(); int newLen = newLine.columnLength(); int nb = Math.max(oldLen, newLen) - (currentPos - curCol); moveVisualCursorTo(currentPos); if (!terminal.puts(Capability.clr_eol)) { rawPrint(' ', nb); cursorPos += nb; } cleared = true; ident = false; break; } } lineIndex++; boolean newWrap = ! newNL && lineIndex < newLines.size(); if (targetCursorPos + 1 == lineIndex * columns1 && (newWrap || ! delayLineWrap)) targetCursorPos++; boolean atRight = (cursorPos - curCol) % columns1 == columns; wrapNeeded = false; if (this.delayedWrapAtEol) { boolean oldWrap = ! oldNL && lineIndex < oldLines.size(); if (newWrap != oldWrap && ! (oldWrap && cleared)) { moveVisualCursorTo(lineIndex*columns1-1, newLines); if (newWrap) wrapNeeded = true; else terminal.puts(Capability.clr_eol); } } else if (atRight) { if (this.wrapAtEol) { terminal.writer().write(" \b"); cursorPos++; } else { terminal.puts(Capability.carriage_return); // CR / not newline. cursorPos = curCol; } currentPos = cursorPos; } } int was = cursorPos; if (cursorPos != targetCursorPos) { moveVisualCursorTo(targetCursorPos < 0 ? currentPos : targetCursorPos, newLines); } oldLines = newLines; if (flush) { terminal.flush(); } } protected boolean deleteLines(int nb) { return perform(Capability.delete_line, Capability.parm_delete_line, nb); } protected boolean insertLines(int nb) { return perform(Capability.insert_line, Capability.parm_insert_line, nb); } protected boolean insertChars(int nb) { return perform(Capability.insert_character, Capability.parm_ich, nb); } protected boolean deleteChars(int nb) { return perform(Capability.delete_character, Capability.parm_dch, nb); } protected boolean can(Capability single, Capability multi) { return terminal.getStringCapability(single) != null || terminal.getStringCapability(multi) != null; } protected boolean perform(Capability single, Capability multi, int nb) { boolean hasMulti = terminal.getStringCapability(multi) != null; boolean hasSingle = terminal.getStringCapability(single) != null; if (hasMulti && (!hasSingle || cost(single) * nb > cost(multi))) { terminal.puts(multi, nb); return true; } else if (hasSingle) { for (int i = 0; i < nb; i++) { terminal.puts(single); } return true; } else { return false; } } private int cost(Capability cap) { return cost.computeIfAbsent(cap, this::computeCost); } private int computeCost(Capability cap) { String s = tput(cap, 0); return s != null ? s.length() : Integer.MAX_VALUE; } private String tput(Capability cap, Object... params) { try { StringWriter sw = new StringWriter(); String d = terminal.getStringCapability(cap); if (d != null) { Curses.tputs(sw, d, params); return sw.toString(); } return null; } catch (IOException e) { throw new IOError(e); } } private static int[] longestCommon(List l1, List l2) { int start1 = 0; int start2 = 0; int max = 0; for (int i = 0; i < l1.size(); i++) { for (int j = 0; j < l2.size(); j++) { int x = 0; while (Objects.equals(l1.get(i + x), l2.get(j + x))) { x++; if (((i + x) >= l1.size()) || ((j + x) >= l2.size())) break; } if (x > max) { max = x; start1 = i; start2 = j; } } } return max != 0 ? new int[] { start1, start2, max } : null; } /** Move cursor from cursorPos to argument, updating cursorPos * We're at the right margin if {@code (cursorPos % columns1) == columns}. * This method knows how to move both *from* and *to* the right margin. */ protected void moveVisualCursorTo(int targetPos, List newLines) { if (cursorPos != targetPos) { boolean atRight = (targetPos % columns1) == columns; moveVisualCursorTo(targetPos - (atRight ? 1 : 0)); if (atRight) { // There is no portable way to move to the right margin // except by writing a character in the right-most column. int row = targetPos / columns1; AttributedString lastChar = row >= newLines.size() ? AttributedString.EMPTY : newLines.get(row).columnSubSequence(columns-1, columns); if (lastChar.length() == 0) rawPrint((int) ' '); else rawPrint(lastChar); cursorPos++; } } } /** Move cursor from cursorPos to argument, updating cursorPos * We're at the right margin if {@code (cursorPos % columns1) == columns}. * This method knows how to move *from* the right margin, * but does not know how to move *to* the right margin. * I.e. {@code (i1 % columns1) == column} is not allowed. */ protected int moveVisualCursorTo(int i1) { int i0 = cursorPos; if (i0 == i1) return i1; int width = columns1; int l0 = i0 / width; int c0 = i0 % width; int l1 = i1 / width; int c1 = i1 % width; if (c0 == columns) { // at right margin terminal.puts(Capability.carriage_return); c0 = 0; } if (l0 > l1) { perform(Capability.cursor_up, Capability.parm_up_cursor, l0 - l1); } else if (l0 < l1) { // TODO: clean the following if (fullScreen) { if (!terminal.puts(Capability.parm_down_cursor, l1 - l0)) { for (int i = l0; i < l1; i++) { terminal.puts(Capability.cursor_down); } if (cursorDownIsNewLine) { c0 = 0; } } } else { terminal.puts(Capability.carriage_return); rawPrint('\n', l1 - l0); c0 = 0; } } if (c0 != 0 && c1 == 0) { terminal.puts(Capability.carriage_return); } else if (c0 < c1) { perform(Capability.cursor_right, Capability.parm_right_cursor, c1 - c0); } else if (c0 > c1) { perform(Capability.cursor_left, Capability.parm_left_cursor, c0 - c1); } cursorPos = i1; return i1; } void rawPrint(char c, int num) { for (int i = 0; i < num; i++) { rawPrint(c); } } void rawPrint(int c) { terminal.writer().write(c); } void rawPrint(AttributedString str) { terminal.writer().write(str.toAnsi(terminal)); } public int wcwidth(String str) { return AttributedString.fromAnsi(str).columnLength(); } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/ExecHelper.java000066400000000000000000000050731311544710100261710ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.util.Objects; /** * Helper methods for running unix commands. */ public final class ExecHelper { private ExecHelper() { } public static String exec(boolean redirectInput, final String... cmd) throws IOException { Objects.requireNonNull(cmd); try { Log.trace("Running: ", cmd); ProcessBuilder pb = new ProcessBuilder(cmd); if (redirectInput) { pb.redirectInput(ProcessBuilder.Redirect.INHERIT); } Process p = pb.start(); String result = waitAndCapture(p); Log.trace("Result: ", result); if (p.exitValue() != 0) { if (result.endsWith("\n")) { result = result.substring(0, result.length() - 1); } throw new IOException("Error executing '" + String.join(" ", (CharSequence[]) cmd) + "': " + result); } return result; } catch (InterruptedException e) { throw (IOException) new InterruptedIOException("Command interrupted").initCause(e); } } public static String waitAndCapture(Process p) throws IOException, InterruptedException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); InputStream in = null; InputStream err = null; OutputStream out = null; try { int c; 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 { close(in, out, err); } return bout.toString(); } private static void close(final Closeable... closeables) { for (Closeable c : closeables) { if (c != null) { try { c.close(); } catch (Exception e) { // Ignore } } } } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/InfoCmp.java000066400000000000000000001023131311544710100254730ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.BufferedReader; import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * Infocmp helper methods. * * @author Guillaume Nodet */ public final class InfoCmp { private static final Map CAPS = new HashMap<>(); private InfoCmp() { } @SuppressWarnings("unused") public enum Capability { auto_left_margin, // auto_left_margin, bw, bw auto_right_margin, // auto_right_margin, am, am back_color_erase, // back_color_erase, bce, ut can_change, // can_change, ccc, cc ceol_standout_glitch, // ceol_standout_glitch, xhp, xs col_addr_glitch, // col_addr_glitch, xhpa, YA cpi_changes_res, // cpi_changes_res, cpix, YF cr_cancels_micro_mode, // cr_cancels_micro_mode, crxm, YB dest_tabs_magic_smso, // dest_tabs_magic_smso, xt, xt eat_newline_glitch, // eat_newline_glitch, xenl, xn erase_overstrike, // erase_overstrike, eo, eo generic_type, // generic_type, gn, gn hard_copy, // hard_copy, hc, hc hard_cursor, // hard_cursor, chts, HC has_meta_key, // has_meta_key, km, km has_print_wheel, // has_print_wheel, daisy, YC has_status_line, // has_status_line, hs, hs hue_lightness_saturation, // hue_lightness_saturation, hls, hl insert_null_glitch, // insert_null_glitch, in, in lpi_changes_res, // lpi_changes_res, lpix, YG memory_above, // memory_above, da, da memory_below, // memory_below, db, db move_insert_mode, // move_insert_mode, mir, mi move_standout_mode, // move_standout_mode, msgr, ms needs_xon_xoff, // needs_xon_xoff, nxon, nx no_esc_ctlc, // no_esc_ctlc, xsb, xb no_pad_char, // no_pad_char, npc, NP non_dest_scroll_region, // non_dest_scroll_region, ndscr, ND non_rev_rmcup, // non_rev_rmcup, nrrmc, NR over_strike, // over_strike, os, os prtr_silent, // prtr_silent, mc5i, 5i row_addr_glitch, // row_addr_glitch, xvpa, YD semi_auto_right_margin, // semi_auto_right_margin, sam, YE status_line_esc_ok, // status_line_esc_ok, eslok, es tilde_glitch, // tilde_glitch, hz, hz transparent_underline, // transparent_underline, ul, ul xon_xoff, // xon_xoff, xon, xo columns, // columns, cols, co init_tabs, // init_tabs, it, it label_height, // label_height, lh, lh label_width, // label_width, lw, lw lines, // lines, lines, li lines_of_memory, // lines_of_memory, lm, lm magic_cookie_glitch, // magic_cookie_glitch, xmc, sg max_attributes, // max_attributes, ma, ma max_colors, // max_colors, colors, Co max_pairs, // max_pairs, pairs, pa maximum_windows, // maximum_windows, wnum, MW no_color_video, // no_color_video, ncv, NC num_labels, // num_labels, nlab, Nl padding_baud_rate, // padding_baud_rate, pb, pb virtual_terminal, // virtual_terminal, vt, vt width_status_line, // width_status_line, wsl, ws bit_image_entwining, // bit_image_entwining, bitwin, Yo bit_image_type, // bit_image_type, bitype, Yp buffer_capacity, // buffer_capacity, bufsz, Ya buttons, // buttons, btns, BT dot_horz_spacing, // dot_horz_spacing, spinh, Yc dot_vert_spacing, // dot_vert_spacing, spinv, Yb max_micro_address, // max_micro_address, maddr, Yd max_micro_jump, // max_micro_jump, mjump, Ye micro_col_size, // micro_col_size, mcs, Yf micro_line_size, // micro_line_size, mls, Yg number_of_pins, // number_of_pins, npins, Yh output_res_char, // output_res_char, orc, Yi output_res_horz_inch, // output_res_horz_inch, orhi, Yk output_res_line, // output_res_line, orl, Yj output_res_vert_inch, // output_res_vert_inch, orvi, Yl print_rate, // print_rate, cps, Ym wide_char_size, // wide_char_size, widcs, Yn acs_chars, // acs_chars, acsc, ac back_tab, // back_tab, cbt, bt bell, // bell, bel, bl carriage_return, // carriage_return, cr, cr change_char_pitch, // change_char_pitch, cpi, ZA change_line_pitch, // change_line_pitch, lpi, ZB change_res_horz, // change_res_horz, chr, ZC change_res_vert, // change_res_vert, cvr, ZD change_scroll_region, // change_scroll_region, csr, cs char_padding, // char_padding, rmp, rP clear_all_tabs, // clear_all_tabs, tbc, ct clear_margins, // clear_margins, mgc, MC clear_screen, // clear_screen, clear, cl clr_bol, // clr_bol, el1, cb clr_eol, // clr_eol, el, ce clr_eos, // clr_eos, ed, cd column_address, // column_address, hpa, ch command_character, // command_character, cmdch, CC create_window, // create_window, cwin, CW cursor_address, // cursor_address, cup, cm cursor_down, // cursor_down, cud1, do cursor_home, // cursor_home, home, ho cursor_invisible, // cursor_invisible, civis, vi cursor_left, // cursor_left, cub1, le cursor_mem_address, // cursor_mem_address, mrcup, CM cursor_normal, // cursor_normal, cnorm, ve cursor_right, // cursor_right, cuf1, nd cursor_to_ll, // cursor_to_ll, ll, ll cursor_up, // cursor_up, cuu1, up cursor_visible, // cursor_visible, cvvis, vs define_char, // define_char, defc, ZE delete_character, // delete_character, dch1, dc delete_line, // delete_line, dl1, dl dial_phone, // dial_phone, dial, DI dis_status_line, // dis_status_line, dsl, ds display_clock, // display_clock, dclk, DK down_half_line, // down_half_line, hd, hd ena_acs, // ena_acs, enacs, eA enter_alt_charset_mode, // enter_alt_charset_mode, smacs, as enter_am_mode, // enter_am_mode, smam, SA enter_blink_mode, // enter_blink_mode, blink, mb enter_bold_mode, // enter_bold_mode, bold, md enter_ca_mode, // enter_ca_mode, smcup, ti enter_delete_mode, // enter_delete_mode, smdc, dm enter_dim_mode, // enter_dim_mode, dim, mh enter_doublewide_mode, // enter_doublewide_mode, swidm, ZF enter_draft_quality, // enter_draft_quality, sdrfq, ZG enter_insert_mode, // enter_insert_mode, smir, im enter_italics_mode, // enter_italics_mode, sitm, ZH enter_leftward_mode, // enter_leftward_mode, slm, ZI enter_micro_mode, // enter_micro_mode, smicm, ZJ enter_near_letter_quality, // enter_near_letter_quality, snlq, ZK enter_normal_quality, // enter_normal_quality, snrmq, ZL enter_protected_mode, // enter_protected_mode, prot, mp enter_reverse_mode, // enter_reverse_mode, rev, mr enter_secure_mode, // enter_secure_mode, invis, mk enter_shadow_mode, // enter_shadow_mode, sshm, ZM enter_standout_mode, // enter_standout_mode, smso, so enter_subscript_mode, // enter_subscript_mode, ssubm, ZN enter_superscript_mode, // enter_superscript_mode, ssupm, ZO enter_underline_mode, // enter_underline_mode, smul, us enter_upward_mode, // enter_upward_mode, sum, ZP enter_xon_mode, // enter_xon_mode, smxon, SX erase_chars, // erase_chars, ech, ec exit_alt_charset_mode, // exit_alt_charset_mode, rmacs, ae exit_am_mode, // exit_am_mode, rmam, RA exit_attribute_mode, // exit_attribute_mode, sgr0, me exit_ca_mode, // exit_ca_mode, rmcup, te exit_delete_mode, // exit_delete_mode, rmdc, ed exit_doublewide_mode, // exit_doublewide_mode, rwidm, ZQ exit_insert_mode, // exit_insert_mode, rmir, ei exit_italics_mode, // exit_italics_mode, ritm, ZR exit_leftward_mode, // exit_leftward_mode, rlm, ZS exit_micro_mode, // exit_micro_mode, rmicm, ZT exit_shadow_mode, // exit_shadow_mode, rshm, ZU exit_standout_mode, // exit_standout_mode, rmso, se exit_subscript_mode, // exit_subscript_mode, rsubm, ZV exit_superscript_mode, // exit_superscript_mode, rsupm, ZW exit_underline_mode, // exit_underline_mode, rmul, ue exit_upward_mode, // exit_upward_mode, rum, ZX exit_xon_mode, // exit_xon_mode, rmxon, RX fixed_pause, // fixed_pause, pause, PA flash_hook, // flash_hook, hook, fh flash_screen, // flash_screen, flash, vb form_feed, // form_feed, ff, ff from_status_line, // from_status_line, fsl, fs goto_window, // goto_window, wingo, WG hangup, // hangup, hup, HU init_1string, // init_1string, is1, i1 init_2string, // init_2string, is2, is init_3string, // init_3string, is3, i3 init_file, // init_file, if, if init_prog, // init_prog, iprog, iP initialize_color, // initialize_color, initc, Ic initialize_pair, // initialize_pair, initp, Ip insert_character, // insert_character, ich1, ic insert_line, // insert_line, il1, al insert_padding, // insert_padding, ip, ip key_a1, // key_a1, ka1, K1 key_a3, // key_a3, ka3, K3 key_b2, // key_b2, kb2, K2 key_backspace, // key_backspace, kbs, kb key_beg, // key_beg, kbeg, @1 key_btab, // key_btab, kcbt, kB key_c1, // key_c1, kc1, K4 key_c3, // key_c3, kc3, K5 key_cancel, // key_cancel, kcan, @2 key_catab, // key_catab, ktbc, ka key_clear, // key_clear, kclr, kC key_close, // key_close, kclo, @3 key_command, // key_command, kcmd, @4 key_copy, // key_copy, kcpy, @5 key_create, // key_create, kcrt, @6 key_ctab, // key_ctab, kctab, kt key_dc, // key_dc, kdch1, kD key_dl, // key_dl, kdl1, kL key_down, // key_down, kcud1, kd key_eic, // key_eic, krmir, kM key_end, // key_end, kend, @7 key_enter, // key_enter, kent, @8 key_eol, // key_eol, kel, kE key_eos, // key_eos, ked, kS key_exit, // key_exit, kext, @9 key_f0, // key_f0, kf0, k0 key_f1, // key_f1, kf1, k1 key_f10, // key_f10, kf10, k; key_f11, // key_f11, kf11, F1 key_f12, // key_f12, kf12, F2 key_f13, // key_f13, kf13, F3 key_f14, // key_f14, kf14, F4 key_f15, // key_f15, kf15, F5 key_f16, // key_f16, kf16, F6 key_f17, // key_f17, kf17, F7 key_f18, // key_f18, kf18, F8 key_f19, // key_f19, kf19, F9 key_f2, // key_f2, kf2, k2 key_f20, // key_f20, kf20, FA key_f21, // key_f21, kf21, FB key_f22, // key_f22, kf22, FC key_f23, // key_f23, kf23, FD key_f24, // key_f24, kf24, FE key_f25, // key_f25, kf25, FF key_f26, // key_f26, kf26, FG key_f27, // key_f27, kf27, FH key_f28, // key_f28, kf28, FI key_f29, // key_f29, kf29, FJ key_f3, // key_f3, kf3, k3 key_f30, // key_f30, kf30, FK key_f31, // key_f31, kf31, FL key_f32, // key_f32, kf32, FM key_f33, // key_f33, kf33, FN key_f34, // key_f34, kf34, FO key_f35, // key_f35, kf35, FP key_f36, // key_f36, kf36, FQ key_f37, // key_f37, kf37, FR key_f38, // key_f38, kf38, FS key_f39, // key_f39, kf39, FT key_f4, // key_f4, kf4, k4 key_f40, // key_f40, kf40, FU key_f41, // key_f41, kf41, FV key_f42, // key_f42, kf42, FW key_f43, // key_f43, kf43, FX key_f44, // key_f44, kf44, FY key_f45, // key_f45, kf45, FZ key_f46, // key_f46, kf46, Fa key_f47, // key_f47, kf47, Fb key_f48, // key_f48, kf48, Fc key_f49, // key_f49, kf49, Fd key_f5, // key_f5, kf5, k5 key_f50, // key_f50, kf50, Fe key_f51, // key_f51, kf51, Ff key_f52, // key_f52, kf52, Fg key_f53, // key_f53, kf53, Fh key_f54, // key_f54, kf54, Fi key_f55, // key_f55, kf55, Fj key_f56, // key_f56, kf56, Fk key_f57, // key_f57, kf57, Fl key_f58, // key_f58, kf58, Fm key_f59, // key_f59, kf59, Fn key_f6, // key_f6, kf6, k6 key_f60, // key_f60, kf60, Fo key_f61, // key_f61, kf61, Fp key_f62, // key_f62, kf62, Fq key_f63, // key_f63, kf63, Fr key_f7, // key_f7, kf7, k7 key_f8, // key_f8, kf8, k8 key_f9, // key_f9, kf9, k9 key_find, // key_find, kfnd, @0 key_help, // key_help, khlp, %1 key_home, // key_home, khome, kh key_ic, // key_ic, kich1, kI key_il, // key_il, kil1, kA key_left, // key_left, kcub1, kl key_ll, // key_ll, kll, kH key_mark, // key_mark, kmrk, %2 key_message, // key_message, kmsg, %3 key_move, // key_move, kmov, %4 key_next, // key_next, knxt, %5 key_npage, // key_npage, knp, kN key_open, // key_open, kopn, %6 key_options, // key_options, kopt, %7 key_ppage, // key_ppage, kpp, kP key_previous, // key_previous, kprv, %8 key_print, // key_print, kprt, %9 key_redo, // key_redo, krdo, %0 key_reference, // key_reference, kref, &1 key_refresh, // key_refresh, krfr, &2 key_replace, // key_replace, krpl, &3 key_restart, // key_restart, krst, &4 key_resume, // key_resume, kres, &5 key_right, // key_right, kcuf1, kr key_save, // key_save, ksav, &6 key_sbeg, // key_sbeg, kBEG, &9 key_scancel, // key_scancel, kCAN, &0 key_scommand, // key_scommand, kCMD, *1 key_scopy, // key_scopy, kCPY, *2 key_screate, // key_screate, kCRT, *3 key_sdc, // key_sdc, kDC, *4 key_sdl, // key_sdl, kDL, *5 key_select, // key_select, kslt, *6 key_send, // key_send, kEND, *7 key_seol, // key_seol, kEOL, *8 key_sexit, // key_sexit, kEXT, *9 key_sf, // key_sf, kind, kF key_sfind, // key_sfind, kFND, *0 key_shelp, // key_shelp, kHLP, #1 key_shome, // key_shome, kHOM, #2 key_sic, // key_sic, kIC, #3 key_sleft, // key_sleft, kLFT, #4 key_smessage, // key_smessage, kMSG, %a key_smove, // key_smove, kMOV, %b key_snext, // key_snext, kNXT, %c key_soptions, // key_soptions, kOPT, %d key_sprevious, // key_sprevious, kPRV, %e key_sprint, // key_sprint, kPRT, %f key_sr, // key_sr, kri, kR key_sredo, // key_sredo, kRDO, %g key_sreplace, // key_sreplace, kRPL, %h key_sright, // key_sright, kRIT, %i key_srsume, // key_srsume, kRES, %j key_ssave, // key_ssave, kSAV, !1 key_ssuspend, // key_ssuspend, kSPD, !2 key_stab, // key_stab, khts, kT key_sundo, // key_sundo, kUND, !3 key_suspend, // key_suspend, kspd, &7 key_undo, // key_undo, kund, &8 key_up, // key_up, kcuu1, ku keypad_local, // keypad_local, rmkx, ke keypad_xmit, // keypad_xmit, smkx, ks lab_f0, // lab_f0, lf0, l0 lab_f1, // lab_f1, lf1, l1 lab_f10, // lab_f10, lf10, la lab_f2, // lab_f2, lf2, l2 lab_f3, // lab_f3, lf3, l3 lab_f4, // lab_f4, lf4, l4 lab_f5, // lab_f5, lf5, l5 lab_f6, // lab_f6, lf6, l6 lab_f7, // lab_f7, lf7, l7 lab_f8, // lab_f8, lf8, l8 lab_f9, // lab_f9, lf9, l9 label_format, // label_format, fln, Lf label_off, // label_off, rmln, LF label_on, // label_on, smln, LO meta_off, // meta_off, rmm, mo meta_on, // meta_on, smm, mm micro_column_address, // micro_column_address, mhpa, ZY micro_down, // micro_down, mcud1, ZZ micro_left, // micro_left, mcub1, Za micro_right, // micro_right, mcuf1, Zb micro_row_address, // micro_row_address, mvpa, Zc micro_up, // micro_up, mcuu1, Zd newline, // newline, nel, nw order_of_pins, // order_of_pins, porder, Ze orig_colors, // orig_colors, oc, oc orig_pair, // orig_pair, op, op pad_char, // pad_char, pad, pc parm_dch, // parm_dch, dch, DC parm_delete_line, // parm_delete_line, dl, DL parm_down_cursor, // parm_down_cursor, cud, DO parm_down_micro, // parm_down_micro, mcud, Zf parm_ich, // parm_ich, ich, IC parm_index, // parm_index, indn, SF parm_insert_line, // parm_insert_line, il, AL parm_left_cursor, // parm_left_cursor, cub, LE parm_left_micro, // parm_left_micro, mcub, Zg parm_right_cursor, // parm_right_cursor, cuf, RI parm_right_micro, // parm_right_micro, mcuf, Zh parm_rindex, // parm_rindex, rin, SR parm_up_cursor, // parm_up_cursor, cuu, UP parm_up_micro, // parm_up_micro, mcuu, Zi pkey_key, // pkey_key, pfkey, pk pkey_local, // pkey_local, pfloc, pl pkey_xmit, // pkey_xmit, pfx, px plab_norm, // plab_norm, pln, pn print_screen, // print_screen, mc0, ps prtr_non, // prtr_non, mc5p, pO prtr_off, // prtr_off, mc4, pf prtr_on, // prtr_on, mc5, po pulse, // pulse, pulse, PU quick_dial, // quick_dial, qdial, QD remove_clock, // remove_clock, rmclk, RC repeat_char, // repeat_char, rep, rp req_for_input, // req_for_input, rfi, RF reset_1string, // reset_1string, rs1, r1 reset_2string, // reset_2string, rs2, r2 reset_3string, // reset_3string, rs3, r3 reset_file, // reset_file, rf, rf restore_cursor, // restore_cursor, rc, rc row_address, // row_address, vpa, cv save_cursor, // save_cursor, sc, sc scroll_forward, // scroll_forward, ind, sf scroll_reverse, // scroll_reverse, ri, sr select_char_set, // select_char_set, scs, Zj set_attributes, // set_attributes, sgr, sa set_background, // set_background, setb, Sb set_bottom_margin, // set_bottom_margin, smgb, Zk set_bottom_margin_parm, // set_bottom_margin_parm, smgbp, Zl set_clock, // set_clock, sclk, SC set_color_pair, // set_color_pair, scp, sp set_foreground, // set_foreground, setf, Sf set_left_margin, // set_left_margin, smgl, ML set_left_margin_parm, // set_left_margin_parm, smglp, Zm set_right_margin, // set_right_margin, smgr, MR set_right_margin_parm, // set_right_margin_parm, smgrp, Zn set_tab, // set_tab, hts, st set_top_margin, // set_top_margin, smgt, Zo set_top_margin_parm, // set_top_margin_parm, smgtp, Zp set_window, // set_window, wind, wi start_bit_image, // start_bit_image, sbim, Zq start_char_set_def, // start_char_set_def, scsd, Zr stop_bit_image, // stop_bit_image, rbim, Zs stop_char_set_def, // stop_char_set_def, rcsd, Zt subscript_characters, // subscript_characters, subcs, Zu superscript_characters, // superscript_characters, supcs, Zv tab, // tab, ht, ta these_cause_cr, // these_cause_cr, docr, Zw to_status_line, // to_status_line, tsl, ts tone, // tone, tone, TO underline_char, // underline_char, uc, uc up_half_line, // up_half_line, hu, hu user0, // user0, u0, u0 user1, // user1, u1, u1 user2, // user2, u2, u2 user3, // user3, u3, u3 user4, // user4, u4, u4 user5, // user5, u5, u5 user6, // user6, u6, u6 user7, // user7, u7, u7 user8, // user8, u8, u8 user9, // user9, u9, u9 wait_tone, // wait_tone, wait, WA xoff_character, // xoff_character, xoffc, XF xon_character, // xon_character, xonc, XN zero_motion, // zero_motion, zerom, Zx alt_scancode_esc, // alt_scancode_esc, scesa, S8 bit_image_carriage_return, // bit_image_carriage_return, bicr, Yv bit_image_newline, // bit_image_newline, binel, Zz bit_image_repeat, // bit_image_repeat, birep, Xy char_set_names, // char_set_names, csnm, Zy code_set_init, // code_set_init, csin, ci color_names, // color_names, colornm, Yw define_bit_image_region, // define_bit_image_region, defbi, Yx device_type, // device_type, devt, dv display_pc_char, // display_pc_char, dispc, S1 end_bit_image_region, // end_bit_image_region, endbi, Yy enter_pc_charset_mode, // enter_pc_charset_mode, smpch, S2 enter_scancode_mode, // enter_scancode_mode, smsc, S4 exit_pc_charset_mode, // exit_pc_charset_mode, rmpch, S3 exit_scancode_mode, // exit_scancode_mode, rmsc, S5 get_mouse, // get_mouse, getm, Gm key_mouse, // key_mouse, kmous, Km mouse_info, // mouse_info, minfo, Mi pc_term_options, // pc_term_options, pctrm, S6 pkey_plab, // pkey_plab, pfxl, xl req_mouse_pos, // req_mouse_pos, reqmp, RQ scancode_escape, // scancode_escape, scesc, S7 set0_des_seq, // set0_des_seq, s0ds, s0 set1_des_seq, // set1_des_seq, s1ds, s1 set2_des_seq, // set2_des_seq, s2ds, s2 set3_des_seq, // set3_des_seq, s3ds, s3 set_a_background, // set_a_background, setab, AB set_a_foreground, // set_a_foreground, setaf, AF set_color_band, // set_color_band, setcolor, Yz set_lr_margin, // set_lr_margin, smglr, ML set_page_length, // set_page_length, slines, YZ set_tb_margin, // set_tb_margin, smgtb, MT enter_horizontal_hl_mode, // enter_horizontal_hl_mode, ehhlm, Xh enter_left_hl_mode, // enter_left_hl_mode, elhlm, Xl enter_low_hl_mode, // enter_low_hl_mode, elohlm, Xo enter_right_hl_mode, // enter_right_hl_mode, erhlm, Xr enter_top_hl_mode, // enter_top_hl_mode, ethlm, Xt enter_vertical_hl_mode, // enter_vertical_hl_mode, evhlm, Xv set_a_attributes, // set_a_attributes, sgr1, sA set_pglen_inch, // set_pglen_inch, slength, sL) ; public String[] getNames() { return getCapabilitiesByName().entrySet().stream() .filter(e -> e.getValue() == this) .map(Map.Entry::getValue) .toArray(String[]::new); } public static Capability byName(String name) { return getCapabilitiesByName().get(name); } } public static Map getCapabilitiesByName() { Map capabilities = new LinkedHashMap<>(); try (InputStream is = InfoCmp.class.getResourceAsStream("capabilities.txt"); BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { br.lines().map(String::trim) .filter(s -> !s.startsWith("#")) .filter(s -> !s.isEmpty()) .forEach(s -> { String[] names = s.split(", "); Capability cap = Enum.valueOf(Capability.class, names[0]); for (String name : names) { capabilities.put(name, cap); } }); return capabilities; } catch (IOException e) { throw new IOError(e); } } public static void setDefaultInfoCmp(String terminal, String caps) { CAPS.putIfAbsent(terminal, caps); } public static void setDefaultInfoCmp(String terminal, Supplier caps) { CAPS.putIfAbsent(terminal, caps); } public static String getInfoCmp( String terminal ) throws IOException, InterruptedException { String caps = getLoadedInfoCmp(terminal); if (caps == null) { Process p = new ProcessBuilder(OSUtils.INFOCMP_COMMAND, terminal).start(); caps = ExecHelper.waitAndCapture(p); CAPS.put(terminal, caps); } return caps; } public static String getLoadedInfoCmp(String terminal) { Object caps = CAPS.get(terminal); if (caps instanceof Supplier) { caps = ((Supplier) caps).get(); } return (String) caps; } public static void parseInfoCmp( String capabilities, Set bools, Map ints, Map strings ) { Map capsByName = getCapabilitiesByName(); String[] lines = capabilities.split("\n"); for (int i = 1; i < lines.length; i++) { Matcher m = Pattern.compile("\\s*(([^,]|\\\\,)+)\\s*[,$]").matcher(lines[i]); while (m.find()) { String cap = m.group(1); if (cap.contains("#")) { int index = cap.indexOf('#'); String key = cap.substring(0, index); String val = cap.substring(index + 1); int iVal; if (val.startsWith("0x")) { iVal = Integer.parseInt(val.substring(2), 16); } else { iVal = Integer.parseInt(val); } Capability c = capsByName.get(key); if (c != null) { ints.put(c, iVal); } } else if (cap.contains("=")) { int index = cap.indexOf('='); String key = cap.substring(0, index); String val = cap.substring(index + 1); Capability c = capsByName.get(key); if (c != null) { strings.put(c, val); } } else { Capability c = capsByName.get(cap); if (c != null) { bools.add(c); } } } } } static String loadDefaultInfoCmp(String name) { try (InputStream is = InfoCmp.class.getResourceAsStream(name + ".caps"); BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { return br.lines().collect(Collectors.joining("\n", "", "\n")); } catch (IOException e) { throw new IOError(e); } } static { for (String s : Arrays.asList("dumb", "ansi", "xterm", "xterm-256color", "windows", "screen", "screen-256color")) { setDefaultInfoCmp(s, () -> loadDefaultInfoCmp(s)); } } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/InputStreamReader.java000066400000000000000000000301211311544710100275330ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; import java.nio.charset.MalformedInputException; import java.nio.charset.UnmappableCharacterException; /** * * NOTE for JLine: the default InputStreamReader that comes from the JRE * usually read more bytes than needed from the input stream, which * is not usable in a character per character model used in the terminal. * We thus use the harmony code which only reads the minimal number of bytes. */ /** * A class for turning a byte stream into a character stream. Data read from the * source input stream is converted into characters by either a default or a * provided character converter. The default encoding is taken from the * "file.encoding" system property. {@code InputStreamReader} contains a buffer * of bytes read from the source stream and converts these into characters as * needed. The buffer size is 8K. * * @see OutputStreamWriter */ public class InputStreamReader extends Reader { private InputStream in; private static final int BUFFER_SIZE = 4; private boolean endOfInput = false; CharsetDecoder decoder; ByteBuffer bytes = ByteBuffer.allocate(BUFFER_SIZE); char pending = (char) -1; /** * Constructs a new {@code InputStreamReader} on the {@link InputStream} * {@code in}. This constructor sets the character converter to the encoding * specified in the "file.encoding" property and falls back to ISO 8859_1 * (ISO-Latin-1) if the property doesn't exist. * * @param in * the input stream from which to read characters. */ public InputStreamReader(InputStream in) { super(in); this.in = in; decoder = Charset.defaultCharset().newDecoder().onMalformedInput( CodingErrorAction.REPLACE).onUnmappableCharacter( CodingErrorAction.REPLACE); bytes.limit(0); } /** * Constructs a new InputStreamReader on the InputStream {@code in}. The * character converter that is used to decode bytes into characters is * identified by name by {@code enc}. If the encoding cannot be found, an * UnsupportedEncodingException error is thrown. * * @param in * the InputStream from which to read characters. * @param enc * identifies the character converter to use. * @throws NullPointerException * if {@code enc} is {@code null}. * @throws UnsupportedEncodingException * if the encoding specified by {@code enc} cannot be found. */ public InputStreamReader(InputStream in, final String enc) throws UnsupportedEncodingException { super(in); if (enc == null) { throw new NullPointerException(); } this.in = in; try { decoder = Charset.forName(enc).newDecoder().onMalformedInput( CodingErrorAction.REPLACE).onUnmappableCharacter( CodingErrorAction.REPLACE); } catch (IllegalArgumentException e) { throw (UnsupportedEncodingException) new UnsupportedEncodingException(enc).initCause(e); } bytes.limit(0); } /** * Constructs a new InputStreamReader on the InputStream {@code in} and * CharsetDecoder {@code dec}. * * @param in * the source InputStream from which to read characters. * @param dec * the CharsetDecoder used by the character conversion. */ public InputStreamReader(InputStream in, CharsetDecoder dec) { super(in); dec.averageCharsPerByte(); this.in = in; decoder = dec; bytes.limit(0); } /** * Constructs a new InputStreamReader on the InputStream {@code in} and * Charset {@code charset}. * * @param in * the source InputStream from which to read characters. * @param charset * the Charset that defines the character converter */ public InputStreamReader(InputStream in, Charset charset) { super(in); this.in = in; decoder = charset.newDecoder().onMalformedInput( CodingErrorAction.REPLACE).onUnmappableCharacter( CodingErrorAction.REPLACE); bytes.limit(0); } /** * Closes this reader. This implementation closes the source InputStream and * releases all local storage. * * @throws IOException * if an error occurs attempting to close this reader. */ @Override public void close() throws IOException { synchronized (lock) { decoder = null; if (in != null) { in.close(); in = null; } } } /** * Returns the name of the encoding used to convert bytes into characters. * The value {@code null} is returned if this reader has been closed. * * @return the name of the character converter or {@code null} if this * reader is closed. */ public String getEncoding() { if (!isOpen()) { return null; } return decoder.charset().name(); } /** * Reads a single character from this reader and returns it as an integer * with the two higher-order bytes set to 0. Returns -1 if the end of the * reader has been reached. The byte value is either obtained from * converting bytes in this reader's buffer or by first filling the buffer * from the source InputStream and then reading from the buffer. * * @return the character read or -1 if the end of the reader has been * reached. * @throws IOException * if this reader is closed or some other I/O error occurs. */ @Override public int read() throws IOException { synchronized (lock) { if (!isOpen()) { throw new ClosedException("InputStreamReader is closed."); } if (pending != (char) -1) { char c = pending; pending = (char) -1; return c; } char buf[] = new char[2]; int nb = read(buf, 0, 2); if (nb == 2) { pending = buf[1]; } if (nb > 0) { return buf[0]; } else { return -1; } } } /** * Reads at most {@code length} characters from this reader and stores them * at position {@code offset} in the character array {@code buf}. Returns * the number of characters actually read or -1 if the end of the reader has * been reached. The bytes are either obtained from converting bytes in this * reader's buffer or by first filling the buffer from the source * InputStream and then reading from the buffer. * * @param buf * the array to store the characters read. * @param offset * the initial position in {@code buf} to store the characters * read from this reader. * @param length * the maximum number of characters to read. * @return the number of characters read or -1 if the end of the reader has * been reached. * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code length < 0}, or if * {@code offset + length} is greater than the length of * {@code buf}. * @throws IOException * if this reader is closed or some other I/O error occurs. */ @Override public int read(char[] buf, int offset, int length) throws IOException { synchronized (lock) { if (!isOpen()) { throw new IOException("InputStreamReader is closed."); } if (offset < 0 || offset > buf.length - length || length < 0) { throw new IndexOutOfBoundsException(); } if (length == 0) { return 0; } CharBuffer out = CharBuffer.wrap(buf, offset, length); CoderResult result = CoderResult.UNDERFLOW; // bytes.remaining() indicates number of bytes in buffer // when 1-st time entered, it'll be equal to zero boolean needInput = !bytes.hasRemaining(); while (out.position() == offset) { // fill the buffer if needed if (needInput) { try { if ((in.available() == 0) && (out.position() > offset)) { // we could return the result without blocking read break; } } catch (IOException e) { // available didn't work so just try the read } int off = bytes.arrayOffset() + bytes.limit(); int was_red = in.read(bytes.array(), off, 1); if (was_red == -1) { endOfInput = true; break; } else if (was_red == 0) { break; } bytes.limit(bytes.limit() + was_red); } // decode bytes result = decoder.decode(bytes, out, false); if (result.isUnderflow()) { // compact the buffer if no space left if (bytes.limit() == bytes.capacity()) { bytes.compact(); bytes.limit(bytes.position()); bytes.position(0); } needInput = true; } else { break; } } if (result == CoderResult.UNDERFLOW && endOfInput) { result = decoder.decode(bytes, out, true); decoder.flush(out); decoder.reset(); } if (result.isMalformed()) { throw new MalformedInputException(result.length()); } else if (result.isUnmappable()) { throw new UnmappableCharacterException(result.length()); } return out.position() - offset == 0 ? -1 : out.position() - offset; } } /* * Answer a boolean indicating whether or not this InputStreamReader is * open. */ private boolean isOpen() { return in != null; } /** * Indicates whether this reader is ready to be read without blocking. If * the result is {@code true}, the next {@code read()} will not block. If * the result is {@code false} then this reader may or may not block when * {@code read()} is called. This implementation returns {@code true} if * there are bytes available in the buffer or the source stream has bytes * available. * * @return {@code true} if the receiver will not block when {@code read()} * is called, {@code false} if unknown or blocking will occur. * @throws IOException * if this reader is closed or some other I/O error occurs. */ @Override public boolean ready() throws IOException { synchronized (lock) { if (in == null) { throw new IOException("InputStreamReader is closed."); } try { return bytes.hasRemaining() || in.available() > 0; } catch (IOException e) { return false; } } } }jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/Levenshtein.java000066400000000000000000000122601311544710100264250ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.util.HashMap; import java.util.Map; /** * The Damerau-Levenshtein Algorithm is an extension to the Levenshtein * Algorithm which solves the edit distance problem between a source string and * a target string with the following operations: * *
    *
  • Character Insertion
  • *
  • Character Deletion
  • *
  • Character Replacement
  • *
  • Adjacent Character Swap
  • *
* * Note that the adjacent character swap operation is an edit that may be * applied when two adjacent characters in the source string match two adjacent * characters in the target string, but in reverse order, rather than a general * allowance for adjacent character swaps. *

* * This implementation allows the client to specify the costs of the various * edit operations with the restriction that the cost of two swap operations * must not be less than the cost of a delete operation followed by an insert * operation. This restriction is required to preclude two swaps involving the * same character being required for optimality which, in turn, enables a fast * dynamic programming solution. *

* * The running time of the Damerau-Levenshtein algorithm is O(n*m) where n is * the length of the source string and m is the length of the target string. * This implementation consumes O(n*m) space. * * @author Kevin L. Stern */ public class Levenshtein { public static int distance(CharSequence lhs, CharSequence rhs) { return distance(lhs, rhs, 1, 1, 1, 1); } public static int distance(CharSequence source, CharSequence target, int deleteCost, int insertCost, int replaceCost, int swapCost) { /* * Required to facilitate the premise to the algorithm that two swaps of the * same character are never required for optimality. */ if (2 * swapCost < insertCost + deleteCost) { throw new IllegalArgumentException("Unsupported cost assignment"); } if (source.length() == 0) { return target.length() * insertCost; } if (target.length() == 0) { return source.length() * deleteCost; } int[][] table = new int[source.length()][target.length()]; Map sourceIndexByCharacter = new HashMap<>(); if (source.charAt(0) != target.charAt(0)) { table[0][0] = Math.min(replaceCost, deleteCost + insertCost); } sourceIndexByCharacter.put(source.charAt(0), 0); for (int i = 1; i < source.length(); i++) { int deleteDistance = table[i - 1][0] + deleteCost; int insertDistance = (i + 1) * deleteCost + insertCost; int matchDistance = i * deleteCost + (source.charAt(i) == target.charAt(0) ? 0 : replaceCost); table[i][0] = Math.min(Math.min(deleteDistance, insertDistance), matchDistance); } for (int j = 1; j < target.length(); j++) { int deleteDistance = (j + 1) * insertCost + deleteCost; int insertDistance = table[0][j - 1] + insertCost; int matchDistance = j * insertCost + (source.charAt(0) == target.charAt(j) ? 0 : replaceCost); table[0][j] = Math.min(Math.min(deleteDistance, insertDistance), matchDistance); } for (int i = 1; i < source.length(); i++) { int maxSourceLetterMatchIndex = source.charAt(i) == target.charAt(0) ? 0 : -1; for (int j = 1; j < target.length(); j++) { Integer candidateSwapIndex = sourceIndexByCharacter.get(target.charAt(j)); int jSwap = maxSourceLetterMatchIndex; int deleteDistance = table[i - 1][j] + deleteCost; int insertDistance = table[i][j - 1] + insertCost; int matchDistance = table[i - 1][j - 1]; if (source.charAt(i) != target.charAt(j)) { matchDistance += replaceCost; } else { maxSourceLetterMatchIndex = j; } int swapDistance; if (candidateSwapIndex != null && jSwap != -1) { int iSwap = candidateSwapIndex; int preSwapCost; if (iSwap == 0 && jSwap == 0) { preSwapCost = 0; } else { preSwapCost = table[Math.max(0, iSwap - 1)][Math.max(0, jSwap - 1)]; } swapDistance = preSwapCost + (i - iSwap - 1) * deleteCost + (j - jSwap - 1) * insertCost + swapCost; } else { swapDistance = Integer.MAX_VALUE; } table[i][j] = Math.min(Math.min(Math.min(deleteDistance, insertDistance), matchDistance), swapDistance); } sourceIndexByCharacter.put(source.charAt(i), i); } return table[source.length() - 1][target.length() - 1]; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/Log.java000066400000000000000000000072251311544710100246670ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; /** * Internal logger. * * @author Jason Dillon * @author Guillaume Nodet * @since 2.0 */ public final class Log { public static void trace(final Object... messages) { log(Level.FINEST, messages); } public static void trace(Supplier supplier) { log(Level.FINEST, supplier); } public static void debug(Supplier supplier) { log(Level.FINE, supplier); } public static void debug(final Object... messages) { log(Level.FINE, messages); } public static void info(final Object... messages) { log(Level.INFO, messages); } public static void warn(final Object... messages) { log(Level.WARNING, messages); } public static void error(final Object... messages) { log(Level.SEVERE, messages); } public static boolean isDebugEnabled() { return isEnabled(Level.FINE); } /** * Helper to support rendering messages. */ static void render(final PrintStream out, final Object message) { if (message != null && message.getClass().isArray()) { Object[] array = (Object[]) message; out.print("["); for (int i = 0; i < array.length; i++) { out.print(array[i]); if (i + 1 < array.length) { out.print(","); } } out.print("]"); } else { out.print(message); } } static LogRecord createRecord(final Level level, final Object... messages) { Throwable cause = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos); for (int i = 0; i < messages.length; i++) { // Special handling for the last message if its a throwable, render its stack on the next line if (i + 1 == messages.length && messages[i] instanceof Throwable) { cause = (Throwable) messages[i]; } else { render(ps, messages[i]); } } ps.close(); LogRecord r = new LogRecord(level, baos.toString()); r.setThrown(cause); return r; } static LogRecord createRecord(final Level level, final Supplier message) { return new LogRecord(level, message.get()); } static void log(final Level level, final Supplier message) { logr(level, () -> createRecord(level, message)); } static void log(final Level level, final Object... messages) { logr(level, () -> createRecord(level, messages)); } static void logr(final Level level, final Supplier record) { Logger logger = Logger.getLogger("org.jline"); if (logger.isLoggable(level)) { // inform record of the logger-name LogRecord tmp = record.get(); tmp.setLoggerName(logger.getName()); logger.log(tmp); } } static boolean isEnabled(Level level) { Logger logger = Logger.getLogger("org.jline"); return logger.isLoggable(level); } }jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/NonBlockingReader.java000066400000000000000000000224021311544710100274660ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.IOException; import java.io.InterruptedIOException; import java.io.Reader; /** * This class wraps a regular reader and allows it to appear as if it * is non-blocking; that is, reads can be performed against it that timeout * if no data is seen for a period of time. This effect is achieved by having * a separate thread perform all non-blocking read requests and then * waiting on the thread to complete. * *

VERY IMPORTANT NOTES *

    *
  • This class is not thread safe. It expects at most one reader. *
  • The {@link #shutdown()} method must be called in order to shut down * the thread that handles blocking I/O. *
* @since 2.7 * @author Scott C. Gray */ public class NonBlockingReader extends Reader implements Runnable { public static final int READ_EXPIRED = -2; private Reader in; // The actual input stream private int ch = READ_EXPIRED; // Recently read character private String name; private boolean threadIsReading = false; private IOException exception = null; private long threadDelay = 60 * 1000; private Thread thread; /** * Creates a NonBlockingReader out of a normal blocking * reader. Note that this call also spawn a separate thread to perform the * blocking I/O on behalf of the thread that is using this class. The * {@link #shutdown()} method must be called in order to shut this thread down. * @param in The reader to wrap */ public NonBlockingReader(String name, Reader in) { this.in = in; this.name = name; } private synchronized void startReadingThreadIfNeeded() { if (thread == null) { thread = new Thread(this); thread.setName(name + " non blocking reader thread"); thread.setDaemon(true); thread.start(); } } /** * Shuts down the thread that is handling blocking I/O. Note that if the * thread is currently blocked waiting for I/O it will not actually * shut down until the I/O is received. */ public synchronized void shutdown() { if (thread != null) { notify(); } } @Override public void close() throws IOException { /* * The underlying input stream is closed first. This means that if the * I/O thread was blocked waiting on input, it will be woken for us. */ in.close(); shutdown(); } @Override public int read() throws IOException { return read(0L, false); } /** * Peeks to see if there is a byte waiting in the input stream without * actually consuming the byte. * * @param timeout The amount of time to wait, 0 == forever * @return -1 on eof, -2 if the timeout expired with no available input * or the character that was read (without consuming it). */ public int peek(long timeout) throws IOException { return read(timeout, true); } /** * Attempts to read a character from the input stream for a specific * period of time. * @param timeout The amount of time to wait for the character * @return The character read, -1 if EOF is reached, or -2 if the * read timed out. */ public int read(long timeout) throws IOException { return read(timeout, false); } @Override public synchronized boolean ready() throws IOException { return ch >= 0 || in.ready(); } /** * Attempts to read a character from the input stream for a specific * period of time. * @param timeout The amount of time to wait for the character * @return The character read, -1 if EOF is reached, or -2 if the * read timed out. */ private synchronized int read(long timeout, boolean isPeek) throws IOException { /* * If the thread hit an IOException, we report it. */ if (exception != null) { assert ch == READ_EXPIRED; IOException toBeThrown = exception; if (!isPeek) exception = null; throw toBeThrown; } /* * If there was a pending character from the thread, then * we send it. If the timeout is 0L or the thread was shut down * then do a local read. */ if (ch >= -1) { assert exception == null; } else if (!isPeek && timeout <= 0L && !threadIsReading) { ch = in.read(); } else { /* * If the thread isn't reading already, then ask it to do so. */ if (!threadIsReading) { threadIsReading = true; startReadingThreadIfNeeded(); notifyAll(); } boolean isInfinite = (timeout <= 0L); /* * So the thread is currently doing the reading for us. So * now we play the waiting game. */ while (isInfinite || timeout > 0L) { long start = System.currentTimeMillis (); try { wait(timeout); } catch (InterruptedException e) { exception = (IOException) new InterruptedIOException().initCause(e); } if (exception != null) { assert ch == READ_EXPIRED; IOException toBeThrown = exception; if (!isPeek) exception = null; throw toBeThrown; } if (ch >= -1) { assert exception == null; break; } if (!isInfinite) { timeout -= System.currentTimeMillis() - start; } } } /* * ch is the character that was just read. Either we set it because * a local read was performed or the read thread set it (or failed to * change it). We will return it's value, but if this was a peek * operation, then we leave it in place. */ int ret = ch; if (!isPeek) { ch = READ_EXPIRED; } return ret; } /** * This version of read() is very specific to jline's purposes, it * will always always return a single byte at a time, rather than filling * the entire buffer. */ @Override public int read (char[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = this.read(0L); if (c == -1) { return -1; } b[off] = (char)c; return 1; } //@Override public void run () { Log.debug("NonBlockingReader start"); boolean needToRead; try { while (true) { /* * Synchronize to grab variables accessed by both this thread * and the accessing thread. */ synchronized (this) { needToRead = this.threadIsReading; try { /* * Nothing to do? Then wait. */ if (!needToRead) { wait(threadDelay); } } catch (InterruptedException e) { /* IGNORED */ } needToRead = this.threadIsReading; if (!needToRead) { return; } } /* * We're not shutting down, but we need to read. This cannot * happen while we are holding the lock (which we aren't now). */ int charRead = READ_EXPIRED; IOException failure = null; try { charRead = in.read(); } catch (IOException e) { failure = e; } /* * Re-grab the lock to update the state. */ synchronized (this) { exception = failure; ch = charRead; threadIsReading = false; notify(); } } } catch (Throwable t) { Log.warn("Error in NonBlockingReader thread", t); } finally { Log.debug("NonBlockingReader shutdown"); synchronized (this) { thread = null; } } } public synchronized void clear() throws IOException { while (ready()) { read(); } } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/OSUtils.java000066400000000000000000000045751311544710100255150ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.File; public class OSUtils { public static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("win"); public static final boolean IS_CYGWIN = IS_WINDOWS && System.getenv("PWD") != null && System.getenv("PWD").startsWith("/") && !"cygwin".equals(System.getenv("TERM")); public static final boolean IS_MINGW = IS_WINDOWS && System.getenv("MSYSTEM") != null && System.getenv("MSYSTEM").startsWith("MINGW"); public static final boolean IS_OSX = System.getProperty("os.name").toLowerCase().contains("mac"); public static String TTY_COMMAND; public static String STTY_COMMAND; public static String STTY_F_OPTION; public static String INFOCMP_COMMAND; static { String tty; String stty; String sttyfopt; String infocmp; if (OSUtils.IS_CYGWIN || OSUtils.IS_MINGW) { tty = "tty.exe"; stty = "stty.exe"; sttyfopt = null; infocmp = "infocmp.exe"; String path = System.getenv("PATH"); if (path != null) { String[] paths = path.split(";"); for (String p : paths) { if (new File(p, "tty.exe").exists()) { tty = new File(p, "tty.exe").getAbsolutePath(); } if (new File(p, "stty.exe").exists()) { stty = new File(p, "stty.exe").getAbsolutePath(); } if (new File(p, "infocmp.exe").exists()) { infocmp = new File(p, "infocmp.exe").getAbsolutePath(); } } } } else { tty = "tty"; stty = "stty"; infocmp = "infocmp"; if (IS_OSX) { sttyfopt = "-f"; } else { sttyfopt = "-F"; } } TTY_COMMAND = tty; STTY_COMMAND = stty; STTY_F_OPTION = sttyfopt; INFOCMP_COMMAND = infocmp; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/ShutdownHooks.java000066400000000000000000000060601311544710100267610ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * Manages the JLine shutdown-hook thread and tasks to execute on shutdown. * * @author Jason Dillon * @since 2.7 */ public final class ShutdownHooks { private static final List tasks = new ArrayList<>(); private static Thread hook; public static synchronized T add(final T task) { Objects.requireNonNull(task); // Install the hook thread if needed if (hook == null) { hook = addHook(new Thread("JLine Shutdown Hook") { @Override public void run() { runTasks(); } }); } // Track the task Log.debug("Adding shutdown-hook task: ", task); tasks.add(task); return task; } private static synchronized void runTasks() { Log.debug("Running all shutdown-hook tasks"); // Iterate through copy of tasks list for (Task task : tasks.toArray(new Task[tasks.size()])) { Log.debug("Running task: ", task); try { task.run(); } catch (Throwable e) { Log.warn("Task failed", e); } } tasks.clear(); } private static Thread addHook(final Thread thread) { Log.debug("Registering shutdown-hook: ", thread); try { Runtime.getRuntime().addShutdownHook(thread); } catch (AbstractMethodError e) { // JDK 1.3+ only method. Bummer. Log.debug("Failed to register shutdown-hook", e); } return thread; } public static synchronized void remove(final Task task) { Objects.requireNonNull(task); // ignore if hook never installed if (hook == null) { return; } // Drop the task tasks.remove(task); // If there are no more tasks, then remove the hook thread if (tasks.isEmpty()) { removeHook(hook); hook = null; } } private static void removeHook(final Thread thread) { Log.debug("Removing shutdown-hook: ", thread); try { Runtime.getRuntime().removeShutdownHook(thread); } catch (AbstractMethodError e) { // JDK 1.3+ only method. Bummer. Log.debug("Failed to remove shutdown-hook", e); } catch (IllegalStateException e) { // The VM is shutting down, not a big deal; ignore } } /** * Essentially a {@link Runnable} which allows running to throw an exception. */ public interface Task { void run() throws Exception; } }jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/Signals.java000066400000000000000000000106731311544710100255470ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.lang.reflect.Proxy; import java.util.Objects; /** * Signals helpers. * * @author Guillaume Nodet * @since 3.0 */ public final class Signals { private Signals() { } /** * * @param name the signal, CONT, STOP, etc... * @param handler the callback to run * * @return an object that needs to be passed to the {@link #unregister(String, Object)} * method to unregister the handler */ public static Object register(String name, Runnable handler) { Objects.requireNonNull(handler); return register(name, handler, handler.getClass().getClassLoader()); } public static Object register(String name, final Runnable handler, ClassLoader loader) { try { Class signalHandlerClass = Class.forName("sun.misc.SignalHandler"); // Implement signal handler Object signalHandler = Proxy.newProxyInstance(loader, new Class[]{signalHandlerClass}, (proxy, method, args) -> { // only method we are proxying is handle() if (method.getDeclaringClass() == Object.class) { if ("toString".equals(method.getName())) { return handler.toString(); } } else if (method.getDeclaringClass() == signalHandlerClass) { Log.trace(() -> "Calling handler " + toString(handler) + " for signal " + name); handler.run(); } return null; }); return doRegister(name, signalHandler); } catch (Exception e) { // Ignore this one too, if the above failed, the signal API is incompatible with what we're expecting Log.debug("Error registering handler for signal ", name, e); return null; } } public static Object registerDefault(String name) { try { Class signalHandlerClass = Class.forName("sun.misc.SignalHandler"); return doRegister(name, signalHandlerClass.getField("SIG_DFL").get(null)); } catch (Exception e) { // Ignore this one too, if the above failed, the signal API is incompatible with what we're expecting Log.debug("Error registering default handler for signal ", name, e); return null; } } public static void unregister(String name, Object previous) { try { // We should make sure the current signal is the one we registered if (previous != null) { doRegister(name, previous); } } catch (Exception e) { // Ignore Log.debug("Error unregistering handler for signal ", name, e); } } private static Object doRegister(String name, Object handler) throws Exception { Log.trace(() -> "Registering signal " + name + " with handler " + toString(handler)); if ("QUIT".equals(name) || "INFO".equals(name) && "9".equals(System.getProperty("java.specification.version"))) { Log.trace(() -> "Ignoring unsupported signal " + name); return null; } Class signalClass = Class.forName("sun.misc.Signal"); Class signalHandlerClass = Class.forName("sun.misc.SignalHandler"); Object signal = signalClass.getConstructor(String.class).newInstance(name); return signalClass.getMethod("handle", signalClass, signalHandlerClass) .invoke(null, signal, handler); } @SuppressWarnings("") private static String toString(Object handler) { try { Class signalHandlerClass = Class.forName("sun.misc.SignalHandler"); if (handler == signalHandlerClass.getField("SIG_DFL").get(null)) { return "SIG_DFL"; } if (handler == signalHandlerClass.getField("SIG_IGN").get(null)) { return "SIG_IGN"; } } catch (Throwable t) { // ignore } return handler != null ? handler.toString() : "null"; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/WCWidth.java000066400000000000000000000221171311544710100254540ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; public final class WCWidth { private WCWidth() { } /* The following two functions define the column width of an ISO 10646 * character as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return * value of -1. * * - Non-spacing and enclosing combining characters (general * category code Mn or Me in the Unicode database) have a * column width of 0. * * - SOFT HYPHEN (U+00AD) has a column width of 1. * * - Other format characters (general category code Cf in the Unicode * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) * have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian * Full-width (F) category as defined in Unicode Technical * Report #11 have a column width of 2. * * - All remaining characters (including all printable * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ public static int wcwidth(int ucs) { /* test for 8-bit control characters */ if (ucs == 0) return 0; if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; /* binary search in table of non-spacing characters */ if (bisearch(ucs, combining, combining.length - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ return 1 + ((ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ ucs == 0x2329 || ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd))) ? 1 : 0); } /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static Interval[] combining = { new Interval( 0x0300, 0x036F ), new Interval( 0x0483, 0x0486 ), new Interval( 0x0488, 0x0489 ), new Interval( 0x0591, 0x05BD ), new Interval( 0x05BF, 0x05BF ), new Interval( 0x05C1, 0x05C2 ), new Interval( 0x05C4, 0x05C5 ), new Interval( 0x05C7, 0x05C7 ), new Interval( 0x0600, 0x0603 ), new Interval( 0x0610, 0x0615 ), new Interval( 0x064B, 0x065E ), new Interval( 0x0670, 0x0670 ), new Interval( 0x06D6, 0x06E4 ), new Interval( 0x06E7, 0x06E8 ), new Interval( 0x06EA, 0x06ED ), new Interval( 0x070F, 0x070F ), new Interval( 0x0711, 0x0711 ), new Interval( 0x0730, 0x074A ), new Interval( 0x07A6, 0x07B0 ), new Interval( 0x07EB, 0x07F3 ), new Interval( 0x0901, 0x0902 ), new Interval( 0x093C, 0x093C ), new Interval( 0x0941, 0x0948 ), new Interval( 0x094D, 0x094D ), new Interval( 0x0951, 0x0954 ), new Interval( 0x0962, 0x0963 ), new Interval( 0x0981, 0x0981 ), new Interval( 0x09BC, 0x09BC ), new Interval( 0x09C1, 0x09C4 ), new Interval( 0x09CD, 0x09CD ), new Interval( 0x09E2, 0x09E3 ), new Interval( 0x0A01, 0x0A02 ), new Interval( 0x0A3C, 0x0A3C ), new Interval( 0x0A41, 0x0A42 ), new Interval( 0x0A47, 0x0A48 ), new Interval( 0x0A4B, 0x0A4D ), new Interval( 0x0A70, 0x0A71 ), new Interval( 0x0A81, 0x0A82 ), new Interval( 0x0ABC, 0x0ABC ), new Interval( 0x0AC1, 0x0AC5 ), new Interval( 0x0AC7, 0x0AC8 ), new Interval( 0x0ACD, 0x0ACD ), new Interval( 0x0AE2, 0x0AE3 ), new Interval( 0x0B01, 0x0B01 ), new Interval( 0x0B3C, 0x0B3C ), new Interval( 0x0B3F, 0x0B3F ), new Interval( 0x0B41, 0x0B43 ), new Interval( 0x0B4D, 0x0B4D ), new Interval( 0x0B56, 0x0B56 ), new Interval( 0x0B82, 0x0B82 ), new Interval( 0x0BC0, 0x0BC0 ), new Interval( 0x0BCD, 0x0BCD ), new Interval( 0x0C3E, 0x0C40 ), new Interval( 0x0C46, 0x0C48 ), new Interval( 0x0C4A, 0x0C4D ), new Interval( 0x0C55, 0x0C56 ), new Interval( 0x0CBC, 0x0CBC ), new Interval( 0x0CBF, 0x0CBF ), new Interval( 0x0CC6, 0x0CC6 ), new Interval( 0x0CCC, 0x0CCD ), new Interval( 0x0CE2, 0x0CE3 ), new Interval( 0x0D41, 0x0D43 ), new Interval( 0x0D4D, 0x0D4D ), new Interval( 0x0DCA, 0x0DCA ), new Interval( 0x0DD2, 0x0DD4 ), new Interval( 0x0DD6, 0x0DD6 ), new Interval( 0x0E31, 0x0E31 ), new Interval( 0x0E34, 0x0E3A ), new Interval( 0x0E47, 0x0E4E ), new Interval( 0x0EB1, 0x0EB1 ), new Interval( 0x0EB4, 0x0EB9 ), new Interval( 0x0EBB, 0x0EBC ), new Interval( 0x0EC8, 0x0ECD ), new Interval( 0x0F18, 0x0F19 ), new Interval( 0x0F35, 0x0F35 ), new Interval( 0x0F37, 0x0F37 ), new Interval( 0x0F39, 0x0F39 ), new Interval( 0x0F71, 0x0F7E ), new Interval( 0x0F80, 0x0F84 ), new Interval( 0x0F86, 0x0F87 ), new Interval( 0x0F90, 0x0F97 ), new Interval( 0x0F99, 0x0FBC ), new Interval( 0x0FC6, 0x0FC6 ), new Interval( 0x102D, 0x1030 ), new Interval( 0x1032, 0x1032 ), new Interval( 0x1036, 0x1037 ), new Interval( 0x1039, 0x1039 ), new Interval( 0x1058, 0x1059 ), new Interval( 0x1160, 0x11FF ), new Interval( 0x135F, 0x135F ), new Interval( 0x1712, 0x1714 ), new Interval( 0x1732, 0x1734 ), new Interval( 0x1752, 0x1753 ), new Interval( 0x1772, 0x1773 ), new Interval( 0x17B4, 0x17B5 ), new Interval( 0x17B7, 0x17BD ), new Interval( 0x17C6, 0x17C6 ), new Interval( 0x17C9, 0x17D3 ), new Interval( 0x17DD, 0x17DD ), new Interval( 0x180B, 0x180D ), new Interval( 0x18A9, 0x18A9 ), new Interval( 0x1920, 0x1922 ), new Interval( 0x1927, 0x1928 ), new Interval( 0x1932, 0x1932 ), new Interval( 0x1939, 0x193B ), new Interval( 0x1A17, 0x1A18 ), new Interval( 0x1B00, 0x1B03 ), new Interval( 0x1B34, 0x1B34 ), new Interval( 0x1B36, 0x1B3A ), new Interval( 0x1B3C, 0x1B3C ), new Interval( 0x1B42, 0x1B42 ), new Interval( 0x1B6B, 0x1B73 ), new Interval( 0x1DC0, 0x1DCA ), new Interval( 0x1DFE, 0x1DFF ), new Interval( 0x200B, 0x200F ), new Interval( 0x202A, 0x202E ), new Interval( 0x2060, 0x2063 ), new Interval( 0x206A, 0x206F ), new Interval( 0x20D0, 0x20EF ), new Interval( 0x302A, 0x302F ), new Interval( 0x3099, 0x309A ), new Interval( 0xA806, 0xA806 ), new Interval( 0xA80B, 0xA80B ), new Interval( 0xA825, 0xA826 ), new Interval( 0xFB1E, 0xFB1E ), new Interval( 0xFE00, 0xFE0F ), new Interval( 0xFE20, 0xFE23 ), new Interval( 0xFEFF, 0xFEFF ), new Interval( 0xFFF9, 0xFFFB ), new Interval( 0x10A01, 0x10A03 ), new Interval( 0x10A05, 0x10A06 ), new Interval( 0x10A0C, 0x10A0F ), new Interval( 0x10A38, 0x10A3A ), new Interval( 0x10A3F, 0x10A3F ), new Interval( 0x1D167, 0x1D169 ), new Interval( 0x1D173, 0x1D182 ), new Interval( 0x1D185, 0x1D18B ), new Interval( 0x1D1AA, 0x1D1AD ), new Interval( 0x1D242, 0x1D244 ), new Interval( 0xE0001, 0xE0001 ), new Interval( 0xE0020, 0xE007F ), new Interval( 0xE0100, 0xE01EF ) }; private static class Interval { public final int first; public final int last; public Interval(int first, int last) { this.first = first; this.last = last; } } /* auxiliary function for binary search in interval table */ private static boolean bisearch(int ucs, Interval[] table, int max) { int min = 0; int mid; if (ucs < table[0].first || ucs > table[max].last) return false; while (max >= min) { mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; else if (ucs < table[mid].first) max = mid - 1; else return true; } return false; } } jline3-jline-3.3.1/terminal/src/main/java/org/jline/utils/package-info.java000066400000000000000000000005131311544710100264630ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ /** * JLine 3. * * @since 3.0 */ package org.jline.utils;jline3-jline-3.3.1/terminal/src/main/resources/000077500000000000000000000000001311544710100213365ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/resources/org/000077500000000000000000000000001311544710100221255ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/resources/org/jline/000077500000000000000000000000001311544710100232265ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/resources/org/jline/utils/000077500000000000000000000000001311544710100243665ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/main/resources/org/jline/utils/ansi.caps000066400000000000000000000024651311544710100261770ustar00rootroot00000000000000# Reconstructed via infocmp from file: /usr/share/terminfo/61/ansi ansi|ansi/pc-term compatible with color, am, mc5i, mir, msgr, colors#8, cols#80, it#8, lines#24, ncv#3, pairs#64, acsc=+\020\,\021-\030.^Y0\333`\004a\261f\370g\361h\260j\331k\277l\332m\300n\305o~p\304q\304r\304s_t\303u\264v\301w\302x\263y\363z\362{\343|\330}\234~\376, bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, clear=\E[H\E[J, cr=^M, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB, cud1=\E[B, cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, ed=\E[J, el=\E[K, el1=\E[1K, home=\E[H, hpa=\E[%i%p1%dG, ht=\E[I, hts=\EH, ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, ind=^J, indn=\E[%p1%dS, invis=\E[8m, kbs=^H, kcbt=\E[Z, kcub1=\E[D, kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A, khome=\E[H, kich1=\E[L, mc4=\E[4i, mc5=\E[5i, nel=\r\E[S, op=\E[39;49m, rep=%p1%c\E[%p2%{1}%-%db, rev=\E[7m, rin=\E[%p1%dT, rmacs=\E[10m, rmpch=\E[10m, rmso=\E[m, rmul=\E[m, s0ds=\E(B, s1ds=\E)B, s2ds=\E*B, s3ds=\E+B, setab=\E[4%p1%dm, setaf=\E[3%p1%dm, sgr=\E[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m, sgr0=\E[0;10m, smacs=\E[11m, smpch=\E[11m, smso=\E[7m, smul=\E[4m, tbc=\E[2g, u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?%[;0123456789]c, u9=\E[c, vpa=\E[%i%p1%dd, jline3-jline-3.3.1/terminal/src/main/resources/org/jline/utils/capabilities.txt000066400000000000000000000247201311544710100275650ustar00rootroot00000000000000# # Copyright (c) 2002-2016, the original author or authors. # # This software is distributable under the BSD license. See the terms of the # BSD license in the documentation provided with this software. # # http://www.opensource.org/licenses/bsd-license.php # auto_left_margin, bw, bw auto_right_margin, am, am back_color_erase, bce, ut can_change, ccc, cc ceol_standout_glitch, xhp, xs col_addr_glitch, xhpa, YA cpi_changes_res, cpix, YF cr_cancels_micro_mode, crxm, YB dest_tabs_magic_smso, xt, xt eat_newline_glitch, xenl, xn erase_overstrike, eo, eo generic_type, gn, gn hard_copy, hc, hc hard_cursor, chts, HC has_meta_key, km, km has_print_wheel, daisy, YC has_status_line, hs, hs hue_lightness_saturation, hls, hl insert_null_glitch, in, in lpi_changes_res, lpix, YG memory_above, da, da memory_below, db, db move_insert_mode, mir, mi move_standout_mode, msgr, ms needs_xon_xoff, nxon, nx no_esc_ctlc, xsb, xb no_pad_char, npc, NP non_dest_scroll_region, ndscr, ND non_rev_rmcup, nrrmc, NR over_strike, os, os prtr_silent, mc5i, 5i row_addr_glitch, xvpa, YD semi_auto_right_margin, sam, YE status_line_esc_ok, eslok, es tilde_glitch, hz, hz transparent_underline, ul, ul xon_xoff, xon, xo columns, cols, co init_tabs, it, it label_height, lh, lh label_width, lw, lw lines, lines, li lines_of_memory, lm, lm magic_cookie_glitch, xmc, sg max_attributes, ma, ma max_colors, colors, Co max_pairs, pairs, pa maximum_windows, wnum, MW no_color_video, ncv, NC num_labels, nlab, Nl padding_baud_rate, pb, pb virtual_terminal, vt, vt width_status_line, wsl, ws bit_image_entwining, bitwin, Yo bit_image_type, bitype, Yp buffer_capacity, bufsz, Ya buttons, btns, BT dot_horz_spacing, spinh, Yc dot_vert_spacing, spinv, Yb max_micro_address, maddr, Yd max_micro_jump, mjump, Ye micro_col_size, mcs, Yf micro_line_size, mls, Yg number_of_pins, npins, Yh output_res_char, orc, Yi output_res_horz_inch, orhi, Yk output_res_line, orl, Yj output_res_vert_inch, orvi, Yl print_rate, cps, Ym wide_char_size, widcs, Yn acs_chars, acsc, ac back_tab, cbt, bt bell, bel, bl carriage_return, cr, cr change_char_pitch, cpi, ZA change_line_pitch, lpi, ZB change_res_horz, chr, ZC change_res_vert, cvr, ZD change_scroll_region, csr, cs char_padding, rmp, rP clear_all_tabs, tbc, ct clear_margins, mgc, MC clear_screen, clear, cl clr_bol, el1, cb clr_eol, el, ce clr_eos, ed, cd column_address, hpa, ch command_character, cmdch, CC create_window, cwin, CW cursor_address, cup, cm cursor_down, cud1, do cursor_home, home, ho cursor_invisible, civis, vi cursor_left, cub1, le cursor_mem_address, mrcup, CM cursor_normal, cnorm, ve cursor_right, cuf1, nd cursor_to_ll, ll, ll cursor_up, cuu1, up cursor_visible, cvvis, vs define_char, defc, ZE delete_character, dch1, dc delete_line, dl1, dl dial_phone, dial, DI dis_status_line, dsl, ds display_clock, dclk, DK down_half_line, hd, hd ena_acs, enacs, eA enter_alt_charset_mode, smacs, as enter_am_mode, smam, SA enter_blink_mode, blink, mb enter_bold_mode, bold, md enter_ca_mode, smcup, ti enter_delete_mode, smdc, dm enter_dim_mode, dim, mh enter_doublewide_mode, swidm, ZF enter_draft_quality, sdrfq, ZG enter_insert_mode, smir, im enter_italics_mode, sitm, ZH enter_leftward_mode, slm, ZI enter_micro_mode, smicm, ZJ enter_near_letter_quality, snlq, ZK enter_normal_quality, snrmq, ZL enter_protected_mode, prot, mp enter_reverse_mode, rev, mr enter_secure_mode, invis, mk enter_shadow_mode, sshm, ZM enter_standout_mode, smso, so enter_subscript_mode, ssubm, ZN enter_superscript_mode, ssupm, ZO enter_underline_mode, smul, us enter_upward_mode, sum, ZP enter_xon_mode, smxon, SX erase_chars, ech, ec exit_alt_charset_mode, rmacs, ae exit_am_mode, rmam, RA exit_attribute_mode, sgr0, me exit_ca_mode, rmcup, te exit_delete_mode, rmdc, ed exit_doublewide_mode, rwidm, ZQ exit_insert_mode, rmir, ei exit_italics_mode, ritm, ZR exit_leftward_mode, rlm, ZS exit_micro_mode, rmicm, ZT exit_shadow_mode, rshm, ZU exit_standout_mode, rmso, se exit_subscript_mode, rsubm, ZV exit_superscript_mode, rsupm, ZW exit_underline_mode, rmul, ue exit_upward_mode, rum, ZX exit_xon_mode, rmxon, RX fixed_pause, pause, PA flash_hook, hook, fh flash_screen, flash, vb form_feed, ff, ff from_status_line, fsl, fs goto_window, wingo, WG hangup, hup, HU init_1string, is1, i1 init_2string, is2, is init_3string, is3, i3 init_file, if, if init_prog, iprog, iP initialize_color, initc, Ic initialize_pair, initp, Ip insert_character, ich1, ic insert_line, il1, al insert_padding, ip, ip key_a1, ka1, K1 key_a3, ka3, K3 key_b2, kb2, K2 key_backspace, kbs, kb key_beg, kbeg, @1 key_btab, kcbt, kB key_c1, kc1, K4 key_c3, kc3, K5 key_cancel, kcan, @2 key_catab, ktbc, ka key_clear, kclr, kC key_close, kclo, @3 key_command, kcmd, @4 key_copy, kcpy, @5 key_create, kcrt, @6 key_ctab, kctab, kt key_dc, kdch1, kD key_dl, kdl1, kL key_down, kcud1, kd key_eic, krmir, kM key_end, kend, @7 key_enter, kent, @8 key_eol, kel, kE key_eos, ked, kS key_exit, kext, @9 key_f0, kf0, k0 key_f1, kf1, k1 key_f10, kf10, k; key_f11, kf11, F1 key_f12, kf12, F2 key_f13, kf13, F3 key_f14, kf14, F4 key_f15, kf15, F5 key_f16, kf16, F6 key_f17, kf17, F7 key_f18, kf18, F8 key_f19, kf19, F9 key_f2, kf2, k2 key_f20, kf20, FA key_f21, kf21, FB key_f22, kf22, FC key_f23, kf23, FD key_f24, kf24, FE key_f25, kf25, FF key_f26, kf26, FG key_f27, kf27, FH key_f28, kf28, FI key_f29, kf29, FJ key_f3, kf3, k3 key_f30, kf30, FK key_f31, kf31, FL key_f32, kf32, FM key_f33, kf33, FN key_f34, kf34, FO key_f35, kf35, FP key_f36, kf36, FQ key_f37, kf37, FR key_f38, kf38, FS key_f39, kf39, FT key_f4, kf4, k4 key_f40, kf40, FU key_f41, kf41, FV key_f42, kf42, FW key_f43, kf43, FX key_f44, kf44, FY key_f45, kf45, FZ key_f46, kf46, Fa key_f47, kf47, Fb key_f48, kf48, Fc key_f49, kf49, Fd key_f5, kf5, k5 key_f50, kf50, Fe key_f51, kf51, Ff key_f52, kf52, Fg key_f53, kf53, Fh key_f54, kf54, Fi key_f55, kf55, Fj key_f56, kf56, Fk key_f57, kf57, Fl key_f58, kf58, Fm key_f59, kf59, Fn key_f6, kf6, k6 key_f60, kf60, Fo key_f61, kf61, Fp key_f62, kf62, Fq key_f63, kf63, Fr key_f7, kf7, k7 key_f8, kf8, k8 key_f9, kf9, k9 key_find, kfnd, @0 key_help, khlp, %1 key_home, khome, kh key_ic, kich1, kI key_il, kil1, kA key_left, kcub1, kl key_ll, kll, kH key_mark, kmrk, %2 key_message, kmsg, %3 key_move, kmov, %4 key_next, knxt, %5 key_npage, knp, kN key_open, kopn, %6 key_options, kopt, %7 key_ppage, kpp, kP key_previous, kprv, %8 key_print, kprt, %9 key_redo, krdo, %0 key_reference, kref, &1 key_refresh, krfr, &2 key_replace, krpl, &3 key_restart, krst, &4 key_resume, kres, &5 key_right, kcuf1, kr key_save, ksav, &6 key_sbeg, kBEG, &9 key_scancel, kCAN, &0 key_scommand, kCMD, *1 key_scopy, kCPY, *2 key_screate, kCRT, *3 key_sdc, kDC, *4 key_sdl, kDL, *5 key_select, kslt, *6 key_send, kEND, *7 key_seol, kEOL, *8 key_sexit, kEXT, *9 key_sf, kind, kF key_sfind, kFND, *0 key_shelp, kHLP, #1 key_shome, kHOM, #2 key_sic, kIC, #3 key_sleft, kLFT, #4 key_smessage, kMSG, %a key_smove, kMOV, %b key_snext, kNXT, %c key_soptions, kOPT, %d key_sprevious, kPRV, %e key_sprint, kPRT, %f key_sr, kri, kR key_sredo, kRDO, %g key_sreplace, kRPL, %h key_sright, kRIT, %i key_srsume, kRES, %j key_ssave, kSAV, !1 key_ssuspend, kSPD, !2 key_stab, khts, kT key_sundo, kUND, !3 key_suspend, kspd, &7 key_undo, kund, &8 key_up, kcuu1, ku keypad_local, rmkx, ke keypad_xmit, smkx, ks lab_f0, lf0, l0 lab_f1, lf1, l1 lab_f10, lf10, la lab_f2, lf2, l2 lab_f3, lf3, l3 lab_f4, lf4, l4 lab_f5, lf5, l5 lab_f6, lf6, l6 lab_f7, lf7, l7 lab_f8, lf8, l8 lab_f9, lf9, l9 label_format, fln, Lf label_off, rmln, LF label_on, smln, LO meta_off, rmm, mo meta_on, smm, mm micro_column_address, mhpa, ZY micro_down, mcud1, ZZ micro_left, mcub1, Za micro_right, mcuf1, Zb micro_row_address, mvpa, Zc micro_up, mcuu1, Zd newline, nel, nw order_of_pins, porder, Ze orig_colors, oc, oc orig_pair, op, op pad_char, pad, pc parm_dch, dch, DC parm_delete_line, dl, DL parm_down_cursor, cud, DO parm_down_micro, mcud, Zf parm_ich, ich, IC parm_index, indn, SF parm_insert_line, il, AL parm_left_cursor, cub, LE parm_left_micro, mcub, Zg parm_right_cursor, cuf, RI parm_right_micro, mcuf, Zh parm_rindex, rin, SR parm_up_cursor, cuu, UP parm_up_micro, mcuu, Zi pkey_key, pfkey, pk pkey_local, pfloc, pl pkey_xmit, pfx, px plab_norm, pln, pn print_screen, mc0, ps prtr_non, mc5p, pO prtr_off, mc4, pf prtr_on, mc5, po pulse, pulse, PU quick_dial, qdial, QD remove_clock, rmclk, RC repeat_char, rep, rp req_for_input, rfi, RF reset_1string, rs1, r1 reset_2string, rs2, r2 reset_3string, rs3, r3 reset_file, rf, rf restore_cursor, rc, rc row_address, vpa, cv save_cursor, sc, sc scroll_forward, ind, sf scroll_reverse, ri, sr select_char_set, scs, Zj set_attributes, sgr, sa set_background, setb, Sb set_bottom_margin, smgb, Zk set_bottom_margin_parm, smgbp, Zl set_clock, sclk, SC set_color_pair, scp, sp set_foreground, setf, Sf set_left_margin, smgl, ML set_left_margin_parm, smglp, Zm set_right_margin, smgr, MR set_right_margin_parm, smgrp, Zn set_tab, hts, st set_top_margin, smgt, Zo set_top_margin_parm, smgtp, Zp set_window, wind, wi start_bit_image, sbim, Zq start_char_set_def, scsd, Zr stop_bit_image, rbim, Zs stop_char_set_def, rcsd, Zt subscript_characters, subcs, Zu superscript_characters, supcs, Zv tab, ht, ta these_cause_cr, docr, Zw to_status_line, tsl, ts tone, tone, TO underline_char, uc, uc up_half_line, hu, hu user0, u0, u0 user1, u1, u1 user2, u2, u2 user3, u3, u3 user4, u4, u4 user5, u5, u5 user6, u6, u6 user7, u7, u7 user8, u8, u8 user9, u9, u9 wait_tone, wait, WA xoff_character, xoffc, XF xon_character, xonc, XN zero_motion, zerom, Zx alt_scancode_esc, scesa, S8 bit_image_carriage_return, bicr, Yv bit_image_newline, binel, Zz bit_image_repeat, birep, Xy char_set_names, csnm, Zy code_set_init, csin, ci color_names, colornm, Yw define_bit_image_region, defbi, Yx device_type, devt, dv display_pc_char, dispc, S1 end_bit_image_region, endbi, Yy enter_pc_charset_mode, smpch, S2 enter_scancode_mode, smsc, S4 exit_pc_charset_mode, rmpch, S3 exit_scancode_mode, rmsc, S5 get_mouse, getm, Gm key_mouse, kmous, Km mouse_info, minfo, Mi pc_term_options, pctrm, S6 pkey_plab, pfxl, xl req_mouse_pos, reqmp, RQ scancode_escape, scesc, S7 set0_des_seq, s0ds, s0 set1_des_seq, s1ds, s1 set2_des_seq, s2ds, s2 set3_des_seq, s3ds, s3 set_a_background, setab, AB set_a_foreground, setaf, AF set_color_band, setcolor, Yz set_lr_margin, smglr, ML set_page_length, slines, YZ set_tb_margin, smgtb, MT enter_horizontal_hl_mode, ehhlm, Xh enter_left_hl_mode, elhlm, Xl enter_low_hl_mode, elohlm, Xo enter_right_hl_mode, erhlm, Xr enter_top_hl_mode, ethlm, Xt enter_vertical_hl_mode, evhlm, Xv set_a_attributes, sgr1, sA set_pglen_inch, slength, sL jline3-jline-3.3.1/terminal/src/main/resources/org/jline/utils/dumb.caps000066400000000000000000000002461311544710100261670ustar00rootroot00000000000000# Reconstructed via infocmp from file: /usr/share/terminfo/64/dumb dumb|80-column dumb tty, am, cols#80, bel=^G, cr=^M, cud1=^J, ind=^J, jline3-jline-3.3.1/terminal/src/main/resources/org/jline/utils/screen-256color.caps000066400000000000000000000030151311544710100300650ustar00rootroot00000000000000# Reconstructed via infocmp from file: /usr/share/terminfo/73/screen-256color screen-256color|GNU Screen with 256 colors, am, km, mir, msgr, xenl, colors#256, cols#80, it#8, lines#24, ncv#3, pairs#32767, acsc=++\,\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, clear=\E[H\E[J, cnorm=\E[34h\E[?25h, cr=^M, csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\EM, cvvis=\E[34l, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, dl1=\E[M, ed=\E[J, el=\E[K, el1=\E[1K, enacs=\E(B\E)0, flash=\Eg, home=\E[H, ht=^I, hts=\EH, ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, ind=^J, initc@, is2=\E)0, kbs=^H, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, kdch1=\E[3~, kend=\E[4~, kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, khome=\E[1~, kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, nel=\EE, op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, rmacs=^O, rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, rmso=\E[23m, rmul=\E[24m, rs2=\Ec\E[?1000l\E[?25h, sc=\E7, setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, sgr=\E[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;, sgr0=\E[m\017, smacs=^N, smcup=\E[?1049h, smir=\E[4h, smkx=\E[?1h\E=, smso=\E[3m, smul=\E[4m, tbc=\E[3g, jline3-jline-3.3.1/terminal/src/main/resources/org/jline/utils/screen.caps000066400000000000000000000026121311544710100265160ustar00rootroot00000000000000# Reconstructed via infocmp from file: /usr/share/terminfo/73/screen screen|VT 100/ANSI X3.64 virtual terminal, am, km, mir, msgr, xenl, colors#8, cols#80, it#8, lines#24, ncv#3, pairs#64, acsc=++\,\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, clear=\E[H\E[J, cnorm=\E[34h\E[?25h, cr=^M, csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\EM, cvvis=\E[34l, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, dl1=\E[M, ed=\E[J, el=\E[K, el1=\E[1K, enacs=\E(B\E)0, flash=\Eg, home=\E[H, ht=^I, hts=\EH, ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, ind=^J, is2=\E)0, kbs=^H, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, kdch1=\E[3~, kend=\E[4~, kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, khome=\E[1~, kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, nel=\EE, op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, rmacs=^O, rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, rmso=\E[23m, rmul=\E[24m, rs2=\Ec\E[?1000l\E[?25h, sc=\E7, setab=\E[4%p1%dm, setaf=\E[3%p1%dm, sgr=\E[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;, sgr0=\E[m\017, smacs=^N, smcup=\E[?1049h, smir=\E[4h, smkx=\E[?1h\E=, smso=\E[3m, smul=\E[4m, tbc=\E[3g, jline3-jline-3.3.1/terminal/src/main/resources/org/jline/utils/windows.caps000066400000000000000000000017431311544710100267350ustar00rootroot00000000000000windows|windows terminal compatibility, am, mc5i, mir, msgr, colors#16, cols#80, it#8, lines#24, ncv#3, pairs#64, bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, clear=\E[H\E[J, cr=^M, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB, cud1=\E[B, cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, il=\E[%p1%dL, il1=\E[L, dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, el=\E[K, ed=\E[2K, el1=\E[1K, home=\E[H, hpa=\E[%i%p1%dG, ind=^J, invis=\E[8m, kbs=^H, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, khome=\E[H, op=\E[39;49m, rev=\E[7m, rmacs=\E[10m, rmpch=\E[10m, rmso=\E[m, rmul=\E[m, setab=\E[4%p1%dm, setaf=\E[3%p1%dm, sgr=\E[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m, sgr0=\E[0;10m, smso=\E[7m, smul=\E[4m, kdch1=\E[3~, kich1=\E[2~, kend=\E[4~, knp=\E[6~, kpp=\E[5~, kf1=\EOP, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, jline3-jline-3.3.1/terminal/src/main/resources/org/jline/utils/xterm-256color.caps000066400000000000000000000054411311544710100277520ustar00rootroot00000000000000# Reconstructed via infocmp from file: /usr/share/terminfo/78/xterm-256color xterm-256color|xterm with 256 colors, am, bce, ccc, km, mc5i, mir, msgr, npc, xenl, colors#256, cols#80, it#8, lines#24, pairs#32767, acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=^M, csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, ed=\E[J, el=\E[K, el1=\E[1K, flash=\E[?5h$<100/>\E[?5l, home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, ind=^J, indn=\E[%p1%dS, initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, invis=\E[8m, is2=\E[!p\E[?3;4l\E[4l\E>, kDC=\E[3;2~, kEND=\E[1;2F, kHOM=\E[1;2H, kIC=\E[2;2~, kLFT=\E[1;2D, kNXT=\E[6;2~, kPRV=\E[5;2~, kRIT=\E[1;2C, kb2=\EOE, kbs=^H, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, kdch1=\E[3~, kend=\EOF, kent=\EOM, kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[1;2P, kf14=\E[1;2Q, kf15=\E[1;2R, kf16=\E[1;2S, kf17=\E[15;2~, kf18=\E[17;2~, kf19=\E[18;2~, kf2=\EOQ, kf20=\E[19;2~, kf21=\E[20;2~, kf22=\E[21;2~, kf23=\E[23;2~, kf24=\E[24;2~, kf25=\E[1;5P, kf26=\E[1;5Q, kf27=\E[1;5R, kf28=\E[1;5S, kf29=\E[15;5~, kf3=\EOR, kf30=\E[17;5~, kf31=\E[18;5~, kf32=\E[19;5~, kf33=\E[20;5~, kf34=\E[21;5~, kf35=\E[23;5~, kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q, kf39=\E[1;6R, kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~, kf42=\E[17;6~, kf43=\E[18;6~, kf44=\E[19;6~, kf45=\E[20;6~, kf46=\E[21;6~, kf47=\E[23;6~, kf48=\E[24;6~, kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q, kf51=\E[1;3R, kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~, kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~, kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~, kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, khome=\EOH, kich1=\E[2~, kind=\E[1;2B, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kri=\E[1;2A, mc0=\E[i, mc4=\E[4i, mc5=\E[5i, meml=\El, memu=\Em, op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, rin=\E[%p1%dT, rmacs=\E(B, rmam=\E[?7l, rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, rmm=\E[?1034l, rmso=\E[27m, rmul=\E[24m, rs1=\Ec, rs2=\E[!p\E[?3;4l\E[4l\E>, sc=\E7, setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, sgr0=\E(B\E[m, smacs=\E(0, smam=\E[?7h, smcup=\E[?1049h, smir=\E[4h, smkx=\E[?1h\E=, smm=\E[?1034h, smso=\E[7m, smul=\E[4m, tbc=\E[3g, u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?1;2c, u9=\E[c, vpa=\E[%i%p1%dd, jline3-jline-3.3.1/terminal/src/main/resources/org/jline/utils/xterm.caps000066400000000000000000000053371311544710100264050ustar00rootroot00000000000000# Reconstructed via infocmp from file: /usr/share/terminfo/78/xterm xterm|xterm terminal emulator (X Window System), am, bce, km, mc5i, mir, msgr, npc, xenl, colors#8, cols#80, it#8, lines#24, pairs#64, acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=^M, csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, ed=\E[J, el=\E[K, el1=\E[1K, flash=\E[?5h$<100/>\E[?5l, home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, ind=^J, indn=\E[%p1%dS, invis=\E[8m, is2=\E[!p\E[?3;4l\E[4l\E>, kDC=\E[3;2~, kEND=\E[1;2F, kHOM=\E[1;2H, kIC=\E[2;2~, kLFT=\E[1;2D, kNXT=\E[6;2~, kPRV=\E[5;2~, kRIT=\E[1;2C, kb2=\EOE, kbs=^H, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, kdch1=\E[3~, kend=\EOF, kent=\EOM, kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[1;2P, kf14=\E[1;2Q, kf15=\E[1;2R, kf16=\E[1;2S, kf17=\E[15;2~, kf18=\E[17;2~, kf19=\E[18;2~, kf2=\EOQ, kf20=\E[19;2~, kf21=\E[20;2~, kf22=\E[21;2~, kf23=\E[23;2~, kf24=\E[24;2~, kf25=\E[1;5P, kf26=\E[1;5Q, kf27=\E[1;5R, kf28=\E[1;5S, kf29=\E[15;5~, kf3=\EOR, kf30=\E[17;5~, kf31=\E[18;5~, kf32=\E[19;5~, kf33=\E[20;5~, kf34=\E[21;5~, kf35=\E[23;5~, kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q, kf39=\E[1;6R, kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~, kf42=\E[17;6~, kf43=\E[18;6~, kf44=\E[19;6~, kf45=\E[20;6~, kf46=\E[21;6~, kf47=\E[23;6~, kf48=\E[24;6~, kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q, kf51=\E[1;3R, kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~, kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~, kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~, kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, khome=\EOH, kich1=\E[2~, kind=\E[1;2B, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kri=\E[1;2A, mc0=\E[i, mc4=\E[4i, mc5=\E[5i, meml=\El, memu=\Em, op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, rin=\E[%p1%dT, rmacs=\E(B, rmam=\E[?7l, rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, rmm=\E[?1034l, rmso=\E[27m, rmul=\E[24m, rs1=\Ec, rs2=\E[!p\E[?3;4l\E[4l\E>, sc=\E7, setab=\E[4%p1%dm, setaf=\E[3%p1%dm, setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, sgr0=\E(B\E[m, smacs=\E(0, smam=\E[?7h, smcup=\E[?1049h, smir=\E[4h, smkx=\E[?1h\E=, smm=\E[?1034h, smso=\E[7m, smul=\E[4m, tbc=\E[3g, u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?1;2c, u9=\E[c, vpa=\E[%i%p1%dd, jline3-jline-3.3.1/terminal/src/test/000077500000000000000000000000001311544710100173575ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/test/java/000077500000000000000000000000001311544710100203005ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/test/java/org/000077500000000000000000000000001311544710100210675ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/test/java/org/jline/000077500000000000000000000000001311544710100221705ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/test/java/org/jline/terminal/000077500000000000000000000000001311544710100240035ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/test/java/org/jline/terminal/impl/000077500000000000000000000000001311544710100247445ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/test/java/org/jline/terminal/impl/ExecPtyTest.java000066400000000000000000000363071311544710100300410ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import java.io.IOException; import java.util.EnumSet; import org.jline.terminal.Attributes; import org.jline.terminal.Attributes.ControlChar; import org.jline.terminal.Attributes.ControlFlag; import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Attributes.LocalFlag; import org.jline.terminal.Attributes.OutputFlag; import org.jline.terminal.Size; import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertEquals; public class ExecPtyTest { private final String linuxSttySample = "speed 38400 baud; rows 85; columns 244; line = 0;\n" + "intr = ^C; quit = ^\\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;\n" + "-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts\n" + "-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8\n" + "opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0\n" + "isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke"; private final String solarisSttySample = "speed 38400 baud; \n" + "rows = 85; columns = 244; ypixels = 0; xpixels = 0;\n" + "csdata ?\n" + "eucw 1:0:0:0, scrw 1:0:0:0\n" + "intr = ^c; quit = ^\\; erase = ^?; kill = ^u;\n" + "eof = ^d; eol = -^?; eol2 = -^?; swtch = ;\n" + "start = ^q; stop = ^s; susp = ^z; dsusp = ^y;\n" + "rprnt = ^r; flush = ^o; werase = ^w; lnext = ^v;\n" + "-parenb -parodd cs8 -cstopb -hupcl cread -clocal -loblk -crtscts -crtsxoff -parext \n" + "-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc \n" + "ixon ixany -ixoff imaxbel \n" + "isig icanon -xcase echo echoe echok -echonl -noflsh \n" + "-tostop echoctl -echoprt echoke -defecho -flusho -pendin iexten \n" + "opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3"; private final String aixSttySample = "speed 38400 baud; 85 rows; 244 columns;\n" + "eucw 1:1:0:0, scrw 1:1:0:0:\n" + "intr = ^C; quit = ^\\; erase = ^?; kill = ^U; eof = ^D; eol = \n" + "eol2 = ; start = ^Q; stop = ^S; susp = ^Z; dsusp = ^Y; reprint = ^R\n" + "discard = ^O; werase = ^W; lnext = ^V\n" + "-parenb -parodd cs8 -cstopb -hupcl cread -clocal -parext \n" + "-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc \n" + "ixon ixany -ixoff imaxbel \n" + "isig icanon -xcase echo echoe echok -echonl -noflsh \n" + "-tostop echoctl -echoprt echoke -flusho -pending iexten \n" + "opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3"; private final String macOsSttySample = "speed 9600 baud; 85 rows; 244 columns;\n" + "lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl\n" + "-echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo\n" + "-extproc\n" + "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8\n" + "-ignbrk brkint -inpck -ignpar -parmrk\n" + "oflags: opost onlcr -oxtabs -onocr -onlret\n" + "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow\n" + "-dtrflow -mdmbuf\n" + "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = ;\n" + "eol2 = ; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;\n" + "min = 1; quit = ^\\; reprint = ^R; start = ^Q; status = ^T;\n" + "stop = ^S; susp = ^Z; time = 0; werase = ^W;"; private final String netBsdSttySample = "speed 38400 baud; 85 rows; 244 columns;\n" + "lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl\n" + " -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo\n" + " -extproc\n" + "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -ignbrk\n" + " brkint -inpck -ignpar -parmrk\n" + "oflags: opost onlcr -ocrnl oxtabs onocr onlret\n" + "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -mdmbuf\n" + " -cdtrcts\n" + "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = ;\n" + " eol2 = ; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;\n" + " min = 1; quit = ^\\; reprint = ^R; start = ^Q; status = ^T;\n" + " stop = ^S; susp = ^Z; time = 0; werase = ^W;"; private final String freeBsdSttySample = "speed 9600 baud; 85 rows; 244 columns;\n" + "lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl\n" + " -echoprt -altwerase -noflsh -tostop -flusho -pendin -nokerninfo\n" + " -extproc\n" + "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -ignbrk\n" + " brkint -inpck -ignpar -parmrk\n" + "oflags: opost onlcr -ocrnl tab0 -onocr -onlret\n" + "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow\n" + " -dtrflow -mdmbuf\n" + "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = ;\n" + " eol2 = ; erase = ^?; erase2 = ^H; intr = ^C; kill = ^U;\n" + " lnext = ^V; min = 1; quit = ^\\; reprint = ^R; start = ^Q;\n" + " status = ^T; stop = ^S; susp = ^Z; time = 0; werase = ^W;"; private final String hpuxSttySample = "speed 38400 baud; line = 0;\n" + "rows = 85; columns = 244\n" + "min = 4; time = 0;\n" + "intr = DEL; quit = ^\\; erase = #; kill = @\n" + "eof = ^D; eol = ^@; eol2 ; swtch = ^@\n" + "stop = ^S; start = ^Q; susp ; dsusp \n" + "werase ; lnext \n" + "-parenb -parodd cs8 -cstopb hupcl cread -clocal -loblk -crts\n" + "-ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc\n" + "ixon ixany -ixoff -imaxbel -rtsxoff -ctsxon -ienqak\n" + "isig icanon -iexten -xcase echo -echoe echok -echonl -noflsh\n" + "-echoctl -echoprt -echoke -flusho -pendin\n" + "opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel -tostop"; @Test public void testParseSize() throws IOException { Assert.assertEquals(new Size(244, 85), ExecPty.doGetSize(linuxSttySample)); assertEquals(new Size(244, 85), ExecPty.doGetSize(solarisSttySample)); assertEquals(new Size(244, 85), ExecPty.doGetSize(aixSttySample)); assertEquals(new Size(244, 85), ExecPty.doGetSize(macOsSttySample)); assertEquals(new Size(244, 85), ExecPty.doGetSize(netBsdSttySample)); assertEquals(new Size(244, 85), ExecPty.doGetSize(freeBsdSttySample)); assertEquals(new Size(244, 85), ExecPty.doGetSize(hpuxSttySample)); } @Test public void testParseAttributesLinux() throws IOException { Attributes attributes = ExecPty.doGetAttr(linuxSttySample); // -ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8 assertEquals(EnumSet.of(InputFlag.BRKINT, InputFlag.ICRNL, InputFlag.IXON, InputFlag.IXANY, InputFlag.IMAXBEL, InputFlag.IUTF8), attributes.getInputFlags()); // opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 assertEquals(EnumSet.of(OutputFlag.OPOST, OutputFlag.ONLCR), attributes.getOutputFlags()); // -parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts assertEquals(EnumSet.of(ControlFlag.CREAD, ControlFlag.HUPCL, ControlFlag.CS8), attributes.getControlFlags()); // isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke assertEquals(EnumSet.of(LocalFlag.ISIG, LocalFlag.ICANON, LocalFlag.IEXTEN, LocalFlag.ECHO, LocalFlag.ECHOK, LocalFlag.ECHOCTL, LocalFlag.ECHOKE, LocalFlag.ECHOE), attributes.getLocalFlags()); // intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0 assertEquals(ExecPty.parseControlChar("^C"), attributes.getControlChar(ControlChar.VINTR)); assertEquals(ExecPty.parseControlChar("^\\"), attributes.getControlChar(ControlChar.VQUIT)); assertEquals(ExecPty.parseControlChar("^?"), attributes.getControlChar(ControlChar.VERASE)); assertEquals(ExecPty.parseControlChar("^U"), attributes.getControlChar(ControlChar.VKILL)); assertEquals(ExecPty.parseControlChar("^D"), attributes.getControlChar(ControlChar.VEOF)); assertEquals(ExecPty.parseControlChar("M-^?"), attributes.getControlChar(ControlChar.VEOL)); assertEquals(ExecPty.parseControlChar("M-^?"), attributes.getControlChar(ControlChar.VEOL2)); assertEquals(ExecPty.parseControlChar("^Q"), attributes.getControlChar(ControlChar.VSTART)); assertEquals(ExecPty.parseControlChar("^S"), attributes.getControlChar(ControlChar.VSTOP)); assertEquals(ExecPty.parseControlChar("^Z"), attributes.getControlChar(ControlChar.VSUSP)); assertEquals(ExecPty.parseControlChar("^R"), attributes.getControlChar(ControlChar.VREPRINT)); assertEquals(ExecPty.parseControlChar("^W"), attributes.getControlChar(ControlChar.VWERASE)); assertEquals(ExecPty.parseControlChar("^V"), attributes.getControlChar(ControlChar.VLNEXT)); assertEquals(1, attributes.getControlChar(ControlChar.VMIN)); assertEquals(0, attributes.getControlChar(ControlChar.VTIME)); } @Test public void testParseAttributesSolaris() throws IOException { Attributes attributes = ExecPty.doGetAttr(solarisSttySample); // -ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc ixon ixany -ixoff imaxbel assertEquals(EnumSet.of(InputFlag.BRKINT, InputFlag.ICRNL, InputFlag.IXON, InputFlag.IXANY, InputFlag.IMAXBEL), attributes.getInputFlags()); // opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3 assertEquals(EnumSet.of(OutputFlag.OPOST, OutputFlag.ONLCR), attributes.getOutputFlags()); // -parenb -parodd cs8 -cstopb -hupcl cread -clocal -loblk -crtscts -crtsxoff -parext assertEquals(EnumSet.of(ControlFlag.CREAD, ControlFlag.CS8), attributes.getControlFlags()); // isig icanon -xcase echo echoe echok -echonl -noflsh -tostop echoctl -echoprt echoke -defecho -flusho -pendin iexten assertEquals(EnumSet.of(LocalFlag.ISIG, LocalFlag.ICANON, LocalFlag.IEXTEN, LocalFlag.ECHO, LocalFlag.ECHOK, LocalFlag.ECHOCTL, LocalFlag.ECHOKE, LocalFlag.ECHOE), attributes.getLocalFlags()); // intr = ^c; quit = ^\\; erase = ^?; kill = ^u; eof = ^d; eol = -^?; eol2 = -^?; swtch = ; start = ^q; stop = ^s; susp = ^z; dsusp = ^y; rprnt = ^r; flush = ^o; werase = ^w; lnext = ^v; assertEquals(ExecPty.parseControlChar("^C"), attributes.getControlChar(ControlChar.VINTR)); assertEquals(ExecPty.parseControlChar("^\\"), attributes.getControlChar(ControlChar.VQUIT)); assertEquals(ExecPty.parseControlChar("^?"), attributes.getControlChar(ControlChar.VERASE)); assertEquals(ExecPty.parseControlChar("^U"), attributes.getControlChar(ControlChar.VKILL)); assertEquals(ExecPty.parseControlChar("^D"), attributes.getControlChar(ControlChar.VEOF)); assertEquals(ExecPty.parseControlChar("-^?"), attributes.getControlChar(ControlChar.VEOL)); assertEquals(ExecPty.parseControlChar("-^?"), attributes.getControlChar(ControlChar.VEOL2)); assertEquals(ExecPty.parseControlChar("^Q"), attributes.getControlChar(ControlChar.VSTART)); assertEquals(ExecPty.parseControlChar("^S"), attributes.getControlChar(ControlChar.VSTOP)); assertEquals(ExecPty.parseControlChar("^Z"), attributes.getControlChar(ControlChar.VSUSP)); assertEquals(ExecPty.parseControlChar("^R"), attributes.getControlChar(ControlChar.VREPRINT)); assertEquals(ExecPty.parseControlChar("^W"), attributes.getControlChar(ControlChar.VWERASE)); assertEquals(ExecPty.parseControlChar("^V"), attributes.getControlChar(ControlChar.VLNEXT)); assertEquals(-1, attributes.getControlChar(ControlChar.VMIN)); assertEquals(-1, attributes.getControlChar(ControlChar.VTIME)); } @Test public void testParseAttributesHpux() throws IOException { Attributes attributes = ExecPty.doGetAttr(hpuxSttySample); // -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc ixon ixany -ixoff -imaxbel -rtsxoff -ctsxon -ienqak assertEquals(EnumSet.of(InputFlag.BRKINT, InputFlag.IGNPAR, InputFlag.ISTRIP, InputFlag.ICRNL, InputFlag.IXON, InputFlag.IXANY), attributes.getInputFlags()); // opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel -tostop assertEquals(EnumSet.of(OutputFlag.OPOST, OutputFlag.ONLCR), attributes.getOutputFlags()); // -parenb -parodd cs8 -cstopb hupcl cread -clocal -loblk -crts assertEquals(EnumSet.of(ControlFlag.CREAD, ControlFlag.CS8, ControlFlag.HUPCL), attributes.getControlFlags()); // isig icanon -iexten -xcase echo -echoe echok -echonl -noflsh -echoctl -echoprt -echoke -flusho -pendin assertEquals(EnumSet.of(LocalFlag.ISIG, LocalFlag.ICANON, LocalFlag.ECHO, LocalFlag.ECHOK), attributes.getLocalFlags()); // min = 4; time = 0; intr = DEL; quit = ^\\; erase = #; kill = @; eof = ^D; eol = ^@; eol2 ; swtch = ^@; stop = ^S; start = ^Q; susp ; dsusp ; werase ; lnext ; assertEquals(127, attributes.getControlChar(ControlChar.VINTR)); assertEquals(ExecPty.parseControlChar("^\\"), attributes.getControlChar(ControlChar.VQUIT)); assertEquals('#', attributes.getControlChar(ControlChar.VERASE)); assertEquals('@', attributes.getControlChar(ControlChar.VKILL)); assertEquals(ExecPty.parseControlChar("^D"), attributes.getControlChar(ControlChar.VEOF)); assertEquals(0, attributes.getControlChar(ControlChar.VEOL)); assertEquals(-1, attributes.getControlChar(ControlChar.VEOL2)); assertEquals(ExecPty.parseControlChar("^Q"), attributes.getControlChar(ControlChar.VSTART)); assertEquals(ExecPty.parseControlChar("^S"), attributes.getControlChar(ControlChar.VSTOP)); assertEquals(-1, attributes.getControlChar(ControlChar.VSUSP)); assertEquals(-1, attributes.getControlChar(ControlChar.VREPRINT)); assertEquals(-1, attributes.getControlChar(ControlChar.VWERASE)); assertEquals(-1, attributes.getControlChar(ControlChar.VLNEXT)); assertEquals(4, attributes.getControlChar(ControlChar.VMIN)); assertEquals(0, attributes.getControlChar(ControlChar.VTIME)); } } jline3-jline-3.3.1/terminal/src/test/java/org/jline/terminal/impl/PosixSysTerminalTest.java000066400000000000000000000062011311544710100317430ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.terminal.impl; import org.easymock.EasyMock; import org.jline.terminal.Attributes; import org.jline.terminal.Terminal.Signal; import org.jline.terminal.Terminal.SignalHandler; import org.jline.terminal.spi.Pty; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.Charset; import static org.junit.Assert.assertEquals; public class PosixSysTerminalTest { @Test public void testNativeSignalsDefault() throws Exception { Pty pty = EasyMock.createNiceMock(Pty.class); EasyMock.expect(pty.getAttr()).andReturn(new Attributes()).anyTimes(); EasyMock.expect(pty.getSlaveInput()).andReturn(new ByteArrayInputStream(new byte[0])).anyTimes(); EasyMock.expect(pty.getSlaveOutput()).andReturn(new ByteArrayOutputStream()).anyTimes(); EasyMock.replay(pty); try (PosixSysTerminal terminal = new PosixSysTerminal( "name", "ansi", pty, Charset.defaultCharset().name(), true, SignalHandler.SIG_DFL)) { assertEquals(Signal.values().length, terminal.nativeHandlers.size()); } } @Test public void testNativeSignalsIgnore() throws Exception { Pty pty = EasyMock.createNiceMock(Pty.class); EasyMock.expect(pty.getAttr()).andReturn(new Attributes()).anyTimes(); EasyMock.expect(pty.getSlaveInput()).andReturn(new ByteArrayInputStream(new byte[0])).anyTimes(); EasyMock.expect(pty.getSlaveOutput()).andReturn(new ByteArrayOutputStream()).anyTimes(); EasyMock.replay(pty); try (PosixSysTerminal terminal = new PosixSysTerminal( "name", "ansi", pty, Charset.defaultCharset().name(), true, SignalHandler.SIG_IGN)) { assertEquals(Signal.values().length, terminal.nativeHandlers.size()); } } @Test public void testNativeSignalsRegister() throws Exception { Pty pty = EasyMock.createNiceMock(Pty.class); EasyMock.expect(pty.getAttr()).andReturn(new Attributes()).anyTimes(); EasyMock.expect(pty.getSlaveInput()).andReturn(new ByteArrayInputStream(new byte[0])).anyTimes(); EasyMock.expect(pty.getSlaveOutput()).andReturn(new ByteArrayOutputStream()).anyTimes(); EasyMock.replay(pty); try (PosixSysTerminal terminal = new PosixSysTerminal( "name", "ansi", pty, Charset.defaultCharset().name(), true, SignalHandler.SIG_DFL)) { assertEquals(Signal.values().length, terminal.nativeHandlers.size()); SignalHandler prev = terminal.handle(Signal.INT, s -> {}); assertEquals(Signal.values().length, terminal.nativeHandlers.size()); terminal.handle(Signal.INT, prev); assertEquals(Signal.values().length, terminal.nativeHandlers.size()); } } } jline3-jline-3.3.1/terminal/src/test/java/org/jline/utils/000077500000000000000000000000001311544710100233305ustar00rootroot00000000000000jline3-jline-3.3.1/terminal/src/test/java/org/jline/utils/AttributedStringTest.java000066400000000000000000000114531311544710100303350ustar00rootroot00000000000000package org.jline.utils; import org.jline.terminal.Terminal; import org.jline.terminal.impl.DumbTerminal; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; import static org.junit.Assert.assertEquals; public class AttributedStringTest { @Test public void test() { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.append("echo "); sb.append("foo", AttributedStyle.BOLD); assertEquals("echo \033[1mfoo\033[0m", sb.toAnsi()); assertEquals("o f", sb.toString().substring(3, 6)); assertEquals("o f", sb.columnSubSequence(3, 6).toString()); assertEquals("o \033[1mf\033[0m", sb.columnSubSequence(3, 6).toAnsi()); sb.append(" "); sb.style(AttributedStyle.DEFAULT.background(3)); sb.append("blue"); sb.style(AttributedStyle.DEFAULT.backgroundOff()); sb.append(" "); assertEquals("echo \033[1mfoo\033[0m \033[43mblue\033[0m ", sb.toAnsi()); sb.setLength(0); sb.append("echo "); sb.style(AttributedStyle.BOLD); sb.append("foo"); sb.append(" "); sb.style(sb.style().background(3)); sb.append("blue"); sb.style(sb.style().backgroundOff()); sb.append(" "); assertEquals("echo \033[1mfoo \033[43mblue\033[49m \033[0m", sb.toAnsi()); sb.setLength(0); sb.style(AttributedStyle.DEFAULT); sb.append("plain"); sb.style(sb.style().hidden()); sb.append("\033[38;5;120m"); sb.style(sb.style().hiddenOff()); sb.append("green"); sb.style(sb.style().hidden()); sb.append("\033[39m"); sb.style(sb.style().hiddenOff()); sb.append("plain"); assertEquals("plain\033[38;5;120mgreen\033[39mplain", sb.toAnsi()); assertEquals("plaingreenplain".length(), sb.toAttributedString().columnLength()); } @Test public void testRuns() { String ansi = "echo \033[1mfoo\033[0m \033[43mblue\033[0m "; AttributedString sb = AttributedString.fromAnsi(ansi); assertEquals(0, sb.runStart(2)); assertEquals(5, sb.runLimit(2)); assertEquals(5, sb.runStart(5)); assertEquals(8, sb.runLimit(6)); assertEquals(8, sb.runStart(8)); assertEquals(9, sb.runLimit(8)); assertEquals(9, sb.runStart(9)); assertEquals(13, sb.runLimit(9)); } @Test public void testAnsi() { String ansi = "echo \033[1mfoo \033[43mblue\033[0m "; AttributedString str = AttributedString.fromAnsi(ansi); assertEquals(ansi, str.toAnsi()); } @Test public void testBoldThenFaint() { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.styled(AttributedStyle::bold, "bold "); sb.styled(AttributedStyle::faint, "faint"); assertEquals("\u001b[1mbold \u001b[22;2mfaint\u001b[0m", sb.toAnsi()); } @Test public void testBoldAndFaint() { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.styled(AttributedStyle::bold, s -> s.append("bold ").styled(AttributedStyle::faint, "faint")); assertEquals("\u001b[1mbold \u001b[2mfaint\u001b[0m", sb.toAnsi()); } @Test public void test256Colors() throws IOException { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.style(sb.style().background(254)); sb.append("Hello"); assertEquals("\033[48;5;254mHello\033[0m", sb.toAnsi( new DumbTerminal("dumb", "xterm-256color", new ByteArrayInputStream(new byte[0]), new ByteArrayOutputStream(), Charset.defaultCharset().name()))); } @Test public void testCharWidth() { AttributedStringBuilder sb = new AttributedStringBuilder(); sb.append("\u2329\u2329\u2329\u2329"); // 〈〈〈 assertEquals(4, sb.length()); assertEquals(8, sb.columnLength()); assertEquals("", sb.columnSubSequence(0, 1).toString()); assertEquals("\u2329", sb.columnSubSequence(1, 3).toString()); assertEquals("\u2329\u2329\u2329", sb.columnSubSequence(3, 8).toString()); } @Test public void testColors() { String ansiStr = new AttributedStringBuilder() .append("This i") .style(AttributedStyle.DEFAULT.foreground(AttributedStyle.BLUE)) .append("s") .style(AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW)) .append(" a") .style(AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)) .append(" Test.") .toAnsi(); assertEquals("This i\u001B[34ms\u001B[33m a\u001B[31m Test.\u001B[0m", ansiStr); } } jline3-jline-3.3.1/terminal/src/test/java/org/jline/utils/CursesTest.java000066400000000000000000000014751311544710100263060ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.io.StringWriter; import org.junit.Test; import static org.junit.Assert.assertEquals; /** * @author Guillaume Nodet */ public class CursesTest { @Test public void testTputs() throws Exception { assertEquals("\033[3;4r", tputs("\\E[%i%p1%d;%p2%dr", 2, 3)); } private String tputs(String cap, Object... params) throws Exception { StringWriter sw = new StringWriter(); Curses.tputs(sw, cap, params); return sw.toString(); } } jline3-jline-3.3.1/terminal/src/test/java/org/jline/utils/InfoCmpTest.java000066400000000000000000000035471311544710100263770ustar00rootroot00000000000000/* * Copyright (c) 2002-2016, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. * * http://www.opensource.org/licenses/bsd-license.php */ package org.jline.utils; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.jline.utils.InfoCmp.Capability; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * @author Guillaume Nodet */ public class InfoCmpTest { @Test public void testInfoCmp() { Set bools = new HashSet<>(); Map ints = new HashMap<>(); Map strings = new HashMap<>(); String infocmp = InfoCmp.getLoadedInfoCmp("ansi"); InfoCmp.parseInfoCmp(infocmp, bools, ints, strings); assertEquals(4, bools.size()); assertTrue(strings.containsKey(Capability.byName("acsc"))); } @Test public void testInfoCmpWithHexa() { Set bools = new HashSet<>(); Map ints = new HashMap<>(); Map strings = new HashMap<>(); String infocmp = "xterm-256color|xterm with 256 colors,\n" + "\tam, bce, ccc, km, mc5i, mir, msgr, npc, xenl,\n" + "\tcolors#0x100, cols#80, it#8, lines#24, pairs#0x7fff,\n" + "\tacsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,\n" + "\tbel=^G, blink=\\E[5m, bold=\\E[1m, cbt=\\E[Z, civis=\\E[?25l\n"; InfoCmp.parseInfoCmp(infocmp, bools, ints, strings); assertEquals(0x100, (int) ints.get(Capability.max_colors)); assertEquals(0x7fff, (int) ints.get(Capability.max_pairs)); } }