From: <ls...@us...> - 2007-08-02 18:15:29
|
Revision: 3374 http://jnode.svn.sourceforge.net/jnode/?rev=3374&view=rev Author: lsantha Date: 2007-08-02 11:15:27 -0700 (Thu, 02 Aug 2007) Log Message: ----------- Keyboard input patch by Stephen Crawley. This patch enables line editting in programs like js and bsh. Modified Paths: -------------- trunk/core/src/core/org/jnode/log4j/config/Log4jConfigurePlugin.java trunk/core/src/core/org/jnode/util/SystemInputStream.java trunk/core/src/driver/org/jnode/driver/console/ConsoleManager.java trunk/core/src/driver/org/jnode/driver/console/TextConsole.java trunk/core/src/driver/org/jnode/driver/console/spi/AbstractConsoleManager.java trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsole.java trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsoleManager.java trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsolePlugin.java trunk/core/src/driver/org/jnode/driver/input/AbstractKeyboardDriver.java trunk/core/src/driver/org/jnode/driver/input/Key.java trunk/core/src/driver/org/jnode/driver/input/KeyboardInterpreter.java trunk/distr/src/apps/org/jnode/apps/console/SwingConsole.java trunk/distr/src/apps/org/jnode/apps/editor/TextEditor.java trunk/distr/src/emu/org/jnode/emu/EditEmu.java trunk/distr/src/emu/org/jnode/emu/ShellEmu.java trunk/fs/src/fs/org/jnode/fs/command/CatCommand.java trunk/gui/src/test/org/jnode/test/gui/ConsoleTest2.java trunk/gui/src/test/org/jnode/test/gui/FBConsole.java trunk/shell/descriptors/org.jnode.shell.xml trunk/shell/src/shell/org/jnode/shell/CommandShell.java trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java trunk/shell/src/shell/org/jnode/shell/Shell.java trunk/shell/src/shell/org/jnode/shell/ThreadCommandInvoker.java trunk/shell/src/shell/org/jnode/shell/command/HistoryCommand.java trunk/shell/src/shell/org/jnode/shell/command/driver/console/ConsoleCommand.java trunk/textui/src/textui/charva/awt/Toolkit.java Added Paths: ----------- trunk/core/src/driver/org/jnode/driver/console/CommandHistory.java trunk/core/src/driver/org/jnode/driver/console/CompletionInfo.java trunk/core/src/driver/org/jnode/driver/console/InputCompleter.java trunk/core/src/driver/org/jnode/driver/console/textscreen/KeyboardInputStream.java trunk/core/src/driver/org/jnode/driver/console/textscreen/Line.java Removed Paths: ------------- trunk/core/src/driver/org/jnode/driver/input/KeyboardInputStream.java trunk/shell/src/shell/org/jnode/shell/CommandHistory.java trunk/shell/src/shell/org/jnode/shell/CompletionInfo.java trunk/shell/src/shell/org/jnode/shell/Line.java Modified: trunk/core/src/core/org/jnode/log4j/config/Log4jConfigurePlugin.java =================================================================== --- trunk/core/src/core/org/jnode/log4j/config/Log4jConfigurePlugin.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/core/org/jnode/log4j/config/Log4jConfigurePlugin.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -62,7 +62,13 @@ try { // Create the appenders final ConsoleManager conMgr = InitialNaming.lookup(ConsoleManager.NAME); - final TextConsole console = (TextConsole)conMgr.createConsole("Log4j", ConsoleManager.CreateOptions.TEXT | ConsoleManager.CreateOptions.SCROLLABLE | ConsoleManager.CreateOptions.NO_SYSTEM_OUT_ERR_IN); + final TextConsole console = + (TextConsole)conMgr.createConsole( + "Log4j", + (ConsoleManager.CreateOptions.TEXT | + ConsoleManager.CreateOptions.SCROLLABLE | + ConsoleManager.CreateOptions.NO_SYSTEM_OUT_ERR | + ConsoleManager.CreateOptions.NO_LINE_EDITTING)); conMgr.registerConsole(console); console.setAcceleratorKeyCode(KeyEvent.VK_F7); Modified: trunk/core/src/core/org/jnode/util/SystemInputStream.java =================================================================== --- trunk/core/src/core/org/jnode/util/SystemInputStream.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/core/org/jnode/util/SystemInputStream.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -61,7 +61,9 @@ final public void setIn(InputStream in) { - localeIn.set(in); + if (in != this) { + localeIn.set(in); + } } /** @@ -101,7 +103,7 @@ */ public void initialize(InputStream systemIn) { - if(this.systemIn == EMPTY) // register only the first keyboard + if (this.systemIn == EMPTY && systemIn != this) // register only the first keyboard { this.systemIn = systemIn; } Added: trunk/core/src/driver/org/jnode/driver/console/CommandHistory.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/CommandHistory.java (rev 0) +++ trunk/core/src/driver/org/jnode/driver/console/CommandHistory.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -0,0 +1,94 @@ +/* + * $Id: CommandHistory.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.driver.console; + +import java.util.ArrayList; +import java.util.List; + +/** Used to keep an archive of commands. + * @author Matt Paine + */ +public class CommandHistory { + + /** Holds the commands. **/ + private final List<String> history = new ArrayList<String>(); + + /** Constructs a CommandHistory object. **/ + public CommandHistory() { + } + + /** Adds a command to the archive. + * @param line The CommandLine to add to the archive. + **/ + public void addCommand(String line) { + if( history.contains(line) ) + history.remove(line); + history.add(line); + } + + /** Returns the number of commands held in the archive. + * @return the number of commands held in the archive. + **/ + public int size() { + return history.size(); + } + + /** Gets a command at a given index. + * TODO: make exception more specific + * @param index The index (starting at zero) of the command to return. + * @return The command at the index given or null if no command found + * (out of bounds index). + **/ + public String getCommand(int index) { + String retCommand = null; + try { + retCommand = (String)history.get(index); + } catch (Exception ex) { + } + return retCommand; + } + + /** Searches for the most recent command types starting with the specified + * string. + * @param start The string to search for. + * @return The most recent command matching the search string. + **/ + public String getCommand(String start) { + return getCommand(findCommand(start)); + } + + /** Searches the collection for the most recent command starting with the + * specified string. + * @param start the string to search for. + * @return the index number of the specified string (or -1 if not found). + **/ + private int findCommand(String start) { + for (int x = 0; x < history.size(); x++) { + String cmdLine = (String)history.get(x); + if (cmdLine != null) + if (cmdLine.startsWith(start)) + return x; + } + return -1; + } + +} Added: trunk/core/src/driver/org/jnode/driver/console/CompletionInfo.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/CompletionInfo.java (rev 0) +++ trunk/core/src/driver/org/jnode/driver/console/CompletionInfo.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -0,0 +1,94 @@ +/* + * $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.driver.console; + +/** + * @author Ewout Prangsma (ep...@us...) + */ +public class CompletionInfo { + private String[] items = null; + + private String completed = null; + + private boolean newPrompt = false; + + /** + * @return Returns the completed. + */ + public String getCompleted() { + return completed; + } + + /** + * @param completed + * The completed to set. + */ + public void setCompleted(String completed) { + this.completed = completed; + } + + /** + * get the possible completions + * + * @return Returns the items. + */ + public String[] getItems() { + return items; + } + + /** + * Specify the possible completions + * + * @param items + * The items to set. + */ + public void setItems(String[] items) { + this.items = items; + this.completed = null; + this.newPrompt = true; + } + + /** + * Do we have more than one possible completion ? + * + * @return + */ + public boolean hasItems() { + return items != null; + } + + /** + * Specify if we need a new prompt or not + * + * @param newPrompt + */ + public void setNewPrompt(boolean newPrompt) { + this.newPrompt = newPrompt; + } + + /** + * @return true if we need to display a new prompt + */ + public boolean needNewPrompt() { + return newPrompt; + } +} Modified: trunk/core/src/driver/org/jnode/driver/console/ConsoleManager.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/ConsoleManager.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/console/ConsoleManager.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -22,6 +22,7 @@ package org.jnode.driver.console; import java.util.Set; +import java.io.InputStream; import java.io.PrintStream; import org.jnode.driver.input.KeyboardListener; @@ -112,11 +113,15 @@ /** Create a scrollable console. */ public static final int SCROLLABLE = 0x02; - /** Do not claim System.out, err, in when focused. */ - public static final int NO_SYSTEM_OUT_ERR_IN = 0x04; + /** Do not claim System.out, err when focused. */ + public static final int NO_SYSTEM_OUT_ERR = 0x04; /** Stack console on the current screen */ public static final int STACKED = 0x08; + + /** Do not create a line-editting input stream for the console. + * The console's input will be whatever System.in currently is. */ + public static final int NO_LINE_EDITTING = 0x10; } /** Added: trunk/core/src/driver/org/jnode/driver/console/InputCompleter.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/InputCompleter.java (rev 0) +++ trunk/core/src/driver/org/jnode/driver/console/InputCompleter.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -0,0 +1,8 @@ +package org.jnode.driver.console; + + +public interface InputCompleter { + CompletionInfo complete(String partial); + + CommandHistory getCommandHistory(); +} Modified: trunk/core/src/driver/org/jnode/driver/console/TextConsole.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/TextConsole.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/console/TextConsole.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -21,6 +21,7 @@ package org.jnode.driver.console; +import java.io.InputStream; import java.io.PrintStream; /** @@ -155,6 +156,12 @@ */ public void ensureVisible(int row); + /** + * Gets the input stream of this console. + * @return + */ + public InputStream getIn(); + /** * Gets the error stream of this console. * @return @@ -177,4 +184,17 @@ * @param visible */ public void setCursorVisible(boolean visible); + + + /** + * Get the console's input completer + * @return The completer or <code>null</code>. + */ + public InputCompleter getCompleter(); + + /** + * Ges the console's input completer + * @param The new completer or <code>null</code>. + */ + public void setCompleter(InputCompleter completer); } Modified: trunk/core/src/driver/org/jnode/driver/console/spi/AbstractConsoleManager.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/spi/AbstractConsoleManager.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/console/spi/AbstractConsoleManager.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -98,6 +98,10 @@ } } + protected KeyboardAPI getKeyboardApi() { + return kbApi; + } + /** * Add a pointer device * Added: trunk/core/src/driver/org/jnode/driver/console/textscreen/KeyboardInputStream.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/textscreen/KeyboardInputStream.java (rev 0) +++ trunk/core/src/driver/org/jnode/driver/console/textscreen/KeyboardInputStream.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -0,0 +1,444 @@ +package org.jnode.driver.console.textscreen; + +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; + +import javax.naming.NameNotFoundException; + +import org.jnode.driver.ApiNotFoundException; +import org.jnode.driver.Device; +import org.jnode.driver.DeviceListener; +import org.jnode.driver.DeviceManager; +import org.jnode.driver.console.CompletionInfo; +import org.jnode.driver.console.InputCompleter; +import org.jnode.driver.console.TextConsole; +import org.jnode.driver.input.KeyboardAPI; +import org.jnode.driver.input.KeyboardEvent; +import org.jnode.driver.input.KeyboardListener; +import org.jnode.naming.InitialNaming; +import org.jnode.system.BootLog; +import org.jnode.system.event.FocusEvent; +import org.jnode.system.event.FocusListener; +import org.jnode.util.Queue; + + +/** + * KeyInputStream maps keyboard events into a stream of characters. Current functionality includes: + * <ul> + * <li>line buffering and line editing, using a text console, + * <li>integrated input history and completion, + * <li>CTRL-D is interpretted as a 'soft' EOF mark,KeyboardInputStream + * <li>listens to keyboard focus events. + * </ul> + * + * Future enhancements include: + * <ul> + * <li>a "raw" mode in which characters and other keyboard events are delivered without line editing, + * <li>a "no echo" mode in which line editting occurs without echoing of input characters, + * <li>making the active characters and keycodes "soft", + * <li>making completion and history context sensitive; e.g. when switching between a shell and + * an application, and + * <li>code refactoring to support classical terminal devices and remote consoles. + * </ul> + * + * Bugs: + * <ul> + * <li>The current method of echoing the input is suboptimal, and is broken in the case where an + * application outputs a prompt string to stdout or stderr. + * </ul> + * + * @author cr...@jn... + * + */ +public class KeyboardInputStream extends InputStream +implements KeyboardListener, FocusListener, DeviceListener { + + public static final byte CTRL_L = 12; + public static final byte CTRL_D = 4; + + private boolean eof; + + private byte[] buffer; + private int pos; + private int lim; + + private static final char NO_CHAR = 0; + + private KeyboardAPI api; + private DeviceManager devMan; + + /** The queue of keyboard events */ + private final Queue<KeyboardEvent> queue = new Queue<KeyboardEvent>(); + + private final Line currentLine; + private final TextConsole console; + private InputCompleter completer; + private final PrintStream out; + private boolean hasFocus; + + private String currentPrompt; + + /** + * Contains an index to the current history line, counting from zero. The value + * -1 denotes the current line. + */ + private int historyIndex = -1; + + /** + * Contains the current line; i.e. the text being entered by the user. + */ + private String newestLine = ""; + + public KeyboardInputStream(KeyboardAPI api, TextConsole console) { + if (api != null) { + this.api = api; + this.api.addKeyboardListener(this); + } + else { + try { + this.devMan = InitialNaming.lookup(DeviceManager.NAME); + this.devMan.addListener(this); + } + catch (NameNotFoundException ex) { + BootLog.error("DeviceManager not found", ex); + } + } + this.console = console; + this.out = console.getOut(); + this.currentLine = new Line(console); + this.pos = this.lim = 0; + } + + private void registerKeyboardApi(Device device) { + if (this.api == null) { + try { + this.api = device.getAPI(KeyboardAPI.class); + this.api.addKeyboardListener(this); + } + catch (ApiNotFoundException ex) { + BootLog.error("KeyboardAPI not found", ex); + } + this.devMan.removeListener(this); + } + } + + public boolean isSoftEOF() { + return eof; + } + + public void clearSoftEOF() { + eof = false; + } + + @Override + public int available() throws IOException { + if (eof) { + return 0; /* per the JDK 1.6 API docs */ + } + else { + // FIXME - what about the case where the line buffer is empty + // and there are unconsumed input events in the queue? + return currentLine.getLineLength(); + } + } + + /** + * @see org.jnode.driver.input.KeyboardListener#keyPressed(org.jnode.driver.input.KeyboardEvent) + */ + public void keyPressed(KeyboardEvent event) { + if (hasFocus) { + queue.add(event); + } + } + + /** + * @see org.jnode.driver.input.KeyboardListener#keyReleased(org.jnode.driver.input.KeyboardEvent) + */ + public void keyReleased(KeyboardEvent event) { + } + + /** + * @see java.io.InputStream#close() + */ + public void close() throws IOException { + if (api != null) { + api.removeKeyboardListener(this); + } + super.close(); + } + + /** + * Pull a keyboard event from the queue and process it. + */ + private boolean processEvent() { + KeyboardEvent event = queue.get(); + if (!event.isConsumed()) { + char ch = event.getKeyChar(); + if (ch != NO_CHAR) { + event.consume(); + return !processChar(ch); + } + else { + int kc = event.getKeyCode(); + int mods = event.getModifiers(); + if (processVirtualKeystroke(kc, mods)) { + event.consume(); + } + return true; + } + } + else { + return true; + } + } + + /** + * Process a keystroke interpretted as a character. + * + * @param ch + * @return <code>true</code> if the character should cause the current line + * buffer contents to be returned to the user. + */ + private boolean processChar(char ch) { + boolean breakChar = false; + switch (ch) { + // if its a backspace we want to remove one from the end of our current + // line + case KeyEvent.VK_BACK_SPACE: + if (currentLine.backspace()) { + refreshCurrentLine(); + } + break; + // if its a delete we want to remove one under the cursor + case KeyEvent.VK_DELETE: + currentLine.delete(); + refreshCurrentLine(); + break; + // if its an enter key we want to process the command, and then resume + // the thread + case '\n': + currentLine.moveEnd(); + refreshCurrentLine(); + out.println(); + currentLine.appendChar(ch); + breakChar = true; + break; + // if it's the tab key, we want to trigger command line completion + case '\t': + if (completer != null) { + CompletionInfo completion = currentLine.complete(currentPrompt); + if (completion.needNewPrompt()) { + currentLine.start(true); + } + refreshCurrentLine(); + } + break; + case CTRL_D: + currentLine.moveEnd(); + refreshCurrentLine(); + out.println(); + eof = true; + breakChar = true; + break; + case CTRL_L: + this.console.clear(); + this.console.setCursor(0, 0); + out.print(currentPrompt); + currentLine.start(); + refreshCurrentLine(); + break; + default: + // otherwise add it to our current line + currentLine.appendChar(ch); + refreshCurrentLine(); + } + return breakChar; + } + + /** + * Process a keystroke that doesn't have an associated char value. + * @param code + * @param modifiers + * @return <code>true</code> if the keystroke has been recognized and + * acted on, <code>false</code> otherwise. + */ + private boolean processVirtualKeystroke(int code, int modifiers) { + if (modifiers != 0) { + return false; + } + switch (code) { + case KeyEvent.VK_UP: + // Previous history item + if (completer != null) { + if (historyIndex == -1) { + newestLine = currentLine.getContent(); + historyIndex = completer.getCommandHistory().size(); + } + historyIndex--; + redisplay(); + } + break; + case KeyEvent.VK_DOWN: + // Next history item + if (completer != null) { + if (historyIndex == completer.getCommandHistory().size() - 1) + historyIndex = -2; + else if (historyIndex == -1) + newestLine = currentLine.getContent(); + historyIndex++; + redisplay(); + } + break; + case KeyEvent.VK_LEFT: + // Left the cursor goes left + if (currentLine.moveLeft()) { + refreshCurrentLine(); + } + break; + case KeyEvent.VK_RIGHT: + // Right the cursor goes right + if (currentLine.moveRight()) { + refreshCurrentLine(); + } + break; + case KeyEvent.VK_HOME: + // The cursor goes at the start + currentLine.moveBegin(); + refreshCurrentLine(); + break; + case KeyEvent.VK_END: + // the cursor goes at the end of line + currentLine.moveEnd(); + refreshCurrentLine(); + break; + default: + // ignore other virtual keys. + return false; + } + return true; + } + + private void refreshCurrentLine() { + currentLine.refreshCurrentLine(currentPrompt); + } + + private void redisplay() { + if (historyIndex == -1) { + currentLine.setContent(newestLine); + } + else { + currentLine.setContent(completer.getCommandHistory().getCommand(historyIndex)); + } + refreshCurrentLine(); + currentLine.moveEnd(); + } + + private boolean fillBuffer() throws IOException { + int x = console.getCursorX(); + int y = console.getCursorY(); + StringBuffer sb = new StringBuffer(x); + for (int i = 0; i < x; i++) { + sb.append(console.getChar(i, y)); + } + currentPrompt = sb.toString(); + + currentLine.start(); + while (processEvent()) { /* */ } + buffer = currentLine.consumeBytes(); + lim = buffer.length; + pos = 0; + return pos < lim; + } + + @Override + public int read() throws IOException { + if (pos >= lim) { + if (eof || !fillBuffer()) { + return -1; + } + } + return buffer[pos++]; + } + + @Override + public int read(byte[] buff, int off, int len) throws IOException { + int nosRead = 0; + if (pos >= lim) { + if (eof || !fillBuffer()) { + return -1; + } + } + while (nosRead < len && pos < lim) { + buff[nosRead + off] = buffer[pos]; + nosRead++; + pos++; + } + return nosRead; + } + + @Override + public int read(byte[] buff) throws IOException { + return read(buff, 0, buff.length); + } + + @Override + public void mark(int readLimit) { + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public void reset() throws IOException { + throw new UnsupportedOperationException("Mark/reset not supported"); + } + + @Override + public long skip(long n) throws IOException { + // I don't expect this method will be used much. + for (long i = 0; i < n; i++) { + if (read() == -1) { + return i; + } + } + return n; + } + + public InputCompleter getCompleter() { + return completer; + } + + public void setCompleter(InputCompleter completer) { + this.completer = completer; + } + + public void focusGained(FocusEvent event) { + hasFocus = true; + } + + public void focusLost(FocusEvent event) { + hasFocus = false; + } + + /** + * @param device + * @see org.jnode.driver.DeviceListener#deviceStarted(org.jnode.driver.Device) + */ + public void deviceStarted(Device device) { + if (device.implementsAPI(KeyboardAPI.class)) { + registerKeyboardApi(device); + } + } + + /** + * @param device + * @see org.jnode.driver.DeviceListener#deviceStop(org.jnode.driver.Device) + */ + public void deviceStop(Device device) { + /* Do nothing */ + } +} Added: trunk/core/src/driver/org/jnode/driver/console/textscreen/Line.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/textscreen/Line.java (rev 0) +++ trunk/core/src/driver/org/jnode/driver/console/textscreen/Line.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -0,0 +1,323 @@ +/* + * $Id: Line.java 3119 2007-02-11 22:25:20Z fduminy $ + * + * 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.driver.console.textscreen; + +import java.io.PrintStream; + +import org.jnode.driver.console.CompletionInfo; +import org.jnode.driver.console.InputCompleter; +import org.jnode.driver.console.TextConsole; + + +/** + * A class that handles the content of the current command line in the shell. + * That can be : - a new command that the user is beeing editing - an existing + * command (from the command history) + * + * This class also handles the current cursor position in the command line and + * keep trace of the position (consoleX, consoleY) of the first character of the + * command line (to handle commands that are multilines). + * + * @author Ewout Prangsma (ep...@us...) + * @author Fabien DUMINY (fd...@jn...) + */ +class Line { + //TODO get the real screen width (in columns) + final static private int SCREEN_WIDTH = 80; + + private int consoleX; + + private int consoleY; + + /** + * Contains the current position of the cursor on the currentLine + */ + private int posOnCurrentLine = 0; + + /** Contains the current line * */ + private StringBuffer currentLine = new StringBuffer(80); + + private boolean shortened = true; + + private int oldLength = 0; + + private int maxLength = 0; + + private final TextConsole console; + + private PrintStream out; + + public Line(TextConsole console) { + this.console = console; + this.out = console.getOut(); + } + + public void start() { + start(false); + } + + public boolean isEmpty() { + return currentLine.toString().trim().length() == 0; + } + + public void start(boolean keepContent) { + if (keepContent) { + // we stay at the same position in X coordinate + // only Y may have changed + consoleY = console.getCursorY(); + } else { + consoleX = console.getCursorX(); + consoleY = console.getCursorY(); + + setContent(""); + console.setCursor( consoleX,consoleY);//move the cursor to the start of the line. + } + } + + public String getContent() { + return currentLine.toString(); + } + + public byte[] getBytes() { + return currentLine.toString().getBytes(); + } + + public byte[] consumeBytes() { + byte[] res = getBytes(); + currentLine.setLength(0); + return res; + } + + public void setContent(String content) { + startModif(); + currentLine.setLength(0); + currentLine.append(content); + moveEnd(); + endModif(); + } + + public boolean moveLeft() { + if (posOnCurrentLine > 0) { + posOnCurrentLine--; + return true; + } + return false; + } + + public boolean moveRight() { + if (posOnCurrentLine < currentLine.length()) { + posOnCurrentLine++; + return true; + } + return false; + } + + public void moveEnd() { + posOnCurrentLine = currentLine.length(); + } + + public void moveBegin() { + posOnCurrentLine = 0; + } + + public boolean backspace() { + if (posOnCurrentLine > 0) { + moveLeft(); + delete(); + return true; + } + return false; + } + + public void delete() { + if ((posOnCurrentLine >= 0) + && (posOnCurrentLine < currentLine.length())) { + startModif(); + currentLine.deleteCharAt(posOnCurrentLine); + endModif(); + } + } + + public CompletionInfo complete(String currentPrompt) { + CompletionInfo info = null; + InputCompleter completer = console.getCompleter(); + if (posOnCurrentLine != currentLine.length()) { + String ending = currentLine.substring(posOnCurrentLine); + info = completer.complete(currentLine.substring(0, posOnCurrentLine)); + printList(info, currentPrompt); + if (info.getCompleted() != null) { + setContent(info.getCompleted() + ending); + posOnCurrentLine = currentLine.length() - ending.length(); + } + } else { + info = completer.complete(currentLine.toString()); + printList(info, currentPrompt); + if (info.getCompleted() != null) { + setContent(info.getCompleted()); + posOnCurrentLine = currentLine.length(); + } + } + + return info; + } + + protected void printList(CompletionInfo info, String currentPrompt) { + if ((info != null) && info.hasItems()) { + int oldPosOnCurrentLine = posOnCurrentLine; + moveEnd(); + refreshCurrentLine(currentPrompt); + + out.println(); + String[] list = info.getItems(); + + final int minItemsToSplit = 5; + if(list.length > minItemsToSplit) + { + list = splitInColumns(list); + } + + // display items column (may be single or multiple columns) + for (String item : list) + { + // item may actually be a single item or in fact multiple items + if((item.length() % SCREEN_WIDTH) == 0) + { + // we are already at the first column of the next line + out.print(item); + } + else + { + // we aren't at the first column of the next line + out.println(item); + } + } + + posOnCurrentLine = oldPosOnCurrentLine; + } + } + + protected String[] splitInColumns(String[] items) + { + final int separatorWidth = 3; + + // compute the maximum width of items + int maxWidth = 0; + for(String item : items) + { + if(item.length() > maxWidth) + { + maxWidth = item.length(); + } + } + + final int columnWidth = Math.min(SCREEN_WIDTH, maxWidth + separatorWidth); + final int nbColumns = SCREEN_WIDTH / columnWidth; + final boolean lastLineIsFull = ((items.length % nbColumns) == 0); + 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 ; ) + { + for(int c = 0 ; c < nbColumns ; c++) + { + final String item = items[itemNum++]; + line.append(item); + + // add some blanks + final int nbBlanks = columnWidth - item.length(); + for(int i = 0 ; i < nbBlanks ; i++) + { + line.append(' '); + } + + if(itemNum >= items.length) break; + } + + lines[lineNum++] = line.toString(); + line.setLength(0); // clear the buffer + } + + return lines; + } + + public void appendChar(char c) { + startModif(); + if (posOnCurrentLine == currentLine.length()) { + currentLine.append(c); + } else { + currentLine.insert(posOnCurrentLine, c); + } + posOnCurrentLine++; + endModif(); + } + + protected void startModif() { + shortened = false; + oldLength = currentLine.length(); + } + + protected void endModif() { + maxLength = Math.max(oldLength, currentLine.length()); + shortened = oldLength > currentLine.length(); + oldLength = 0; + } + + public void refreshCurrentLine(String currentPrompt) { + try { + int x = consoleX; + int width = console.getWidth(); + int nbLines = ((x + maxLength) / width); + + if (((x + maxLength) % width) != 0) + nbLines++; + + // if the line has not been shortened (delete, backspace...) + if (!shortened) + // scroll up the buffer if necessary, and get the new y + console.ensureVisible(consoleY + nbLines - 1); + + for (int i = 0; i < nbLines; i++) { + console.clearRow(consoleY + i); + } + + // print the input line + console.setCursor(0, consoleY); + out.print(currentPrompt + currentLine); + + int posCurX = x + posOnCurrentLine; + int posCurY = consoleY; + if (posCurX >= width) { + posCurY += posCurX / width; + posCurX = (posCurX % width); + } + console.setCursor(posCurX, posCurY); + } catch (Exception e) { + //todo: why is it ignored? + } + } + + public int getLineLength() { + return currentLine.length(); + } +} Modified: trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsole.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsole.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsole.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -21,16 +21,19 @@ package org.jnode.driver.console.textscreen; +import java.io.InputStream; import java.io.PrintStream; import java.security.AccessController; import java.security.PrivilegedAction; import org.jnode.driver.console.ConsoleManager; +import org.jnode.driver.console.InputCompleter; import org.jnode.driver.console.TextConsole; import org.jnode.driver.console.spi.AbstractConsole; import org.jnode.driver.console.spi.ConsoleOutputStream; import org.jnode.driver.textscreen.TextScreen; import org.jnode.system.event.FocusEvent; +import org.jnode.system.event.FocusListener; import org.jnode.vm.isolate.VmIsolate; /** @@ -56,10 +59,14 @@ /** Current Y position */ private int curY; + + private InputStream in; private final PrintStream out; private final PrintStream err; + + private InputStream savedIn; private PrintStream savedOut; @@ -67,7 +74,7 @@ private boolean cursorVisible = true; - private final boolean claimSystemOutErrIn; + private final boolean claimSystemOutErr; private VmIsolate myIsolate; @@ -86,7 +93,7 @@ this, 0x07)); this.savedErr = this.err = new PrintStream(new ConsoleOutputStream( this, 0x04)); - this.claimSystemOutErrIn = ((options & ConsoleManager.CreateOptions.NO_SYSTEM_OUT_ERR_IN) == 0); + this.claimSystemOutErr = ((options & ConsoleManager.CreateOptions.NO_SYSTEM_OUT_ERR) == 0); this.myIsolate = VmIsolate.currentIsolate(); } @@ -285,7 +292,33 @@ syncScreen(); } + public InputCompleter getCompleter() { + if (in instanceof KeyboardInputStream) { + return ((KeyboardInputStream) in).getCompleter(); + } + else { + return null; + } + } + + public void setCompleter(InputCompleter completer) { + if (in instanceof KeyboardInputStream) { + ((KeyboardInputStream) in).setCompleter(completer); + } + } + /** + * @see org.jnode.driver.console.TextConsole#getIn() + */ + public InputStream getIn() { + return in; + } + + void setIn(InputStream in) { + this.savedIn = this.in = in; + } + + /** * @see org.jnode.driver.console.TextConsole#getErr() */ public PrintStream getErr() { @@ -323,7 +356,10 @@ public void focusGained(FocusEvent event) { super.focusGained(event); syncScreen(); - if (claimSystemOutErrIn) { + if (in instanceof FocusListener) { + ((FocusListener) in).focusGained(event); + } + if (claimSystemOutErr) { myIsolate.invokeAndWait(new Runnable() { public void run() { AccessController.doPrivileged(new PrivilegedAction<Object>() { @@ -342,7 +378,10 @@ * @see org.jnode.system.event.FocusListener#focusLost(org.jnode.system.event.FocusEvent) */ public void focusLost(FocusEvent event) { - if (claimSystemOutErrIn) { + if (in instanceof FocusListener) { + ((FocusListener) in).focusLost(event); + } + if (claimSystemOutErr) { myIsolate.invokeAndWait(new Runnable() { public void run() { savedOut = System.out; Modified: trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsoleManager.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsoleManager.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsoleManager.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -21,11 +21,10 @@ package org.jnode.driver.console.textscreen; -import java.awt.event.KeyEvent; +import java.io.InputStream; import javax.naming.NameNotFoundException; -import org.jnode.driver.console.Console; import org.jnode.driver.console.ConsoleException; import org.jnode.driver.console.spi.AbstractConsoleManager; import org.jnode.driver.textscreen.ScrollableTextScreen; @@ -53,7 +52,7 @@ * @see org.jnode.driver.console.ConsoleManager#createConsole(String, int) */ public TextScreenConsole createConsole(String name, int options) { - if ((options & CreateOptions.TEXT) != 0) { + if ((options & CreateOptions.TEXT) != 0) { final TextScreenManager tsm; tsm = getTextScreenManager(); final TextScreenConsole console; @@ -69,6 +68,11 @@ screen = tsm.getSystemScreen().createCompatibleBufferScreen(); console = new TextScreenConsole(this, name, screen, options); } + InputStream in = System.in; + if ((options & CreateOptions.NO_LINE_EDITTING) == 0) { + in = new KeyboardInputStream(getKeyboardApi(), console); + } + console.setIn(in); if ((options & CreateOptions.STACKED) != 0){ stackConsole(console); } else { @@ -81,7 +85,7 @@ throw new IllegalArgumentException("Unknown option " + options); } } - + protected TextScreenManager getTextScreenManager() { TextScreenManager tsm; try { Modified: trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsolePlugin.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsolePlugin.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/console/textscreen/TextScreenConsolePlugin.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -55,7 +55,10 @@ InitialNaming.bind(ConsoleManager.NAME, mgr); // Create the first console - final TextConsole first = (TextConsole)mgr.createConsole(null, ConsoleManager.CreateOptions.TEXT | ConsoleManager.CreateOptions.SCROLLABLE); + final TextConsole first = (TextConsole) mgr.createConsole( + null, + (ConsoleManager.CreateOptions.TEXT | + ConsoleManager.CreateOptions.SCROLLABLE)); mgr.focus(first); System.setOut(first.getOut()); System.setErr(first.getErr()); Modified: trunk/core/src/driver/org/jnode/driver/input/AbstractKeyboardDriver.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/input/AbstractKeyboardDriver.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/input/AbstractKeyboardDriver.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -31,8 +31,6 @@ import org.jnode.driver.Device; import org.jnode.driver.DriverException; -import org.jnode.system.BootLog; -import org.jnode.util.EmptyInputStream; import org.jnode.util.SystemInputStream; /** @@ -105,18 +103,18 @@ dev.registerAPI(KeyboardAPI.class, this); dev.registerAPI(SystemTriggerAPI.class, this); - // If no inputstream has been defined, create and set one. - kis = null; - if (channel != null) { - kis = new KeyboardInputStream(this); - } - final InputStream systemIn = kis; - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - SystemInputStream.getInstance().initialize(systemIn); - return null; - } - }); +// // If no inputstream has been defined, create and set one. +// kis = null; +// if (channel != null) { +// kis = new KeyboardInputStream(this); +// } +// final InputStream systemIn = kis; +// AccessController.doPrivileged(new PrivilegedAction() { +// public Object run() { +// SystemInputStream.getInstance().initialize(systemIn); +// return null; +// } +// }); } /** Modified: trunk/core/src/driver/org/jnode/driver/input/Key.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/input/Key.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/input/Key.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -125,6 +125,11 @@ { this.upperChar = upperChar; } + + public char getControlChar() + { + return (char) (upperChar & 0x1f); + } public char getAltGrChar() { Deleted: trunk/core/src/driver/org/jnode/driver/input/KeyboardInputStream.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/input/KeyboardInputStream.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/input/KeyboardInputStream.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -1,153 +0,0 @@ -/* - * $Id$ - * - * 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.driver.input; - -import java.io.IOException; -import java.io.InputStream; - -import org.jnode.util.Queue; - -/** - * @author epr - */ -public class KeyboardInputStream extends InputStream implements KeyboardListener { - - private final KeyboardAPI api; - /** The queue of keyboard events */ - private final Queue<KeyboardEvent> queue = new Queue<KeyboardEvent>(); - /** Should we echo keys on the System.out? */ - private boolean echo = true; - - /** - * Create a new instance - * @param api the keyboard API - */ - public KeyboardInputStream(KeyboardAPI api) { - this.api = api; - api.addKeyboardListener(this); - } - - /** - * @see java.io.InputStream#available() - */ - public int available() throws IOException { - return 0; - } - - /* - Doesn't block after the first blocking read. - public int read(byte[] b, int off, int len) throws IOException { - if (off < 0 || len < 0 || b.length - off < len) - throw new IndexOutOfBoundsException(); - - long time = System.currentTimeMillis(); - - int i = 0; - int ch = read(time); - - for(;;) { - b[off + i ++] = (byte) ch; - - if(i < len && queue.size() > 0) - ch = read(time); - else - break; - } - - return i; - } - */ - - public int read(byte[] b, int off, int len) throws IOException { - if (off < 0 || len < 0 || b.length - off < len) - throw new IndexOutOfBoundsException(); - - long time = System.currentTimeMillis(); - - int i = 0; - int ch = read(time); - - for(;;) { - b[off + i ++] = (byte) ch; - - if(i < len && ch != '\n') - ch = read(time); - else - break; - } - - return i; - } - - /** - * @see java.io.InputStream#read() - */ - public int read() throws IOException { - long time = System.currentTimeMillis(); - return read(time); - } - - private int read(long time){ - while (true) { - KeyboardEvent event = queue.get(); - int c = event2char(event, time); - if(c > 0) - return c; - } - } - - private int event2char(KeyboardEvent event, long time) { - if (!event.isConsumed() && event.getTime() - time > -10000) { - event.consume(); - char ch = event.getKeyChar(); - if (ch != 0) { - if (echo) { - System.out.print(ch); - } - return ch; - } - } - return -1; - } - - /** - * @see org.jnode.driver.input.KeyboardListener#keyPressed(org.jnode.driver.input.KeyboardEvent) - */ - public void keyPressed(KeyboardEvent event) { - queue.add(event); - } - - /** - * @see org.jnode.driver.input.KeyboardListener#keyReleased(org.jnode.driver.input.KeyboardEvent) - */ - public void keyReleased(KeyboardEvent event) { - } - - /** - * @see java.io.InputStream#close() - */ - public void close() throws IOException { - api.removeKeyboardListener(this); - super.close(); - } - -} Modified: trunk/core/src/driver/org/jnode/driver/input/KeyboardInterpreter.java =================================================================== --- trunk/core/src/driver/org/jnode/driver/input/KeyboardInterpreter.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/core/src/driver/org/jnode/driver/input/KeyboardInterpreter.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -32,6 +32,10 @@ */ public abstract class KeyboardInterpreter { + // FIXME We are currently using the character zero (0x0000) to indicate that no + // (Unicode) character corresponds to a keycode. This means that we cannot represent + // the NUL control character. Instead we should really be using 0xffff which Unicode + // defines to be a non-character. private int flags; @@ -110,8 +114,13 @@ catch (UnsupportedKeyException e) { final char ch; - if ((flags & InputEvent.SHIFT_DOWN_MASK) != 0) + + if ((flags & InputEvent.CTRL_DOWN_MASK) != 0) { + ch = keys.getKey(scancode).getControlChar(); + } + else if ((flags & InputEvent.SHIFT_DOWN_MASK) != 0) + { ch = keys.getKey(scancode).getUpperChar(); if (!extendedMode) Modified: trunk/distr/src/apps/org/jnode/apps/console/SwingConsole.java =================================================================== --- trunk/distr/src/apps/org/jnode/apps/console/SwingConsole.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/distr/src/apps/org/jnode/apps/console/SwingConsole.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -28,9 +28,11 @@ TextScreenConsoleManager manager = (TextScreenConsoleManager) sm.getCurrentShell().getConsole().getManager(); SwingTextScreenConsoleManager cm = new SwingTextScreenConsoleManager(); cm.setParent(manager); - new Thread(new CommandShell((TextConsole) cm.createConsole(null, - ConsoleManager.CreateOptions.TEXT | ConsoleManager.CreateOptions.SCROLLABLE)), "SwingConsoleCommandShell"). - start(); + TextConsole console = cm.createConsole( + null, + (ConsoleManager.CreateOptions.TEXT | + ConsoleManager.CreateOptions.SCROLLABLE)); + new Thread(new CommandShell(console), "SwingConsoleCommandShell").start(); synchronized(SwingConsole.class){ frame = cm.getFrame(); Modified: trunk/distr/src/apps/org/jnode/apps/editor/TextEditor.java =================================================================== --- trunk/distr/src/apps/org/jnode/apps/editor/TextEditor.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/distr/src/apps/org/jnode/apps/editor/TextEditor.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -36,10 +36,12 @@ ShellManager sm = InitialNaming.lookup(ShellManager.NAME); TextScreenConsoleManager manager = (TextScreenConsoleManager) sm.getCurrentShell().getConsole().getManager(); - TextConsole console = manager.createConsole("editor", - ConsoleManager.CreateOptions.TEXT | - ConsoleManager.CreateOptions.STACKED | - ConsoleManager.CreateOptions.NO_SYSTEM_OUT_ERR_IN); + TextConsole console = manager.createConsole( + "editor", + (ConsoleManager.CreateOptions.TEXT | + ConsoleManager.CreateOptions.STACKED | + ConsoleManager.CreateOptions.NO_LINE_EDITTING | + ConsoleManager.CreateOptions.NO_SYSTEM_OUT_ERR)); manager.focus(console); TextEditor te = new TextEditor(console); Modified: trunk/distr/src/emu/org/jnode/emu/EditEmu.java =================================================================== --- trunk/distr/src/emu/org/jnode/emu/EditEmu.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/distr/src/emu/org/jnode/emu/EditEmu.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -21,8 +21,11 @@ } SwingTextScreenConsoleManager cm = new SwingTextScreenConsoleManager(); - final TextScreenConsole console = cm.createConsole(null, - ConsoleManager.CreateOptions.TEXT | ConsoleManager.CreateOptions.NO_SYSTEM_OUT_ERR_IN); + final TextScreenConsole console = cm.createConsole( + null, + (ConsoleManager.CreateOptions.TEXT | + ConsoleManager.CreateOptions.NO_SYSTEM_OUT_ERR | + ConsoleManager.CreateOptions.NO_LINE_EDITTING)); TextEditor te = new TextEditor(console); File f = new File(argv[0]); Modified: trunk/distr/src/emu/org/jnode/emu/ShellEmu.java =================================================================== --- trunk/distr/src/emu/org/jnode/emu/ShellEmu.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/distr/src/emu/org/jnode/emu/ShellEmu.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -12,8 +12,9 @@ public static void main(String[] argv) throws Exception { initEnv(); SwingTextScreenConsoleManager cm = new SwingTextScreenConsoleManager(); - new Thread(new CommandShell(cm.createConsole("Console 1", - ConsoleManager.CreateOptions.TEXT | ConsoleManager.CreateOptions.SCROLLABLE))). - start(); + new Thread(new CommandShell(cm.createConsole( + "Console 1", + (ConsoleManager.CreateOptions.TEXT | + ConsoleManager.CreateOptions.SCROLLABLE)))).start(); } } Modified: trunk/fs/src/fs/org/jnode/fs/command/CatCommand.java =================================================================== --- trunk/fs/src/fs/org/jnode/fs/command/CatCommand.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/fs/src/fs/org/jnode/fs/command/CatCommand.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -22,6 +22,7 @@ package org.jnode.fs.command; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.net.MalformedURLException; @@ -38,6 +39,7 @@ /** * @author epr * @author Andreas H\u00e4nel + * @author Stephen Crawley */ public class CatCommand implements Command{ @@ -45,8 +47,10 @@ "the file (or URL) to print out"); public static Help.Info HELP_INFO = new Help.Info("cat", - "Print the contents of the given file (or URL)", - new Parameter[] { new Parameter(ARG_FILE, Parameter.MANDATORY)}); + "Print the contents of the given file (or URL). " + + "If the file is omitted, standard input is read until EOF is reached; " + + "e.g. ^D when reading keyboard input.", + new Parameter[] { new Parameter(ARG_FILE, Parameter.OPTIONAL)}); public static void main(String[] args) throws Exception { new CatCommand().execute(new CommandLine(args), System.in, System.out, System.err); @@ -54,20 +58,40 @@ public void execute(CommandLine commandLine, InputStream in, PrintStream out, PrintStream err) throws Exception { ParsedArguments cmdLine = HELP_INFO.parse(commandLine.toStringArray()); - URL url = openURL(ARG_FILE.getValue(cmdLine)); - InputStream is = url.openStream(); - if (is == null) { - err.println("Not found " + ARG_FILE.getValue(cmdLine)); - } else { - int len; - final byte[] buf = new byte[ 1024]; - while ((len = is.read(buf)) > 0) { - out.write(buf, 0, len); - } - out.println(); - out.flush(); - is.close(); - } + String fileName = ARG_FILE.getValue(cmdLine); + InputStream is = null; + try { + if (fileName == null) { + is = in; + } + else { + URL url = openURL(fileName); + try { + is = url.openStream(); + } + catch (IOException ex) { + /* drop through ... */ + } + if (is == null) { + err.println("Not found " + ARG_FILE.getValue(cmdLine)); + // FIXME ... System.exit(1); + return; + } + } + int len; + final byte[] buf = new byte[ 1024]; + while ((len = is.read(buf)) > 0) { + out.write(buf, 0, len); + } + // FIXME ... Why are we adding an extra newline??? + out.println(); + out.flush(); + } + finally { + if (is != null && fileName != null) { + is.close(); + } + } } private URL openURL(String fname) throws MalformedURLException { Modified: trunk/gui/src/test/org/jnode/test/gui/ConsoleTest2.java =================================================================== --- trunk/gui/src/test/org/jnode/test/gui/ConsoleTest2.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/gui/src/test/org/jnode/test/gui/ConsoleTest2.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -62,7 +62,8 @@ screen = new JTextAreaTextScreen(80,24); manager = new TextScreenConsoleManager(); - console = new TextScreenConsole(manager, "test", screen, ConsoleManager.CreateOptions.TEXT); + console = new TextScreenConsole( + manager, "test", screen, ConsoleManager.CreateOptions.TEXT); manager.focus(console); CommandShell commandShell = new CommandShell(console); Modified: trunk/gui/src/test/org/jnode/test/gui/FBConsole.java =================================================================== --- trunk/gui/src/test/org/jnode/test/gui/FBConsole.java 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/gui/src/test/org/jnode/test/gui/FBConsole.java 2007-08-02 18:15:27 UTC (rev 3374) @@ -58,7 +58,8 @@ ScrollableTextScreen ts = new FBConsole.FBPcTextScreen(g).createCompatibleScrollableBufferScreen(500); - ScrollableTextScreenConsole first = new ScrollableTextScreenConsole(mgr, "console", ts, + ScrollableTextScreenConsole first = + new ScrollableTextScreenConsole(mgr, "console", ts, ConsoleManager.CreateOptions.TEXT | ConsoleManager.CreateOptions.SCROLLABLE); Modified: trunk/shell/descriptors/org.jnode.shell.xml =================================================================== --- trunk/shell/descriptors/org.jnode.shell.xml 2007-07-31 18:23:49 UTC (rev 3373) +++ trunk/shell/descriptors/org.jnode.shell.xml 2007-08-02 18:15:27 UTC (rev 3374) @@ -32,6 +32,7 @@ <permission class="java.io.FilePermission" name="<<ALL FILES>>" actions="read,write"/> <permission class="java.lang.RuntimePermission" name="modifyThreadGroup"/> <permission class="java.lang.RuntimePermission" name="modifyThread"/> + <permission class="java.lang.RuntimePermission" name="setIO"/> <permission class="java.net.SocketPermission" name="*" actions="resolve,listen,connect"/> <permission class="java.net.SocketPermission" name="*:0-" actions="connect,reso... [truncated message content] |