You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Paul Speed <pa...@objectsciences.com> on 2002/11/25 22:56:16 UTC

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

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>