|
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.
|