|
From: <cr...@us...> - 2009-08-01 16:04:18
|
Revision: 5626
http://jnode.svn.sourceforge.net/jnode/?rev=5626&view=rev
Author: crawley
Date: 2009-08-01 16:04:05 +0000 (Sat, 01 Aug 2009)
Log Message:
-----------
Implemented the bjorne 'read' 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
Added Paths:
-----------
trunk/shell/src/shell/org/jnode/shell/bjorne/ReadBuiltin.java
Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-07-30 14:06:07 UTC (rev 5625)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-08-01 16:04:05 UTC (rev 5626)
@@ -156,6 +156,7 @@
setVariable("PS1", "$ ");
setVariable("PS2", "> ");
setVariable("PS4", "+ ");
+ setVariable("IFS", " \t\n");
}
/**
@@ -568,7 +569,7 @@
* @param wordTokens the destination for the tokens.
* @throws ShellException
*/
- private void splitAndAppend(BjorneToken token, List<BjorneToken> wordTokens)
+ void splitAndAppend(BjorneToken token, List<BjorneToken> wordTokens)
throws ShellException {
String text = token.getText();
StringBuffer sb = null;
Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-07-30 14:06:07 UTC (rev 5625)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-08-01 16:04:05 UTC (rev 5626)
@@ -137,6 +137,7 @@
BUILTINS.put("continue", ContinueBuiltin.FACTORY);
BUILTINS.put("exit", ExitBuiltin.FACTORY);
BUILTINS.put("export", ExportBuiltin.FACTORY);
+ BUILTINS.put("read", ReadBuiltin.FACTORY);
BUILTINS.put("readonly", ReadonlyBuiltin.FACTORY);
BUILTINS.put("return", ReturnBuiltin.FACTORY);
BUILTINS.put("set", SetBuiltin.FACTORY);
@@ -153,6 +154,8 @@
private BjorneContext context;
private BjorneParser parser;
+
+ private Reader reader;
public BjorneInterpreter() {
this.context = new BjorneContext(this);
@@ -210,13 +213,15 @@
myContext.setIO(1, new CommandOutput(capture), true);
}
BjorneTokenizer tokens = new BjorneTokenizer(reader);
- // (Save the current parser value in the case where we are called
+ // (Save the current parser and reader objects in the case where we are called
// recursively ... to interpret a back-tick command.)
- BjorneParser saved = parser;
+ BjorneParser savedParser = this.parser;
+ Reader savedReader = this.reader;
+ this.reader = reader;
parser = new BjorneParser(tokens);
try {
do {
- CommandNode tree = parser.parse();
+ CommandNode tree = this.parser.parse();
if (tree == null) {
break;
}
@@ -227,7 +232,8 @@
} while (script);
return myContext.getLastReturnCode();
} finally {
- parser = saved;
+ this.parser = savedParser;
+ this.reader = savedReader;
}
}
@@ -288,6 +294,10 @@
public boolean supportsMultiline() {
return true;
}
+
+ Reader getReader() {
+ return this.reader;
+ }
private void bindShell(CommandShell shell) {
if (this.shell != shell) {
Added: trunk/shell/src/shell/org/jnode/shell/bjorne/ReadBuiltin.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/ReadBuiltin.java (rev 0)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/ReadBuiltin.java 2009-08-01 16:04:05 UTC (rev 5626)
@@ -0,0 +1,212 @@
+/*
+ * $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.io.Reader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jnode.shell.ShellSyntaxException;
+import org.jnode.shell.syntax.Argument;
+import org.jnode.shell.syntax.ArgumentSyntax;
+import org.jnode.shell.syntax.FlagArgument;
+import org.jnode.shell.syntax.OptionSyntax;
+import org.jnode.shell.syntax.OptionalSyntax;
+import org.jnode.shell.syntax.RepeatSyntax;
+import org.jnode.shell.syntax.SequenceSyntax;
+import org.jnode.shell.syntax.SyntaxBundle;
+
+/**
+ * This class implements the 'read' built-in.
+ *
+ * @author cr...@jn...
+ */
+final class ReadBuiltin extends BjorneBuiltin {
+ private static final SyntaxBundle SYNTAX =
+ new SyntaxBundle("read", new SequenceSyntax(
+ new OptionalSyntax(new OptionSyntax("noEscape", 'r')),
+ new RepeatSyntax(new ArgumentSyntax("varName"), 0, Integer.MAX_VALUE)));
+
+ static final Factory FACTORY = new Factory() {
+ public BjorneBuiltinCommandInfo buildCommandInfo(BjorneContext context) {
+ return new BjorneBuiltinCommandInfo("read", SYNTAX, new ReadBuiltin(context), context);
+ }
+ };
+
+
+ private final VariableNameArgument varNameArg;
+ private final FlagArgument noEscapeArg = new FlagArgument(
+ "noEscape", Argument.OPTIONAL, "if set, '\' does not escape a newline");
+
+ private final BjorneContext context;
+ Pattern ifsSplittingPattern;
+ Pattern ifsTrimmingPattern;
+
+ ReadBuiltin(BjorneContext context) {
+ super("Read a line of input and repopulate the shell 'args'");
+ this.context = context;
+ varNameArg = new VariableNameArgument(
+ "varName", context, Argument.OPTIONAL | Argument.MULTIPLE, "shell variables to be set");
+ registerArguments(noEscapeArg, varNameArg);
+ }
+
+ public void execute() throws Exception {
+ boolean escapeCheck = !noEscapeArg.isSet();
+ String line = readLine(getInput().getReader(), escapeCheck);
+ String[] varNames = varNameArg.getValues();
+ if (varNames.length > 0) {
+ String[] fields = extractFields(line, varNames.length);
+ for (int i = 0; i < varNames.length; i++) {
+ String value = (i >= fields.length || fields[i] == null) ? "" : fields[i];
+ context.getParent().setVariable(varNames[i], value);
+ }
+ }
+ }
+
+ private String[] extractFields(String line, int nosVars) throws ShellSyntaxException {
+ String ifs = context.variable("IFS");
+ if (ifs == null) {
+ ifs = " \t\n";
+ } else if (ifs.length() == 0) {
+ return new String[]{line};
+ }
+
+ String[] fields = new String[nosVars];
+ createIfsPatterns(ifs);
+ String content;
+ if (ifsTrimmingPattern != null) {
+ Matcher trimMatcher = ifsTrimmingPattern.matcher(line);
+ trimMatcher.matches();
+ content = trimMatcher.group(1);
+ } else {
+ content = line;
+ }
+ if (line.length() == 0) {
+ return new String[0];
+ }
+ Matcher fieldMatcher = null;
+ for (int i = 0; i < fields.length - 1; i++) {
+ if (fieldMatcher == null) {
+ fieldMatcher = ifsSplittingPattern.matcher(content);
+ } else {
+ fieldMatcher.reset(content);
+ }
+ if (fieldMatcher.matches()) {
+ fields[i] = fieldMatcher.group(1);
+ content = fieldMatcher.group(2);
+ } else {
+ fields[i] = content;
+ content = null;
+ break;
+ }
+ }
+ if (content != null) {
+ fields[fields.length - 1] = content;
+ }
+ return fields;
+ }
+
+ private void createIfsPatterns(String ifs) {
+ if (ifs.equals(" ") || ifs.equals("\t") || ifs.equals("\n")) {
+ ifsTrimmingPattern = Pattern.compile("[ \\t\\n]*(.*[^ \\t\\n])[ \\t\\n]*");
+ ifsSplittingPattern = Pattern.compile("([^ \\t\\n]+)[ \\t\\n]+([^ \\t\\n].*)");
+ } else {
+ // First separate the IFS into whitespace and non-whitespace characters,
+ // adding '\' escapes for any that characters that need to be escaped.
+ StringBuilder sb1 = new StringBuilder(4);
+ StringBuilder sb2 = new StringBuilder(4);
+ for (char ch : ifs.toCharArray()) {
+ switch (ch) {
+ case ' ':
+ sb1.append(' ');
+ break;
+ case '\t':
+ sb1.append("\\t");
+ break;
+ case '\n':
+ sb1.append("\\n");
+ break;
+ case '.':
+ case '?':
+ case '*':
+ case '+':
+ case '[':
+ case ']':
+ case '(':
+ case ')':
+ case '|':
+ case '{':
+ case '}':
+ case '\\':
+ case '^':
+ case '$':
+ case '-':
+ sb2.append('\\').append(ch);
+ break;
+ default:
+ sb2.append("\\\n");
+ break;
+ }
+ }
+ String ifsWhitespace = sb1.toString();
+ String ifsNonWhitespace = sb2.toString();
+ // If we have any IFS whitespace, create the pattern to trim it.
+ if (ifsWhitespace.length() == 0) {
+ ifsTrimmingPattern = null;
+ } else {
+ ifsTrimmingPattern = Pattern.compile(
+ "[" + ifsWhitespace + "]*(.*[^" + ifsWhitespace + "])[" + ifsWhitespace + "]*");
+ }
+ // Create the pattern to split a (possibly empty) field
+ if (ifsWhitespace.length() > 0 && ifsNonWhitespace.length() > 0) {
+ ifsSplittingPattern = Pattern.compile(
+ "([^" + ifsWhitespace + ifsNonWhitespace + "]*)[" + ifsWhitespace + "]*[" +
+ ifsNonWhitespace + "][" + ifsWhitespace + "]*(|[^" + ifsWhitespace + "].*)");
+ } else if (ifsWhitespace.length() > 0) {
+ ifsSplittingPattern = Pattern.compile(
+ "([^" + ifsWhitespace +"]*)[" + ifsWhitespace + "]+(|[^" + ifsWhitespace + "].*)");
+ } else {
+ ifsSplittingPattern = Pattern.compile(
+ "([^" + ifsNonWhitespace + "]*)[" + ifsNonWhitespace + "](.*)");
+ }
+ }
+ }
+
+ private String readLine(Reader reader, boolean escapeCheck) throws IOException {
+ StringBuilder sb = new StringBuilder(40);
+ int ch;
+ while ((ch = reader.read()) != -1 && ch != '\n') {
+ if (ch == '\\' && escapeCheck) {
+ ch = reader.read();
+ if (ch == -1) {
+ sb.append('\\');
+ break;
+ } else if (ch != '\n') {
+ sb.append('\\').append((char) ch);
+ }
+ } else {
+ sb.append((char) ch);
+ }
+ }
+ return sb.toString();
+ }
+}
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-07-30 14:06:07 UTC (rev 5625)
+++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-builtin-tests.xml 2009-08-01 16:04:05 UTC (rev 5626)
@@ -128,4 +128,27 @@
A --
</output>
</testSpec>
+ <testSpec title="read" runMode="AS_SCRIPT" rc="0">
+ <script>#!bjorne
+ read <<EOF
+1 2 3
+EOF
+ read A B C <<EOF
+1 2 3
+EOF
+ echo +$A+ +$B+ +$C+
+ read A B C D <<EOF
+ 1 2 3
+EOF
+ echo +$A+ +$B+ +$C+ +$D+
+ read A B <<EOF
+ 1 2 3
+EOF
+ echo +$A+ +$B+
+ </script>
+ <output>+1+ +2+ +3+
++1+ +2+ +3+ ++
++1+ +2 3+
+</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.
|