Dan,

You might want to port all SSI changes to tomcat 5 as well.

Thanks,
Amy

[EMAIL PROTECTED] wrote:
dsandberg 2002/11/25 02:15:43

Modified: catalina/src/share/org/apache/catalina/ssi SSICommand.java
SSIConfig.java SSIEcho.java SSIExec.java
SSIFlastmod.java SSIFsize.java SSIInclude.java
SSIMediator.java SSIPrintenv.java SSIProcessor.java
SSISet.java SSIStopProcessingException.java
tester/src/bin tester.xml
Added: catalina/src/share/org/apache/catalina/ssi
ExpressionParseTree.java ExpressionTokenizer.java
SSIConditional.java SSIConditionalState.java
tester/web SSIConditional09.shtml
tester/web/golden SSIConditional09.txt
Log:
Added back Paul Speed's conditional SSI enhancement. Updated code/regression tests to better emulate Apache SSI. Fixed bug w/ expression parser's handling of literals.
Revision Changes Path
1.2 +5 -3 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSICommand.java
Index: SSICommand.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSICommand.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- SSICommand.java 24 May 2002 16:35:39 -0000 1.1
+++ SSICommand.java 25 Nov 2002 10:15:42 -0000 1.2
@@ -79,12 +79,14 @@
* Write the output of the command to the writer.
*
* @param ssiMediator the ssi mediator
+ * @param commandName the name of the actual command ( ie. echo )
* @param paramNames The parameter names
* @param paramValues The parameter values
* @param writer the writer to output to
* @throws SSIStopProcessingException if SSI processing should be aborted
*/
public void process(SSIMediator ssiMediator,
+ String commandName,
String[] paramNames,
String[] paramValues,
PrintWriter writer) throws SSIStopProcessingException;
1.3 +6 -4 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIConfig.java
Index: SSIConfig.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIConfig.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- SSIConfig.java 24 Nov 2002 06:22:36 -0000 1.2
+++ SSIConfig.java 25 Nov 2002 10:15:42 -0000 1.3
@@ -72,6 +72,7 @@
* Implements the Server-side #exec command
*
* @author Bip Thelin
+ * @author Paul Speed
* @author Dan Sandberg
* @version $Revision$, $Date$
*/
@@ -80,6 +81,7 @@
* @see SSICommand
*/
public void process(SSIMediator ssiMediator,
+ String commandName,
String[] paramNames, String[] paramValues,
PrintWriter writer ) {
1.2 +6 -4 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIEcho.java
Index: SSIEcho.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIEcho.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- SSIEcho.java 24 May 2002 04:38:58 -0000 1.1
+++ SSIEcho.java 25 Nov 2002 10:15:42 -0000 1.2
@@ -71,6 +71,7 @@
* Return the result associated with the supplied Server Variable.
*
* @author Bip Thelin
+ * @author Paul Speed
* @author Dan Sandberg
* @version $Revision$, $Date$
*/
@@ -82,6 +83,7 @@
* @see SSICommand
*/
public void process(SSIMediator ssiMediator,
+ String commandName,
String[] paramNames,
String[] paramValues,
PrintWriter writer) {
1.3 +7 -5 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIExec.java
Index: SSIExec.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIExec.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- SSIExec.java 24 Nov 2002 06:22:36 -0000 1.2
+++ SSIExec.java 25 Nov 2002 10:15:42 -0000 1.3
@@ -89,6 +89,7 @@
* * @author Bip Thelin
* @author Amy Roh
+ * @author Paul Speed
* @author Dan Sandberg
* @version $Revision$, $Date$
*
@@ -101,6 +102,7 @@
* @see SSICommand
*/
public void process(SSIMediator ssiMediator,
+ String commandName,
String[] paramNames,
String[] paramValues,
PrintWriter writer) {
@@ -111,7 +113,7 @@
String substitutedValue = ssiMediator.substituteVariables( paramValue );
if ( paramName.equalsIgnoreCase("cgi") ) {
- ssiInclude.process( ssiMediator, new String[] {"virtual"}, new String[] {substitutedValue}, writer );
+ ssiInclude.process( ssiMediator, "include", new String[] {"virtual"}, new String[] {substitutedValue}, writer );
} else if ( paramName.equalsIgnoreCase("cmd") ) {
boolean foundProgram = false;
try {
1.3 +6 -4 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIFlastmod.java
Index: SSIFlastmod.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIFlastmod.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- SSIFlastmod.java 24 Nov 2002 06:22:36 -0000 1.2
+++ SSIFlastmod.java 25 Nov 2002 10:15:42 -0000 1.3
@@ -75,6 +75,7 @@
* Implements the Server-side #flastmod command
*
* @author Bip Thelin
+ * @author Paul Speed
* @author Dan Sandberg
* @version $Revision$, $Date$
*/
@@ -83,6 +84,7 @@
* @see SSICommand
*/
public void process(SSIMediator ssiMediator,
+ String commandName,
String[] paramNames,
String[] paramValues,
PrintWriter writer) {
1.4 +9 -7 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIFsize.java
Index: SSIFsize.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIFsize.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- SSIFsize.java 24 Nov 2002 06:22:36 -0000 1.3
+++ SSIFsize.java 25 Nov 2002 10:15:42 -0000 1.4
@@ -72,6 +72,7 @@
* Implements the Server-side #fsize command
*
* @author Bip Thelin
+ * @author Paul Speed
* @author Dan Sandberg
* @version $Revision$, $Date$
*/
@@ -83,9 +84,10 @@
* @see SSICommand
*/
public void process(SSIMediator ssiMediator,
- String[] paramNames,
- String[] paramValues,
- PrintWriter writer) {
+ String commandName,
+ String[] paramNames,
+ String[] paramValues,
+ PrintWriter writer) {
String configErrMsg = ssiMediator.getConfigErrMsg();
for(int i=0;i<paramNames.length;i++) {
1.3 +6 -4 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIInclude.java
Index: SSIInclude.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIInclude.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- SSIInclude.java 24 Nov 2002 06:22:36 -0000 1.2
+++ SSIInclude.java 25 Nov 2002 10:15:42 -0000 1.3
@@ -75,6 +75,7 @@
* Implements the Server-side #include command
*
* @author Bip Thelin
+ * @author Paul Speed
* @author Dan Sandberg
* @version $Revision$, $Date$
*/
@@ -83,6 +84,7 @@
* @see SSICommand
*/
public void process(SSIMediator ssiMediator,
+ String commandName,
String[] paramNames,
String[] paramValues,
PrintWriter writer) {
1.3 +10 -4 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIMediator.java
Index: SSIMediator.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIMediator.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- SSIMediator.java 24 Nov 2002 06:22:36 -0000 1.2
+++ SSIMediator.java 25 Nov 2002 10:15:42 -0000 1.3
@@ -86,6 +86,7 @@
*
* @author Bip Thelin
* @author Amy Roh
+ * @author Paul Speed
* @author Dan Sandberg
* @version $Revision$, $Date$
*/
@@ -102,6 +103,7 @@
protected Date lastModifiedDate;
protected int debug;
protected Strftime strftime;
+ protected SSIConditionalState conditionalState = new SSIConditionalState();
static {
//We try to encode only the same characters that apache does
@@ -163,6 +165,10 @@
public String getConfigSizeFmt() {
return configSizeFmt;
+ }
+
+ public SSIConditionalState getConditionalState() {
+ return conditionalState;
}
public Collection getVariableNames() {
1.2 +5 -4 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIPrintenv.java
Index: SSIPrintenv.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIPrintenv.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- SSIPrintenv.java 24 May 2002 04:38:58 -0000 1.1
+++ SSIPrintenv.java 25 Nov 2002 10:15:42 -0000 1.2
@@ -79,6 +79,7 @@
* @see SSICommand
*/
public void process(SSIMediator ssiMediator,
+ String commandName,
String[] paramNames,
String[] paramValues,
PrintWriter writer) {
1.2 +82 -41 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIProcessor.java
Index: SSIProcessor.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIProcessor.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- SSIProcessor.java 24 May 2002 04:38:58 -0000 1.1
+++ SSIProcessor.java 25 Nov 2002 10:15:42 -0000 1.2
@@ -108,6 +108,11 @@
addCommand( "fsize", new SSIFsize() );
addCommand( "printenv", new SSIPrintenv() );
addCommand( "set", new SSISet() );
+ SSIConditional ssiConditional = new SSIConditional();
+ addCommand( "if", ssiConditional );
+ addCommand( "elif", ssiConditional );
+ addCommand( "endif", ssiConditional );
+ addCommand( "else", ssiConditional );
}
public void addCommand( String name, SSICommand command ) {
@@ -147,7 +152,9 @@
index += COMMAND_START.length();
command.setLength( 0 ); //clear the command string
} else {
- writer.write( c );
+ if ( !ssiMediator.getConditionalState().processConditionalCommandsOnly ) {
+ writer.write( c );
+ }
index++;
}
} else {
@@ -159,20 +166,29 @@
ssiExternalResolver.log( "SSIProcessor.process -- processing command: " + strCmd, null );
}
String[] paramNames = parseParamNames(command, strCmd.length());
- String[] paramValues = parseParamValues(command, strCmd.length());
+ String[] paramValues = parseParamValues(command, strCmd.length(), paramNames.length );

//We need to fetch this value each time, since it may change during the loop
String configErrMsg = ssiMediator.getConfigErrMsg(); SSICommand ssiCommand = (SSICommand) commands.get(strCmd.toLowerCase());
- if ( ssiCommand != null ) {
- if ( paramNames.length==paramValues.length ) { - ssiCommand.process( ssiMediator, paramNames, paramValues, writer );
- } else {
- ssiExternalResolver.log( "Parameter names count does not match parameter values count on command: " + strCmd, null );
- writer.write( configErrMsg );
- }
+ String errorMessage = null;
+ if ( ssiCommand == null ) {
+ errorMessage = "Unknown command: " + strCmd;
+ } else if ( paramValues == null ) {
+ errorMessage = "Error parsing directive parameters.";
+ } else if ( paramNames.length!=paramValues.length ) { + errorMessage = "Parameter names count does not match parameter values count on command: " + strCmd;
} else {
- ssiExternalResolver.log( "Unknown command: " + strCmd, null);
+ // don't process the command if we are processing conditional commands only and the
+ // command is not conditional
+ if ( !ssiMediator.getConditionalState().processConditionalCommandsOnly ||
+ ssiCommand instanceof SSIConditional ) {
+ ssiCommand.process( ssiMediator, strCmd, paramNames, paramValues, writer );
+ }
+ }
+
+ if ( errorMessage != null ) {
+ ssiExternalResolver.log( errorMessage, null );
writer.write( configErrMsg );
}
} else {
@@ -214,20 +230,29 @@
bIdx++;
}
- retBuf.append('"');
+ retBuf.append('=');
inside=!inside;
quotes=0;
- while(bIdx < cmd.length()&&quotes!=2) {
- if(cmd.charAt(bIdx)=='"')
- quotes++;
+ boolean escaped=false;
+ for ( ; bIdx < cmd.length() && quotes != 2; bIdx++ ) {
+ char c = cmd.charAt(bIdx);
+
+ // Need to skip escaped characters
+ if (c=='\\' && !escaped) {
+ escaped = true;
+ bIdx++;
+ continue;
+ }
+ escaped = false;
- bIdx++;
+ if (c=='"')
+ quotes++;
}
}
}
- StringTokenizer str = new StringTokenizer(retBuf.toString(), "\"");
+ StringTokenizer str = new StringTokenizer(retBuf.toString(), "=");
String[] retString = new String[str.countTokens()];
while(str.hasMoreTokens()) {
@@ -243,15 +268,14 @@
* @param cmd a value of type 'StringBuffer'
* @return a value of type 'String[]'
*/
- protected String[] parseParamValues(StringBuffer cmd, int start) {
- int bIdx = start;
- int i = 0;
- int quotes = 0;
+ protected String[] parseParamValues(StringBuffer cmd, int start, int count) {
+ int valIndex = 0;
boolean inside = false;
- StringBuffer retBuf = new StringBuffer();
+ String[] vals = new String[count];
+ StringBuffer sb = new StringBuffer();
- while(bIdx < cmd.length()) {
- if(!inside) {
+ for (int bIdx = start; bIdx < cmd.length(); bIdx++ ) {
+ if (!inside) {
while(bIdx < cmd.length()&&
cmd.charAt(bIdx)!='"')
bIdx++;
@@ -261,26 +285,43 @@
inside=!inside;
} else {
- while(bIdx < cmd.length() && cmd.charAt(bIdx)!='"') {
- retBuf.append(cmd.charAt(bIdx));
- bIdx++;
- }
+ boolean escaped=false;
+ for ( ; bIdx < cmd.length(); bIdx++) {
- retBuf.append('"');
- inside=!inside;
- }
+ char c = cmd.charAt(bIdx);
- bIdx++;
- }
+ // Check for escapes
+ if (c=='\\' && !escaped) {
+ escaped = true;
+ continue;
+ }
+
+ // If we reach the other " then stop
+ if (c=='"' && !escaped)
+ break;
+
+ // Since parsing of attributes and var
+ // substitution is done in separate places,
+ // we need to leave escape in the string
+ if (c=='$' && escaped)
+ sb.append( '\\' );
- StringTokenizer str = new StringTokenizer(retBuf.toString(), "\"");
- String[] retString = new String[str.countTokens()];
+ escaped = false;
+ sb.append(c);
+ }
- while(str.hasMoreTokens()) {
- retString[i++] = str.nextToken();
+ // If we hit the end without seeing a quote
+ // the signal an error
+ if (bIdx == cmd.length())
+ return null;
+
+ vals[valIndex++] = sb.toString();
+ sb.delete( 0, sb.length() ); // clear the buffer
+ inside=!inside;
+ }
}
- return retString;
+ return vals;
}
/**
1.3 +10 -8 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSISet.java
Index: SSISet.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSISet.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- SSISet.java 24 Nov 2002 06:22:36 -0000 1.2
+++ SSISet.java 25 Nov 2002 10:15:42 -0000 1.3
@@ -70,6 +70,7 @@
/**
* Implements the Server-side #set command
*
+ * @author Paul Speed * @author Dan Sandberg
* @version $Revision$, $Date$
*/
@@ -77,10 +78,11 @@
/**
* @see SSICommand
*/
- public void process(SSIMediator ssiMediator,
- String[] paramNames,
- String[] paramValues,
- PrintWriter writer) throws SSIStopProcessingException {
+ public void process( SSIMediator ssiMediator,
+ String commandName,
+ String[] paramNames,
+ String[] paramValues,
+ PrintWriter writer) throws SSIStopProcessingException {
String errorMessage = ssiMediator.getConfigErrMsg();
String variableName = null;
1.2 +5 -4 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIStopProcessingException.java
Index: SSIStopProcessingException.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIStopProcessingException.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- SSIStopProcessingException.java 24 May 2002 04:38:58 -0000 1.1
+++ SSIStopProcessingException.java 25 Nov 2002 10:15:42 -0000 1.2
@@ -67,6 +67,7 @@
* Exception used to tell SSIProcessor that it should stop processing SSI commands.
* This is used to mimick the Apache behavior in #set with invalid attributes.
*
+ * @author Paul Speed
* @author Dan Sandberg
* @version $Revision$, $Date$
*/
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/ExpressionParseTree.java
Index: ExpressionParseTree.java
===================================================================
/*
* ExpressionParseTree.java
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/ExpressionParseTree.java,v 1.1 2002/11/25 10:15:42 dsandberg Exp $
* $Revision: 1.1 $
* $Date: 2002/11/25 10:15:42 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.ssi;
import java.util.LinkedList;
import java.util.List;
import java.text.ParseException;
/**
* Represents a parsed expression.
*
* @version $Revision: 1.1 $
* @author Paul Speed
*/
public class ExpressionParseTree
{
/**
* Contains the current set of completed nodes. This
* is a workspace for the parser.
*/
private LinkedList nodeStack = new LinkedList();
/**
* Contains operator nodes that don't yet have values.
* This is a workspace for the parser.
*/
private LinkedList oppStack = new LinkedList();
/**
* The root node after the expression has been parsed.
*/
private Node root;
/**
* The SSIMediator to use when evaluating the expressions.
*/
private SSIMediator ssiMediator;
/**
* Creates a new parse tree for the specified expression.
*/
public ExpressionParseTree( String expr,
SSIMediator ssiMediator )
throws ParseException {
this.ssiMediator = ssiMediator;
parseExpression( expr );
}
/**
* Evaluates the tree and returns true or false. The specified
* SSIMediator is used to resolve variable references.
*/
public boolean evaluateTree() {
return root.evaluate();
}
/**
* Pushes a new operator onto the opp stack, resolving existing
* opps as needed.
*/
private void pushOpp( OppNode node ) {
// If node is null then it's just a group marker
if( node == null ) {
oppStack.add( 0, node );
return;
}
while (true) {
if (oppStack.size() == 0)
break;
OppNode top = (OppNode)oppStack.get(0);
// If the top is a spacer then don't pop
// anything
if (top == null)
break;
// If the top node has a lower precedence then
// let it stay
if (top.getPrecedence() < node.getPrecedence())
break;
// Remove the top node
oppStack.remove(0);
// Let it fill its branches
top.popValues( nodeStack );
// Stick it on the resolved node stack
nodeStack.add( 0, top );
}
// Add the new node to the opp stack
oppStack.add( 0, node );
}
/**
* Resolves all pending opp nodes on the stack until the
* next group marker is reached.
*/
private void resolveGroup() {
OppNode top = null;
while ((top=(OppNode)oppStack.remove(0)) != null ) {
// Let it fill its branches
top.popValues( nodeStack );
// Stick it on the resolved node stack
nodeStack.add( 0, top );
}
}
/**
* Parses the specified expression into a tree of
* parse nodes.
*/
private void parseExpression( String expr ) throws ParseException {
StringNode currStringNode = null;
// We cheat a little and start an artificial
// group right away. It makes finishing easier.
pushOpp( null );
ExpressionTokenizer et = new ExpressionTokenizer(expr);
while (et.hasMoreTokens()) {
int token = et.nextToken();
if (token != ExpressionTokenizer.TOKEN_STRING)
currStringNode = null;
switch (token) {
case ExpressionTokenizer.TOKEN_STRING:
if (currStringNode == null) {
currStringNode = new StringNode( et.getTokenValue() );
nodeStack.add( 0, currStringNode );
} else {
// Add to the existing
currStringNode.value.append( " " );
currStringNode.value.append( et.getTokenValue() );
}
break;
case ExpressionTokenizer.TOKEN_AND:
pushOpp( new AndNode() );
break;
case ExpressionTokenizer.TOKEN_OR:
pushOpp( new OrNode() );
break;
case ExpressionTokenizer.TOKEN_NOT:
pushOpp( new NotNode() );
break;
case ExpressionTokenizer.TOKEN_EQ:
pushOpp( new EqualNode() );
break;
case ExpressionTokenizer.TOKEN_NOT_EQ:
pushOpp( new NotNode() );
// Sneak the regular node in. The NOT will
// be resolved when the next opp comes along.
oppStack.add( 0, new EqualNode() );
break;
case ExpressionTokenizer.TOKEN_RBRACE:
// Closeout the current group
resolveGroup();
break;
case ExpressionTokenizer.TOKEN_LBRACE:
// Push a group marker
pushOpp( null );
break;
case ExpressionTokenizer.TOKEN_GE:
pushOpp( new NotNode() );
// Similar stategy to NOT_EQ above, except this
// is NOT less than
oppStack.add( 0, new LessThanNode() );
break;
case ExpressionTokenizer.TOKEN_LE:
pushOpp( new NotNode() );
// Similar stategy to NOT_EQ above, except this
// is NOT greater than
oppStack.add( 0, new GreaterThanNode() );
break;
case ExpressionTokenizer.TOKEN_GT:
pushOpp( new GreaterThanNode() );
break;
case ExpressionTokenizer.TOKEN_LT:
pushOpp( new LessThanNode() );
break;
case ExpressionTokenizer.TOKEN_END:
break;
}
}
// Finish off the rest of the opps
resolveGroup();
if (nodeStack.size() == 0) {
throw new ParseException( "No nodes created.",
et.getIndex() );
}
if (nodeStack.size() > 1) {
throw new ParseException( "Extra nodes created.",
et.getIndex() );
}
if (oppStack.size() != 0) {
throw new ParseException( "Unused opp nodes exist.",
et.getIndex() );
}
root = (Node)nodeStack.get(0);
}
/**
* A node in the expression parse tree.
*/
private abstract class Node {
/**
* Return true if the node evaluates to true.
*/
public abstract boolean evaluate();
}
/**
* A node the represents a String value
*/
private class StringNode extends Node {
StringBuffer value;
String resolved = null;
public StringNode( String value ) {
this.value = new StringBuffer(value);
}
/**
* Resolves any variable references and returns the
* value string.
*/
public String getValue() {
if (resolved == null)
resolved = ssiMediator.substituteVariables( value.toString() ) ;
return resolved;
}
/**
* Returns true if the string is not empty.
*/
public boolean evaluate() {
return !(getValue().length() == 0);
}
public String toString() {
return value.toString();
}
}
private static final int PRECEDENCE_NOT = 5;
private static final int PRECEDENCE_COMPARE = 4;
private static final int PRECEDENCE_LOGICAL = 1;
/**
* A node implementation that represents an operation.
*/
private abstract class OppNode extends Node {
/**
* The left branch.
*/
Node left;
/**
* The right branch.
*/
Node right;
/**
* Returns a preference level suitable for comparison to
* other OppNode preference levels.
*/
public abstract int getPrecedence();
/**
* Lets the node pop its own branch nodes off the front of
* the specified list. The default pulls two.
*/
public void popValues( List values ) {
right = (Node)values.remove(0);
left = (Node)values.remove(0);
}
}
private final class NotNode extends OppNode {
public boolean evaluate() {
return !left.evaluate();
}
public int getPrecedence() {
return PRECEDENCE_NOT;
}
/**
* Overridden to pop only one value.
*/
public void popValues( List values ) {
left = (Node)values.remove(0);
}
public String toString() {
return left + " NOT";
}
}
private final class AndNode extends OppNode {
public boolean evaluate() {
if (!left.evaluate()) // Short circuit
return false;
return right.evaluate();
}
public int getPrecedence() {
return PRECEDENCE_LOGICAL;
}
public String toString() {
return left + " " + right + " AND";
}
}
private final class OrNode extends OppNode {
public boolean evaluate() {
if (left.evaluate()) // Short circuit
return true;
return right.evaluate();
}
public int getPrecedence() {
return PRECEDENCE_LOGICAL;
}
public String toString() {
return left + " " + right + " OR";
}
}
private abstract class CompareNode extends OppNode {
protected int compareBranches() {
String val1 = ((StringNode)left).getValue();
String val2 = ((StringNode)right).getValue();
return val1.compareTo(val2);
}
}
private final class EqualNode extends CompareNode {
public boolean evaluate() {
return (compareBranches() == 0);
}
public int getPrecedence() {
return PRECEDENCE_COMPARE;
}
public String toString() {
return left + " " + right + " EQ";
}
}
private final class GreaterThanNode extends CompareNode {
public boolean evaluate() {
return (compareBranches() > 0);
}
public int getPrecedence() {
return PRECEDENCE_COMPARE;
}
public String toString() {
return left + " " + right + " GT";
}
}
private final class LessThanNode extends CompareNode {
public boolean evaluate() {
return (compareBranches() < 0);
}
public int getPrecedence() {
return PRECEDENCE_COMPARE;
}
public String toString() {
return left + " " + right + " LT";
}
}
}
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/ExpressionTokenizer.java
Index: ExpressionTokenizer.java
===================================================================
/*
* ExpressionTokenizer.java
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/ExpressionTokenizer.java,v 1.1 2002/11/25 10:15:42 dsandberg Exp $
* $Revision: 1.1 $
* $Date: 2002/11/25 10:15:42 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.ssi;
/**
* Parses an expression string to return the individual tokens.
* This is patterned similar to the StreamTokenizer in the JDK
* but customized for SSI conditional expression parsing.
*
* @version $Revision: 1.1 $
* @author Paul Speed
*/
public class ExpressionTokenizer {
public static final int TOKEN_STRING = 0;
public static final int TOKEN_AND = 1;
public static final int TOKEN_OR = 2;
public static final int TOKEN_NOT = 3;
public static final int TOKEN_EQ = 4;
public static final int TOKEN_NOT_EQ = 5;
public static final int TOKEN_RBRACE = 6;
public static final int TOKEN_LBRACE = 7;
public static final int TOKEN_GE = 8;
public static final int TOKEN_LE = 9;
public static final int TOKEN_GT = 10;
public static final int TOKEN_LT = 11;
public static final int TOKEN_END = 12;
private char[] expr;
private int tokenType = TOKEN_STRING;
private String tokenVal = null;
private int index;
private int length;
/**
* Creates a new parser for the specified expression.
*/
public ExpressionTokenizer( String expr ) {
this.expr = expr.trim().toCharArray();
this.length = this.expr.length;
}
/**
* Returns true if there are more tokens.
*/
public boolean hasMoreTokens() {
return index < length;
}
/**
* Returns the current index for error reporting purposes.
*/
public int getIndex() {
return index;
}
protected boolean isMetaChar( char c ) {
return Character.isWhitespace( c ) || c == '(' || c == ')' || c == '!' || c == '<' || c == '>' || c == '|' || c == '&' || c == '=';
}
/**
* Returns the next token type and initializes any
* state variables accordingly.
*/
public int nextToken() {
// Skip any leading white space
while (index<length && Character.isWhitespace(expr[index]))
index++;
// Clear the current token val
tokenVal = null;
if (index == length)
return TOKEN_END; // End of string
int start = index;
char currentChar = expr[index];
char nextChar = (char)0;
index++;
if (index < length)
nextChar = expr[index];
// Check for a known token start
switch (currentChar) {
case '(':
return TOKEN_LBRACE;
case ')':
return TOKEN_RBRACE;
case '=':
return TOKEN_EQ;
case '!':
if (nextChar == '=') {
index++;
return TOKEN_NOT_EQ;
} else {
return TOKEN_NOT;
}
case '|':
if (nextChar == '|') {
index++;
return TOKEN_OR;
}
break;
case '&':
if (nextChar == '&') {
index++;
return TOKEN_AND;
}
break;
case '>':
if (nextChar == '=') {
index++;
return TOKEN_GE; // Greater than or equal
} else {
return TOKEN_GT; // Greater than
}
case '<':
if (nextChar == '=') {
index++;
return TOKEN_LE; // Less than or equal
} else {
return TOKEN_LT; // Less than
}
default:
// Otherwise it's a string
break;
}
int end = index;
// If it's a quoted string then end is the next unescaped quote
if (currentChar == '"' || currentChar == '\'') {
char endChar = currentChar;
boolean escaped = false;
start++;
for ( ; index < length; index++) {
if (expr[index] == '\\' && !escaped) {
escaped = true;
continue;
}
if (expr[index] == endChar && !escaped)
break;
escaped = false;
}
end = index;
index++; // Skip the end quote
} else {
// End is the next whitespace character
for ( ; index < length; index++) {
if ( isMetaChar(expr[index]) )
break;
}
end = index;
}
// Extract the string from the array
this.tokenVal = new String( expr, start, end - start );
return TOKEN_STRING;
}
/**
* Returns the String value of the token if it was type
* TOKEN_STRING. Otherwise null is returned.
*/
public String getTokenValue() {
return tokenVal;
}
}
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIConditional.java
Index: SSIConditional.java
===================================================================
/*
* SSIConditional.java
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIConditional.java,v 1.1 2002/11/25 10:15:42 dsandberg Exp $
* $Revision: 1.1 $
* $Date: 2002/11/25 10:15:42 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.ssi;
import java.io.PrintWriter;
import java.text.ParseException;
import java.util.LinkedList;
import java.util.List;
import javax.servlet.ServletOutputStream;
/**
* SSI command that handles all conditional directives.
*
* @version $Revision: 1.1 $
* @author Paul Speed
*/
public class SSIConditional implements SSICommand {
/**
* @see SSICommand
*/
public void process( SSIMediator ssiMediator,
String commandName,
String[] paramNames,
String[] paramValues,
PrintWriter writer) throws SSIStopProcessingException {
// Retrieve the current state information
SSIConditionalState state = ssiMediator.getConditionalState();
if ( "if".equalsIgnoreCase( commandName ) ) {
// Do nothing if we are nested in a false branch
// except count it
if ( state.processConditionalCommandsOnly ) {
state.nestingCount++;
return;
}
state.nestingCount = 0;
// Evaluate the expression
if ( evaluateArguments(paramNames, paramValues, ssiMediator) ) {
// No more branches can be taken for this if block
state.branchTaken = true;
} else {
// Do not process this branch
state.processConditionalCommandsOnly = true;
state.branchTaken = false;
}
} else if ( "elif".equalsIgnoreCase( commandName ) ) {
// No need to even execute if we are nested in
// a false branch
if (state.nestingCount > 0)
return;
// If a branch was already taken in this if block
// then disable output and return
if ( state.branchTaken ) {
state.processConditionalCommandsOnly = true;
return;
}
// Evaluate the expression
if ( evaluateArguments(paramNames, paramValues, ssiMediator) ) {
// Turn back on output and mark the branch
state.processConditionalCommandsOnly = false;
state.branchTaken = true;
} else {
// Do not process this branch
state.processConditionalCommandsOnly = true;
state.branchTaken = false;
}
} else if ( "else".equalsIgnoreCase( commandName ) ) {
// No need to even execute if we are nested in
// a false branch
if (state.nestingCount > 0)
return;
// If we've already taken another branch then
// disable output otherwise enable it.
state.processConditionalCommandsOnly = state.branchTaken;
// And in any case, it's safe to say a branch
// has been taken.
state.branchTaken = true;
} else if ( "endif".equalsIgnoreCase( commandName ) ) {
// If we are nested inside a false branch then pop out
// one level on the nesting count
if (state.nestingCount > 0) {
state.nestingCount--;
return;
}
// Turn output back on
state.processConditionalCommandsOnly = false;
// Reset the branch status for any outer if blocks,
// since clearly we took a branch to have gotten here
// in the first place.
state.branchTaken = true;
} else {
throw new SSIStopProcessingException();
//throw new SsiCommandException( "Not a conditional command:" + cmdName );
}
}
/**
* Retrieves the expression from the specified arguments
* and peforms the necessary evaluation steps.
*/
private boolean evaluateArguments( String[] names, String[] values,
SSIMediator ssiMediator ) throws SSIStopProcessingException {
String expr = getExpression( names, values );
if (expr == null) {
throw new SSIStopProcessingException();
//throw new SsiCommandException( "No expression specified." );
}
try {
ExpressionParseTree tree = new ExpressionParseTree( expr,
ssiMediator );
return tree.evaluateTree();
} catch (ParseException e) {
//throw new SsiCommandException( "Error parsing expression." );
throw new SSIStopProcessingException();
}
}
/**
* Returns the "expr" if the arg name is appropriate, otherwise
* returns null.
*/
private String getExpression( String[] paramNames, String[] paramValues ) {
if ( "expr".equalsIgnoreCase( paramNames[0]) )
return paramValues[0];
return null;
}
}
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIConditionalState.java
Index: SSIConditionalState.java
===================================================================
/*
* SSIConditionalState.java
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/ssi/SSIConditionalState.java,v 1.1 2002/11/25 10:15:42 dsandberg Exp $
* $Revision: 1.1 $
* $Date: 2002/11/25 10:15:42 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.ssi;
/**
* This class is used by SSIMediator and SSIConditional to keep track of state information necessary to process
* the nested conditional commands ( if, elif, else, endif ).
*
* @version $Revision: 1.1 $
* @author Dan Sandberg
* @author Paul Speed
*/
class SSIConditionalState {
/**
* Set to true if the current conditional has already been
* completed, i.e.: a branch was taken.
*/
boolean branchTaken = false;
/**
* Counts the number of nested false branches.
*/
int nestingCount = 0;
/**
* Set to true if only conditional commands ( if, elif, else, endif ) should be processed.
*/
boolean processConditionalCommandsOnly = false;
}
1.85 +36 -0 jakarta-tomcat-4.0/tester/src/bin/tester.xml
Index: tester.xml
===================================================================
RCS file: /home/cvs/jakarta-tomcat-4.0/tester/src/bin/tester.xml,v
retrieving revision 1.84
retrieving revision 1.85
diff -u -r1.84 -r1.85
--- tester.xml 24 Nov 2002 06:22:36 -0000 1.84
+++ tester.xml 25 Nov 2002 10:15:43 -0000 1.85
@@ -1849,6 +1849,42 @@
golden="${golden.path}/SSIFsize02.txt"/>
<tester host="${host}" port="${port}" protocol="${protocol}"
+ request="${context.path}/SSIConditional01.shtml" debug="${debug}"
+ golden="${golden.path}/SSIConditional01.txt"/>
+
+ <tester host="${host}" port="${port}" protocol="${protocol}"
+ request="${context.path}/SSIConditional02.shtml" debug="${debug}"
+ golden="${golden.path}/SSIConditional02.txt"/>
+
+ <tester host="${host}" port="${port}" protocol="${protocol}"
+ request="${context.path}/SSIConditional03.shtml" debug="${debug}"
+ golden="${golden.path}/SSIConditional03.txt"/>
+
+ <tester host="${host}" port="${port}" protocol="${protocol}"
+ request="${context.path}/SSIConditional04.shtml" debug="${debug}"
+ golden="${golden.path}/SSIConditional04.txt"/>
+
+ <tester host="${host}" port="${port}" protocol="${protocol}"
+ request="${context.path}/SSIConditional05.shtml" debug="${debug}"
+ golden="${golden.path}/SSIConditional05.txt"/>
+
+ <tester host="${host}" port="${port}" protocol="${protocol}"
+ request="${context.path}/SSIConditional06.shtml" debug="${debug}"
+ golden="${golden.path}/SSIConditional06.txt"/>
+
+ <tester host="${host}" port="${port}" protocol="${protocol}"
+ request="${context.path}/SSIConditional07.shtml" debug="${debug}"
+ golden="${golden.path}/SSIConditional07.txt"/>
+
+ <tester host="${host}" port="${port}" protocol="${protocol}"
+ request="${context.path}/SSIConditional08.shtml" debug="${debug}"
+ golden="${golden.path}/SSIConditional08.txt"/>
+
+ <tester host="${host}" port="${port}" protocol="${protocol}"
+ request="${context.path}/SSIConditional09.shtml" debug="${debug}"
+ golden="${golden.path}/SSIConditional09.txt"/>
+
+ <tester host="${host}" port="${port}" protocol="${protocol}"
request="${context.path}/SSIVarSub01.shtml" debug="${debug}"
golden="${golden.path}/SSIVarSub01.txt"/>
1.1 jakarta-tomcat-4.0/tester/web/SSIConditional09.shtml
Index: SSIConditional09.shtml
===================================================================
1
<!--#if expr="1=2" -->
a
##<!--#if expr="1=2" -->
b
##<!--#else -->
c
##<!--#endif -->
d
<!--#elif expr="2=2" -->
e
##<!--#if expr="2=2" -->
f
####<!--#if expr="1=2" -->
**11
####<!--#elif expr="2=3" -->
**22
####<!--#endif -->
**33
##<!--#else -->
g
##<!--#endif -->
h
<!--#else -->
i
##<!--#if expr="1=1" -->
j
##<!--#else -->
k
##<!--#endif -->
l
<!--#endif -->
#
now we test extra #endif commands
<!--#endif -->
n
<!--#endif -->
o
1.1 jakarta-tomcat-4.0/tester/web/golden/SSIConditional09.txt
Index: SSIConditional09.txt
===================================================================
1
e
##
f
####
**33
##
h
#
now we test extra #endif commands
n
o

--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>




--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to