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 craw...@jnode.org
+ */
+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 &lt;&lt;EOF
+1 2 3
+EOF
+                read A B C &lt;&lt;EOF 
+1 2 3
+EOF
+                echo +$A+ +$B+ +$C+
+                read A B C D &lt;&lt;EOF 
+ 1 2 3 
+EOF
+                echo +$A+ +$B+ +$C+ +$D+
+                read A B &lt;&lt;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.

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
Jnode-svn-commits mailing list
Jnode-svn-commits@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jnode-svn-commits

Reply via email to