OpenJDK / portola / portola
changeset 31541:8fc82d01db26
Merge
author | lana |
---|---|
date | Thu, 09 Jul 2015 16:37:55 -0700 |
parents | 7ee165176843 6efd719b3330 |
children | 8d8f66194222 |
files | jdk/make/non-build-utils/sharing/README.txt jdk/make/non-build-utils/sharing/tests/GHello.java jdk/make/non-build-utils/sharing/tests/Hello.java jdk/make/non-build-utils/sharing/tests/JHello.java jdk/make/non-build-utils/src/build/tools/makeclasslist/MakeClasslist.java |
diffstat | 146 files changed, 10937 insertions(+), 683 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/lib/Lib-jdk.internal.le.gmk Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,60 @@ +# +# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +include LibCommon.gmk + +################################################################################ + +ifeq ($(OPENJDK_TARGET_OS), windows) + + LIBLE_SRC := $(JDK_TOPDIR)/src/jdk.internal.le/$(OPENJDK_TARGET_OS_TYPE)/native/lible \ + # + LIBLE_CPPFLAGS := \ + $(addprefix -I, $(LIBLE_SRC)) \ + -I$(SUPPORT_OUTPUTDIR)/headers/jdk.internal.le \ + # + + $(eval $(call SetupNativeCompilation,BUILD_LIBLE, \ + LIBRARY := le, \ + OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ + SRC := $(LIBLE_SRC), \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBJAVA_HEADER_FLAGS)\ + $(LIBLE_CPPFLAGS), \ + LDFLAGS := $(LDFLAGS_JDKLIB), \ + LDFLAGS_SUFFIX := $(LDFLAGS_JDKLIB_SUFFIX) user32.lib, \ + VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \ + RC_FLAGS := $(RC_FLAGS) \ + -D "JDK_FNAME=le.dll" \ + -D "JDK_INTERNAL_NAME=le" \ + -D "JDK_FTYPE=0x2L", \ + OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/lible, \ + DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES))) + + TARGETS += $(BUILD_LIBLE) + +endif # OPENJDK_TARGET_OS + +################################################################################
--- a/jdk/make/non-build-utils/sharing/README.txt Thu Jul 09 13:49:36 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -This directory contains tools and tests associated with creating the -class list for class data sharing. - -The class list is produced by running the refWorkload startup3 benchmark with -the -XX:+TraceClassLoadingPreorder option. The -Xshare:off option must also be -used so that bootclasspath classes are loaded from rt.jar. The MakeClasslist -program should be built into the jar file makeclasslist.jar and is run -on one of the logs from each of the benchmarks in the following fashion: - -cd .../<resultsdir>/results.startup3 -$JAVA_HOME/bin/java -jar makeclasslist.jar results.Noop/results_1/log results.Framer/results_1/log results.XFramer/results_1/log results.JEdit/results_1/log results.LimeWire/results_1/log results.NetBeans50/results_1/log - -Presently, $JAVA_HOME must be the same path used to run the startup3 benchmark. - -The logs are deliberately concatenated in roughly smallest to largest order -based on application size. The resulting output is redirected into a file -and results in one of classlist.solaris, classlist.linux, classlist.macosx, -or classlist.windows. These files are checked in to the workspace. A -necessary checksum (AddJsum.java) is added to the final classlist -(installed in lib/ or jre/lib/) during the build process by the -makefiles in make/java/redist. - -In a forthcoming JDK build we plan to manually add the dependent -classes for the calendar manager Glow, which pulls in the Preferences -classes and, on Unix platforms, the XML parsing classes. - -The properties file supplied to the refworkload is approximately the -following: - -javahome=/usr/java/j2sdk1.8.0 -resultsdir=classlist-run -iterations=1 -benchmarks=startup3 -globalvmoptions=-client -Xshare:off -XX:+TraceClassLoadingPreorder
--- a/jdk/make/non-build-utils/sharing/tests/GHello.java Thu Jul 09 13:49:36 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -import java.awt.Font; -import java.awt.Frame; -import java.awt.Label; - -public class GHello extends Frame { - - public static void main(String[] args) { - System.out.println("Hello"); - - new GHello().show(); - if (args.length == 1 && args[0].equals("quit")) { - try { - Thread.currentThread().sleep(200); - } catch (InterruptedException e) { - } - System.exit(0); - } - } - - - GHello() { - Label label = new Label("Hello"); - label.setFont(new Font("Monospaced", Font.PLAIN, 144)); - add(label); - pack(); - } -}
--- a/jdk/make/non-build-utils/sharing/tests/Hello.java Thu Jul 09 13:49:36 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - - -public class Hello { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } -}
--- a/jdk/make/non-build-utils/sharing/tests/JHello.java Thu Jul 09 13:49:36 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -import java.awt.Font; -import javax.swing.JFrame; -import javax.swing.JLabel; - -public class JHello extends JFrame { - - public static void main(String[] args) { - System.out.println("Hello"); - - new JHello().show(); - if (args.length == 1 && args[0].equals("quit")) { - try { - Thread.currentThread().sleep(1000); - } catch (InterruptedException e) { - } - System.exit(0); - } - } - - - JHello() { - JLabel jlabel = new JLabel("Hello"); - jlabel.setFont(new Font("Monospaced", Font.PLAIN, 144)); - getContentPane().add(jlabel); - pack(); - } -}
--- a/jdk/make/non-build-utils/src/build/tools/makeclasslist/MakeClasslist.java Thu Jul 09 13:49:36 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package build.tools.makeclasslist; - -import java.io.*; -import java.util.*; -import java.util.jar.*; - -/** Reads a set of files containing the output of java - -XX:+TraceClassLoadingPreorder runs. Finds all classes that were - loaded from the bootstrap class path by comparing the prefix of - the load path to the current JRE's java.home system property. - Prints the names of these classes to stdout. -*/ - -public class MakeClasslist { - public static void main(String[] args) throws IOException { - List<String> classes = new ArrayList<>(); - String origJavaHome = System.getProperty("java.home"); - String javaHome = origJavaHome.toLowerCase(); - if (javaHome.endsWith("jre")) { - origJavaHome = origJavaHome.substring(0, origJavaHome.length() - 4); - javaHome = javaHome.substring(0, javaHome.length() - 4); - } - for (int i = 0; i < args.length; i++) { - try { - File file = new File(args[i]); - BufferedReader reader = new BufferedReader(new FileReader(file)); - String line = null; - while ((line = reader.readLine()) != null) { - StringTokenizer tok = new StringTokenizer(line, "[ \t\n\r\f"); - if (tok.hasMoreTokens()) { - String t = tok.nextToken(); - // Understand only "Loading" from -XX:+TraceClassLoadingPreorder. - // This ignores old "Loaded" from -verbose:class to force correct - // classlist generation on Mustang. - if (t.equals("Loading")) { - t = tok.nextToken(); - t = t.replace('.', '/'); - - // Check to make sure it came from the boot class path - if (tok.hasMoreTokens()) { - String tmp = tok.nextToken(); - if (tmp.equals("from")) { - if (tok.hasMoreTokens()) { - tmp = tok.nextToken().toLowerCase(); - // System.err.println("Loaded " + t + " from " + tmp); - if (tmp.startsWith(javaHome)) { - // OK, remember this class for later - classes.add(t); - } - } - } - } - } - } - } - } catch (IOException e) { - System.err.println("Error reading file " + args[i]); - throw(e); - } - } - - Set<String> seenClasses = new HashSet<>(); - - for (String str : classes) { - if (seenClasses.add(str)) { - System.out.println(str); - } - } - - // Try to complete certain packages - // Note: not using this new code yet; need to consider whether the - // footprint increase is worth any startup gains - // Note also that the packages considered below for completion are - // (obviously) platform-specific - // JarFile rtJar = new JarFile(origJavaHome + File.separator + - // "jre" + File.separator + - // "lib" + File.separator + - // "rt.jar"); - // completePackage(seenClasses, rtJar, "java/awt"); - // completePackage(seenClasses, rtJar, "sun/awt"); - // completePackage(seenClasses, rtJar, "sun/awt/X11"); - // completePackage(seenClasses, rtJar, "java/awt/im/spi"); - // completePackage(seenClasses, rtJar, "java/lang"); - } - - private static void completePackage(Set<String> seenClasses, - JarFile jar, - String packageName) { - int len = packageName.length(); - Enumeration<JarEntry> entries = jar.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - String name = entry.getName(); - if (name.startsWith(packageName) && - name.endsWith(".class") && - name.lastIndexOf('/') == len) { - // Trim ".class" from end - name = name.substring(0, name.length() - 6); - if (seenClasses.add(name)) { - System.out.println(name); - } - } - } - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/non-build-utils/src/build/tools/makeclasslist/makeClasslist.js Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * This tool is used to help create the class list for class data sharing. + * + * The classlist is produced internally by first running a select number of + * startup benchmarks with the -XX:DumpLoadedClassList=<file> option, then + * running this tool in the following fashion to produce a complete classlist: + * + * jjs -scripting makeClasslist.js -- list1 list2 list3 > classlist.platform + * + * The lists should be listed in roughly smallest to largest order based on + * application size. + * + * After generating the classlist it's necessary to add a checksum (using + * AddJsum.java) before checking it into the workspace as the corresponding + * platform-specific classlist, such as make/data/classlist/classlist.linux + */ +"use strict"; +var classlist = []; +var seenClasses = {}; + +for (var a in $ARG) { + var arg = $ARG[a]; + + var classes = readFully(arg).replace(/[\r\n]+/g, "\n").split("\n"); + + for (var c in classes) { + var clazz = classes[c]; + if (clazz !== "" && seenClasses[clazz] === undefined) { + seenClasses[clazz] = clazz; + classlist.push(clazz); + } + } +} + +for (c in classlist) { + print(classlist[c]); +}
--- a/jdk/src/java.base/share/classes/java/net/SocksSocketImpl.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/net/SocksSocketImpl.java Thu Jul 09 16:37:55 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import sun.net.SocksProxy; +import sun.net.spi.DefaultProxySelector; import sun.net.www.ParseUtil; /* import org.ietf.jgss.*; */ @@ -69,12 +70,21 @@ server = ad.getHostString(); serverPort = ad.getPort(); } + useV4 = useV4(proxy); } void setV4() { useV4 = true; } + private static boolean useV4(Proxy proxy) { + if (proxy instanceof SocksProxy + && ((SocksProxy)proxy).protocolVersion() == 4) { + return true; + } + return DefaultProxySelector.socksProxyVersion() == 4; + } + private synchronized void privilegedConnect(final String host, final int port, final int timeout) @@ -398,11 +408,7 @@ // Use getHostString() to avoid reverse lookups server = ((InetSocketAddress) p.address()).getHostString(); serverPort = ((InetSocketAddress) p.address()).getPort(); - if (p instanceof SocksProxy) { - if (((SocksProxy)p).protocolVersion() == 4) { - useV4 = true; - } - } + useV4 = useV4(p); // Connects to the SOCKS server try { @@ -715,11 +721,7 @@ // Use getHostString() to avoid reverse lookups server = ((InetSocketAddress) p.address()).getHostString(); serverPort = ((InetSocketAddress) p.address()).getPort(); - if (p instanceof SocksProxy) { - if (((SocksProxy)p).protocolVersion() == 4) { - useV4 = true; - } - } + useV4 = useV4(p); // Connects to the SOCKS server try {
--- a/jdk/src/java.base/share/classes/java/security/AccessControlContext.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/security/AccessControlContext.java Thu Jul 09 16:37:55 2015 -0700 @@ -76,7 +76,7 @@ public final class AccessControlContext { - private ProtectionDomain context[]; + private ProtectionDomain[] context; // isPrivileged and isAuthorized are referenced by the VM - do not remove // or change their names private boolean isPrivileged; @@ -89,13 +89,13 @@ private DomainCombiner combiner = null; // limited privilege scope - private Permission permissions[]; + private Permission[] permissions; private AccessControlContext parent; private boolean isWrapped; // is constrained by limited privilege scope? private boolean isLimited; - private ProtectionDomain limitedContext[]; + private ProtectionDomain[] limitedContext; private static boolean debugInit = false; private static Debug debug = null; @@ -123,7 +123,7 @@ * changes to the array will not affect this AccessControlContext. * @throws NullPointerException if {@code context} is {@code null} */ - public AccessControlContext(ProtectionDomain context[]) + public AccessControlContext(ProtectionDomain[] context) { if (context.length == 0) { this.context = null; @@ -282,7 +282,7 @@ * package private constructor for AccessController.getContext() */ - AccessControlContext(ProtectionDomain context[], + AccessControlContext(ProtectionDomain[] context, boolean isPrivileged) { this.context = context; @@ -643,7 +643,7 @@ /* * Combine the current (stack) and assigned domains. */ - private static ProtectionDomain[] combine(ProtectionDomain[]current, + private static ProtectionDomain[] combine(ProtectionDomain[] current, ProtectionDomain[] assigned) { // current could be null if only system code is on the stack; @@ -666,7 +666,7 @@ int n = (skipAssigned) ? 0 : assigned.length; // now we combine both of them, and create a new context - ProtectionDomain pd[] = new ProtectionDomain[slen + n]; + ProtectionDomain[] pd = new ProtectionDomain[slen + n]; // first copy in the assigned context domains, no need to compress if (!skipAssigned) { @@ -695,7 +695,7 @@ } else if (skipAssigned && n == slen) { return current; } - ProtectionDomain tmp[] = new ProtectionDomain[n]; + ProtectionDomain[] tmp = new ProtectionDomain[n]; System.arraycopy(pd, 0, tmp, 0, n); pd = tmp; }
--- a/jdk/src/java.base/share/classes/java/security/CodeSource.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/security/CodeSource.java Thu Jul 09 16:37:55 2015 -0700 @@ -65,7 +65,7 @@ /* * The code signers. Certificate chains are concatenated. */ - private transient java.security.cert.Certificate certs[] = null; + private transient java.security.cert.Certificate[] certs = null; // cached SocketPermission used for matchLocation private transient SocketPermission sp; @@ -91,7 +91,7 @@ * @param certs the certificate(s). It may be null. The contents of the * array are copied to protect against subsequent modification. */ - public CodeSource(URL url, java.security.cert.Certificate certs[]) { + public CodeSource(URL url, java.security.cert.Certificate[] certs) { this.location = url; if (url != null) { this.locationNoFragString = URLUtil.urlNoFragString(url);
--- a/jdk/src/java.base/share/classes/java/security/Permissions.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/security/Permissions.java Thu Jul 09 16:37:55 2015 -0700 @@ -289,9 +289,9 @@ if (unresolvedPerms == null) return null; - java.security.cert.Certificate certs[] = null; + java.security.cert.Certificate[] certs = null; - Object signers[] = p.getClass().getSigners(); + Object[] signers = p.getClass().getSigners(); int n = 0; if (signers != null) {
--- a/jdk/src/java.base/share/classes/java/security/SecureRandom.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/security/SecureRandom.java Thu Jul 09 16:37:55 2015 -0700 @@ -69,7 +69,7 @@ * * <pre> * SecureRandom random = new SecureRandom(); - * byte bytes[] = new byte[20]; + * byte[] bytes = new byte[20]; * random.nextBytes(bytes); * </pre> * @@ -77,7 +77,7 @@ * to generate a given number of seed bytes (to seed other random number * generators, for example): * <pre> - * byte seed[] = random.generateSeed(20); + * byte[] seed = random.generateSeed(20); * </pre> * * Note: Depending on the implementation, the {@code generateSeed} and @@ -186,7 +186,7 @@ * * @param seed the seed. */ - public SecureRandom(byte seed[]) { + public SecureRandom(byte[] seed) { super(0); getDefaultPRNG(true, seed); } @@ -486,7 +486,7 @@ @Override final protected int next(int numBits) { int numBytes = (numBits+7)/8; - byte b[] = new byte[numBytes]; + byte[] b = new byte[numBytes]; int next = 0; nextBytes(b);
--- a/jdk/src/java.base/share/classes/java/security/UnresolvedPermission.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/security/UnresolvedPermission.java Thu Jul 09 16:37:55 2015 -0700 @@ -130,7 +130,7 @@ */ private String actions; - private transient java.security.cert.Certificate certs[]; + private transient java.security.cert.Certificate[] certs; /** * Creates a new UnresolvedPermission containing the permission @@ -152,7 +152,7 @@ public UnresolvedPermission(String type, String name, String actions, - java.security.cert.Certificate certs[]) + java.security.cert.Certificate[] certs) { super(type); @@ -224,7 +224,7 @@ * try and resolve this permission using the class loader of the permission * that was passed in. */ - Permission resolve(Permission p, java.security.cert.Certificate certs[]) { + Permission resolve(Permission p, java.security.cert.Certificate[] certs) { if (this.certs != null) { // if p wasn't signed, we don't have a match if (certs == null) {
--- a/jdk/src/java.base/share/classes/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java Thu Jul 09 16:37:55 2015 -0700 @@ -54,7 +54,7 @@ private final BigInteger primeExponentP; private final BigInteger primeExponentQ; private final BigInteger crtCoefficient; - private final RSAOtherPrimeInfo otherPrimeInfo[]; + private final RSAOtherPrimeInfo[] otherPrimeInfo; /** * Creates a new {@code RSAMultiPrimePrivateCrtKeySpec}
--- a/jdk/src/java.base/share/classes/java/util/ArrayList.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/ArrayList.java Thu Jul 09 16:37:55 2015 -0700 @@ -178,7 +178,8 @@ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { - // c.toArray might (incorrectly) not return Object[] (see 6260652) + // defend against c.toArray (incorrectly) not returning Object[] + // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else {
--- a/jdk/src/java.base/share/classes/java/util/Arrays.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/Arrays.java Thu Jul 09 16:37:55 2015 -0700 @@ -3820,7 +3820,7 @@ @Override public Object[] toArray() { - return a.clone(); + return Arrays.copyOf(a, a.length, Object[].class); } @Override
--- a/jdk/src/java.base/share/classes/java/util/Vector.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/Vector.java Thu Jul 09 16:37:55 2015 -0700 @@ -174,7 +174,8 @@ public Vector(Collection<? extends E> c) { elementData = c.toArray(); elementCount = elementData.length; - // c.toArray might (incorrectly) not return Object[] (see 6260652) + // defend against c.toArray (incorrectly) not returning Object[] + // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, elementCount, Object[].class); }
--- a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Thu Jul 09 16:37:55 2015 -0700 @@ -134,7 +134,8 @@ elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { elements = c.toArray(); - // c.toArray might (incorrectly) not return Object[] (see 6260652) + // defend against c.toArray (incorrectly) not returning Object[] + // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, elements.length, Object[].class); }
--- a/jdk/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java Thu Jul 09 16:37:55 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,6 +106,15 @@ } } + public static int socksProxyVersion() { + return AccessController.doPrivileged( + new PrivilegedAction<Integer>() { + @Override public Integer run() { + return NetProperties.getInteger(SOCKS_PROXY_VERSION, 5); + } + }); + } + /** * How to deal with "non proxy hosts": * since we do have to generate a pattern we don't want to do that if @@ -302,8 +311,7 @@ saddr = InetSocketAddress.createUnresolved(phost, pport); // Socks is *always* the last on the list. if (j == (props[i].length - 1)) { - int version = NetProperties.getInteger(SOCKS_PROXY_VERSION, 5).intValue(); - return SocksProxy.create(saddr, version); + return SocksProxy.create(saddr, socksProxyVersion()); } else { return new Proxy(Proxy.Type.HTTP, saddr); }
--- a/jdk/src/java.base/share/classes/sun/security/pkcs/PKCS7.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/pkcs/PKCS7.java Thu Jul 09 16:37:55 2015 -0700 @@ -507,7 +507,7 @@ // certificates (optional) if (certificates != null && certificates.length != 0) { // cast to X509CertImpl[] since X509CertImpl implements DerEncoder - X509CertImpl implCerts[] = new X509CertImpl[certificates.length]; + X509CertImpl[] implCerts = new X509CertImpl[certificates.length]; for (int i = 0; i < certificates.length; i++) { if (certificates[i] instanceof X509CertImpl) implCerts[i] = (X509CertImpl) certificates[i];
--- a/jdk/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java Thu Jul 09 16:37:55 2015 -0700 @@ -78,7 +78,7 @@ * data is stored and transmitted losslessly, but no knowledge * about this particular algorithm is available. */ - private PKCS8Key (AlgorithmId algid, byte key []) + private PKCS8Key (AlgorithmId algid, byte[] key) throws InvalidKeyException { this.algid = algid; this.key = key;
--- a/jdk/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Thu Jul 09 16:37:55 2015 -0700 @@ -154,28 +154,28 @@ private static final Debug debug = Debug.getInstance("pkcs12"); - private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2}; - private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3}; - private static final int secretBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 5}; + private static final int[] keyBag = {1, 2, 840, 113549, 1, 12, 10, 1, 2}; + private static final int[] certBag = {1, 2, 840, 113549, 1, 12, 10, 1, 3}; + private static final int[] secretBag = {1, 2, 840, 113549, 1, 12, 10, 1, 5}; - private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20}; - private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21}; + private static final int[] pkcs9Name = {1, 2, 840, 113549, 1, 9, 20}; + private static final int[] pkcs9KeyId = {1, 2, 840, 113549, 1, 9, 21}; - private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1}; + private static final int[] pkcs9certType = {1, 2, 840, 113549, 1, 9, 22, 1}; - private static final int pbeWithSHAAnd40BitRC2CBC[] = + private static final int[] pbeWithSHAAnd40BitRC2CBC = {1, 2, 840, 113549, 1, 12, 1, 6}; - private static final int pbeWithSHAAnd3KeyTripleDESCBC[] = + private static final int[] pbeWithSHAAnd3KeyTripleDESCBC = {1, 2, 840, 113549, 1, 12, 1, 3}; - private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13}; + private static final int[] pbes2 = {1, 2, 840, 113549, 1, 5, 13}; // TODO: temporary Oracle OID /* * { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894) * jdk(746875) crypto(1) id-at-trustedKeyUsage(1) } */ - private static final int TrustedKeyUsage[] = + private static final int[] TrustedKeyUsage = {2, 16, 840, 1, 113894, 746875, 1, 1}; - private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0}; + private static final int[] AnyExtendedKeyUsage = {2, 5, 29, 37, 0}; private static ObjectIdentifier PKCS8ShroudedKeyBag_OID; private static ObjectIdentifier CertBag_OID; @@ -243,7 +243,7 @@ // A private key entry and its supporting certificate chain private static class PrivateKeyEntry extends KeyEntry { byte[] protectedPrivKey; - Certificate chain[]; + Certificate[] chain; }; // A secret key
--- a/jdk/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java Thu Jul 09 16:37:55 2015 -0700 @@ -403,7 +403,7 @@ debug.println(" "+perm); } } catch (ClassNotFoundException cnfe) { - Certificate certs[]; + Certificate[] certs; if (pe.signedBy != null) { certs = getCertificates(keyStore, pe.signedBy); } else { @@ -623,7 +623,7 @@ init(); } - final CodeSource codesource[] = {null}; + final CodeSource[] codesource = {null}; codesource[0] = canonicalizeCodebase(cs, true); @@ -666,7 +666,7 @@ // now see if any of the keys are trusted ids. if (!ignoreIdentityScope) { - Certificate certs[] = codesource[0].getCertificates(); + Certificate[] certs = codesource[0].getCertificates(); if (certs != null) { for (int k=0; k < certs.length; k++) { if (aliasMapping.get(certs[k]) == null &&
--- a/jdk/src/java.base/share/classes/sun/security/provider/DSAParameterGenerator.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/DSAParameterGenerator.java Thu Jul 09 16:37:55 2015 -0700 @@ -237,7 +237,7 @@ BigInteger offset = ONE; /* Step 11 */ for (counter = 0; counter < 4*valueL; counter++) { - BigInteger V[] = new BigInteger[n + 1]; + BigInteger[] V = new BigInteger[n + 1]; /* Step 11.1 */ for (int j = 0; j <= n; j++) { BigInteger J = BigInteger.valueOf(j);
--- a/jdk/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java Thu Jul 09 16:37:55 2015 -0700 @@ -82,7 +82,7 @@ private static class KeyEntry { Date date; // the creation date of this entry byte[] protectedPrivKey; - Certificate chain[]; + Certificate[] chain; }; // Trusted certificates @@ -604,7 +604,7 @@ * the keystore (such as deleting or modifying key or * certificate entries). */ - byte digest[] = md.digest(); + byte[] digest = md.digest(); dos.write(digest); dos.flush(); @@ -770,9 +770,8 @@ * with */ if (password != null) { - byte computed[], actual[]; - computed = md.digest(); - actual = new byte[computed.length]; + byte[] computed = md.digest(); + byte[] actual = new byte[computed.length]; dis.readFully(actual); for (int i = 0; i < computed.length; i++) { if (computed[i] != actual[i]) {
--- a/jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java Thu Jul 09 16:37:55 2015 -0700 @@ -795,7 +795,7 @@ // an unresolved permission which will be resolved // when implies is called // Add it to entry - Certificate certs[]; + Certificate[] certs; if (pe.signedBy != null) { certs = getCertificates(keyStore, pe.signedBy, @@ -817,7 +817,7 @@ debug.println(" "+perm); } } catch (ClassNotFoundException cnfe) { - Certificate certs[]; + Certificate[] certs; if (pe.signedBy != null) { certs = getCertificates(keyStore, pe.signedBy, @@ -2032,7 +2032,7 @@ * * @serial */ - private Certificate certs[]; + private Certificate[] certs; /** * Creates a new SelfPermission containing the permission @@ -2048,7 +2048,7 @@ * certificate first and the (root) certificate authority last). */ public SelfPermission(String type, String name, String actions, - Certificate certs[]) + Certificate[] certs) { super(type); if (type == null) {
--- a/jdk/src/java.base/share/classes/sun/security/provider/PolicyParser.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/PolicyParser.java Thu Jul 09 16:37:55 2015 -0700 @@ -1353,7 +1353,7 @@ } } - public static void main(String arg[]) throws Exception { + public static void main(String[] arg) throws Exception { try (FileReader fr = new FileReader(arg[0]); FileWriter fw = new FileWriter(arg[1])) { PolicyParser pp = new PolicyParser(true);
--- a/jdk/src/java.base/share/classes/sun/security/provider/SecureRandom.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/SecureRandom.java Thu Jul 09 16:37:55 2015 -0700 @@ -85,7 +85,7 @@ * * @param seed the seed. */ - private SecureRandom(byte seed[]) { + private SecureRandom(byte[] seed) { init(seed); }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ByteBufferInputStream.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ByteBufferInputStream.java Thu Jul 09 16:37:55 2015 -0700 @@ -70,7 +70,7 @@ * Increments position(). */ @Override - public int read(byte b[]) throws IOException { + public int read(byte[] b) throws IOException { if (bb == null) { throw new IOException("read on a closed InputStream"); @@ -85,7 +85,7 @@ * Increments position(). */ @Override - public int read(byte b[], int off, int len) throws IOException { + public int read(byte[] b, int off, int len) throws IOException { if (bb == null) { throw new IOException("read on a closed InputStream");
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Thu Jul 09 16:37:55 2015 -0700 @@ -810,7 +810,7 @@ String alias = null; int keytypesTmpSize = keytypesTmp.size(); if (keytypesTmpSize != 0) { - String keytypes[] = + String[] keytypes = keytypesTmp.toArray(new String[keytypesTmpSize]); if (conn != null) {
--- a/jdk/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java Thu Jul 09 16:37:55 2015 -0700 @@ -48,7 +48,7 @@ * This value may be empty if it was included in the * client's certificate ... */ - private byte dh_Yc[]; // 1 to 2^16 -1 bytes + private byte[] dh_Yc; // 1 to 2^16 -1 bytes BigInteger getClientPublicKey() { return dh_Yc == null ? null : new BigInteger(1, dh_Yc);
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java Thu Jul 09 16:37:55 2015 -0700 @@ -146,7 +146,7 @@ byte[] getBytes8() throws IOException { int len = getInt8(); verifyLength(len); - byte b[] = new byte[len]; + byte[] b = new byte[len]; read(b); return b; @@ -155,7 +155,7 @@ public byte[] getBytes16() throws IOException { int len = getInt16(); verifyLength(len); - byte b[] = new byte[len]; + byte[] b = new byte[len]; read(b); return b; @@ -164,7 +164,7 @@ byte[] getBytes24() throws IOException { int len = getInt24(); verifyLength(len); - byte b[] = new byte[len]; + byte[] b = new byte[len]; read(b); return b;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java Thu Jul 09 16:37:55 2015 -0700 @@ -689,8 +689,8 @@ static final class RSA_ServerKeyExchange extends ServerKeyExchange { - private byte rsa_modulus[]; // 1 to 2^16 - 1 bytes - private byte rsa_exponent[]; // 1 to 2^16 - 1 bytes + private byte[] rsa_modulus; // 1 to 2^16 - 1 bytes + private byte[] rsa_exponent; // 1 to 2^16 - 1 bytes private Signature signature; private byte[] signatureBytes; @@ -698,7 +698,7 @@ /* * Hash the nonces and the ephemeral RSA public key. */ - private void updateSignature(byte clntNonce[], byte svrNonce[]) + private void updateSignature(byte[] clntNonce, byte[] svrNonce) throws SignatureException { int tmp; @@ -827,11 +827,11 @@ private final static boolean dhKeyExchangeFix = Debug.getBooleanProperty("com.sun.net.ssl.dhKeyExchangeFix", true); - private byte dh_p []; // 1 to 2^16 - 1 bytes - private byte dh_g []; // 1 to 2^16 - 1 bytes - private byte dh_Ys []; // 1 to 2^16 - 1 bytes + private byte[] dh_p; // 1 to 2^16 - 1 bytes + private byte[] dh_g; // 1 to 2^16 - 1 bytes + private byte[] dh_Ys; // 1 to 2^16 - 1 bytes - private byte signature []; + private byte[] signature; // protocol version being established using this ServerKeyExchange message ProtocolVersion protocolVersion; @@ -857,8 +857,8 @@ * with the cert chain which was sent ... for DHE_DSS and DHE_RSA * key exchange. (Constructor called by server.) */ - DH_ServerKeyExchange(DHCrypt obj, PrivateKey key, byte clntNonce[], - byte svrNonce[], SecureRandom sr, + DH_ServerKeyExchange(DHCrypt obj, PrivateKey key, byte[] clntNonce, + byte[] svrNonce, SecureRandom sr, SignatureAndHashAlgorithm signAlgorithm, ProtocolVersion protocolVersion) throws GeneralSecurityException { @@ -913,7 +913,7 @@ * DHE_DSS or DHE_RSA key exchange. (Called by client.) */ DH_ServerKeyExchange(HandshakeInStream input, PublicKey publicKey, - byte clntNonce[], byte svrNonce[], int messageSize, + byte[] clntNonce, byte[] svrNonce, int messageSize, Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs, ProtocolVersion protocolVersion) throws IOException, GeneralSecurityException { @@ -948,7 +948,7 @@ } // read the signature - byte signature[]; + byte[] signature; if (dhKeyExchangeFix) { signature = input.getBytes16(); } else { @@ -1004,8 +1004,8 @@ /* * Update sig with nonces and Diffie-Hellman public key. */ - private void updateSignature(Signature sig, byte clntNonce[], - byte svrNonce[]) throws SignatureException { + private void updateSignature(Signature sig, byte[] clntNonce, + byte[] svrNonce) throws SignatureException { int tmp; sig.update(clntNonce); @@ -1268,8 +1268,8 @@ } } - private void updateSignature(Signature sig, byte clntNonce[], - byte svrNonce[]) throws SignatureException { + private void updateSignature(Signature sig, byte[] clntNonce, + byte[] svrNonce) throws SignatureException { sig.update(clntNonce); sig.update(svrNonce); @@ -1334,7 +1334,7 @@ * DER encoded distinguished name. * TLS requires that its not longer than 65535 bytes. */ - byte name[]; + byte[] name; DistinguishedName(HandshakeInStream input) throws IOException { name = input.getBytes16(); @@ -1411,8 +1411,8 @@ private final static byte[] TYPES_ECC = { cct_rsa_sign, cct_dss_sign, cct_ecdsa_sign }; - byte types []; // 1 to 255 types - DistinguishedName authorities []; // 3 to 2^16 - 1 + byte[] types; // 1 to 255 types + DistinguishedName[] authorities; // 3 to 2^16 - 1 // ... "3" because that's the smallest DER-encoded X500 DN // protocol version being established using this CertificateRequest message @@ -1424,7 +1424,7 @@ // length of supported_signature_algorithms private int algorithmsLen; - CertificateRequest(X509Certificate ca[], KeyExchange keyExchange, + CertificateRequest(X509Certificate[] ca, KeyExchange keyExchange, Collection<SignatureAndHashAlgorithm> signAlgs, ProtocolVersion protocolVersion) throws IOException { @@ -2063,7 +2063,7 @@ if (protocolVersion.useTLS10PlusSpec()) { // TLS 1.0+ try { - byte [] seed; + byte[] seed; String prfAlg; PRF prf;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java Thu Jul 09 16:37:55 2015 -0700 @@ -119,7 +119,7 @@ } } - public void putBytes16(byte b[]) throws IOException { + public void putBytes16(byte[] b) throws IOException { if (b == null) { putInt16(0); } else { @@ -128,7 +128,7 @@ } } - void putBytes24(byte b[]) throws IOException { + void putBytes24(byte[] b) throws IOException { if (b == null) { putInt24(0); } else {
--- a/jdk/src/java.base/share/classes/sun/security/ssl/MAC.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/MAC.java Thu Jul 09 16:37:55 2015 -0700 @@ -52,7 +52,7 @@ final static MAC TLS_NULL = new MAC(false); // Value of the null MAC is fixed - private static final byte nullMAC[] = new byte[0]; + private static final byte[] nullMAC = new byte[0]; // internal identifier for the MAC algorithm private final MacAlg macAlg;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/RandomCookie.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/RandomCookie.java Thu Jul 09 16:37:55 2015 -0700 @@ -38,7 +38,7 @@ */ final class RandomCookie { - byte random_bytes[]; // exactly 32 bytes + byte[] random_bytes; // exactly 32 bytes RandomCookie(SecureRandom generator) { long temp = System.currentTimeMillis() / 1000;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Thu Jul 09 16:37:55 2015 -0700 @@ -986,7 +986,7 @@ ClientKeyExchangeService.find(keyExchange.name) == null) { CertificateRequest m4; - X509Certificate caCerts[]; + X509Certificate[] caCerts; Collection<SignatureAndHashAlgorithm> localSignAlgs = null; if (protocolVersion.useTLS12PlusSpec()) {
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SessionId.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SessionId.java Thu Jul 09 16:37:55 2015 -0700 @@ -43,7 +43,7 @@ class SessionId { static int MAX_LENGTH = 32; - private byte sessionId []; // max 32 bytes + private byte[] sessionId; // max 32 bytes /** Constructs a new session ID ... perhaps for a rejoinable session */ SessionId (boolean isRejoinable, SecureRandom generator) @@ -56,7 +56,7 @@ } /** Constructs a session ID from a byte array (max size 32 bytes) */ - SessionId (byte sessionId []) + SessionId (byte[] sessionId) { this.sessionId = sessionId; } /** Returns the length of the ID, in bytes */ @@ -64,7 +64,7 @@ { return sessionId.length; } /** Returns the bytes in the ID. May be an empty array. */ - byte [] getId () + byte[] getId () { return sessionId.clone (); } @@ -106,7 +106,7 @@ return false; SessionId s = (SessionId) obj; - byte b [] = s.getId (); + byte[] b = s.getId (); if (b.length != sessionId.length) return false;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java Thu Jul 09 16:37:55 2015 -0700 @@ -94,13 +94,13 @@ } @Override - public void checkClientTrusted(X509Certificate chain[], String authType) + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { checkTrusted(chain, authType, (Socket)null, true); } @Override - public void checkServerTrusted(X509Certificate chain[], String authType) + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { checkTrusted(chain, authType, (Socket)null, false); }
--- a/jdk/src/java.base/share/classes/sun/security/util/ManifestDigester.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/ManifestDigester.java Thu Jul 09 16:37:55 2015 -0700 @@ -37,7 +37,7 @@ public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes"; /** the raw bytes of the manifest */ - private byte rawBytes[]; + private byte[] rawBytes; /** the offset/length pair for a section */ private HashMap<String, Entry> entries; // key is a UTF-8 string @@ -107,7 +107,7 @@ return false; } - public ManifestDigester(byte bytes[]) + public ManifestDigester(byte[] bytes) { rawBytes = bytes; entries = new HashMap<>(); @@ -181,7 +181,7 @@ } } - private boolean isNameAttr(byte bytes[], int start) + private boolean isNameAttr(byte[] bytes, int start) { return ((bytes[start] == 'N') || (bytes[start] == 'n')) && ((bytes[start+1] == 'a') || (bytes[start+1] == 'A')) && @@ -261,11 +261,10 @@ return e; } - public byte[] manifestDigest(MessageDigest md) - { - md.reset(); - md.update(rawBytes, 0, rawBytes.length); - return md.digest(); - } + public byte[] manifestDigest(MessageDigest md) { + md.reset(); + md.update(rawBytes, 0, rawBytes.length); + return md.digest(); + } }
--- a/jdk/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java Thu Jul 09 16:37:55 2015 -0700 @@ -165,7 +165,7 @@ /** * update the digests for the digests we are interested in */ - public void update(byte buffer[], int off, int len) { + public void update(byte[] buffer, int off, int len) { if (skip) return; for (int i=0; i < digests.size(); i++) {
--- a/jdk/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java Thu Jul 09 16:37:55 2015 -0700 @@ -212,7 +212,7 @@ * Constructor, from an array of integers. * Validity check included. */ - public ObjectIdentifier (int values []) throws IOException + public ObjectIdentifier(int[] values) throws IOException { checkCount(values.length); checkFirstComponent(values[0]);
--- a/jdk/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java Thu Jul 09 16:37:55 2015 -0700 @@ -55,7 +55,7 @@ private PKCS7 block; /** the raw bytes of the .SF file */ - private byte sfBytes[]; + private byte[] sfBytes; /** the name of the signature block file, uppercased and without * the extension (.DSA/.RSA/.EC) @@ -84,7 +84,7 @@ public SignatureFileVerifier(ArrayList<CodeSigner[]> signerCache, ManifestDigester md, String name, - byte rawBytes[]) + byte[] rawBytes) throws IOException, CertificateException { // new PKCS7() calls CertificateFactory.getInstance() @@ -129,7 +129,7 @@ * used to set the raw bytes of the .SF file when it * is external to the signature block file. */ - public void setSignatureFile(byte sfBytes[]) + public void setSignatureFile(byte[] sfBytes) { this.sfBytes = sfBytes; } @@ -511,7 +511,7 @@ * CodeSigner objects. We do this only *once* for a given * signature block file. */ - private CodeSigner[] getSigners(SignerInfo infos[], PKCS7 block) + private CodeSigner[] getSigners(SignerInfo[] infos, PKCS7 block) throws IOException, NoSuchAlgorithmException, SignatureException, CertificateException {
--- a/jdk/src/java.base/share/classes/sun/security/x509/AVA.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/AVA.java Thu Jul 09 16:37:55 2015 -0700 @@ -967,7 +967,7 @@ previousWhite = false; - byte valueBytes[] = null; + byte[] valueBytes = null; try { valueBytes = Character.toString(c).getBytes("UTF8"); } catch (IOException ie) { @@ -1051,7 +1051,7 @@ // using the hex format below. This will be used only // when the value is not a string type - byte data [] = value.toByteArray(); + byte[] data = value.toByteArray(); retval.append('#'); for (int i = 0; i < data.length; i++) {
--- a/jdk/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java Thu Jul 09 16:37:55 2015 -0700 @@ -117,7 +117,7 @@ * @param q the DSS/DSA parameter "Q" * @param g the DSS/DSA parameter "G" */ - public AlgIdDSA (byte p [], byte q [], byte g []) + public AlgIdDSA (byte[] p, byte[] q, byte[] g) throws IOException { this (new BigInteger (1, p),
--- a/jdk/src/java.base/share/classes/sun/security/x509/AlgorithmId.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/AlgorithmId.java Thu Jul 09 16:37:55 2015 -0700 @@ -648,12 +648,12 @@ /* * COMMON PUBLIC KEY TYPES */ - private static final int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 }; - private static final int DH_PKIX_data[] = { 1, 2, 840, 10046, 2, 1 }; - private static final int DSA_OIW_data[] = { 1, 3, 14, 3, 2, 12 }; - private static final int DSA_PKIX_data[] = { 1, 2, 840, 10040, 4, 1 }; - private static final int RSA_data[] = { 2, 5, 8, 1, 1 }; - private static final int RSAEncryption_data[] = + private static final int[] DH_data = { 1, 2, 840, 113549, 1, 3, 1 }; + private static final int[] DH_PKIX_data = { 1, 2, 840, 10046, 2, 1 }; + private static final int[] DSA_OIW_data = { 1, 3, 14, 3, 2, 12 }; + private static final int[] DSA_PKIX_data = { 1, 2, 840, 10040, 4, 1 }; + private static final int[] RSA_data = { 2, 5, 8, 1, 1 }; + private static final int[] RSAEncryption_data = { 1, 2, 840, 113549, 1, 1, 1 }; public static final ObjectIdentifier DH_oid; @@ -674,27 +674,27 @@ /* * COMMON SIGNATURE ALGORITHMS */ - private static final int md2WithRSAEncryption_data[] = + private static final int[] md2WithRSAEncryption_data = { 1, 2, 840, 113549, 1, 1, 2 }; - private static final int md5WithRSAEncryption_data[] = + private static final int[] md5WithRSAEncryption_data = { 1, 2, 840, 113549, 1, 1, 4 }; - private static final int sha1WithRSAEncryption_data[] = + private static final int[] sha1WithRSAEncryption_data = { 1, 2, 840, 113549, 1, 1, 5 }; - private static final int sha1WithRSAEncryption_OIW_data[] = + private static final int[] sha1WithRSAEncryption_OIW_data = { 1, 3, 14, 3, 2, 29 }; - private static final int sha224WithRSAEncryption_data[] = + private static final int[] sha224WithRSAEncryption_data = { 1, 2, 840, 113549, 1, 1, 14 }; - private static final int sha256WithRSAEncryption_data[] = + private static final int[] sha256WithRSAEncryption_data = { 1, 2, 840, 113549, 1, 1, 11 }; - private static final int sha384WithRSAEncryption_data[] = + private static final int[] sha384WithRSAEncryption_data = { 1, 2, 840, 113549, 1, 1, 12 }; - private static final int sha512WithRSAEncryption_data[] = + private static final int[] sha512WithRSAEncryption_data = { 1, 2, 840, 113549, 1, 1, 13 }; - private static final int shaWithDSA_OIW_data[] = + private static final int[] shaWithDSA_OIW_data = { 1, 3, 14, 3, 2, 13 }; - private static final int sha1WithDSA_OIW_data[] = + private static final int[] sha1WithDSA_OIW_data = { 1, 3, 14, 3, 2, 27 }; - private static final int dsaWithSHA1_PKIX_data[] = + private static final int[] dsaWithSHA1_PKIX_data = { 1, 2, 840, 10040, 4, 3 }; public static final ObjectIdentifier md2WithRSAEncryption_oid;
--- a/jdk/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java Thu Jul 09 16:37:55 2015 -0700 @@ -69,7 +69,7 @@ public static final String S_MIME_CA = "s_mime_ca"; public static final String OBJECT_SIGNING_CA = "object_signing_ca"; - private static final int CertType_data[] = { 2, 16, 840, 1, 113730, 1, 1 }; + private static final int[] CertType_data = { 2, 16, 840, 1, 113730, 1, 1 }; /** * Object identifier for the Netscape-Cert-Type extension.
--- a/jdk/src/java.base/share/classes/sun/security/x509/OIDMap.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/OIDMap.java Thu Jul 09 16:37:55 2015 -0700 @@ -102,7 +102,7 @@ private static final String OCSPNOCHECK = ROOT + "." + OCSPNoCheckExtension.NAME; - private static final int NetscapeCertType_data[] = + private static final int[] NetscapeCertType_data = { 2, 16, 840, 1, 113730, 1, 1 }; /** Map ObjectIdentifier(oid) -> OIDInfo(info) */
--- a/jdk/src/java.base/share/classes/sun/security/x509/PKIXExtensions.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/PKIXExtensions.java Thu Jul 09 16:37:55 2015 -0700 @@ -49,32 +49,32 @@ */ public class PKIXExtensions { // The object identifiers - private static final int AuthorityKey_data [] = { 2, 5, 29, 35 }; - private static final int SubjectKey_data [] = { 2, 5, 29, 14 }; - private static final int KeyUsage_data [] = { 2, 5, 29, 15 }; - private static final int PrivateKeyUsage_data [] = { 2, 5, 29, 16 }; - private static final int CertificatePolicies_data [] = { 2, 5, 29, 32 }; - private static final int PolicyMappings_data [] = { 2, 5, 29, 33 }; - private static final int SubjectAlternativeName_data [] = { 2, 5, 29, 17 }; - private static final int IssuerAlternativeName_data [] = { 2, 5, 29, 18 }; - private static final int SubjectDirectoryAttributes_data [] = { 2, 5, 29, 9 }; - private static final int BasicConstraints_data [] = { 2, 5, 29, 19 }; - private static final int NameConstraints_data [] = { 2, 5, 29, 30 }; - private static final int PolicyConstraints_data [] = { 2, 5, 29, 36 }; - private static final int CRLDistributionPoints_data [] = { 2, 5, 29, 31 }; - private static final int CRLNumber_data [] = { 2, 5, 29, 20 }; - private static final int IssuingDistributionPoint_data [] = { 2, 5, 29, 28 }; - private static final int DeltaCRLIndicator_data [] = { 2, 5, 29, 27 }; - private static final int ReasonCode_data [] = { 2, 5, 29, 21 }; - private static final int HoldInstructionCode_data [] = { 2, 5, 29, 23 }; - private static final int InvalidityDate_data [] = { 2, 5, 29, 24 }; - private static final int ExtendedKeyUsage_data [] = { 2, 5, 29, 37 }; - private static final int InhibitAnyPolicy_data [] = { 2, 5, 29, 54 }; - private static final int CertificateIssuer_data [] = { 2, 5, 29, 29 }; - private static final int AuthInfoAccess_data [] = { 1, 3, 6, 1, 5, 5, 7, 1, 1}; - private static final int SubjectInfoAccess_data [] = { 1, 3, 6, 1, 5, 5, 7, 1, 11}; - private static final int FreshestCRL_data [] = { 2, 5, 29, 46 }; - private static final int OCSPNoCheck_data [] = { 1, 3, 6, 1, 5, 5, 7, + private static final int[] AuthorityKey_data = { 2, 5, 29, 35 }; + private static final int[] SubjectKey_data = { 2, 5, 29, 14 }; + private static final int[] KeyUsage_data = { 2, 5, 29, 15 }; + private static final int[] PrivateKeyUsage_data = { 2, 5, 29, 16 }; + private static final int[] CertificatePolicies_data = { 2, 5, 29, 32 }; + private static final int[] PolicyMappings_data = { 2, 5, 29, 33 }; + private static final int[] SubjectAlternativeName_data = { 2, 5, 29, 17 }; + private static final int[] IssuerAlternativeName_data = { 2, 5, 29, 18 }; + private static final int[] SubjectDirectoryAttributes_data = { 2, 5, 29, 9 }; + private static final int[] BasicConstraints_data = { 2, 5, 29, 19 }; + private static final int[] NameConstraints_data = { 2, 5, 29, 30 }; + private static final int[] PolicyConstraints_data = { 2, 5, 29, 36 }; + private static final int[] CRLDistributionPoints_data = { 2, 5, 29, 31 }; + private static final int[] CRLNumber_data = { 2, 5, 29, 20 }; + private static final int[] IssuingDistributionPoint_data = { 2, 5, 29, 28 }; + private static final int[] DeltaCRLIndicator_data = { 2, 5, 29, 27 }; + private static final int[] ReasonCode_data = { 2, 5, 29, 21 }; + private static final int[] HoldInstructionCode_data = { 2, 5, 29, 23 }; + private static final int[] InvalidityDate_data = { 2, 5, 29, 24 }; + private static final int[] ExtendedKeyUsage_data = { 2, 5, 29, 37 }; + private static final int[] InhibitAnyPolicy_data = { 2, 5, 29, 54 }; + private static final int[] CertificateIssuer_data = { 2, 5, 29, 29 }; + private static final int[] AuthInfoAccess_data = { 1, 3, 6, 1, 5, 5, 7, 1, 1}; + private static final int[] SubjectInfoAccess_data = { 1, 3, 6, 1, 5, 5, 7, 1, 11}; + private static final int[] FreshestCRL_data = { 2, 5, 29, 46 }; + private static final int[] OCSPNoCheck_data = { 1, 3, 6, 1, 5, 5, 7, 48, 1, 5}; /**
--- a/jdk/src/java.base/share/classes/sun/security/x509/X500Name.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/X500Name.java Thu Jul 09 16:37:55 2015 -0700 @@ -1119,25 +1119,25 @@ * Includes all those specified in RFC 5280 as MUST or SHOULD * be recognized */ - private static final int commonName_data[] = { 2, 5, 4, 3 }; - private static final int SURNAME_DATA[] = { 2, 5, 4, 4 }; - private static final int SERIALNUMBER_DATA[] = { 2, 5, 4, 5 }; - private static final int countryName_data[] = { 2, 5, 4, 6 }; - private static final int localityName_data[] = { 2, 5, 4, 7 }; - private static final int stateName_data[] = { 2, 5, 4, 8 }; - private static final int streetAddress_data[] = { 2, 5, 4, 9 }; - private static final int orgName_data[] = { 2, 5, 4, 10 }; - private static final int orgUnitName_data[] = { 2, 5, 4, 11 }; - private static final int title_data[] = { 2, 5, 4, 12 }; - private static final int GIVENNAME_DATA[] = { 2, 5, 4, 42 }; - private static final int INITIALS_DATA[] = { 2, 5, 4, 43 }; - private static final int GENERATIONQUALIFIER_DATA[] = { 2, 5, 4, 44 }; - private static final int DNQUALIFIER_DATA[] = { 2, 5, 4, 46 }; + private static final int[] commonName_data = { 2, 5, 4, 3 }; + private static final int[] SURNAME_DATA = { 2, 5, 4, 4 }; + private static final int[] SERIALNUMBER_DATA = { 2, 5, 4, 5 }; + private static final int[] countryName_data = { 2, 5, 4, 6 }; + private static final int[] localityName_data = { 2, 5, 4, 7 }; + private static final int[] stateName_data = { 2, 5, 4, 8 }; + private static final int[] streetAddress_data = { 2, 5, 4, 9 }; + private static final int[] orgName_data = { 2, 5, 4, 10 }; + private static final int[] orgUnitName_data = { 2, 5, 4, 11 }; + private static final int[] title_data = { 2, 5, 4, 12 }; + private static final int[] GIVENNAME_DATA = { 2, 5, 4, 42 }; + private static final int[] INITIALS_DATA = { 2, 5, 4, 43 }; + private static final int[] GENERATIONQUALIFIER_DATA = { 2, 5, 4, 44 }; + private static final int[] DNQUALIFIER_DATA = { 2, 5, 4, 46 }; - private static final int ipAddress_data[] = { 1, 3, 6, 1, 4, 1, 42, 2, 11, 2, 1 }; - private static final int DOMAIN_COMPONENT_DATA[] = + private static final int[] ipAddress_data = { 1, 3, 6, 1, 4, 1, 42, 2, 11, 2, 1 }; + private static final int[] DOMAIN_COMPONENT_DATA = { 0, 9, 2342, 19200300, 100, 1, 25 }; - private static final int userid_data[] = + private static final int[] userid_data = { 0, 9, 2342, 19200300, 100, 1, 1 };
--- a/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java Thu Jul 09 16:37:55 2015 -0700 @@ -1086,7 +1086,7 @@ throw new CRLException("Invalid DER-encoded CRL data"); signedCRL = val.toByteArray(); - DerValue seq[] = new DerValue[3]; + DerValue[] seq = new DerValue[3]; seq[0] = val.data.getDerValue(); seq[1] = val.data.getDerValue();
--- a/jdk/src/java.desktop/share/classes/sun/awt/util/IdentityArrayList.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.desktop/share/classes/sun/awt/util/IdentityArrayList.java Thu Jul 09 16:37:55 2015 -0700 @@ -142,7 +142,8 @@ public IdentityArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; - // c.toArray might (incorrectly) not return Object[] (see 6260652) + // defend against c.toArray (incorrectly) not returning Object[] + // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }
--- a/jdk/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java Thu Jul 09 16:37:55 2015 -0700 @@ -427,7 +427,7 @@ /* - public static void main(String args[]) throws Exception { + public static void main(String[] args) throws Exception { ServicePermission this_ = new ServicePermission(args[0], "accept"); ServicePermission that_ =
--- a/jdk/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java Thu Jul 09 16:37:55 2015 -0700 @@ -75,7 +75,7 @@ } GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, - int lifetime, Oid mechs[], int usage) + int lifetime, Oid[] mechs, int usage) throws GSSException { init(gssManager); boolean defaultList = false;
--- a/jdk/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java Thu Jul 09 16:37:55 2015 -0700 @@ -128,7 +128,7 @@ return new GSSNameImpl(this, nameStr, nameType); } - public GSSName createName(byte name[], Oid nameType) + public GSSName createName(byte[] name, Oid nameType) throws GSSException { return new GSSNameImpl(this, name, nameType); } @@ -138,7 +138,7 @@ return new GSSNameImpl(this, nameStr, nameType, mech); } - public GSSName createName(byte name[], Oid nameType, Oid mech) + public GSSName createName(byte[] name, Oid nameType, Oid mech) throws GSSException { return new GSSNameImpl(this, name, nameType, mech); } @@ -155,7 +155,7 @@ } public GSSCredential createCredential(GSSName aName, - int lifetime, Oid mechs[], int usage) + int lifetime, Oid[] mechs, int usage) throws GSSException { return wrap(new GSSCredentialImpl(this, aName, lifetime, mechs, usage)); }
--- a/jdk/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Thu Jul 09 16:37:55 2015 -0700 @@ -159,7 +159,7 @@ /** * Constructor for Krb5Context to import a previously exported context. */ - public Krb5Context(GSSCaller caller, byte [] interProcessToken) + public Krb5Context(GSSCaller caller, byte[] interProcessToken) throws GSSException { throw new GSSException(GSSException.UNAVAILABLE, -1, "GSS Import Context not available"); @@ -905,7 +905,7 @@ * and verifyMIC care about the remote sequence number (peerSeqNumber). */ - public final byte[] wrap(byte inBuf[], int offset, int len, + public final byte[] wrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException { if (DEBUG) { System.out.println("Krb5Context.wrap: data=[" @@ -943,7 +943,7 @@ } } - public final int wrap(byte inBuf[], int inOffset, int len, + public final int wrap(byte[] inBuf, int inOffset, int len, byte[] outBuf, int outOffset, MessageProp msgProp) throws GSSException { @@ -977,7 +977,7 @@ } } - public final void wrap(byte inBuf[], int offset, int len, + public final void wrap(byte[] inBuf, int offset, int len, OutputStream os, MessageProp msgProp) throws GSSException { @@ -1032,7 +1032,7 @@ wrap(data, 0, data.length, os, msgProp); } - public final byte[] unwrap(byte inBuf[], int offset, int len, + public final byte[] unwrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException { @@ -1069,7 +1069,7 @@ return data; } - public final int unwrap(byte inBuf[], int inOffset, int len, + public final int unwrap(byte[] inBuf, int inOffset, int len, byte[] outBuf, int outOffset, MessageProp msgProp) throws GSSException { @@ -1141,7 +1141,7 @@ } } - public final byte[] getMIC(byte []inMsg, int offset, int len, + public final byte[] getMIC(byte[] inMsg, int offset, int len, MessageProp msgProp) throws GSSException { @@ -1166,7 +1166,7 @@ } } - private int getMIC(byte []inMsg, int offset, int len, + private int getMIC(byte[] inMsg, int offset, int len, byte[] outBuf, int outOffset, MessageProp msgProp) throws GSSException { @@ -1236,7 +1236,7 @@ getMIC(data, 0, data.length, os, msgProp); } - public final void verifyMIC(byte []inTok, int tokOffset, int tokLen, + public final void verifyMIC(byte[] inTok, int tokOffset, int tokLen, byte[] inMsg, int msgOffset, int msgLen, MessageProp msgProp) throws GSSException { @@ -1293,7 +1293,7 @@ * @param os the output token will be written to this stream * @exception GSSException */ - public final byte [] export() throws GSSException { + public final byte[] export() throws GSSException { throw new GSSException(GSSException.UNAVAILABLE, -1, "GSS Export Context not available"); }
--- a/jdk/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSContextSpi.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSContextSpi.java Thu Jul 09 16:37:55 2015 -0700 @@ -265,7 +265,7 @@ /** * For apps that want simplicity and don't care about buffer copies. */ - public byte[] wrap(byte inBuf[], int offset, int len, + public byte[] wrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException; /** @@ -275,7 +275,7 @@ * * NOTE: This method is not defined in public class org.ietf.jgss.GSSContext * - public int wrap(byte inBuf[], int inOffset, int len, + public int wrap(byte[] inBuf, int inOffset, int len, byte[] outBuf, int outOffset, MessageProp msgProp) throws GSSException; @@ -292,7 +292,7 @@ * * NOTE: This method is not defined in public class org.ietf.jgss.GSSContext * - public void wrap(byte inBuf[], int offset, int len, + public void wrap(byte[] inBuf, int offset, int len, OutputStream os, MessageProp msgProp) throws GSSException; */ @@ -314,7 +314,7 @@ /** * For apps that want simplicity and don't care about buffer copies. */ - public byte[] unwrap(byte inBuf[], int offset, int len, + public byte[] unwrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException; /** @@ -324,7 +324,7 @@ * * NOTE: This method is not defined in public class org.ietf.jgss.GSSContext * - public int unwrap(byte inBuf[], int inOffset, int len, + public int unwrap(byte[] inBuf, int inOffset, int len, byte[] outBuf, int outOffset, MessageProp msgProp) throws GSSException; @@ -356,7 +356,7 @@ MessageProp msgProp) throws GSSException; - public byte[] getMIC(byte []inMsg, int offset, int len, + public byte[] getMIC(byte[] inMsg, int offset, int len, MessageProp msgProp) throws GSSException; /** @@ -372,7 +372,7 @@ public void verifyMIC(InputStream is, InputStream msgStr, MessageProp mProp) throws GSSException; - public void verifyMIC(byte []inTok, int tokOffset, int tokLen, + public void verifyMIC(byte[] inTok, int tokOffset, int tokLen, byte[] inMsg, int msgOffset, int msgLen, MessageProp msgProp) throws GSSException;
--- a/jdk/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java Thu Jul 09 16:37:55 2015 -0700 @@ -372,7 +372,7 @@ } return cStub.wrap(pContext, data, msgProp); } - public void wrap(byte inBuf[], int offset, int len, + public void wrap(byte[] inBuf, int offset, int len, OutputStream os, MessageProp msgProp) throws GSSException { try {
--- a/jdk/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java Thu Jul 09 16:37:55 2015 -0700 @@ -78,7 +78,7 @@ if (DEBUG) err.printStackTrace(); return null; } - String gssLibs[] = new String[0]; + String[] gssLibs = new String[0]; String defaultLib = System.getProperty(LIB_PROP); if (defaultLib == null || defaultLib.trim().equals("")) { String osname = System.getProperty("os.name");
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java Thu Jul 09 16:37:55 2015 -0700 @@ -568,7 +568,7 @@ temp.putInteger(bint); bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); temp = new DerOutputStream(); - DerValue der[] = new DerValue[nameStrings.length]; + DerValue[] der = new DerValue[nameStrings.length]; for (int i = 0; i < nameStrings.length; i++) { der[i] = new KerberosString(nameStrings[i]).toDerValue(); }
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/Authenticator.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/Authenticator.java Thu Jul 09 16:37:55 2015 -0700 @@ -198,7 +198,7 @@ if (authorizationData != null) { v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x08), authorizationData.asn1Encode())); } - DerValue der[] = new DerValue[v.size()]; + DerValue[] der = new DerValue[v.size()]; v.copyInto(der); temp = new DerOutputStream(); temp.putSequence(der);
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/AuthorizationData.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/AuthorizationData.java Thu Jul 09 16:37:55 2015 -0700 @@ -120,7 +120,7 @@ */ public byte[] asn1Encode() throws Asn1Exception, IOException { DerOutputStream bytes = new DerOutputStream(); - DerValue der[] = new DerValue[entry.length]; + DerValue[] der = new DerValue[entry.length]; for (int i = 0; i < entry.length; i++) { der[i] = new DerValue(entry[i].asn1Encode()); }
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/EncAPRepPart.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/EncAPRepPart.java Thu Jul 09 16:37:55 2015 -0700 @@ -151,7 +151,7 @@ v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x03), temp.toByteArray())); } - DerValue der[] = new DerValue[v.size()]; + DerValue[] der = new DerValue[v.size()]; v.copyInto(der); temp = new DerOutputStream(); temp.putSequence(der);
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/EncKrbCredPart.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/EncKrbCredPart.java Thu Jul 09 16:37:55 2015 -0700 @@ -129,7 +129,7 @@ subDer = der.getData().getDerValue(); if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x00) { - DerValue derValues[] = subDer.getData().getSequence(1); + DerValue[] derValues = subDer.getData().getSequence(1); ticketInfo = new KrbCredInfo[derValues.length]; for (int i = 0; i < derValues.length; i++) { ticketInfo[i] = new KrbCredInfo(derValues[i]);
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/HostAddresses.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/HostAddresses.java Thu Jul 09 16:37:55 2015 -0700 @@ -98,8 +98,8 @@ throw new KrbException(Krb5.KRB_ERR_GENERIC, "Bad name"); String host = components[1]; - InetAddress addr[] = InetAddress.getAllByName(host); - HostAddress hAddrs[] = new HostAddress[addr.length]; + InetAddress[] addr = InetAddress.getAllByName(host); + HostAddress[] hAddrs = new HostAddress[addr.length]; for (int i = 0; i < addr.length; i++) { hAddrs[i] = new HostAddress(addr[i]);
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/KDCReqBody.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/KDCReqBody.java Thu Jul 09 16:37:55 2015 -0700 @@ -269,7 +269,7 @@ ticketsTemp.write(DerValue.tag_SequenceOf, temp); v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0B), ticketsTemp.toByteArray())); } - DerValue der[] = new DerValue[v.size()]; + DerValue[] der = new DerValue[v.size()]; v.copyInto(der); temp = new DerOutputStream(); temp.putSequence(der);
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbCredInfo.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbCredInfo.java Thu Jul 09 16:37:55 2015 -0700 @@ -172,7 +172,7 @@ } if (caddr != null) v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0A), caddr.asn1Encode())); - DerValue der[] = new DerValue[v.size()]; + DerValue[] der = new DerValue[v.size()]; v.copyInto(der); DerOutputStream out = new DerOutputStream(); out.putSequence(der);
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/NetClient.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/NetClient.java Thu Jul 09 16:37:55 2015 -0700 @@ -200,7 +200,7 @@ @Override public byte[] receive() throws IOException { - byte ibuf[] = new byte[bufSize]; + byte[] ibuf = new byte[bufSize]; dgPacketIn = new DatagramPacket(ibuf, ibuf.length); try { dgSocket.receive(dgPacketIn);
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/Ticket.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/Ticket.java Thu Jul 09 16:37:55 2015 -0700 @@ -135,7 +135,7 @@ public byte[] asn1Encode() throws Asn1Exception, IOException { DerOutputStream bytes = new DerOutputStream(); DerOutputStream temp = new DerOutputStream(); - DerValue der[] = new DerValue[4]; + DerValue[] der = new DerValue[4]; temp.putInteger(BigInteger.valueOf(tkt_vno)); bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), sname.getRealm().asn1Encode());
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java Thu Jul 09 16:37:55 2015 -0700 @@ -357,7 +357,7 @@ if (DEBUG) { System.out.println(">>>DEBUG <CCacheInputStream> key type: " + key.getEType()); } - long times[] = readTimes(); + long[] times = readTimes(); KerberosTime authtime = new KerberosTime(times[0]); KerberosTime starttime = (times[1]==0) ? null : new KerberosTime(times[1]); @@ -374,9 +374,9 @@ ((renewTill==null)?"null":renewTill.toDate().toString())); } boolean skey = readskey(); - boolean flags[] = readFlags(); + boolean[] flags = readFlags(); TicketFlags tFlags = new TicketFlags(flags); - HostAddress addr[] = readAddr(); + HostAddress[] addr = readAddr(); HostAddresses addrs = null; if (addr != null) { addrs = new HostAddresses(addr);
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/crypto/crc32.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/crypto/crc32.java Thu Jul 09 16:37:55 2015 -0700 @@ -112,7 +112,7 @@ * This version is more efficient than the byte-at-a-time version; * it avoids data copies and reduces per-byte call overhead. */ - protected synchronized void engineUpdate(byte input[], int offset, + protected synchronized void engineUpdate(byte[] input, int offset, int len) { processData(input, offset, len); }
--- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java Thu Jul 09 16:37:55 2015 -0700 @@ -53,8 +53,8 @@ final class KerberosPreMasterSecret { private ProtocolVersion protocolVersion; // preMaster [0,1] - private byte preMaster[]; // 48 bytes - private byte encrypted[]; + private byte[] preMaster; // 48 bytes + private byte[] encrypted; /** * Constructor used by client to generate premaster secret.
--- a/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/ClientFactoryImpl.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/ClientFactoryImpl.java Thu Jul 09 16:37:55 2015 -0700 @@ -47,13 +47,13 @@ * @author Rosanna Lee */ final public class ClientFactoryImpl implements SaslClientFactory { - private static final String myMechs[] = { + private static final String[] myMechs = { "EXTERNAL", "CRAM-MD5", "PLAIN", }; - private static final int mechPolicies[] = { + private static final int[] mechPolicies = { // %%% RL: Policies should actually depend on the external channel PolicyUtils.NOPLAINTEXT|PolicyUtils.NOACTIVE|PolicyUtils.NODICTIONARY, PolicyUtils.NOPLAINTEXT|PolicyUtils.NOANONYMOUS, // CRAM-MD5
--- a/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/CramMD5Server.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/CramMD5Server.java Thu Jul 09 16:37:55 2015 -0700 @@ -165,7 +165,7 @@ PasswordCallback pcb = new PasswordCallback("CRAM-MD5 password: ", false); cbh.handle(new Callback[]{ncb,pcb}); - char pwChars[] = pcb.getPassword(); + char[] pwChars = pcb.getPassword(); if (pwChars == null || pwChars.length == 0) { // user has no password; OK to disclose to server aborted = true; @@ -190,7 +190,7 @@ clearPassword(); // Check whether digest is as expected - byte [] expectedDigest = digest.getBytes("UTF8"); + byte[] expectedDigest = digest.getBytes("UTF8"); int digestLen = responseData.length - ulen - 1; if (expectedDigest.length != digestLen) { aborted = true;
--- a/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/ServerFactoryImpl.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/ServerFactoryImpl.java Thu Jul 09 16:37:55 2015 -0700 @@ -41,11 +41,11 @@ * @author Rosanna Lee */ final public class ServerFactoryImpl implements SaslServerFactory { - private static final String myMechs[] = { + private static final String[] myMechs = { "CRAM-MD5", // }; - private static final int mechPolicies[] = { + private static final int[] mechPolicies = { PolicyUtils.NOPLAINTEXT|PolicyUtils.NOANONYMOUS, // CRAM-MD5 };
--- a/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/digest/DigestMD5Base.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/digest/DigestMD5Base.java Thu Jul 09 16:37:55 2015 -0700 @@ -272,7 +272,7 @@ */ /** This array maps the characters to their 6 bit values */ - private final static char pem_array[] = { + private final static char[] pem_array = { // 0 1 2 3 4 5 6 7 'A','B','C','D','E','F','G','H', // 0 'I','J','K','L','M','N','O','P', // 1 @@ -1068,7 +1068,7 @@ byte[] hMAC_MD5 = m.doFinal(); /* First 10 bytes of HMAC_MD5 digest */ - byte macBuffer[] = new byte[10]; + byte[] macBuffer = new byte[10]; System.arraycopy(hMAC_MD5, 0, macBuffer, 0, 10); return macBuffer;
--- a/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/digest/FactoryImpl.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/digest/FactoryImpl.java Thu Jul 09 16:37:55 2015 -0700 @@ -44,9 +44,9 @@ public final class FactoryImpl implements SaslClientFactory, SaslServerFactory{ - private static final String myMechs[] = { "DIGEST-MD5" }; + private static final String[] myMechs = { "DIGEST-MD5" }; private static final int DIGEST_MD5 = 0; - private static final int mechPolicies[] = { + private static final int[] mechPolicies = { PolicyUtils.NOPLAINTEXT|PolicyUtils.NOANONYMOUS}; /**
--- a/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/ntlm/FactoryImpl.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/java.security.sasl/share/classes/com/sun/security/sasl/ntlm/FactoryImpl.java Thu Jul 09 16:37:55 2015 -0700 @@ -43,8 +43,8 @@ public final class FactoryImpl implements SaslClientFactory, SaslServerFactory{ - private static final String myMechs[] = { "NTLM" }; - private static final int mechPolicies[] = { + private static final String[] myMechs = { "NTLM" }; + private static final int[] mechPolicies = { PolicyUtils.NOPLAINTEXT|PolicyUtils.NOANONYMOUS };
--- a/jdk/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/KeyStore.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/KeyStore.java Thu Jul 09 16:37:55 2015 -0700 @@ -66,7 +66,7 @@ class KeyEntry { private Key privateKey; - private X509Certificate certChain[]; + private X509Certificate[] certChain; private String alias; KeyEntry(Key key, X509Certificate[] chain) {
--- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Cipher.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Cipher.java Thu Jul 09 16:37:55 2015 -0700 @@ -175,7 +175,7 @@ this.algorithm = algorithm; this.mechanism = mechanism; - String algoParts[] = algorithm.split("/"); + String[] algoParts = algorithm.split("/"); if (algoParts[0].startsWith("AES")) { blockSize = 16;
--- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11KeyStore.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11KeyStore.java Thu Jul 09 16:37:55 2015 -0700 @@ -164,7 +164,7 @@ private X509Certificate cert = null; // chain - private X509Certificate chain[] = null; + private X509Certificate[] chain = null; // true if CKA_ID for private key and cert match up private boolean matched = false;
--- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/wrapper/CK_AES_CTR_PARAMS.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/wrapper/CK_AES_CTR_PARAMS.java Thu Jul 09 16:37:55 2015 -0700 @@ -42,7 +42,7 @@ public class CK_AES_CTR_PARAMS { private final long ulCounterBits; - private final byte cb[]; + private final byte[] cb; public CK_AES_CTR_PARAMS(byte[] cb) { ulCounterBits = 128;
--- a/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/UcryptoException.java Thu Jul 09 13:49:36 2015 -0700 +++ b/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/UcryptoException.java Thu Jul 09 16:37:55 2015 -0700 @@ -40,7 +40,7 @@ private static final long serialVersionUID = -933864511110035746L; // NOTE: check /usr/include/sys/crypto/common.h for updates - private static final String ERROR_MSG[] = { + private static final String[] ERROR_MSG = { "CRYPTO_SUCCESS", "CRYPTO_CANCEL", "CRYPTO_HOST_MEMORY",
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/NoInterruptUnixTerminal.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline; + +// Based on Apache Karaf impl + +/** + * Non-interruptible (via CTRL-C) {@link UnixTerminal}. + * + * @since 2.0 + */ +public class NoInterruptUnixTerminal + extends UnixTerminal +{ + public NoInterruptUnixTerminal() throws Exception { + super(); + } + + @Override + public void init() throws Exception { + super.init(); + getSettings().set("intr undef"); + } + + @Override + public void restore() throws Exception { + getSettings().set("intr ^C"); + super.restore(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/Terminal.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Representation of the input terminal for a platform. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public interface Terminal +{ + void init() throws Exception; + + void restore() throws Exception; + + void reset() throws Exception; + + boolean isSupported(); + + int getWidth(); + + int getHeight(); + + boolean isAnsiSupported(); + + /** + * When ANSI is not natively handled, the output will have to be wrapped. + */ + OutputStream wrapOutIfNeeded(OutputStream out); + + /** + * When using native support, return the InputStream to use for reading characters + * else return the input stream passed as a parameter. + * + * @since 2.6 + */ + InputStream wrapInIfNeeded(InputStream in) throws IOException; + + /** + * For terminals that don't wrap when character is written in last column, + * only when the next character is written. + * These are the ones that have 'am' and 'xn' termcap attributes (xterm and + * rxvt flavors falls under that category) + */ + boolean hasWeirdWrap(); + + boolean isEchoEnabled(); + + void setEchoEnabled(boolean enabled); + + String getOutputEncoding(); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/TerminalFactory.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; + +import jdk.internal.jline.internal.Configuration; +import jdk.internal.jline.internal.Log; +import jdk.internal.jline.internal.Preconditions; +import static jdk.internal.jline.internal.Preconditions.checkNotNull; + +/** + * Creates terminal instances. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public class TerminalFactory +{ + public static final String JLINE_TERMINAL = "jline.terminal"; + + public static final String AUTO = "auto"; + + public static final String UNIX = "unix"; + + public static final String WIN = "win"; + + public static final String WINDOWS = "windows"; + + public static final String NONE = "none"; + + public static final String OFF = "off"; + + public static final String FALSE = "false"; + + private static Terminal term = null; + + public static synchronized Terminal create() { + if (Log.TRACE) { + //noinspection ThrowableInstanceNeverThrown + Log.trace(new Throwable("CREATE MARKER")); + } + + String type = Configuration.getString(JLINE_TERMINAL, AUTO); + if ("dumb".equals(System.getenv("TERM"))) { + type = "none"; + Log.debug("$TERM=dumb; setting type=", type); + } + + Log.debug("Creating terminal; type=", type); + + Terminal t; + try { + String tmp = type.toLowerCase(); + + if (tmp.equals(UNIX)) { + t = getFlavor(Flavor.UNIX); + } + else if (tmp.equals(WIN) | tmp.equals(WINDOWS)) { + t = getFlavor(Flavor.WINDOWS); + } + else if (tmp.equals(NONE) || tmp.equals(OFF) || tmp.equals(FALSE)) { + t = new UnsupportedTerminal(); + } + else { + if (tmp.equals(AUTO)) { + String os = Configuration.getOsName(); + Flavor flavor = Flavor.UNIX; + if (os.contains(WINDOWS)) { + flavor = Flavor.WINDOWS; + } + t = getFlavor(flavor); + } + else { + try { + t = (Terminal) Thread.currentThread().getContextClassLoader().loadClass(type).newInstance(); + } + catch (Exception e) { + throw new IllegalArgumentException(MessageFormat.format("Invalid terminal type: {0}", type), e); + } + } + } + } + catch (Exception e) { + Log.error("Failed to construct terminal; falling back to unsupported", e); + t = new UnsupportedTerminal(); + } + + Log.debug("Created Terminal: ", t); + + try { + t.init(); + } + catch (Throwable e) { + Log.error("Terminal initialization failed; falling back to unsupported", e); + return new UnsupportedTerminal(); + } + + return t; + } + + public static synchronized void reset() { + term = null; + } + + public static synchronized void resetIf(final Terminal t) { + if(t == term) { + reset(); + } + } + + public static enum Type + { + AUTO, + WINDOWS, + UNIX, + NONE + } + + public static synchronized void configure(final String type) { + checkNotNull(type); + System.setProperty(JLINE_TERMINAL, type); + } + + public static synchronized void configure(final Type type) { + checkNotNull(type); + configure(type.name().toLowerCase()); + } + + // + // Flavor Support + // + + public static enum Flavor + { + WINDOWS, + UNIX + } + + private static final Map<Flavor, Callable<? extends Terminal>> FLAVORS = new HashMap<>(); + + static { +// registerFlavor(Flavor.WINDOWS, AnsiWindowsTerminal.class); +// registerFlavor(Flavor.UNIX, UnixTerminal.class); + registerFlavor(Flavor.WINDOWS, WindowsTerminal :: new); + registerFlavor(Flavor.UNIX, UnixTerminal :: new); + } + + public static synchronized Terminal get() { + if (term == null) { + term = create(); + } + return term; + } + + public static Terminal getFlavor(final Flavor flavor) throws Exception { + return FLAVORS.getOrDefault(flavor, () -> {throw new InternalError();}).call(); + } + + public static void registerFlavor(final Flavor flavor, final Callable<? extends Terminal> sup) { + FLAVORS.put(flavor, sup); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/TerminalSupport.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import jdk.internal.jline.internal.Log; +import jdk.internal.jline.internal.ShutdownHooks; +import jdk.internal.jline.internal.ShutdownHooks.Task; + +/** + * Provides support for {@link Terminal} instances. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public abstract class TerminalSupport + implements Terminal +{ + public static final int DEFAULT_WIDTH = 80; + + public static final int DEFAULT_HEIGHT = 24; + + private Task shutdownTask; + + private boolean supported; + + private boolean echoEnabled; + + private boolean ansiSupported; + + protected TerminalSupport(final boolean supported) { + this.supported = supported; + } + + public void init() throws Exception { + if (shutdownTask != null) { + ShutdownHooks.remove(shutdownTask); + } + // Register a task to restore the terminal on shutdown + this.shutdownTask = ShutdownHooks.add(new Task() + { + public void run() throws Exception { + restore(); + } + }); + } + + public void restore() throws Exception { + TerminalFactory.resetIf(this); + if (shutdownTask != null) { + ShutdownHooks.remove(shutdownTask); + shutdownTask = null; + } + } + + public void reset() throws Exception { + restore(); + init(); + } + + public final boolean isSupported() { + return supported; + } + + public synchronized boolean isAnsiSupported() { + return ansiSupported; + } + + protected synchronized void setAnsiSupported(final boolean supported) { + this.ansiSupported = supported; + Log.debug("Ansi supported: ", supported); + } + + /** + * Subclass to change behavior if needed. + * @return the passed out + */ + public OutputStream wrapOutIfNeeded(OutputStream out) { + return out; + } + + /** + * Defaults to true which was the behaviour before this method was added. + */ + public boolean hasWeirdWrap() { + return true; + } + + public int getWidth() { + return DEFAULT_WIDTH; + } + + public int getHeight() { + return DEFAULT_HEIGHT; + } + + public synchronized boolean isEchoEnabled() { + return echoEnabled; + } + + public synchronized void setEchoEnabled(final boolean enabled) { + this.echoEnabled = enabled; + Log.debug("Echo enabled: ", enabled); + } + + public InputStream wrapInIfNeeded(InputStream in) throws IOException { + return in; + } + + public String getOutputEncoding() { + // null for unknown + return null; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/UnixTerminal.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline; + +import jdk.internal.jline.internal.Log; +import jdk.internal.jline.internal.TerminalLineSettings; + +/** + * Terminal that is used for unix platforms. Terminal initialization + * is handled by issuing the <em>stty</em> command against the + * <em>/dev/tty</em> file to disable character echoing and enable + * character input. All known unix systems (including + * Linux and Macintosh OS X) support the <em>stty</em>), so this + * implementation should work for an reasonable POSIX system. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:dwkemp@gmail.com">Dale Kemp</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @author <a href="mailto:jbonofre@apache.org">Jean-Baptiste Onofr\u00E9</a> + * @since 2.0 + */ +public class UnixTerminal + extends TerminalSupport +{ + private final TerminalLineSettings settings = new TerminalLineSettings(); + + public UnixTerminal() throws Exception { + super(true); + } + + protected TerminalLineSettings getSettings() { + return settings; + } + + /** + * Remove line-buffered input by invoking "stty -icanon min 1" + * against the current terminal. + */ + @Override + public void init() throws Exception { + super.init(); + + setAnsiSupported(true); + + // Set the console to be character-buffered instead of line-buffered. + // Make sure we're distinguishing carriage return from newline. + // Allow ctrl-s keypress to be used (as forward search) + settings.set("-icanon min 1 -icrnl -inlcr -ixon"); + settings.set("dsusp undef"); + + setEchoEnabled(false); + } + + /** + * Restore the original terminal configuration, which can be used when + * shutting down the console reader. The ConsoleReader cannot be + * used after calling this method. + */ + @Override + public void restore() throws Exception { + settings.restore(); + super.restore(); + } + + /** + * Returns the value of <tt>stty columns</tt> param. + */ + @Override + public int getWidth() { + int w = settings.getProperty("columns"); + return w < 1 ? DEFAULT_WIDTH : w; + } + + /** + * Returns the value of <tt>stty rows>/tt> param. + */ + @Override + public int getHeight() { + int h = settings.getProperty("rows"); + return h < 1 ? DEFAULT_HEIGHT : h; + } + + @Override + public synchronized void setEchoEnabled(final boolean enabled) { + try { + if (enabled) { + settings.set("echo"); + } + else { + settings.set("-echo"); + } + super.setEchoEnabled(enabled); + } + catch (Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + Log.error("Failed to ", (enabled ? "enable" : "disable"), " echo", e); + } + } + + public void disableInterruptCharacter() + { + try { + settings.set("intr undef"); + } + catch (Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + Log.error("Failed to disable interrupt character", e); + } + } + + public void enableInterruptCharacter() + { + try { + settings.set("intr ^C"); + } + catch (Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + Log.error("Failed to enable interrupt character", e); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/UnsupportedTerminal.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline; + +/** + * An unsupported terminal. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public class UnsupportedTerminal + extends TerminalSupport +{ + public UnsupportedTerminal() { + super(false); + setAnsiSupported(false); + setEchoEnabled(true); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/WindowsTerminal.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import jdk.internal.jline.internal.Configuration; +import jdk.internal.jline.internal.Log; +//import org.fusesource.jansi.internal.WindowsSupport; +//import org.fusesource.jansi.internal.Kernel32; +//import static org.fusesource.jansi.internal.Kernel32.*; + +import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_ECHO_INPUT; +import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_LINE_INPUT; +import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_PROCESSED_INPUT; +import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_WINDOW_INPUT; + +/** + * Terminal implementation for Microsoft Windows. Terminal initialization in + * {@link #init} is accomplished by extracting the + * <em>jline_<i>version</i>.dll</em>, saving it to the system temporary + * directoy (determined by the setting of the <em>java.io.tmpdir</em> System + * property), loading the library, and then calling the Win32 APIs <a + * href="http://msdn.microsoft.com/library/default.asp? + * url=/library/en-us/dllproc/base/setconsolemode.asp">SetConsoleMode</a> and + * <a href="http://msdn.microsoft.com/library/default.asp? + * url=/library/en-us/dllproc/base/getconsolemode.asp">GetConsoleMode</a> to + * disable character echoing. + * <p/> + * <p> + * By default, the {@link #wrapInIfNeeded(java.io.InputStream)} method will attempt + * to test to see if the specified {@link InputStream} is {@link System#in} or a wrapper + * around {@link FileDescriptor#in}, and if so, will bypass the character reading to + * directly invoke the readc() method in the JNI library. This is so the class + * can read special keys (like arrow keys) which are otherwise inaccessible via + * the {@link System#in} stream. Using JNI reading can be bypassed by setting + * the <code>jline.WindowsTerminal.directConsole</code> system property + * to <code>false</code>. + * </p> + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public class WindowsTerminal + extends TerminalSupport +{ + public static final String DIRECT_CONSOLE = WindowsTerminal.class.getName() + ".directConsole"; + + public static final String ANSI = WindowsTerminal.class.getName() + ".ansi"; + + private boolean directConsole; + + private int originalMode; + + public WindowsTerminal() throws Exception { + super(true); + } + + @Override + public void init() throws Exception { + super.init(); + +// setAnsiSupported(Configuration.getBoolean(ANSI, true)); + setAnsiSupported(false); + + // + // FIXME: Need a way to disable direct console and sysin detection muck + // + + setDirectConsole(Configuration.getBoolean(DIRECT_CONSOLE, true)); + + this.originalMode = getConsoleMode(); + setConsoleMode(originalMode & ~ENABLE_ECHO_INPUT.code); + setEchoEnabled(false); + } + + /** + * Restore the original terminal configuration, which can be used when + * shutting down the console reader. The ConsoleReader cannot be + * used after calling this method. + */ + @Override + public void restore() throws Exception { + // restore the old console mode + setConsoleMode(originalMode); + super.restore(); + } + + @Override + public int getWidth() { + int w = getWindowsTerminalWidth(); + return w < 1 ? DEFAULT_WIDTH : w; + } + + @Override + public int getHeight() { + int h = getWindowsTerminalHeight(); + return h < 1 ? DEFAULT_HEIGHT : h; + } + + @Override + public void setEchoEnabled(final boolean enabled) { + // Must set these four modes at the same time to make it work fine. + if (enabled) { + setConsoleMode(getConsoleMode() | + ENABLE_ECHO_INPUT.code | + ENABLE_LINE_INPUT.code | + ENABLE_PROCESSED_INPUT.code | + ENABLE_WINDOW_INPUT.code); + } + else { + setConsoleMode(getConsoleMode() & + ~(ENABLE_LINE_INPUT.code | + ENABLE_ECHO_INPUT.code | + ENABLE_PROCESSED_INPUT.code | + ENABLE_WINDOW_INPUT.code)); + } + super.setEchoEnabled(enabled); + } + + /** + * Whether or not to allow the use of the JNI console interaction. + */ + public void setDirectConsole(final boolean flag) { + this.directConsole = flag; + Log.debug("Direct console: ", flag); + } + + /** + * Whether or not to allow the use of the JNI console interaction. + */ + public Boolean getDirectConsole() { + return directConsole; + } + + + @Override + public InputStream wrapInIfNeeded(InputStream in) throws IOException { + if (directConsole && isSystemIn(in)) { + return new InputStream() { + private byte[] buf = null; + int bufIdx = 0; + + @Override + public int read() throws IOException { + while (buf == null || bufIdx == buf.length) { + buf = readConsoleInput(); + bufIdx = 0; + } + int c = buf[bufIdx] & 0xFF; + bufIdx++; + return c; + } + }; + } else { + return super.wrapInIfNeeded(in); + } + } + + protected boolean isSystemIn(final InputStream in) throws IOException { + if (in == null) { + return false; + } + else if (in == System.in) { + return true; + } + else if (in instanceof FileInputStream && ((FileInputStream) in).getFD() == FileDescriptor.in) { + return true; + } + + return false; + } + + @Override + public String getOutputEncoding() { + int codepage = getConsoleOutputCodepage(); + //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; + } + Log.debug("can't figure out the Java Charset of this code page (" + codepage + ")..."); + return super.getOutputEncoding(); + } + + // + // Original code: + // +// private int getConsoleMode() { +// return WindowsSupport.getConsoleMode(); +// } +// +// private void setConsoleMode(int mode) { +// WindowsSupport.setConsoleMode(mode); +// } +// +// private byte[] readConsoleInput() { +// // XXX does how many events to read in one call matter? +// INPUT_RECORD[] events = null; +// try { +// events = WindowsSupport.readConsoleInput(1); +// } catch (IOException e) { +// Log.debug("read Windows console input error: ", e); +// } +// if (events == null) { +// return new byte[0]; +// } +// StringBuilder sb = new StringBuilder(); +// for (int i = 0; i < events.length; i++ ) { +// KEY_EVENT_RECORD keyEvent = events[i].keyEvent; +// //Log.trace(keyEvent.keyDown? "KEY_DOWN" : "KEY_UP", "key code:", keyEvent.keyCode, "char:", (long)keyEvent.uchar); +// if (keyEvent.keyDown) { +// if (keyEvent.uchar > 0) { +// // support some C1 control sequences: ALT + [@-_] (and [a-z]?) => ESC <ascii> +// // 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; +// if (((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z')) +// && ((keyEvent.controlKeyState & altState) != 0) && ((keyEvent.controlKeyState & ctrlState) == 0)) { +// sb.append('\u001B'); // ESC +// } +// +// sb.append(keyEvent.uchar); +// continue; +// } +// // virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx +// // just add support for basic editing keys (no control state, no numpad keys) +// String escapeSequence = null; +// switch (keyEvent.keyCode) { +// case 0x21: // VK_PRIOR PageUp +// escapeSequence = "\u001B[5~"; +// break; +// case 0x22: // VK_NEXT PageDown +// escapeSequence = "\u001B[6~"; +// break; +// case 0x23: // VK_END +// escapeSequence = "\u001B[4~"; +// break; +// case 0x24: // VK_HOME +// escapeSequence = "\u001B[1~"; +// break; +// case 0x25: // VK_LEFT +// escapeSequence = "\u001B[D"; +// break; +// case 0x26: // VK_UP +// escapeSequence = "\u001B[A"; +// break; +// case 0x27: // VK_RIGHT +// escapeSequence = "\u001B[C"; +// break; +// case 0x28: // VK_DOWN +// escapeSequence = "\u001B[B"; +// break; +// case 0x2D: // VK_INSERT +// escapeSequence = "\u001B[2~"; +// break; +// case 0x2E: // VK_DELETE +// escapeSequence = "\u001B[3~"; +// break; +// default: +// break; +// } +// if (escapeSequence != null) { +// for (int k = 0; k < keyEvent.repeatCount; k++) { +// 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(); +// } +// +// private int getConsoleOutputCodepage() { +// return Kernel32.GetConsoleOutputCP(); +// } +// +// private int getWindowsTerminalWidth() { +// return WindowsSupport.getWindowsTerminalWidth(); +// } +// +// private int getWindowsTerminalHeight() { +// return WindowsSupport.getWindowsTerminalHeight(); +// } + + // + // Native Bits + // + static { + System.loadLibrary("le"); + initIDs(); + } + + private static native void initIDs(); + + private native int getConsoleMode(); + + private native void setConsoleMode(int mode); + + private byte[] readConsoleInput() { + KEY_EVENT_RECORD keyEvent = readKeyEvent(); + + return convertKeys(keyEvent).getBytes(); + } + + public static String convertKeys(KEY_EVENT_RECORD keyEvent) { + if (keyEvent == null) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + + if (keyEvent.keyDown) { + if (keyEvent.uchar > 0) { + // support some C1 control sequences: ALT + [@-_] (and [a-z]?) => ESC <ascii> + // http://en.wikipedia.org/wiki/C0_and_C1_control_codes#C1_set + final int altState = KEY_EVENT_RECORD.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.CTRL_PRESSED; + + boolean handled = false; + + if ((keyEvent.controlKeyState & ctrlState) != 0) { + switch (keyEvent.keyCode) { + case 0x43: //Ctrl-C + sb.append("\003"); + handled = true; + break; + } + } + + if ((keyEvent.controlKeyState & KEY_EVENT_RECORD.SHIFT_PRESSED) != 0) { + switch (keyEvent.keyCode) { + case 0x09: //Shift-Tab + sb.append("\033\133\132"); + handled = true; + break; + } + } + + if (!handled) { + if (((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z')) + && ((keyEvent.controlKeyState & altState) != 0) && ((keyEvent.controlKeyState & ctrlState) == 0)) { + sb.append('\u001B'); // ESC + } + + sb.append(keyEvent.uchar); + } + } else { + // virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx + // just add support for basic editing keys (no control state, no numpad keys) + String escapeSequence = null; + switch (keyEvent.keyCode) { + case 0x21: // VK_PRIOR PageUp + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[5~", "\u001B[5;%d~"); + break; + case 0x22: // VK_NEXT PageDown + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[6~", "\u001B[6;%d~"); + break; + case 0x23: // VK_END + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[4~", "\u001B[4;%d~"); + break; + case 0x24: // VK_HOME + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[1~", "\u001B[1;%d~"); + break; + case 0x25: // VK_LEFT + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[D", "\u001B[1;%dD"); + break; + case 0x26: // VK_UP + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[A", "\u001B[1;%dA"); + break; + case 0x27: // VK_RIGHT + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[C", "\u001B[1;%dC"); + break; + case 0x28: // VK_DOWN + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[B", "\u001B[1;%dB"); + break; + case 0x2D: // VK_INSERT + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[2~", "\u001B[2;%d~"); + break; + case 0x2E: // VK_DELETE + escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[3~", "\u001B[3;%d~"); + break; + default: + break; + } + if (escapeSequence != null) { + for (int k = 0; k < keyEvent.repeatCount; k++) { + 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(); + } + + private static String escapeSequence(int controlKeyState, String noControlSequence, String withControlSequence) { + int controlNum = 1; + + if ((controlKeyState & KEY_EVENT_RECORD.SHIFT_PRESSED) != 0) { + controlNum += 1; + } + + if ((controlKeyState & KEY_EVENT_RECORD.ALT_PRESSED) != 0) { + controlNum += 2; + } + + if ((controlKeyState & KEY_EVENT_RECORD.CTRL_PRESSED) != 0) { + controlNum += 4; + } + + if (controlNum > 1) { + return String.format(withControlSequence, controlNum); + } else { + return noControlSequence; + } + } + + private native KEY_EVENT_RECORD readKeyEvent(); + + public static class KEY_EVENT_RECORD { + public final static int ALT_PRESSED = 0x3; + public final static int CTRL_PRESSED = 0xC; + public final static int SHIFT_PRESSED = 0x10; + public final boolean keyDown; + public final char uchar; + public final int controlKeyState; + public final int keyCode; + public final int repeatCount; + + public KEY_EVENT_RECORD(boolean keyDown, char uchar, int controlKeyState, int keyCode, int repeatCount) { + this.keyDown = keyDown; + this.uchar = uchar; + this.controlKeyState = controlKeyState; + this.keyCode = keyCode; + this.repeatCount = repeatCount; + } + + } + + private native int getConsoleOutputCodepage(); + + private native int getWindowsTerminalWidth(); + + private native int getWindowsTerminalHeight(); + + /** + * Console mode + * <p/> + * Constants copied <tt>wincon.h</tt>. + */ + public static enum ConsoleMode + { + /** + * The ReadFile or ReadConsole function returns only when a carriage return + * character is read. If this mode is disable, the functions return when one + * or more characters are available. + */ + ENABLE_LINE_INPUT(2), + + /** + * Characters read by the ReadFile or ReadConsole function are written to + * the active screen buffer as they are read. This mode can be used only if + * the ENABLE_LINE_INPUT mode is also enabled. + */ + ENABLE_ECHO_INPUT(4), + + /** + * CTRL+C is processed by the system and is not placed in the input buffer. + * If the input buffer is being read by ReadFile or ReadConsole, other + * control keys are processed by the system and are not returned in the + * ReadFile or ReadConsole buffer. If the ENABLE_LINE_INPUT mode is also + * enabled, backspace, carriage return, and linefeed characters are handled + * by the system. + */ + ENABLE_PROCESSED_INPUT(1), + + /** + * User interactions that change the size of the console screen buffer are + * reported in the console's input buffee. Information about these events + * can be read from the input buffer by applications using + * theReadConsoleInput function, but not by those using ReadFile + * orReadConsole. + */ + ENABLE_WINDOW_INPUT(8), + + /** + * If the mouse pointer is within the borders of the console window and the + * window has the keyboard focus, mouse events generated by mouse movement + * and button presses are placed in the input buffer. These events are + * discarded by ReadFile or ReadConsole, even when this mode is enabled. + */ + ENABLE_MOUSE_INPUT(16), + + /** + * When enabled, text entered in a console window will be inserted at the + * current cursor location and all text following that location will not be + * overwritten. When disabled, all following text will be overwritten. An OR + * operation must be performed with this flag and the ENABLE_EXTENDED_FLAGS + * flag to enable this functionality. + */ + ENABLE_PROCESSED_OUTPUT(1), + + /** + * This flag enables the user to use the mouse to select and edit text. To + * enable this option, use the OR to combine this flag with + * ENABLE_EXTENDED_FLAGS. + */ + ENABLE_WRAP_AT_EOL_OUTPUT(2),; + + public final int code; + + ConsoleMode(final int code) { + this.code = code; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/ConsoleKeys.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline.console; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.internal.jline.internal.Log; + +/** + * @author St\u00E5le W. Pedersen <stale.pedersen@jboss.org> + */ +public class ConsoleKeys { + + private KeyMap keys; + + private Map<String, KeyMap> keyMaps; + private Map<String, String> variables = new HashMap<String,String>(); + + public ConsoleKeys(String appName, URL inputrcUrl) { + keyMaps = KeyMap.keyMaps(); + loadKeys(appName, inputrcUrl); + } + + protected boolean isViEditMode() { + return keys.isViKeyMap(); + } + + protected boolean setKeyMap (String name) { + KeyMap map = keyMaps.get(name); + if (map == null) { + return false; + } + this.keys = map; + return true; + } + + protected Map<String, KeyMap> getKeyMaps() { + return keyMaps; + } + + protected KeyMap getKeys() { + return keys; + } + + protected void setKeys(KeyMap keys) { + this.keys = keys; + } + + protected boolean getViEditMode() { + return keys.isViKeyMap (); + } + + protected void loadKeys(String appName, URL inputrcUrl) { + keys = keyMaps.get(KeyMap.EMACS); + + try { + InputStream input = inputrcUrl.openStream(); + try { + loadKeys(input, appName); + Log.debug("Loaded user configuration: ", inputrcUrl); + } + finally { + try { + input.close(); + } catch (IOException e) { + // Ignore + } + } + } + catch (IOException e) { + if (inputrcUrl.getProtocol().equals("file")) { + File file = new File(inputrcUrl.getPath()); + if (file.exists()) { + Log.warn("Unable to read user configuration: ", inputrcUrl, e); + } + } else { + Log.warn("Unable to read user configuration: ", inputrcUrl, e); + } + } + } + + private void loadKeys(InputStream input, String appName) throws IOException { + BufferedReader reader = new BufferedReader( new java.io.InputStreamReader( input ) ); + String line; + boolean parsing = true; + List<Boolean> ifsStack = new ArrayList<Boolean>(); + while ( (line = reader.readLine()) != null ) { + try { + line = line.trim(); + if (line.length() == 0) { + continue; + } + if (line.charAt(0) == '#') { + continue; + } + int i = 0; + if (line.charAt(i) == '$') { + String cmd; + String args; + for (++i; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); + int s = i; + for (; i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t'); i++); + cmd = line.substring(s, i); + for (; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); + s = i; + for (; i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t'); i++); + args = line.substring(s, i); + if ("if".equalsIgnoreCase(cmd)) { + ifsStack.add( parsing ); + if (!parsing) { + continue; + } + if (args.startsWith("term=")) { + // TODO + } else if (args.startsWith("mode=")) { + if (args.equalsIgnoreCase("mode=vi")) { + parsing = isViEditMode(); + } else if (args.equals("mode=emacs")) { + parsing = !isViEditMode(); + } else { + parsing = false; + } + } else { + parsing = args.equalsIgnoreCase(appName); + } + } else if ("else".equalsIgnoreCase(cmd)) { + if (ifsStack.isEmpty()) { + throw new IllegalArgumentException("$else found without matching $if"); + } + boolean invert = true; + for (boolean b : ifsStack) { + if (!b) { + invert = false; + break; + } + } + if (invert) { + parsing = !parsing; + } + } else if ("endif".equalsIgnoreCase(cmd)) { + if (ifsStack.isEmpty()) { + throw new IllegalArgumentException("endif found without matching $if"); + } + parsing = ifsStack.remove( ifsStack.size() - 1 ); + } else if ("include".equalsIgnoreCase(cmd)) { + // TODO + } + continue; + } + if (!parsing) { + continue; + } + boolean equivalency; + String keySeq = ""; + if (line.charAt(i++) == '"') { + boolean esc = false; + for (;; i++) { + if (i >= line.length()) { + throw new IllegalArgumentException("Missing closing quote on line '" + line + "'"); + } + if (esc) { + esc = false; + } else if (line.charAt(i) == '\\') { + esc = true; + } else if (line.charAt(i) == '"') { + break; + } + } + } + for (; i < line.length() && line.charAt(i) != ':' + && line.charAt(i) != ' ' && line.charAt(i) != '\t' + ; i++); + keySeq = line.substring(0, i); + equivalency = (i + 1 < line.length() && line.charAt(i) == ':' && line.charAt(i + 1) == '='); + i++; + if (equivalency) { + i++; + } + if (keySeq.equalsIgnoreCase("set")) { + String key; + String val; + for (; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); + int s = i; + for (; i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t'); i++); + key = line.substring( s, i ); + for (; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); + s = i; + for (; i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t'); i++); + val = line.substring( s, i ); + setVar( key, val ); + } else { + for (; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); + int start = i; + if (i < line.length() && (line.charAt(i) == '\'' || line.charAt(i) == '\"')) { + char delim = line.charAt(i++); + boolean esc = false; + for (;; i++) { + if (i >= line.length()) { + break; + } + if (esc) { + esc = false; + } else if (line.charAt(i) == '\\') { + esc = true; + } else if (line.charAt(i) == delim) { + break; + } + } + } + for (; i < line.length() && line.charAt(i) != ' ' && line.charAt(i) != '\t'; i++); + String val = line.substring(Math.min(start, line.length()), Math.min(i, line.length())); + if (keySeq.charAt(0) == '"') { + keySeq = translateQuoted(keySeq); + } else { + // Bind key name + String keyName = keySeq.lastIndexOf('-') > 0 ? keySeq.substring( keySeq.lastIndexOf('-') + 1 ) : keySeq; + char key = getKeyFromName(keyName); + keyName = keySeq.toLowerCase(); + keySeq = ""; + if (keyName.contains("meta-") || keyName.contains("m-")) { + keySeq += "\u001b"; + } + if (keyName.contains("control-") || keyName.contains("c-") || keyName.contains("ctrl-")) { + key = (char)(Character.toUpperCase( key ) & 0x1f); + } + keySeq += key; + } + if (val.length() > 0 && (val.charAt(0) == '\'' || val.charAt(0) == '\"')) { + keys.bind( keySeq, translateQuoted(val) ); + } else { + String operationName = val.replace('-', '_').toUpperCase(); + try { + keys.bind(keySeq, Operation.valueOf(operationName)); + } catch(IllegalArgumentException e) { + Log.info("Unable to bind key for unsupported operation: ", val); + } + } + } + } catch (IllegalArgumentException e) { + Log.warn("Unable to parse user configuration: ", e); + } + } + } + + private String translateQuoted(String keySeq) { + int i; + String str = keySeq.substring( 1, keySeq.length() - 1 ); + keySeq = ""; + for (i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == '\\') { + boolean ctrl = str.regionMatches(i, "\\C-", 0, 3)|| str.regionMatches(i, "\\M-\\C-", 0, 6); + boolean meta = str.regionMatches(i, "\\M-", 0, 3)|| str.regionMatches(i, "\\C-\\M-", 0, 6); + i += (meta ? 3 : 0) + (ctrl ? 3 : 0) + (!meta && !ctrl ? 1 : 0); + if (i >= str.length()) { + break; + } + c = str.charAt(i); + if (meta) { + keySeq += "\u001b"; + } + if (ctrl) { + c = c == '?' ? 0x7f : (char)(Character.toUpperCase( c ) & 0x1f); + } + if (!meta && !ctrl) { + switch (c) { + case 'a': c = 0x07; break; + case 'b': c = '\b'; break; + case 'd': c = 0x7f; break; + 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); + } + 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); + } + 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; + } + } + keySeq += c; + } else { + keySeq += c; + } + } + return keySeq; + } + + private char getKeyFromName(String name) { + if ("DEL".equalsIgnoreCase(name) || "Rubout".equalsIgnoreCase(name)) { + return 0x7f; + } else if ("ESC".equalsIgnoreCase(name) || "Escape".equalsIgnoreCase(name)) { + return '\033'; + } else if ("LFD".equalsIgnoreCase(name) || "NewLine".equalsIgnoreCase(name)) { + return '\n'; + } else if ("RET".equalsIgnoreCase(name) || "Return".equalsIgnoreCase(name)) { + return '\r'; + } else if ("SPC".equalsIgnoreCase(name) || "Space".equalsIgnoreCase(name)) { + return ' '; + } else if ("Tab".equalsIgnoreCase(name)) { + return '\t'; + } else { + return name.charAt(0); + } + } + + private void setVar(String key, String val) { + if ("keymap".equalsIgnoreCase(key)) { + if (keyMaps.containsKey(val)) { + keys = keyMaps.get(val); + } + } else if ("editing-mode".equals(key)) { + if ("vi".equalsIgnoreCase(val)) { + keys = keyMaps.get(KeyMap.VI_INSERT); + } else if ("emacs".equalsIgnoreCase(key)) { + keys = keyMaps.get(KeyMap.EMACS); + } + } else if ("blink-matching-paren".equals(key)) { + if ("on".equalsIgnoreCase(val)) { + keys.setBlinkMatchingParen(true); + } else if ("off".equalsIgnoreCase(val)) { + keys.setBlinkMatchingParen(false); + } + } + + /* + * Technically variables should be defined as a functor class + * so that validation on the variable value can be done at parse + * time. This is a stop-gap. + */ + variables.put(key, val); + } + + /** + * Retrieves the value of a variable that was set in the .inputrc file + * during processing + * @param var The variable name + * @return The variable value. + */ + public String getVariable(String var) { + return variables.get (var); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/ConsoleReader.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,4006 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline.console; + +//import java.awt.*; +//import java.awt.datatransfer.Clipboard; +//import java.awt.datatransfer.DataFlavor; +//import java.awt.datatransfer.Transferable; +//import java.awt.datatransfer.UnsupportedFlavorException; +//import java.awt.event.ActionListener; +//import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +//import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +//import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +//import java.util.Map; +import java.util.ResourceBundle; +import java.util.Stack; +import java.util.regex.Pattern; + +import jdk.internal.jline.Terminal; +import jdk.internal.jline.TerminalFactory; +import jdk.internal.jline.UnixTerminal; +import jdk.internal.jline.console.completer.CandidateListCompletionHandler; +import jdk.internal.jline.console.completer.Completer; +import jdk.internal.jline.console.completer.CompletionHandler; +import jdk.internal.jline.console.history.History; +import jdk.internal.jline.console.history.MemoryHistory; +import jdk.internal.jline.internal.Configuration; +import jdk.internal.jline.internal.InputStreamReader; +import jdk.internal.jline.internal.Log; +import jdk.internal.jline.internal.NonBlockingInputStream; +import jdk.internal.jline.internal.Nullable; +import jdk.internal.jline.internal.Urls; +//import org.fusesource.jansi.AnsiOutputStream; + +import static jdk.internal.jline.internal.Preconditions.checkNotNull; + +/** + * A reader for console applications. It supports custom tab-completion, + * saveable command history, and command line editing. On some platforms, + * platform-specific commands will need to be issued before the reader will + * function properly. See {@link jline.Terminal#init} for convenience + * methods for issuing platform-specific setup commands. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a> + */ +public class ConsoleReader +{ + public static final String JLINE_NOBELL = "jline.nobell"; + + public static final String JLINE_ESC_TIMEOUT = "jline.esc.timeout"; + + public static final String JLINE_INPUTRC = "jline.inputrc"; + + public static final String INPUT_RC = ".inputrc"; + + public static final String DEFAULT_INPUT_RC = "/etc/inputrc"; + + public static final char BACKSPACE = '\b'; + + public static final char RESET_LINE = '\r'; + + public static final char KEYBOARD_BELL = '\07'; + + public static final char NULL_MASK = 0; + + public static final int TAB_WIDTH = 4; + + private static final ResourceBundle + resources = ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName()); + + private final Terminal terminal; + + private final Writer out; + + private final CursorBuffer buf = new CursorBuffer(); + + private String prompt; + private int promptLen; + + private boolean expandEvents = true; + + private boolean bellEnabled = !Configuration.getBoolean(JLINE_NOBELL, true); + + private boolean handleUserInterrupt = false; + + private Character mask; + + private Character echoCharacter; + + private StringBuffer searchTerm = null; + + private String previousSearchTerm = ""; + + private int searchIndex = -1; + + private int parenBlinkTimeout = 500; + + /* + * The reader and the nonBlockingInput go hand-in-hand. The reader wraps + * the nonBlockingInput, but we have to retain a handle to it so that + * we can shut down its blocking read thread when we go away. + */ + private NonBlockingInputStream in; + private long escapeTimeout; + private Reader reader; + + /* + * TODO: Please read the comments about this in setInput(), but this needs + * to be done away with. + */ + private boolean isUnitTestInput; + + /** + * Last character searched for with a vi character search + */ + private char charSearchChar = 0; // Character to search for + private char charSearchLastInvokeChar = 0; // Most recent invocation key + private char charSearchFirstInvokeChar = 0;// First character that invoked + + /** + * The vi yank buffer + */ + private String yankBuffer = ""; + + private KillRing killRing = new KillRing(); + + private String encoding; + + private boolean recording; + + private String macro = ""; + + private String appName; + + private URL inputrcUrl; + + private ConsoleKeys consoleKeys; + + private String commentBegin = null; + + private boolean skipLF = false; + + /** + * Set to true if the reader should attempt to detect copy-n-paste. The + * effect of this that an attempt is made to detect if tab is quickly + * followed by another character, then it is assumed that the tab was + * a literal tab as part of a copy-and-paste operation and is inserted as + * such. + */ + private boolean copyPasteDetection = false; + + /* + * Current internal state of the line reader + */ + private State state = State.NORMAL; + + /** + * Possible states in which the current readline operation may be in. + */ + private static enum State { + /** + * The user is just typing away + */ + NORMAL, + /** + * In the middle of a emacs seach + */ + SEARCH, + FORWARD_SEARCH, + /** + * VI "yank-to" operation ("y" during move mode) + */ + VI_YANK_TO, + /** + * VI "delete-to" operation ("d" during move mode) + */ + VI_DELETE_TO, + /** + * VI "change-to" operation ("c" during move mode) + */ + VI_CHANGE_TO + } + + public ConsoleReader() throws IOException { + this(null, new FileInputStream(FileDescriptor.in), System.out, null); + } + + public ConsoleReader(final InputStream in, final OutputStream out) throws IOException { + this(null, in, out, null); + } + + public ConsoleReader(final InputStream in, final OutputStream out, final Terminal term) throws IOException { + this(null, in, out, term); + } + + public ConsoleReader(final @Nullable String appName, final InputStream in, final OutputStream out, final @Nullable Terminal term) throws IOException { + this(appName, in, out, term, null); + } + + public ConsoleReader(final @Nullable String appName, final InputStream in, final OutputStream out, final @Nullable Terminal term, final @Nullable String encoding) + throws IOException + { + this.appName = appName != null ? appName : "JLine"; + this.encoding = encoding != null ? encoding : Configuration.getEncoding(); + this.terminal = term != null ? term : TerminalFactory.get(); + String outEncoding = terminal.getOutputEncoding() != null? terminal.getOutputEncoding() : this.encoding; + this.out = new OutputStreamWriter(terminal.wrapOutIfNeeded(out), outEncoding); + setInput( in ); + + this.inputrcUrl = getInputRc(); + + consoleKeys = new ConsoleKeys(this.appName, inputrcUrl); + } + + private URL getInputRc() throws IOException { + String path = Configuration.getString(JLINE_INPUTRC); + if (path == null) { + File f = new File(Configuration.getUserHome(), INPUT_RC); + if (!f.exists()) { + f = new File(DEFAULT_INPUT_RC); + } + return f.toURI().toURL(); + } else { + return Urls.create(path); + } + } + + public KeyMap getKeys() { + return consoleKeys.getKeys(); + } + + void setInput(final InputStream in) throws IOException { + this.escapeTimeout = Configuration.getLong(JLINE_ESC_TIMEOUT, 100); + /* + * This is gross and here is how to fix it. In getCurrentPosition() + * and getCurrentAnsiRow(), the logic is disabled when running unit + * tests and the fact that it is a unit test is determined by knowing + * if the original input stream was a ByteArrayInputStream. So, this + * is our test to do this. What SHOULD happen is that the unit + * tests should pass in a terminal that is appropriately configured + * such that whatever behavior they expect to happen (or not happen) + * happens (or doesn't). + * + * So, TODO, get rid of this and fix the unit tests. + */ + this.isUnitTestInput = in instanceof ByteArrayInputStream; + boolean nonBlockingEnabled = + escapeTimeout > 0L + && terminal.isSupported() + && in != null; + + /* + * If we had a non-blocking thread already going, then shut it down + * and start a new one. + */ + if (this.in != null) { + this.in.shutdown(); + } + + final InputStream wrapped = terminal.wrapInIfNeeded( in ); + + this.in = new NonBlockingInputStream(wrapped, nonBlockingEnabled); + this.reader = new InputStreamReader( this.in, encoding ); + } + + /** + * Shuts the console reader down. This method should be called when you + * have completed using the reader as it shuts down and cleans up resources + * that would otherwise be "leaked". + */ + public void shutdown() { + if (in != null) { + in.shutdown(); + } + } + + /** + * Shuts down the ConsoleReader if the JVM attempts to clean it up. + */ + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } + finally { + super.finalize(); + } + } + + public InputStream getInput() { + return in; + } + + public Writer getOutput() { + return out; + } + + public Terminal getTerminal() { + return terminal; + } + + public CursorBuffer getCursorBuffer() { + return buf; + } + + public void setExpandEvents(final boolean expand) { + this.expandEvents = expand; + } + + public boolean getExpandEvents() { + return expandEvents; + } + + /** + * Enables or disables copy and paste detection. The effect of enabling this + * this setting is that when a tab is received immediately followed by another + * character, the tab will not be treated as a completion, but as a tab literal. + * @param onoff true if detection is enabled + */ + public void setCopyPasteDetection(final boolean onoff) { + copyPasteDetection = onoff; + } + + /** + * @return true if copy and paste detection is enabled. + */ + public boolean isCopyPasteDetectionEnabled() { + return copyPasteDetection; + } + + /** + * Set whether the console bell is enabled. + * + * @param enabled true if enabled; false otherwise + * @since 2.7 + */ + public void setBellEnabled(boolean enabled) { + this.bellEnabled = enabled; + } + + /** + * Get whether the console bell is enabled + * + * @return true if enabled; false otherwise + * @since 2.7 + */ + public boolean getBellEnabled() { + return bellEnabled; + } + + /** + * Set whether user interrupts (ctrl-C) are handled by having JLine + * throw {@link UserInterruptException} from {@link #readLine}. + * Otherwise, the JVM will handle {@code SIGINT} as normal, which + * usually causes it to exit. The default is {@code false}. + * + * @since 2.10 + */ + public void setHandleUserInterrupt(boolean enabled) + { + this.handleUserInterrupt = enabled; + } + + /** + * Get whether user interrupt handling is enabled + * + * @return true if enabled; false otherwise + * @since 2.10 + */ + public boolean getHandleUserInterrupt() + { + return handleUserInterrupt; + } + + /** + * Sets the string that will be used to start a comment when the + * insert-comment key is struck. + * @param commentBegin The begin comment string. + * @since 2.7 + */ + public void setCommentBegin(String commentBegin) { + this.commentBegin = commentBegin; + } + + /** + * @return the string that will be used to start a comment when the + * insert-comment key is struck. + * @since 2.7 + */ + public String getCommentBegin() { + String str = commentBegin; + + if (str == null) { + str = consoleKeys.getVariable("comment-begin"); + if (str == null) { + str = "#"; + } + } + return str; + } + + public void setPrompt(final String prompt) { + this.prompt = prompt; + this.promptLen = ((prompt == null) ? 0 : stripAnsi(lastLine(prompt)).length()); + } + + public String getPrompt() { + return prompt; + } + + /** + * Set the echo character. For example, to have "*" entered when a password is typed: + * <p/> + * <pre> + * myConsoleReader.setEchoCharacter(new Character('*')); + * </pre> + * <p/> + * Setting the character to + * <p/> + * <pre> + * null + * </pre> + * <p/> + * will restore normal character echoing. Setting the character to + * <p/> + * <pre> + * new Character(0) + * </pre> + * <p/> + * will cause nothing to be echoed. + * + * @param c the character to echo to the console in place of the typed character. + */ + public void setEchoCharacter(final Character c) { + this.echoCharacter = c; + } + + /** + * Returns the echo character. + */ + public Character getEchoCharacter() { + return echoCharacter; + } + + /** + * Erase the current line. + * + * @return false if we failed (e.g., the buffer was empty) + */ + protected final boolean resetLine() throws IOException { + if (buf.cursor == 0) { + return false; + } + + StringBuilder killed = new StringBuilder(); + + while (buf.cursor > 0) { + char c = buf.current(); + if (c == 0) { + break; + } + + killed.append(c); + backspace(); + } + + String copy = killed.reverse().toString(); + killRing.addBackwards(copy); + + return true; + } + + int getCursorPosition() { + // FIXME: does not handle anything but a line with a prompt absolute position + return promptLen + buf.cursor; + } + + /** + * Returns the text after the last '\n'. + * prompt is returned if no '\n' characters are present. + * null is returned if prompt is null. + */ + private String lastLine(String str) { + if (str == null) return ""; + int last = str.lastIndexOf("\n"); + + if (last >= 0) { + return str.substring(last + 1, str.length()); + } + + return str; + } + + String stripAnsi(String str) { + if (str == null) return ""; + return ANSI_CODE_PATTERN.matcher(str).replaceAll(""); +// try { +// ByteArrayOutputStream baos = new ByteArrayOutputStream(); +// AnsiOutputStream aos = new AnsiOutputStream(baos); +// aos.write(str.getBytes()); +// aos.flush(); +// return baos.toString(); +// } catch (IOException e) { +// return str; +// } + } + //where: + private static final Pattern ANSI_CODE_PATTERN = Pattern.compile("\033\\[[^@-~]*[@-~]"); + + /** + * Move the cursor position to the specified absolute index. + */ + public final boolean setCursorPosition(final int position) throws IOException { + if (position == buf.cursor) { + return true; + } + + return moveCursor(position - buf.cursor) != 0; + } + + /** + * Set the current buffer's content to the specified {@link String}. The + * visual console will be modified to show the current buffer. + * + * @param buffer the new contents of the buffer. + */ + private void setBuffer(final String buffer) throws IOException { + // don't bother modifying it if it is unchanged + if (buffer.equals(buf.buffer.toString())) { + return; + } + + // obtain the difference between the current buffer and the new one + int sameIndex = 0; + + for (int i = 0, l1 = buffer.length(), l2 = buf.buffer.length(); (i < l1) + && (i < l2); i++) { + if (buffer.charAt(i) == buf.buffer.charAt(i)) { + sameIndex++; + } + else { + break; + } + } + + int diff = buf.cursor - sameIndex; + if (diff < 0) { // we can't backspace here so try from the end of the buffer + moveToEnd(); + diff = buf.buffer.length() - sameIndex; + } + + backspace(diff); // go back for the differences + killLine(); // clear to the end of the line + buf.buffer.setLength(sameIndex); // the new length + putString(buffer.substring(sameIndex)); // append the differences + } + + private void setBuffer(final CharSequence buffer) throws IOException { + setBuffer(String.valueOf(buffer)); + } + + private void setBufferKeepPos(final String buffer) throws IOException { + int pos = buf.cursor; + setBuffer(buffer); + setCursorPosition(pos); + } + + private void setBufferKeepPos(final CharSequence buffer) throws IOException { + setBufferKeepPos(String.valueOf(buffer)); + } + + /** + * Output put the prompt + the current buffer + */ + public final void drawLine() throws IOException { + String prompt = getPrompt(); + if (prompt != null) { + print(prompt); + } + + print(buf.buffer.toString()); + + if (buf.length() != buf.cursor) { // not at end of line + back(buf.length() - buf.cursor - 1); + } + // force drawBuffer to check for weird wrap (after clear screen) + drawBuffer(); + } + + /** + * Clear the line and redraw it. + */ + public final void redrawLine() throws IOException { + print(RESET_LINE); +// flush(); + drawLine(); + } + + /** + * Clear the buffer and add its contents to the history. + * + * @return the former contents of the buffer. + */ + final String finishBuffer() throws IOException { // FIXME: Package protected because used by tests + String str = buf.buffer.toString(); + String historyLine = str; + + if (expandEvents) { + try { + str = expandEvents(str); + // all post-expansion occurrences of '!' must have been escaped, so re-add escape to each + historyLine = str.replace("!", "\\!"); + // only leading '^' results in expansion, so only re-add escape for that case + historyLine = historyLine.replaceAll("^\\^", "\\\\^"); + } catch(IllegalArgumentException e) { + Log.error("Could not expand event", e); + beep(); + buf.clear(); + str = ""; + } + } + + // we only add it to the history if the buffer is not empty + // and if mask is null, since having a mask typically means + // the string was a password. We clear the mask after this call + if (str.length() > 0) { + if (mask == null && isHistoryEnabled()) { + history.add(historyLine); + } + else { + mask = null; + } + } + + history.moveToEnd(); + + buf.buffer.setLength(0); + buf.cursor = 0; + + return str; + } + + /** + * Expand event designator such as !!, !#, !3, etc... + * See http://www.gnu.org/software/bash/manual/html_node/Event-Designators.html + */ + @SuppressWarnings("fallthrough") + protected String expandEvents(String str) throws IOException { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + 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 + if (i + 1 < str.length()) { + char nextChar = str.charAt(i+1); + if (nextChar == '!' || (nextChar == '^' && i == 0)) { + c = nextChar; + i++; + } + } + sb.append(c); + break; + case '!': + if (i + 1 < str.length()) { + c = str.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).toString(); + break; + case '#': + sb.append(sb.toString()); + break; + case '?': + i1 = str.indexOf('?', i + 1); + if (i1 < 0) { + i1 = str.length(); + } + String sc = str.substring(i + 1, i1); + i = i1; + idx = searchBackwards(sc); + if (idx < 0) { + throw new IllegalArgumentException("!?" + sc + ": event not found"); + } else { + rep = history.get(idx).toString(); + } + break; + case '$': + if (history.size() == 0) { + throw new IllegalArgumentException("!$: event not found"); + } + String previous = history.get(history.index() - 1).toString().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 < str.length(); i++) { + c = str.charAt(i); + if (c < '0' || c > '9') { + break; + } + } + idx = 0; + try { + idx = Integer.parseInt(str.substring(i1, i)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException((neg ? "!-" : "!") + str.substring(i1, i) + ": event not found"); + } + if (neg) { + if (idx > 0 && idx <= history.size()) { + rep = (history.get(history.index() - idx)).toString(); + } else { + throw new IllegalArgumentException((neg ? "!-" : "!") + str.substring(i1, i) + ": event not found"); + } + } else { + if (idx > history.index() - history.size() && idx <= history.index()) { + rep = (history.get(idx - 1)).toString(); + } else { + throw new IllegalArgumentException((neg ? "!-" : "!") + str.substring(i1, i) + ": event not found"); + } + } + break; + default: + String ss = str.substring(i); + i = str.length(); + idx = searchBackwards(ss, history.index(), true); + if (idx < 0) { + throw new IllegalArgumentException("!" + ss + ": event not found"); + } else { + rep = history.get(idx).toString(); + } + break; + } + if (rep != null) { + sb.append(rep); + } + } else { + sb.append(c); + } + break; + case '^': + if (i == 0) { + int i1 = str.indexOf('^', i + 1); + int i2 = str.indexOf('^', i1 + 1); + if (i2 < 0) { + i2 = str.length(); + } + if (i1 > 0 && i2 > 0) { + String s1 = str.substring(i + 1, i1); + String s2 = str.substring(i1 + 1, i2); + String s = history.get(history.index() - 1).toString().replace(s1, s2); + sb.append(s); + i = i2 + 1; + break; + } + } + sb.append(c); + break; + default: + sb.append(c); + break; + } + } + String result = sb.toString(); + if (!str.equals(result)) { + print(result); + println(); + flush(); + } + return result; + + } + + /** + * Write out the specified string to the buffer and the output stream. + */ + public final void putString(final CharSequence str) throws IOException { + buf.write(str); + if (mask == null) { + // no masking + print(str); + } else if (mask == NULL_MASK) { + // don't print anything + } else { + print(mask, str.length()); + } + drawBuffer(); + } + + /** + * Redraw the rest of the buffer from the cursor onwards. This is necessary + * for inserting text into the buffer. + * + * @param clear the number of characters to clear after the end of the buffer + */ + private void drawBuffer(final int clear) throws IOException { + // debug ("drawBuffer: " + clear); + if (buf.cursor == buf.length() && clear == 0) { + } else { + char[] chars = buf.buffer.substring(buf.cursor).toCharArray(); + if (mask != null) { + Arrays.fill(chars, mask); + } + if (terminal.hasWeirdWrap()) { + // need to determine if wrapping will occur: + int width = terminal.getWidth(); + int pos = getCursorPosition(); + for (int i = 0; i < chars.length; i++) { + print(chars[i]); + if ((pos + i + 1) % width == 0) { + print(32); // move cursor to next line by printing dummy space + print(13); // CR / not newline. + } + } + } else { + print(chars); + } + clearAhead(clear, chars.length); + if (terminal.isAnsiSupported()) { + if (chars.length > 0) { + back(chars.length); + } + } else { + back(chars.length); + } + } + if (terminal.hasWeirdWrap()) { + int width = terminal.getWidth(); + // best guess on whether the cursor is in that weird location... + // Need to do this without calling ansi cursor location methods + // otherwise it breaks paste of wrapped lines in xterm. + if (getCursorPosition() > 0 && (getCursorPosition() % width == 0) + && buf.cursor == buf.length() && clear == 0) { + // the following workaround is reverse-engineered from looking + // at what bash sent to the terminal in the same situation + print(32); // move cursor to next line by printing dummy space + print(13); // CR / not newline. + } + } + } + + /** + * Redraw the rest of the buffer from the cursor onwards. This is necessary + * for inserting text into the buffer. + */ + private void drawBuffer() throws IOException { + drawBuffer(0); + } + + /** + * Clear ahead the specified number of characters without moving the cursor. + * + * @param num the number of characters to clear + * @param delta the difference between the internal cursor and the screen + * cursor - if > 0, assume some stuff was printed and weird wrap has to be + * checked + */ + private void clearAhead(final int num, int delta) throws IOException { + if (num == 0) { + return; + } + + if (terminal.isAnsiSupported()) { + int width = terminal.getWidth(); + int screenCursorCol = getCursorPosition() + delta; + // clear current line + printAnsiSequence("K"); + // if cursor+num wraps, then we need to clear the line(s) below too + int curCol = screenCursorCol % width; + int endCol = (screenCursorCol + num - 1) % width; + int lines = num / width; + if (endCol < curCol) lines++; + for (int i = 0; i < lines; i++) { + printAnsiSequence("B"); + printAnsiSequence("2K"); + } + for (int i = 0; i < lines; i++) { + printAnsiSequence("A"); + } + return; + } + + // print blank extra characters + print(' ', num); + + // we need to flush here so a "clever" console doesn't just ignore the redundancy + // of a space followed by a backspace. +// flush(); + + // reset the visual cursor + back(num); + +// flush(); + } + + /** + * Move the visual cursor backwards without modifying the buffer cursor. + */ + protected void back(final int num) throws IOException { + if (num == 0) return; + if (terminal.isAnsiSupported()) { + int width = getTerminal().getWidth(); + int cursor = getCursorPosition(); + int realCursor = cursor + num; + int realCol = realCursor % width; + int newCol = cursor % width; + int moveup = num / width; + int delta = realCol - newCol; + if (delta < 0) moveup++; + if (moveup > 0) { + printAnsiSequence(moveup + "A"); + } + printAnsiSequence((1 + newCol) + "G"); + return; + } + print(BACKSPACE, num); +// flush(); + } + + /** + * Flush the console output stream. This is important for printout out single characters (like a backspace or + * keyboard) that we want the console to handle immediately. + */ + public void flush() throws IOException { + out.flush(); + } + + private int backspaceAll() throws IOException { + return backspace(Integer.MAX_VALUE); + } + + /** + * Issue <em>num</em> backspaces. + * + * @return the number of characters backed up + */ + private int backspace(final int num) throws IOException { + if (buf.cursor == 0) { + return 0; + } + + int count = 0; + + int termwidth = getTerminal().getWidth(); + int lines = getCursorPosition() / termwidth; + count = moveCursor(-1 * num) * -1; + buf.buffer.delete(buf.cursor, buf.cursor + count); + if (getCursorPosition() / termwidth != lines) { + if (terminal.isAnsiSupported()) { + // debug("doing backspace redraw: " + getCursorPosition() + " on " + termwidth + ": " + lines); + printAnsiSequence("K"); + // if cursor+num wraps, then we need to clear the line(s) below too + // last char printed is one pos less than cursor so we subtract + // one +/* + // TODO: fixme (does not work - test with reverse search with wrapping line and CTRL-E) + int endCol = (getCursorPosition() + num - 1) % termwidth; + int curCol = getCursorPosition() % termwidth; + if (endCol < curCol) lines++; + for (int i = 1; i < lines; i++) { + printAnsiSequence("B"); + printAnsiSequence("2K"); + } + for (int i = 1; i < lines; i++) { + printAnsiSequence("A"); + } + return count; +*/ + } + } + drawBuffer(count); + + return count; + } + + /** + * Issue a backspace. + * + * @return true if successful + */ + public boolean backspace() throws IOException { + return backspace(1) == 1; + } + + protected boolean moveToEnd() throws IOException { + if (buf.cursor == buf.length()) { + return true; + } + return moveCursor(buf.length() - buf.cursor) > 0; + } + + /** + * Delete the character at the current position and redraw the remainder of the buffer. + */ + private boolean deleteCurrentCharacter() throws IOException { + if (buf.length() == 0 || buf.cursor == buf.length()) { + return false; + } + + buf.buffer.deleteCharAt(buf.cursor); + drawBuffer(1); + return true; + } + + /** + * 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 + */ + private Operation viDeleteChangeYankToRemap (Operation op) { + switch (op) { + case VI_EOF_MAYBE: + case ABORT: + case BACKWARD_CHAR: + case FORWARD_CHAR: + case END_OF_LINE: + case VI_MATCH: + case VI_BEGNNING_OF_LINE_OR_ARG_DIGIT: + case VI_ARG_DIGIT: + case VI_PREV_WORD: + case VI_END_WORD: + case VI_CHAR_SEARCH: + case VI_NEXT_WORD: + case VI_FIRST_PRINT: + case VI_GOTO_MARK: + case VI_COLUMN: + case VI_DELETE_TO: + case VI_YANK_TO: + case VI_CHANGE_TO: + return op; + + default: + return Operation.VI_MOVEMENT_MODE; + } + } + + /** + * Deletes the previous character from the cursor position + * @param count number of times to do it. + * @return true if it was done. + * @throws IOException + */ + private boolean viRubout(int count) throws IOException { + boolean ok = true; + for (int i = 0; ok && i < count; i++) { + ok = backspace(); + } + return ok; + } + + /** + * Deletes the character you are sitting on and sucks the rest of + * the line in from the right. + * @param count Number of times to perform the operation. + * @return true if its works, false if it didn't + * @throws IOException + */ + private boolean viDelete(int count) throws IOException { + boolean ok = true; + for (int i = 0; ok && i < count; i++) { + ok = deleteCurrentCharacter(); + } + return ok; + } + + /** + * 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. + * @param count The number of times to repeat + * @return true if it completed successfully, false if not all + * case changes could be completed. + * @throws IOException + */ + private boolean viChangeCase(int count) throws IOException { + boolean ok = true; + for (int i = 0; ok && i < count; i++) { + + ok = buf.cursor < buf.buffer.length (); + if (ok) { + char ch = buf.buffer.charAt(buf.cursor); + if (Character.isUpperCase(ch)) { + ch = Character.toLowerCase(ch); + } + else if (Character.isLowerCase(ch)) { + ch = Character.toUpperCase(ch); + } + buf.buffer.setCharAt(buf.cursor, ch); + drawBuffer(1); + moveCursor(1); + } + } + return ok; + } + + /** + * Implements the vi change character command (in move-mode "r" + * followed by the character to change to). + * @param count Number of times to perform the action + * @param c The character to change to + * @return Whether or not there were problems encountered + * @throws IOException + */ + private boolean viChangeChar(int count, int c) throws IOException { + // EOF, ESC, or CTRL-C aborts. + if (c < 0 || c == '\033' || c == '\003') { + return true; + } + + boolean ok = true; + for (int i = 0; ok && i < count; i++) { + ok = buf.cursor < buf.buffer.length (); + if (ok) { + buf.buffer.setCharAt(buf.cursor, (char) c); + drawBuffer(1); + if (i < (count-1)) { + moveCursor(1); + } + } + } + return ok; + } + + /** + * This is a close facsimile of the actual vi previous word logic. In + * actual vi words are determined by boundaries of identity characterse. + * This logic is a bit more simple and simply looks at white space or + * digits or characters. It should be revised at some point. + * + * @param count number of iterations + * @return true if the move was successful, false otherwise + * @throws IOException + */ + private boolean viPreviousWord(int count) throws IOException { + boolean ok = true; + if (buf.cursor == 0) { + return false; + } + + int pos = buf.cursor - 1; + for (int i = 0; pos > 0 && i < count; i++) { + // If we are on white space, then move back. + while (pos > 0 && isWhitespace(buf.buffer.charAt(pos))) { + --pos; + } + + while (pos > 0 && !isDelimiter(buf.buffer.charAt(pos-1))) { + --pos; + } + + if (pos > 0 && i < (count-1)) { + --pos; + } + } + setCursorPosition(pos); + return ok; + } + + /** + * 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 + * @throws IOException + */ + private boolean viDeleteTo(int startPos, int endPos, boolean isChange) throws IOException { + if (startPos == endPos) { + return true; + } + + if (endPos < startPos) { + int tmp = endPos; + endPos = startPos; + startPos = tmp; + } + + setCursorPosition(startPos); + buf.cursor = startPos; + buf.buffer.delete(startPos, endPos); + drawBuffer(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()) { + moveCursor(-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 exaple "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 + * @throws IOException + */ + private boolean viYankTo(int startPos, int endPos) throws IOException { + int cursorPos = startPos; + + if (endPos < startPos) { + int tmp = endPos; + endPos = startPos; + startPos = tmp; + } + + if (startPos == endPos) { + yankBuffer = ""; + return true; + } + + yankBuffer = buf.buffer.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. + */ + setCursorPosition(cursorPos); + return true; + } + + /** + * Pasts the yank buffer to the right of the current cursor position + * and moves the cursor to the end of the pasted region. + * + * @param count Number of times to perform the operation. + * @return true if it worked, false otherwise + * @throws IOException + */ + private boolean viPut(int count) throws IOException { + if (yankBuffer.length () == 0) { + return true; + } + if (buf.cursor < buf.buffer.length ()) { + moveCursor(1); + } + for (int i = 0; i < count; i++) { + putString(yankBuffer); + } + moveCursor(-1); + return true; + } + + /** + * Searches forward of the current position for a character and moves + * the cursor onto it. + * @param count Number of times to repeat the process. + * @param ch The character to search for + * @return true if the char was found, false otherwise + * @throws IOException + */ + private boolean viCharSearch(int count, int invokeChar, int ch) throws IOException { + if (ch < 0 || invokeChar < 0) { + return false; + } + + char searchChar = (char)ch; + boolean isForward; + boolean stopBefore; + + /* + * The character stuff turns out to be hairy. Here is how it works: + * f - search forward for ch + * F - search backward for ch + * t - search forward for ch, but stop just before the match + * T - search backward for ch, but stop just after the match + * ; - After [fFtT;], repeat the last search, after ',' reverse it + * , - After [fFtT;], reverse the last search, after ',' repeat it + */ + if (invokeChar == ';' || invokeChar == ',') { + // No recent search done? Then bail + if (charSearchChar == 0) { + return false; + } + + // Reverse direction if switching between ',' and ';' + if (charSearchLastInvokeChar == ';' || charSearchLastInvokeChar == ',') { + if (charSearchLastInvokeChar != invokeChar) { + charSearchFirstInvokeChar = switchCase(charSearchFirstInvokeChar); + } + } + else { + if (invokeChar == ',') { + charSearchFirstInvokeChar = switchCase(charSearchFirstInvokeChar); + } + } + + searchChar = charSearchChar; + } + else { + charSearchChar = searchChar; + charSearchFirstInvokeChar = (char) invokeChar; + } + + charSearchLastInvokeChar = (char)invokeChar; + + isForward = Character.isLowerCase(charSearchFirstInvokeChar); + stopBefore = (Character.toLowerCase(charSearchFirstInvokeChar) == 't'); + + boolean ok = false; + + if (isForward) { + while (count-- > 0) { + int pos = buf.cursor + 1; + while (pos < buf.buffer.length()) { + if (buf.buffer.charAt(pos) == searchChar) { + setCursorPosition(pos); + ok = true; + break; + } + ++pos; + } + } + + if (ok) { + if (stopBefore) + moveCursor(-1); + + /* + * When in yank-to, move-to, del-to state we actually want to + * go to the character after the one we landed on to make sure + * that the character we ended up on is included in the + * operation + */ + if (isInViMoveOperationState()) { + moveCursor(1); + } + } + } + else { + while (count-- > 0) { + int pos = buf.cursor - 1; + while (pos >= 0) { + if (buf.buffer.charAt(pos) == searchChar) { + setCursorPosition(pos); + ok = true; + break; + } + --pos; + } + } + + if (ok && stopBefore) + moveCursor(1); + } + + return ok; + } + + private char switchCase(char ch) { + if (Character.isUpperCase(ch)) { + return Character.toLowerCase(ch); + } + return Character.toUpperCase(ch); + } + + /** + * @return true if line reader is in the middle of doing a change-to + * delete-to or yank-to. + */ + private final boolean isInViMoveOperationState() { + return state == State.VI_CHANGE_TO + || state == State.VI_DELETE_TO + || state == State.VI_YANK_TO; + } + + /** + * This is a close facsimile of the actual vi next word logic. + * As with viPreviousWord() this probably needs to be improved + * at some point. + * + * @param count number of iterations + * @return true if the move was successful, false otherwise + * @throws IOException + */ + private boolean viNextWord(int count) throws IOException { + int pos = buf.cursor; + int end = buf.buffer.length(); + + for (int i = 0; pos < end && i < count; i++) { + // Skip over letter/digits + while (pos < end && !isDelimiter(buf.buffer.charAt(pos))) { + ++pos; + } + + /* + * Don't you love special cases? During delete-to and yank-to + * operations the word movement is normal. However, during a + * change-to, the trailing spaces behind the last word are + * left in tact. + */ + if (i < (count-1) || !(state == State.VI_CHANGE_TO)) { + while (pos < end && isDelimiter(buf.buffer.charAt(pos))) { + ++pos; + } + } + } + + setCursorPosition(pos); + return true; + } + + /** + * Implements a close facsimile of the vi end-of-word movement. + * If the character is on white space, it takes you to the end + * of the next word. If it is on the last character of a word + * it takes you to the next of the next word. Any other character + * of a word, takes you to the end of the current word. + * + * @param count Number of times to repeat the action + * @return true if it worked. + * @throws IOException + */ + private boolean viEndWord(int count) throws IOException { + int pos = buf.cursor; + int end = buf.buffer.length(); + + for (int i = 0; pos < end && i < count; i++) { + if (pos < (end-1) + && !isDelimiter(buf.buffer.charAt(pos)) + && isDelimiter(buf.buffer.charAt (pos+1))) { + ++pos; + } + + // If we are on white space, then move back. + while (pos < end && isDelimiter(buf.buffer.charAt(pos))) { + ++pos; + } + + while (pos < (end-1) && !isDelimiter(buf.buffer.charAt(pos+1))) { + ++pos; + } + } + setCursorPosition(pos); + return true; + } + + private boolean previousWord() throws IOException { + while (isDelimiter(buf.current()) && (moveCursor(-1) != 0)) { + // nothing + } + + while (!isDelimiter(buf.current()) && (moveCursor(-1) != 0)) { + // nothing + } + + return true; + } + + private boolean nextWord() throws IOException { + while (isDelimiter(buf.nextChar()) && (moveCursor(1) != 0)) { + // nothing + } + + while (!isDelimiter(buf.nextChar()) && (moveCursor(1) != 0)) { + // nothing + } + + return true; + } + + /** + * Deletes to the beginning of the word that the cursor is sitting on. + * If the cursor is on white-space, it deletes that and to the beginning + * of the word before it. If the user is not on a word or whitespace + * it deletes up to the end of the previous word. + * + * @param count Number of times to perform the operation + * @return true if it worked, false if you tried to delete too many words + * @throws IOException + */ + private boolean unixWordRubout(int count) throws IOException { + boolean success = true; + StringBuilder killed = new StringBuilder(); + + for (; count > 0; --count) { + if (buf.cursor == 0) { + success = false; + break; + } + + while (isWhitespace(buf.current())) { + char c = buf.current(); + if (c == 0) { + break; + } + + killed.append(c); + backspace(); + } + + while (!isWhitespace(buf.current())) { + char c = buf.current(); + if (c == 0) { + break; + } + + killed.append(c); + backspace(); + } + } + + String copy = killed.reverse().toString(); + killRing.addBackwards(copy); + + return success; + } + + private String insertComment(boolean isViMode) throws IOException { + String comment = this.getCommentBegin (); + setCursorPosition(0); + putString(comment); + if (isViMode) { + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + } + return accept(); + } + + /** + * Similar to putString() but allows the string to be repeated a specific + * number of times, allowing easy support of vi digit arguments to a given + * command. The string is placed as the current cursor position. + * + * @param count The count of times to insert the string. + * @param str The string to insert + * @return true if the operation is a success, false otherwise + * @throws IOException + */ + private boolean insert(int count, final CharSequence str) throws IOException { + for (int i = 0; i < count; i++) { + buf.write(str); + if (mask == null) { + // no masking + print(str); + } else if (mask == NULL_MASK) { + // don't print anything + } else { + print(mask, str.length()); + } + } + drawBuffer(); + return true; + } + + /** + * Implements vi search ("/" or "?"). + * @throws IOException + */ + @SuppressWarnings("fallthrough") + private int viSearch(char searchChar) throws IOException { + boolean isForward = (searchChar == '/'); + + /* + * This is a little gross, I'm sure there is a more appropriate way + * of saving and restoring state. + */ + CursorBuffer origBuffer = buf.copy(); + + // Clear the contents of the current line and + setCursorPosition (0); + killLine(); + + // Our new "prompt" is the character that got us into search mode. + putString(Character.toString(searchChar)); + flush(); + + boolean isAborted = false; + boolean isComplete = false; + + /* + * Readline doesn't seem to do any special character map handling + * here, so I think we are safe. + */ + int ch = -1; + while (!isAborted && !isComplete && (ch = readCharacter()) != -1) { + switch (ch) { + case '\033': // ESC + /* + * The ESC behavior doesn't appear to be readline behavior, + * but it is a little tweak of my own. I like it. + */ + isAborted = true; + break; + case '\010': // Backspace + case '\177': // Delete + backspace(); + /* + * Backspacing through the "prompt" aborts the search. + */ + if (buf.cursor == 0) { + isAborted = true; + } + break; + case '\012': // NL + case '\015': // CR + isComplete = true; + break; + default: + putString(Character.toString((char) ch)); + } + + flush(); + } + + // If we aborted, then put ourself at the end of the original buffer. + if (ch == -1 || isAborted) { + setCursorPosition(0); + killLine(); + putString(origBuffer.buffer); + setCursorPosition(origBuffer.cursor); + return -1; + } + + /* + * The first character of the buffer was the search character itself + * so we discard it. + */ + String searchTerm = buf.buffer.substring(1); + int idx = -1; + + /* + * The semantics of the history thing is gross when you want to + * explicitly iterate over entries (without an iterator) as size() + * returns the actual number of entries in the list but get() + * doesn't work the way you think. + */ + int end = history.index(); + int start = (end <= history.size()) ? 0 : end - history.size(); + + if (isForward) { + for (int i = start; i < end; i++) { + if (history.get(i).toString().contains(searchTerm)) { + idx = i; + break; + } + } + } + else { + for (int i = end-1; i >= start; i--) { + if (history.get(i).toString().contains(searchTerm)) { + idx = i; + break; + } + } + } + + /* + * No match? Then restore what we were working on, but make sure + * the cursor is at the beginning of the line. + */ + if (idx == -1) { + setCursorPosition(0); + killLine(); + putString(origBuffer.buffer); + setCursorPosition(0); + return -1; + } + + /* + * Show the match. + */ + setCursorPosition(0); + killLine(); + putString(history.get(idx)); + setCursorPosition(0); + flush(); + + /* + * While searching really only the "n" and "N" keys are interpreted + * as movement, any other key is treated as if you are editing the + * line with it, so we return it back up to the caller for interpretation. + */ + isComplete = false; + while (!isComplete && (ch = readCharacter()) != -1) { + boolean forward = isForward; + switch (ch) { + case 'p': case 'P': + forward = !isForward; + // Fallthru + case 'n': case 'N': + boolean isMatch = false; + if (forward) { + for (int i = idx+1; !isMatch && i < end; i++) { + if (history.get(i).toString().contains(searchTerm)) { + idx = i; + isMatch = true; + } + } + } + else { + for (int i = idx - 1; !isMatch && i >= start; i--) { + if (history.get(i).toString().contains(searchTerm)) { + idx = i; + isMatch = true; + } + } + } + if (isMatch) { + setCursorPosition(0); + killLine(); + putString(history.get(idx)); + setCursorPosition(0); + } + break; + default: + isComplete = true; + } + flush(); + } + + /* + * Complete? + */ + return ch; + } + + public void setParenBlinkTimeout(int timeout) { + parenBlinkTimeout = timeout; + } + + private void insertClose(String s) throws IOException { + putString(s); + int closePosition = buf.cursor; + + moveCursor(-1); + viMatch(); + + + if (in.isNonBlockingEnabled()) { + in.peek(parenBlinkTimeout); + } + + setCursorPosition(closePosition); + } + + /** + * 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. + * @throws IOException + */ + private boolean viMatch() throws IOException { + int pos = buf.cursor; + + if (pos == buf.length()) { + return false; + } + + int type = getBracketType(buf.buffer.charAt (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.buffer.length ()) { + return false; + } + + int curType = getBracketType(buf.buffer.charAt (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 && isInViMoveOperationState()) + ++pos; + + setCursorPosition(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. + */ + private int getBracketType (char ch) { + switch (ch) { + case '[': return 1; + case ']': return -1; + case '{': return 2; + case '}': return -2; + case '(': return 3; + case ')': return -3; + default: + return 0; + } + } + + private boolean deletePreviousWord() throws IOException { + StringBuilder killed = new StringBuilder(); + char c; + + while (isDelimiter((c = buf.current()))) { + if (c == 0) { + break; + } + + killed.append(c); + backspace(); + } + + while (!isDelimiter((c = buf.current()))) { + if (c == 0) { + break; + } + + killed.append(c); + backspace(); + } + + String copy = killed.reverse().toString(); + killRing.addBackwards(copy); + return true; + } + + private boolean deleteNextWord() throws IOException { + StringBuilder killed = new StringBuilder(); + char c; + + while (isDelimiter((c = buf.nextChar()))) { + if (c == 0) { + break; + } + killed.append(c); + delete(); + } + + while (!isDelimiter((c = buf.nextChar()))) { + if (c == 0) { + break; + } + killed.append(c); + delete(); + } + + String copy = killed.toString(); + killRing.add(copy); + + return true; + } + + private boolean capitalizeWord() throws IOException { + boolean first = true; + int i = 1; + char c; + while (buf.cursor + i - 1< buf.length() && !isDelimiter((c = buf.buffer.charAt(buf.cursor + i - 1)))) { + buf.buffer.setCharAt(buf.cursor + i - 1, first ? Character.toUpperCase(c) : Character.toLowerCase(c)); + first = false; + i++; + } + drawBuffer(); + moveCursor(i - 1); + return true; + } + + private boolean upCaseWord() throws IOException { + int i = 1; + char c; + while (buf.cursor + i - 1 < buf.length() && !isDelimiter((c = buf.buffer.charAt(buf.cursor + i - 1)))) { + buf.buffer.setCharAt(buf.cursor + i - 1, Character.toUpperCase(c)); + i++; + } + drawBuffer(); + moveCursor(i - 1); + return true; + } + + private boolean downCaseWord() throws IOException { + int i = 1; + char c; + while (buf.cursor + i - 1 < buf.length() && !isDelimiter((c = buf.buffer.charAt(buf.cursor + i - 1)))) { + buf.buffer.setCharAt(buf.cursor + i - 1, Character.toLowerCase(c)); + i++; + } + drawBuffer(); + moveCursor(i - 1); + return true; + } + + /** + * Performs character transpose. The character prior to the cursor and the + * character under the cursor are swapped and the cursor is advanced one + * character unless you are already at the end of the line. + * + * @param count The number of times to perform the transpose + * @return true if the operation succeeded, false otherwise (e.g. transpose + * cannot happen at the beginning of the line). + * @throws IOException + */ + private boolean transposeChars(int count) throws IOException { + for (; count > 0; --count) { + if (buf.cursor == 0 || buf.cursor == buf.buffer.length()) { + return false; + } + + int first = buf.cursor-1; + int second = buf.cursor; + + char tmp = buf.buffer.charAt (first); + buf.buffer.setCharAt(first, buf.buffer.charAt(second)); + buf.buffer.setCharAt(second, tmp); + + // This could be done more efficiently by only re-drawing at the end. + moveInternal(-1); + drawBuffer(); + moveInternal(2); + } + + return true; + } + + public boolean isKeyMap(String name) { + // Current keymap. + KeyMap map = consoleKeys.getKeys(); + KeyMap mapByName = consoleKeys.getKeyMaps().get(name); + + if (mapByName == null) + return false; + + /* + * This may not be safe to do, but there doesn't appear to be a + * clean way to find this information out. + */ + return map == mapByName; + } + + + /** + * The equivalent of hitting <RET>. The line is considered + * complete and is returned. + * + * @return The completed line of text. + * @throws IOException + */ + public String accept() throws IOException { + moveToEnd(); + println(); // output newline + flush(); + return finishBuffer(); + } + + private void abort() throws IOException { + beep(); + buf.clear(); + println(); + redrawLine(); + } + + /** + * Move the cursor <i>where</i> characters. + * + * @param num If less than 0, move abs(<i>where</i>) to the left, otherwise move <i>where</i> to the right. + * @return The number of spaces we moved + */ + public int moveCursor(final int num) throws IOException { + int where = num; + + if ((buf.cursor == 0) && (where <= 0)) { + return 0; + } + + if ((buf.cursor == buf.buffer.length()) && (where >= 0)) { + return 0; + } + + if ((buf.cursor + where) < 0) { + where = -buf.cursor; + } + else if ((buf.cursor + where) > buf.buffer.length()) { + where = buf.buffer.length() - buf.cursor; + } + + moveInternal(where); + + return where; + } + + /** + * Move the cursor <i>where</i> characters, without checking the current buffer. + * + * @param where the number of characters to move to the right or left. + */ + private void moveInternal(final int where) throws IOException { + // debug ("move cursor " + where + " (" + // + buf.cursor + " => " + (buf.cursor + where) + ")"); + buf.cursor += where; + + if (terminal.isAnsiSupported()) { + if (where < 0) { + back(Math.abs(where)); + } else { + int width = getTerminal().getWidth(); + int cursor = getCursorPosition(); + int oldLine = (cursor - where) / width; + int newLine = cursor / width; + if (newLine > oldLine) { + printAnsiSequence((newLine - oldLine) + "B"); + } + printAnsiSequence(1 +(cursor % width) + "G"); + } +// flush(); + return; + } + + char c; + + if (where < 0) { + int len = 0; + for (int i = buf.cursor; i < buf.cursor - where; i++) { + if (buf.buffer.charAt(i) == '\t') { + len += TAB_WIDTH; + } + else { + len++; + } + } + + char chars[] = new char[len]; + Arrays.fill(chars, BACKSPACE); + out.write(chars); + + return; + } + else if (buf.cursor == 0) { + return; + } + else if (mask != null) { + c = mask; + } + else { + print(buf.buffer.substring(buf.cursor - where, buf.cursor).toCharArray()); + return; + } + + // null character mask: don't output anything + if (mask == NULL_MASK) { + return; + } + + print(c, Math.abs(where)); + } + + // FIXME: replace() is not used + + public final boolean replace(final int num, final String replacement) { + buf.buffer.replace(buf.cursor - num, buf.cursor, replacement); + try { + moveCursor(-num); + drawBuffer(Math.max(0, num - replacement.length())); + moveCursor(replacement.length()); + } + catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * Read a character from the console. + * + * @return the character, or -1 if an EOF is received. + */ + public final int readCharacter() throws IOException { + int c = reader.read(); + if (c >= 0) { + Log.trace("Keystroke: ", c); + // clear any echo characters + if (terminal.isSupported()) { + clearEcho(c); + } + } + return c; + } + + /** + * Clear the echoed characters for the specified character code. + */ + private int clearEcho(final int c) throws IOException { + // if the terminal is not echoing, then ignore + if (!terminal.isEchoEnabled()) { + return 0; + } + + // otherwise, clear + int num = countEchoCharacters(c); + back(num); + drawBuffer(num); + + return num; + } + + private int countEchoCharacters(final int c) { + // tabs as special: we need to determine the number of spaces + // to cancel based on what out current cursor position is + if (c == 9) { + int tabStop = 8; // will this ever be different? + int position = getCursorPosition(); + + return tabStop - (position % tabStop); + } + + return getPrintableCharacters(c).length(); + } + + /** + * Return the number of characters that will be printed when the specified + * character is echoed to the screen + * + * Adapted from cat by Torbjorn Granlund, as repeated in stty by David MacKenzie. + */ + private StringBuilder getPrintableCharacters(final int ch) { + StringBuilder sbuff = new StringBuilder(); + + if (ch >= 32) { + if (ch < 127) { + sbuff.append(ch); + } + else if (ch == 127) { + sbuff.append('^'); + sbuff.append('?'); + } + else { + sbuff.append('M'); + sbuff.append('-'); + + if (ch >= (128 + 32)) { + if (ch < (128 + 127)) { + sbuff.append((char) (ch - 128)); + } + else { + sbuff.append('^'); + sbuff.append('?'); + } + } + else { + sbuff.append('^'); + sbuff.append((char) (ch - 128 + 64)); + } + } + } + else { + sbuff.append('^'); + sbuff.append((char) (ch + 64)); + } + + return sbuff; + } + + public final int readCharacter(final char... allowed) throws IOException { + // if we restrict to a limited set and the current character is not in the set, then try again. + char c; + + Arrays.sort(allowed); // always need to sort before binarySearch + + while (Arrays.binarySearch(allowed, c = (char) readCharacter()) < 0) { + // nothing + } + + return c; + } + + // + // Key Bindings + // + + public static final String JLINE_COMPLETION_THRESHOLD = "jline.completion.threshold"; + + // + // Line Reading + // + + /** + * Read the next line and return the contents of the buffer. + */ + public String readLine() throws IOException { + return readLine((String) null); + } + + /** + * Read the next line with the specified character mask. If null, then + * characters will be echoed. If 0, then no characters will be echoed. + */ + public String readLine(final Character mask) throws IOException { + return readLine(null, mask); + } + + public String readLine(final String prompt) throws IOException { + return readLine(prompt, null); + } + + /** + * Sets the current keymap by name. Supported keymaps are "emacs", + * "vi-insert", "vi-move". + * @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) { + return consoleKeys.setKeyMap(name); + } + + /** + * 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 consoleKeys.getKeys().getName(); + } + + /** + * Read a line from the <i>in</i> {@link InputStream}, and return the line + * (without any trailing newlines). + * + * @param prompt The prompt to issue to the console, may be null. + * @return A line that is read from the terminal, or null if there was null input (e.g., <i>CTRL-D</i> + * was pressed). + */ + public String readLine(String prompt, final Character mask) throws IOException { + // prompt may be null + // mask may be null + + /* + * 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. + */ + int repeatCount = 0; + + // FIXME: This blows, each call to readLine will reset the console's state which doesn't seem very nice. + this.mask = mask; + if (prompt != null) { + setPrompt(prompt); + } + else { + prompt = getPrompt(); + } + + try { + if (!terminal.isSupported()) { + beforeReadLine(prompt, mask); + } + + if (prompt != null && prompt.length() > 0) { + out.write(prompt); + out.flush(); + } + + // if the terminal is unsupported, just use plain-java reading + if (!terminal.isSupported()) { + return readLineSimple(); + } + + if (handleUserInterrupt && (terminal instanceof UnixTerminal)) { + ((UnixTerminal) terminal).disableInterruptCharacter(); + } + + String originalPrompt = this.prompt; + + state = State.NORMAL; + + boolean success = true; + + StringBuilder sb = new StringBuilder(); + Stack<Character> pushBackChar = new Stack<Character>(); + while (true) { + int c = pushBackChar.isEmpty() ? readCharacter() : pushBackChar.pop (); + if (c == -1) { + return null; + } + sb.appendCodePoint(c); + + if (recording) { + macro += new String(new int[]{c}, 0, 1); + } + + Object o = getKeys().getBound( sb ); + /* + * 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 (!recording && !(o instanceof KeyMap)) { + if (o != Operation.YANK_POP && o != Operation.YANK) { + killRing.resetLastYank(); + } + if (o != Operation.KILL_LINE && o != Operation.KILL_WHOLE_LINE + && o != Operation.BACKWARD_KILL_WORD && o != Operation.KILL_WORD + && o != Operation.UNIX_LINE_DISCARD && o != Operation.UNIX_WORD_RUBOUT) { + killRing.resetLastKill(); + } + } + + if (o == Operation.DO_LOWERCASE_VERSION) { + sb.setLength( sb.length() - 1); + sb.append( Character.toLowerCase( (char) c )); + o = getKeys().getBound( sb ); + } + + /* + * A KeyMap indicates that the key that was struck has a + * number of keys that can follow it as indicated in the + * map. This is used primarily for Emacs style ESC-META-x + * lookups. Since more keys must follow, go back to waiting + * for the next key. + */ + if ( o instanceof KeyMap ) { + /* + * The ESC key (#27) is special in that it is ambiguous until + * you know what is coming next. The ESC could be a literal + * escape, like the user entering vi-move mode, or it could + * be part of a terminal control sequence. The following + * logic attempts to disambiguate things in the same + * fashion as regular vi or readline. + * + * When ESC is encountered and there is no other pending + * character in the pushback queue, then attempt to peek + * into the input stream (if the feature is enabled) for + * 150ms. If nothing else is coming, then assume it is + * not a terminal control sequence, but a raw escape. + */ + if (c == 27 + && pushBackChar.isEmpty() + && in.isNonBlockingEnabled() + && in.peek(escapeTimeout) == -2) { + o = ((KeyMap) o).getAnotherKey(); + if (o == null || o instanceof KeyMap) { + continue; + } + sb.setLength(0); + } + else { + continue; + } + } + + /* + * If we didn't find a binding for the key and there is + * more than one character accumulated then start checking + * the largest span of characters from the beginning to + * see if there is a binding for them. + * + * For example if our buffer has ESC,CTRL-M,C the getBound() + * called previously indicated that there is no binding for + * this sequence, so this then checks ESC,CTRL-M, and failing + * that, just ESC. Each keystroke that is pealed off the end + * during these tests is stuffed onto the pushback buffer so + * they won't be lost. + * + * If there is no binding found, then we go back to waiting for + * input. + */ + while ( o == null && sb.length() > 0 ) { + c = sb.charAt( sb.length() - 1 ); + sb.setLength( sb.length() - 1 ); + Object o2 = getKeys().getBound( sb ); + if ( o2 instanceof KeyMap ) { + o = ((KeyMap) o2).getAnotherKey(); + if ( o == null ) { + continue; + } else { + pushBackChar.push( (char) c ); + } + } + } + + if ( o == null ) { + continue; + } + Log.trace("Binding: ", o); + + + // Handle macros + if (o instanceof String) { + String macro = (String) o; + for (int i = 0; i < macro.length(); i++) { + pushBackChar.push(macro.charAt(macro.length() - 1 - i)); + } + sb.setLength( 0 ); + continue; + } + + // Handle custom callbacks + //original code: +// if (o instanceof ActionListener) { +// ((ActionListener) o).actionPerformed(null); +// sb.setLength( 0 ); +// continue; +// } + //using reflection to avoid dependency on java.desktop: + try { + Class<?> actionListener = + Class.forName("java.awt.event.ActionListener", false, ClassLoader.getSystemClassLoader()); + Class<?> actionEvent = + Class.forName("java.awt.event.ActionEvent", false, ClassLoader.getSystemClassLoader()); + if (actionListener.isAssignableFrom(o.getClass())) { + Method actionPerformed = + actionListener.getMethod("actionPerformed", actionEvent); + try { + actionPerformed.invoke(o, (Object) null); + } catch (InvocationTargetException ex ) { + Log.error("Exception while running registered action", ex); + } + sb.setLength( 0 ); + continue; + } + } catch (ReflectiveOperationException ex) { + //ignore + } + + // Search mode. + // + // Note that we have to do this first, because if there is a command + // not linked to a search command, we leave the search mode and fall + // through to the normal state. + if (state == State.SEARCH || state == State.FORWARD_SEARCH) { + int cursorDest = -1; + switch ( ((Operation) o )) { + case ABORT: + state = State.NORMAL; + buf.clear(); + buf.buffer.append(searchTerm); + break; + + case REVERSE_SEARCH_HISTORY: + state = State.SEARCH; + if (searchTerm.length() == 0) { + searchTerm.append(previousSearchTerm); + } + + if (searchIndex > 0) { + searchIndex = searchBackwards(searchTerm.toString(), searchIndex); + } + break; + + case FORWARD_SEARCH_HISTORY: + state = State.FORWARD_SEARCH; + if (searchTerm.length() == 0) { + searchTerm.append(previousSearchTerm); + } + + if (searchIndex > -1 && searchIndex < history.size() - 1) { + searchIndex = searchForwards(searchTerm.toString(), searchIndex); + } + break; + + case BACKWARD_DELETE_CHAR: + if (searchTerm.length() > 0) { + searchTerm.deleteCharAt(searchTerm.length() - 1); + if (state == State.SEARCH) { + searchIndex = searchBackwards(searchTerm.toString()); + } else { + searchIndex = searchForwards(searchTerm.toString()); + } + } + break; + + case SELF_INSERT: + searchTerm.appendCodePoint(c); + if (state == State.SEARCH) { + searchIndex = searchBackwards(searchTerm.toString()); + } else { + searchIndex = searchForwards(searchTerm.toString()); + } + break; + + default: + // Set buffer and cursor position to the found string. + if (searchIndex != -1) { + history.moveTo(searchIndex); + // set cursor position to the found string + cursorDest = history.current().toString().indexOf(searchTerm.toString()); + } + state = State.NORMAL; + break; + } + + // if we're still in search mode, print the search status + if (state == State.SEARCH || state == State.FORWARD_SEARCH) { + if (searchTerm.length() == 0) { + if (state == State.SEARCH) { + printSearchStatus("", ""); + } else { + printForwardSearchStatus("", ""); + } + searchIndex = -1; + } else { + if (searchIndex == -1) { + beep(); + printSearchStatus(searchTerm.toString(), ""); + } else if (state == State.SEARCH) { + printSearchStatus(searchTerm.toString(), history.get(searchIndex).toString()); + } else { + printForwardSearchStatus(searchTerm.toString(), history.get(searchIndex).toString()); + } + } + } + // otherwise, restore the line + else { + restoreLine(originalPrompt, cursorDest); + } + } + if (state != State.SEARCH && state != State.FORWARD_SEARCH) { + /* + * If this is still false at the end of the switch, then + * we reset our repeatCount to 0. + */ + boolean 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. + */ + int count = (repeatCount == 0) ? 1 : repeatCount; + + /* + * Default success to true. You only need to explicitly + * set it if something goes wrong. + */ + success = true; + + if (o instanceof Operation) { + Operation op = (Operation)o; + /* + * Current location of the cursor (prior to the operation). + * These are used by vi *-to operation (e.g. delete-to) + * so we know where we came from. + */ + int cursorStart = buf.cursor; + State origState = state; + + /* + * If we are on a "vi" movement based operation, then we + * need to restrict the sets of inputs pretty heavily. + */ + if (state == State.VI_CHANGE_TO + || state == State.VI_YANK_TO + || state == State.VI_DELETE_TO) { + + op = viDeleteChangeYankToRemap(op); + } + + switch ( op ) { + case COMPLETE: // tab + // There is an annoyance with tab completion in that + // sometimes the user is actually pasting input in that + // has physical tabs in it. This attempts to look at how + // quickly a character follows the tab, if the character + // follows *immediately*, we assume it is a tab literal. + boolean isTabLiteral = false; + if (copyPasteDetection + && c == 9 + && (!pushBackChar.isEmpty() + || (in.isNonBlockingEnabled() && in.peek(escapeTimeout) != -2))) { + isTabLiteral = true; + } + + if (! isTabLiteral) { + success = complete(); + } + else { + putString(sb); + } + break; + + case POSSIBLE_COMPLETIONS: + printCompletionCandidates(); + break; + + case BEGINNING_OF_LINE: + success = setCursorPosition(0); + break; + + case YANK: + success = yank(); + break; + + case YANK_POP: + success = yankPop(); + break; + + case KILL_LINE: // CTRL-K + success = killLine(); + break; + + case KILL_WHOLE_LINE: + success = setCursorPosition(0) && killLine(); + break; + + case CLEAR_SCREEN: // CTRL-L + success = clearScreen(); + redrawLine(); + break; + + case OVERWRITE_MODE: + buf.setOverTyping(!buf.isOverTyping()); + break; + + case SELF_INSERT: + putString(sb); + break; + + case ACCEPT_LINE: + return accept(); + + case ABORT: + if (searchTerm == null) { + abort(); + } + break; + + case INTERRUPT: + if (handleUserInterrupt) { + println(); + flush(); + String partialLine = buf.buffer.toString(); + buf.clear(); + history.moveToEnd(); + throw new UserInterruptException(partialLine); + } + break; + + /* + * VI_MOVE_ACCEPT_LINE is the result of an ENTER + * while in move mode. This is the same as a normal + * ACCEPT_LINE, except that we need to enter + * insert mode as well. + */ + case VI_MOVE_ACCEPT_LINE: + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + return accept(); + + case BACKWARD_WORD: + success = previousWord(); + break; + + case FORWARD_WORD: + success = nextWord(); + break; + + case PREVIOUS_HISTORY: + success = moveHistory(false); + break; + + /* + * According to bash/readline move through history + * in "vi" mode will move the cursor to the + * start of the line. If there is no previous + * history, then the cursor doesn't move. + */ + case VI_PREVIOUS_HISTORY: + success = moveHistory(false, count) + && setCursorPosition(0); + break; + + case NEXT_HISTORY: + success = moveHistory(true); + break; + + /* + * According to bash/readline move through history + * in "vi" mode will move the cursor to the + * start of the line. If there is no next history, + * then the cursor doesn't move. + */ + case VI_NEXT_HISTORY: + success = moveHistory(true, count) + && setCursorPosition(0); + break; + + case BACKWARD_DELETE_CHAR: // backspace + success = backspace(); + break; + + case EXIT_OR_DELETE_CHAR: + if (buf.buffer.length() == 0) { + return null; + } + success = deleteCurrentCharacter(); + break; + + case DELETE_CHAR: // delete + success = deleteCurrentCharacter(); + break; + + case BACKWARD_CHAR: + success = moveCursor(-(count)) != 0; + break; + + case FORWARD_CHAR: + success = moveCursor(count) != 0; + break; + + case UNIX_LINE_DISCARD: + success = resetLine(); + break; + + case UNIX_WORD_RUBOUT: + success = unixWordRubout(count); + break; + + case BACKWARD_KILL_WORD: + success = deletePreviousWord(); + break; + + case KILL_WORD: + success = deleteNextWord(); + break; + + case BEGINNING_OF_HISTORY: + success = history.moveToFirst(); + if (success) { + setBuffer(history.current()); + } + break; + + case END_OF_HISTORY: + success = history.moveToLast(); + if (success) { + setBuffer(history.current()); + } + break; + + case HISTORY_SEARCH_BACKWARD: + searchTerm = new StringBuffer(buf.upToCursor()); + searchIndex = searchBackwards(searchTerm.toString(), history.index(), true); + + if (searchIndex == -1) { + beep(); + } else { + // Maintain cursor position while searching. + success = history.moveTo(searchIndex); + if (success) { + setBufferKeepPos(history.current()); + } + } + break; + + case HISTORY_SEARCH_FORWARD: + searchTerm = new StringBuffer(buf.upToCursor()); + int index = history.index() + 1; + + if (index == history.size()) { + history.moveToEnd(); + setBufferKeepPos(searchTerm.toString()); + } else if (index < history.size()) { + searchIndex = searchForwards(searchTerm.toString(), index, true); + if (searchIndex == -1) { + beep(); + } else { + // Maintain cursor position while searching. + success = history.moveTo(searchIndex); + if (success) { + setBufferKeepPos(history.current()); + } + } + } + break; + + case REVERSE_SEARCH_HISTORY: + if (searchTerm != null) { + previousSearchTerm = searchTerm.toString(); + } + searchTerm = new StringBuffer(buf.buffer); + state = State.SEARCH; + if (searchTerm.length() > 0) { + searchIndex = searchBackwards(searchTerm.toString()); + if (searchIndex == -1) { + beep(); + } + printSearchStatus(searchTerm.toString(), + searchIndex > -1 ? history.get(searchIndex).toString() : ""); + } else { + searchIndex = -1; + printSearchStatus("", ""); + } + break; + + case FORWARD_SEARCH_HISTORY: + if (searchTerm != null) { + previousSearchTerm = searchTerm.toString(); + } + searchTerm = new StringBuffer(buf.buffer); + state = State.FORWARD_SEARCH; + if (searchTerm.length() > 0) { + searchIndex = searchForwards(searchTerm.toString()); + if (searchIndex == -1) { + beep(); + } + printForwardSearchStatus(searchTerm.toString(), + searchIndex > -1 ? history.get(searchIndex).toString() : ""); + } else { + searchIndex = -1; + printForwardSearchStatus("", ""); + } + break; + + case CAPITALIZE_WORD: + success = capitalizeWord(); + break; + + case UPCASE_WORD: + success = upCaseWord(); + break; + + case DOWNCASE_WORD: + success = downCaseWord(); + break; + + case END_OF_LINE: + success = moveToEnd(); + break; + + case TAB_INSERT: + putString( "\t" ); + break; + + case RE_READ_INIT_FILE: + consoleKeys.loadKeys(appName, inputrcUrl); + break; + + case START_KBD_MACRO: + recording = true; + break; + + case END_KBD_MACRO: + recording = false; + macro = macro.substring(0, macro.length() - sb.length()); + break; + + case CALL_LAST_KBD_MACRO: + for (int i = 0; i < macro.length(); i++) { + pushBackChar.push(macro.charAt(macro.length() - 1 - i)); + } + sb.setLength( 0 ); + break; + + case VI_EDITING_MODE: + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + break; + + case VI_MOVEMENT_MODE: + /* + * 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 expclit entry to movement + * mode. + */ + if (state == State.NORMAL) { + moveCursor(-1); + } + consoleKeys.setKeyMap(KeyMap.VI_MOVE); + break; + + case VI_INSERTION_MODE: + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + break; + + case VI_APPEND_MODE: + moveCursor(1); + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + break; + + case VI_APPEND_EOL: + success = moveToEnd(); + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + break; + + /* + * Handler for CTRL-D. Attempts to follow readline + * behavior. If the line is empty, then it is an EOF + * otherwise it is as if the user hit enter. + */ + case VI_EOF_MAYBE: + if (buf.buffer.length() == 0) { + return null; + } + return accept(); + + case TRANSPOSE_CHARS: + success = transposeChars(count); + break; + + case INSERT_COMMENT: + return insertComment (false); + + case INSERT_CLOSE_CURLY: + insertClose("}"); + break; + + case INSERT_CLOSE_PAREN: + insertClose(")"); + break; + + case INSERT_CLOSE_SQUARE: + insertClose("]"); + break; + + case VI_INSERT_COMMENT: + return insertComment (true); + + case VI_MATCH: + success = viMatch (); + break; + + case VI_SEARCH: + int lastChar = viSearch(sb.charAt (0)); + if (lastChar != -1) { + pushBackChar.push((char)lastChar); + } + break; + + case VI_ARG_DIGIT: + repeatCount = (repeatCount * 10) + sb.charAt(0) - '0'; + isArgDigit = true; + break; + + case VI_BEGNNING_OF_LINE_OR_ARG_DIGIT: + if (repeatCount > 0) { + repeatCount = (repeatCount * 10) + sb.charAt(0) - '0'; + isArgDigit = true; + } + else { + success = setCursorPosition(0); + } + break; + + case VI_FIRST_PRINT: + success = setCursorPosition(0) && viNextWord(1); + break; + + case VI_PREV_WORD: + success = viPreviousWord(count); + break; + + case VI_NEXT_WORD: + success = viNextWord(count); + break; + + case VI_END_WORD: + success = viEndWord(count); + break; + + case VI_INSERT_BEG: + success = setCursorPosition(0); + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + break; + + case VI_RUBOUT: + success = viRubout(count); + break; + + case VI_DELETE: + success = viDelete(count); + break; + + case VI_DELETE_TO: + /* + * 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 (state == State.VI_DELETE_TO) { + success = setCursorPosition(0) && killLine(); + state = origState = State.NORMAL; + } + else { + state = State.VI_DELETE_TO; + } + break; + + case VI_YANK_TO: + // Similar to delete-to, a "yy" yanks the whole line. + if (state == State.VI_YANK_TO) { + yankBuffer = buf.buffer.toString(); + state = origState = State.NORMAL; + } + else { + state = State.VI_YANK_TO; + } + break; + + case VI_CHANGE_TO: + if (state == State.VI_CHANGE_TO) { + success = setCursorPosition(0) && killLine(); + state = origState = State.NORMAL; + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + } + else { + state = State.VI_CHANGE_TO; + } + break; + + case VI_KILL_WHOLE_LINE: + success = setCursorPosition(0) && killLine(); + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + break; + + case VI_PUT: + success = viPut(count); + break; + + case VI_CHAR_SEARCH: { + // ';' and ',' don't need another character. They indicate repeat next or repeat prev. + int searchChar = (c != ';' && c != ',') + ? (pushBackChar.isEmpty() + ? readCharacter() + : pushBackChar.pop ()) + : 0; + + success = viCharSearch(count, c, searchChar); + } + break; + + case VI_CHANGE_CASE: + success = viChangeCase(count); + break; + + case VI_CHANGE_CHAR: + success = viChangeChar(count, + pushBackChar.isEmpty() + ? readCharacter() + : pushBackChar.pop()); + break; + + case VI_DELETE_TO_EOL: + success = viDeleteTo(buf.cursor, buf.buffer.length(), false); + break; + + case VI_CHANGE_TO_EOL: + success = viDeleteTo(buf.cursor, buf.buffer.length(), true); + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + break; + + case EMACS_EDITING_MODE: + consoleKeys.setKeyMap(KeyMap.EMACS); + break; + + default: + break; + } + + /* + * If we were in a yank-to, delete-to, move-to + * when this operation started, then fall back to + */ + if (origState != State.NORMAL) { + if (origState == State.VI_DELETE_TO) { + success = viDeleteTo(cursorStart, buf.cursor, false); + } + else if (origState == State.VI_CHANGE_TO) { + success = viDeleteTo(cursorStart, buf.cursor, true); + consoleKeys.setKeyMap(KeyMap.VI_INSERT); + } + else if (origState == State.VI_YANK_TO) { + success = viYankTo(cursorStart, buf.cursor); + } + state = State.NORMAL; + } + + /* + * Another subtly. The check for the NORMAL state is + * to ensure that we do not clear out the repeat + * count when in delete-to, yank-to, or move-to modes. + */ + if (state == State.NORMAL && !isArgDigit) { + /* + * If the operation performed wasn't a vi argument + * digit, then clear out the current repeatCount; + */ + repeatCount = 0; + } + + if (state != State.SEARCH && state != State.FORWARD_SEARCH) { + previousSearchTerm = ""; + searchTerm = null; + searchIndex = -1; + } + } + } + if (!success) { + beep(); + } + sb.setLength( 0 ); + flush(); + } + } + finally { + if (!terminal.isSupported()) { + afterReadLine(); + } + if (handleUserInterrupt && (terminal instanceof UnixTerminal)) { + ((UnixTerminal) terminal).enableInterruptCharacter(); + } + } + } + + /** + * Read a line for unsupported terminals. + */ + private String readLineSimple() throws IOException { + StringBuilder buff = new StringBuilder(); + + if (skipLF) { + skipLF = false; + + int i = readCharacter(); + + if (i == -1 || i == '\r') { + return buff.toString(); + } else if (i == '\n') { + // ignore + } else { + buff.append((char) i); + } + } + + while (true) { + int i = readCharacter(); + + if (i == -1 && buff.length() == 0) { + return null; + } + + if (i == -1 || i == '\n') { + return buff.toString(); + } else if (i == '\r') { + skipLF = true; + return buff.toString(); + } else { + buff.append((char) i); + } + } + } + + // + // Completion + // + + private final List<Completer> completers = new LinkedList<Completer>(); + + private CompletionHandler completionHandler = new CandidateListCompletionHandler(); + + /** + * Add the specified {@link jline.console.completer.Completer} to the list of handlers for tab-completion. + * + * @param completer the {@link jline.console.completer.Completer} to add + * @return true if it was successfully added + */ + public boolean addCompleter(final Completer completer) { + return completers.add(completer); + } + + /** + * Remove the specified {@link jline.console.completer.Completer} from the list of handlers for tab-completion. + * + * @param completer The {@link Completer} to remove + * @return True if it was successfully removed + */ + public boolean removeCompleter(final Completer completer) { + return completers.remove(completer); + } + + /** + * Returns an unmodifiable list of all the completers. + */ + public Collection<Completer> getCompleters() { + return Collections.unmodifiableList(completers); + } + + public void setCompletionHandler(final CompletionHandler handler) { + this.completionHandler = checkNotNull(handler); + } + + public CompletionHandler getCompletionHandler() { + return this.completionHandler; + } + + /** + * Use the completers to modify the buffer with the appropriate completions. + * + * @return true if successful + */ + protected boolean complete() throws IOException { + // debug ("tab for (" + buf + ")"); + if (completers.size() == 0) { + return false; + } + + List<CharSequence> candidates = new LinkedList<CharSequence>(); + String bufstr = buf.buffer.toString(); + int cursor = buf.cursor; + + int position = -1; + + for (Completer comp : completers) { + if ((position = comp.complete(bufstr, cursor, candidates)) != -1) { + break; + } + } + + return candidates.size() != 0 && getCompletionHandler().complete(this, candidates, position); + } + + protected void printCompletionCandidates() throws IOException { + // debug ("tab for (" + buf + ")"); + if (completers.size() == 0) { + return; + } + + List<CharSequence> candidates = new LinkedList<CharSequence>(); + String bufstr = buf.buffer.toString(); + int cursor = buf.cursor; + + for (Completer comp : completers) { + if (comp.complete(bufstr, cursor, candidates) != -1) { + break; + } + } + CandidateListCompletionHandler.printCandidates(this, candidates); + drawLine(); + } + + /** + * The number of tab-completion candidates above which a warning will be + * prompted before showing all the candidates. + */ + private int autoprintThreshold = Configuration.getInteger(JLINE_COMPLETION_THRESHOLD, 100); // same default as bash + + /** + * @param threshold the number of candidates to print without issuing a warning. + */ + public void setAutoprintThreshold(final int threshold) { + this.autoprintThreshold = threshold; + } + + /** + * @return the number of candidates to print without issuing a warning. + */ + public int getAutoprintThreshold() { + return autoprintThreshold; + } + + private boolean paginationEnabled; + + /** + * Whether to use pagination when the number of rows of candidates exceeds the height of the terminal. + */ + public void setPaginationEnabled(final boolean enabled) { + this.paginationEnabled = enabled; + } + + /** + * Whether to use pagination when the number of rows of candidates exceeds the height of the terminal. + */ + public boolean isPaginationEnabled() { + return paginationEnabled; + } + + // + // History + // + + private History history = new MemoryHistory(); + + public void setHistory(final History history) { + this.history = history; + } + + public History getHistory() { + return history; + } + + private boolean historyEnabled = true; + + /** + * Whether or not to add new commands to the history buffer. + */ + public void setHistoryEnabled(final boolean enabled) { + this.historyEnabled = enabled; + } + + /** + * Whether or not to add new commands to the history buffer. + */ + public boolean isHistoryEnabled() { + return historyEnabled; + } + + /** + * 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 + * @throws IOException + */ + private boolean moveHistory(final boolean next, int count) throws IOException { + boolean ok = true; + for (int i = 0; i < count && (ok = moveHistory(next)); i++) { + /* empty */ + } + return ok; + } + + /** + * Move up or down the history tree. + */ + private boolean moveHistory(final boolean next) throws IOException { + if (next && !history.next()) { + return false; + } + else if (!next && !history.previous()) { + return false; + } + + setBuffer(history.current()); + + return true; + } + + // + // Printing + // + + public static final String CR = Configuration.getLineSeparator(); + + /** + * Output the specified character to the output stream without manipulating the current buffer. + */ + private void print(final int c) throws IOException { + if (c == '\t') { + char chars[] = new char[TAB_WIDTH]; + Arrays.fill(chars, ' '); + out.write(chars); + return; + } + + out.write(c); + } + + /** + * Output the specified characters to the output stream without manipulating the current buffer. + */ + private void print(final char... buff) throws IOException { + int len = 0; + for (char c : buff) { + if (c == '\t') { + len += TAB_WIDTH; + } + else { + len++; + } + } + + char chars[]; + if (len == buff.length) { + chars = buff; + } + else { + chars = new char[len]; + int pos = 0; + for (char c : buff) { + if (c == '\t') { + Arrays.fill(chars, pos, pos + TAB_WIDTH, ' '); + pos += TAB_WIDTH; + } + else { + chars[pos] = c; + pos++; + } + } + } + + out.write(chars); + } + + private void print(final char c, final int num) throws IOException { + if (num == 1) { + print(c); + } + else { + char[] chars = new char[num]; + Arrays.fill(chars, c); + print(chars); + } + } + + /** + * Output the specified string to the output stream (but not the buffer). + */ + public final void print(final CharSequence s) throws IOException { + print(checkNotNull(s).toString().toCharArray()); + } + + public final void println(final CharSequence s) throws IOException { + print(checkNotNull(s).toString().toCharArray()); + println(); + } + + /** + * Output a platform-dependant newline. + */ + public final void println() throws IOException { + print(CR); +// flush(); + } + + // + // Actions + // + + /** + * Issue a delete. + * + * @return true if successful + */ + public final boolean delete() throws IOException { + if (buf.cursor == buf.buffer.length()) { + return false; + } + + buf.buffer.delete(buf.cursor, buf.cursor + 1); + drawBuffer(1); + + return true; + } + + /** + * Kill the buffer ahead of the current cursor position. + * + * @return true if successful + */ + public boolean killLine() throws IOException { + int cp = buf.cursor; + int len = buf.buffer.length(); + + if (cp >= len) { + return false; + } + + int num = len - cp; + clearAhead(num, 0); + + char[] killed = new char[num]; + buf.buffer.getChars(cp, (cp + num), killed, 0); + buf.buffer.delete(cp, (cp + num)); + + String copy = new String(killed); + killRing.add(copy); + + return true; + } + + public boolean yank() throws IOException { + String yanked = killRing.yank(); + + if (yanked == null) { + return false; + } + putString(yanked); + return true; + } + + public boolean yankPop() throws IOException { + if (!killRing.lastYank()) { + return false; + } + String current = killRing.yank(); + if (current == null) { + // This shouldn't happen. + return false; + } + backspace(current.length()); + String yanked = killRing.yankPop(); + if (yanked == null) { + // This shouldn't happen. + return false; + } + + putString(yanked); + return true; + } + + /** + * Clear the screen by issuing the ANSI "clear screen" code. + */ + public boolean clearScreen() throws IOException { + if (!terminal.isAnsiSupported()) { + return false; + } + + // send the ANSI code to clear the screen + printAnsiSequence("2J"); + + // then send the ANSI code to go to position 1,1 + printAnsiSequence("1;1H"); + + return true; + } + + /** + * Issue an audible keyboard bell. + */ + public void beep() throws IOException { + if (bellEnabled) { + print(KEYBOARD_BELL); + // need to flush so the console actually beeps + flush(); + } + } + + //disabled to avoid dependency on java.desktop: +// /** +// * Paste the contents of the clipboard into the console buffer +// * +// * @return true if clipboard contents pasted +// */ +// public boolean paste() throws IOException { +// Clipboard clipboard; +// try { // May throw ugly exception on system without X +// clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); +// } +// catch (Exception e) { +// return false; +// } +// +// if (clipboard == null) { +// return false; +// } +// +// Transferable transferable = clipboard.getContents(null); +// +// if (transferable == null) { +// return false; +// } +// +// try { +// @SuppressWarnings("deprecation") +// Object content = transferable.getTransferData(DataFlavor.plainTextFlavor); +// +// // This fix was suggested in bug #1060649 at +// // http://sourceforge.net/tracker/index.php?func=detail&aid=1060649&group_id=64033&atid=506056 +// // to get around the deprecated DataFlavor.plainTextFlavor, but it +// // raises a UnsupportedFlavorException on Mac OS X +// +// if (content == null) { +// try { +// content = new DataFlavor().getReaderForText(transferable); +// } +// catch (Exception e) { +// // ignore +// } +// } +// +// if (content == null) { +// return false; +// } +// +// String value; +// +// if (content instanceof Reader) { +// // TODO: we might want instead connect to the input stream +// // so we can interpret individual lines +// value = ""; +// String line; +// +// BufferedReader read = new BufferedReader((Reader) content); +// while ((line = read.readLine()) != null) { +// if (value.length() > 0) { +// value += "\n"; +// } +// +// value += line; +// } +// } +// else { +// value = content.toString(); +// } +// +// if (value == null) { +// return true; +// } +// +// putString(value); +// +// return true; +// } +// catch (UnsupportedFlavorException e) { +// Log.error("Paste failed: ", e); +// +// return false; +// } +// } + + //disabled to avoid dependency on java.desktop: +// // +// // Triggered Actions +// // +// +// private final Map<Character, ActionListener> triggeredActions = new HashMap<Character, ActionListener>(); +// +// /** +// * Adding a triggered Action allows to give another curse of action if a character passed the pre-processing. +// * <p/> +// * Say you want to close the application if the user enter q. +// * addTriggerAction('q', new ActionListener(){ System.exit(0); }); would do the trick. +// */ +// public void addTriggeredAction(final char c, final ActionListener listener) { +// triggeredActions.put(c, listener); +// } + + // + // Formatted Output + // + + /** + * Output the specified {@link Collection} in proper columns. + */ + public void printColumns(final Collection<? extends CharSequence> items) throws IOException { + if (items == null || items.isEmpty()) { + return; + } + + int width = getTerminal().getWidth(); + int height = getTerminal().getHeight(); + + int maxWidth = 0; + for (CharSequence item : items) { + maxWidth = Math.max(maxWidth, item.length()); + } + maxWidth = maxWidth + 3; + Log.debug("Max width: ", maxWidth); + + int showLines; + if (isPaginationEnabled()) { + showLines = height - 1; // page limit + } + else { + showLines = Integer.MAX_VALUE; + } + + StringBuilder buff = new StringBuilder(); + for (CharSequence item : items) { + if ((buff.length() + maxWidth) > width) { + println(buff); + buff.setLength(0); + + if (--showLines == 0) { + // Overflow + print(resources.getString("DISPLAY_MORE")); + flush(); + int c = readCharacter(); + if (c == '\r' || c == '\n') { + // one step forward + showLines = 1; + } + else if (c != 'q') { + // page forward + showLines = height - 1; + } + + back(resources.getString("DISPLAY_MORE").length()); + if (c == 'q') { + // cancel + break; + } + } + } + + // NOTE: toString() is important here due to AnsiString being retarded + buff.append(item.toString()); + for (int i = 0; i < (maxWidth - item.length()); i++) { + buff.append(' '); + } + } + + if (buff.length() > 0) { + println(buff); + } + } + + // + // Non-supported Terminal Support + // + + private Thread maskThread; + + private void beforeReadLine(final String prompt, final Character mask) { + if (mask != null && maskThread == null) { + final String fullPrompt = "\r" + prompt + + " " + + " " + + " " + + "\r" + prompt; + + maskThread = new Thread() + { + public void run() { + while (!interrupted()) { + try { + Writer out = getOutput(); + out.write(fullPrompt); + out.flush(); + sleep(3); + } + catch (IOException e) { + return; + } + catch (InterruptedException e) { + return; + } + } + } + }; + + maskThread.setPriority(Thread.MAX_PRIORITY); + maskThread.setDaemon(true); + maskThread.start(); + } + } + + private void afterReadLine() { + if (maskThread != null && maskThread.isAlive()) { + maskThread.interrupt(); + } + + maskThread = null; + } + + /** + * Erases the current line with the existing prompt, then redraws the line + * with the provided prompt and buffer + * @param prompt + * the new prompt + * @param buffer + * the buffer to be drawn + * @param cursorDest + * where you want the cursor set when the line has been drawn. + * -1 for end of line. + * */ + public void resetPromptLine(String prompt, String buffer, int cursorDest) throws IOException { + // move cursor to end of line + moveToEnd(); + + // backspace all text, including prompt + buf.buffer.append(this.prompt); + int promptLength = 0; + if (this.prompt != null) { + promptLength = this.prompt.length(); + } + + buf.cursor += promptLength; + setPrompt(""); + backspaceAll(); + + setPrompt(prompt); + redrawLine(); + setBuffer(buffer); + + // move cursor to destination (-1 will move to end of line) + if (cursorDest < 0) cursorDest = buffer.length(); + setCursorPosition(cursorDest); + + flush(); + } + + public void printSearchStatus(String searchTerm, String match) throws IOException { + printSearchStatus(searchTerm, match, "(reverse-i-search)`"); + } + + public void printForwardSearchStatus(String searchTerm, String match) throws IOException { + printSearchStatus(searchTerm, match, "(i-search)`"); + } + + private void printSearchStatus(String searchTerm, String match, String searchLabel) throws IOException { + String prompt = searchLabel + searchTerm + "': "; + int cursorDest = match.indexOf(searchTerm); + resetPromptLine(prompt, match, cursorDest); + } + + public void restoreLine(String originalPrompt, int cursorDest) throws IOException { + // TODO move cursor to matched string + String prompt = lastLine(originalPrompt); + String buffer = buf.buffer.toString(); + resetPromptLine(prompt, buffer, cursorDest); + } + + // + // 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()); + } + + + public int searchBackwards(String searchTerm, int startIndex, boolean startsWith) { + ListIterator<History.Entry> it = history.entries(startIndex); + while (it.hasPrevious()) { + History.Entry e = it.previous(); + if (startsWith) { + if (e.value().toString().startsWith(searchTerm)) { + return e.index(); + } + } else { + if (e.value().toString().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 int searchForwards(String searchTerm, int startIndex, boolean startsWith) { + if (startIndex >= history.size()) { + startIndex = history.size() - 1; + } + + ListIterator<History.Entry> it = history.entries(startIndex); + + if (searchIndex != -1 && it.hasNext()) { + it.next(); + } + + while (it.hasNext()) { + History.Entry e = it.next(); + if (startsWith) { + if (e.value().toString().startsWith(searchTerm)) { + return e.index(); + } + } else { + if (e.value().toString().contains(searchTerm)) { + return e.index(); + } + } + } + return -1; + } + + // + // 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 + */ + private boolean isDelimiter(final char 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 + */ + private boolean isWhitespace(final char c) { + return Character.isWhitespace (c); + } + + private void printAnsiSequence(String sequence) throws IOException { + print(27); + print('['); + print(sequence); + flush(); // helps with step debugging + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/CursorBuffer.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline.console; + +import static jdk.internal.jline.internal.Preconditions.checkNotNull; + +/** + * A holder for a {@link StringBuilder} that also contains the current cursor position. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public class CursorBuffer +{ + private boolean overTyping = false; + + public int cursor = 0; + + public final StringBuilder buffer = new StringBuilder(); + + public CursorBuffer copy () { + CursorBuffer that = new CursorBuffer(); + that.overTyping = this.overTyping; + that.cursor = this.cursor; + that.buffer.append (this.toString()); + + return that; + } + + public boolean isOverTyping() { + return overTyping; + } + + public void setOverTyping(final boolean b) { + overTyping = b; + } + + public int length() { + return buffer.length(); + } + + public char nextChar() { + if (cursor == buffer.length()) { + return 0; + } else { + return buffer.charAt(cursor); + } + } + + public char current() { + if (cursor <= 0) { + return 0; + } + + return buffer.charAt(cursor - 1); + } + + /** + * 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 {@link #isOverTyping}. + * + * @param c the character to insert + */ + public void write(final char c) { + buffer.insert(cursor++, c); + if (isOverTyping() && cursor < buffer.length()) { + buffer.deleteCharAt(cursor); + } + } + + /** + * Insert the specified chars into the buffer, setting the cursor to the end of the insertion point. + */ + public void write(final CharSequence str) { + checkNotNull(str); + + if (buffer.length() == 0) { + buffer.append(str); + } + else { + buffer.insert(cursor, str); + } + + cursor += str.length(); + + if (isOverTyping() && cursor < buffer.length()) { + buffer.delete(cursor, (cursor + str.length())); + } + } + + public boolean clear() { + if (buffer.length() == 0) { + return false; + } + + buffer.delete(0, buffer.length()); + cursor = 0; + return true; + } + + public String upToCursor() { + if (cursor <= 0) { + return ""; + } + + return buffer.substring(0, cursor); + } + + @Override + public String toString() { + return buffer.toString(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/KeyMap.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline.console; + +import java.util.HashMap; +import java.util.Map; + +/** + * The KeyMap class contains all bindings from keys to operations. + * + * @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a> + * @since 2.6 + */ +public class KeyMap { + + public static final String VI_MOVE = "vi-move"; + public static final String VI_INSERT = "vi-insert"; + public static final String EMACS = "emacs"; + public static final String EMACS_STANDARD = "emacs-standard"; + public static final String EMACS_CTLX = "emacs-ctlx"; + public static final String EMACS_META = "emacs-meta"; + + private static final int KEYMAP_LENGTH = 256; + + private static final Object NULL_FUNCTION = new Object(); + + private Object[] mapping = new Object[KEYMAP_LENGTH]; + private Object anotherKey = null; + private String name; + private boolean isViKeyMap; + + public KeyMap(String name, boolean isViKeyMap) { + this(name, new Object[KEYMAP_LENGTH], isViKeyMap); + } + + protected KeyMap(String name, Object[] mapping, boolean isViKeyMap) { + this.mapping = mapping; + this.name = name; + this.isViKeyMap = isViKeyMap; + } + + public boolean isViKeyMap() { + return isViKeyMap; + } + + public String getName() { + return name; + } + + public Object getAnotherKey() { + return anotherKey; + } + + public void from(KeyMap other) { + this.mapping = other.mapping; + this.anotherKey = other.anotherKey; + } + + public Object getBound( CharSequence keySeq ) { + if (keySeq != null && keySeq.length() > 0) { + KeyMap map = this; + for (int i = 0; i < keySeq.length(); i++) { + char c = keySeq.charAt(i); + if (c > 255) { + return Operation.SELF_INSERT; + } + if (map.mapping[c] instanceof KeyMap) { + if (i == keySeq.length() - 1) { + return map.mapping[c]; + } else { + map = (KeyMap) map.mapping[c]; + } + } else { + return map.mapping[c]; + } + } + } + return null; + } + + public void bindIfNotBound( CharSequence keySeq, Object function ) { + + bind (this, keySeq, function, true); + } + + public void bind( CharSequence keySeq, Object function ) { + + bind (this, keySeq, function, false); + } + + private static void bind( KeyMap map, CharSequence keySeq, Object function ) { + + bind (map, keySeq, function, false); + } + + private static void bind( KeyMap map, CharSequence keySeq, Object 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("anonymous", false); + if (map.mapping[c] != Operation.DO_LOWERCASE_VERSION) { + m.anotherKey = map.mapping[c]; + } + map.mapping[c] = m; + } + map = (KeyMap) map.mapping[c]; + } else { + if (function == null) { + function = NULL_FUNCTION; + } + if (map.mapping[c] instanceof KeyMap) { + map.anotherKey = function; + } else { + Object op = map.mapping[c]; + if (onlyIfNotBound == false + || op == null + || op == Operation.DO_LOWERCASE_VERSION + || op == Operation.VI_MOVEMENT_MODE ) { + + } + + map.mapping[c] = function; + } + } + } + } + } + + public void setBlinkMatchingParen(boolean on) { + if (on) { + bind( "}", Operation.INSERT_CLOSE_CURLY ); + bind( ")", Operation.INSERT_CLOSE_PAREN ); + bind( "]", Operation.INSERT_CLOSE_SQUARE ); + } + } + + private static void bindArrowKeys(KeyMap map) { + + // MS-DOS + bind( map, "\033[0A", Operation.PREVIOUS_HISTORY ); + bind( map, "\033[0B", Operation.BACKWARD_CHAR ); + bind( map, "\033[0C", Operation.FORWARD_CHAR ); + bind( map, "\033[0D", Operation.NEXT_HISTORY ); + + // Windows + bind( map, "\340\000", Operation.KILL_WHOLE_LINE ); + bind( map, "\340\107", Operation.BEGINNING_OF_LINE ); + bind( map, "\340\110", Operation.PREVIOUS_HISTORY ); + bind( map, "\340\111", Operation.BEGINNING_OF_HISTORY ); + bind( map, "\340\113", Operation.BACKWARD_CHAR ); + bind( map, "\340\115", Operation.FORWARD_CHAR ); + bind( map, "\340\117", Operation.END_OF_LINE ); + bind( map, "\340\120", Operation.NEXT_HISTORY ); + bind( map, "\340\121", Operation.END_OF_HISTORY ); + bind( map, "\340\122", Operation.OVERWRITE_MODE ); + bind( map, "\340\123", Operation.DELETE_CHAR ); + + bind( map, "\000\107", Operation.BEGINNING_OF_LINE ); + bind( map, "\000\110", Operation.PREVIOUS_HISTORY ); + bind( map, "\000\111", Operation.BEGINNING_OF_HISTORY ); + bind( map, "\000\110", Operation.PREVIOUS_HISTORY ); + bind( map, "\000\113", Operation.BACKWARD_CHAR ); + bind( map, "\000\115", Operation.FORWARD_CHAR ); + bind( map, "\000\117", Operation.END_OF_LINE ); + bind( map, "\000\120", Operation.NEXT_HISTORY ); + bind( map, "\000\121", Operation.END_OF_HISTORY ); + bind( map, "\000\122", Operation.OVERWRITE_MODE ); + bind( map, "\000\123", Operation.DELETE_CHAR ); + + bind( map, "\033[A", Operation.PREVIOUS_HISTORY ); + bind( map, "\033[B", Operation.NEXT_HISTORY ); + bind( map, "\033[C", Operation.FORWARD_CHAR ); + bind( map, "\033[D", Operation.BACKWARD_CHAR ); + bind( map, "\033[H", Operation.BEGINNING_OF_LINE ); + bind( map, "\033[F", Operation.END_OF_LINE ); + + bind( map, "\033OA", Operation.PREVIOUS_HISTORY ); + bind( map, "\033OB", Operation.NEXT_HISTORY ); + bind( map, "\033OC", Operation.FORWARD_CHAR ); + bind( map, "\033OD", Operation.BACKWARD_CHAR ); + bind( map, "\033OH", Operation.BEGINNING_OF_LINE ); + bind( map, "\033OF", Operation.END_OF_LINE ); + + bind( map, "\033[1~", Operation.BEGINNING_OF_LINE); + bind( map, "\033[4~", Operation.END_OF_LINE); + bind( map, "\033[3~", Operation.DELETE_CHAR); + + // MINGW32 + bind( map, "\0340H", Operation.PREVIOUS_HISTORY ); + bind( map, "\0340P", Operation.NEXT_HISTORY ); + bind( map, "\0340M", Operation.FORWARD_CHAR ); + bind( map, "\0340K", Operation.BACKWARD_CHAR ); + } + +// public boolean isConvertMetaCharsToAscii() { +// return convertMetaCharsToAscii; +// } + +// public void setConvertMetaCharsToAscii(boolean convertMetaCharsToAscii) { +// this.convertMetaCharsToAscii = convertMetaCharsToAscii; +// } + + public static boolean isMeta( char c ) { + return c > 0x7f && c <= 0xff; + } + + public static char unMeta( char c ) { + return (char) (c & 0x7F); + } + + public static char meta( char c ) { + return (char) (c | 0x80); + } + + public static Map<String, KeyMap> keyMaps() { + Map<String, KeyMap> keyMaps = new HashMap<String, KeyMap>(); + + KeyMap emacs = emacs(); + bindArrowKeys(emacs); + keyMaps.put(EMACS, emacs); + keyMaps.put(EMACS_STANDARD, emacs); + keyMaps.put(EMACS_CTLX, (KeyMap) emacs.getBound("\u0018")); + keyMaps.put(EMACS_META, (KeyMap) emacs.getBound("\u001b")); + + KeyMap viMov = viMovement(); + bindArrowKeys(viMov); + keyMaps.put(VI_MOVE, viMov); + keyMaps.put("vi-command", viMov); + + KeyMap viIns = viInsertion(); + bindArrowKeys(viIns); + keyMaps.put(VI_INSERT, viIns); + keyMaps.put("vi", viIns); + + return keyMaps; + } + + public static KeyMap emacs() { + Object[] map = new Object[KEYMAP_LENGTH]; + Object[] ctrl = new Object[] { + // Control keys. + Operation.SET_MARK, /* Control-@ */ + Operation.BEGINNING_OF_LINE, /* Control-A */ + Operation.BACKWARD_CHAR, /* Control-B */ + Operation.INTERRUPT, /* Control-C */ + Operation.EXIT_OR_DELETE_CHAR, /* Control-D */ + Operation.END_OF_LINE, /* Control-E */ + Operation.FORWARD_CHAR, /* Control-F */ + Operation.ABORT, /* Control-G */ + Operation.BACKWARD_DELETE_CHAR, /* Control-H */ + Operation.COMPLETE, /* Control-I */ + Operation.ACCEPT_LINE, /* Control-J */ + Operation.KILL_LINE, /* Control-K */ + Operation.CLEAR_SCREEN, /* Control-L */ + Operation.ACCEPT_LINE, /* Control-M */ + Operation.NEXT_HISTORY, /* Control-N */ + null, /* Control-O */ + Operation.PREVIOUS_HISTORY, /* Control-P */ + Operation.QUOTED_INSERT, /* Control-Q */ + Operation.REVERSE_SEARCH_HISTORY, /* Control-R */ + Operation.FORWARD_SEARCH_HISTORY, /* Control-S */ + Operation.TRANSPOSE_CHARS, /* Control-T */ + Operation.UNIX_LINE_DISCARD, /* Control-U */ + Operation.QUOTED_INSERT, /* Control-V */ + Operation.UNIX_WORD_RUBOUT, /* Control-W */ + emacsCtrlX(), /* Control-X */ + Operation.YANK, /* Control-Y */ + null, /* Control-Z */ + emacsMeta(), /* Control-[ */ + null, /* Control-\ */ + Operation.CHARACTER_SEARCH, /* Control-] */ + null, /* Control-^ */ + Operation.UNDO, /* Control-_ */ + }; + System.arraycopy( ctrl, 0, map, 0, ctrl.length ); + for (int i = 32; i < 256; i++) { + map[i] = Operation.SELF_INSERT; + } + map[DELETE] = Operation.BACKWARD_DELETE_CHAR; + return new KeyMap(EMACS, map, false); + } + + public static final char CTRL_D = (char) 4; + public static final char CTRL_G = (char) 7; + public static final char CTRL_H = (char) 8; + public static final char CTRL_I = (char) 9; + public static final char CTRL_J = (char) 10; + public static final char CTRL_M = (char) 13; + public static final char CTRL_R = (char) 18; + public static final char CTRL_S = (char) 19; + public static final char CTRL_U = (char) 21; + public static final char CTRL_X = (char) 24; + public static final char CTRL_Y = (char) 25; + public static final char ESCAPE = (char) 27; /* Ctrl-[ */ + public static final char CTRL_OB = (char) 27; /* Ctrl-[ */ + public static final char CTRL_CB = (char) 29; /* Ctrl-] */ + + public static final int DELETE = (char) 127; + + public static KeyMap emacsCtrlX() { + Object[] map = new Object[KEYMAP_LENGTH]; + map[CTRL_G] = Operation.ABORT; + map[CTRL_R] = Operation.RE_READ_INIT_FILE; + map[CTRL_U] = Operation.UNDO; + map[CTRL_X] = Operation.EXCHANGE_POINT_AND_MARK; + map['('] = Operation.START_KBD_MACRO; + map[')'] = Operation.END_KBD_MACRO; + for (int i = 'A'; i <= 'Z'; i++) { + map[i] = Operation.DO_LOWERCASE_VERSION; + } + map['e'] = Operation.CALL_LAST_KBD_MACRO; + map[DELETE] = Operation.KILL_LINE; + return new KeyMap(EMACS_CTLX, map, false); + } + + public static KeyMap emacsMeta() { + Object[] map = new Object[KEYMAP_LENGTH]; + map[CTRL_G] = Operation.ABORT; + map[CTRL_H] = Operation.BACKWARD_KILL_WORD; + map[CTRL_I] = Operation.TAB_INSERT; + map[CTRL_J] = Operation.VI_EDITING_MODE; + map[CTRL_M] = Operation.VI_EDITING_MODE; + map[CTRL_R] = Operation.REVERT_LINE; + map[CTRL_Y] = Operation.YANK_NTH_ARG; + map[CTRL_OB] = Operation.COMPLETE; + map[CTRL_CB] = Operation.CHARACTER_SEARCH_BACKWARD; + map[' '] = Operation.SET_MARK; + map['#'] = Operation.INSERT_COMMENT; + map['&'] = Operation.TILDE_EXPAND; + map['*'] = Operation.INSERT_COMPLETIONS; + map['-'] = Operation.DIGIT_ARGUMENT; + map['.'] = Operation.YANK_LAST_ARG; + map['<'] = Operation.BEGINNING_OF_HISTORY; + map['='] = Operation.POSSIBLE_COMPLETIONS; + map['>'] = Operation.END_OF_HISTORY; + map['?'] = Operation.POSSIBLE_COMPLETIONS; + for (int i = 'A'; i <= 'Z'; i++) { + map[i] = Operation.DO_LOWERCASE_VERSION; + } + map['\\'] = Operation.DELETE_HORIZONTAL_SPACE; + map['_'] = Operation.YANK_LAST_ARG; + map['b'] = Operation.BACKWARD_WORD; + map['c'] = Operation.CAPITALIZE_WORD; + map['d'] = Operation.KILL_WORD; + map['f'] = Operation.FORWARD_WORD; + map['l'] = Operation.DOWNCASE_WORD; + map['p'] = Operation.NON_INCREMENTAL_REVERSE_SEARCH_HISTORY; + map['r'] = Operation.REVERT_LINE; + map['t'] = Operation.TRANSPOSE_WORDS; + map['u'] = Operation.UPCASE_WORD; + map['y'] = Operation.YANK_POP; + map['~'] = Operation.TILDE_EXPAND; + map[DELETE] = Operation.BACKWARD_KILL_WORD; + return new KeyMap(EMACS_META, map, false); + } + + public static KeyMap viInsertion() { + Object[] map = new Object[KEYMAP_LENGTH]; + Object[] ctrl = new Object[] { + // Control keys. + null, /* Control-@ */ + Operation.SELF_INSERT, /* Control-A */ + Operation.SELF_INSERT, /* Control-B */ + Operation.SELF_INSERT, /* Control-C */ + Operation.VI_EOF_MAYBE, /* Control-D */ + Operation.SELF_INSERT, /* Control-E */ + Operation.SELF_INSERT, /* Control-F */ + Operation.SELF_INSERT, /* Control-G */ + Operation.BACKWARD_DELETE_CHAR, /* Control-H */ + Operation.COMPLETE, /* Control-I */ + Operation.ACCEPT_LINE, /* Control-J */ + Operation.SELF_INSERT, /* Control-K */ + Operation.SELF_INSERT, /* Control-L */ + Operation.ACCEPT_LINE, /* Control-M */ + Operation.MENU_COMPLETE, /* Control-N */ + Operation.SELF_INSERT, /* Control-O */ + Operation.MENU_COMPLETE_BACKWARD, /* Control-P */ + Operation.SELF_INSERT, /* Control-Q */ + Operation.REVERSE_SEARCH_HISTORY, /* Control-R */ + Operation.FORWARD_SEARCH_HISTORY, /* Control-S */ + Operation.TRANSPOSE_CHARS, /* Control-T */ + Operation.UNIX_LINE_DISCARD, /* Control-U */ + Operation.QUOTED_INSERT, /* Control-V */ + Operation.UNIX_WORD_RUBOUT, /* Control-W */ + Operation.SELF_INSERT, /* Control-X */ + Operation.YANK, /* Control-Y */ + Operation.SELF_INSERT, /* Control-Z */ + Operation.VI_MOVEMENT_MODE, /* Control-[ */ + Operation.SELF_INSERT, /* Control-\ */ + Operation.SELF_INSERT, /* Control-] */ + Operation.SELF_INSERT, /* Control-^ */ + Operation.UNDO, /* Control-_ */ + }; + System.arraycopy( ctrl, 0, map, 0, ctrl.length ); + for (int i = 32; i < 256; i++) { + map[i] = Operation.SELF_INSERT; + } + map[DELETE] = Operation.BACKWARD_DELETE_CHAR; + return new KeyMap(VI_INSERT, map, false); + } + + public static KeyMap viMovement() { + Object[] map = new Object[KEYMAP_LENGTH]; + Object[] low = new Object[] { + // Control keys. + null, /* Control-@ */ + null, /* Control-A */ + null, /* Control-B */ + Operation.INTERRUPT, /* Control-C */ + /* + * ^D is supposed to move down half a screen. In bash + * appears to be ignored. + */ + Operation.VI_EOF_MAYBE, /* Control-D */ + Operation.EMACS_EDITING_MODE, /* Control-E */ + null, /* Control-F */ + Operation.ABORT, /* Control-G */ + Operation.BACKWARD_CHAR, /* Control-H */ + null, /* Control-I */ + Operation.VI_MOVE_ACCEPT_LINE, /* Control-J */ + Operation.KILL_LINE, /* Control-K */ + Operation.CLEAR_SCREEN, /* Control-L */ + Operation.VI_MOVE_ACCEPT_LINE, /* Control-M */ + Operation.VI_NEXT_HISTORY, /* Control-N */ + null, /* Control-O */ + Operation.VI_PREVIOUS_HISTORY, /* Control-P */ + /* + * My testing with readline is the ^Q is ignored. + * Maybe this should be null? + */ + Operation.QUOTED_INSERT, /* Control-Q */ + + /* + * TODO - Very broken. While in forward/reverse + * history search the VI keyset should go out the + * window and we need to enter a very simple keymap. + */ + Operation.REVERSE_SEARCH_HISTORY, /* Control-R */ + /* TODO */ + Operation.FORWARD_SEARCH_HISTORY, /* Control-S */ + Operation.TRANSPOSE_CHARS, /* Control-T */ + Operation.UNIX_LINE_DISCARD, /* Control-U */ + /* TODO */ + Operation.QUOTED_INSERT, /* Control-V */ + Operation.UNIX_WORD_RUBOUT, /* Control-W */ + null, /* Control-X */ + /* TODO */ + Operation.YANK, /* Control-Y */ + null, /* Control-Z */ + emacsMeta(), /* Control-[ */ + null, /* Control-\ */ + /* TODO */ + Operation.CHARACTER_SEARCH, /* Control-] */ + null, /* Control-^ */ + /* TODO */ + Operation.UNDO, /* Control-_ */ + Operation.FORWARD_CHAR, /* SPACE */ + null, /* ! */ + null, /* " */ + Operation.VI_INSERT_COMMENT, /* # */ + Operation.END_OF_LINE, /* $ */ + Operation.VI_MATCH, /* % */ + Operation.VI_TILDE_EXPAND, /* & */ + null, /* ' */ + null, /* ( */ + null, /* ) */ + /* TODO */ + Operation.VI_COMPLETE, /* * */ + Operation.VI_NEXT_HISTORY, /* + */ + Operation.VI_CHAR_SEARCH, /* , */ + Operation.VI_PREVIOUS_HISTORY, /* - */ + /* TODO */ + Operation.VI_REDO, /* . */ + Operation.VI_SEARCH, /* / */ + Operation.VI_BEGNNING_OF_LINE_OR_ARG_DIGIT, /* 0 */ + Operation.VI_ARG_DIGIT, /* 1 */ + Operation.VI_ARG_DIGIT, /* 2 */ + Operation.VI_ARG_DIGIT, /* 3 */ + Operation.VI_ARG_DIGIT, /* 4 */ + Operation.VI_ARG_DIGIT, /* 5 */ + Operation.VI_ARG_DIGIT, /* 6 */ + Operation.VI_ARG_DIGIT, /* 7 */ + Operation.VI_ARG_DIGIT, /* 8 */ + Operation.VI_ARG_DIGIT, /* 9 */ + null, /* : */ + Operation.VI_CHAR_SEARCH, /* ; */ + null, /* < */ + Operation.VI_COMPLETE, /* = */ + null, /* > */ + Operation.VI_SEARCH, /* ? */ + null, /* @ */ + Operation.VI_APPEND_EOL, /* A */ + Operation.VI_PREV_WORD, /* B */ + Operation.VI_CHANGE_TO_EOL, /* C */ + Operation.VI_DELETE_TO_EOL, /* D */ + Operation.VI_END_WORD, /* E */ + Operation.VI_CHAR_SEARCH, /* F */ + /* I need to read up on what this does */ + Operation.VI_FETCH_HISTORY, /* G */ + null, /* H */ + Operation.VI_INSERT_BEG, /* I */ + null, /* J */ + null, /* K */ + null, /* L */ + null, /* M */ + Operation.VI_SEARCH_AGAIN, /* N */ + null, /* O */ + Operation.VI_PUT, /* P */ + null, /* Q */ + /* TODO */ + Operation.VI_REPLACE, /* R */ + Operation.VI_KILL_WHOLE_LINE, /* S */ + Operation.VI_CHAR_SEARCH, /* T */ + /* TODO */ + Operation.REVERT_LINE, /* U */ + null, /* V */ + Operation.VI_NEXT_WORD, /* W */ + Operation.VI_RUBOUT, /* X */ + Operation.VI_YANK_TO, /* Y */ + null, /* Z */ + null, /* [ */ + Operation.VI_COMPLETE, /* \ */ + null, /* ] */ + Operation.VI_FIRST_PRINT, /* ^ */ + Operation.VI_YANK_ARG, /* _ */ + Operation.VI_GOTO_MARK, /* ` */ + Operation.VI_APPEND_MODE, /* a */ + Operation.VI_PREV_WORD, /* b */ + Operation.VI_CHANGE_TO, /* c */ + Operation.VI_DELETE_TO, /* d */ + Operation.VI_END_WORD, /* e */ + Operation.VI_CHAR_SEARCH, /* f */ + null, /* g */ + Operation.BACKWARD_CHAR, /* h */ + Operation.VI_INSERTION_MODE, /* i */ + Operation.NEXT_HISTORY, /* j */ + Operation.PREVIOUS_HISTORY, /* k */ + Operation.FORWARD_CHAR, /* l */ + Operation.VI_SET_MARK, /* m */ + Operation.VI_SEARCH_AGAIN, /* n */ + null, /* o */ + Operation.VI_PUT, /* p */ + null, /* q */ + Operation.VI_CHANGE_CHAR, /* r */ + Operation.VI_SUBST, /* s */ + Operation.VI_CHAR_SEARCH, /* t */ + Operation.UNDO, /* u */ + null, /* v */ + Operation.VI_NEXT_WORD, /* w */ + Operation.VI_DELETE, /* x */ + Operation.VI_YANK_TO, /* y */ + null, /* z */ + null, /* { */ + Operation.VI_COLUMN, /* | */ + null, /* } */ + Operation.VI_CHANGE_CASE, /* ~ */ + Operation.VI_DELETE /* DEL */ + }; + System.arraycopy( low, 0, map, 0, low.length ); + for (int i = 128; i < 256; i++) { + map[i] = null; + } + return new KeyMap(VI_MOVE, map, false); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/KillRing.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2002-2013, 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 jdk.internal.jline.console; + +/** + * 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 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; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/Operation.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline.console; + +/** + * List of all operations. + * + * @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a> + * @since 2.6 + */ +public enum Operation { + + ABORT, + ACCEPT_LINE, + ARROW_KEY_PREFIX, + BACKWARD_BYTE, + BACKWARD_CHAR, + BACKWARD_DELETE_CHAR, + BACKWARD_KILL_LINE, + BACKWARD_KILL_WORD, + BACKWARD_WORD, + BEGINNING_OF_HISTORY, + BEGINNING_OF_LINE, + CALL_LAST_KBD_MACRO, + CAPITALIZE_WORD, + CHARACTER_SEARCH, + CHARACTER_SEARCH_BACKWARD, + CLEAR_SCREEN, + COMPLETE, + COPY_BACKWARD_WORD, + COPY_FORWARD_WORD, + COPY_REGION_AS_KILL, + DELETE_CHAR, + DELETE_CHAR_OR_LIST, + DELETE_HORIZONTAL_SPACE, + DIGIT_ARGUMENT, + DO_LOWERCASE_VERSION, + DOWNCASE_WORD, + DUMP_FUNCTIONS, + DUMP_MACROS, + DUMP_VARIABLES, + EMACS_EDITING_MODE, + END_KBD_MACRO, + END_OF_HISTORY, + END_OF_LINE, + EXCHANGE_POINT_AND_MARK, + EXIT_OR_DELETE_CHAR, + FORWARD_BACKWARD_DELETE_CHAR, + FORWARD_BYTE, + FORWARD_CHAR, + FORWARD_SEARCH_HISTORY, + FORWARD_WORD, + HISTORY_SEARCH_BACKWARD, + HISTORY_SEARCH_FORWARD, + INSERT_CLOSE_CURLY, + INSERT_CLOSE_PAREN, + INSERT_CLOSE_SQUARE, + INSERT_COMMENT, + INSERT_COMPLETIONS, + INTERRUPT, + KILL_WHOLE_LINE, + KILL_LINE, + KILL_REGION, + KILL_WORD, + MENU_COMPLETE, + MENU_COMPLETE_BACKWARD, + NEXT_HISTORY, + NON_INCREMENTAL_FORWARD_SEARCH_HISTORY, + NON_INCREMENTAL_REVERSE_SEARCH_HISTORY, + NON_INCREMENTAL_FORWARD_SEARCH_HISTORY_AGAIN, + NON_INCREMENTAL_REVERSE_SEARCH_HISTORY_AGAIN, + OLD_MENU_COMPLETE, + OVERWRITE_MODE, + PASTE_FROM_CLIPBOARD, + POSSIBLE_COMPLETIONS, + PREVIOUS_HISTORY, + QUOTED_INSERT, + RE_READ_INIT_FILE, + REDRAW_CURRENT_LINE, + REVERSE_SEARCH_HISTORY, + REVERT_LINE, + SELF_INSERT, + SET_MARK, + SKIP_CSI_SEQUENCE, + START_KBD_MACRO, + TAB_INSERT, + TILDE_EXPAND, + TRANSPOSE_CHARS, + TRANSPOSE_WORDS, + TTY_STATUS, + UNDO, + UNIVERSAL_ARGUMENT, + UNIX_FILENAME_RUBOUT, + UNIX_LINE_DISCARD, + UNIX_WORD_RUBOUT, + UPCASE_WORD, + YANK, + YANK_LAST_ARG, + YANK_NTH_ARG, + YANK_POP, + VI_APPEND_EOL, + VI_APPEND_MODE, + VI_ARG_DIGIT, + VI_BACK_TO_INDENT, + VI_BACKWARD_BIGWORD, + VI_BACKWARD_WORD, + VI_BWORD, + VI_CHANGE_CASE, + VI_CHANGE_CHAR, + VI_CHANGE_TO, + VI_CHANGE_TO_EOL, + VI_CHAR_SEARCH, + VI_COLUMN, + VI_COMPLETE, + VI_DELETE, + VI_DELETE_TO, + VI_DELETE_TO_EOL, + VI_EDITING_MODE, + VI_END_BIGWORD, + VI_END_WORD, + VI_EOF_MAYBE, + VI_EWORD, + VI_FWORD, + VI_FETCH_HISTORY, + VI_FIRST_PRINT, + VI_FORWARD_BIGWORD, + VI_FORWARD_WORD, + VI_GOTO_MARK, + VI_INSERT_BEG, + VI_INSERTION_MODE, + VI_KILL_WHOLE_LINE, + VI_MATCH, + VI_MOVEMENT_MODE, + VI_NEXT_WORD, + VI_OVERSTRIKE, + VI_OVERSTRIKE_DELETE, + VI_PREV_WORD, + VI_PUT, + VI_REDO, + VI_REPLACE, + VI_RUBOUT, + VI_SEARCH, + VI_SEARCH_AGAIN, + VI_SET_MARK, + VI_SUBST, + VI_TILDE_EXPAND, + VI_YANK_ARG, + VI_YANK_TO, + VI_MOVE_ACCEPT_LINE, + VI_NEXT_HISTORY, + VI_PREVIOUS_HISTORY, + VI_INSERT_COMMENT, + VI_BEGNNING_OF_LINE_OR_ARG_DIGIT, +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/UserInterruptException.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline.console; + +/** + * This exception is thrown by {@link ConsoleReader#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; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/completer/AggregateCompleter.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline.console.completer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import static jdk.internal.jline.internal.Preconditions.checkNotNull; + +/** + * Completer which contains multiple completers and aggregates them together. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class AggregateCompleter + implements Completer +{ + private final List<Completer> completers = new ArrayList<Completer>(); + + public AggregateCompleter() { + // empty + } + + /** + * Construct an AggregateCompleter with the given collection of completers. + * The completers will be used in the iteration order of the collection. + * + * @param completers the collection of completers + */ + public AggregateCompleter(final Collection<Completer> completers) { + checkNotNull(completers); + this.completers.addAll(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)); + } + + /** + * Retrieve the collection of completers currently being aggregated. + * + * @return the aggregated completers + */ + public Collection<Completer> getCompleters() { + return completers; + } + + /** + * Perform a completion operation across all aggregated completers. + * + * @see Completer#complete(String, int, java.util.List) + * @return the highest completion return value from all completers + */ + public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { + // buffer could be null + checkNotNull(candidates); + + List<Completion> completions = new ArrayList<Completion>(completers.size()); + + // Run each completer, saving its completion results + int max = -1; + for (Completer completer : completers) { + Completion completion = new Completion(candidates); + completion.complete(completer, buffer, cursor); + + // Compute the max cursor position + max = Math.max(max, completion.cursor); + + completions.add(completion); + } + + // Append candidates from completions which have the same cursor position as max + for (Completion completion : completions) { + if (completion.cursor == max) { + candidates.addAll(completion.candidates); + } + } + + return max; + } + + /** + * @return a string representing the aggregated completers + */ + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "completers=" + completers + + '}'; + } + + private class Completion + { + public final List<CharSequence> candidates; + + public int cursor; + + public Completion(final List<CharSequence> candidates) { + checkNotNull(candidates); + this.candidates = new LinkedList<CharSequence>(candidates); + } + + public void complete(final Completer completer, final String buffer, final int cursor) { + checkNotNull(completer); + this.cursor = completer.complete(buffer, cursor, candidates); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/completer/ArgumentCompleter.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline.console.completer; + +import jdk.internal.jline.internal.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import static jdk.internal.jline.internal.Preconditions.checkNotNull; + +/** + * A {@link Completer} implementation that invokes a child completer using the appropriate <i>separator</i> argument. + * This can be used instead of the individual completers having to know about argument parsing semantics. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class ArgumentCompleter + implements Completer +{ + private final ArgumentDelimiter delimiter; + + private final List<Completer> completers = new ArrayList<Completer>(); + + private boolean strict = true; + + /** + * Create a new completer with the specified argument delimiter. + * + * @param delimiter The delimiter for parsing arguments + * @param completers The embedded completers + */ + public ArgumentCompleter(final ArgumentDelimiter delimiter, final Collection<Completer> completers) { + this.delimiter = checkNotNull(delimiter); + checkNotNull(completers); + this.completers.addAll(completers); + } + + /** + * Create a new completer with the specified argument delimiter. + * + * @param delimiter The delimiter for parsing arguments + * @param completers The embedded completers + */ + public ArgumentCompleter(final ArgumentDelimiter delimiter, final Completer... completers) { + this(delimiter, Arrays.asList(completers)); + } + + /** + * Create a new completer with the default {@link WhitespaceArgumentDelimiter}. + * + * @param completers The embedded completers + */ + public ArgumentCompleter(final Completer... completers) { + this(new WhitespaceArgumentDelimiter(), completers); + } + + /** + * Create a new completer with the default {@link WhitespaceArgumentDelimiter}. + * + * @param completers The embedded completers + */ + public ArgumentCompleter(final List<Completer> completers) { + this(new WhitespaceArgumentDelimiter(), 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 ArgumentDelimiter getDelimiter() { + return delimiter; + } + + /** + * @since 2.3 + */ + public List<Completer> getCompleters() { + return completers; + } + + public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { + // buffer can be null + checkNotNull(candidates); + + ArgumentDelimiter delim = getDelimiter(); + ArgumentList list = delim.delimit(buffer, cursor); + int argpos = list.getArgumentPosition(); + int argIndex = list.getCursorArgumentIndex(); + + if (argIndex < 0) { + return -1; + } + + List<Completer> completers = getCompleters(); + Completer completer; + + // if we are beyond the end of the completers, just use the last one + if (argIndex >= completers.size()) { + completer = completers.get(completers.size() - 1); + } + else { + completer = completers.get(argIndex); + } + + // ensure that all the previous completers are successful before allowing this completer to pass (only if strict). + for (int i = 0; isStrict() && (i < argIndex); i++) { + Completer sub = completers.get(i >= completers.size() ? (completers.size() - 1) : i); + String[] args = list.getArguments(); + String arg = (args == null || i >= args.length) ? "" : args[i]; + + List<CharSequence> subCandidates = new LinkedList<CharSequence>(); + + if (sub.complete(arg, arg.length(), subCandidates) == -1) { + return -1; + } + + if (subCandidates.size() == 0) { + return -1; + } + } + + int ret = completer.complete(list.getCursorArgument(), argpos, candidates); + + if (ret == -1) { + return -1; + } + + int pos = ret + list.getBufferPosition() - argpos; + + // Special case: when completing in the middle of a line, and the area under the cursor is a delimiter, + // then trim any delimiters from the candidates, since we do not need to have an extra delimiter. + // + // E.g., if we have a completion for "foo", and we enter "f bar" into the buffer, and move to after the "f" + // and hit TAB, we want "foo bar" instead of "foo bar". + + if ((cursor != buffer.length()) && delim.isDelimiter(buffer, cursor)) { + for (int i = 0; i < candidates.size(); i++) { + CharSequence val = candidates.get(i); + + while (val.length() > 0 && delim.isDelimiter(val, val.length() - 1)) { + val = val.subSequence(0, val.length() - 1); + } + + candidates.set(i, val); + } + } + + Log.trace("Completing ", buffer, " (pos=", cursor, ") with: ", candidates, ": offset=", pos); + + return pos; + } + + /** + * The {@link ArgumentCompleter.ArgumentDelimiter} allows custom breaking up of a {@link String} into individual + * arguments in order to dispatch the arguments to the nested {@link Completer}. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ + public static interface ArgumentDelimiter + { + /** + * Break the specified buffer into individual tokens that can be completed on their own. + * + * @param buffer The buffer to split + * @param pos The current position of the cursor in the buffer + * @return The tokens + */ + ArgumentList delimit(CharSequence buffer, int pos); + + /** + * Returns true if the specified character is a whitespace parameter. + * + * @param buffer The complete command buffer + * @param pos The index of the character in the buffer + * @return True if the character should be a delimiter + */ + boolean isDelimiter(CharSequence buffer, int pos); + } + + /** + * Abstract implementation of a delimiter that uses the {@link #isDelimiter} method to determine if a particular + * character should be used as a delimiter. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ + public abstract static class AbstractArgumentDelimiter + implements ArgumentDelimiter + { + private char[] quoteChars = {'\'', '"'}; + + private char[] escapeChars = {'\\'}; + + 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 ArgumentList delimit(final CharSequence buffer, final int cursor) { + List<String> args = new LinkedList<String>(); + StringBuilder arg = new StringBuilder(); + int argpos = -1; + int bindex = -1; + int quoteStart = -1; + + for (int i = 0; (buffer != null) && (i < buffer.length()); i++) { + // once we reach the cursor, set the + // position of the selected index + if (i == cursor) { + bindex = args.size(); + // the position in the current argument is just the + // length of the current argument + argpos = arg.length(); + } + + if (quoteStart < 0 && isQuoteChar(buffer, i)) { + // Start a quote block + quoteStart = i; + } else if (quoteStart >= 0) { + // In a quote block + if (buffer.charAt(quoteStart) == buffer.charAt(i) && !isEscaped(buffer, i)) { + // End the block; arg could be empty, but that's fine + args.add(arg.toString()); + arg.setLength(0); + quoteStart = -1; + } else if (!isEscapeChar(buffer, i)) { + // Take the next character + arg.append(buffer.charAt(i)); + } + } else { + // Not in a quote block + if (isDelimiter(buffer, i)) { + if (arg.length() > 0) { + args.add(arg.toString()); + arg.setLength(0); // reset the arg + } + } else if (!isEscapeChar(buffer, i)) { + arg.append(buffer.charAt(i)); + } + } + } + + if (cursor == buffer.length()) { + bindex = args.size(); + // the position in the current argument is just the + // length of the current argument + argpos = arg.length(); + } + if (arg.length() > 0) { + args.add(arg.toString()); + } + + return new ArgumentList(args.toArray(new String[args.size()]), bindex, argpos, cursor); + } + + /** + * Returns true if the specified character is a whitespace parameter. Check to ensure that the character is not + * escaped by any of {@link #getQuoteChars}, and is not escaped by ant of the {@link #getEscapeChars}, and + * returns true from {@link #isDelimiterChar}. + * + * @param buffer The complete command buffer + * @param pos The index of the character in the buffer + * @return True if the character should be a delimiter + */ + public boolean isDelimiter(final 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) + * + * @param buffer + * @param pos + * @return + */ + 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 abstract boolean isDelimiterChar(CharSequence buffer, int pos); + } + + /** + * {@link ArgumentCompleter.ArgumentDelimiter} implementation that counts all whitespace (as reported by + * {@link Character#isWhitespace}) as being a delimiter. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ + public static class WhitespaceArgumentDelimiter + extends AbstractArgumentDelimiter + { + /** + * The character is a delimiter if it is whitespace, and the + * preceding character is not an escape character. + */ + @Override + public boolean isDelimiterChar(final CharSequence buffer, final int pos) { + return Character.isWhitespace(buffer.charAt(pos)); + } + } + + /** + * The result of a delimited buffer. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ + public static class ArgumentList + { + private String[] arguments; + + private int cursorArgumentIndex; + + private int argumentPosition; + + private int bufferPosition; + + /** + * @param arguments The array of tokens + * @param cursorArgumentIndex The token index of the cursor + * @param argumentPosition The position of the cursor in the current token + * @param bufferPosition The position of the cursor in the whole buffer + */ + public ArgumentList(final String[] arguments, final int cursorArgumentIndex, final int argumentPosition, final int bufferPosition) { + this.arguments = checkNotNull(arguments); + this.cursorArgumentIndex = cursorArgumentIndex; + this.argumentPosition = argumentPosition; + this.bufferPosition = bufferPosition; + } + + public void setCursorArgumentIndex(final int i) { + this.cursorArgumentIndex = i; + } + + public int getCursorArgumentIndex() { + return this.cursorArgumentIndex; + } + + public String getCursorArgument() { + if ((cursorArgumentIndex < 0) || (cursorArgumentIndex >= arguments.length)) { + return null; + } + + return arguments[cursorArgumentIndex]; + } + + public void setArgumentPosition(final int pos) { + this.argumentPosition = pos; + } + + public int getArgumentPosition() { + return this.argumentPosition; + } + + public void setArguments(final String[] arguments) { + this.arguments = arguments; + } + + public String[] getArguments() { + return this.arguments; + } + + public void setBufferPosition(final int pos) { + this.bufferPosition = pos; + } + + public int getBufferPosition() { + return this.bufferPosition; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/completer/CandidateListCompletionHandler.java Thu Jul 09 16:37:55 2015 -0700 @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2002-2012, 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 jdk.internal.jline.console.completer; + +import jdk.internal.jline.console.ConsoleReader; +import jdk.internal.jline.console.CursorBuffer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.Set; + +/** + * A {@link CompletionHandler} that deals with multiple distinct completions + * by outputting the complete list of possibilities to the console. This + * mimics the behavior of the + * <a href="http://www.gnu.org/directory/readline.html">readline</a> library. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class CandidateListCompletionHandler + implements CompletionHandler +{ + // TODO: handle quotes and escaped quotes && enable automatic escaping of whitespace + + public boolean complete(final ConsoleReader reader, final List<CharSequence> candidates, final int pos) throws + IOException + { + CursorBuffer buf = reader.getCursorBuffer(); + + // if there is only one completion, then fill in the buffer + if (candidates.size() == 1) { + CharSequence value = candidates.get(0); + + // fail if the only candidate is the same as the current buffer + if (value.equals(buf.toString())) { + return false; + } + + setBuffer(reader, value, pos); + + return true; + } + else if (candidates.size() > 1) { + String value = getUnambiguousCompletions(candidates); + setBuffer(reader, value, pos); + } + + printCandidates(reader, candidates); + + // redraw the current console buffer + reader.drawLine(); + + return true; + } + + public static void setBuffer(final ConsoleReader reader, final CharSequence value, final int offset) throws + IOException + { + while ((reader.getCursorBuffer().cursor > offset) && reader.backspace()) { + // empty + } + + reader.putString(value); + reader.setCursorPosition(offset + value.length()); + } + + /** + * Print out the candidates. If the size of the candidates is greater than the + * {@link ConsoleReader#getAutoprintThreshold}, they prompt with a warning. + * + * @param candidates the list of candidates to print + */ + public static void printCandidates(final ConsoleReader reader, Collection<CharSequence> candidates) throws + IOException + { + Set<CharSequence> distinct = new HashSet<CharSequence>(candidates); + + if (distinct.size() > reader.getAutoprintThreshold()) { + //noinspection StringConcatenation + reader.print(Messages.DISPLAY_CANDIDATES.format(candidates.size())); + reader.flush(); + + int c; + + String noOpt = Messages.DISPLAY_CANDIDATES_NO.format(); + String yesOpt = Messages.DISPLAY_CANDIDATES_YES.format(); + char[] allowed = {yesOpt.charAt(0), noOpt.charAt(0)}; + + while ((c = reader.readCharacter(allowed)) != -1) { + String tmp = new String(new char[]{(char) c}); + + if (noOpt.startsWith(tmp)) { + reader.println(); + return; + } + else if (yesOpt.startsWith(tmp)) { + break; + } + else { + reader.beep(); + } + } + } + + // copy the values and make them distinct, without otherwise affecting the ordering. Only do it if the sizes differ. + if (distinct.size() != candidates.size()) { + Collection<CharSequence> copy = new ArrayList<CharSequence>(); + + for (CharSequence next : candidates) { + if (!copy.contains(next)) { + copy.add(next); + } + } + + candidates = copy; + } + + reader.println(); + reader.printColumns(candidates); + } + + /** + * Returns a root that matches all the {@link String} elements of the specified {@link List}, + * or null if there are no commonalities. For example, if the list contains + * <i>foobar</i>, <i>foobaz</i>, <i>foobuz</i>, the method will return <i>foob</i>. + */ + private String getUnambiguo