You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ds...@apache.org on 2002/11/25 11:15:43 UTC

cvs commit: jakarta-tomcat-4.0/tester/web/golden SSIConditional09.txt

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 apache@apache.org.
   *
   * 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 apache@apache.org.
   *
   * 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 apache@apache.org.
   *
   * 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 apache@apache.org.
   *
   * 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:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: cvs commit: jakarta-tomcat-4.0/tester/web/golden SSIConditional09.txt

Posted by Amy Roh <am...@apache.org>.
Dan,

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

Thanks,
Amy

dsandberg@apache.org 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 apache@apache.org.
>    *
>    * 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 apache@apache.org.
>    *
>    * 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 apache@apache.org.
>    *
>    * 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 apache@apache.org.
>    *
>    * 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:   <ma...@jakarta.apache.org>
> For additional commands, e-mail: <ma...@jakarta.apache.org>
> 




--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: cvs commit: jakarta-tomcat-4.0/tester/web/golden SSIConditional09.txt

Posted by Paul Speed <pa...@objectsciences.com>.
Thanks Dan.  Now I have my 15 milliseconds of fame back. ;)

I'll see if I can test the updates with my site soon.
-Paul Speed

dsandberg@apache.org 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 apache@apache.org.
>    *
>    * 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 apache@apache.org.
>    *
>    * 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 apache@apache.org.
>    *
>    * 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 apache@apache.org.
>    *
>    * 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:   <ma...@jakarta.apache.org>
> For additional commands, e-mail: <ma...@jakarta.apache.org>

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>