From: <cr...@us...> - 2009-02-16 13:47:37
|
Revision: 5032 http://jnode.svn.sourceforge.net/jnode/?rev=5032&view=rev Author: crawley Date: 2009-02-16 13:47:27 +0000 (Mon, 16 Feb 2009) Log Message: ----------- Add support for input and output files to the black-box test harness Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml trunk/shell/src/test/org/jnode/test/shell/harness/ClassTestRunner.java trunk/shell/src/test/org/jnode/test/shell/harness/CommandTestRunner.java trunk/shell/src/test/org/jnode/test/shell/harness/ScriptTestRunner.java trunk/shell/src/test/org/jnode/test/shell/harness/TestHarness.java trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnable.java trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecification.java trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecificationParser.java Added Paths: ----------- trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnerBase.java Removed Paths: ------------- trunk/shell/src/test/org/jnode/test/shell/harness/JNodeTestRunnerBase.java Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -39,6 +39,7 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -913,7 +914,7 @@ map.put(var.name, var.value); } } - return map; + return Collections.unmodifiableMap(map); } PrintStream resolvePrintStream(CommandIO commandIOIF) { Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-02-16 13:47:27 UTC (rev 5032) @@ -588,9 +588,9 @@ echo 'a b' echo "a b" echo $* - echo $@ + echo $@@ echo "$*" - echo "$@" + echo "$@@" </script> <arg>arg1</arg> <arg>arg2</arg> @@ -611,4 +611,11 @@ + echo arg1 arg2 </error> </testSpec> + <testSpec title="redirection" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + echo > @TEMP_DIR@/blablah Hi mum + </script> + <file name="blablah" input="false">Hi mum +</file> + </testSpec> </testSet> \ No newline at end of file Modified: trunk/shell/src/test/org/jnode/test/shell/harness/ClassTestRunner.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/ClassTestRunner.java 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/test/org/jnode/test/shell/harness/ClassTestRunner.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -20,9 +20,7 @@ package org.jnode.test.shell.harness; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; +import java.io.IOException; import java.lang.reflect.Method; /** @@ -31,17 +29,10 @@ * * @author cr...@jn... */ -class ClassTestRunner implements TestRunnable { +class ClassTestRunner extends TestRunnerBase implements TestRunnable { - private ByteArrayOutputStream outBucket; - private ByteArrayOutputStream errBucket; - - private final TestSpecification spec; - private final TestHarness harness; - public ClassTestRunner(TestSpecification spec, TestHarness harness) { - this.spec = spec; - this.harness = harness; + super(spec, harness); } @Override @@ -53,24 +44,11 @@ return check() ? 0 : 1; } - private boolean check() { + private boolean check() throws IOException { // When a class is run this way we cannot capture the RC. return harness.expect(outBucket.toString(), spec.getOutputContent(), "output content") & - harness.expect(errBucket.toString(), spec.getErrorContent(), "err content"); + harness.expect(errBucket.toString(), spec.getErrorContent(), "err content") & + checkFiles(); } - - @Override - public void cleanup() { - } - - @Override - public void setup() { - System.setIn(new ByteArrayInputStream(spec.getInputContent().toString().getBytes())); - outBucket = new ByteArrayOutputStream(); - errBucket = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outBucket)); - System.setErr(new PrintStream(errBucket)); - } - } Modified: trunk/shell/src/test/org/jnode/test/shell/harness/CommandTestRunner.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/CommandTestRunner.java 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/test/org/jnode/test/shell/harness/CommandTestRunner.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -20,9 +20,7 @@ package org.jnode.test.shell.harness; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; +import java.io.IOException; import org.jnode.shell.CommandShell; @@ -33,12 +31,8 @@ * * @author cr...@jn... */ -class CommandTestRunner extends JNodeTestRunnerBase implements TestRunnable { +class CommandTestRunner extends TestRunnerBase implements TestRunnable { - private ByteArrayOutputStream outBucket; - private ByteArrayOutputStream errBucket; - - public CommandTestRunner(TestSpecification spec, TestHarness harness) { super(spec, harness); } @@ -55,24 +49,11 @@ return check(rc) ? 0 : 1; } - private boolean check(int rc) { + private boolean check(int rc) throws IOException { return harness.expect(rc, spec.getRc(), "return code") & harness.expect(outBucket.toString(), spec.getOutputContent(), "output content") & - harness.expect(errBucket.toString(), spec.getErrorContent(), "err content"); + harness.expect(errBucket.toString(), spec.getErrorContent(), "err content") & + checkFiles(); } - - @Override - public void cleanup() { - } - - @Override - public void setup() { - System.setIn(new ByteArrayInputStream(spec.getInputContent().toString().getBytes())); - outBucket = new ByteArrayOutputStream(); - errBucket = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outBucket)); - System.setErr(new PrintStream(errBucket)); - } - } Deleted: trunk/shell/src/test/org/jnode/test/shell/harness/JNodeTestRunnerBase.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/JNodeTestRunnerBase.java 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/test/org/jnode/test/shell/harness/JNodeTestRunnerBase.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -1,144 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2009 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.test.shell.harness; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; - -import org.jnode.naming.InitialNaming; -import org.jnode.plugin.PluginManager; -import org.jnode.shell.CommandShell; -import org.jnode.shell.ShellException; - -/** - * This base class supplies functions for getting hold of "the shell" for - * testing commands, configuring required plugins and setting up the - * System streams for a command. - * - * @author cr...@jn... - */ -public abstract class JNodeTestRunnerBase implements TestRunnable { - - private class TeeStream extends FilterOutputStream { - private OutputStream out2; - - public TeeStream(OutputStream out, OutputStream out2) { - super(out); - this.out2 = out2; - } - - @Override - public void close() throws IOException { - super.close(); - out2.close(); - } - - @Override - public void flush() throws IOException { - super.flush(); - out2.flush(); - } - - @Override - public void write(int b) throws IOException { - super.write(b); - out2.write(b); - } - } - - protected ByteArrayOutputStream outBucket; - protected ByteArrayOutputStream errBucket; - - protected final TestSpecification spec; - protected final TestHarness harness; - - protected final boolean usingEmu; - - public JNodeTestRunnerBase(TestSpecification spec, TestHarness harness) { - super(); - this.spec = spec; - this.harness = harness; - this.usingEmu = TestEmu.initEmu(harness.getRoot()); - } - - public CommandShell getShell() throws ShellException { - CommandShell shell = TestEmu.getShell(); - if (shell == null) { - shell = new TestCommandShell(System.in, System.out, System.err); - shell.configureShell(); - } - return shell; - } - - @Override - public void cleanup() { - } - - @Override - public void setup() { - ensurePluginsLoaded(spec.getTestSet()); - for (PluginSpecification plugin : spec.getPlugins()) { - ensurePluginLoaded(plugin); - } - System.setIn(new ByteArrayInputStream(spec.getInputContent().toString().getBytes())); - outBucket = new ByteArrayOutputStream(); - errBucket = new ByteArrayOutputStream(); - if (harness.isDebug()) { - System.setOut(new PrintStream(new TeeStream(outBucket, System.out))); - System.setErr(new PrintStream(new TeeStream(errBucket, System.err))); - } else { - System.setOut(new PrintStream(outBucket)); - System.setErr(new PrintStream(errBucket)); - } - } - - private void ensurePluginsLoaded(TestSetSpecification testSet) { - if (testSet == null) { - return; - } - ensurePluginsLoaded(testSet.getParentSet()); - for (PluginSpecification plugin : spec.getTestSet().getPlugins()) { - ensurePluginLoaded(plugin); - } - } - - protected void ensurePluginLoaded(PluginSpecification plugin) { - if (usingEmu) { - TestEmu.loadPseudoPlugin(plugin.getPluginId(), plugin.getClassName()); - } else { - // TODO - I'm not sure about this. A simpler alternative is to assume - // that all required plugins have already been loaded by JNode. - String ver = (plugin.getPluginVersion().length() == 0) ? - System.getProperty("os.version") : plugin.getPluginVersion(); - try { - PluginManager mgr = InitialNaming.lookup(PluginManager.NAME); - mgr.getRegistry().loadPlugin(mgr.getLoaderManager(), plugin.getPluginId(), ver); - } catch (Exception ex) { - throw new RuntimeException( - "Cannot load plugin '" + plugin.getPluginId() + "/" + ver + "'"); - } - } - } -} Modified: trunk/shell/src/test/org/jnode/test/shell/harness/ScriptTestRunner.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/ScriptTestRunner.java 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/test/org/jnode/test/shell/harness/ScriptTestRunner.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -20,9 +20,13 @@ package org.jnode.test.shell.harness; +import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; -import java.io.Writer; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.Properties; /** @@ -31,7 +35,7 @@ * * @author cr...@jn... */ -class ScriptTestRunner extends JNodeTestRunnerBase implements TestRunnable { +class ScriptTestRunner extends TestRunnerBase implements TestRunnable { private File tempScriptFile; @@ -41,25 +45,28 @@ @Override public int run() throws Exception { + Properties props = new Properties(); + props.setProperty("TEMP_DIR", System.getProperty("java.io.tmpdir")); tempScriptFile = new File(System.getProperty("java.io.tmpdir"), spec.getCommand()); - Writer w = null; + BufferedWriter bw = null; try { - w = new FileWriter(tempScriptFile); - w.write(spec.getScriptContent().toString()); - w.write('\n'); + bw = new BufferedWriter(new FileWriter(tempScriptFile)); + expand(props, spec.getScriptContent().toString(), bw, '@'); + bw.write('\n'); } finally { - w.close(); + bw.close(); } int rc = getShell().runCommandFile(tempScriptFile, spec.getCommand(), spec.getArgs().toArray(new String[0])); return check(rc) ? 0 : 1; } - private boolean check(int rc) { + private boolean check(int rc) throws IOException { return harness.expect(rc, spec.getRc(), "return code") & harness.expect(outBucket.toString(), spec.getOutputContent(), "output content") & - harness.expect(errBucket.toString(), spec.getErrorContent(), "err content"); + harness.expect(errBucket.toString(), spec.getErrorContent(), "err content") & + checkFiles(); } @Override @@ -69,5 +76,56 @@ } super.cleanup(); } - + + /** + * Expand a '@...@' sequences in a template, writing the result to an + * Writer. A sequence '@@' turns into a single '@'. A sequence + * '@name@' expands to the value of the named property if it is defined in + * the property set, or the sequence '@name@' if it does not. A CR, NL or + * EOF in an '@...@' sequence is an error. + * + * @param props the properties to be expanded + * @param template is the template string + * @param w the sink for the expanded template + * @param marker the sequence marker character(defaults to '@') + * @throws IOException + * @throws TestSpecificationException + */ + private void expand(Properties props, String template, BufferedWriter w, char marker) + throws IOException, TestSpecificationException { + int ch; + Reader r = new StringReader(template); + while ((ch = r.read()) != -1) { + if (ch == marker) { + StringBuffer sb = new StringBuffer(20); + while ((ch = r.read()) != marker) { + switch (ch) { + case -1: + throw new TestSpecificationException("Encountered EOF in a " + marker + + "..." + marker + " sequence in script template"); + case '\n': + throw new TestSpecificationException("Encountered newline in a " + + marker + "..." + marker + " sequence in script template"); + default: + sb.append((char) ch); + } + } + if (sb.length() == 0) { + w.write(marker); + } else { + String name = sb.toString(); + String value = props.getProperty(name); + if (value == null) { + w.write(marker); + w.write(sb.toString().toCharArray()); + w.write(marker); + } else { + w.write(value.toCharArray()); + } + } + } else { + w.write((char) ch); + } + } + } } Modified: trunk/shell/src/test/org/jnode/test/shell/harness/TestHarness.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestHarness.java 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestHarness.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -279,4 +279,13 @@ public boolean isDebug() { return debug; } + + public File tempFile(File file) { + return new File(System.getProperty("java.io.tmpdir"), file.toString()); + } + + public boolean fail(String msg) { + report("Incorrect test result in test " + asString(spec.getTitle()) + ": " + msg); + return false; + } } Modified: trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnable.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnable.java 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnable.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -20,6 +20,7 @@ package org.jnode.test.shell.harness; + /** * This is the API implemented by the command test runners. We cannot * use / extend Runnable because we need to propagate any exceptions @@ -31,7 +32,7 @@ public int run() throws Exception; - public void setup(); + public void setup() throws Exception; public void cleanup(); Added: trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnerBase.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnerBase.java (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnerBase.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -0,0 +1,196 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2009 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.test.shell.harness; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +import org.jnode.naming.InitialNaming; +import org.jnode.plugin.PluginManager; +import org.jnode.shell.CommandShell; +import org.jnode.shell.ShellException; +import org.jnode.test.shell.harness.TestSpecification.FileSpecification; + +/** + * This base class supplies functions for getting hold of "the shell" for + * testing commands, configuring required plugins and setting up the + * System streams for a command. + * + * @author cr...@jn... + */ +public abstract class TestRunnerBase implements TestRunnable { + + private class TeeStream extends FilterOutputStream { + private OutputStream out2; + + public TeeStream(OutputStream out, OutputStream out2) { + super(out); + this.out2 = out2; + } + + @Override + public void close() throws IOException { + super.close(); + out2.close(); + } + + @Override + public void flush() throws IOException { + super.flush(); + out2.flush(); + } + + @Override + public void write(int b) throws IOException { + super.write(b); + out2.write(b); + } + } + + protected ByteArrayOutputStream outBucket; + protected ByteArrayOutputStream errBucket; + + protected final TestSpecification spec; + protected final TestHarness harness; + + protected final boolean usingEmu; + + public TestRunnerBase(TestSpecification spec, TestHarness harness) { + super(); + this.spec = spec; + this.harness = harness; + this.usingEmu = TestEmu.initEmu(harness.getRoot()); + } + + public CommandShell getShell() throws ShellException { + CommandShell shell = TestEmu.getShell(); + if (shell == null) { + shell = new TestCommandShell(System.in, System.out, System.err); + shell.configureShell(); + } + return shell; + } + + @Override + public void cleanup() { + if (!harness.isDebug()) { + for (FileSpecification fs : spec.getFiles()) { + if (fs.isInput()) { + File tempFile = harness.tempFile(fs.getFile()); + tempFile.delete(); + } + } + } + } + + @Override + public void setup() throws IOException { + ensurePluginsLoaded(spec.getTestSet()); + for (PluginSpecification plugin : spec.getPlugins()) { + ensurePluginLoaded(plugin); + } + System.setIn(new ByteArrayInputStream(spec.getInputContent().toString().getBytes())); + outBucket = new ByteArrayOutputStream(); + errBucket = new ByteArrayOutputStream(); + if (harness.isDebug()) { + System.setOut(new PrintStream(new TeeStream(outBucket, System.out))); + System.setErr(new PrintStream(new TeeStream(errBucket, System.err))); + } else { + System.setOut(new PrintStream(outBucket)); + System.setErr(new PrintStream(errBucket)); + } + for (FileSpecification fs : spec.getFiles()) { + File tempFile = harness.tempFile(fs.getFile()); + if (fs.isInput()) { + OutputStream os = new FileOutputStream(tempFile); + try { + byte[] bytes = fs.getFileContent().getBytes(); + os.write(bytes); + } finally { + os.close(); + } + } else { + tempFile.delete(); + } + } + } + + protected boolean checkFiles() throws IOException { + boolean ok = true; + for (FileSpecification fs : spec.getFiles()) { + File tempFile = harness.tempFile(fs.getFile()); + if (!fs.isInput()) { + if (!tempFile.exists()) { + harness.fail("file not created: '" + tempFile + "'"); + ok = false; + } else { + int fileLength = (int) tempFile.length(); + byte[] bytes = new byte[fileLength]; + InputStream is = new FileInputStream(tempFile); + try { + is.read(bytes); + } finally { + is.close(); + } + String content = new String(bytes); + ok = ok & harness.expect(content, fs.getFileContent(), "file content (" + tempFile + ")"); + } + } + } + return ok; + } + + private void ensurePluginsLoaded(TestSetSpecification testSet) { + if (testSet == null) { + return; + } + ensurePluginsLoaded(testSet.getParentSet()); + for (PluginSpecification plugin : spec.getTestSet().getPlugins()) { + ensurePluginLoaded(plugin); + } + } + + protected void ensurePluginLoaded(PluginSpecification plugin) { + if (usingEmu) { + TestEmu.loadPseudoPlugin(plugin.getPluginId(), plugin.getClassName()); + } else { + // TODO - I'm not sure about this. A simpler alternative is to assume + // that all required plugins have already been loaded by JNode. + String ver = (plugin.getPluginVersion().length() == 0) ? + System.getProperty("os.version") : plugin.getPluginVersion(); + try { + PluginManager mgr = InitialNaming.lookup(PluginManager.NAME); + mgr.getRegistry().loadPlugin(mgr.getLoaderManager(), plugin.getPluginId(), ver); + } catch (Exception ex) { + throw new RuntimeException( + "Cannot load plugin '" + plugin.getPluginId() + "/" + ver + "'"); + } + } + } +} Modified: trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecification.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecification.java 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecification.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -22,9 +22,7 @@ import java.io.File; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * This represents a command test specification for a command or script. @@ -39,6 +37,31 @@ AS_ALIAS } + public static class FileSpecification { + private final File file; + private final boolean input; + private final String fileContent; + + public FileSpecification(File file, boolean input, + String fileContent) { + this.file = file; + this.input = input; + this.fileContent = fileContent; + } + + public File getFile() { + return file; + } + + public boolean isInput() { + return input; + } + + public String getFileContent() { + return fileContent; + } + } + private final RunMode runMode; private final String command; private final List<String> args = new ArrayList<String>(); @@ -49,19 +72,19 @@ private final String title; private final List<PluginSpecification> plugins = new ArrayList<PluginSpecification>(); private final int rc; - private final Map<File, String> fileMap = new HashMap<File, String>(); + private final List<FileSpecification> files = new ArrayList<FileSpecification>(); private TestSetSpecification testSet; - public TestSpecification(RunMode runMode, String command, String scriptContent2, - String inputContent2, String outputContent2, String errorContent2, + public TestSpecification(RunMode runMode, String command, String scriptContent, + String inputContent, String outputContent, String errorContent, String title, int rc) { super(); this.runMode = runMode; this.command = command; - this.scriptContent = scriptContent2; - this.inputContent = inputContent2; - this.outputContent = outputContent2; - this.errorContent = errorContent2; + this.scriptContent = scriptContent; + this.inputContent = inputContent; + this.outputContent = outputContent; + this.errorContent = errorContent; this.title = title; this.rc = rc; } @@ -86,12 +109,12 @@ plugins.add(plugin); } - public void addFile(File file, String content) { - fileMap.put(file, content); + public void addFile(FileSpecification file) { + files.add(file); } - public Map<File, String> getFileMap() { - return fileMap; + public List<FileSpecification> getFiles() { + return files; } public RunMode getRunMode() { Modified: trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecificationParser.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecificationParser.java 2009-02-15 22:30:50 UTC (rev 5031) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecificationParser.java 2009-02-16 13:47:27 UTC (rev 5032) @@ -113,13 +113,19 @@ private void parseFile(IXMLElement elem, TestSpecification res) throws TestSpecificationException { String fileName = extractAttribute(elem, "name"); - String content = extractElementValue(elem, "content", ""); - res.addFile(new File(fileName), content); + boolean isInput = extractAttribute(elem, "input", "false").equals("true"); + String content = extractElementValue(elem, null, ""); + File file = new File(fileName); + if (file.isAbsolute()) { + throw new TestSpecificationException( + "A '" + elem.getName() + "' element must have a relative 'name''"); + } + res.addFile(new TestSpecification.FileSpecification(file, isInput, content)); } @SuppressWarnings("unused") private String extractElementValue(IXMLElement parent, String name) throws TestSpecificationException { - IXMLElement elem = parent.getFirstChildNamed(name); + IXMLElement elem = name == null ? parent : parent.getFirstChildNamed(name); if (elem == null) { throw new TestSpecificationException( "Element '" + name + "' not found in '" + parent.getName() + "'"); @@ -130,7 +136,7 @@ } private String extractElementValue(IXMLElement parent, String name, String dflt) { - IXMLElement elem = parent.getFirstChildNamed(name); + IXMLElement elem = name == null ? parent : parent.getFirstChildNamed(name); return elem == null ? dflt : elem.getContent(); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-02-24 15:28:40
|
Revision: 5063 http://jnode.svn.sourceforge.net/jnode/?rev=5063&view=rev Author: crawley Date: 2009-02-24 15:28:36 +0000 (Tue, 24 Feb 2009) Log Message: ----------- Implement '( <cmd> ; ... ) > file ' Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/shell/org/jnode/shell/bjorne/ListCommandNode.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-02-24 13:33:41 UTC (rev 5062) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-02-24 15:28:36 UTC (rev 5063) @@ -942,6 +942,12 @@ holders[index].setStream(stream, mine); } } + + void closeStreams() { + for (StreamHolder holder : holders) { + holder.close(); + } + } void performAssignments(BjorneToken[] assignments) throws ShellException { if (assignments != null) { Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/ListCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/ListCommandNode.java 2009-02-24 13:33:41 UTC (rev 5062) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/ListCommandNode.java 2009-02-24 15:28:36 UTC (rev 5063) @@ -71,44 +71,55 @@ @Override public int execute(BjorneContext context) throws ShellException { + int listFlags = getFlags(); int rc = 0; - if (getNodeType() == BjorneInterpreter.CMD_SUBSHELL) { - // This simulates creating a 'subshell'. - context = new BjorneContext(context); - } - int listFlags = getFlags(); - if ((listFlags & BjorneInterpreter.FLAG_PIPE) != 0) { - PipelineStage[] stages = assemblePipeline(context); - boolean done = false; - try { - rc = runPipeline(stages); - done = true; - } finally { - if (!done) { - // If we are propagating an exception, all streams that - // were opened by 'assemblePipeline' must be closed. - for (PipelineStage stage : stages) { - for (StreamHolder holder : stage.holders) { - holder.close(); - } - } + try { + if (getNodeType() == BjorneInterpreter.CMD_SUBSHELL) { + // This simulates creating a 'subshell'. + context = new BjorneContext(context); + StreamHolder[] holders = context.evaluateRedirections(getRedirects()); + for (int i = 0; i < holders.length; i++) { + CommandIO stream = holders[i].getStream(); + context.setStream(i, stream, holders[i].isMine()); } } - } else { - for (CommandNode command : commands) { - int commandFlags = command.getFlags(); - if ((commandFlags & BjorneInterpreter.FLAG_AND_IF) != 0) { - if (context.getLastReturnCode() != 0) { - break; + if ((listFlags & BjorneInterpreter.FLAG_PIPE) != 0) { + PipelineStage[] stages = assemblePipeline(context); + boolean done = false; + try { + rc = runPipeline(stages); + done = true; + } finally { + if (!done) { + // If we are propagating an exception, all streams that + // were opened by 'assemblePipeline' must be closed. + for (PipelineStage stage : stages) { + for (StreamHolder holder : stage.holders) { + holder.close(); + } + } } } - if ((commandFlags & BjorneInterpreter.FLAG_OR_IF) != 0) { - if (context.getLastReturnCode() == 0) { - break; + } else { + for (CommandNode command : commands) { + int commandFlags = command.getFlags(); + if ((commandFlags & BjorneInterpreter.FLAG_AND_IF) != 0) { + if (context.getLastReturnCode() != 0) { + break; + } } + if ((commandFlags & BjorneInterpreter.FLAG_OR_IF) != 0) { + if (context.getLastReturnCode() == 0) { + break; + } + } + rc = command.execute(context); } - rc = command.execute(context); } + } finally { + if (getNodeType() == BjorneInterpreter.CMD_SUBSHELL) { + context.closeStreams(); + } } if ((listFlags & BjorneInterpreter.FLAG_BANG) != 0) { rc = (rc == 0) ? -1 : 0; Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-02-24 13:33:41 UTC (rev 5062) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-02-24 15:28:36 UTC (rev 5063) @@ -629,4 +629,45 @@ <error>Hello mother again </error> </testSpec> + <testSpec title="pipeline" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + echo > @TEMP_DIR@/1 Hi mum + echo > @TEMP_DIR@/2 Hi mum again + cat @TEMP_DIR@/1 @TEMP_DIR@/2 @TEMP_DIR@/1 @TEMP_DIR@/2 | cat + cat @TEMP_DIR@/1 @TEMP_DIR@/2 @TEMP_DIR@/1 @TEMP_DIR@/2 | cat > @TEMP_DIR@/3 + </script> + <file name="1" input="false">Hi mum +</file> + <file name="2" input="false">Hi mum again +</file> + <file name="3" input="false">Hi mum +Hi mum again +Hi mum +Hi mum again +</file> + <output>Hi mum +Hi mum again +Hi mum +Hi mum again +</output> + </testSpec> + <testSpec title="subshell" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + echo > @TEMP_DIR@/1 Hi mum + echo > @TEMP_DIR@/2 Hi mum again + ( cat @TEMP_DIR@/1 ; cat @TEMP_DIR@/2 ) + ( cat @TEMP_DIR@/1 ; cat @TEMP_DIR@/2 ) > @TEMP_DIR@/3 + </script> + <file name="1" input="false">Hi mum +</file> + <file name="2" input="false">Hi mum again +</file> + <file name="3" input="false">Hi mum +Hi mum again +</file> + <output>Hi mum +Hi mum again +</output> + </testSpec> + </testSet> \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-03-10 14:34:47
|
Revision: 5095 http://jnode.svn.sourceforge.net/jnode/?rev=5095&view=rev Author: crawley Date: 2009-03-10 14:34:35 +0000 (Tue, 10 Mar 2009) Log Message: ----------- New classes to be used in a reworked pipeline implementation for Bjorne. (Unfortunately, the classic PipedInput/Output won't work when there are potentially multiple threads reading or writing the same pipe.) Added Paths: ----------- trunk/shell/src/shell/org/jnode/shell/io/Pipeline.java trunk/shell/src/shell/org/jnode/shell/io/PipelineInputStream.java trunk/shell/src/shell/org/jnode/shell/io/PipelineOutputStream.java trunk/shell/src/test/org/jnode/test/shell/io/PipelineTest.java Added: trunk/shell/src/shell/org/jnode/shell/io/Pipeline.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/Pipeline.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/Pipeline.java 2009-03-10 14:34:35 UTC (rev 5095) @@ -0,0 +1,220 @@ +package org.jnode.shell.io; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides a buffered byte-stream pipeline implementation that + * supports multiple sources and sinks. The pipeline has a finite buffer, + * so a thread that reads and writes to the same pipeline risks deadlock. + * <p> + * Unlike the standard Piped* classes, + * Pipeline and its related classes do not try to detect dead pipes based + * on exit of threads. Instead, a pipeline shuts down when the sources + * are closed, or when {@link #shutdown()} method called. + * <p> + * The intended lifecycle of a Pipeline is as follows: + * <ol> + * <li>A Pipeline object is instantiated.</li> + * <li>One or more sources and sinks are created using {@link #createSource()} + * and {@link #createSink()}. + * <li>The Pipeline is activated by calling {@link #activate()}. + * <li>Data is written to the pipeline sources and read from the sinks. + * <li>The pipeline sources are closed, causing the pipeline to shut down cleanly. + * Read calls on the sinks will return any remaining buffered data and then + * signal EOF in the normal way. + * <li>The sinks are closed, and the Pipeline is shutdown. + * </ol> + * + * @author cr...@jn... + */ +public class Pipeline { + // FIXME This first-cut implementation unnecessarily double-copies data when + // a reader is waiting for it. Also, it doesn't fill/empty the buffer in a + // circular fashion. Finally, it doesn't implement atomic writes / reads or + // detect situations where behavior is non-deterministic. + + private List<PipelineInputStream> sinks = + new ArrayList<PipelineInputStream>(); + private List<PipelineOutputStream> sources = + new ArrayList<PipelineOutputStream>(); + + private byte[] buffer = new byte[1024]; + private int pos = 0; + private int lim = 0; + private boolean activated; + + /** + * Create a pipeline, in 'inactive' state. + */ + public Pipeline() { + } + + /** + * Create a sink for a inactive pipeline. + * @return the sink. + * @throws IOException This is thrown if the pipeline is 'active' or 'shut down'. + */ + public synchronized PipelineInputStream createSink() throws IOException { + if (activated) { + throw new IOException("pipeline state wrong"); + } + PipelineInputStream is = new PipelineInputStream(this); + sinks.add(is); + return is; + } + + /** + * Create a source for a inactive pipeline. + * @return the source. + * @throws IOException This is thrown if the pipeline is 'active' or 'shut down'. + */ + public synchronized PipelineOutputStream createSource() throws IOException { + if (activated) { + throw new IOException("pipeline state wrong"); + } + PipelineOutputStream os = new PipelineOutputStream(this); + sources.add(os); + return os; + } + + /** + * Put the pipeline into the 'active' state. + * @throws IOException This is thrown if the pipeline is 'shut down', or + * if it is 'inactive' but there are no sources or sinks. + */ + public synchronized void activate() throws IOException { + if (buffer == null) { + throw new IOException("pipeline state wrong"); + } + if (sinks.isEmpty() || sources.isEmpty()) { + throw new IOException("pipeline has no inputs and/or outputs"); + } + activated = true; + } + + /** + * Test if the pipeline is in the 'active' state. + * @return + */ + public synchronized boolean isActive() { + return activated && buffer != null; + } + + /** + * Test if the pipeline is in the 'shut down' state. + * @return + */ + public synchronized boolean isShutdown() { + return activated && buffer == null; + } + + /** + * Forcibly shut down the pipeline. This will cause any threads + * currently blocked on sources or sinks to get an IOException. + */ + public synchronized void shutdown() { + buffer = null; + this.notifyAll(); + } + + synchronized int available() { + return lim - pos; + } + + synchronized void closeInput(PipelineInputStream input) { + sinks.remove(input); + if (sinks.isEmpty()) { + buffer = null; + this.notifyAll(); + } + } + + synchronized void closeOutput(PipelineOutputStream output) { + sources.remove(output); + if (sources.isEmpty()) { + buffer = null; + this.notifyAll(); + } + } + + synchronized int read(byte[] b, int off, int len) throws IOException { + if (buffer == null) { + throw new IOException("pipeline state wrong"); + } + int startOff = off; + while (off < len && !sources.isEmpty()) { + while (pos == lim) { + try { + this.wait(); + } catch (InterruptedException ex) { + throw new InterruptedIOException(); + } + } + while (off < len && pos != lim) { + b[off++] = buffer[pos++]; + } + if (pos == lim) { + pos = 0; + lim = 0; + } + this.notify(); + } + return (off == startOff) ? -1 : (off - startOff); + } + + synchronized long skip(long n) throws IOException { + if (buffer == null) { + throw new IOException("pipeline state wrong"); + } + long off = 0; + while (off < n && !sources.isEmpty()) { + while (pos == lim) { + try { + this.wait(); + } catch (InterruptedException ex) { + throw new InterruptedIOException(); + } + } + long count = Math.min(lim - pos, n - off); + pos += count; + if (pos == lim) { + pos = 0; + lim = 0; + } + this.notify(); + } + return off == 0 ? -1 : off; + } + + synchronized void flush() throws IOException { + if (buffer == null) { + throw new IOException("pipeline state wrong"); + } + this.notifyAll(); + } + + synchronized void write(byte[] b, int off, int len) throws IOException { + if (buffer == null) { + throw new IOException("pipeline state wrong"); + } + while (off < len) { + while (lim == buffer.length) { + if (sinks.isEmpty()) { + throw new IOException("pipeline broken"); + } + try { + this.wait(); + } catch (InterruptedException ex) { + throw new InterruptedIOException(); + } + } + while (off < len && lim < buffer.length) { + buffer[lim++] = b[off++]; + } + this.notify(); + } + } +} Added: trunk/shell/src/shell/org/jnode/shell/io/PipelineInputStream.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/PipelineInputStream.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/PipelineInputStream.java 2009-03-10 14:34:35 UTC (rev 5095) @@ -0,0 +1,95 @@ +package org.jnode.shell.io; + +import java.io.IOException; +import java.io.InputStream; + +/** + * This class provides the input side of a JNode shell pipeline. + * Unlike the standard PipedInputStream, this one is designed to + * support a pipeline with multiple readers and writers. + * + * @author cr...@jn... + */ +public final class PipelineInputStream extends InputStream { + + private Pipeline pipeline; + + /** + * This is not a public constructor. Use {@link Pipeline#createSink()} + * to create a PipelineInputStream instance. + * + * @param pipeline the parent pipeline. + */ + PipelineInputStream(Pipeline pipeline) { + this.pipeline = pipeline; + } + + @Override + public int read() throws IOException { + if (pipeline == null) { + throw new IOException("pipeline closed"); + } + byte[] buffer = new byte[1]; + int got = pipeline.read(buffer, 0, 1); + if (got == 1) { + return buffer[0]; + } else { + return -1; + } + } + + @Override + public int available() throws IOException { + if (pipeline == null) { + throw new IOException("pipeline closed"); + } + return pipeline.available(); + } + + @Override + public void close() throws IOException { + if (pipeline != null) { + pipeline.closeInput(this); + pipeline = null; + } + } + + @Override + public synchronized void mark(int readlimit) { + // ignore + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (pipeline == null) { + throw new IOException("pipeline closed"); + } + return pipeline.read(b, off, len); + } + + @Override + public int read(byte[] b) throws IOException { + if (pipeline == null) { + throw new IOException("pipeline closed"); + } + return pipeline.read(b, 0, b.length); + } + + @Override + public synchronized void reset() throws IOException { + throw new IOException("reset not supported"); + } + + @Override + public long skip(long n) throws IOException { + if (pipeline == null) { + throw new IOException("pipeline closed"); + } + return pipeline.skip(n); + } +} Added: trunk/shell/src/shell/org/jnode/shell/io/PipelineOutputStream.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/PipelineOutputStream.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/PipelineOutputStream.java 2009-03-10 14:34:35 UTC (rev 5095) @@ -0,0 +1,68 @@ +package org.jnode.shell.io; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * This class provides the output side of a JNode shell pipeline. + * Unlike the standard PipedInputStream, this one is designed to + * support a pipeline with multiple readers and writers. + * + * @author cr...@jn... + */ +public final class PipelineOutputStream extends OutputStream { + + private Pipeline pipeline; + + /** + * This is not a public constructor. Use {@link Pipeline#createSource()} + * to create a PipelineOutputStream instance. + * + * @param pipeline the parent pipeline. + */ + PipelineOutputStream(Pipeline pipeline) { + this.pipeline = pipeline; + } + + @Override + public void close() throws IOException { + if (pipeline != null) { + pipeline.closeOutput(this); + pipeline = null; + } + } + + @Override + public void flush() throws IOException { + if (pipeline == null) { + throw new IOException("pipeline closed"); + } + pipeline.flush(); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (pipeline == null) { + throw new IOException("pipeline closed"); + } + pipeline.write(b, off, len); + } + + @Override + public void write(byte[] b) throws IOException { + if (pipeline == null) { + throw new IOException("pipeline closed"); + } + pipeline.write(b, 0, b.length); + } + + @Override + public void write(int b) throws IOException { + if (pipeline == null) { + throw new IOException("pipeline closed"); + } + byte[] buffer = new byte[]{(byte) b}; + pipeline.write(buffer, 0, 1); + } + +} Added: trunk/shell/src/test/org/jnode/test/shell/io/PipelineTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/io/PipelineTest.java (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/io/PipelineTest.java 2009-03-10 14:34:35 UTC (rev 5095) @@ -0,0 +1,100 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2009 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.test.shell.io; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import junit.framework.TestCase; + +import org.jnode.shell.io.Pipeline; + +public class PipelineTest extends TestCase { + + public void testConstructor() { + new Pipeline(); + } + + public void testLifecycle() throws IOException { + Pipeline p = new Pipeline(); + InputStream is = p.createSink(); + OutputStream os = p.createSource(); + assertFalse(p.isActive()); + assertFalse(p.isShutdown()); + p.activate(); + assertTrue(p.isActive()); + assertFalse(p.isShutdown()); + is.close(); + os.close(); + assertFalse(p.isActive()); + assertTrue(p.isShutdown()); + } + + public void testLifecycle2() throws IOException { + Pipeline p = new Pipeline(); + InputStream is = p.createSink(); + OutputStream os = p.createSource(); + assertFalse(p.isActive()); + assertFalse(p.isShutdown()); + p.activate(); + assertTrue(p.isActive()); + assertFalse(p.isShutdown()); + p.shutdown(); + assertFalse(p.isActive()); + assertTrue(p.isShutdown()); + try { + is.read(); + fail("no exception on read()"); + } catch (IOException ex) { + // expected ... + } + try { + os.write('X'); + fail("no exception on write()"); + } catch (IOException ex) { + // expected ... + } + } + + public void testOneOne() throws IOException { + // This should work ... despite the source and sink being operated from + // the same thread ... because we are reading/writing less than a buffer full. + Pipeline p = new Pipeline(); + InputStream is = p.createSink(); + OutputStream os = p.createSource(); + p.activate(); + assertEquals(0, is.available()); + os.write('A'); + assertEquals(1, is.available()); + assertEquals('A', is.read()); + os.write('B'); + assertEquals('B', is.read()); + assertEquals(0, is.available()); + os.write("the quick brown fox".getBytes()); + int len = "the quick brown fox".length(); + assertEquals(len, is.available()); + byte[] buffer = new byte[100]; + assertEquals(len, is.read(buffer, 0, len)); + assertEquals("the quick brown fox", new String(buffer, 0, len)); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-03-13 11:25:06
|
Revision: 5096 http://jnode.svn.sourceforge.net/jnode/?rev=5096&view=rev Author: crawley Date: 2009-03-13 11:24:46 +0000 (Fri, 13 Mar 2009) Log Message: ----------- Fixed bugs and expanded unit test for Pipeline et al. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/io/Pipeline.java trunk/shell/src/test/org/jnode/test/shell/io/PipelineTest.java Modified: trunk/shell/src/shell/org/jnode/shell/io/Pipeline.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/Pipeline.java 2009-03-10 14:34:35 UTC (rev 5095) +++ trunk/shell/src/shell/org/jnode/shell/io/Pipeline.java 2009-03-13 11:24:46 UTC (rev 5096) @@ -32,49 +32,77 @@ */ public class Pipeline { // FIXME This first-cut implementation unnecessarily double-copies data when - // a reader is waiting for it. Also, it doesn't fill/empty the buffer in a - // circular fashion. Finally, it doesn't implement atomic writes / reads or - // detect situations where behavior is non-deterministic. + // a reader is waiting for it. It doesn't fill/empty the buffer in a + // circular fashion. If there are multiple active readers or writers, + // too many threads get woken up. Finally, this class doesn't implement + // atomic writes / reads or detect cases where behavior is non-deterministic. private List<PipelineInputStream> sinks = new ArrayList<PipelineInputStream>(); private List<PipelineOutputStream> sources = new ArrayList<PipelineOutputStream>(); - private byte[] buffer = new byte[1024]; + private byte[] buffer; private int pos = 0; private int lim = 0; - private boolean activated; + private int state = INITIAL; + private static final int INITIAL = 1; + private static final int ACTIVE = 2; + private static final int CLOSED = 4; + private static final int SHUTDOWN = 8; + + private static final String[] STATE_NAMES = new String[] { + null, "INITIAL", "ACTIVE", null, "CLOSED", + null, null, null, "SHUTDOWN" + }; + /** - * Create a pipeline, in 'inactive' state. + * The default Pipeline buffer size. */ + public static final int DEFAULT_BUFFER_SIZE = 1024; + + /** + * Create a pipeline, in 'inactive' state with the default buffer size; + */ public Pipeline() { + buffer = new byte[DEFAULT_BUFFER_SIZE]; } /** + * Create a pipeline, in 'inactive' state. + * @param bufferSize the pipeline's buffer size. + */ + public Pipeline(int bufferSize) { + buffer = new byte[bufferSize]; + } + + /** * Create a sink for a inactive pipeline. * @return the sink. * @throws IOException This is thrown if the pipeline is 'active' or 'shut down'. */ public synchronized PipelineInputStream createSink() throws IOException { - if (activated) { - throw new IOException("pipeline state wrong"); - } + checkState(INITIAL, "create"); PipelineInputStream is = new PipelineInputStream(this); sinks.add(is); return is; } + private void checkState(int allowedStates, String action) throws IOException { + if ((state & allowedStates) == 0) { + String stateName = STATE_NAMES[state]; + throw new IOException(action + " not allowed in state " + stateName); + } + } + /** * Create a source for a inactive pipeline. * @return the source. * @throws IOException This is thrown if the pipeline is 'active' or 'shut down'. */ public synchronized PipelineOutputStream createSource() throws IOException { - if (activated) { - throw new IOException("pipeline state wrong"); - } + checkState(INITIAL, "create"); PipelineOutputStream os = new PipelineOutputStream(this); sources.add(os); return os; @@ -86,13 +114,11 @@ * if it is 'inactive' but there are no sources or sinks. */ public synchronized void activate() throws IOException { - if (buffer == null) { - throw new IOException("pipeline state wrong"); - } + checkState(INITIAL, "activate"); if (sinks.isEmpty() || sources.isEmpty()) { throw new IOException("pipeline has no inputs and/or outputs"); } - activated = true; + state = ACTIVE; } /** @@ -100,15 +126,23 @@ * @return */ public synchronized boolean isActive() { - return activated && buffer != null; + return state == ACTIVE; } /** + * Test if the pipeline is in the 'closed' state. + * @return + */ + public synchronized boolean isClosed() { + return state == CLOSED; + } + + /** * Test if the pipeline is in the 'shut down' state. * @return */ public synchronized boolean isShutdown() { - return activated && buffer == null; + return state == SHUTDOWN; } /** @@ -116,97 +150,98 @@ * currently blocked on sources or sinks to get an IOException. */ public synchronized void shutdown() { - buffer = null; + state = SHUTDOWN; this.notifyAll(); } - synchronized int available() { + synchronized int available() throws IOException { + checkState(ACTIVE, "available"); return lim - pos; } synchronized void closeInput(PipelineInputStream input) { sinks.remove(input); if (sinks.isEmpty()) { - buffer = null; - this.notifyAll(); + if (state < CLOSED) { + state = CLOSED; + this.notifyAll(); + } } } synchronized void closeOutput(PipelineOutputStream output) { sources.remove(output); if (sources.isEmpty()) { - buffer = null; - this.notifyAll(); + if (state < CLOSED) { + state = CLOSED; + this.notifyAll(); + } } } synchronized int read(byte[] b, int off, int len) throws IOException { - if (buffer == null) { - throw new IOException("pipeline state wrong"); - } + checkState(ACTIVE | CLOSED | SHUTDOWN, "read"); int startOff = off; - while (off < len && !sources.isEmpty()) { - while (pos == lim) { + while (off < len && state <= CLOSED) { + while (pos == lim && state == ACTIVE) { try { this.wait(); } catch (InterruptedException ex) { throw new InterruptedIOException(); } } - while (off < len && pos != lim) { + if (pos == lim) { + break; + } + while (off < len && pos < lim) { b[off++] = buffer[pos++]; } if (pos == lim) { pos = 0; lim = 0; } - this.notify(); + this.notifyAll(); } return (off == startOff) ? -1 : (off - startOff); } synchronized long skip(long n) throws IOException { - if (buffer == null) { - throw new IOException("pipeline state wrong"); - } + checkState(ACTIVE | CLOSED | SHUTDOWN, "skip"); long off = 0; - while (off < n && !sources.isEmpty()) { - while (pos == lim) { + while (off < n && state <= CLOSED) { + while (pos == lim && state == ACTIVE) { try { this.wait(); } catch (InterruptedException ex) { throw new InterruptedIOException(); } } + if (pos == lim) { + break; + } long count = Math.min(lim - pos, n - off); pos += count; if (pos == lim) { pos = 0; lim = 0; } - this.notify(); + this.notifyAll(); } return off == 0 ? -1 : off; } synchronized void flush() throws IOException { - if (buffer == null) { - throw new IOException("pipeline state wrong"); - } + checkState(ACTIVE | CLOSED, "flush"); this.notifyAll(); } synchronized void write(byte[] b, int off, int len) throws IOException { - if (buffer == null) { - throw new IOException("pipeline state wrong"); - } + checkState(ACTIVE, "write"); while (off < len) { while (lim == buffer.length) { - if (sinks.isEmpty()) { - throw new IOException("pipeline broken"); - } try { this.wait(); + checkState(ACTIVE, "write"); } catch (InterruptedException ex) { throw new InterruptedIOException(); } @@ -214,7 +249,7 @@ while (off < len && lim < buffer.length) { buffer[lim++] = b[off++]; } - this.notify(); + this.notifyAll(); } } } Modified: trunk/shell/src/test/org/jnode/test/shell/io/PipelineTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/io/PipelineTest.java 2009-03-10 14:34:35 UTC (rev 5095) +++ trunk/shell/src/test/org/jnode/test/shell/io/PipelineTest.java 2009-03-13 11:24:46 UTC (rev 5096) @@ -17,13 +17,17 @@ * 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.test.shell.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import junit.framework.TestCase; @@ -40,14 +44,17 @@ InputStream is = p.createSink(); OutputStream os = p.createSource(); assertFalse(p.isActive()); + assertFalse(p.isClosed()); assertFalse(p.isShutdown()); p.activate(); assertTrue(p.isActive()); + assertFalse(p.isClosed()); assertFalse(p.isShutdown()); is.close(); os.close(); assertFalse(p.isActive()); - assertTrue(p.isShutdown()); + assertTrue(p.isClosed()); + assertFalse(p.isShutdown()); } public void testLifecycle2() throws IOException { @@ -62,13 +69,8 @@ p.shutdown(); assertFalse(p.isActive()); assertTrue(p.isShutdown()); + assertEquals(-1, is.read()); try { - is.read(); - fail("no exception on read()"); - } catch (IOException ex) { - // expected ... - } - try { os.write('X'); fail("no exception on write()"); } catch (IOException ex) { @@ -97,4 +99,244 @@ assertEquals(len, is.read(buffer, 0, len)); assertEquals("the quick brown fox", new String(buffer, 0, len)); } + + public void testTwo1_One1() throws Throwable { + Pipeline p = new Pipeline(); + InputStream is = p.createSink(); + OutputStream os = p.createSource(); + OutputStream os2 = p.createSource(); + p.activate(); + + Source source = new Source("1".getBytes(), 10000, -1, os); + Source source2 = new Source("2".getBytes(), 10000, -1, os2); + Sink sink = new Sink(20000, 1, -1, is); + + List<Throwable> exceptions = runInThreads(new Runnable[] {source, source2, sink}); + if (exceptions.size() > 0) { + throw exceptions.get(0); + } + assertEquals(10000, sink.getCount((byte) '1')); + assertEquals(10000, sink.getCount((byte) '2')); + } + + public void testTwo10_One10() throws Throwable { + Pipeline p = new Pipeline(); + InputStream is = p.createSink(); + OutputStream os = p.createSource(); + OutputStream os2 = p.createSource(); + p.activate(); + + Source source = new Source("1111111111".getBytes(), 1000, -1, os); + Source source2 = new Source("2222222222".getBytes(), 1000, -1, os2); + Sink sink = new Sink(20000, 10, -1, is); + + List<Throwable> exceptions = runInThreads(new Runnable[] {source, source2, sink}); + if (exceptions.size() > 0) { + throw exceptions.get(0); + } + assertEquals(10000, sink.getCount((byte) '1')); + assertEquals(10000, sink.getCount((byte) '2')); + } + + public void testTwo100_One100() throws Throwable { + Pipeline p = new Pipeline(); + InputStream is = p.createSink(); + OutputStream os = p.createSource(); + OutputStream os2 = p.createSource(); + p.activate(); + + byte[] buff1 = new byte[100]; + Arrays.fill(buff1, (byte) '1'); + Source source = new Source(buff1, 100, -1, os); + byte[] buff2 = new byte[100]; + Arrays.fill(buff2, (byte) '2'); + Source source2 = new Source(buff2, 100, -1, os2); + Sink sink = new Sink(20000, 100, -1, is); + + List<Throwable> exceptions = runInThreads(new Runnable[] {source, source2, sink}); + if (exceptions.size() > 0) { + throw exceptions.get(0); + } + assertEquals(10000, sink.getCount((byte) '1')); + assertEquals(10000, sink.getCount((byte) '2')); + } + + public void testTwo100_One100_SmallBuffer() throws Throwable { + Pipeline p = new Pipeline(100); + InputStream is = p.createSink(); + OutputStream os = p.createSource(); + OutputStream os2 = p.createSource(); + p.activate(); + + byte[] buff1 = new byte[100]; + Arrays.fill(buff1, (byte) '1'); + Source source = new Source(buff1, 100, -1, os); + byte[] buff2 = new byte[100]; + Arrays.fill(buff2, (byte) '2'); + Source source2 = new Source(buff2, 100, -1, os2); + Sink sink = new Sink(20000, 100, -1, is); + + List<Throwable> exceptions = runInThreads(new Runnable[] {source, source2, sink}); + if (exceptions.size() > 0) { + throw exceptions.get(0); + } + assertEquals(10000, sink.getCount((byte) '1')); + assertEquals(10000, sink.getCount((byte) '2')); + } + + /** + * Create Threads for each runnable (with an exception handler), start them, + * join them and return any exceptions + * @param runnables the test runnables + * @return any exceptions caught. + */ + private List<Throwable> runInThreads(Runnable[] runnables) { + final List<Throwable> exceptions = new ArrayList<Throwable>(); + Thread[] threads = new Thread[runnables.length]; + for (int i = 0; i < threads.length; i++) { + System.err.println("create"); + threads[i] = new Thread(runnables[i]); + threads[i].setUncaughtExceptionHandler( + new UncaughtExceptionHandler() { + public void uncaughtException(Thread thr, Throwable exc) { + System.err.println("handled exception " + exc); + exc.printStackTrace(); + exceptions.add(exc); + } + }); + } + for (Thread thread : threads) { + System.err.println("start"); + thread.start(); + } + for (Thread thread : threads) { + try { + System.err.println("join"); + thread.join(); + } catch (InterruptedException ex) { + exceptions.add(ex); + } + } + System.err.println("done"); + return exceptions; + } + + private static class Source implements Runnable { + private byte[] b; + private int count; + private int sleep; + private OutputStream os; + + /** + * Create a test source. + * @param b the buffer to be written + * @param count the number of times + * @param sleep sleep this number of milliseconds between writes; -1 means no sleep. + * @param os write bytes here. + */ + Source(byte[] b, int count, int sleep, OutputStream os) { + this.b = b; + this.count = count; + this.os = os; + this.sleep = sleep; + } + + @Override + public void run() { + try { + for (int i = 0; i < count; i++) { + os.write(b); + if (sleep != -1) { + Thread.sleep(sleep); + } + } + os.close(); + os = null; + } catch (IOException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + System.err.println("wrote " + count); + try { + if (os != null) { + os.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + } + + private static class Sink implements Runnable { + private HashMap<Byte, Integer> counters = new HashMap<Byte, Integer>(); + private InputStream is; + private int max; + private int sleep; + private int len; + + /** + * Create a test Sink. + * @param max if we read more than this number of bytes, throw an exception + * @param len the read buffer size. + * @param sleep sleep this number of milliseconds between reads; -1 means no sleep. + * @param is read bytes from here. + */ + Sink(int max, int len, int sleep, InputStream is) { + this.is = is; + this.max = max; + this.sleep = sleep; + this.len = len; + } + + @Override + public void run() { + try { + int counter = 0; + byte[] buffer = new byte[len]; + int nosRead = is.read(buffer); + while (nosRead != -1) { + for (int i = 0; i < nosRead; i++) { + Byte bb = new Byte((byte) buffer[i]); + Integer cc = counters.get(bb); + int c = (cc == null) ? 1 : (cc.intValue() + 1); + counters.put(bb, new Integer(c)); + if (++counter > max) { + throw new RuntimeException("too many (" + counter + ")"); + } + } + if (sleep != -1) { + Thread.sleep(sleep); + } + nosRead = is.read(buffer); + } + System.err.println("read " + counter); + is.close(); + is = null; + } catch (IOException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Get the number of bytes read for a given byte value. + * @param b the key byte + * @return the number instances of the 'key' byte read. + */ + public int getCount(byte b) { + Integer cc = counters.get(b); + return (cc == null) ? 0 : cc.intValue(); + } + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-03-15 06:00:02
|
Revision: 5108 http://jnode.svn.sourceforge.net/jnode/?rev=5108&view=rev Author: crawley Date: 2009-03-15 05:59:57 +0000 (Sun, 15 Mar 2009) Log Message: ----------- Lots of changes aimed at getting Bjorne pipelines to work. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/AbstractCommand.java trunk/shell/src/shell/org/jnode/shell/AsyncCommandInvoker.java trunk/shell/src/shell/org/jnode/shell/CommandRunnable.java trunk/shell/src/shell/org/jnode/shell/CommandRunner.java trunk/shell/src/shell/org/jnode/shell/CommandShell.java trunk/shell/src/shell/org/jnode/shell/CommandThreadImpl.java trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneBuiltin.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java trunk/shell/src/shell/org/jnode/shell/bjorne/CaseCommandNode.java trunk/shell/src/shell/org/jnode/shell/bjorne/ColonBuiltin.java trunk/shell/src/shell/org/jnode/shell/bjorne/CommandNode.java trunk/shell/src/shell/org/jnode/shell/bjorne/ForCommandNode.java trunk/shell/src/shell/org/jnode/shell/bjorne/FunctionDefinitionNode.java trunk/shell/src/shell/org/jnode/shell/bjorne/IfCommandNode.java trunk/shell/src/shell/org/jnode/shell/bjorne/ListCommandNode.java trunk/shell/src/shell/org/jnode/shell/bjorne/LoopCommandNode.java trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java trunk/shell/src/shell/org/jnode/shell/io/BaseCommandIO.java trunk/shell/src/shell/org/jnode/shell/io/CommandIO.java trunk/shell/src/shell/org/jnode/shell/io/CommandIOMarker.java trunk/shell/src/shell/org/jnode/shell/io/CommandInput.java trunk/shell/src/shell/org/jnode/shell/io/CommandInputOutput.java trunk/shell/src/shell/org/jnode/shell/io/CommandOutput.java trunk/shell/src/shell/org/jnode/shell/io/Pipeline.java trunk/shell/src/shell/org/jnode/shell/io/PipelineOutputStream.java trunk/shell/src/shell/org/jnode/shell/isolate/IsolateCommandInvoker.java trunk/shell/src/shell/org/jnode/shell/isolate/IsolateCommandThreadImpl.java trunk/shell/src/shell/org/jnode/shell/proclet/ProcletCommandInvoker.java trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneContextTests.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml trunk/shell/src/test/org/jnode/test/shell/harness/TestEmu.java Added Paths: ----------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjornePipeline.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneSubshellRunner.java trunk/shell/src/shell/org/jnode/shell/io/CommandIOHolder.java Removed Paths: ------------- trunk/shell/src/shell/org/jnode/shell/bjorne/StreamHolder.java Modified: trunk/shell/src/shell/org/jnode/shell/AbstractCommand.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/AbstractCommand.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/AbstractCommand.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -94,7 +94,7 @@ CommandIO[] myIOs = new CommandIO[] { new CommandInput(System.in), new CommandOutput(System.out), - new CommandOutput(System.err), + new CommandOutput(System.err) }; command.initialize(new CommandLine(args), myIOs); command.execute(); Modified: trunk/shell/src/shell/org/jnode/shell/AsyncCommandInvoker.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/AsyncCommandInvoker.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/AsyncCommandInvoker.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -107,9 +107,8 @@ CommandRunner cr = null; CommandIO[] ios = cmdLine.getStreams(); - CommandIO[] resolvedIOs; try { - resolvedIOs = commandShell.resolveStreams(ios); + commandShell.resolveStreams(ios); } catch (ClassCastException ex) { throw new ShellFailureException("streams array broken", ex); } @@ -120,7 +119,7 @@ throw new ShellInvocationException("Problem while creating command instance", ex); } if (command != null) { - cr = new CommandRunner(this, cmdInfo, cmdLine, resolvedIOs, sysProps, env); + cr = new CommandRunner(this, cmdInfo, cmdLine, ios, sysProps, env); } else { try { method = cmdInfo.getCommandClass().getMethod(MAIN_METHOD, MAIN_ARG_TYPES); @@ -140,7 +139,7 @@ method.setAccessible(true); cr = new CommandRunner( this, cmdInfo, cmdInfo.getCommandClass(), method, - new Object[] {cmdLine.getArguments()}, resolvedIOs, sysProps, env); + new Object[] {cmdLine.getArguments()}, ios, sysProps, env); } catch (NoSuchMethodException e) { // continue; } @@ -149,9 +148,6 @@ "No entry point method found for " + cmdInfo.getCommandClass()); } } - - // These are now the real streams ... - cmdLine.setStreams(resolvedIOs); return cr; } Modified: trunk/shell/src/shell/org/jnode/shell/CommandRunnable.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandRunnable.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/CommandRunnable.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -4,4 +4,6 @@ void flushStreams(); + int getRC(); + } Modified: trunk/shell/src/shell/org/jnode/shell/CommandRunner.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandRunner.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/CommandRunner.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -93,7 +93,6 @@ public void run() { try { - boolean ok = false; try { if (method != null) { try { @@ -121,10 +120,9 @@ // bounce us to the older execute(CommandLine, InputStream, PrintStream, // PrintStream) method. Command cmd = cmdInfo.createCommandInstance(); - cmd.initialize(commandLine, getIos()); + cmd.initialize(commandLine, getIOs()); cmd.execute(); } - ok = true; } catch (PrivilegedActionException ex) { Exception ex2 = ex.getException(); if (ex2 instanceof InvocationTargetException) { @@ -132,26 +130,7 @@ } else { throw ex2; } - } finally { - IOException savedEx = null; - // Make sure that we try to flush all IOs, even if some of the flushes - // result in IOExceptions. - for (CommandIO io : getIos()) { - try { - io.flush(); - } catch (IOException ex) { - shellErr.println("Failed to flush output: " + ex.getMessage()); - if (ok) { - savedEx = ex; - } - } - } - if (savedEx != null) { - // If we weren't already propagating an exception, and the flush failed, - // propagate one of the IOExceptions. - throw savedEx; - } - } + } } catch (SyntaxErrorException ex) { try { HelpFactory.getHelpFactory().getHelp(commandLine.getCommandName(), cmdInfo).usage(shellErr); @@ -197,7 +176,7 @@ return args; } - public CommandIO[] getIos() { + public CommandIO[] getIOs() { return ios; } Modified: trunk/shell/src/shell/org/jnode/shell/CommandShell.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -979,12 +979,10 @@ } } - public CommandIO[] resolveStreams(CommandIO[] ios) { - CommandIO[] res = new CommandIO[ios.length]; - for (int i = 0; i < res.length; i++) { - res[i] = resolveStream(ios[i]); + public void resolveStreams(CommandIO[] ios) { + for (int i = 0; i < ios.length; i++) { + ios[i] = resolveStream(ios[i]); } - return res; } public PrintStream resolvePrintStream(CommandIO io) { Modified: trunk/shell/src/shell/org/jnode/shell/CommandThreadImpl.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandThreadImpl.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/CommandThreadImpl.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -83,11 +83,7 @@ } public int getReturnCode() { - if (this.runner instanceof CommandRunner) { - return ((CommandRunner) this.runner).getRC(); - } else { - return 0; - } + return this.runner.getRC(); } @SuppressWarnings("deprecation") Modified: trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -86,9 +86,9 @@ } try { final CommandIO[] ios = cmdLine.getStreams(); - if (ios[0] != CommandLine.DEFAULT_STDIN - || ios[1] != CommandLine.DEFAULT_STDOUT - || ios[2] != CommandLine.DEFAULT_STDERR) { + if (ios[0] != CommandLine.DEFAULT_STDIN || + ios[1] != CommandLine.DEFAULT_STDOUT || + ios[2] != CommandLine.DEFAULT_STDERR) { err.println("Warning: redirections ignored by the '" + getName() + "' invoker"); } Modified: trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -332,7 +332,6 @@ // squash } prevIOs[Command.STD_OUT] = CommandLine.DEVNULL; - prev.commandLine.setStreams(prevIOs); } } else { // the previous stage has explicitly redirected stdout Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneBuiltin.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneBuiltin.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneBuiltin.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -30,7 +30,7 @@ BjorneContext context) throws ShellException; void error(String msg, BjorneContext context) { - context.resolvePrintStream(context.getStream(Command.STD_ERR)).println(msg); + context.resolvePrintStream(context.getIO(Command.STD_ERR)).println(msg); } } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -49,15 +49,15 @@ import org.jnode.shell.Command; import org.jnode.shell.CommandLine; -import org.jnode.shell.CommandThread; +import org.jnode.shell.CommandShell; import org.jnode.shell.PathnamePattern; import org.jnode.shell.ShellException; import org.jnode.shell.ShellFailureException; import org.jnode.shell.ShellSyntaxException; import org.jnode.shell.io.CommandIO; -import org.jnode.shell.io.CommandIOMarker; import org.jnode.shell.io.CommandInput; import org.jnode.shell.io.CommandOutput; +import org.jnode.shell.io.CommandIOHolder; /** * This class holds the shell variable and stream state for a bjorne shell @@ -70,10 +70,6 @@ */ public class BjorneContext { - public static final CommandIOMarker PIPE_IN = new CommandIOMarker("PIPEIN"); - - public static final CommandIOMarker PIPE_OUT = new CommandIOMarker("PIPEOUT"); - private static final int NONE = 0; private static final int PREHASH = 1; @@ -122,13 +118,13 @@ private String options = ""; - private StreamHolder[] holders; + private CommandIOHolder[] holders; private boolean echoExpansions; private BjorneContext parent; - public BjorneContext(BjorneInterpreter interpreter, StreamHolder[] holders) { + public BjorneContext(BjorneInterpreter interpreter, CommandIOHolder[] holders) { this.interpreter = interpreter; this.holders = holders; this.variables = new HashMap<String, VariableSlot>(); @@ -138,12 +134,12 @@ this(interpreter, defaultStreamHolders()); } - private static StreamHolder[] defaultStreamHolders() { - StreamHolder[] res = new StreamHolder[4]; - res[Command.STD_IN] = new StreamHolder(CommandLine.DEFAULT_STDIN, false); - res[Command.STD_OUT] = new StreamHolder(CommandLine.DEFAULT_STDOUT, false); - res[Command.STD_ERR] = new StreamHolder(CommandLine.DEFAULT_STDERR, false); - res[Command.SHELL_ERR] = new StreamHolder(CommandLine.DEFAULT_STDERR, false); + private static CommandIOHolder[] defaultStreamHolders() { + CommandIOHolder[] res = new CommandIOHolder[4]; + res[Command.STD_IN] = new CommandIOHolder(CommandLine.DEFAULT_STDIN, false); + res[Command.STD_OUT] = new CommandIOHolder(CommandLine.DEFAULT_STDOUT, false); + res[Command.STD_ERR] = new CommandIOHolder(CommandLine.DEFAULT_STDERR, false); + res[Command.SHELL_ERR] = new CommandIOHolder(CommandLine.DEFAULT_STDERR, false); return res; } @@ -227,18 +223,22 @@ /** * Create a copy of some stream holders without passing ownership. */ - public static StreamHolder[] copyStreamHolders(StreamHolder[] holders) { - StreamHolder[] res = new StreamHolder[holders.length]; + public static CommandIOHolder[] copyStreamHolders(CommandIOHolder[] holders) { + CommandIOHolder[] res = new CommandIOHolder[holders.length]; for (int i = 0; i < res.length; i++) { - res[i] = new StreamHolder(holders[i]); + res[i] = new CommandIOHolder(holders[i]); } return res; } - StreamHolder[] getCopyOfHolders() { + CommandIOHolder[] getCopyOfHolders() { return copyStreamHolders(holders); } + CommandIOHolder[] getHolders() { + return holders; + } + void setArgs(String[] args) { this.args = Arrays.asList(args.clone()); } @@ -749,7 +749,7 @@ case QUERY: if (value == null) { String msg = word.length() > 0 ? word : (parameter + " is unset"); - resolvePrintStream(getStream(Command.STD_ERR)).println(msg); + resolvePrintStream(getIO(Command.STD_ERR)).println(msg); throw new BjorneControlException(BjorneInterpreter.BRANCH_EXIT, 1); } else { return value; @@ -757,7 +757,7 @@ case COLONQUERY: if (value == null || value.length() == 0) { String msg = word.length() > 0 ? word : (parameter + " is unset or null"); - resolvePrintStream(getStream(Command.STD_ERR)).println(msg); + resolvePrintStream(getIO(Command.STD_ERR)).println(msg); throw new BjorneControlException(BjorneInterpreter.BRANCH_EXIT, 1); } else { return value; @@ -925,30 +925,54 @@ return interpreter.resolveInputStream(stream); } - CommandIO getStream(int index) { + CommandIO getIO(int index) { if (index < 0) { throw new ShellFailureException("negative stream index"); } else if (index < holders.length) { - return holders[index].stream; + return holders[index].getIO(); } else { return null; } } - void setStream(int index, CommandIO stream, boolean mine) { + void setIO(int index, CommandIO io, boolean mine) { if (index < 0 || index >= holders.length) { - throw new ShellFailureException("negative stream index"); + throw new ShellFailureException("bad stream index"); } else { - holders[index].setStream(stream, mine); + holders[index].setIO(io, mine); } } - void closeStreams() { - for (StreamHolder holder : holders) { + void setIO(int index, CommandIOHolder holder) { + if (index < 0 || index >= holders.length) { + throw new ShellFailureException("bad stream index"); + } else { + holders[index].setIO(holder); + } + } + + void closeIOs() { + for (CommandIOHolder holder : holders) { holder.close(); } } + + void flushIOs() { + for (CommandIOHolder holder : holders) { + holder.flush(); + } + } + + CommandIO[] getIOs() { + CommandIO[] io = new CommandIO[holders.length]; + int i = 0; + for (CommandIOHolder holder : holders) { + io[i++] = (holder == null) ? null : holder.getIO(); + } + return io; + } + void performAssignments(BjorneToken[] assignments) throws ShellException { if (assignments != null) { for (int i = 0; i < assignments.length; i++) { @@ -972,8 +996,10 @@ * input/outputStreamTuple streams for this command. * @throws ShellException */ - StreamHolder[] evaluateRedirections(RedirectionNode[] redirects) throws ShellException { - return evaluateRedirections(redirects, copyStreamHolders(holders)); + CommandIOHolder[] evaluateRedirections(RedirectionNode[] redirects) throws ShellException { + CommandIOHolder[] res = copyStreamHolders(holders); + evaluateRedirections(redirects, res); + return res; } /** @@ -984,10 +1010,10 @@ * @return the stream state after redirections * @throws ShellException */ - StreamHolder[] evaluateRedirections( - RedirectionNode[] redirects, StreamHolder[] holders) throws ShellException { + void evaluateRedirections( + RedirectionNode[] redirects, CommandIOHolder[] holders) throws ShellException { if (redirects == null) { - return holders; + return; } boolean ok = false; try { @@ -1018,12 +1044,12 @@ } // If necessary, grow the fd table. if (fd >= holders.length) { - StreamHolder[] tmp = new StreamHolder[fd + 1]; + CommandIOHolder[] tmp = new CommandIOHolder[fd + 1]; System.arraycopy(holders, 0, tmp, 0, fd + 1); holders = tmp; } - StreamHolder stream; + CommandIOHolder stream; switch (redir.getRedirectionType()) { case REDIR_DLESS: throw new UnsupportedOperationException("<<"); @@ -1037,9 +1063,9 @@ throw new ShellException("File already exists"); } CommandOutput tmp = new CommandOutput(new FileOutputStream(file)); - stream = new StreamHolder(tmp, true); + stream = new CommandIOHolder(tmp, true); } catch (IOException ex) { - throw new ShellException("Cannot open input file", ex); + throw new ShellException("Cannot open output file", ex); } break; @@ -1048,9 +1074,9 @@ try { FileOutputStream tmp = new FileOutputStream(redir.getArg().getText(), redir.getRedirectionType() == REDIR_DGREAT); - stream = new StreamHolder(new CommandOutput(tmp), true); + stream = new CommandIOHolder(new CommandOutput(tmp), true); } catch (IOException ex) { - throw new ShellException("Cannot open input file", ex); + throw new ShellException("Cannot open output file", ex); } break; @@ -1058,7 +1084,7 @@ try { File file = new File(redir.getArg().getText()); CommandInput tmp = new CommandInput(new FileInputStream(file)); - stream = new StreamHolder(tmp, true); + stream = new CommandIOHolder(tmp, true); } catch (IOException ex) { throw new ShellException("Cannot open input file", ex); } @@ -1067,7 +1093,7 @@ case REDIR_LESSAND: try { int fromFd = Integer.parseInt(redir.getArg().getText()); - stream = (fromFd >= holders.length) ? null : new StreamHolder(holders[fromFd]); + stream = (fromFd >= holders.length) ? null : new CommandIOHolder(holders[fromFd]); } catch (NumberFormatException ex) { throw new ShellException("Invalid fd after >&"); } @@ -1076,7 +1102,7 @@ case REDIR_GREATAND: try { int fromFd = Integer.parseInt(redir.getArg().getText()); - stream = (fromFd >= holders.length) ? null : new StreamHolder(holders[fromFd]); + stream = (fromFd >= holders.length) ? null : new CommandIOHolder(holders[fromFd]); } catch (NumberFormatException ex) { throw new ShellException("Invalid fd after >&"); } @@ -1092,18 +1118,13 @@ ok = true; } finally { if (!ok) { - for (StreamHolder holder : holders) { + for (CommandIOHolder holder : holders) { holder.close(); } } } - return holders; } - public CommandThread fork(CommandLine command, CommandIO[] ios) throws ShellException { - return interpreter.fork(command, ios); - } - public boolean patternMatch(CharSequence text, CharSequence pat) { int flags = PathnamePattern.EAGER | PathnamePattern.DEFAULT_FLAGS; Pattern regex = PathnamePattern.compilePosixShellPattern(pat, flags); @@ -1117,4 +1138,13 @@ public int nosArgs() { return args.size(); } + + public CommandShell getShell() { + return interpreter.getShell(); + } + + public String getName() { + return interpreter.getUniqueName(); + } + } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -49,7 +49,6 @@ import org.jnode.shell.CommandInterpreter; import org.jnode.shell.CommandLine; import org.jnode.shell.CommandShell; -import org.jnode.shell.CommandThread; import org.jnode.shell.Completable; import org.jnode.shell.IncompleteCommandException; import org.jnode.shell.ShellException; @@ -137,6 +136,8 @@ new HashMap<String, BjorneBuiltin>(); private static boolean DEBUG = false; + + private static long subshellCount; static { BUILTINS.put("break", new BreakBuiltin()); @@ -235,7 +236,7 @@ myContext = this.context; } else { myContext = new BjorneContext(this); - myContext.setStream(1, new CommandOutput(capture), true); + myContext.setIO(1, new CommandOutput(capture), true); } BjorneTokenizer tokens = new BjorneTokenizer(command); CommandNode tree = new BjorneParser(tokens, "> ").parse(); @@ -363,11 +364,12 @@ public InputStream resolveInputStream(CommandIO stream) { return shell.resolveInputStream(stream); } + + private static synchronized long getSubshellNumber() { + return subshellCount++; + } - public CommandThread fork(CommandLine command, CommandIO[] streams) - throws ShellException { - command.setStreams(streams); - CommandInfo cmdInfo = command.parseCommandLine(shell); - return shell.invokeAsynchronous(command, cmdInfo); + public String getUniqueName() { + return getName() + "-" + getSubshellNumber(); } } Added: trunk/shell/src/shell/org/jnode/shell/bjorne/BjornePipeline.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjornePipeline.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjornePipeline.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -0,0 +1,224 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2009 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.bjorne; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.jnode.shell.Command; +import org.jnode.shell.CommandShell; +import org.jnode.shell.CommandThread; +import org.jnode.shell.ShellException; +import org.jnode.shell.ShellFailureException; +import org.jnode.shell.ThreadExitListener; +import org.jnode.shell.io.CommandIO; +import org.jnode.shell.io.CommandIOHolder; +import org.jnode.shell.io.CommandIOMarker; +import org.jnode.shell.io.CommandInput; +import org.jnode.shell.io.CommandOutput; +import org.jnode.shell.io.Pipeline; + +/** + * This class deals with construction and running of multi-command + * pipelines for the Bjorne shell. + * + * @author cr...@jn... + */ +class BjornePipeline { + private static class PipelineStage { + private String stageName; + private CommandNode command; + private BjorneContext context; + private CommandThread thread; + private CommandIOHolder[] holders; + private BjornePipeline nestedPipeline; + } + + private int stageCount = 0; + private int nextStage = 0; + private final PipelineStage[] stages; + private final BjornePipeline parent; + private final Map<String, Pipeline> pipes; + private int activeStageCount; + + + BjornePipeline(int len) { + this.stages = new PipelineStage[len]; + this.parent = null; + this.pipes = new HashMap<String, Pipeline>(); + } + + BjornePipeline(BjornePipeline parent, int len) { + this.stages = new PipelineStage[len]; + this.parent = parent; + this.pipes = null; + } + + void closeStreams() { + for (PipelineStage stage : stages) { + if (stage != null) { + for (CommandIOHolder holder : stage.holders) { + holder.close(); + } + if (stage.nestedPipeline != null) { + stage.nestedPipeline.closeStreams(); + } + } + } + } + + void wire() throws ShellException { + evaluateRedirections(); + createPipes(); + activatePipes(); + } + + private void evaluateRedirections() throws ShellException { + for (PipelineStage stage : stages) { + RedirectionNode[] redirects = stage.command.getRedirects(); + stage.context.evaluateRedirections(redirects, stage.holders); + } + } + + private void createPipes() throws ShellFailureException { + try { + for (PipelineStage stage : stages) { + for (CommandIOHolder holder : stage.holders) { + CommandIO io = holder.getIO(); + if (io instanceof CommandIOMarker) { + CommandIOMarker marker = (CommandIOMarker) io; + String name = marker.getName(); + + if (name.startsWith("PIPE-")) { + holder.setIO( + (marker.getDirection() == CommandIO.DIRECTION_OUT ? + getOutPipeIO(name) : getInPipeIO(name)), + true); + } + } + } + if (stage.nestedPipeline != null) { + stage.nestedPipeline.createPipes(); + } + } + } catch (IOException ex) { + throw new ShellFailureException("IO error while creating pipes."); + } + } + + private CommandIO getOutPipeIO(String name) throws IOException { + if (parent != null) { + return parent.getOutPipeIO(name); + } else { + Pipeline pipe = pipes.get(name); + if (pipe == null) { + pipe = new Pipeline(); + pipes.put(name, pipe); + } + return new CommandOutput(pipe.createSource()); + } + } + + private CommandIO getInPipeIO(String name) throws IOException { + if (parent != null) { + return parent.getInPipeIO(name); + } else { + Pipeline pipe = pipes.get(name); + if (pipe == null) { + pipe = new Pipeline(); + pipes.put(name, pipe); + } + return new CommandInput(pipe.createSink()); + } + } + + private void activatePipes() { + for (Pipeline pipe : pipes.values()) { + try { + pipe.activate(); + } catch (IOException ex) { + pipe.shutdown(); + } + } + } + + int run(CommandShell shell) throws ShellException { + for (PipelineStage stage : stages) { + stage.thread = stage.command.fork(shell, stage.context); + } + activeStageCount = stages.length; + synchronized (this) { + for (PipelineStage stage : stages) { + ThreadCallback callback = new ThreadCallback(stage.context); + stage.thread.start(callback); + } + while (activeStageCount > 0) { + try { + this.wait(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + break; + } + } + return stages[stages.length - 1].thread.getReturnCode(); + } + } + + void addStage(CommandNode commandNode, BjorneContext context) throws ShellException { + int i = nextStage++; + PipelineStage stage = stages[i] = new PipelineStage(); + stage.stageName = stageName(); + stage.command = commandNode; + stage.holders = context.getHolders(); + if (i > 0) { + String pipeName = "PIPE-" + stages[i - 1].stageName; + stages[i - 1].holders[Command.STD_OUT].setIO( + new CommandIOMarker(pipeName, CommandIO.DIRECTION_OUT), true); + stage.holders[Command.STD_IN].setIO( + new CommandIOMarker(pipeName, CommandIO.DIRECTION_IN), true); + } + stage.context = context; + stage.nestedPipeline = commandNode.buildPipeline(context); + context.evaluateRedirections(commandNode.getRedirects(), stage.holders); + } + + private String stageName() { + return (parent != null) ? parent.stageName() : ("STAGE-" + ++stageCount); + } + + private class ThreadCallback implements ThreadExitListener { + private BjorneContext context; + + public ThreadCallback(BjorneContext context) { + this.context = context; + } + + public void notifyThreadExited(CommandThread thread) { + synchronized (BjornePipeline.this) { + context.closeIOs(); + activeStageCount--; + if (activeStageCount <= 0) { + BjornePipeline.this.notify(); + } + } + } + } +} Added: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneSubshellRunner.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneSubshellRunner.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneSubshellRunner.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -0,0 +1,35 @@ +package org.jnode.shell.bjorne; + +import org.jnode.shell.CommandRunnable; +import org.jnode.shell.ShellException; + +public abstract class BjorneSubshellRunner implements CommandRunnable { + private int rc; + private final BjorneContext context; + + public BjorneSubshellRunner(BjorneContext context) { + super(); + this.context = context; + } + + @Override + public void flushStreams() { + context.flushIOs(); + } + + @Override + public int getRC() { + return rc; + } + + public final void run() { + try { + rc = doRun(); + } catch (ShellException ex) { + // FIXME ... this isn't right ... + rc = 1; + } + } + + protected abstract int doRun() throws ShellException; +} Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/CaseCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/CaseCommandNode.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/CaseCommandNode.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -20,6 +20,10 @@ package org.jnode.shell.bjorne; +import org.jnode.shell.CommandRunnable; +import org.jnode.shell.CommandShell; +import org.jnode.shell.CommandThread; +import org.jnode.shell.CommandThreadImpl; import org.jnode.shell.ShellException; public class CaseCommandNode extends CommandNode { @@ -75,4 +79,16 @@ } return rc; } + + @Override + public CommandThread fork(CommandShell shell, final BjorneContext context) + throws ShellException { + + CommandRunnable cr = new BjorneSubshellRunner(context) { + @Override + public int doRun() throws ShellException { + return CaseCommandNode.this.execute(context); + }}; + return new CommandThreadImpl(cr, context.getName()); + } } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/ColonBuiltin.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/ColonBuiltin.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/ColonBuiltin.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -30,7 +30,6 @@ * @author cr...@jn... */ final class ColonBuiltin extends BjorneBuiltin { - @SuppressWarnings("deprecation") public int invoke(CommandLine command, BjorneInterpreter interpreter, BjorneContext context) throws ShellException { return 0; Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/CommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/CommandNode.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/CommandNode.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -20,6 +20,8 @@ package org.jnode.shell.bjorne; +import org.jnode.shell.CommandShell; +import org.jnode.shell.CommandThread; import org.jnode.shell.ShellException; public abstract class CommandNode { @@ -59,6 +61,9 @@ public abstract int execute(BjorneContext context) throws ShellException; + public abstract CommandThread fork(CommandShell shell, BjorneContext context) + throws ShellException; + public String toString() { StringBuffer sb = new StringBuffer(); sb.append("nodeType=").append(nodeType); @@ -82,4 +87,9 @@ } sb.append(']'); } + + public BjornePipeline buildPipeline(BjorneContext context) throws ShellException { + return null; + } + } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/ForCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/ForCommandNode.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/ForCommandNode.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -20,6 +20,10 @@ package org.jnode.shell.bjorne; +import org.jnode.shell.CommandRunnable; +import org.jnode.shell.CommandShell; +import org.jnode.shell.CommandThread; +import org.jnode.shell.CommandThreadImpl; import org.jnode.shell.ShellException; public class ForCommandNode extends CommandNode { @@ -70,4 +74,16 @@ } return rc; } + + @Override + public CommandThread fork(CommandShell shell, final BjorneContext context) + throws ShellException { + + CommandRunnable cr = new BjorneSubshellRunner(context) { + @Override + public int doRun() throws ShellException { + return ForCommandNode.this.execute(context); + }}; + return new CommandThreadImpl(cr, context.getName()); + } } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/FunctionDefinitionNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/FunctionDefinitionNode.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/FunctionDefinitionNode.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -20,6 +20,12 @@ package org.jnode.shell.bjorne; +import org.jnode.shell.CommandRunnable; +import org.jnode.shell.CommandShell; +import org.jnode.shell.CommandThread; +import org.jnode.shell.CommandThreadImpl; +import org.jnode.shell.ShellException; + public class FunctionDefinitionNode extends CommandNode { private final BjorneToken name; @@ -52,4 +58,18 @@ public int execute(BjorneContext context) { return -1; } + + @Override + public CommandThread fork(CommandShell shell, final BjorneContext context) + throws ShellException { + + CommandRunnable cr = new BjorneSubshellRunner(context) { + @Override + public int doRun() throws ShellException { + return FunctionDefinitionNode.this.execute(context); + }}; + return new CommandThreadImpl(cr, context.getName()); + } + + } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/IfCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/IfCommandNode.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/IfCommandNode.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -20,6 +20,10 @@ package org.jnode.shell.bjorne; +import org.jnode.shell.CommandRunnable; +import org.jnode.shell.CommandShell; +import org.jnode.shell.CommandThread; +import org.jnode.shell.CommandThreadImpl; import org.jnode.shell.ShellException; public class IfCommandNode extends CommandNode { @@ -81,4 +85,18 @@ } return rc; } + + @Override + public CommandThread fork(CommandShell shell, final BjorneContext context) + throws ShellException { + + CommandRunnable cr = new BjorneSubshellRunner(context) { + @Override + public int doRun() throws ShellException { + return IfCommandNode.this.execute(context); + }}; + return new CommandThreadImpl(cr, context.getName()); + } + + } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/ListCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/ListCommandNode.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/ListCommandNode.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -20,34 +20,20 @@ package org.jnode.shell.bjorne; -import java.io.IOException; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; import java.util.List; import org.jnode.driver.console.CompletionInfo; -import org.jnode.shell.CommandLine; +import org.jnode.shell.CommandRunnable; import org.jnode.shell.CommandShell; import org.jnode.shell.CommandThread; +import org.jnode.shell.CommandThreadImpl; import org.jnode.shell.Completable; import org.jnode.shell.ShellException; -import org.jnode.shell.ShellFailureException; -import org.jnode.shell.ThreadExitListener; import org.jnode.shell.help.CompletionException; -import org.jnode.shell.io.CommandIO; -import org.jnode.shell.io.CommandInput; -import org.jnode.shell.io.CommandOutput; -import org.jnode.shell.io.NullInputStream; -import org.jnode.shell.io.NullOutputStream; +import org.jnode.shell.io.CommandIOHolder; public class ListCommandNode extends CommandNode implements Completable { - private static class PipelineStage { - private CommandLine command; - private BjorneContext context; - private CommandThread thread; - private StreamHolder[] holders; - } private final CommandNode[] commands; @@ -74,33 +60,24 @@ int listFlags = getFlags(); int rc = 0; try { - if (getNodeType() == BjorneInterpreter.CMD_SUBSHELL) { - // This simulates creating a 'subshell'. - context = new BjorneContext(context); - StreamHolder[] holders = context.evaluateRedirections(getRedirects()); - for (int i = 0; i < holders.length; i++) { - CommandIO stream = holders[i].getStream(); - context.setStream(i, stream, holders[i].isMine()); - } - } if ((listFlags & BjorneInterpreter.FLAG_PIPE) != 0) { - PipelineStage[] stages = assemblePipeline(context); - boolean done = false; + BjornePipeline pipeline = buildPipeline(context); try { - rc = runPipeline(stages); - done = true; + pipeline.wire(); + rc = pipeline.run(context.getShell()); } finally { - if (!done) { - // If we are propagating an exception, all streams that - // were opened by 'assemblePipeline' must be closed. - for (PipelineStage stage : stages) { - for (StreamHolder holder : stage.holders) { - holder.close(); - } - } - } + pipeline.closeStreams(); } } else { + if (getNodeType() == BjorneInterpreter.CMD_SUBSHELL) { + // This simulates creating a 'subshell'. + context = new BjorneContext(context); + CommandIOHolder[] holders = context.evaluateRedirections(getRedirects()); + for (int i = 0; i < holders.length; i++) { + context.setIO(i, holders[i]); + } + } + for (CommandNode command : commands) { int commandFlags = command.getFlags(); if ((commandFlags & BjorneInterpreter.FLAG_AND_IF) != 0) { @@ -118,7 +95,7 @@ } } finally { if (getNodeType() == BjorneInterpreter.CMD_SUBSHELL) { - context.closeStreams(); + context.closeIOs(); } } if ((listFlags & BjorneInterpreter.FLAG_BANG) != 0) { @@ -127,119 +104,29 @@ return rc; } - private class ThreadCallback implements ThreadExitListener { - private PipelineStage[] stages; - private int count; + @Override + public CommandThread fork(CommandShell shell, final BjorneContext context) + throws ShellException { - ThreadCallback(PipelineStage[] stages) { - this.stages = stages; - this.count = stages.length; - } - - public void notifyThreadExited(CommandThread thread) { - synchronized (stages) { - for (PipelineStage stage : stages) { - if (stage.thread == thread) { - for (StreamHolder holder : stage.holders) { - holder.close(); - } - break; - } - } - count--; - if (count <= 0) { - stages.notify(); - } - } - } + CommandRunnable cr = new BjorneSubshellRunner(context) { + @Override + public int doRun() throws ShellException { + return ListCommandNode.this.execute(context); + }}; + return new CommandThreadImpl(cr, context.getName()); } - - private int runPipeline(final PipelineStage[] stages) throws ShellException { - for (PipelineStage stage : stages) { - CommandIO[] streams = new CommandIO[stage.holders.length]; - for (int i = 0; i < streams.length; i++) { - streams[i] = stage.holders[i].getStream(); - } - stage.thread = stage.context.fork(stage.command, streams); - } - synchronized (stages) { - ThreadCallback callback = new ThreadCallback(stages); - for (PipelineStage stage : stages) { - stage.thread.start(callback); - } - while (callback.count > 0) { - try { - stages.wait(); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - break; - } - } - return stages[stages.length - 1].thread.getReturnCode(); - } - } - private PipelineStage[] assemblePipeline(BjorneContext context) throws ShellException { + public BjornePipeline buildPipeline(BjorneContext context) throws ShellException { int len = commands.length; - final StreamHolder pipeInMarker = new StreamHolder(BjorneContext.PIPE_IN, false); - final StreamHolder pipeOutMarker = new StreamHolder(BjorneContext.PIPE_OUT, false); - PipelineStage[] stages = new PipelineStage[len]; + BjornePipeline pipeline = new BjornePipeline(len); for (int i = 0; i < len; i++) { - SimpleCommandNode commandNode = (SimpleCommandNode) commands[i]; - PipelineStage stage = stages[i] = new PipelineStage(); - stage.context = new BjorneContext(context); - stage.context.performAssignments(commandNode.getAssignments()); - stage.command = stage.context.expandAndSplit(commandNode.getWords()); - stage.holders = context.getCopyOfHolders(); - if (i < len - 1) { - stage.holders[1] = pipeOutMarker; - } - if (i > 0) { - stage.holders[0] = pipeInMarker; - } - stage.context.evaluateRedirections(commandNode.getRedirects(), stage.holders); + CommandNode commandNode = commands[i]; + BjorneContext childContext = new BjorneContext(context); + pipeline.addStage(commandNode, childContext); } - for (int i = 0; i < len - 1; i++) { - StreamHolder newIn = null, newOut = null; - PipelineStage thisStage = stages[i]; - PipelineStage nextStage = stages[i + 1]; - if (thisStage.holders[1] == pipeOutMarker) { - if (nextStage.holders[0] == pipeInMarker) { - PipedOutputStream pipeOut = new PipedOutputStream(); - PipedInputStream pipeIn = new PipedInputStream(); - try { - pipeIn.connect(pipeOut); - } catch (IOException ex) { - throw new ShellFailureException("plumbing failure", ex); - } - newIn = new StreamHolder(new CommandInput(pipeIn), true); - newOut = new StreamHolder(new CommandOutput(pipeOut), true); - } else { - newOut = new StreamHolder(new CommandOutput(new NullOutputStream()), true); - } - } else { - if (nextStage.holders[0] == pipeInMarker) { - newIn = new StreamHolder(new CommandInput(new NullInputStream()), true); - } - } - if (newOut != null) { - for (int j = 0; j < thisStage.holders.length; j++) { - if (thisStage.holders[j] == pipeOutMarker) { - thisStage.holders[j] = newOut; - } - } - } - if (newIn != null) { - for (int j = 0; j < nextStage.holders.length; j++) { - if (nextStage.holders[j] == pipeInMarker) { - nextStage.holders[j] = newIn; - } - } - } - } - return stages; + return pipeline; } - + @Override public void complete(CompletionInfo completion, CommandShell shell) throws CompletionException { // TODO Auto-generated method stub Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/LoopCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/LoopCommandNode.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/LoopCommandNode.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -20,6 +20,10 @@ package org.jnode.shell.bjorne; +import org.jnode.shell.CommandRunnable; +import org.jnode.shell.CommandShell; +import org.jnode.shell.CommandThread; +import org.jnode.shell.CommandThreadImpl; import org.jnode.shell.ShellException; public class LoopCommandNode extends CommandNode { @@ -85,4 +89,16 @@ } return rc; } + + @Override + public CommandThread fork(CommandShell shell, final BjorneContext context) + throws ShellException { + + CommandRunnable cr = new BjorneSubshellRunner(context) { + @Override + public int doRun() throws ShellException { + return LoopCommandNode.this.execute(context); + }}; + return new CommandThreadImpl(cr, context.getName()); + } } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -21,12 +21,15 @@ package org.jnode.shell.bjorne; import org.jnode.driver.console.CompletionInfo; +import org.jnode.shell.CommandInfo; import org.jnode.shell.CommandLine; import org.jnode.shell.CommandShell; +import org.jnode.shell.CommandThread; import org.jnode.shell.ShellException; import org.jnode.shell.ShellFailureException; import org.jnode.shell.help.CompletionException; import org.jnode.shell.io.CommandIO; +import org.jnode.shell.io.CommandIOHolder; public class SimpleCommandNode extends CommandNode implements BjorneCompletable { @@ -68,7 +71,7 @@ @Override public int execute(final BjorneContext context) throws ShellException { - StreamHolder[] holders = null; + CommandIOHolder[] holders = null; int rc; try { BjorneToken[] words = getWords(); @@ -85,20 +88,20 @@ BjorneContext childContext = new BjorneContext(context); childContext.performAssignments(assignments); holders = childContext.evaluateRedirections(getRedirects()); - CommandIO[] streams = new CommandIO[holders.length]; - for (int i = 0; i < streams.length; i++) { - streams[i] = holders[i].getStream(); + CommandIO[] ios = new CommandIO[holders.length]; + for (int i = 0; i < ios.length; i++) { + ios[i] = holders[i].getIO(); } if ((getFlags() & BjorneInterpreter.FLAG_ASYNC) != 0) { throw new ShellFailureException( "asynchronous execution (&) not implemented yet"); } else { - rc = childContext.execute(command, streams); + rc = childContext.execute(command, ios); } } } finally { if (holders != null) { - for (StreamHolder holder : holders) { + for (CommandIOHolder holder : holders) { holder.close(); } } @@ -109,6 +112,14 @@ context.setLastReturnCode(rc); return rc; } + + public CommandThread fork(CommandShell shell, BjorneContext context) + throws ShellException { + CommandLine command = context.expandAndSplit(getWords()); + command.setStreams(context.getIOs()); + CommandInfo cmdInfo = command.parseCommandLine(shell); + return shell.invokeAsynchronous(command, cmdInfo); + } @Override public void complete(CompletionInfo completion, BjorneContext context, CommandShell shell) Deleted: trunk/shell/src/shell/org/jnode/shell/bjorne/StreamHolder.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/StreamHolder.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/StreamHolder.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -1,65 +0,0 @@ -/* - * $Id$ - * - * Copyright (C) 2003-2009 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.bjorne; - -import java.io.IOException; - -import org.jnode.shell.io.CommandIO; - -public class StreamHolder { - CommandIO stream; - private boolean isMine; - - public StreamHolder(CommandIO stream, boolean isMine) { - this.stream = stream; - this.isMine = isMine; - } - - public StreamHolder(StreamHolder other) { - this.stream = other.stream; - this.isMine = false; - } - - public CommandIO getStream() { - return stream; - } - - public void setStream(CommandIO stream, boolean isMine) { - close(); - this.stream = stream; - this.isMine = isMine; - } - - public void close() { - if (isMine) { - try { - isMine = false; // just in case we call close twice - stream.close(); - } catch (IOException ex) { - // FIXME - should we squash or report this? - } - } - } - - public boolean isMine() { - return isMine; - } -} Modified: trunk/shell/src/shell/org/jnode/shell/io/BaseCommandIO.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/BaseCommandIO.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/io/BaseCommandIO.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -78,10 +78,25 @@ } } - public abstract void close() throws IOException; + public final boolean isPipe() { + if (systemObject == null) { + return false; + } else { + return IOUtils.isPipe(systemObject); + } + } - public void flush() throws IOException { + public synchronized final void close() throws IOException { + doClose(); } + + abstract void doClose() throws IOException; + + public synchronized final void flush() throws IOException { + doFlush(); + } + + abstract void doFlush() throws IOException; @Override public final PrintStream getPrintStream() throws CommandIOException { Modified: trunk/shell/src/shell/org/jnode/shell/io/CommandIO.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/CommandIO.java 2009-03-15 05:55:22 UTC (rev 5107) +++ trunk/shell/src/shell/org/jnode/shell/io/CommandIO.java 2009-03-15 05:59:57 UTC (rev 5108) @@ -129,6 +129,13 @@ * @return <code>true</code> if the associated stream is interactive. */ public boolean isTTY(); + + /** + * Query if this CommandIO is associated with a pipe. + * + * @return <code>true</code> if the associated stream is a pipe. + */ + public boolean isPipe(); /** * Obtain the 'base' stream object for this CommandIO. This will be @@ -155,4 +162,5 @@ * @throws IOException */ public void flush() throws IOException; + } Added: trunk/shell/src/shell/org/jnode/shell/io/CommandIOHolder.java ==========================================... [truncated message content] |
From: <cr...@us...> - 2009-03-16 14:17:38
|
Revision: 5110 http://jnode.svn.sourceforge.net/jnode/?rev=5110&view=rev Author: crawley Date: 2009-03-16 14:17:28 +0000 (Mon, 16 Mar 2009) Log Message: ----------- Fix for bug that caused bjorne to try to open '>' files twice. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjornePipeline.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjornePipeline.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjornePipeline.java 2009-03-15 14:34:32 UTC (rev 5109) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjornePipeline.java 2009-03-16 14:17:28 UTC (rev 5110) @@ -86,18 +86,10 @@ } void wire() throws ShellException { - evaluateRedirections(); createPipes(); activatePipes(); } - private void evaluateRedirections() throws ShellException { - for (PipelineStage stage : stages) { - RedirectionNode[] redirects = stage.command.getRedirects(); - stage.context.evaluateRedirections(redirects, stage.holders); - } - } - private void createPipes() throws ShellFailureException { try { for (PipelineStage stage : stages) { Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-03-15 14:34:32 UTC (rev 5109) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-03-16 14:17:28 UTC (rev 5110) @@ -703,5 +703,13 @@ Hi mum again </file> </testSpec> + <testSpec title="subshell 4" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + ( echo Hi mum ; echo Hi mum again ) | cat > @TEMP_DIR@/1 + </script> + <file name="1" input="false">Hi mum +Hi mum again +</file> + </testSpec> </testSet> \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-03-20 13:26:48
|
Revision: 5126 http://jnode.svn.sourceforge.net/jnode/?rev=5126&view=rev Author: crawley Date: 2009-03-20 13:26:45 +0000 (Fri, 20 Mar 2009) Log Message: ----------- Implementation of HERE documents ... expansion done yet. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java trunk/shell/src/shell/org/jnode/shell/bjorne/RedirectionNode.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-20 12:58:04 UTC (rev 5125) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-20 13:26:45 UTC (rev 5126) @@ -36,6 +36,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; @@ -1050,11 +1051,15 @@ } CommandIOHolder stream; + CommandInput in; + CommandOutput out; switch (redir.getRedirectionType()) { case REDIR_DLESS: - throw new UnsupportedOperationException("<<"); case REDIR_DLESSDASH: - throw new UnsupportedOperationException("<<-"); + // FIXME do expansion + in = new CommandInput(new StringReader(redir.getHereDocument())); + stream = new CommandIOHolder(in, true); + break; case REDIR_GREAT: try { @@ -1062,8 +1067,8 @@ if (isNoClobber() && file.exists()) { throw new ShellException("File already exists"); } - CommandOutput tmp = new CommandOutput(new FileOutputStream(file)); - stream = new CommandIOHolder(tmp, true); + out = new CommandOutput(new FileOutputStream(file)); + stream = new CommandIOHolder(out, true); } catch (IOException ex) { throw new ShellException("Cannot open output file", ex); } @@ -1083,8 +1088,8 @@ case REDIR_LESS: try { File file = new File(redir.getArg().getText()); - CommandInput tmp = new CommandInput(new FileInputStream(file)); - stream = new CommandIOHolder(tmp, true); + in = new CommandInput(new FileInputStream(file)); + stream = new CommandIOHolder(in, true); } catch (IOException ex) { throw new ShellException("Cannot open input file", ex); } @@ -1093,16 +1098,18 @@ case REDIR_LESSAND: try { int fromFd = Integer.parseInt(redir.getArg().getText()); - stream = (fromFd >= holders.length) ? null : new CommandIOHolder(holders[fromFd]); + stream = (fromFd >= holders.length) ? null : + new CommandIOHolder(holders[fromFd]); } catch (NumberFormatException ex) { - throw new ShellException("Invalid fd after >&"); + throw new ShellException("Invalid fd after <&"); } break; case REDIR_GREATAND: try { int fromFd = Integer.parseInt(redir.getArg().getText()); - stream = (fromFd >= holders.length) ? null : new CommandIOHolder(holders[fromFd]); + stream = (fromFd >= holders.length) ? null : + new CommandIOHolder(holders[fromFd]); } catch (NumberFormatException ex) { throw new ShellException("Invalid fd after >&"); } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-03-20 12:58:04 UTC (rev 5125) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-03-20 13:26:45 UTC (rev 5126) @@ -79,6 +79,7 @@ import static org.jnode.shell.bjorne.BjorneToken.TOK_WHILE; import static org.jnode.shell.bjorne.BjorneToken.TOK_WORD; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -90,6 +91,9 @@ private final BjorneTokenizer tokens; private final String continuationPrompt; + + private final List<RedirectionNode> hereRedirections = + new ArrayList<RedirectionNode>(); public BjorneParser(BjorneTokenizer tokens, String continuationPrompt) { this.tokens = tokens; @@ -103,13 +107,14 @@ * @throws ShellSyntaxException */ public CommandNode parse() throws ShellSyntaxException { + hereRedirections.clear(); List<CommandNode> commands = new LinkedList<CommandNode>(); while (tokens.peek().getTokenType() != TOK_END_OF_STREAM) { CommandNode command = parseList(); commands.add(command); - tokens.next(); - skipLineBreaks(); + processLineBreaks(); } + captureHereDocuments(); return listToNode(commands); } @@ -172,13 +177,13 @@ } commands.add(command); tokens.next(); - skipLineBreaks(); + processLineBreaks(); } return listToNode(commands); } private CommandNode parseOptAndOr() throws ShellSyntaxException { - skipLineBreaks(); + processLineBreaks(); switch (tokens.peek(RULE_1_CONTEXT).getTokenType()) { case TOK_LBRACE: case TOK_LPAREN: @@ -230,7 +235,7 @@ commands.add(parseCommand()); while (tokens.peek().getTokenType() == TOK_BAR) { tokens.next(); - skipLineBreaks(); + processLineBreaks(); commands.add(parseCommand()); } boolean pipe = commands.size() > 1; @@ -359,7 +364,7 @@ if (tt != TOK_RPAREN) { syntaxError("expected matching ')' in function_definition", tt); } - skipLineBreaks(); + processLineBreaks(); return new FunctionDefinitionNode(fname, parseFunctionBody()); } @@ -432,7 +437,9 @@ if (tt2 != TOK_WORD) { syntaxError("expected a filename after " + token, tt2); } - break; + // (The corresponding token type and redirection type values are the + // same ...) + return new RedirectionNode(tt, io, arg); case TOK_DLESS: case TOK_DLESSDASH: arg = tokens.next(); @@ -440,14 +447,14 @@ if (tt3 != TOK_WORD) { syntaxError("expected a here-end marker " + token, tt3); } - // TODO ... need to grab the HERE document ... - break; + RedirectionNode res = new RedirectionNode(tt, io, arg); + // (The HERE document will be captured when we reach the next + // real (i.e. not '\' escaped) line break ... see processLineBreaks()) + hereRedirections.add(res); + return res; default: throw new ShellSyntaxException("expected a redirection token"); } - // (The corresponding token type and redirection type values are the - // same ...) - return new RedirectionNode(tt, io, arg); } private RedirectionNode[] parseOptRedirects() throws ShellSyntaxException { @@ -490,23 +497,25 @@ private CommandNode parseCompoundList() throws ShellSyntaxException { List<CommandNode> commands = new LinkedList<CommandNode>(); - skipLineBreaks(); + processLineBreaks(); CommandNode command = parseAndOr(); LOOP: while (command != null) { commands.add(command); switch (tokens.peek().getTokenType()) { case TOK_SEMI: + tokens.next(); break; case TOK_END_OF_LINE: break; case TOK_AMP: command.setFlag(FLAG_ASYNC); + tokens.next(); break; default: break LOOP; } - tokens.next(); + processLineBreaks(); command = parseOptAndOr(); } return listToNode(commands); @@ -527,21 +536,21 @@ tokens.next(); BjorneToken word = tokens.next(); List<CaseItemNode> caseItems = new LinkedList<CaseItemNode>(); - skipLineBreaks(); + processLineBreaks(); int tt = tokens.next(RULE_6_CONTEXT).getTokenType(); if (tt != TOK_IN) { syntaxError("expected 'in' in case_clause", tt); } - skipLineBreaks(); + processLineBreaks(); BjorneToken token = tokens.peek(RULE_1_CONTEXT); while (token.getTokenType() != TOK_ESAC) { caseItems.add(parseCaseItem()); - skipLineBreaks(); + processLineBreaks(); token = tokens.peek(RULE_1_CONTEXT); tt = token.getTokenType(); if (tt == TOK_DSEMI) { tokens.next(); - skipLineBreaks(); + processLineBreaks(); token = tokens.peek(RULE_1_CONTEXT); } else if (tt != TOK_ESAC) { syntaxError("expected ';;' or 'esac' after case_item", tt); @@ -566,12 +575,12 @@ syntaxError("expected ')' after pattern in case_item", tt); } CommandNode body = null; - skipLineBreaks(); + processLineBreaks(); token = tokens.peek(RULE_1_CONTEXT); if (token.getTokenType() != TOK_DSEMI && token.getTokenType() != TOK_ESAC) { body = parseCompoundList(); - skipLineBreaks(); + processLineBreaks(); } return new CaseItemNode(pattern, body); @@ -601,7 +610,7 @@ if (tt != TOK_NAME) { syntaxError("expected a NAME following 'for'", tt); } - skipLineBreaks(); + processLineBreaks(); List<BjorneToken> words = new LinkedList<BjorneToken>(); if (tokens.peek(RULE_6_CONTEXT).getTokenType() == TOK_IN) { tokens.next(); @@ -618,10 +627,10 @@ switch (tt) { case TOK_SEMI: tokens.next(); - skipLineBreaks(); + processLineBreaks(); break; case TOK_END_OF_LINE: - skipLineBreaks(); + processLineBreaks(); break; default: syntaxError("expected a ';' following wordlist", tt); @@ -632,13 +641,13 @@ } private CommandNode parseDoGroup() throws ShellSyntaxException { - skipLineBreaks(); + processLineBreaks(); int tt = tokens.next(RULE_1_CONTEXT).getTokenType(); if (tt != TOK_DO) { syntaxError("expected the 'do' of a do_group", tt); } CommandNode body = parseCompoundList(); - skipLineBreaks(); + processLineBreaks(); tt = tokens.next(RULE_1_CONTEXT).getTokenType(); if (tt != TOK_DONE) { syntaxError("expected a command or 'done'", tt); @@ -663,14 +672,14 @@ private IfCommandNode parseIfCommand() throws ShellSyntaxException { tokens.next(); CommandNode cond = parseCompoundList(); - skipLineBreaks(); + processLineBreaks(); int tt = tokens.next(RULE_1_CONTEXT).getTokenType(); if (tt != TOK_THEN) { syntaxError("expected a 'then' in if_clause", tt); } CommandNode thenPart = parseCompoundList(); CommandNode elsePart = parseOptElsePart(); - skipLineBreaks(); + processLineBreaks(); tt = tokens.next(RULE_1_CONTEXT).getTokenType(); if (tt != TOK_FI) { syntaxError("expected an 'elif', 'else' or 'fi'", tt); @@ -679,11 +688,11 @@ } private CommandNode parseOptElsePart() throws ShellSyntaxException { - skipLineBreaks(); + processLineBreaks(); switch (tokens.next(RULE_1_CONTEXT).getTokenType()) { case TOK_ELIF: CommandNode cond = parseCompoundList(); - skipLineBreaks(); + processLineBreaks(); int tt = tokens.next(RULE_1_CONTEXT).getTokenType(); if (tt != TOK_THEN) { syntaxError("expected a 'then' in else_part", tt); @@ -707,12 +716,37 @@ } } - private void skipLineBreaks() { - while (tokens.peek().getTokenType() == TOK_END_OF_LINE) { + private void processLineBreaks() throws IncompleteCommandException { + if (tokens.peek().getTokenType() == TOK_END_OF_LINE) { tokens.next(); + captureHereDocuments(); + while (tokens.peek().getTokenType() == TOK_END_OF_LINE) { + tokens.next(); + } } } + private void captureHereDocuments() throws IncompleteCommandException { + for (RedirectionNode redirection : hereRedirections) { + StringBuilder sb = new StringBuilder(); + String marker = redirection.getArg().getText(); + boolean trimTabs = redirection.getRedirectionType() == TOK_DLESSDASH; + while (true) { + String line = tokens.readHereLine(trimTabs); + if (line == null) { + throw new IncompleteCommandException("EOF reached while looking for '" + + marker + "' to end a HERE document", continuationPrompt); + } + if (line.equals(marker)) { + break; + } + sb.append(line).append('\n'); + } + redirection.setHereDocument(sb.toString()); + } + hereRedirections.clear(); + } + private CommandNode listToNode(List<? extends CommandNode> commands) { int len = commands.size(); if (len == 0) { Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java 2009-03-20 12:58:04 UTC (rev 5125) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java 2009-03-20 13:26:45 UTC (rev 5126) @@ -113,6 +113,9 @@ * @throws IncompleteCommandException */ private char[] foldContinuations(String text) throws IncompleteCommandException { + // FIXME this is wrong ... if we are going to imitate the documented behaviour + // of bash. (In bash, if the the MARKER is quoted, '\<newline>' is apparently + // not interpreted as a continuation.) if (text.indexOf('\\') == -1) { return text.toCharArray(); } @@ -248,6 +251,26 @@ public void remove() { throw new UnsupportedOperationException("remove not supported"); } + + public String readHereLine(boolean trimTabs) { + StringBuilder sb = new StringBuilder(40); + while (true) { + int ch = nextCh(); + switch (ch) { + case '\n': + return sb.toString(); + case EOS: + return (sb.length() > 0) ? sb.toString() : null; + case '\t': + if (!trimTabs || sb.length() > 0) { + sb.append('\t'); + } + break; + default: + sb.append((char) ch); + } + } + } /** * Parse and return the next token Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/RedirectionNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/RedirectionNode.java 2009-03-20 12:58:04 UTC (rev 5125) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/RedirectionNode.java 2009-03-20 13:26:45 UTC (rev 5126) @@ -21,11 +21,13 @@ package org.jnode.shell.bjorne; public class RedirectionNode { - public final int redirectionType; + private final int redirectionType; - public final BjorneToken io; + private final BjorneToken io; - public final BjorneToken arg; + private final BjorneToken arg; + + private String hereDocument; public RedirectionNode(final int redirectionType, BjorneToken io, BjorneToken arg) { @@ -47,6 +49,14 @@ return redirectionType; } + public void setHereDocument(String hereDocument) { + this.hereDocument = hereDocument; + } + + public String getHereDocument() { + return hereDocument; + } + public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Redirect{"); Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-03-20 12:58:04 UTC (rev 5125) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-03-20 13:26:45 UTC (rev 5126) @@ -628,6 +628,16 @@ </output> <error>Hello mother again </error> + </testSpec><testSpec title="here" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + cat <<EOF > @TEMP_DIR@/1 +Hi mum +Hi mum again +EOF + </script> + <file name="1" input="false">Hi mum +Hi mum again +</file> </testSpec> <testSpec title="pipeline" command="test" runMode="AS_SCRIPT" rc="0"> <script>#!bjorne This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-03-21 01:13:55
|
Revision: 5134 http://jnode.svn.sourceforge.net/jnode/?rev=5134&view=rev Author: crawley Date: 2009-03-21 01:13:41 +0000 (Sat, 21 Mar 2009) Log Message: ----------- Implemented expansion of HERE documents. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java trunk/shell/src/shell/org/jnode/shell/bjorne/RedirectionNode.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-20 20:42:48 UTC (rev 5133) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-21 01:13:41 UTC (rev 5134) @@ -1056,8 +1056,11 @@ switch (redir.getRedirectionType()) { case REDIR_DLESS: case REDIR_DLESSDASH: - // FIXME do expansion - in = new CommandInput(new StringReader(redir.getHereDocument())); + String here = redir.getHereDocument(); + if (redir.isHereDocumentExpandable()) { + here = expand(here).toString(); + } + in = new CommandInput(new StringReader(here)); stream = new CommandIOHolder(in, true); break; Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java 2009-03-20 20:42:48 UTC (rev 5133) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java 2009-03-21 01:13:41 UTC (rev 5134) @@ -252,6 +252,14 @@ throw new UnsupportedOperationException("remove not supported"); } + /** + * This method bypasses normal tokenization and reads a raw line of + * text up to the next NL (or the end of stream). + * + * @param trimTabs if {@code true}, trim any leading TABs from the line + * @return the line read without the terminating NL. If we got an + * end of stream immediately, return {@code null}. + */ public String readHereLine(boolean trimTabs) { StringBuilder sb = new StringBuilder(40); while (true) { Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/RedirectionNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/RedirectionNode.java 2009-03-20 20:42:48 UTC (rev 5133) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/RedirectionNode.java 2009-03-21 01:13:41 UTC (rev 5134) @@ -26,8 +26,10 @@ private final BjorneToken io; private final BjorneToken arg; + + private String hereDocument; - private String hereDocument; + private boolean expandable = true; public RedirectionNode(final int redirectionType, BjorneToken io, BjorneToken arg) { @@ -50,6 +52,8 @@ } public void setHereDocument(String hereDocument) { + // FIXME ... should analyze the document and set 'expandable' + // if there anything that requires expansion. this.hereDocument = hereDocument; } @@ -70,4 +74,8 @@ sb.append("}"); return sb.toString(); } + + public boolean isHereDocumentExpandable() { + return expandable; + } } Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-03-20 20:42:48 UTC (rev 5133) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-03-21 01:13:41 UTC (rev 5134) @@ -639,6 +639,32 @@ Hi mum again </file> </testSpec> + <testSpec title="here 2" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + for i in 1 2 3 4 5 ; do cat <<EOF ; done +Hi mum $i +EOF + </script> + <output>Hi mum 1 +Hi mum 2 +Hi mum 3 +Hi mum 4 +Hi mum 5 +</output> + </testSpec> + <testSpec title="here 3" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + for i in 1 2 3 4 5 ; do cat <<EOF ; done +Hi mum `echo $i` +EOF + </script> + <output>Hi mum 1 +Hi mum 2 +Hi mum 3 +Hi mum 4 +Hi mum 5 +</output> + </testSpec> <testSpec title="pipeline" command="test" runMode="AS_SCRIPT" rc="0"> <script>#!bjorne echo > @TEMP_DIR@/1 Hi mum This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-03-29 04:20:42
|
Revision: 5174 http://jnode.svn.sourceforge.net/jnode/?rev=5174&view=rev Author: crawley Date: 2009-03-29 04:20:40 +0000 (Sun, 29 Mar 2009) Log Message: ----------- Bug fix: an empty line in a script would clear $?. Also added a partly implemented / partly tested version of the 'alias' builtin. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml Added Paths: ----------- trunk/shell/src/shell/org/jnode/shell/bjorne/AliasBuiltin.java Added: trunk/shell/src/shell/org/jnode/shell/bjorne/AliasBuiltin.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/AliasBuiltin.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/AliasBuiltin.java 2009-03-29 04:20:40 UTC (rev 5174) @@ -0,0 +1,90 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2009 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.bjorne; + +import java.io.PrintStream; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + +import org.jnode.shell.Command; +import org.jnode.shell.CommandLine; +import org.jnode.shell.ShellException; + +/** + * This class implements the 'alias' built-in. + * + * @author cr...@jn... + */ +final class AliasBuiltin extends BjorneBuiltin { + @SuppressWarnings("deprecation") + public int invoke(CommandLine command, BjorneInterpreter interpreter, + BjorneContext context) throws ShellException { + Iterator<String> args = command.iterator(); + context = context.getParent(); + PrintStream ps = context.resolvePrintStream(context.getIO(Command.STD_ERR)); + int rc = 0; + if (!args.hasNext()) { + printAliases(ps, context.getAliases()); + } else { + while (args.hasNext()) { + String arg = args.next(); + int pos = arg.indexOf('='); + String aliasName; + String alias; + if (pos <= 0) { + aliasName = arg; + alias = null; + } else { + aliasName = arg.substring(0, pos); + alias = arg.substring(pos + 1); + } + if (alias == null) { + alias = context.getAlias(aliasName); + if (alias == null) { + error("alias: " + aliasName + " not found", context); + rc = 1; + } else { + printAlias(ps, aliasName, alias); + } + } else { + if (!BjorneToken.isName(aliasName)) { + error("alias: " + aliasName + ": not a valid alias name", context); + } + context.defineAlias(aliasName, alias); + } + } + } + return rc; + } + + private void printAliases(PrintStream ps, TreeMap<String, String> aliases) { + for (Map.Entry<String, String> entry : aliases.entrySet()) { + printAlias(ps, entry.getKey(), entry.getValue()); + } + } + + private void printAlias(PrintStream ps, String aliasName, String alias) { + ps.println(aliasName + "=" + alias); + } + + +} Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-29 03:11:28 UTC (rev 5173) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-29 04:20:40 UTC (rev 5174) @@ -45,6 +45,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -102,6 +103,8 @@ private final BjorneInterpreter interpreter; private Map<String, VariableSlot> variables; + + private TreeMap<String, String> aliases; private String command = ""; @@ -129,6 +132,7 @@ this.interpreter = interpreter; this.holders = holders; this.variables = new HashMap<String, VariableSlot>(); + this.aliases = new TreeMap<String, String>(); } public BjorneContext(BjorneInterpreter interpreter) { @@ -299,8 +303,42 @@ var.exported = exported; } } + + /** + * Get the complete alias map. + * @return the alias map + */ + TreeMap<String, String> getAliases() { + return aliases; + } + + /** + * Lookup an alias + * @param aliasName the (possible) alias name + * @return the alias string or {@code null} + */ + String getAlias(String aliasName) { + return aliases.get(aliasName); + } + + /** + * Define an alias + * @param aliasName the alias name + * @param alias the alias. + */ + void defineAlias(String aliasName, String alias) { + aliases.put(aliasName, alias); + } /** + * Undefine an alias + * @param aliasName the alias name + */ + void undefineAlias(String aliasName) { + aliases.remove(aliasName); + } + + /** * Perform expand-and-split processing on a list of word tokens. The resulting * wordTokens are assembled into a CommandLine. * Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-03-29 03:11:28 UTC (rev 5173) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-03-29 04:20:40 UTC (rev 5174) @@ -140,6 +140,7 @@ private static long subshellCount; static { + BUILTINS.put("alias", new AliasBuiltin()); BUILTINS.put("break", new BreakBuiltin()); BUILTINS.put("continue", new ContinueBuiltin()); BUILTINS.put("exit", new ExitBuiltin()); @@ -242,7 +243,7 @@ CommandNode tree = new BjorneParser(tokens, "> ").parse(); if (tree == null) { // An empty command line - return 0; + return myContext.getLastReturnCode(); } if (DEBUG) { System.err.println(tree); Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-03-29 03:11:28 UTC (rev 5173) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-03-29 04:20:40 UTC (rev 5174) @@ -3,12 +3,25 @@ <plugin id="org.jnode.shell.command.posix"/> <testSpec title="exit 0" command="test" runMode="AS_SCRIPT" rc="0"> <script>#!bjorne -exit 0 + exit 0 </script> </testSpec> - <testSpec title="exit 99" command="test" runMode="AS_SCRIPT" rc="99"> + <testSpec title="exit 99" runMode="AS_SCRIPT" rc="99"> <script>#!bjorne -exit 99 + exit 99 </script> </testSpec> + <testSpec title="alias" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + alias > @TEMP_DIR@/1 + </script> + <file name="1" input="false"></file> + </testSpec> + <testSpec title="alias 2" command="test" runMode="AS_SCRIPT" rc="1"> + <script>#!bjorne + alias fred + </script> + <error>alias: fred not found +</error> + </testSpec> </testSet> \ No newline at end of file Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-03-29 03:11:28 UTC (rev 5173) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-03-29 04:20:40 UTC (rev 5174) @@ -22,9 +22,17 @@ echo $? false echo $? +false + +echo $? +false +# no comment +echo $? </script> <output>0 1 +1 +1 </output> </testSpec> <testSpec title="``" command="test" runMode="AS_SCRIPT" rc="0"> @@ -164,7 +172,7 @@ A is 1 </output> </testSpec> - <testSpec title="while ... do ... done" command="test" runMode="AS_SCRIPT" rc="0"> + <testSpec title="while ... do ... done" command="test" runMode="AS_SCRIPT" rc="1"> <script>#!bjorne A=5 while expr $A != 0 ; do echo A is $A ; A=`expr $A - 1`; done @@ -182,7 +190,7 @@ 0 </output> </testSpec> - <testSpec title="while ... do ... done multi-line" command="test" runMode="AS_SCRIPT" rc="0"> + <testSpec title="while ... do ... done multi-line" command="test" runMode="AS_SCRIPT" rc="1"> <script>#!bjorne A=5 while expr $A != 0 ; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-03-29 07:24:25
|
Revision: 5175 http://jnode.svn.sourceforge.net/jnode/?rev=5175&view=rev Author: crawley Date: 2009-03-29 07:24:21 +0000 (Sun, 29 Mar 2009) Log Message: ----------- Added 'unalias' and fixed bugs in 'alias'. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/AliasBuiltin.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml Added Paths: ----------- trunk/shell/src/shell/org/jnode/shell/bjorne/UnaliasBuiltin.java Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/AliasBuiltin.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/AliasBuiltin.java 2009-03-29 04:20:40 UTC (rev 5174) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/AliasBuiltin.java 2009-03-29 07:24:21 UTC (rev 5175) @@ -40,10 +40,11 @@ BjorneContext context) throws ShellException { Iterator<String> args = command.iterator(); context = context.getParent(); - PrintStream ps = context.resolvePrintStream(context.getIO(Command.STD_ERR)); + PrintStream out = context.resolvePrintStream(context.getIO(Command.STD_OUT)); + PrintStream err = context.resolvePrintStream(context.getIO(Command.STD_ERR)); int rc = 0; if (!args.hasNext()) { - printAliases(ps, context.getAliases()); + printAliases(out, context.getAliases()); } else { while (args.hasNext()) { String arg = args.next(); @@ -60,14 +61,14 @@ if (alias == null) { alias = context.getAlias(aliasName); if (alias == null) { - error("alias: " + aliasName + " not found", context); + err.println("alias: " + aliasName + " not found"); rc = 1; } else { - printAlias(ps, aliasName, alias); + printAlias(out, aliasName, alias); } } else { if (!BjorneToken.isName(aliasName)) { - error("alias: " + aliasName + ": not a valid alias name", context); + err.println("alias: " + aliasName + ": not a valid alias name"); } context.defineAlias(aliasName, alias); } @@ -83,7 +84,7 @@ } private void printAlias(PrintStream ps, String aliasName, String alias) { - ps.println(aliasName + "=" + alias); + ps.println(aliasName + "='" + alias + "'"); } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-03-29 04:20:40 UTC (rev 5174) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-03-29 07:24:21 UTC (rev 5175) @@ -148,8 +148,9 @@ BUILTINS.put("return", new ReturnBuiltin()); BUILTINS.put("set", new SetBuiltin()); BUILTINS.put("shift", new ShiftBuiltin()); + BUILTINS.put("source", new SourceBuiltin()); + BUILTINS.put("unalias", new UnaliasBuiltin()); BUILTINS.put(".", new SourceBuiltin()); - BUILTINS.put("source", new SourceBuiltin()); BUILTINS.put(":", new ColonBuiltin()); } Added: trunk/shell/src/shell/org/jnode/shell/bjorne/UnaliasBuiltin.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/UnaliasBuiltin.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/UnaliasBuiltin.java 2009-03-29 07:24:21 UTC (rev 5175) @@ -0,0 +1,60 @@ +/* + * $Id$ + * + * Copyright (C) 2003-2009 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.bjorne; + +import java.io.PrintStream; +import java.util.Iterator; + +import org.jnode.shell.Command; +import org.jnode.shell.CommandLine; +import org.jnode.shell.ShellException; + +/** + * This class implements the 'unalias' built-in. + * + * @author cr...@jn... + */ +final class UnaliasBuiltin extends BjorneBuiltin { + @SuppressWarnings("deprecation") + public int invoke(CommandLine command, BjorneInterpreter interpreter, + BjorneContext context) throws ShellException { + Iterator<String> args = command.iterator(); + context = context.getParent(); + PrintStream err = context.resolvePrintStream(context.getIO(Command.STD_ERR)); + int rc = 0; + while (args.hasNext()) { + String arg = args.next(); + if (arg.equals("-a")) { + context.getAliases().clear(); + break; + } else { + String alias = context.getAlias(arg); + if (alias == null) { + err.println("alias: " + arg + " not found"); + rc = 1; + } else { + context.undefineAlias(arg); + } + } + } + return rc; + } +} Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-03-29 04:20:40 UTC (rev 5174) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-03-29 07:24:21 UTC (rev 5175) @@ -24,4 +24,30 @@ <error>alias: fred not found </error> </testSpec> + <testSpec title="alias 3" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + alias fred=ls + alias fred + alias jim="echo hi" + alias jim + alias + </script> + <output>fred='ls' +jim='echo hi' +fred='ls' +jim='echo hi' +</output> + </testSpec> + <testSpec title="unalias" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + alias fred=ls + alias jim="echo hi" + unalias jim + alias + unalias -a + alias + </script> + <output>fred='ls' +</output> + </testSpec> </testSet> \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-03-29 13:08:23
|
Revision: 5180 http://jnode.svn.sourceforge.net/jnode/?rev=5180&view=rev Author: crawley Date: 2009-03-29 13:08:19 +0000 (Sun, 29 Mar 2009) Log Message: ----------- Bjorne shell aliases should now work Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-29 10:53:16 UTC (rev 5179) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-03-29 13:08:19 UTC (rev 5180) @@ -52,6 +52,7 @@ import org.jnode.shell.Command; import org.jnode.shell.CommandLine; import org.jnode.shell.CommandShell; +import org.jnode.shell.IncompleteCommandException; import org.jnode.shell.PathnamePattern; import org.jnode.shell.ShellException; import org.jnode.shell.ShellFailureException; @@ -159,6 +160,7 @@ this.interpreter = parent.interpreter; this.holders = copyStreamHolders(parent.holders); this.variables = copyVariables(parent.variables); + this.aliases = new TreeMap<String, String>(parent.aliases); this.globbing = parent.globbing; this.tildeExpansion = parent.tildeExpansion; this.echoExpansions = parent.echoExpansions; @@ -1238,4 +1240,39 @@ return interpreter.getUniqueName(); } + public BjorneToken[] substituteAliases(BjorneToken[] words) + throws IncompleteCommandException { + String alias = aliases.get(words[0].getText()); + if (alias == null) { + return words; + } + List<BjorneToken> list = new LinkedList<BjorneToken>(Arrays.asList(words)); + substituteAliases(list, 0, 0); + return list.toArray(new BjorneToken[list.size()]); + } + + private void substituteAliases(List<BjorneToken> list, int pos, int depth) + throws IncompleteCommandException { + if (depth > 10) { + throw new ShellFailureException("probable cycle detected in alias expansion"); + } + String aliasName = list.get(pos).getText(); + String alias = aliases.get(aliasName); + if (alias == null) { + return; + } + BjorneTokenizer tokens = new BjorneTokenizer(alias); + list.remove(pos); + int i = 0; + while (tokens.hasNext()) { + list.add(pos + i, tokens.next()); + if (i == 0 && !aliasName.equals(list.get(pos + i).getText())) { + substituteAliases(list, pos + i, depth + 1); + } + i++; + } + if (alias.endsWith(" ") && pos + i < list.size()) { + substituteAliases(list, pos + i, depth + 1); + } + } } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java 2009-03-29 10:53:16 UTC (rev 5179) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java 2009-03-29 13:08:19 UTC (rev 5180) @@ -83,6 +83,9 @@ childContext.evaluateRedirections(getRedirects()); rc = 0; } else { + // FIXME ... strictly speaking, alias substitution should be performed + // before "applying the grammatical rules" (i.e. parsing). + words = context.substituteAliases(words); CommandLine command = context.expandAndSplit(words); // Assignments and redirections are done in the command's context BjorneContext childContext = new BjorneContext(context); Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-03-29 10:53:16 UTC (rev 5179) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-03-29 13:08:19 UTC (rev 5180) @@ -38,6 +38,30 @@ jim='echo hi' </output> </testSpec> + <testSpec title="alias 4" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + alias fred="echo hi" + fred + fred ho + fred fred + </script> + <output>hi +hi ho +hi fred +</output> + </testSpec> + <testSpec title="alias 5" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + alias fred="echo hi " + fred + fred ho + fred fred + </script> + <output>hi +hi ho +hi echo hi +</output> + </testSpec> <testSpec title="unalias" command="test" runMode="AS_SCRIPT" rc="0"> <script>#!bjorne alias fred=ls This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-04-13 10:06:06
|
Revision: 5260 http://jnode.svn.sourceforge.net/jnode/?rev=5260&view=rev Author: crawley Date: 2009-04-13 10:06:04 +0000 (Mon, 13 Apr 2009) Log Message: ----------- Fix bug found by cluster - globbing is now suppressed by quotes (modulo some other bugs in PathnamePattern that I'm working on.) Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneToken.java trunk/shell/src/shell/org/jnode/shell/bjorne/CaseCommandNode.java trunk/shell/src/shell/org/jnode/shell/bjorne/ForCommandNode.java trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneContextTests.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml Modified: trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-04-13 09:39:48 UTC (rev 5259) +++ trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-04-13 10:06:04 UTC (rev 5260) @@ -117,43 +117,51 @@ * match "a'c"; i.e. a filename containing a single-quote character. */ public static final int SINGLE_QUOTE_ESCAPES = 0x10; + + /** + * When set, this flag causes characters inside matching double-quote + * characters to be match literal characters in the pathname. Only a '\' is + * unaffected. Thus ""a*c"" will match the file "a*c", but ""a\"c"" will + * match "a"c"; i.e. a filename containing a double-quote character. + */ + public static final int DOUBLE_QUOTE_ESCAPES = 0x20; /** * When set, this flag causes the [...] character class syntax to be * recognized. */ - public static final int CHARACTER_CLASSES = 0x20; + public static final int CHARACTER_CLASSES = 0x40; /** * When set, the pattern is anchored to the left of the string to be searched. * This is set implicitly by the pathname matching methods. */ - public static final int ANCHOR_LEFT = 0x40; + public static final int ANCHOR_LEFT = 0x80; /** * When set, the pattern is anchored to the right of the string to be searched. * This is set implicitly by the pathname matching methods. */ - public static final int ANCHOR_RIGHT = 0x80; + public static final int ANCHOR_RIGHT = 0x100; /** * When set, '*' is eager, matching as many characters as possible. * This is set implicitly by the pathname matching methods. * matching is always eager. */ - public static final int EAGER = 0x100; + public static final int EAGER = 0x200; /** * When set, an unescaped '/' inside a character class causes the entire class * to be interpreted as a literal character sequence. * This is set implicitly by the pathname matching methods. */ - public static final int SLASH_DISABLES_CHARACTER_CLASSES = 0x200; + public static final int SLASH_DISABLES_CHARACTER_CLASSES = 0x400; public static final int DEFAULT_FLAGS = SORT_MATCHES | HIDE_DOT_FILENAMES | INCLUDE_DOT_AND_DOTDOT | BACKSLASH_ESCAPES | SINGLE_QUOTE_ESCAPES - | CHARACTER_CLASSES; + | DOUBLE_QUOTE_ESCAPES | CHARACTER_CLASSES; private static final boolean DEBUG = false; @@ -322,6 +330,15 @@ } return pat; } + + /** + * Clear the pattern cache + */ + public static void clearCache() { + synchronized (PathnamePattern.class) { + cache = null; + } + } /** * Provide a fast determination if a string requires pattern expansion, @@ -365,6 +382,11 @@ return true; } break; + case '\"': + if ((flags & DOUBLE_QUOTE_ESCAPES) != 0) { + return true; + } + break; default: } } @@ -384,7 +406,7 @@ // meta-characters. int len = pattern.length(); StringBuffer sb = new StringBuffer(len); - boolean quoted = false; + char quote = 0; boolean eager = (flags & EAGER) != 0; if ((flags & ANCHOR_LEFT) != 0) { sb.append('^'); @@ -393,8 +415,8 @@ char ch = pattern.charAt(i); switch (ch) { case '?': - if (quoted) { - sb.append(ch); + if (quote != 0) { + sb.append(protect(ch)); } else if (i == 0 && (flags & HIDE_DOT_FILENAMES) != 0) { sb.append("[^\\.]"); } else { @@ -402,8 +424,8 @@ } break; case '*': - if (quoted) { - sb.append(ch); + if (quote != 0) { + sb.append(protect(ch)); } else if (i == 0 && (flags & HIDE_DOT_FILENAMES) != 0) { sb.append("(|[^\\.]").append(eager ? ".*" : ".*?").append(")"); } else { @@ -461,11 +483,30 @@ break; case '\'': if ((flags & SINGLE_QUOTE_ESCAPES) != 0) { - quoted = !quoted; + if (quote == '\'') { + quote = 0; + } else if (quote == 0) { + quote = '\''; + } else { + sb.append(protect(ch)); + } } else { sb.append(protect(ch)); } break; + case '\"': + if ((flags & DOUBLE_QUOTE_ESCAPES) != 0) { + if (quote == '\"') { + quote = 0; + } else if (quote == 0) { + quote = '\"'; + } else { + sb.append(protect(ch)); + } + } else { + sb.append(protect(ch)); + } + break; default: sb.append(protect(ch)); } @@ -499,4 +540,19 @@ public String toString() { return source; } + + public String toRegexString() { + StringBuffer sb = new StringBuffer(); + sb.append("PathnamePattern{source='").append(this.source); + sb.append("',absolute=").append(this.isAbsolute); + sb.append(",pattern=["); + for (int i = 0; i < this.pattern.length; i++) { + if (i > 0) { + sb.append(","); + } + sb.append('\'').append(pattern[i]).append('\''); + } + sb.append("]}"); + return sb.toString(); + } } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-04-13 09:39:48 UTC (rev 5259) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-04-13 10:06:04 UTC (rev 5260) @@ -339,69 +339,109 @@ void undefineAlias(String aliasName) { aliases.remove(aliasName); } - + /** - * Perform expand-and-split processing on a list of word tokens. The resulting + * Perform expand-and-split processing on an array of word tokens. The resulting * wordTokens are assembled into a CommandLine. * * @param tokens the tokens to be expanded and split into words * @return the command line * @throws ShellException */ - public CommandLine expandAndSplit(Iterable<BjorneToken> tokens) throws ShellException { - List<BjorneToken> wordTokens = new LinkedList<BjorneToken>(); - expandAndSplit(tokens, wordTokens); - return makeCommandLine(wordTokens); + public CommandLine buildCommandLine(BjorneToken ... tokens) throws ShellException { + List<BjorneToken> wordTokens = expandAndSplit(tokens); + int nosWords = wordTokens.size(); + if (nosWords == 0) { + return new CommandLine(null, null); + } else { + BjorneToken alias = wordTokens.remove(0); + BjorneToken[] args = wordTokens.toArray(new BjorneToken[nosWords - 1]); + return new CommandLine(alias, args, null); + } } /** - * Perform expand-and-split processing on an array of word tokens. The resulting - * wordTokens are assembled into a CommandLine. + * Perform expand-and-split processing on a list of word tokens. * * @param tokens the tokens to be expanded and split into words - * @return the command line * @throws ShellException */ - public CommandLine expandAndSplit(BjorneToken[] tokens) throws ShellException { + public List<BjorneToken> expandAndSplit(Iterable<BjorneToken> tokens) + throws ShellException { List<BjorneToken> wordTokens = new LinkedList<BjorneToken>(); - expandAndSplit(tokens, wordTokens); - return makeCommandLine(wordTokens); + for (BjorneToken token : tokens) { + dollarBacktickSplit(token, wordTokens); + } + wordTokens = doFileExpansions(wordTokens); + wordTokens = dequote(wordTokens); + return wordTokens; } /** - * Perform expand-and-split processing on a list of word tokens. The resulting - * tokens are appended to wordTokens. + * Perform full expand-and-split processing on an array of word tokens. * * @param tokens the tokens to be expanded and split into words - * @param wordTokens append expanded/split tokens to this list * @throws ShellException */ - public void expandAndSplit(Iterable<BjorneToken> tokens, List<BjorneToken> wordTokens) + public List<BjorneToken> expandAndSplit(BjorneToken ... tokens) throws ShellException { + List<BjorneToken> wordTokens = new LinkedList<BjorneToken>(); for (BjorneToken token : tokens) { - expandSplitAndAppend(token, wordTokens); + dollarBacktickSplit(token, wordTokens); } + wordTokens = doFileExpansions(wordTokens); + wordTokens = dequote(wordTokens); + return wordTokens; } + private List<BjorneToken> dequote(List<BjorneToken> wordTokens) { + List<BjorneToken> resTokens = new LinkedList<BjorneToken>(); + for (BjorneToken token : wordTokens) { + String text = token.getText(); + int len = text.length(); + StringBuffer sb = new StringBuffer(len); + int quote = 0; + for (int i = 0; i < len; i++) { + char ch = text.charAt(i); + switch (ch) { + case '"': + case '\'': + if (quote == 0) { + quote = ch; + } else if (quote == ch) { + quote = 0; + } else { + sb.append(ch); + } + break; + case '\\': + if (i + 1 < len) { + ch = text.charAt(++i); + } + sb.append(ch); + break; + default: + sb.append(ch); + break; + } + } + resTokens.add(token.remake(sb)); + } + return resTokens; + } + /** - * Perform expand-and-split processing on an array of word tokens. The resulting - * tokens are appended to wordTokens. + * Do dollar and backtick expansion on a token, split into words, retokenize and + * append the resulting tokens to 'wordTokens. * - * @param tokens the tokens to be expanded and split into words - * @param wordTokens append expanded/split tokens to this list + * @param token + * @param wordTokens * @throws ShellException */ - public void expandAndSplit(BjorneToken[] tokens, List<BjorneToken> wordTokens) + private void dollarBacktickSplit(BjorneToken token, List<BjorneToken> wordTokens) throws ShellException { - for (BjorneToken token : tokens) { - expandSplitAndAppend(token, wordTokens); - } - } - - private void expandSplitAndAppend(BjorneToken token, List<BjorneToken> wordTokens) - throws ShellException { String word = token.getText(); - CharSequence expanded = expand(word); + CharSequence expanded = dollarBacktickExpand(word); if (expanded == word) { splitAndAppend(token, wordTokens); } else { @@ -411,21 +451,8 @@ } } } - - /** - * Perform expand-and-split processing on a sequence of characters. This method is only - * used in tests at the moment, and probably should be removed. (It does not set token - * attributes properly ...) - * - * @param text the characters to be split - * @return the command line - * @throws ShellException - */ - public List<BjorneToken> expandAndSplit(CharSequence text) throws ShellException { - return split(expand(text)); - } - - private CommandLine makeCommandLine(List<BjorneToken> wordTokens) { + + private List<BjorneToken> doFileExpansions(List<BjorneToken> wordTokens) { if (globbing || tildeExpansion) { List<BjorneToken> globbedWordTokens = new LinkedList<BjorneToken>(); for (BjorneToken wordToken : wordTokens) { @@ -438,15 +465,9 @@ globbedWordTokens.add(wordToken); } } - wordTokens = globbedWordTokens; - } - int nosWords = wordTokens.size(); - if (nosWords == 0) { - return new CommandLine(null, null); + return globbedWordTokens; } else { - BjorneToken alias = wordTokens.remove(0); - BjorneToken[] args = wordTokens.toArray(new BjorneToken[nosWords - 1]); - return new CommandLine(alias, args, null); + return wordTokens; } } @@ -476,6 +497,7 @@ return; } PathnamePattern pattern = PathnamePattern.compilePathPattern(word); + // Expand using the current directory as the base for relative path patterns. LinkedList<String> paths = pattern.expand(new File(".")); // If it doesn't match anything, a pattern 'expands' to itself. if (paths.isEmpty()) { @@ -486,24 +508,10 @@ } } } - - /** - * Split a character sequence into word tokens, dealing with and removing any - * non-literal quotes. - * - * @param text the characters to be split - * @return the resulting list of tokens. - * @throws ShellException - */ - public List<BjorneToken> split(CharSequence text) throws ShellException { - List<BjorneToken> wordTokens = new LinkedList<BjorneToken>(); - splitAndAppend(new BjorneToken(BjorneToken.TOK_WORD, text.toString(), -1, -1), wordTokens); - return wordTokens; - } /** - * Split a token into a series of word tokens, dealing with and removing any - * non-literal quotes. The resulting tokens are appended to a supplied list. + * Split a token into a series of word tokens, leaving quoting intact. + * The resulting tokens are appended to a supplied list. * * @param token the token to be split * @param wordTokens the destination for the tokens. @@ -522,14 +530,10 @@ case '\'': if (quote == 0) { quote = ch; - if (sb == null) { - sb = new StringBuffer(); - } } else if (quote == ch) { quote = 0; - } else { - sb = accumulate(sb, ch); - } + } + sb = accumulate(sb, ch); break; case ' ': case '\t': @@ -544,6 +548,7 @@ break; case '\\': if (i + 1 < len) { + sb = accumulate(sb, ch); ch = text.charAt(++i); } sb = accumulate(sb, ch); @@ -577,13 +582,14 @@ } /** - * Perform '$' expansion and backtick substitution. Any quotes and escapes should be preserved (?!?!?) + * Perform '$' expansion and backtick substitution. Any quotes and escapes must + * be preserved so that they escape globbing and tilde expansion. * * @param text the characters to be expanded * @return the result of the expansion. * @throws ShellException */ - public CharSequence expand(CharSequence text) throws ShellException { + public CharSequence dollarBacktickExpand(CharSequence text) throws ShellException { CharIterator ci = new CharIterator(text); StringBuffer sb = new StringBuffer(text.length()); char quote = 0; @@ -1066,7 +1072,7 @@ throw new ShellFailureException("misplaced '=' in assignment"); } String name = assignment.substring(0, pos); - String value = expand(assignment.substring(pos + 1)).toString(); + String value = dollarBacktickExpand(assignment.substring(pos + 1)).toString(); this.setVariable(name, value); } } @@ -1141,7 +1147,7 @@ case REDIR_DLESSDASH: String here = redir.getHereDocument(); if (redir.isHereDocumentExpandable()) { - here = expand(here).toString(); + here = dollarBacktickExpand(here).toString(); } in = new CommandInput(new StringReader(here)); stream = new CommandIOHolder(in, true); Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneToken.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneToken.java 2009-04-13 09:39:48 UTC (rev 5259) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneToken.java 2009-04-13 10:06:04 UTC (rev 5260) @@ -129,6 +129,11 @@ validate(); } + public BjorneToken(final String text) { + super(text == null ? "" : text, TOK_WORD, 0, 0); + validate(); + } + public BjorneToken remake(CharSequence newText) { if (newText.length() == 0) { return null; Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/CaseCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/CaseCommandNode.java 2009-04-13 09:39:48 UTC (rev 5259) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/CaseCommandNode.java 2009-04-13 10:06:04 UTC (rev 5260) @@ -62,11 +62,11 @@ public int execute(BjorneContext context) throws ShellException { int rc = 0; - CharSequence expandedWord = context.expand(word.text); + CharSequence expandedWord = context.dollarBacktickExpand(word.text); LOOP: for (CaseItemNode caseItem : caseItems) { for (BjorneToken pattern : caseItem.getPattern()) { - CharSequence pat = context.expand(pattern.text); + CharSequence pat = context.dollarBacktickExpand(pattern.text); if (context.patternMatch(expandedWord, pat)) { rc = caseItem.getBody().execute(context); break LOOP; Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/ForCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/ForCommandNode.java 2009-04-13 09:39:48 UTC (rev 5259) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/ForCommandNode.java 2009-04-13 10:06:04 UTC (rev 5260) @@ -20,7 +20,6 @@ package org.jnode.shell.bjorne; -import java.util.LinkedList; import java.util.List; import org.jnode.shell.CommandRunnable; @@ -94,8 +93,7 @@ @Override public int execute(BjorneContext context) throws ShellException { int rc = 0; - List<BjorneToken> expanded = new LinkedList<BjorneToken>(); - context.expandAndSplit(words, expanded); + List<BjorneToken> expanded = context.expandAndSplit(words); for (BjorneToken word : expanded) { context.setVariable(var.getText(), word.getText()); rc = body.execute(context); Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java 2009-04-13 09:39:48 UTC (rev 5259) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java 2009-04-13 10:06:04 UTC (rev 5260) @@ -86,7 +86,7 @@ // FIXME ... strictly speaking, alias substitution should be performed // before "applying the grammatical rules" (i.e. parsing). words = context.substituteAliases(words); - CommandLine command = context.expandAndSplit(words); + CommandLine command = context.buildCommandLine(words); // Assignments and redirections are done in the command's context BjorneContext childContext = new BjorneContext(context); childContext.performAssignments(assignments); @@ -118,7 +118,7 @@ public CommandThread fork(CommandShell shell, BjorneContext context) throws ShellException { - CommandLine command = context.expandAndSplit(getWords()); + CommandLine command = context.buildCommandLine(getWords()); command.setStreams(context.getIOs()); CommandInfo cmdInfo = command.parseCommandLine(shell); return shell.invokeAsynchronous(command, cmdInfo); @@ -128,7 +128,7 @@ public void complete(CompletionInfo completion, BjorneContext context, CommandShell shell) throws CompletionException { try { - CommandLine command = context.expandAndSplit(words); + CommandLine command = context.buildCommandLine(words); command.complete(completion, shell); } catch (ShellException ex) { throw new CompletionException("Shell exception", ex); Modified: trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-04-13 09:39:48 UTC (rev 5259) +++ trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-04-13 10:06:04 UTC (rev 5260) @@ -48,17 +48,36 @@ public void testCompilePosixShellPattern() { assertEquals("abc", PathnamePattern.compilePosixShellPattern("abc", 0).toString()); assertEquals("abc", PathnamePattern.compilePosixShellPattern("abc", DF).toString()); + assertEquals(".", PathnamePattern.compilePosixShellPattern("?", 0).toString()); assertEquals("[^\\.]", PathnamePattern.compilePosixShellPattern("?", DF).toString()); + assertEquals(".*?", PathnamePattern.compilePosixShellPattern("*", 0).toString()); assertEquals("(|[^\\.].*?)", PathnamePattern.compilePosixShellPattern("*", DF).toString()); + assertEquals(".*?a.*?", PathnamePattern.compilePosixShellPattern("*a*", 0).toString()); assertEquals("(|[^\\.].*?)a.*?", PathnamePattern.compilePosixShellPattern("*a*", DF).toString()); + assertEquals("\".*?a.*?\"", PathnamePattern.compilePosixShellPattern("\"*a*\"", 0).toString()); assertEquals("\\*a\\*", PathnamePattern.compilePosixShellPattern("\"*a*\"", DF).toString()); + assertEquals("\'.*?a.*?\'", PathnamePattern.compilePosixShellPattern("\'*a*\'", 0).toString()); assertEquals("\\*a\\*", PathnamePattern.compilePosixShellPattern("\'*a*\'", DF).toString()); + assertEquals("\\\\.*?a.*?", PathnamePattern.compilePosixShellPattern("\\*a*", 0).toString()); assertEquals("\\*a.*?", PathnamePattern.compilePosixShellPattern("\\*a*", DF).toString()); } + + public void testCompilePathPattern() { + assertEquals("PathnamePattern{source='abc',absolute=false,pattern=['abc']}", + PathnamePattern.compilePathPattern("abc", DF).toRegexString()); + + assertEquals("PathnamePattern{source='?',absolute=false,pattern=['^[^\\.]$']}", + PathnamePattern.compilePathPattern("?", DF).toRegexString()); + + // The following (which matches an empty pathname component) is suboptimal but + // not incorrect. In practice, we should never encounter an empty pathname component. + assertEquals("PathnamePattern{source='*',absolute=false,pattern=['^(|[^\\.].*)$']}", + PathnamePattern.compilePathPattern("*", DF).toRegexString()); + } } Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneContextTests.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneContextTests.java 2009-04-13 09:39:48 UTC (rev 5259) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneContextTests.java 2009-04-13 10:06:04 UTC (rev 5260) @@ -28,6 +28,7 @@ import junit.framework.TestCase; import org.jnode.shell.CommandLine; +import org.jnode.shell.PathnamePattern; import org.jnode.shell.ShellException; import org.jnode.shell.bjorne.BjorneContext; import org.jnode.shell.bjorne.BjorneToken; @@ -41,83 +42,88 @@ public void testExpand1() throws ShellException { BjorneContext context = new BjorneContext(null, null); - List<BjorneToken> expansion = context.expandAndSplit(""); + List<BjorneToken> expansion = context.expandAndSplit(); checkExpansion(expansion, new String[] {}); } - public void testExpand2() throws ShellException { - BjorneContext context = new BjorneContext(null, null); - List<BjorneToken> expansion = context.expandAndSplit(" "); - checkExpansion(expansion, new String[] {}); - } - public void testExpand3() throws ShellException { BjorneContext context = new BjorneContext(null, null); - List<BjorneToken> expansion = context.expandAndSplit("hi"); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("hi")); checkExpansion(expansion, new String[] {"hi"}); } public void testExpand4() throws ShellException { BjorneContext context = new BjorneContext(null, null); - List<BjorneToken> expansion = context.expandAndSplit("hi there "); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("hi there")); checkExpansion(expansion, new String[] {"hi", "there"}); } public void testExpand5() throws ShellException { BjorneContext context = new BjorneContext(null, null); - List<BjorneToken> expansion = context.expandAndSplit("'hi there '"); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("'hi there '")); checkExpansion(expansion, new String[] {"hi there "}); } public void testExpand6() throws ShellException { BjorneContext context = new BjorneContext(null, null); - List<BjorneToken> expansion = context.expandAndSplit("\"hi there \" "); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("\"hi there \" ")); checkExpansion(expansion, new String[] {"hi there "}); } public void testExpand7() throws ShellException { BjorneContext context = new BjorneContext(null, null); - List<BjorneToken> expansion = context.expandAndSplit("hi\\ there"); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("hi\\ there")); checkExpansion(expansion, new String[] {"hi there"}); } public void testExpand8() throws ShellException { BjorneContext context = new BjorneContext(null, null); - List<BjorneToken> expansion = context.expandAndSplit("\\\"hi\\ there\\\""); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("\\\"hi\\ there\\\"")); checkExpansion(expansion, new String[] {"\"hi there\""}); } public void testExpand9() throws ShellException { BjorneContext context = new BjorneContext(null, null); - List<BjorneToken> expansion = context.expandAndSplit("$?"); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("$?")); checkExpansion(expansion, new String[] {"0"}); } public void testExpand10() throws ShellException { BjorneContext context = new BjorneContext(null, null); context.setVariable("A", "A"); - List<BjorneToken> expansion = context.expandAndSplit("$A"); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("$A")); checkExpansion(expansion, new String[] {"A"}); } public void testExpand11() throws ShellException { BjorneContext context = new BjorneContext(null, null); context.setVariable("A", "A"); - List<BjorneToken> expansion = context.expandAndSplit("\\$A"); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("\\$A")); checkExpansion(expansion, new String[] {"$A"}); } public void testExpand12() throws ShellException { BjorneContext context = new BjorneContext(null, null); context.setVariable("A", "A"); - List<BjorneToken> expansion = context.expandAndSplit("\"$A\""); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("\"$A\"")); checkExpansion(expansion, new String[] {"A"}); } public void testExpand13() throws ShellException { BjorneContext context = new BjorneContext(null, null); context.setVariable("A", "A"); - List<BjorneToken> expansion = context.expandAndSplit("'$A'"); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("'$A'")); checkExpansion(expansion, new String[] {"$A"}); } @@ -125,31 +131,43 @@ BjorneContext parentContext = new BjorneContext(null, new CommandIOHolder[0]); parentContext.setVariable("A", "A"); BjorneContext context = new BjorneContext(parentContext); - List<BjorneToken> expansion = context.expandAndSplit("'$A'"); + List<BjorneToken> expansion = context.expandAndSplit( + new BjorneToken("'$A'")); checkExpansion(expansion, new String[] {"$A"}); } public void testExpand15() throws Exception { + PathnamePattern.clearCache(); BjorneContext context = new BjorneContext(null, null); assertEquals(true, context.isGlobbing()); assertEquals(true, context.isTildeExpansion()); if (new File("../README.txt").exists()) { - CommandLine expansion = context.expandAndSplit(context.split("../README.*")); + CommandLine expansion = context.buildCommandLine(new BjorneToken("../README.*")); checkExpansion(expansion, new String[] {"../README.txt"}); + expansion = context.buildCommandLine(new BjorneToken("../README.\\*")); + checkExpansion(expansion, new String[] {"../README.*"}); + expansion = context.buildCommandLine(new BjorneToken("\"../README.*\"")); + checkExpansion(expansion, new String[] {"../README.*"}); + expansion = context.buildCommandLine(new BjorneToken("\'../README.*\'")); + checkExpansion(expansion, new String[] {"../README.*"}); + + context.setGlobbing(false); + expansion = context.buildCommandLine(new BjorneToken("../README.*")); + checkExpansion(expansion, new String[] {"../README.*"}); + } else { + System.err.println("skipping 'glob' tests ... no ../README.txt"); } - context.setGlobbing(false); - CommandLine expansion = context.expandAndSplit(context.split("../README.*")); - checkExpansion(expansion, new String[] {"../README.*"}); + } public void testExpand16() throws Exception { BjorneContext context = new BjorneContext(null, null); assertEquals(true, context.isGlobbing()); assertEquals(true, context.isTildeExpansion()); - CommandLine expansion = context.expandAndSplit(context.split("~")); + CommandLine expansion = context.buildCommandLine(new BjorneToken("~")); checkExpansion(expansion, new String[] {System.getProperty("user.home")}); context.setTildeExpansion(false); - expansion = context.expandAndSplit(context.split("~")); + expansion = context.buildCommandLine(new BjorneToken("~")); checkExpansion(expansion, new String[] {"~"}); } Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-04-13 09:39:48 UTC (rev 5259) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-04-13 10:06:04 UTC (rev 5260) @@ -1,6 +1,7 @@ <testSet title="Bjorne interpreter tests"> <plugin id="org.jnode.shell.bjorne" class="org.jnode.test.shell.bjorne.BjornePseudoPlugin"/> <plugin id="org.jnode.shell.command.posix"/> + <plugin id="org.jnode.fs.command"/> <testSpec title="simple" command="test" runMode="AS_SCRIPT" rc="0"> <script>#!bjorne echo HI @@ -630,6 +631,40 @@ + echo arg1 arg2 </error> </testSpec> + <testSpec title="globbing" command="test" runMode="AS_SCRIPT" rc="0"> + <script>#!bjorne + echo > @TEMP_DIR@/xyzzy Hi mum + echo @TEMP_DIR@/* + echo @TEMP_DIR@/\* + echo @TEMP_DIR@/"*" + echo @TEMP_DIR@/'*' + echo "@TEMP_DIR@/*" + echo '@TEMP_DIR@/*' + PWD=`pwd` + # echo $PWD + cd @TEMP_DIR@ + # pwd + echo * + echo \* + echo "*" + echo '*' + cd $PWD + # pwd + </script> + <file name="xyzzy" input="false">Hi mum +</file> + <output>/tmp/jnodeTestDir/xyzzy +/tmp/jnodeTestDir/* +/tmp/jnodeTestDir/* +/tmp/jnodeTestDir/* +/tmp/jnodeTestDir/* +/tmp/jnodeTestDir/* +xyzzy +* +* +* +</output> + </testSpec> <testSpec title="redirection" command="test" runMode="AS_SCRIPT" rc="0"> <script>#!bjorne echo > @TEMP_DIR@/1 Hi mum @@ -647,7 +682,8 @@ </output> <error>Hello mother again </error> - </testSpec><testSpec title="here" command="test" runMode="AS_SCRIPT" rc="0"> + </testSpec> + <testSpec title="here" command="test" runMode="AS_SCRIPT" rc="0"> <script>#!bjorne cat <<EOF > @TEMP_DIR@/1 Hi mum This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-04-13 14:01:17
|
Revision: 5262 http://jnode.svn.sourceforge.net/jnode/?rev=5262&view=rev Author: crawley Date: 2009-04-13 13:24:06 +0000 (Mon, 13 Apr 2009) Log Message: ----------- Fix for bug where expanding the pattern "/tmp/*" gave strings with the leading "/" missing. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java Modified: trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-04-13 11:02:36 UTC (rev 5261) +++ trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-04-13 13:24:06 UTC (rev 5262) @@ -252,6 +252,9 @@ for (File match : matches) { String name = match.getName(); + if (pos == 0 && isAbsolute) { + name = File.separator + name; + } if (pos == pattern.length - 1) { res.add(name); } else if (match.isDirectory()) { Modified: trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-04-13 11:02:36 UTC (rev 5261) +++ trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-04-13 13:24:06 UTC (rev 5262) @@ -20,6 +20,9 @@ package org.jnode.test.shell; +import java.io.File; +import java.util.LinkedList; + import junit.framework.TestCase; import org.jnode.shell.PathnamePattern; @@ -80,4 +83,17 @@ assertEquals("PathnamePattern{source='*',absolute=false,pattern=['^(|[^\\.].*)$']}", PathnamePattern.compilePathPattern("*", DF).toRegexString()); } + + public void testExpand() { + PathnamePattern pat = PathnamePattern.compilePathPattern("/tmp/*"); + LinkedList<String> list = pat.expand(new File(".")); + for (String path : list) { + assertTrue(new File(path).exists()); + } + pat = PathnamePattern.compilePathPattern("*"); + list = pat.expand(new File(".")); + for (String path : list) { + assertTrue(new File(path).exists()); + } + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-04-13 14:57:04
|
Revision: 5263 http://jnode.svn.sourceforge.net/jnode/?rev=5263&view=rev Author: crawley Date: 2009-04-13 14:56:44 +0000 (Mon, 13 Apr 2009) Log Message: ----------- Tests and a minor bugfix. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml Modified: trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-04-13 13:24:06 UTC (rev 5262) +++ trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-04-13 14:56:44 UTC (rev 5263) @@ -299,16 +299,17 @@ } } + String src = source; boolean isAbsolute; - if (source.startsWith(File.separator)) { - while (source.startsWith(File.separator)) { - source = source.substring(1); + if (src.startsWith(File.separator)) { + while (src.startsWith(File.separator)) { + src = src.substring(1); } isAbsolute = true; } else { isAbsolute = false; } - String[] parts = source.split(File.separator + "+", -1); + String[] parts = src.split(File.separator + "+", -1); Object[] res = new Object[parts.length]; for (int i = 0; i < parts.length; i++) { String part = parts[i]; Modified: trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-04-13 13:24:06 UTC (rev 5262) +++ trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-04-13 14:56:44 UTC (rev 5263) @@ -82,6 +82,18 @@ // not incorrect. In practice, we should never encounter an empty pathname component. assertEquals("PathnamePattern{source='*',absolute=false,pattern=['^(|[^\\.].*)$']}", PathnamePattern.compilePathPattern("*", DF).toRegexString()); + + assertEquals("PathnamePattern{source='\"*\"',absolute=false,pattern=['^\\*$']}", + PathnamePattern.compilePathPattern("\"*\"", DF).toRegexString()); + + assertEquals("PathnamePattern{source='a/b',absolute=false,pattern=['a','b']}", + PathnamePattern.compilePathPattern("a/b", DF).toRegexString()); + + assertEquals("PathnamePattern{source='a/*',absolute=false,pattern=['a','^(|[^\\.].*)$']}", + PathnamePattern.compilePathPattern("a/*", DF).toRegexString()); + + assertEquals("PathnamePattern{source='/a/*',absolute=true,pattern=['a','^(|[^\\.].*)$']}", + PathnamePattern.compilePathPattern("/a/*", DF).toRegexString()); } public void testExpand() { Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-04-13 13:24:06 UTC (rev 5262) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-04-13 14:56:44 UTC (rev 5263) @@ -640,16 +640,6 @@ echo @TEMP_DIR@/'*' echo "@TEMP_DIR@/*" echo '@TEMP_DIR@/*' - PWD=`pwd` - # echo $PWD - cd @TEMP_DIR@ - # pwd - echo * - echo \* - echo "*" - echo '*' - cd $PWD - # pwd </script> <file name="xyzzy" input="false">Hi mum </file> @@ -659,10 +649,6 @@ /tmp/jnodeTestDir/* /tmp/jnodeTestDir/* /tmp/jnodeTestDir/* -xyzzy -* -* -* </output> </testSpec> <testSpec title="redirection" command="test" runMode="AS_SCRIPT" rc="0"> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-04-14 13:03:02
|
Revision: 5266 http://jnode.svn.sourceforge.net/jnode/?rev=5266&view=rev Author: crawley Date: 2009-04-14 13:02:59 +0000 (Tue, 14 Apr 2009) Log Message: ----------- Fixed the remaining bug with handling of quotes, and another with the handling of doubled '/' characters. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java Modified: trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-04-13 19:28:59 UTC (rev 5265) +++ trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-04-14 13:02:59 UTC (rev 5266) @@ -23,6 +23,7 @@ import java.io.File; import java.io.FilenameFilter; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -166,16 +167,16 @@ private static final boolean DEBUG = false; private final String source; - private final Object[] pattern; - private final boolean isAbsolute; + private ArrayList<Object> patterns; + private boolean isAbsolute; + private char lastQuote; // Use a weak reference for the pattern cache to avoid storage leakage. private static WeakReference<HashMap<String, PathnamePattern>> cache; - private PathnamePattern(String source, Object[] pattern, boolean isAbsolute) { + private PathnamePattern(String source) { this.source = source; - this.pattern = pattern; - this.isAbsolute = isAbsolute; + this.patterns = new ArrayList<Object>(); } /** @@ -218,13 +219,13 @@ private LinkedList<String> doGlob(File current, int pos, int flags) { LinkedList<File> matches = new LinkedList<File>(); LinkedList<String> res = new LinkedList<String>(); - if (pattern[pos] instanceof String) { - File file = new File(current, (String) pattern[pos]); + if (patterns.get(pos) instanceof String) { + File file = new File(current, (String) patterns.get(pos)); if (file.exists()) { matches.add(file); } } else { - final Pattern pat = (Pattern) pattern[pos]; + final Pattern pat = (Pattern) patterns.get(pos); final Matcher mat = pat.matcher(""); final FilenameFilter filter = new FilenameFilter() { public boolean accept(File dir, String name) { @@ -255,7 +256,7 @@ if (pos == 0 && isAbsolute) { name = File.separator + name; } - if (pos == pattern.length - 1) { + if (pos == patterns.size() - 1) { res.add(name); } else if (match.isDirectory()) { LinkedList<String> subList = doGlob(match, pos + 1, flags); @@ -299,40 +300,37 @@ } } - String src = source; - boolean isAbsolute; - if (src.startsWith(File.separator)) { - while (src.startsWith(File.separator)) { - src = src.substring(1); - } - isAbsolute = true; - } else { - isAbsolute = false; - } - String[] parts = src.split(File.separator + "+", -1); - Object[] res = new Object[parts.length]; + PathnamePattern pp = new PathnamePattern(source); + String[] parts = source.split(File.separator + "+", -1); for (int i = 0; i < parts.length; i++) { String part = parts[i]; - if (isPattern(part, flags)) { - res[i] = compilePosixShellPattern(part, - flags | ANCHOR_LEFT | ANCHOR_RIGHT | EAGER | SLASH_DISABLES_CHARACTER_CLASSES); + Object pat = (isPattern(part, flags)) ? + compilePosixShellPattern(part, + flags | ANCHOR_LEFT | ANCHOR_RIGHT | EAGER | SLASH_DISABLES_CHARACTER_CLASSES, + pp) : part; + if (pat == null || pat.toString().length() == 0) { + if (i == 0) { + pp.isAbsolute = true; + } } else { - res[i] = part; + pp.patterns.add(pat); } if (DEBUG) { - System.err.println(i + ": " + res[i]); + System.err.println(i + ": " + pat); } } - PathnamePattern pat = new PathnamePattern(source, res, isAbsolute); + if (pp.lastQuote != 0) { + throw new IllegalArgumentException("Unbalanced quotes in pattern"); + } synchronized (PathnamePattern.class) { HashMap<String, PathnamePattern> cp = null; if (cache == null || (cp = cache.get()) == null) { cp = new HashMap<String, PathnamePattern>(); cache = new WeakReference<HashMap<String, PathnamePattern>>(cp); } - cp.put(key, pat); + cp.put(key, pp); } - return pat; + return pp; } /** @@ -402,19 +400,28 @@ * generates a {@link Pattern} that can be matched against a character sequence. * * @param pattern the pattern in shell syntax. + * @param flags compilation flags * @return the corresponding regex as a {@link Pattern}. */ public static Pattern compilePosixShellPattern(CharSequence pattern, int flags) { + return compilePosixShellPattern(pattern, flags, null); + } + + /** + * @param pattern the pattern in shell syntax. + * @param flags compilation flags + * @param pp if not {@code null}, + * @return the corresponding regex as a {@link Pattern}. + */ + private static Pattern compilePosixShellPattern( + CharSequence pattern, int flags, PathnamePattern pp) { // This method needs to be really careful to avoid 'ordinary' characters // in the source pattern being accidentally mapped to Java regex // meta-characters. int len = pattern.length(); StringBuffer sb = new StringBuffer(len); - char quote = 0; + char quote = (pp == null) ? ((char) 0) : pp.lastQuote; boolean eager = (flags & EAGER) != 0; - if ((flags & ANCHOR_LEFT) != 0) { - sb.append('^'); - } for (int i = 0; i < len; i++) { char ch = pattern.charAt(i); switch (ch) { @@ -515,6 +522,15 @@ sb.append(protect(ch)); } } + if (pp != null) { + pp.lastQuote = quote; + } + if (sb.length() == 0) { + return null; + } + if ((flags & ANCHOR_LEFT) != 0) { + sb.insert(0, '^'); + } if ((flags & ANCHOR_RIGHT) != 0) { sb.append('$'); } @@ -549,12 +565,13 @@ StringBuffer sb = new StringBuffer(); sb.append("PathnamePattern{source='").append(this.source); sb.append("',absolute=").append(this.isAbsolute); - sb.append(",pattern=["); - for (int i = 0; i < this.pattern.length; i++) { + sb.append(",patterns=["); + int len = this.patterns.size(); + for (int i = 0; i < len; i++) { if (i > 0) { sb.append(","); } - sb.append('\'').append(pattern[i]).append('\''); + sb.append('\'').append(patterns.get(i)).append('\''); } sb.append("]}"); return sb.toString(); Modified: trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-04-13 19:28:59 UTC (rev 5265) +++ trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-04-14 13:02:59 UTC (rev 5266) @@ -61,6 +61,9 @@ assertEquals(".*?a.*?", PathnamePattern.compilePosixShellPattern("*a*", 0).toString()); assertEquals("(|[^\\.].*?)a.*?", PathnamePattern.compilePosixShellPattern("*a*", DF).toString()); + assertEquals("a.*?a.*?a", PathnamePattern.compilePosixShellPattern("a*a*a", 0).toString()); + assertEquals("a.*?a.*?a", PathnamePattern.compilePosixShellPattern("a*a*a", DF).toString()); + assertEquals("\".*?a.*?\"", PathnamePattern.compilePosixShellPattern("\"*a*\"", 0).toString()); assertEquals("\\*a\\*", PathnamePattern.compilePosixShellPattern("\"*a*\"", DF).toString()); @@ -72,28 +75,43 @@ } public void testCompilePathPattern() { - assertEquals("PathnamePattern{source='abc',absolute=false,pattern=['abc']}", + assertEquals("PathnamePattern{source='abc',absolute=false,patterns=['abc']}", PathnamePattern.compilePathPattern("abc", DF).toRegexString()); - assertEquals("PathnamePattern{source='?',absolute=false,pattern=['^[^\\.]$']}", + assertEquals("PathnamePattern{source='?',absolute=false,patterns=['^[^\\.]$']}", PathnamePattern.compilePathPattern("?", DF).toRegexString()); // The following (which matches an empty pathname component) is suboptimal but // not incorrect. In practice, we should never encounter an empty pathname component. - assertEquals("PathnamePattern{source='*',absolute=false,pattern=['^(|[^\\.].*)$']}", + assertEquals("PathnamePattern{source='*',absolute=false,patterns=['^(|[^\\.].*)$']}", PathnamePattern.compilePathPattern("*", DF).toRegexString()); - assertEquals("PathnamePattern{source='\"*\"',absolute=false,pattern=['^\\*$']}", + assertEquals("PathnamePattern{source='\"*\"',absolute=false,patterns=['^\\*$']}", PathnamePattern.compilePathPattern("\"*\"", DF).toRegexString()); - assertEquals("PathnamePattern{source='a/b',absolute=false,pattern=['a','b']}", + assertEquals("PathnamePattern{source='a/b',absolute=false,patterns=['a','b']}", PathnamePattern.compilePathPattern("a/b", DF).toRegexString()); - assertEquals("PathnamePattern{source='a/*',absolute=false,pattern=['a','^(|[^\\.].*)$']}", + assertEquals("PathnamePattern{source='a/*',absolute=false,patterns=['a','^(|[^\\.].*)$']}", PathnamePattern.compilePathPattern("a/*", DF).toRegexString()); - assertEquals("PathnamePattern{source='/a/*',absolute=true,pattern=['a','^(|[^\\.].*)$']}", + assertEquals("PathnamePattern{source='/a/*',absolute=true,patterns=['a','^(|[^\\.].*)$']}", PathnamePattern.compilePathPattern("/a/*", DF).toRegexString()); + + assertEquals("PathnamePattern{source='/a/\\*',absolute=true,patterns=['a','^\\*$']}", + PathnamePattern.compilePathPattern("/a/\\*", DF).toRegexString()); + + assertEquals("PathnamePattern{source='a//\"*\"',absolute=false,patterns=['a','^\\*$']}", + PathnamePattern.compilePathPattern("a//\"*\"", DF).toRegexString()); + + assertEquals("PathnamePattern{source='/a/\"*\"',absolute=true,patterns=['a','^\\*$']}", + PathnamePattern.compilePathPattern("/a/\"*\"", DF).toRegexString()); + + assertEquals("PathnamePattern{source='\"/a/*\"',absolute=true,patterns=['a','^\\*$']}", + PathnamePattern.compilePathPattern("\"/a/*\"", DF).toRegexString()); + + assertEquals("PathnamePattern{source='\"/a/*\"',absolute=true,patterns=['a','^\\*$']}", + PathnamePattern.compilePathPattern("\"/a/*\"", DF).toRegexString()); } public void testExpand() { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-04-20 14:53:59
|
Revision: 5324 http://jnode.svn.sourceforge.net/jnode/?rev=5324&view=rev Author: crawley Date: 2009-04-20 14:53:47 +0000 (Mon, 20 Apr 2009) Log Message: ----------- Yesterdays changes to CommandShell messed up initialization in the EmuShell ... and hence for TestHarness. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/CommandShell.java trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnerBase.java Modified: trunk/shell/src/shell/org/jnode/shell/CommandShell.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2009-04-20 11:39:59 UTC (rev 5323) +++ trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2009-04-20 14:53:47 UTC (rev 5324) @@ -272,9 +272,10 @@ * @param syntaxMgr test framework supplies a syntax manager */ protected CommandShell(AliasManager aliasMgr, SyntaxManager syntaxMgr) { + this.debugEnabled = true; this.aliasMgr = aliasMgr; this.syntaxMgr = syntaxMgr; - this.debugEnabled = true; + propertyMap = initShellProperties(); setupStreams( new InputStreamReader(System.in), new OutputStreamWriter(System.out), @@ -479,7 +480,7 @@ try { setupFromProperties(); - } catch (ShellException ex) { + } catch (Throwable ex) { errPW.println("Problem shell configuration"); errPW.println(ex.getMessage()); stackTrace(ex); @@ -488,7 +489,7 @@ propertyMap.put(INTERPRETER_PROPERTY_NAME, FALLBACK_INTERPRETER); try { setupFromProperties(); - } catch (ShellException ex2) { + } catch (Throwable ex2) { throw new ShellFailureException( "Bailing out: fatal error during CommandShell configuration", ex2); } @@ -498,6 +499,11 @@ ownThread = Thread.currentThread(); } + public void configureEmuShell() throws ShellException { + propertyMap.put(INVOKER_PROPERTY_NAME, "thread"); + configureShell(); + } + @Override public String getProperty(String propName) { return propertyMap.get(propName); Modified: trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnerBase.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnerBase.java 2009-04-20 11:39:59 UTC (rev 5323) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnerBase.java 2009-04-20 14:53:47 UTC (rev 5324) @@ -92,7 +92,7 @@ public CommandShell getShell() throws ShellException { CommandShell shell = new TestCommandShell(System.in, System.out, System.err); - shell.configureShell(); + shell.configureEmuShell(); return shell; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-04-26 14:28:12
|
Revision: 5350 http://jnode.svn.sourceforge.net/jnode/?rev=5350&view=rev Author: crawley Date: 2009-04-26 14:28:11 +0000 (Sun, 26 Apr 2009) Log Message: ----------- Fix bjorne so that builtins are only recognized if they first word of a "simple command" matches a builtin command name BEFORE any expansion. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-04-26 14:25:50 UTC (rev 5349) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-04-26 14:28:11 UTC (rev 5350) @@ -983,7 +983,7 @@ throw new ShellFailureException("not implemented"); } - int execute(CommandLine command, CommandIO[] streams) throws ShellException { + int execute(CommandLine command, CommandIO[] streams, boolean isBuiltin) throws ShellException { if (isEchoExpansions()) { StringBuilder sb = new StringBuilder(); sb.append(" + ").append(command.getCommandName()); @@ -993,7 +993,7 @@ resolvePrintStream(streams[Command.STD_ERR]).println(sb); } Map<String, String> env = buildEnvFromExports(); - lastReturnCode = interpreter.executeCommand(command, this, streams, null, env); + lastReturnCode = interpreter.executeCommand(command, this, streams, null, env, isBuiltin); return lastReturnCode; } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-04-26 14:25:50 UTC (rev 5349) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-04-26 14:28:11 UTC (rev 5350) @@ -130,7 +130,7 @@ public static final int FLAG_PIPE = 0x0010; public static final CommandNode EMPTY = - new SimpleCommandNode(CMD_EMPTY, new BjorneToken[0]); + new SimpleCommandNode(CMD_EMPTY, new BjorneToken[0], false); private static HashMap<String, BjorneBuiltin> BUILTINS = new HashMap<String, BjorneBuiltin>(); @@ -327,14 +327,18 @@ this.shell = shell; } } + + static boolean isBuiltin(String commandWord) { + return BUILTINS.containsKey(commandWord); + } int executeCommand(CommandLine cmdLine, BjorneContext context, CommandIO[] streams, - Properties sysProps, Map<String, String> env) + Properties sysProps, Map<String, String> env, boolean isBuiltin) throws ShellException { - BjorneBuiltin builtin = BUILTINS.get(cmdLine.getCommandName()); - if (builtin != null) { + if (isBuiltin) { // FIXME ... built-in commands should use the Syntax mechanisms so - // that completion, help, etc work as expected. + // that completion, help, etc will work as expected. + BjorneBuiltin builtin = BUILTINS.get(cmdLine.getCommandName()); return builtin.invoke(cmdLine, this, context); } else { cmdLine.setStreams(streams); Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-04-26 14:25:50 UTC (rev 5349) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-04-26 14:28:11 UTC (rev 5350) @@ -276,6 +276,7 @@ List<BjorneToken> assignments = new LinkedList<BjorneToken>(); List<RedirectionNode> redirects = new LinkedList<RedirectionNode>(); List<BjorneToken> words = new LinkedList<BjorneToken>(); + boolean builtin = false; // Deal with cmd_prefix'es before the command name; i.e. assignments and // redirections @@ -334,12 +335,19 @@ break LOOP; } } + if (words.size() > 0) { + String commandWord = words.get(0).getText(); + builtin = BjorneInterpreter.isBuiltin(commandWord); + // FIXME ... built-in commands should use the Syntax mechanisms so + // that completion, help, etc will work as expected. + } } else { // An empty command is legal, as are assignments and redirections // w/o a command. } SimpleCommandNode res = - new SimpleCommandNode(CMD_COMMAND, words.toArray(new BjorneToken[words.size()])); + new SimpleCommandNode(CMD_COMMAND, + words.toArray(new BjorneToken[words.size()]), builtin); if (!redirects.isEmpty()) { res.setRedirects(redirects.toArray(new RedirectionNode[redirects.size()])); } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java 2009-04-26 14:25:50 UTC (rev 5349) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/SimpleCommandNode.java 2009-04-26 14:28:11 UTC (rev 5350) @@ -36,10 +36,13 @@ private BjorneToken[] assignments; private final BjorneToken[] words; + + private final boolean builtin; - public SimpleCommandNode(int nodeType, BjorneToken[] words) { + public SimpleCommandNode(int nodeType, BjorneToken[] words, boolean builtin) { super(nodeType); this.words = words; + this.builtin = builtin; } public void setAssignments(BjorneToken[] assignments) { @@ -54,6 +57,10 @@ return assignments; } + public boolean isBuiltin() { + return builtin; + } + public String toString() { StringBuffer sb = new StringBuffer(); sb.append("SimpleCommand{").append(super.toString()); @@ -61,6 +68,9 @@ sb.append(",assignments="); appendArray(sb, assignments); } + if (builtin) { + sb.append(",builtin=true"); + } if (words != null) { sb.append(",words="); appendArray(sb, words); @@ -99,7 +109,7 @@ throw new ShellFailureException( "asynchronous execution (&) not implemented yet"); } else { - rc = childContext.execute(command, ios); + rc = childContext.execute(command, ios, builtin); } } } finally { Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-04-26 14:25:50 UTC (rev 5349) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-04-26 14:28:11 UTC (rev 5350) @@ -74,4 +74,22 @@ <output>fred='ls' </output> </testSpec> + <testSpec title="builtins recognized early" command="test" + runMode="AS_SCRIPT" rc="0" trapException="org.jnode.shell.ShellException"> + <script>#!bjorne + alias fred=ls + alias + unalias -a + echo done + UNALIAS=unalias + alias fred=dir + alias + $UNALIAS -a + echo done + </script> + <output>fred='ls' +done +fred='dir' +</output> + </testSpec> </testSet> \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-04-30 14:11:12
|
Revision: 5373 http://jnode.svn.sourceforge.net/jnode/?rev=5373&view=rev Author: crawley Date: 2009-04-30 14:11:06 +0000 (Thu, 30 Apr 2009) Log Message: ----------- Starting to get black-box tests running again after reorg. Modified Paths: -------------- trunk/shell/src/emu/org/jnode/emu/Emu.java trunk/shell/src/test/org/jnode/test/shell/all-tests.xml trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml Modified: trunk/shell/src/emu/org/jnode/emu/Emu.java =================================================================== --- trunk/shell/src/emu/org/jnode/emu/Emu.java 2009-04-30 12:27:53 UTC (rev 5372) +++ trunk/shell/src/emu/org/jnode/emu/Emu.java 2009-04-30 14:11:06 UTC (rev 5373) @@ -55,13 +55,21 @@ * @author cr...@jn... */ public class Emu { - private static final String[] ALL_PROJECTS = new String[]{ - "core", "distr", "fs", "gui", "net", "shell", "sound", "textui" + // FIXME configuring a hard-coded list of projects is a bad idea. + private static final String[] ALL_PROJECTS = new String[]{ + "cli", "core", "distr", "fs", "gui", "net", "shell", "sound", "textui" }; // FIXME configuring a hard-coded list of command plugins is a bad idea. private static final String[] DEFAULT_PLUGIN_IDS = new String[] { - "org.jnode.shell.command", + "org.jnode.command.archive", + "org.jnode.command.common", + "org.jnode.command.dev.ant", + "org.jnode.command.dev", + "org.jnode.command.file", + "org.jnode.command.net", + "org.jnode.command.system", + "org.jnode.command.util", "org.jnode.shell.command.driver.console", "org.jnode.apps.editor", "org.jnode.apps.edit", Modified: trunk/shell/src/test/org/jnode/test/shell/all-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/all-tests.xml 2009-04-30 12:27:53 UTC (rev 5372) +++ trunk/shell/src/test/org/jnode/test/shell/all-tests.xml 2009-04-30 14:11:06 UTC (rev 5373) @@ -1,8 +1,8 @@ <testSet title="All shell tests"> <include setName="bjorne/bjorne-shell-tests.xml"/> <include setName="bjorne/bjorne-builtin-tests.xml"/> - <include setName="command/posix/posix-command-tests.xml"/> - <include setName="command/posix/basename-command-tests.xml"/> - <include setName="command/posix/dirname-command-tests.xml"/> - <include setName="command/posix/wc-command-tests.xml"/> +<!-- <include setName="command/posix/posix-command-tests.xml"/>--> +<!-- <include setName="command/posix/basename-command-tests.xml"/>--> +<!-- <include setName="command/posix/dirname-command-tests.xml"/>--> +<!-- <include setName="command/posix/wc-command-tests.xml"/>--> </testSet> Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-04-30 12:27:53 UTC (rev 5372) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-04-30 14:11:06 UTC (rev 5373) @@ -1,6 +1,6 @@ <testSet title="Bjorne interpreter tests"> <plugin id="org.jnode.shell.bjorne" class="org.jnode.test.shell.bjorne.BjornePseudoPlugin"/> - <plugin id="org.jnode.shell.command.posix"/> + <plugin id="org.jnode.command.common"/> <testSpec title="exit 0" command="test" runMode="AS_SCRIPT" rc="0"> <script>#!bjorne exit 0 Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-04-30 12:27:53 UTC (rev 5372) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-04-30 14:11:06 UTC (rev 5373) @@ -1,7 +1,7 @@ <testSet title="Bjorne interpreter tests"> <plugin id="org.jnode.shell.bjorne" class="org.jnode.test.shell.bjorne.BjornePseudoPlugin"/> - <plugin id="org.jnode.shell.command.posix"/> - <plugin id="org.jnode.fs.command"/> + <plugin id="org.jnode.command.common"/> + <plugin id="org.jnode.command.file"/> <testSpec title="simple" command="test" runMode="AS_SCRIPT" rc="0"> <script>#!bjorne echo HI This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-05-11 12:36:14
|
Revision: 5471 http://jnode.svn.sourceforge.net/jnode/?rev=5471&view=rev Author: crawley Date: 2009-05-11 12:36:06 +0000 (Mon, 11 May 2009) Log Message: ----------- Curly brackets in path patterns need to be escaped ... until they are implemented. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java Modified: trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-05-09 12:11:05 UTC (rev 5470) +++ trunk/shell/src/shell/org/jnode/shell/PathnamePattern.java 2009-05-11 12:36:06 UTC (rev 5471) @@ -549,6 +549,8 @@ case '*': case '?': case '$': + case '{': + case '}': case '^': case '\\': return "\\" + ch; Modified: trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-05-09 12:11:05 UTC (rev 5470) +++ trunk/shell/src/test/org/jnode/test/shell/PathnamePatternTest.java 2009-05-11 12:36:06 UTC (rev 5471) @@ -112,6 +112,9 @@ assertEquals("PathnamePattern{source='\"/a/*\"',absolute=true,patterns=['a','^\\*$']}", PathnamePattern.compilePathPattern("\"/a/*\"", DF).toRegexString()); + + assertEquals("PathnamePattern{source='{print \\$1}',absolute=false,patterns=['^\\{print \\$1\\}$']}", + PathnamePattern.compilePathPattern("{print \\$1}", DF).toRegexString()); } public void testExpand() { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <chr...@us...> - 2009-05-12 03:07:41
|
Revision: 5484 http://jnode.svn.sourceforge.net/jnode/?rev=5484&view=rev Author: chrisboertien Date: 2009-05-12 03:07:33 +0000 (Tue, 12 May 2009) Log Message: ----------- compile fix Signed-off-by: chrisboertien <chr...@gm...> Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/syntax/ArgumentSpecLoader.java trunk/shell/src/test/org/jnode/test/shell/syntax/TestSyntaxManager.java Modified: trunk/shell/src/shell/org/jnode/shell/syntax/ArgumentSpecLoader.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/syntax/ArgumentSpecLoader.java 2009-05-12 01:01:18 UTC (rev 5483) +++ trunk/shell/src/shell/org/jnode/shell/syntax/ArgumentSpecLoader.java 2009-05-12 03:07:33 UTC (rev 5484) @@ -207,7 +207,7 @@ } } - static class ArgumentSpec<T extends Argument<?>> { + public static class ArgumentSpec<T extends Argument<?>> { private Constructor<T> ctor; private Object[] params; Modified: trunk/shell/src/test/org/jnode/test/shell/syntax/TestSyntaxManager.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/syntax/TestSyntaxManager.java 2009-05-12 01:01:18 UTC (rev 5483) +++ trunk/shell/src/test/org/jnode/test/shell/syntax/TestSyntaxManager.java 2009-05-12 03:07:33 UTC (rev 5484) @@ -23,6 +23,8 @@ import java.util.Collection; import java.util.HashMap; +import org.jnode.shell.syntax.ArgumentBundle; +import org.jnode.shell.syntax.ArgumentSpecLoader.ArgumentSpec; import org.jnode.shell.syntax.SyntaxBundle; import org.jnode.shell.syntax.SyntaxManager; @@ -36,7 +38,11 @@ public void add(SyntaxBundle bundle) { syntaxes.put(bundle.getAlias(), bundle); } - + + public void add(String alias, ArgumentSpec[] args) { + return; + } + public SyntaxBundle remove(String alias) { return syntaxes.remove(alias); } @@ -44,6 +50,10 @@ public SyntaxBundle getSyntaxBundle(String alias) { return syntaxes.get(alias); } + + public ArgumentBundle getArgumentBundle(String alias) { + return null; + } public SyntaxManager createSyntaxManager() { return new TestSyntaxManager(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-05-27 14:29:43
|
Revision: 5528 http://jnode.svn.sourceforge.net/jnode/?rev=5528&view=rev Author: crawley Date: 2009-05-27 14:29:36 +0000 (Wed, 27 May 2009) Log Message: ----------- Another bjorne completion bug. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneCompleter.java trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneCompleter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneCompleter.java 2009-05-27 13:09:32 UTC (rev 5527) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneCompleter.java 2009-05-27 14:29:36 UTC (rev 5528) @@ -35,8 +35,12 @@ public void complete(CompletionInfo completion, CommandShell shell) throws CompletionException { Logger.getLogger(BjorneCompleter.class).debug(toString()); if (endToken == null) { - new CommandLine(null, null).complete(completion, shell); - return; + if (penultimateToken == null) { + new CommandLine(null, null).complete(completion, shell); + return; + } + endToken = penultimateToken; + endExpectedSet = penultimateExpectedSet; } if (command != null) { BjorneToken[] words = command.getWords(); Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java 2009-05-27 13:09:32 UTC (rev 5527) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java 2009-05-27 14:29:36 UTC (rev 5528) @@ -114,6 +114,18 @@ doCompletionTest("if cpuid\nthen echo hi ; fi", "TTTTETT"); } + public void testIf3Command() throws ShellSyntaxException, CompletionException { + doCompletionTest("if cpuid ; then\necho hi ; fi", "TTTTTETT"); + } + + public void testIf4Command() throws ShellSyntaxException, CompletionException { + doCompletionTest("if cpuid ; then\necho hi\nfi", "TTTTTET"); + } + + public void testWhileCommand() throws ShellSyntaxException, CompletionException { + doCompletionTest("while cpuid ; do echo hi ; done", "TTTTTETT"); + } + private void doCompletionTest(String input, String flags) throws ShellSyntaxException, CompletionException { BjorneInterpreter interpreter = new BjorneInterpreter(); @@ -152,7 +164,9 @@ // Maybe completions, maybe not } for (String completionWord : completionWords) { - assertTrue(completionWord.startsWith(lastWord)); + if (!completionWord.startsWith(lastWord)) { + fail("completion(s) don't start with '" + lastWord + "': " + diag(partial, completions)); + } } } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-05-31 02:13:58
|
Revision: 5531 http://jnode.svn.sourceforge.net/jnode/?rev=5531&view=rev Author: crawley Date: 2009-05-31 02:13:50 +0000 (Sun, 31 May 2009) Log Message: ----------- javadoc and checkstyle fixes Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/ArgumentCompleter.java trunk/shell/src/shell/org/jnode/shell/CommandInfo.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/bjorne/BjorneParser.java trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxManager.java trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java Modified: trunk/shell/src/shell/org/jnode/shell/ArgumentCompleter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/ArgumentCompleter.java 2009-05-31 01:45:21 UTC (rev 5530) +++ trunk/shell/src/shell/org/jnode/shell/ArgumentCompleter.java 2009-05-31 02:13:50 UTC (rev 5531) @@ -22,7 +22,6 @@ import org.apache.log4j.Logger; import org.jnode.driver.console.CompletionInfo; -import org.jnode.shell.bjorne.BjorneCompleter; import org.jnode.shell.syntax.Argument; import org.jnode.shell.syntax.ArgumentBundle; Modified: trunk/shell/src/shell/org/jnode/shell/CommandInfo.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandInfo.java 2009-05-31 01:45:21 UTC (rev 5530) +++ trunk/shell/src/shell/org/jnode/shell/CommandInfo.java 2009-05-31 02:13:50 UTC (rev 5531) @@ -55,7 +55,6 @@ * @param clazz the designated {@code Class} for executing the command * @param commandName the name, or alias, for the command * @param syntaxBundle the syntax definition to parse the command line against - * @param argBundle the optional {@code ArgumentBundle} to parse the command line against */ public CommandInfo(Class<?> clazz, String commandName, SyntaxBundle syntaxBundle, boolean internal) { this.clazz = clazz; @@ -94,7 +93,7 @@ } /** - * Checks wether this command is considered internal or not. + * Checks whether this command is considered internal or not. * * @return true if this is an internal command * @see org.jnode.shell.alias.AliasManager#isInternal Modified: trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java 2009-05-31 01:45:21 UTC (rev 5530) +++ trunk/shell/src/shell/org/jnode/shell/DefaultCommandInvoker.java 2009-05-31 02:13:50 UTC (rev 5531) @@ -43,7 +43,7 @@ /** * This CommandInvoker runs a command in the current thread, using the command * classes <code>public static void main(String[] args)</code> entry point. - * The {@link #invokeAsynchronous(CommandLine, CommandInfo)} method is not + * The {@link #invokeAsynchronous(CommandLine)} method is not * supported for this implementation of the CommandInvoker API. * * @author Sam Reid @@ -76,7 +76,10 @@ } /** - * Invoke the command. + * Invoke the command, running is by calling the entry point method from the + * current thread. No redirection is allowed. + * + * @param cmdLine the command line. */ public int invoke(CommandLine cmdLine) throws ShellException { CommandInfo cmdInfo = cmdLine.parseCommandLine(shell); Modified: trunk/shell/src/shell/org/jnode/shell/Shell.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/Shell.java 2009-05-31 01:45:21 UTC (rev 5530) +++ trunk/shell/src/shell/org/jnode/shell/Shell.java 2009-05-31 02:13:50 UTC (rev 5531) @@ -105,25 +105,25 @@ * Set a shell property. Some properties have special meaning to a Shell * and may cause its behavior to change. * - * @param propName the name of the property + * @param key the name of the property * @param value the property value * @throws ShellException This may be thrown if the name / value pair is * not acceptable. */ - public void setProperty(String propName, String value) throws ShellException; + public void setProperty(String key, String value) throws ShellException; /** * Get the current value of a shell property. * - * @param propName the property name. + * @param key the property name. * @return the property value or {@code null} */ - public String getProperty(String propName); + public String getProperty(String key); /** * Remove a shell property. Special properties typically may not be removed, * - * @param propName the name of the property + * @param key the name of the property * @throws ShellException This may be thrown if the property cannot be removed. */ public void removeProperty(String key) throws ShellException; Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-05-31 01:45:21 UTC (rev 5530) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-05-31 02:13:50 UTC (rev 5531) @@ -612,7 +612,8 @@ CommandNode cond = parseCompoundList(TOK_THEN_BIT); allowLineBreaks(); expectNext(TOK_THEN_BIT, RULE_1_CONTEXT); - return new IfCommandNode(CMD_ELIF, cond, parseCompoundList(TOK_ELIF_BIT | TOK_ELSE_BIT | TOK_FI_BIT), parseOptElsePart()); + return new IfCommandNode(CMD_ELIF, cond, + parseCompoundList(TOK_ELIF_BIT | TOK_ELSE_BIT | TOK_FI_BIT), parseOptElsePart()); } else { return parseCompoundList(TOK_FI_BIT); } Modified: trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxManager.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxManager.java 2009-05-31 01:45:21 UTC (rev 5530) +++ trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxManager.java 2009-05-31 02:13:50 UTC (rev 5531) @@ -25,7 +25,8 @@ /** * A SyntaxManager manages the association between a command "alias" and - * the Syntax that specifies its argument syntax. + * the Syntax that specifies its argument syntax. The manager can also + * record a set of argument specs for non-native commands. * * @author cr...@jn... */ @@ -39,14 +40,23 @@ public static final String SYNTAXES_EP_NAME = "org.jnode.shell.syntaxes"; /** - * Add a syntax bundle + * Add a syntax bundle, using the alias name embedded in the bundle. * * @param bundle The syntax to be added */ public abstract void add(SyntaxBundle bundle); /** - * Remove the syntaxBundle and argumentBundle(if one exists) for an alias + * Add the argument specs for a non-native command; i.e. one which does + * not define and register its own arguments. + * + * @param argSpecs the specs for the arguments + * @param alias the alias + */ + public abstract void add(String alias, ArgumentSpec<?>[] argSpecs); + + /** + * Remove the syntaxBundle and argumentBundle (if one exists) for an alias * * @param alias The alias */ @@ -55,20 +65,12 @@ /** * Gets the syntax bundle for a given alias * - * @param alias The alias + * @param alias the alias * @return The syntax for the given alias, or <code>null</code> */ public abstract SyntaxBundle getSyntaxBundle(String alias); /** - * Add an argument bundle for a bare command. - * - * @param bundle an argument bundle holding the arguments of a bare command - * @param alias the alias to bind the arguments to - */ - public abstract void add(String alias, ArgumentSpec<?>[] args); - - /** * Gets the argument bundle for a given alias if one exists. * * @param alias an alias that corresponds to a particular bundle Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java 2009-05-31 01:45:21 UTC (rev 5530) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java 2009-05-31 02:13:50 UTC (rev 5531) @@ -259,25 +259,30 @@ switch (flags.charAt(inWord)) { case 'T': // Expect completions - assertTrue("got no completions: " + diag(partial, completions), completionWords.size() > 0); + assertTrue("got no completions: " + diag(partial, completions), + completionWords.size() > 0); break; case 'F': // Expect no completions - assertTrue("got unexpected completions: " + diag(partial, completions), completionWords.size() == 0); + assertTrue("got unexpected completions: " + diag(partial, completions), + completionWords.size() == 0); break; case 'E': // Expect completions if the last char is ' ', otherwise not if (wordStart >= partial.length()) { - assertTrue("got no completions: " + diag(partial, completions), completionWords.size() > 0); + assertTrue("got no completions: " + diag(partial, completions), + completionWords.size() > 0); } else { - assertTrue("got unexpected completions: " + diag(partial, completions), completionWords.size() == 0); + assertTrue("got unexpected completions: " + diag(partial, completions), + completionWords.size() == 0); } break; case 'Z': // Expect completions if the last char is NOT ' ' if (wordStart >= partial.length()) { } else { - assertTrue("got no completions: " + diag(partial, completions), completionWords.size() > 0); + assertTrue("got no completions: " + diag(partial, completions), + completionWords.size() > 0); } break; case '?': @@ -285,7 +290,8 @@ } for (String completionWord : completionWords) { if (!completionWord.startsWith(lastWord)) { - fail("completion(s) don't start with '" + lastWord + "': " + diag(partial, completions)); + fail("completion(s) don't start with '" + lastWord + "': " + + diag(partial, completions)); } } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-05-31 04:15:59
|
Revision: 5532 http://jnode.svn.sourceforge.net/jnode/?rev=5532&view=rev Author: crawley Date: 2009-05-31 04:15:41 +0000 (Sun, 31 May 2009) Log Message: ----------- Completion tidyups Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/ArgumentCompleter.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneCompleter.java trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java Modified: trunk/shell/src/shell/org/jnode/shell/ArgumentCompleter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/ArgumentCompleter.java 2009-05-31 02:13:50 UTC (rev 5531) +++ trunk/shell/src/shell/org/jnode/shell/ArgumentCompleter.java 2009-05-31 04:15:41 UTC (rev 5532) @@ -51,7 +51,6 @@ } public void complete(CompletionInfo completion, CommandShell shell) { - Logger.getLogger(ArgumentCompleter.class).debug("text = " + (token == null ? "" : token.text)); argument.complete(completion, token == null ? "" : token.text, 0); if (token != null) { completion.setCompletionStart(token.start); Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneCompleter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneCompleter.java 2009-05-31 02:13:50 UTC (rev 5531) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneCompleter.java 2009-05-31 04:15:41 UTC (rev 5532) @@ -61,11 +61,9 @@ } if (command != null) { BjorneToken[] words = command.getWords(); - if (words.length > 0 && words[words.length - 1] == penultimateToken) { + if (words.length > 1 && words[words.length - 1] == penultimateToken) { boolean argumentAnticipated = penultimateToken.end < endToken.end; command.complete(completion, context, shell, argumentAnticipated); - } else if (words.length == 0) { - new CommandLine(null, null).complete(completion, shell); } } String partial; Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java 2009-05-31 02:13:50 UTC (rev 5531) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneCompletionTests.java 2009-05-31 04:15:41 UTC (rev 5532) @@ -228,7 +228,7 @@ public void testBad2Command() throws ShellSyntaxException, CompletionException { try { - doCompletionTest("if fi ;", "T?"); + doCompletionTest("if fi ;", "T??"); } catch (CompletionException ex) { assertEquals("Cannot find an alias or load a command class for 'fi'", ex.getMessage()); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-06-07 04:58:48
|
Revision: 5543 http://jnode.svn.sourceforge.net/jnode/?rev=5543&view=rev Author: crawley Date: 2009-06-07 04:58:47 +0000 (Sun, 07 Jun 2009) Log Message: ----------- Adding 'eager' forms of the Powerset and Repeat syntaxes. (The latter is not implemented yet.) Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/syntax/OptionalSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/PowersetSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/RepeatSyntax.java trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxSpecLoader.java trunk/shell/src/test/org/jnode/test/shell/syntax/PowersetSyntaxTest.java Modified: trunk/shell/src/shell/org/jnode/shell/syntax/OptionalSyntax.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/syntax/OptionalSyntax.java 2009-06-03 14:47:18 UTC (rev 5542) +++ trunk/shell/src/shell/org/jnode/shell/syntax/OptionalSyntax.java 2009-06-07 04:58:47 UTC (rev 5543) @@ -43,11 +43,11 @@ } public OptionalSyntax(String label, Syntax...syntaxes) { - this(label, null, syntaxes); + this(label, null, false, syntaxes); } public OptionalSyntax(Syntax...syntaxes) { - this(null, null, syntaxes); + this(null, null, false, syntaxes); } @Override Modified: trunk/shell/src/shell/org/jnode/shell/syntax/PowersetSyntax.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/syntax/PowersetSyntax.java 2009-06-03 14:47:18 UTC (rev 5542) +++ trunk/shell/src/shell/org/jnode/shell/syntax/PowersetSyntax.java 2009-06-07 04:58:47 UTC (rev 5543) @@ -28,21 +28,26 @@ * The syntax allows a child syntax to appear more than once, subject to Argument * multiplicity constraints. As with an Alternatives, the child syntaxes are tried * one at a time in the same order as the they were provided to the constructor. + * The 'eager' parameter specifies whether the syntax is 'eager' (i.e. matching as + * many instances as possible) or 'lazy' (i.e. matching as few instances as possible). * * @author cr...@jn... */ public class PowersetSyntax extends GroupSyntax { - public PowersetSyntax(String label, String description, Syntax...syntaxes) { + private final boolean eager; + + public PowersetSyntax(String label, boolean eager, String description, Syntax...syntaxes) { super(label, description, syntaxes); + this.eager = eager; } public PowersetSyntax(String label, Syntax...syntaxes) { - this(label, null, syntaxes); + this(label, false, null, syntaxes); } public PowersetSyntax(Syntax...syntaxes) { - this(null, null, syntaxes); + this(null, false, null, syntaxes); } @Override @@ -58,9 +63,17 @@ childMuSyntaxes[i] = children[i].prepare(bundle); } String label = this.label == null ? MuSyntax.genLabel() : this.label; - MuSyntax res = new MuAlternation(label, null, - new MuSequence(new MuAlternation((String) null, childMuSyntaxes), - new MuBackReference(label))); + MuSyntax res; + if (eager) { + res = new MuAlternation(label, + new MuSequence(new MuAlternation((String) null, childMuSyntaxes), + new MuBackReference(label)), + null); + } else { + res = new MuAlternation(label, null, + new MuSequence(new MuAlternation((String) null, childMuSyntaxes), + new MuBackReference(label))); + } res.resolveBackReferences(); return res; } @@ -89,6 +102,10 @@ @Override public XMLElement toXML() { - return basicElement("powerSet"); + XMLElement element = basicElement("powerSet"); + if (eager) { + element.setAttribute("eager", "true"); + } + return element; } } Modified: trunk/shell/src/shell/org/jnode/shell/syntax/RepeatSyntax.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/syntax/RepeatSyntax.java 2009-06-03 14:47:18 UTC (rev 5542) +++ trunk/shell/src/shell/org/jnode/shell/syntax/RepeatSyntax.java 2009-06-07 04:58:47 UTC (rev 5543) @@ -24,8 +24,11 @@ /** - * A RepeatedSyntax instance specifies that a given 'child' syntax may appear - * some number of times. + * A RepeatedSyntax instance specifies that a given 'child' syntax may be repeated + * a number of times as determined by the constructor arguments. These allow you + * to specify a minimum and/or maximum bound on the number of repetitions, and to + * specify whether the syntax is 'eager' (i.e. matching as many instances as possible) or + * 'lazy' (i.e. matching as few instances as possible). * * @author cr...@jn... */ @@ -34,6 +37,7 @@ private final Syntax child; private final int minCount; private final int maxCount; + private final boolean eager; /** * Construct syntax with caller-specified repetition count range and a label. @@ -42,9 +46,12 @@ * @param child the child Syntax that may be repeated. * @param minCount the minimum number of occurrences required. * @param maxCount the maximum number of occurrences allowed. + * @param eager if {@code true}, the syntax matches as many child instances + * as possible, subject to the 'maxCount' constraint. * @param description the description for this syntax */ - public RepeatSyntax(String label, Syntax child, int minCount, int maxCount, String description) { + public RepeatSyntax(String label, Syntax child, int minCount, int maxCount, + boolean eager, String description) { super(label, description, child); if (minCount < 0 || maxCount < minCount) { throw new IllegalArgumentException("bad min/max counts"); @@ -52,6 +59,7 @@ this.child = child; this.minCount = minCount; this.maxCount = maxCount; + this.eager = eager; } /** @@ -63,7 +71,7 @@ * @param maxCount the maximum number of occurrences allowed. */ public RepeatSyntax(String label, Syntax child, int minCount, int maxCount) { - this(label, child, minCount, maxCount, null); + this(label, child, minCount, maxCount, false, null); } /** @@ -74,7 +82,7 @@ * @param maxCount the maximum number of occurrences allowed. */ public RepeatSyntax(Syntax child, int minCount, int maxCount) { - this(null, child, minCount, maxCount, null); + this(null, child, minCount, maxCount, false, null); } /** @@ -83,7 +91,7 @@ * @param child the child Syntax that may be repeated. */ public RepeatSyntax(Syntax child) { - this(null, child, 0, Integer.MAX_VALUE, null); + this(null, child, 0, Integer.MAX_VALUE, false, null); } @Override @@ -170,6 +178,9 @@ if (maxCount != Integer.MAX_VALUE) { element.setAttribute("maxCount", maxCount); } + if (eager) { + element.setAttribute("eager", "true"); + } return element; } Modified: trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxSpecLoader.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxSpecLoader.java 2009-06-03 14:47:18 UTC (rev 5542) +++ trunk/shell/src/shell/org/jnode/shell/syntax/SyntaxSpecLoader.java 2009-06-07 04:58:47 UTC (rev 5543) @@ -100,13 +100,15 @@ } } else if (kind.equals("powerset")) { int nos = syntaxElement.getNosChildren(); + boolean eager = getFlag(syntaxElement, "eager", false); Syntax[] members = new Syntax[nos]; for (int i = 0; i < nos; i++) { members[i] = doLoad(syntaxElement.getChild(i)); } - return new PowersetSyntax(label, description, members); + return new PowersetSyntax(label, eager, description, members); } else if (kind.equals("repeat")) { int nos = syntaxElement.getNosChildren(); + boolean eager = getFlag(syntaxElement, "eager", false); int minCount = getCount(syntaxElement, "minCount", 0); int maxCount = getCount(syntaxElement, "maxCount", Integer.MAX_VALUE); Syntax[] members = new Syntax[nos]; @@ -114,7 +116,7 @@ members[i] = doLoad(syntaxElement.getChild(i)); } Syntax childSyntax = (members.length == 1) ? members[0] : new SequenceSyntax(members); - return new RepeatSyntax(label, childSyntax, minCount, maxCount, description); + return new RepeatSyntax(label, childSyntax, minCount, maxCount, eager, description); } else if (kind.equals("sequence")) { int nos = syntaxElement.getNosChildren(); Syntax[] seq = new Syntax[nos]; Modified: trunk/shell/src/test/org/jnode/test/shell/syntax/PowersetSyntaxTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/syntax/PowersetSyntaxTest.java 2009-06-03 14:47:18 UTC (rev 5542) +++ trunk/shell/src/test/org/jnode/test/shell/syntax/PowersetSyntaxTest.java 2009-06-07 04:58:47 UTC (rev 5543) @@ -34,6 +34,9 @@ import org.jnode.shell.syntax.IntegerArgument; import org.jnode.shell.syntax.OptionSyntax; import org.jnode.shell.syntax.PowersetSyntax; +import org.jnode.shell.syntax.RepeatSyntax; +import org.jnode.shell.syntax.SequenceSyntax; +import org.jnode.shell.syntax.StringArgument; import org.jnode.shell.syntax.Syntax; public class PowersetSyntaxTest extends TestCase { @@ -43,9 +46,11 @@ new FileArgument("fileArg", Argument.OPTIONAL + Argument.MULTIPLE); private final IntegerArgument intArg = new IntegerArgument("intArg", Argument.OPTIONAL + Argument.MULTIPLE); + private final StringArgument otherArg = + new StringArgument("otherArg", Argument.OPTIONAL + Argument.MULTIPLE); public Test() { - registerArguments(fileArg, intArg); + registerArguments(fileArg, intArg, otherArg); } public void execute() throws Exception { @@ -116,4 +121,129 @@ // expected } } + + public void testLazy() throws Exception { + TestShell shell = new TestShell(); + shell.addAlias("cmd", "org.jnode.test.shell.syntax.PowersetSyntaxTest$Test"); + shell.addSyntax("cmd", new SequenceSyntax( + new PowersetSyntax( + new OptionSyntax("intArg", 'i'), + new OptionSyntax("fileArg", 'f')), + new RepeatSyntax(new OptionSyntax("otherArg", 'i')))); + + CommandLine cl; + CommandInfo cmdInfo; + Command cmd; + + cl = new CommandLine(new Token("cmd"), new Token[]{}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(0, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + + cl = new CommandLine(new Token("cmd"), new Token[]{new Token("-f"), new Token("F1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(1, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + assertEquals("F1", cmd.getArgumentBundle().getArgument("fileArg").getValue().toString()); + + cl = new CommandLine(new Token("cmd"), + new Token[]{new Token("-f"), new Token("F1"), new Token("-i"), new Token("1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(1, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(1, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + assertEquals("F1", cmd.getArgumentBundle().getArgument("fileArg").getValue().toString()); + assertEquals("1", cmd.getArgumentBundle().getArgument("otherArg").getValue().toString()); + + cl = new CommandLine(new Token("cmd"), + new Token[]{new Token("-i"), new Token("1"), new Token("-f"), new Token("F1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(1, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(1, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + assertEquals("F1", cmd.getArgumentBundle().getArgument("fileArg").getValue().toString()); + assertEquals("1", cmd.getArgumentBundle().getArgument("intArg").getValue().toString()); + + cl = new CommandLine(new Token("cmd"), + new Token[]{ + new Token("-i"), new Token("1"), new Token("-f"), new Token("F1"), + new Token("-i"), new Token("2")}, + null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(1, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(1, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(1, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + assertEquals("F1", cmd.getArgumentBundle().getArgument("fileArg").getValue().toString()); + assertEquals("1", cmd.getArgumentBundle().getArgument("intArg").getValue().toString()); + assertEquals("2", cmd.getArgumentBundle().getArgument("otherArg").getValue().toString()); + } + + + + public void testEager() throws Exception { + TestShell shell = new TestShell(); + shell.addAlias("cmd", "org.jnode.test.shell.syntax.PowersetSyntaxTest$Test"); + shell.addSyntax("cmd", new SequenceSyntax( + new PowersetSyntax(null, true, null, + new OptionSyntax("intArg", 'i'), + new OptionSyntax("fileArg", 'f')), + new RepeatSyntax(new OptionSyntax("otherArg", 'i')))); + + CommandLine cl; + CommandInfo cmdInfo; + Command cmd; + + cl = new CommandLine(new Token("cmd"), new Token[]{}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(0, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + + cl = new CommandLine(new Token("cmd"), new Token[]{new Token("-f"), new Token("F1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(1, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + assertEquals("F1", cmd.getArgumentBundle().getArgument("fileArg").getValue().toString()); + + cl = new CommandLine(new Token("cmd"), + new Token[]{new Token("-f"), new Token("F1"), new Token("-i"), new Token("1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(1, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(1, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + assertEquals("F1", cmd.getArgumentBundle().getArgument("fileArg").getValue().toString()); + assertEquals("1", cmd.getArgumentBundle().getArgument("intArg").getValue().toString()); + + cl = new CommandLine(new Token("cmd"), + new Token[]{new Token("-i"), new Token("1"), new Token("-f"), new Token("F1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(1, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(1, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + assertEquals("F1", cmd.getArgumentBundle().getArgument("fileArg").getValue().toString()); + assertEquals("1", cmd.getArgumentBundle().getArgument("intArg").getValue().toString()); + + cl = new CommandLine(new Token("cmd"), + new Token[]{ + new Token("-i"), new Token("1"), new Token("-f"), new Token("F1"), + new Token("-i"), new Token("2")}, + null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(1, cmd.getArgumentBundle().getArgument("fileArg").getValues().length); + assertEquals(2, cmd.getArgumentBundle().getArgument("intArg").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("otherArg").getValues().length); + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cr...@us...> - 2009-06-07 05:49:44
|
Revision: 5545 http://jnode.svn.sourceforge.net/jnode/?rev=5545&view=rev Author: crawley Date: 2009-06-07 05:49:03 +0000 (Sun, 07 Jun 2009) Log Message: ----------- Implemented eager form of RepeatSyntax ( + fixed unit test regression) Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/syntax/RepeatSyntax.java trunk/shell/src/test/org/jnode/test/shell/syntax/RepeatSyntaxTest.java Modified: trunk/shell/src/shell/org/jnode/shell/syntax/RepeatSyntax.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/syntax/RepeatSyntax.java 2009-06-07 05:30:04 UTC (rev 5544) +++ trunk/shell/src/shell/org/jnode/shell/syntax/RepeatSyntax.java 2009-06-07 05:49:03 UTC (rev 5545) @@ -110,16 +110,28 @@ MuSyntax childSyntax = child.prepare(bundle); MuSyntax res, tail; if (maxCount == Integer.MAX_VALUE) { - tail = new MuAlternation(label, - null, - new MuSequence(childSyntax, new MuBackReference(label))); + if (eager) { + tail = new MuAlternation(label, + new MuSequence(childSyntax, new MuBackReference(label)), + null); + } else { + tail = new MuAlternation(label, + null, + new MuSequence(childSyntax, new MuBackReference(label))); + } } else { int tailCount = maxCount - minCount; tail = null; while (tailCount-- > 0) { - tail = new MuAlternation( - (MuSyntax) null, - (tail == null) ? childSyntax : new MuSequence(childSyntax, tail)); + if (eager) { + tail = new MuAlternation( + (tail == null) ? childSyntax : new MuSequence(childSyntax, tail), + null); + } else { + tail = new MuAlternation( + (MuSyntax) null, + (tail == null) ? childSyntax : new MuSequence(childSyntax, tail)); + } } } if (minCount == 0) { Modified: trunk/shell/src/test/org/jnode/test/shell/syntax/RepeatSyntaxTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/syntax/RepeatSyntaxTest.java 2009-06-07 05:30:04 UTC (rev 5544) +++ trunk/shell/src/test/org/jnode/test/shell/syntax/RepeatSyntaxTest.java 2009-06-07 05:49:03 UTC (rev 5545) @@ -32,20 +32,23 @@ import org.jnode.shell.syntax.CommandSyntaxException; import org.jnode.shell.syntax.FileArgument; import org.jnode.shell.syntax.RepeatSyntax; +import org.jnode.shell.syntax.SequenceSyntax; import org.jnode.shell.syntax.Syntax; public class RepeatSyntaxTest extends TestCase { public static class Test extends AbstractCommand { - private final FileArgument arg = + private final FileArgument arg1 = new FileArgument("arg1", Argument.OPTIONAL + Argument.MULTIPLE); + private final FileArgument arg2 = + new FileArgument("arg2", Argument.OPTIONAL + Argument.MULTIPLE); public Test() { - registerArguments(arg); + registerArguments(arg1, arg2); } public void execute() throws Exception { - getOutput().getPrintWriter().print(arg.getValue()); + getOutput().getPrintWriter().print(arg1.getValue()); } } @@ -63,7 +66,7 @@ public void testZeroToMany() throws Exception { TestShell shell = new TestShell(); - shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatedSyntaxTest$Test"); + shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatSyntaxTest$Test"); shell.addSyntax("cmd", new RepeatSyntax(new ArgumentSyntax("arg1"))); CommandLine cl = new CommandLine(new Token("cmd"), new Token[]{}, null); @@ -85,7 +88,7 @@ public void testOneToMany() throws Exception { TestShell shell = new TestShell(); - shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatedSyntaxTest$Test"); + shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatSyntaxTest$Test"); shell.addSyntax("cmd", new RepeatSyntax(new ArgumentSyntax("arg1"), 1, Integer.MAX_VALUE)); CommandLine cl; @@ -113,7 +116,7 @@ public void testOneToTwo() throws Exception { TestShell shell = new TestShell(); - shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatedSyntaxTest$Test"); + shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatSyntaxTest$Test"); shell.addSyntax("cmd", new RepeatSyntax(new ArgumentSyntax("arg1"), 1, 2)); CommandLine cl; @@ -150,7 +153,7 @@ public void testThreeToSix() throws Exception { TestShell shell = new TestShell(); - shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatedSyntaxTest$Test"); + shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatSyntaxTest$Test"); shell.addSyntax("cmd", new RepeatSyntax(new ArgumentSyntax("arg1"), 3, 6)); CommandLine cl; @@ -215,4 +218,56 @@ // expected } } + + public void testLazy() throws Exception { + TestShell shell = new TestShell(); + shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatSyntaxTest$Test"); + shell.addSyntax("cmd", new SequenceSyntax( + new RepeatSyntax(new ArgumentSyntax("arg1")), + new RepeatSyntax(new ArgumentSyntax("arg2")))); + + CommandLine cl = new CommandLine(new Token("cmd"), new Token[]{}, null); + CommandInfo cmdInfo = cl.parseCommandLine(shell); + Command cmd = cmdInfo.createCommandInstance(); + assertEquals(0, cmd.getArgumentBundle().getArgument("arg1").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("arg2").getValues().length); + + cl = new CommandLine(new Token("cmd"), new Token[]{new Token("F1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(0, cmd.getArgumentBundle().getArgument("arg1").getValues().length); + assertEquals(1, cmd.getArgumentBundle().getArgument("arg2").getValues().length); + + cl = new CommandLine(new Token("cmd"), new Token[]{new Token("F1"), new Token("F1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(0, cmd.getArgumentBundle().getArgument("arg1").getValues().length); + assertEquals(2, cmd.getArgumentBundle().getArgument("arg2").getValues().length); + } + + public void testEager() throws Exception { + TestShell shell = new TestShell(); + shell.addAlias("cmd", "org.jnode.test.shell.syntax.RepeatSyntaxTest$Test"); + shell.addSyntax("cmd", new SequenceSyntax( + new RepeatSyntax(null, new ArgumentSyntax("arg1"), 0, Integer.MAX_VALUE, true, null), + new RepeatSyntax(new ArgumentSyntax("arg2")))); + + CommandLine cl = new CommandLine(new Token("cmd"), new Token[]{}, null); + CommandInfo cmdInfo = cl.parseCommandLine(shell); + Command cmd = cmdInfo.createCommandInstance(); + assertEquals(0, cmd.getArgumentBundle().getArgument("arg1").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("arg2").getValues().length); + + cl = new CommandLine(new Token("cmd"), new Token[]{new Token("F1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(1, cmd.getArgumentBundle().getArgument("arg1").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("arg2").getValues().length); + + cl = new CommandLine(new Token("cmd"), new Token[]{new Token("F1"), new Token("F1")}, null); + cmdInfo = cl.parseCommandLine(shell); + cmd = cmdInfo.createCommandInstance(); + assertEquals(2, cmd.getArgumentBundle().getArgument("arg1").getValues().length); + assertEquals(0, cmd.getArgumentBundle().getArgument("arg2").getValues().length); + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |