From: <cr...@us...> - 2009-02-04 13:19:45
|
Revision: 4997 http://jnode.svn.sourceforge.net/jnode/?rev=4997&view=rev Author: crawley Date: 2009-02-04 13:19:41 +0000 (Wed, 04 Feb 2009) Log Message: ----------- Added hooks to shell for multi-line commands (with continuation prompts). Implemented hooks in bjorne parser, and fixed various latent bugs with handling of newlines. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java trunk/shell/src/shell/org/jnode/shell/CommandShell.java trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTests.java trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml Added Paths: ----------- trunk/shell/src/shell/org/jnode/shell/IncompleteCommandException.java Modified: trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java 2009-02-02 19:44:07 UTC (rev 4996) +++ trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java 2009-02-04 13:19:41 UTC (rev 4997) @@ -113,4 +113,18 @@ * @throws ShellException */ boolean help(CommandShell shell, String partial, PrintWriter pw) throws ShellException; + + /** + * This method should <code>true</code> if the interpreter supports continuation lines. If so, + * it should throw IncompleteCommandException if it is expecting more input from the + * user. The shell will respond by reading the next line from the user, appending it + * to the previous input, and attempting to interpret the line again. Obviously, the + * interpreter needs to be side-effect free prior to throwing the exception. + * <p> + * If this method returns <code>false</code>, the interpreter will treat IncompleteCommandException + * as a regular ShellSyntaxException. + * + * @return <code>true</code> if this interpreter supports continuation lines. + */ + boolean supportsMultilineCommands(); } Modified: trunk/shell/src/shell/org/jnode/shell/CommandShell.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2009-02-02 19:44:07 UTC (rev 4996) +++ trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2009-02-04 13:19:41 UTC (rev 4997) @@ -335,9 +335,32 @@ clearEof(); outPW.print(prompt()); readingCommand = true; - String line = readInputLine().trim(); + String line = readInputLine(); if (line.length() > 0) { - runCommand(line, true, this.interpreter); + boolean done = false; + do { + try { + runCommand(line, true, this.interpreter); + done = true; + } catch (IncompleteCommandException ex) { + String continuation = null; + if (this.interpreter.supportsMultilineCommands()) { + String prompt = ex.getPrompt(); + if (prompt != null) { + outPW.print(prompt); + } + continuation = readInputLine(); + } + if (continuation == null) { + diagnose(ex); + break; + } else { + line = line + "\n" + continuation; + } + } catch (ShellException ex) { + diagnose(ex); + } + } while (!done); } if (VmSystem.isShuttingDown()) { @@ -351,6 +374,47 @@ } } + private void diagnose(ShellException ex) { + Throwable cause = ex.getCause(); + // Try to turn this into something that is moderately intelligible + // for the common cases ... + if (cause != null) { + errPW.println(ex.getMessage()); + if (cause instanceof CommandSyntaxException) { + List<Context> argErrors = ((CommandSyntaxException) cause).getArgErrors(); + if (argErrors != null) { + // The parser can produce many errors as each of the alternatives + // in the tree are explored. The following assumes that errors + // produced when we get farthest along in the token stream are most + // likely to be the "real" errors. + int rightmostPos = 0; + for (Context context : argErrors) { + if (context.sourcePos > rightmostPos) { + rightmostPos = context.sourcePos; + } + } + for (Context context : argErrors) { + if (context.sourcePos < rightmostPos) { + continue; + } + if (context.token != null) { + errPW.println(" " + context.exception.getMessage() + ": " + + context.token.text); + } else { + errPW.println(" " + context.exception.getMessage() + ": " + + context.syntax.format()); + } + } + } + } else { + errPW.println(cause.getMessage()); + } + } else { + errPW.println("Shell exception: " + ex.getMessage()); + } + stackTrace(ex); + } + public void configureShell() { try { ShellUtils.getShellManager().registerShell(this); @@ -465,7 +529,7 @@ } private int runCommand(String cmdLineStr, boolean interactive, - CommandInterpreter interpreter) { + CommandInterpreter interpreter) throws ShellException { if (interactive) { clearEof(); readingCommand = false; @@ -473,50 +537,7 @@ // for input completion applicationHistory.set(new InputHistory()); } - int rc = 0; - try { - rc = interpreter.interpret(this, cmdLineStr); - } catch (ShellException ex) { - Throwable cause = ex.getCause(); - // Try to turn this into something that is moderately intelligible - // for the common cases ... - if (cause != null) { - errPW.println(ex.getMessage()); - if (cause instanceof CommandSyntaxException) { - List<Context> argErrors = ((CommandSyntaxException) cause).getArgErrors(); - if (argErrors != null) { - // The parser can produce many errors as each of the alternatives - // in the tree are explored. The following assumes that errors - // produced when we get farthest along in the token stream are most - // likely to be the "real" errors. - int rightmostPos = 0; - for (Context context : argErrors) { - if (context.sourcePos > rightmostPos) { - rightmostPos = context.sourcePos; - } - } - for (Context context : argErrors) { - if (context.sourcePos < rightmostPos) { - continue; - } - if (context.token != null) { - errPW.println(" " + context.exception.getMessage() + ": " + - context.token.text); - } else { - errPW.println(" " + context.exception.getMessage() + ": " + - context.syntax.format()); - } - } - } - } else { - errPW.println(cause.getMessage()); - } - } else { - errPW.println("Shell exception: " + ex.getMessage()); - } - rc = -1; - stackTrace(ex); - } + int rc = interpreter.interpret(this, cmdLineStr); if (interactive) { applicationHistory.set(null); Modified: trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java 2009-02-02 19:44:07 UTC (rev 4996) +++ trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java 2009-02-04 13:19:41 UTC (rev 4997) @@ -139,6 +139,11 @@ } @Override + public boolean supportsMultilineCommands() { + return false; + } + + @Override public int interpret(CommandShell shell, File file) throws ShellException { try { return interpret(shell, new FileReader(file)); Added: trunk/shell/src/shell/org/jnode/shell/IncompleteCommandException.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/IncompleteCommandException.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/IncompleteCommandException.java 2009-02-04 13:19:41 UTC (rev 4997) @@ -0,0 +1,36 @@ +/* + * $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; + +public class IncompleteCommandException extends ShellSyntaxException { + + private static final long serialVersionUID = 3710602404013731870L; + + private final String prompt; + + public IncompleteCommandException(String msg, String prompt) { + super(msg); + this.prompt = prompt; + } + + public String getPrompt() { + return prompt; + } +} Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-02-02 19:44:07 UTC (rev 4996) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-02-04 13:19:41 UTC (rev 4997) @@ -49,6 +49,7 @@ 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; import org.jnode.shell.ShellFailureException; import org.jnode.shell.ShellSyntaxException; @@ -188,7 +189,7 @@ public Completable parsePartial(CommandShell shell, String partial) throws ShellSyntaxException { bindShell(shell); BjorneTokenizer tokens = new BjorneTokenizer(partial); - final CommandNode tree = new BjorneParser(tokens).parse(); + final CommandNode tree = new BjorneParser(tokens, "> ").parse(); if (tree instanceof BjorneCompletable) { return new Completable() { @Override @@ -228,7 +229,7 @@ myContext.setStream(1, new CommandOutput(capture), true); } BjorneTokenizer tokens = new BjorneTokenizer(command); - CommandNode tree = new BjorneParser(tokens).parse(); + CommandNode tree = new BjorneParser(tokens, "> ").parse(); if (tree == null) { // An empty command line return 0; @@ -242,6 +243,11 @@ } return tree.execute((BjorneContext) myContext); } + + @Override + public boolean supportsMultilineCommands() { + return true; + } @Override public int interpret(CommandShell shell, Reader reader) throws ShellException { @@ -251,24 +257,34 @@ String line; int rc = 0; while ((line = br.readLine()) != null) { - try { - rc = interpret(shell, line, null, false); - } catch (BjorneControlException ex) { - switch (ex.getControl()) { + boolean done = false; + do { + try { + rc = interpret(shell, line, null, false); + done = true; + } catch (BjorneControlException ex) { + switch (ex.getControl()) { case BjorneInterpreter.BRANCH_EXIT: // The script will exit immediately return ex.getCount(); case BjorneInterpreter.BRANCH_BREAK: throw new ShellSyntaxException( - "'break' has been executed in an inappropriate context"); + "'break' has been executed in an inappropriate context"); case BjorneInterpreter.BRANCH_CONTINUE: throw new ShellSyntaxException( - "'continue' has been executed in an inappropriate context"); + "'continue' has been executed in an inappropriate context"); case BjorneInterpreter.BRANCH_RETURN: throw new ShellSyntaxException( - "'return' has been executed in an inappropriate context"); + "'return' has been executed in an inappropriate context"); + } + } catch (IncompleteCommandException ex) { + String continuation = br.readLine(); + if (continuation == null) { + throw ex; + } + line = line + "\n" + continuation; } - } + } while (!done); } return rc; } catch (IOException ex) { Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-02-02 19:44:07 UTC (rev 4996) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-02-04 13:19:41 UTC (rev 4997) @@ -82,14 +82,18 @@ import java.util.LinkedList; import java.util.List; +import org.jnode.shell.IncompleteCommandException; import org.jnode.shell.ShellFailureException; import org.jnode.shell.ShellSyntaxException; public class BjorneParser { private final BjorneTokenizer tokens; + + private final String continuationPrompt; - public BjorneParser(BjorneTokenizer tokens) { + public BjorneParser(BjorneTokenizer tokens, String continuationPrompt) { this.tokens = tokens; + this.continuationPrompt = continuationPrompt; } /** @@ -174,6 +178,7 @@ } private CommandNode parseOptAndOr() throws ShellSyntaxException { + skipLineBreaks(); switch (tokens.peek(RULE_1_CONTEXT).getTokenType()) { case TOK_LBRACE: case TOK_LPAREN: @@ -349,9 +354,9 @@ return null; } tokens.next(); - if (tokens.next().getTokenType() != TOK_RPAREN) { - throw new ShellSyntaxException( - "expected matching ')' in function_definition"); + int tt = tokens.next().getTokenType(); + if (tt != TOK_RPAREN) { + syntaxError("expected matching ')' in function_definition", tt); } skipLineBreaks(); return new FunctionDefinitionNode(fname, parseFunctionBody()); @@ -412,8 +417,8 @@ io = token; token = tokens.next(); } - int tokenType = token.getTokenType(); - switch (tokenType) { + int tt = token.getTokenType(); + switch (tt) { case TOK_LESS: case TOK_GREAT: case TOK_DGREAT: @@ -422,15 +427,17 @@ case TOK_LESSGREAT: case TOK_CLOBBER: arg = tokens.next(); - if (arg.getTokenType() != TOK_WORD) { - throw new ShellSyntaxException("expected a filename after " + token); + tt = arg.getTokenType(); + if (tt != TOK_WORD) { + syntaxError("expected a filename after " + token, tt); } break; case TOK_DLESS: case TOK_DLESSDASH: arg = tokens.next(); + tt = arg.getTokenType(); if (arg.getTokenType() != TOK_WORD) { - throw new ShellSyntaxException("expected a here-end marker " + token); + syntaxError("expected a here-end marker " + token, tt); } // TODO ... need to grab the HERE document ... break; @@ -439,7 +446,7 @@ } // (The corresponding token type and redirection type values are the // same ...) - return new RedirectionNode(tokenType, io, arg); + return new RedirectionNode(tt, io, arg); } private RedirectionNode[] parseOptRedirects() throws ShellSyntaxException { @@ -472,8 +479,9 @@ private CommandNode parseSubshell() throws ShellSyntaxException { tokens.next(); CommandNode compoundList = parseCompoundList(); - if (tokens.next().getTokenType() != TOK_RPAREN) { - throw new ShellSyntaxException("expected ')'"); + int tt = tokens.next().getTokenType(); + if (tt != TOK_RPAREN) { + syntaxError("expected ')'", tt); } compoundList.setNodeType(CMD_SUBSHELL); return compoundList; @@ -486,12 +494,14 @@ LOOP: while (command != null) { commands.add(command); - skipLineBreaks(); switch (tokens.peek().getTokenType()) { case TOK_SEMI: break; + case TOK_END_OF_LINE: + break; case TOK_AMP: command.setFlag(FLAG_ASYNC); + break; default: break LOOP; } @@ -504,8 +514,9 @@ private CommandNode parseBraceGroup() throws ShellSyntaxException { tokens.next(); CommandNode compoundList = parseCompoundList(); - if (tokens.peek().getTokenType() != TOK_RBRACE) { - throw new ShellSyntaxException("expected '}'"); + int tt = tokens.peek().getTokenType(); + if (tt != TOK_RBRACE) { + syntaxError("expected '}'", tt); } compoundList.setNodeType(CMD_BRACE_GROUP); return compoundList; @@ -516,8 +527,9 @@ BjorneToken word = tokens.next(); List<CaseItemNode> caseItems = new LinkedList<CaseItemNode>(); skipLineBreaks(); - if (tokens.next(RULE_6_CONTEXT).getTokenType() != TOK_IN) { - throw new ShellSyntaxException("expected 'in' in case_clause"); + int tt = tokens.next(RULE_6_CONTEXT).getTokenType(); + if (tt != TOK_IN) { + syntaxError("expected 'in' in case_clause", tt); } skipLineBreaks(); BjorneToken token = tokens.peek(RULE_1_CONTEXT); @@ -525,13 +537,13 @@ caseItems.add(parseCaseItem()); skipLineBreaks(); token = tokens.peek(RULE_1_CONTEXT); - if (token.getTokenType() == TOK_DSEMI) { + tt = token.getTokenType(); + if (tt == TOK_DSEMI) { tokens.next(); skipLineBreaks(); token = tokens.peek(RULE_1_CONTEXT); - } else if (token.getTokenType() != TOK_ESAC) { - throw new ShellSyntaxException( - "expected ';;' or 'esac' after case_item"); + } else if (tt != TOK_ESAC) { + syntaxError("expected ';;' or 'esac' after case_item", tt); } } tokens.next(); @@ -548,9 +560,9 @@ token = tokens.next(); } BjorneToken[] pattern = parsePattern(); - if (tokens.next().getTokenType() != TOK_RPAREN) { - throw new ShellSyntaxException( - "expected ')' after pattern in case_item"); + int tt = tokens.next().getTokenType(); + if (tt != TOK_RPAREN) { + syntaxError("expected ')' after pattern in case_item", tt); } CommandNode body = null; skipLineBreaks(); @@ -567,9 +579,9 @@ private BjorneToken[] parsePattern() throws ShellSyntaxException { List<BjorneToken> pattern = new LinkedList<BjorneToken>(); while (true) { - BjorneToken token = tokens.next(); - if (token.getTokenType() != TOK_WORD) { - throw new ShellSyntaxException("expected WORD in pattern"); + int tt = tokens.next().getTokenType(); + if (tt != TOK_WORD) { + syntaxError("expected WORD in pattern", tt); } if (tokens.peek().getTokenType() != TOK_BAR) { break; @@ -582,8 +594,9 @@ private ForCommandNode parseForCommand() throws ShellSyntaxException { tokens.next(); BjorneToken var = tokens.next(RULE_5_CONTEXT); - if (var.getTokenType() != TOK_NAME) { - throw new ShellSyntaxException("expected a NAME following 'for'"); + int tt = var.getTokenType(); + if (tt != TOK_NAME) { + syntaxError("expected a NAME following 'for'", tt); } skipLineBreaks(); List<BjorneToken> words = new LinkedList<BjorneToken>(); @@ -596,10 +609,10 @@ word = tokens.peek(); } if (words.isEmpty()) { - throw new ShellSyntaxException( - "expected a wordlist following 'in'"); + syntaxError("expected a wordlist following 'in'", word.getTokenType()); } - switch (tokens.peek().getTokenType()) { + tt = tokens.peek().getTokenType(); + switch (tt) { case TOK_SEMI: tokens.next(); skipLineBreaks(); @@ -608,7 +621,7 @@ skipLineBreaks(); break; default: - throw new ShellSyntaxException("expected a ';' following wordlist"); + syntaxError("expected a ';' following wordlist", tt); } } return new ForCommandNode(var, @@ -616,13 +629,16 @@ } private CommandNode parseDoGroup() throws ShellSyntaxException { - BjorneToken token = tokens.next(RULE_1_CONTEXT); - if (token.getTokenType() != TOK_DO) { - throw new ShellSyntaxException("expected the 'do' of a do_group"); + skipLineBreaks(); + int tt = tokens.next(RULE_1_CONTEXT).getTokenType(); + if (tt != TOK_DO) { + syntaxError("expected the 'do' of a do_group", tt); } CommandNode body = parseCompoundList(); - if (tokens.next(RULE_1_CONTEXT).getTokenType() != TOK_DONE) { - throw new ShellSyntaxException("expected a command or 'done'"); + skipLineBreaks(); + tt = tokens.next(RULE_1_CONTEXT).getTokenType(); + if (tt != TOK_DONE) { + syntaxError("expected a command or 'done'", tt); } return body; } @@ -644,23 +660,30 @@ private IfCommandNode parseIfCommand() throws ShellSyntaxException { tokens.next(); CommandNode cond = parseCompoundList(); - if (tokens.next(RULE_1_CONTEXT).getTokenType() != TOK_THEN) { - throw new ShellSyntaxException("expected a 'then' in if_clause"); + skipLineBreaks(); + 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(); - if (tokens.next(RULE_1_CONTEXT).getTokenType() != TOK_FI) { - throw new ShellSyntaxException("expected an 'elif', 'else' or 'fi'"); + skipLineBreaks(); + tt = tokens.next(RULE_1_CONTEXT).getTokenType(); + if (tt != TOK_FI) { + syntaxError("expected an 'elif', 'else' or 'fi'", tt); } return new IfCommandNode(CMD_IF, cond, thenPart, elsePart); } private CommandNode parseOptElsePart() throws ShellSyntaxException { + skipLineBreaks(); switch (tokens.next(RULE_1_CONTEXT).getTokenType()) { case TOK_ELIF: CommandNode cond = parseCompoundList(); - if (tokens.next(RULE_1_CONTEXT).getTokenType() != TOK_THEN) { - throw new ShellSyntaxException("expected a 'then' in else_part"); + skipLineBreaks(); + int tt = tokens.next(RULE_1_CONTEXT).getTokenType(); + if (tt != TOK_THEN) { + syntaxError("expected a 'then' in else_part", tt); } return new IfCommandNode(CMD_ELIF, cond, parseCompoundList(), parseOptElsePart()); @@ -671,6 +694,15 @@ return null; } } + + private void syntaxError(String msg, int tt) throws ShellSyntaxException { + if (tt == TOK_END_OF_STREAM) { + throw new IncompleteCommandException(msg, continuationPrompt); + } else { + System.err.println("tt is " + tt); + throw new ShellSyntaxException(msg); + } + } private void skipLineBreaks() { while (tokens.peek().getTokenType() == TOK_END_OF_LINE) { Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTests.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTests.java 2009-02-02 19:44:07 UTC (rev 4996) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTests.java 2009-02-04 13:19:41 UTC (rev 4997) @@ -31,7 +31,7 @@ private static final boolean DEBUG = true; public void testParser() { - new BjorneParser(new BjorneTokenizer("")); + new BjorneParser(new BjorneTokenizer(""), null); } public void test1() throws ShellException { @@ -147,7 +147,7 @@ } private String doTest(String input) throws ShellException { - BjorneParser p = new BjorneParser(new BjorneTokenizer(input, DEBUG)); + BjorneParser p = new BjorneParser(new BjorneTokenizer(input, DEBUG), null); String res = p.parse().toString(); if (DEBUG) { System.err.println(res); 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-02 19:44:07 UTC (rev 4996) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-02-04 13:19:41 UTC (rev 4997) @@ -63,6 +63,19 @@ <rc>0</rc> </testSpec> <testSpec> + <title>if ... then ... fi multi-line</title> + <command>test</command> + <runMode>AS_SCRIPT</runMode> + <script>#!bjorne +if true +then echo HI +fi + </script> + <output>HI +</output> + <rc>0</rc> + </testSpec> + <testSpec> <title>if ... then ... else ... fi</title> <command>test</command> <runMode>AS_SCRIPT</runMode> @@ -76,6 +89,25 @@ <rc>0</rc> </testSpec> <testSpec> + <title>if ... then ... else ... fi multi-line</title> + <command>test</command> + <runMode>AS_SCRIPT</runMode> + <script>#!bjorne +if true ; +then echo HI ; +else echo HO; +fi +if false +then echo HI +else echo HO +fi + </script> + <output>HI +HO +</output> + <rc>0</rc> + </testSpec> + <testSpec> <title>if ... then ... elif ... else ... fi</title> <command>test</command> <runMode>AS_SCRIPT</runMode> @@ -91,6 +123,38 @@ <rc>0</rc> </testSpec> <testSpec> + <title>if ... then ... elif ... else ... fi multi-line</title> + <command>test</command> + <runMode>AS_SCRIPT</runMode> + <script>#!bjorne +if true ; +then echo HI ; +elif false ; +then echo HO ; +else +echo HUM; +fi +if false ; +then echo HI ; +elif true ; +then +echo HO ; +else echo HUM; +fi +if false +then echo HI +elif false +then echo HO +else echo HUM +fi + </script> + <output>HI +HO +HUM +</output> + <rc>0</rc> + </testSpec> + <testSpec> <title>while ... do ... done</title> <command>test</command> <runMode>AS_SCRIPT</runMode> @@ -113,6 +177,31 @@ <rc>0</rc> </testSpec> <testSpec> + <title>while ... do ... done multi-line</title> + <command>test</command> + <runMode>AS_SCRIPT</runMode> + <script>#!bjorne +A=5 +while expr $A != 0 ; +do echo A is $A ; +A=`expr $A - 1`; +done + </script> + <output>1 +A is 5 +1 +A is 4 +1 +A is 3 +1 +A is 2 +1 +A is 1 +0 +</output> + <rc>0</rc> + </testSpec> + <testSpec> <title>while ... do ... break ... done</title> <command>test</command> <runMode>AS_SCRIPT</runMode> @@ -136,6 +225,33 @@ <rc>0</rc> </testSpec> <testSpec> + <title>while ... do ... break ... done multi-line</title> + <command>test</command> + <runMode>AS_SCRIPT</runMode> + <script>#!bjorne + A=5 + while expr $A != 0 + do + echo A is $A + if expr $A = 2 ; then break ; fi ; A=`expr $A - 1` + done + </script> + <output>1 +A is 5 +0 +1 +A is 4 +0 +1 +A is 3 +0 +1 +A is 2 +1 +</output> + <rc>0</rc> + </testSpec> + <testSpec> <title>${...} expansions</title> <command>test</command> <runMode>AS_SCRIPT</runMode> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |