From: <cr...@us...> - 2008-08-24 14:03:08
|
Revision: 4493 http://jnode.svn.sourceforge.net/jnode/?rev=4493&view=rev Author: crawley Date: 2008-08-24 14:03:04 +0000 (Sun, 24 Aug 2008) Log Message: ----------- Fix TAB completion to use the current interpreter to escape any meta-characters in each completion found. Modified Paths: -------------- trunk/core/src/driver/org/jnode/driver/console/CompletionInfo.java trunk/core/src/driver/org/jnode/driver/console/textscreen/Line.java trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java trunk/shell/src/shell/org/jnode/shell/CommandShell.java trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java trunk/shell/src/shell/org/jnode/shell/syntax/URLArgument.java trunk/shell/src/test/org/jnode/test/shell/CompletionInfoTest.java Added Paths: ----------- trunk/shell/src/shell/org/jnode/shell/CommandCompletions.java Modified: trunk/core/src/driver/org/jnode/driver/console/CompletionInfo.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/CompletionInfo.java 2008-08-24 11:38:12 UTC (rev 4492) +++ trunk/core/src/driver/org/jnode/driver/console/CompletionInfo.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -29,14 +29,8 @@ * @author Ewout Prangsma (ep...@us...) * @author cr...@jn... */ -public class CompletionInfo { - private static final SortedSet<String> NO_COMPLETIONS = - Collections.unmodifiableSortedSet(new TreeSet<String>()); +public interface CompletionInfo { - private TreeSet<String> completions; - - private int completionStart = -1; - /** * This method is called to register a possible completion. A null or empty * completion string will be quietly ignored. @@ -45,18 +39,7 @@ * @param partial if <code>true</code>, further completions of the * completion string may be possible. */ - public void addCompletion(String completion, boolean partial) { - if (completion == null || completion.length() == 0) { - return; - } - if (completions == null) { - completions = new TreeSet<String>(); - } - if (!partial) { - completion += ' '; - } - completions.add(completion); - } + public void addCompletion(String completion, boolean partial); /** * This method is called to register a completion than cannot be completed @@ -64,53 +47,22 @@ * * @param completion the completion string */ - public void addCompletion(String completion) { - addCompletion(completion, false); - } + public void addCompletion(String completion); /** * Retrieve the completion details. * * @return a TreeSet consisting of all possible completions */ - public SortedSet<String> getCompletions() { - return completions == null ? NO_COMPLETIONS : completions; - } + public SortedSet<String> getCompletions(); /** - * Render for debug purposes - */ - public String toString() { - StringBuilder sb = new StringBuilder("CompletionInfo{"); - sb.append("competionStart=").append(completionStart); - sb.append(",completions="); - if (completions == null) { - sb.append("null"); - } else { - sb.append("{"); - boolean first = true; - for (String completion : completions) { - if (first) { - first = false; - } else { - sb.append(","); - } - sb.append(completion); - } - sb.append("]"); - } - return sb.toString(); - } - - /** * The completion start is the offset in the original string of the first * character to be replaced with the 'completed'. * * @return the completion start position, or <code>-1</code> */ - public int getCompletionStart() { - return completionStart; - } + public int getCompletionStart(); /** * Set the completion start position. This can only be set once. After that, @@ -118,15 +70,7 @@ * * @param completionStart */ - public void setCompletionStart(int completionStart) { - if (this.completionStart != completionStart) { - if (this.completionStart != -1) { - throw new IllegalArgumentException( - "completionStart cannot be changed"); - } - this.completionStart = completionStart; - } - } + public void setCompletionStart(int completionStart); /** * Get the combined completion string. If there are multiple alternatives, @@ -136,31 +80,5 @@ * * @return the combined completion, or <code>null</code>. */ - public String getCompletion() { - if (completions == null) { - return null; - } - int nos = completions.size(); - if (nos == 0) { - return null; - } - if (nos == 1) { - return completions.first(); - } - String common = completions.first(); - for (String completion : completions) { - if (common != completion && !completion.startsWith(common)) { - for (int i = 0; i < common.length(); i++) { - if (common.charAt(i) != completion.charAt(i)) { - if (i == 0) { - return null; - } - common = common.substring(0, i); - break; - } - } - } - } - return common; - } + public String getCompletion(); } Modified: trunk/core/src/driver/org/jnode/driver/console/textscreen/Line.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/textscreen/Line.java 2008-08-24 11:38:12 UTC (rev 4492) +++ trunk/core/src/driver/org/jnode/driver/console/textscreen/Line.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -231,9 +231,9 @@ final int nbLines = (items.length / nbColumns) + (lastLineIsFull ? 0 : 1); String[] lines = new String[nbLines]; - StringBuilder line = new StringBuilder(SCREEN_WIDTH); int lineNum = 0; for (int itemNum = 0; itemNum < items.length;) { + StringBuilder line = new StringBuilder(SCREEN_WIDTH); for (int c = 0; c < nbColumns; c++) { final String item = items[itemNum++]; line.append(item); @@ -246,9 +246,7 @@ if (itemNum >= items.length) break; } - lines[lineNum++] = line.toString(); - line.setLength(0); // clear the buffer } return lines; Added: trunk/shell/src/shell/org/jnode/shell/CommandCompletions.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandCompletions.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/CommandCompletions.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -0,0 +1,190 @@ +/* + * $Id: CompletionInfo.java 2224 2006-01-01 12:49:03Z epr $ + * + * JNode.org + * Copyright (C) 2003-2006 JNode.org + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; If not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.jnode.shell; + +import java.util.Collections; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.jnode.driver.console.CompletionInfo; + +/** + * @author Ewout Prangsma (ep...@us...) + * @author cr...@jn... + */ +public class CommandCompletions implements CompletionInfo { + private static final SortedSet<String> NO_COMPLETIONS = + Collections.unmodifiableSortedSet(new TreeSet<String>()); + + private TreeSet<String> completions; + + private int completionStart = -1; + + private final CommandInterpreter interpreter; + + /** + * Instantiate with a CommandInterpreter instance that will be used + * to escape shell meta-characters in the completions. + * @param interpreter the CommandInterpreter for escaping completions. + */ + public CommandCompletions(CommandInterpreter interpreter) { + this.interpreter = interpreter; + } + + /** + * Instantiate without a CommandInterpreter. Completions are captured + * as-is. + */ + public CommandCompletions() { + this.interpreter = null; + } + + /** + * This method is called to register a possible completion. A null or empty + * completion string will be quietly ignored. + * + * @param completion the completion string + * @param partial if <code>true</code>, further completions of the + * completion string may be possible. + */ + public void addCompletion(String completion, boolean partial) { + if (completion == null || completion.length() == 0) { + return; + } + if (completions == null) { + completions = new TreeSet<String>(); + } + if (interpreter != null) { + completion = interpreter.escapeWord(completion); + } + if (!partial) { + completion += ' '; + } + completions.add(completion); + } + + /** + * This method is called to register a completion than cannot be completed + * further. A null or empty completion string will be quietly ignored. + * + * @param completion the completion string + */ + public void addCompletion(String completion) { + addCompletion(completion, false); + } + + /** + * Retrieve the completion details. + * + * @return a TreeSet consisting of all possible completions + */ + public SortedSet<String> getCompletions() { + return completions == null ? NO_COMPLETIONS : completions; + } + + /** + * Render for debug purposes + */ + public String toString() { + StringBuilder sb = new StringBuilder("CompletionInfo{"); + sb.append("competionStart=").append(completionStart); + sb.append(",completions="); + if (completions == null) { + sb.append("null"); + } else { + sb.append("{"); + boolean first = true; + for (String completion : completions) { + if (first) { + first = false; + } else { + sb.append(","); + } + sb.append(completion); + } + sb.append("]"); + } + return sb.toString(); + } + + /** + * The completion start is the offset in the original string of the first + * character to be replaced with the 'completed'. + * + * @return the completion start position, or <code>-1</code> + */ + public int getCompletionStart() { + return completionStart; + } + + /** + * Set the completion start position. This can only be set once. After that, + * attempts to change the start position will throw {@link IllegalArgumentException}. + * + * @param completionStart + */ + public void setCompletionStart(int completionStart) { + if (this.completionStart != completionStart) { + if (this.completionStart != -1) { + throw new IllegalArgumentException( + "completionStart cannot be changed"); + } + this.completionStart = completionStart; + } + } + + /** + * Get the combined completion string. If there are multiple alternatives, + * this will be the longest common left substring of the alternatives. If + * the substring is zero length, or if there were no alternatives in the + * first place, the result is <code>null</code>. + * + * @return the combined completion, or <code>null</code>. + */ + public String getCompletion() { + if (completions == null) { + return null; + } + int nos = completions.size(); + if (nos == 0) { + return null; + } + if (nos == 1) { + return completions.first(); + } + String common = completions.first(); + for (String completion : completions) { + if (common != completion && !completion.startsWith(common)) { + for (int i = 0; i < common.length(); i++) { + if (common.charAt(i) != completion.charAt(i)) { + if (i == 0) { + return null; + } + common = common.substring(0, i); + break; + } + } + } + } + return common; + } +} Modified: trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java 2008-08-24 11:38:12 UTC (rev 4492) +++ trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -64,4 +64,14 @@ * @return the name */ String getName(); + + /** + * Add escape sequences (or quotes) to protect any characters in the + * supplied word so that it can (for example) be appended to a partial + * command line by the completer. + * + * @param word the word to be escaped + * @return the word with any necessary escaping or quoting added. + */ + String escapeWord(String word); } Modified: trunk/shell/src/shell/org/jnode/shell/CommandShell.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2008-08-24 11:38:12 UTC (rev 4492) +++ trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -613,7 +613,7 @@ public CompletionInfo complete(String partial) { if (!readingCommand) { // dummy completion behavior for application input. - return new CompletionInfo(); + return new CommandCompletions(); } // workaround to set the currentShell to this shell @@ -625,7 +625,7 @@ } // do command completion - completion = new CompletionInfo(); + completion = new CommandCompletions(interpreter); try { Completable cl = parseCommandLine(partial); if (cl != null) { Modified: trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java 2008-08-24 11:38:12 UTC (rev 4492) +++ trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -128,7 +128,73 @@ res.setArgumentAnticipated(tokenizer.whitespaceAfterLast()); return res; } + + @Override + public String escapeWord(String word) { + return escapeWord(word, false); + } + protected String escapeWord(String word, boolean escapeRedirects) { + final int len = word.length(); + StringBuilder sb = new StringBuilder(len); + for (int i = 0; i < len; i++) { + char ch = word.charAt(i); + switch (ch) { + case ESCAPE_B: + sb.append(ESCAPE_CHAR).append(B); + break; + case ESCAPE_N: + sb.append(ESCAPE_CHAR).append(N); + break; + case ESCAPE_R: + sb.append(ESCAPE_CHAR).append(R); + break; + case ESCAPE_T: + sb.append(ESCAPE_CHAR).append(T); + break; + case ESCAPE_CHAR: + sb.append(ESCAPE_CHAR).append(ESCAPE_CHAR); + break; + case FULL_ESCAPE_CHAR: + sb.append(ESCAPE_CHAR).append(FULL_ESCAPE_CHAR); + break; + case QUOTE_CHAR: + sb.append(ESCAPE_CHAR).append(QUOTE_CHAR); + break; + case COMMENT_CHAR: + sb.append(ESCAPE_CHAR).append(COMMENT_CHAR); + break; + case SPACE_CHAR: + sb.append(ESCAPE_CHAR).append(SPACE_CHAR); + break; + case PIPE_CHAR: + if (escapeRedirects) { + sb.append(ESCAPE_CHAR).append(PIPE_CHAR); + } else { + sb.append(PIPE_CHAR); + } + break; + case SEND_OUTPUT_TO_CHAR: + if (escapeRedirects) { + sb.append(ESCAPE_CHAR).append(SEND_OUTPUT_TO_CHAR); + } else { + sb.append(SEND_OUTPUT_TO_CHAR); + } + break; + case GET_INPUT_FROM_CHAR: + if (escapeRedirects) { + sb.append(ESCAPE_CHAR).append(GET_INPUT_FROM_CHAR); + } else { + sb.append(GET_INPUT_FROM_CHAR); + } + break; + default: + sb.append(ch); + } + } + return sb.toString(); + } + /** * A simple command line tokenizer for the 'built-in' interpreters. It * understands quoting, some '\' escapes, and (depending on constructor Modified: trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java 2008-08-24 11:38:12 UTC (rev 4492) +++ trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -81,6 +81,11 @@ List<CommandDescriptor> commands = new LinkedList<CommandDescriptor>(); return parse(tokenizer, commands, true); } + + @Override + public String escapeWord(String word) { + return escapeWord(word, true); + } /** * This method parses the shell input into command lines. If we are completing, Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2008-08-24 11:38:12 UTC (rev 4492) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -132,6 +132,12 @@ // TODO Auto-generated method stub return null; } + + @Override + public String escapeWord(String word) { + // TODO Auto-generated method stub + return null; + } int interpret(CommandShell shell, String command, OutputStream capture, boolean source) throws ShellException { Modified: trunk/shell/src/shell/org/jnode/shell/syntax/URLArgument.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/syntax/URLArgument.java 2008-08-24 11:38:12 UTC (rev 4492) +++ trunk/shell/src/shell/org/jnode/shell/syntax/URLArgument.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -24,6 +24,7 @@ import java.net.URL; import org.jnode.driver.console.CompletionInfo; +import org.jnode.shell.CommandCompletions; import org.jnode.shell.CommandLine.Token; /** @@ -71,7 +72,7 @@ (url.getQuery() == null || url.getQuery().length() == 0)) { // Use a FileArgument to do the work of completing the pathname, // capturing the results using our own CompletionInfo object. - CompletionInfo myCompletion = new CompletionInfo(); + CompletionInfo myCompletion = new CommandCompletions(); new FileArgument(null, getFlags()).complete(myCompletion, url.getPath()); // Then turn the completions back into "file:" URLs for (String c : myCompletion.getCompletions()) { Modified: trunk/shell/src/test/org/jnode/test/shell/CompletionInfoTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/CompletionInfoTest.java 2008-08-24 11:38:12 UTC (rev 4492) +++ trunk/shell/src/test/org/jnode/test/shell/CompletionInfoTest.java 2008-08-24 14:03:04 UTC (rev 4493) @@ -24,6 +24,7 @@ import java.util.SortedSet; import junit.framework.TestCase; import org.jnode.driver.console.CompletionInfo; +import org.jnode.shell.CommandCompletions; /** * Test key methods of the CompletionInfo class. @@ -33,11 +34,11 @@ public class CompletionInfoTest extends TestCase { public void testConstructor() { - new CompletionInfo(); + new CommandCompletions(); } public void testAddCompletion() { - CompletionInfo ci = new CompletionInfo(); + CompletionInfo ci = new CommandCompletions(); ci.addCompletion("full-1"); ci.addCompletion("full-2", false); @@ -52,7 +53,7 @@ } public void testSetCompletionStart() { - CompletionInfo ci = new CompletionInfo(); + CompletionInfo ci = new CommandCompletions(); assertEquals(-1, ci.getCompletionStart()); ci.setCompletionStart(-1); assertEquals(-1, ci.getCompletionStart()); @@ -69,7 +70,7 @@ } public void testGetCompletion() { - CompletionInfo ci = new CompletionInfo(); + CompletionInfo ci = new CommandCompletions(); assertEquals(null, ci.getCompletion()); ci.addCompletion("full-1"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |