httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Paul J. Reder" <rede...@raleigh.ibm.com>
Subject [Patch]: mod_include.c port to 2.0 buckets (includes new mod_include.h)
Date Mon, 20 Nov 2000 15:15:23 GMT
Finally, The promised port/rewrite of mod_include. Sorry it took so long.

This is a CVS diff from locus. It includes the new mod_include.h file
after the mod_include.c diff listing. Sorry it is so long, but it was
a rewrite ;)

-- 
Paul J. Reder
-----------------------------------------------------------
"The strength of the Constitution lies entirely in the determination of each
citizen to defend it.  Only if every single citizen feels duty bound to do
his share in this defense are the constitutional rights secure."
-- Albert Einstein

----------------------------------------------------------------------------


Index: standard/mod_include.c
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/modules/standard/mod_include.c,v
retrieving revision 1.74
diff -r1.74 mod_include.c
102c102
< #include "ap_mpm.h"
---
> #include "mod_include.h"
115,135d114
< #define STARTING_SEQUENCE "<!--#"
< #define ENDING_SEQUENCE "-->"
< 
< #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
< #define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
< #define SIZEFMT_BYTES 0
< #define SIZEFMT_KMG 1
< #ifdef CHARSET_EBCDIC
< #define RAW_ASCII_CHAR(ch)  apr_xlate_conv_byte(ap_hdrs_from_ascii, (unsigned char)ch)
< #else /*CHARSET_EBCDIC*/
< #define RAW_ASCII_CHAR(ch)  (ch)
< #endif /*CHARSET_EBCDIC*/
< 
< module AP_MODULE_DECLARE_DATA includes_module;
< 
< /* just need some arbitrary non-NULL pointer which can't also be a request_rec */
< #define NESTED_INCLUDE_MAGIC	(&includes_module)
< 
< /* TODO: changing directory should be handled by CreateProcess */
< #define ap_chdir_file(x) do {} while(0)
< 
184,186c163,168
< #define OUTBUFSIZE 4096
< 
< static ap_bucket *find_string(ap_bucket *dptr, const char *str, ap_bucket *end)
---
> /* This function returns either a pointer to the split bucket containing the
>  * first byte of the BEGINNING_SEQUENCE (after finding a complete match) or it
>  * returns NULL if no match found.
>  */
> static ap_bucket *find_start_sequence(ap_bucket *dptr, include_ctx_t *ctx,
>                                       ap_bucket *end, int *do_cleanup)
188c170
<     apr_size_t len;
---
>     apr_ssize_t len;
191c173,175
<     int state = 0;
---
>     const char *str = STARTING_SEQUENCE;
> 
>     *do_cleanup = 0;
204,205c188,194
<             if (*c == str[state]) {
<                 state++;
---
>             if (*c == str[ctx->parse_pos]) {
>                 if (ctx->state == PRE_HEAD) {
>                     ctx->state             = PARSE_HEAD;
>                     ctx->head_start_bucket = dptr;
>                     ctx->head_start_index  = c - buf;
>                 }
>                 ctx->parse_pos++;
208,211c197,224
<                 if (str[state] == '\0') {
<                     /* We want to split the bucket at the '<' and '>' 
<                      * respectively.  That means adjusting where we split based
<                      * on what we are searching for.
---
>                 if (str[ctx->parse_pos] == '\0') {
>                     ap_bucket   *tmp_bkt;
>                     apr_ssize_t  start_index;
> 
>                     /* We want to split the bucket at the '<'. */
>                     ctx->state            = PARSE_TAG;
>                     ctx->tag_length       = 0;
>                     ctx->parse_pos        = 0;
>                     ctx->tag_start_bucket = dptr;
>                     ctx->tag_start_index  = c - buf;
>                     if (ctx->head_start_index > 0) {
>                         start_index = (c - buf) - ctx->head_start_index;
>                         ap_bucket_split(ctx->head_start_bucket, ctx->head_start_index);
>                         tmp_bkt = AP_BUCKET_NEXT(ctx->head_start_bucket);
>                         if (dptr == ctx->head_start_bucket) {
>                             ctx->tag_start_bucket = tmp_bkt;
>                             ctx->tag_start_index  = start_index;
>                         }
>                         ctx->head_start_bucket = tmp_bkt;
>                         ctx->head_start_index  = 0;
>                     }
>                     return ctx->head_start_bucket;
>                 }
>                 else if (ctx->parse_pos != 0) {
>                     /* The reason for this, is that we need to make sure 
>                      * that we catch cases like <<!--#.  This makes the 
>                      * second check after the original check fails.
>                      * If parse_pos was already 0 then we already checked this.
213,214c226,231
<                     if (str[0] == '<') {
<                         ap_bucket_split(dptr, c - buf - strlen(str));
---
>                     *do_cleanup = 1;
>                     if (*c == str[0]) {
>                         ctx->parse_pos         = 1;
>                         ctx->state             = PARSE_HEAD;
>                         ctx->head_start_bucket = dptr;
>                         ctx->head_start_index  = c - buf;
217c234,289
<                         ap_bucket_split(dptr, c - buf);
---
>                         ctx->parse_pos         = 0;
>                         ctx->state             = PRE_HEAD;
>                         ctx->head_start_bucket = NULL;
>                         ctx->head_start_index  = 0;
>                     }
>                 }
>             }
>             c++;
>         }
>         dptr = AP_BUCKET_NEXT(dptr);
>     } while (AP_BUCKET_PREV(dptr) != end);
>     return NULL;
> }
> 
> static ap_bucket *find_end_sequence(ap_bucket *dptr, include_ctx_t *ctx, ap_bucket *end)
> {
>     apr_ssize_t len;
>     const char *c;
>     const char *buf;
>     const char *str = ENDING_SEQUENCE;
> 
>     do {
>         if (AP_BUCKET_IS_EOS(dptr)) {
>             break;
>         }
>         ap_bucket_read(dptr, &buf, &len, 0);
>         /* XXX handle retcodes */
>         if (len == 0) { /* end of pipe? */
>             break;
>         }
>         if (dptr == ctx->tag_start_bucket) {
>             c = buf + ctx->tag_start_index;
>         }
>         else {
>             c = buf;
>         }
>         while (c - buf != len) {
>             if (*c == str[ctx->parse_pos]) {
>                 if (ctx->state == PARSE_TAG) {
>                     ctx->state             = PARSE_TAIL;
>                     ctx->tail_start_bucket = dptr;
>                     ctx->tail_start_index  = c - buf;
>                 }
>                 ctx->parse_pos++;
>             }
>             else {
>                 if (ctx->state == PARSE_TAG) {
>                     if (ctx->tag_length == 0) {
>                         if (!apr_isspace(*c)) {
>                             ctx->tag_start_bucket = dptr;
>                             ctx->tag_start_index  = c - buf;
>                             ctx->tag_length       = 1;
>                         }
>                     }
>                     else {
>                         ctx->tag_length++;
219d290
<                     return AP_BUCKET_NEXT(dptr);
222,229c293,327
<                     state = 0;
<                     /* The reason for this, is that we need to make sure 
<                      * that we catch cases like <<--#.  This makes the 
<                      * second check after the original check fails.
<                      */
<                      if (*c == str[state]) {
<                          state++;
<                      }
---
>                     if (str[ctx->parse_pos] == '\0') {
>                         ap_bucket *tmp_buck = dptr;
> 
>                         /* We want to split the bucket at the '>'. The
>                          * end of the END_SEQUENCE is in the current bucket.
>                          * The beginning might be in a previous bucket.
>                          */
>                         ctx->state = PARSED;
>                         if ((c - buf) > 0) {
>                             ap_bucket_split(dptr, c - buf);
>                             tmp_buck = AP_BUCKET_NEXT(dptr);
>                         }
>                         return (tmp_buck);
>                     }
>                     else if (ctx->parse_pos != 0) {
>                         /* The reason for this, is that we need to make sure 
>                          * that we catch cases like --->.  This makes the 
>                          * second check after the original check fails.
>                          * If parse_pos was already 0 then we already checked this.
>                          */
>                          ctx->tag_length += ctx->parse_pos;
> 
>                          if (*c == str[0]) {
>                              ctx->parse_pos         = 1;
>                              ctx->state             = PARSE_TAIL;
>                              ctx->tail_start_bucket = dptr;
>                              ctx->tail_start_index  = c - buf;
>                          }
>                          else {
>                              ctx->parse_pos         = 0;
>                              ctx->state             = PARSE_TAG;
>                              ctx->tail_start_bucket = NULL;
>                              ctx->tail_start_index  = 0;
>                          }
>                     }
238a337,413
> /* This function culls through the buckets that have been set aside in the 
>  * ssi_tag_brigade and copies just the directive part of the SSI tag (none
>  * of the start and end delimiter bytes are copied). The is_allocated paramter
>  * is set according to the following algorithm:
>  *         0 : The combined directive resides in a single bucket (the combined_tag
>  *             pointer is set to the beginning of the directive in the bucket), or
>  *             the combined directive will fit in the local stack buffer.
>  *         1 : The combined directive was too big to fit in the local stack buffer.
>  *             A buffer is allocated and the combined directive is copied into it.
>  */
> static apr_status_t get_combined_directive (include_ctx_t *ctx,
>                                             ap_bucket_brigade *bb,
>                                             int *is_allocated,
>                                             char *tmp_buf, int tmp_buf_size)
> {
>     int         done = 0;
>     ap_bucket  *dptr, *ctx_end, *bb_end;
>     const char *tmp_from;
>     apr_ssize_t tmp_from_len;
> 
>     *is_allocated = 0;
>     
>     /* If the tag length is longer than the tmp buffer, allocate space. */
>     if (ctx->tag_length > tmp_buf_size-1) {
>         if ((ctx->combined_tag = malloc (ctx->tag_length + 1)) == NULL) {
>             return (APR_ENOMEM);
>         }
>         else {
>             *is_allocated = 1;   /* Mark that it is allocated for later freeing. */
>         }
>     }     /* Else, just use the temp buffer. */
>     else {
>         ctx->combined_tag = tmp_buf;
>     }
> 
>     /* Prime the pump. Start at the beginning of the tag... */
>     dptr = ctx->tag_start_bucket;
>     ap_bucket_read (dptr, &tmp_from, &tmp_from_len, 0);  /* Read the bucket... */
> 
>     /* Adjust the pointer to start at the tag within the bucket... */
>     if (dptr == ctx->tail_start_bucket) {
>         tmp_from_len -= (tmp_from_len - ctx->tail_start_index);
>     }
>     tmp_from          = &tmp_from[ctx->tag_start_index];
>     tmp_from_len     -= ctx->tag_start_index;
>     ctx->curr_tag_pos = ctx->combined_tag;
>     ctx_end           = AP_BRIGADE_LAST(ctx->ssi_tag_brigade);
>     bb_end            = AP_BRIGADE_LAST(bb);
> 
>     do {   /* Loop through and copy all of the pertinent bucket contents into */
>         memcpy (ctx->curr_tag_pos, tmp_from, tmp_from_len);    /* the buffer. */
>         ctx->curr_tag_pos += tmp_from_len;
> 
>         if (dptr == ctx->tail_start_bucket) { /* If I just copied the contents*/
>             done = 1;                         /*  of the tail_start_bucket    */
>         }                                     /*  then I'm done.              */
>         else {                                       /* If I reach the end of */
>             dptr = AP_BUCKET_NEXT (dptr);            /* ssi_tag_brigade then I*/
>             if (AP_BUCKET_PREV(dptr) == ctx_end) {   /* need to continue with */
>                 dptr = AP_BRIGADE_FIRST(bb);         /* the current brigade,  */
>             }                             /* until I find tail_start_bucket...*/
>             
>             ap_bucket_read (dptr, &tmp_from, &tmp_from_len, 0);
> 
>             if (dptr == ctx->tail_start_bucket) {
>                 tmp_from_len -= (tmp_from_len - ctx->tail_start_index);
>             }
>         }
>     } while ((!done) && (AP_BUCKET_PREV(dptr) != bb_end) &&
>              ((ctx->curr_tag_pos - ctx->combined_tag) < ctx->tag_length));
> 
>     ctx->combined_tag[ctx->tag_length] = '\0';
>     ctx->curr_tag_pos = ctx->combined_tag;
> 
>     return (APR_SUCCESS);
> }
> 
334,336c509,514
<  * extract the next tag name and value.
<  * if there are no more tags, set the tag name to 'done'
<  * the tag value is html decoded if dodecode is non-zero
---
>  * Extract the next tag name and value.
>  * If there are no more tags, set the tag name to NULL.
>  * The tag value is html decoded if dodecode is non-zero.
>  * The tag value may be NULL if there is no tag value..
>  *    format:
>  *        [WS]<Tag>[WS]=[WS]['|"]<Value>['|"|WS]
339,345c517
< static char *get_tag(apr_pool_t *p, ap_bucket *in, char *tag, int tagbuf_len, int dodecode, apr_off_t *offset)
< {
<     ap_bucket *dptr = in;
<     const char *c;
<     const char *str;
<     apr_size_t length; 
<     char *t = tag, *tag_val, term;
---
> #define SKIP_TAG_WHITESPACE(ptr) while ((*ptr != '\0') && (apr_isspace (*ptr))) ptr++
347,348c519,523
<     /* makes code below a little less cluttered */
<     --tagbuf_len;
---
> static char *get_tag_and_value(include_ctx_t *ctx, char **tag, int dodecode)
> {
>     char *c = ctx->curr_tag_pos;
>     int   shift_val = 0; 
>     char *tag_val = NULL, term = '\0';
350,365c525,526
<     /* Remove all whitespace */
<     do {
<         ap_bucket_read(dptr, &str, &length, 0);
<         c = str + *offset;
<         *offset = 0;
<         while (c - str < length) {
<             if (!apr_isspace(*c)) {
<                 break;
<             }
<             c++;
<         }
<         if (!apr_isspace(*c)) {
<             break;
<         }
<         dptr = AP_BUCKET_NEXT(dptr);
<     } while (dptr);
---
>     SKIP_TAG_WHITESPACE(c);
>     *tag = c;             /* First non-whitespace character (could be NULL). */
367,368c528,529
<     /* tags can't start with - */
<     if (*c == '-') {
---
>     while ((*c != '\0') && (*c != '=') && (!apr_isspace(*c))) {
>         *c = apr_tolower(*c);    /* find end of tag, lowercasing as we go... */
370,388d530
<         if (c == '\0') {
<             ap_bucket_read(dptr, &str, &length, 0);
<             c = str;
<         }
<         if (*c == '-') {
<             do {
<                 c++;
<                 if (c == '\0') {
<                     ap_bucket_read(dptr, &str, &length, 0);
<                     c = str;
<                 }
<             } while (apr_isspace(*c));
<             if (*c == '>') {
<                 apr_cpystrn(tag, "done", tagbuf_len);
<                 *offset = c - str;
<                 return tag;
<             }
<         }
<         return NULL;            /* failed */
391,404c533,550
<     /* find end of tag name */
<     while (1) {
<         if (t - tag == tagbuf_len) {
<             *t = '\0';
<             return NULL;
<         }
<         if (*c == '=' || apr_isspace(*c)) {
<             break;
<         }
<         *(t++) = apr_tolower(*c);
<         c++;
<         if (c == '\0') {
<             ap_bucket_read(dptr, &str, &length, 0);
<             c = str;
---
>     if ((*c == '\0') || (**tag == '=')) {
>         if ((**tag == '\0') || (**tag == '=')) {
>             *tag = NULL;
>         }
>         ctx->curr_tag_pos = c;
>         return NULL; /* We have found the end of the buffer. */
>     }                /* We might have a tag, but definitely no value. */
> 
>     if (*c == '=') {
>         *c++ = '\0';     /* Overwrite the '=' with a terminating byte after tag. */
>     }
>     else {               /* Try skipping WS to find the '='. */
>         *c++ = '\0';     /* Terminate the tag... */
>         SKIP_TAG_WHITESPACE(c);
>         
>         if (*c != '=') {     /* There needs to be an equal sign if there's a value. */
>             ctx->curr_tag_pos = c;
>             return NULL;  /* There apparently was no value. */
406,415c552,553
<     }
< 
<     *t++ = '\0';
<     tag_val = t;
< 
<     while (apr_isspace(*c)) {
<         c++;
<         if (c == '\0') {
<             ap_bucket_read(dptr, &str, &length, 0);
<             c = str;
---
>         else {
>             c++; /* Skip the equals sign. */
418,421d555
<     if (*c != '=') {
<         /* XXX may need to ungetc() here (see pre-bucketized code) */
<         return NULL;
<     }
423,434c557,559
<     do {
<         c++;
<         if (c == '\0') {
<             ap_bucket_read(dptr, &str, &length, 0);
<             c = str;
<         }
<     } while (apr_isspace(*c));
< 
<     /* we should allow a 'name' as a value */
< 
<     if (*c != '"' && *c != '\'') {
<         return NULL;
---
>     SKIP_TAG_WHITESPACE(c);
>     if (*c == '"' || *c == '\'') {    /* Allow quoted values for space inclusion. */
>         term = *c++;     /* NOTE: This does not pass the quotes on return. */
436,449c561,566
<     term = *c;
<     while (1) {
<         c++;
<         if (c == '\0') {
<             ap_bucket_read(dptr, &str, &length, 0);
<             c = str;
<         }
<         if (t - tag == tagbuf_len) {
<             *t = '\0';
<             return NULL;
<         }
< /* Want to accept \" as a valid character within a string. */
<         if (*c == '\\') {
<             *(t++) = *c;         /* Add backslash */
---
>     
>     tag_val = c;
>     while ((*c != '\0') &&
>            (((term != '\0') && (*c != term)) ||
>             ((term == '\0') && (!apr_isspace(*c))))) {
>         if (*c == '\\') {  /* Accept \" and \' as valid char in string. */
451,456c568,572
<             if (c == '\0') {
<                 ap_bucket_read(dptr, &str, &length, 0);
<                 c = str;
<             }
<             if (*c == term) {    /* Only if */
<                 *(--t) = *c;     /* Replace backslash ONLY for terminator */
---
>             if (*c == term) { /* Overwrite the "\" during the embedded  */
>                 shift_val++;  /* escape sequence of '\"' or "\'". Shift */
>             }                 /* bytes from here to next delimiter.     */
>             if (shift_val > 0) {
>                 *(c-shift_val) = *c;
459,460c575,578
<         else if (*c == term) {
<             break;
---
> 
>         c++;
>         if (shift_val > 0) {
>             *(c-shift_val) = *c;
462d579
<         *(t++) = *c;
464c581,583
<     *t = '\0';
---
>     
>     *c++ = '\0'; /* Overwrites delimiter (term or WS) with NULL. */
>     ctx->curr_tag_pos = c;
468,469c587,588
<     *offset = c - str + 1;
<     return apr_pstrdup(p, tag_val);
---
> 
>     return (tag_val);
472c591
< static int get_directive(ap_bucket *in, char *dest, size_t len, apr_pool_t *p)
---
> static char *get_directive(include_ctx_t *ctx, dir_token_id *fnd_token)
474,478c593,595
<     ap_bucket *dptr = in;
<     char *d = dest;
<     const char *c;
<     const char *str;
<     apr_size_t length; 
---
>     char *c = ctx->curr_tag_pos;
>     char *dest;
>     int len = 0;
480,481c597
<     /* make room for nul terminator */
<     --len;
---
>     SKIP_TAG_WHITESPACE(c);
483,496c599,603
<     while (dptr) {
<         ap_bucket_read(dptr, &str, &length, 0);
<         /* need to start past the <!--#
<          */
<         c = str + strlen(STARTING_SEQUENCE);
<         while (c - str < length) {
<             if (!apr_isspace(*c)) {
<                 break;
<             }
<         }
<         if (!apr_isspace(*c)) {
<             break;
<         }
<         dptr = AP_BUCKET_NEXT(dptr);
---
>     dest = c;
>     /* now get directive */
>     while ((*c != '\0') && (!apr_isspace(*c))) {
>         *c++ = apr_tolower(*c);
>         len++;
497a605,607
>     
>     *c++ = '\0';
>     ctx->curr_tag_pos = c;
499,514c609,631
<     /* now get directive */
<     while (dptr) {
<         if (c - str >= length) {
<             ap_bucket_read(dptr, &str, &length, 0);
<         }
<         while (c - str < length) {
< 	    if (d - dest == (int)len) {
< 	        return 1;
< 	    }
<             *d++ = apr_tolower(*c);
<             c++;
<             if (apr_isspace(*c)) {
<                 break;
<             }
<         }
<         if (apr_isspace(*c)) {
---
>     *fnd_token = TOK_UNKNOWN;
>     switch (len) {
>     case 2: if      (!strcmp(dest, "if"))       *fnd_token = TOK_IF;
>             break;
>     case 3: if      (!strcmp(dest, "set"))      *fnd_token = TOK_SET;
>             break;
>     case 4: if      (!strcmp(dest, "else"))     *fnd_token = TOK_ELSE;
>             else if (!strcmp(dest, "elif"))     *fnd_token = TOK_ELIF;
>             else if (!strcmp(dest, "exec"))     *fnd_token = TOK_EXEC;
>             else if (!strcmp(dest, "echo"))     *fnd_token = TOK_ECHO;
> #ifdef USE_PERL_SSI                             
>             else if (!strcmp(dest, "perl"))     *fnd_token = TOK_PERL;
> #endif
>             break;
>     case 5: if      (!strcmp(dest, "endif"))    *fnd_token = TOK_ENDIF;
>             else if (!strcmp(dest, "fsize"))    *fnd_token = TOK_FSIZE;
>             break;
>     case 6: if      (!strcmp(dest, "config"))   *fnd_token = TOK_CONFIG;
>             break;
>     case 7: if      (!strcmp(dest, "include"))  *fnd_token = TOK_INCLUDE;
>             break;
>     case 8: if      (!strcmp(dest, "flastmod")) *fnd_token = TOK_FLASTMOD;
>             else if (!strcmp(dest, "printenv")) *fnd_token = TOK_PRINTENV;
516,517d632
<         }
<         dptr = AP_BUCKET_NEXT(dptr);
519,520c634,635
<     *d = '\0';
<     return 0;
---
> 
>     return (dest);
553c668
< 		char var[MAX_STRING_LEN];
---
> /* pjr hack     char var[MAX_STRING_LEN]; */
555c670
< 		const char *end_of_var_name;	/* end of var name + 1 */
---
> 		char *end_of_var_name;	/* end of var name + 1 */
557a673
>                 char        tmp_store;
573c689
< 		    end_of_var_name = in;
---
> 		    (const char *)end_of_var_name = in;
581c697
< 		    end_of_var_name = in;
---
> 		    (const char *)end_of_var_name = in;
587,589c703,713
< 		    l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l;
< 		    memcpy(var, start_of_var_name, l);
< 		    var[l] = '\0';
---
> /* pjr - this is a test hack to avoid a memcpy. Make sure that this works...
> *		    l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l;
> *		    memcpy(var, start_of_var_name, l);
> *		    var[l] = '\0';
> *
> *		    val = apr_table_get(r->subprocess_env, var);
> */
> /* pjr hack */      tmp_store        = *end_of_var_name;
> /* pjr hack */      *end_of_var_name = '\0';
> /* pjr hack */      val = apr_table_get(r->subprocess_env, start_of_var_name);
> /* pjr hack */      *end_of_var_name = tmp_store;
591d714
< 		    val = apr_table_get(r->subprocess_env, var);
628c751,752
< static int include_cgi(char *s, request_rec *r, ap_filter_t *next)
---
> static int include_cgi(char *s, request_rec *r, ap_filter_t *next,
>                        ap_bucket *head_ptr, ap_bucket **inserted_head)
631a756
>     ap_bucket  *tmp_buck, *tmp2_buck;
663a789
>         apr_ssize_t len_loc;
664a791
> 
666c793,808
<         ap_rvputs(r, "<A HREF=\"", location, "\">", location, "</A>", NULL);
---
>         len_loc = strlen(location);
> 
>         tmp_buck = ap_bucket_create_immortal("<A HREF=\"", sizeof("<A HREF=\""));
>         AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
>         tmp2_buck = ap_bucket_create_transient(location, len_loc);
>         AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
>         tmp2_buck = ap_bucket_create_immortal("\">", sizeof("\">"));
>         AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
>         tmp2_buck = ap_bucket_create_transient(location, len_loc);
>         AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
>         tmp2_buck = ap_bucket_create_immortal("</A>", sizeof("</A>"));
>         AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
> 
>         if (*inserted_head == NULL) {
>             *inserted_head = tmp_buck;
>         }
715,716c857,858
< static int handle_include(ap_bucket *in, request_rec *r, ap_filter_t *next,
<                           const char *error, int noexec)
---
> static int handle_include(include_ctx_t *ctx, request_rec *r, ap_filter_t *next,
>                           ap_bucket *head_ptr, ap_bucket **inserted_head)
718c860,862
<     char tag[MAX_STRING_LEN];
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
>     ap_bucket  *tmp_buck;
720,721d863
<     char *tag_val;
<     apr_off_t offset = strlen("include ") + strlen(STARTING_SEQUENCE);
722a865
>     *inserted_head = NULL;
724,725c867,873
<         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) {
<             return 1;
---
>         if (!(tag_val = get_tag_and_value(ctx, &tag, 1))) {
>             if (tag == NULL) {
>                 return (0);
>             }
>             else {
>                 return (1);
>             }
750c898
<             if (!error_fmt && noexec && rr->content_type
---
>             if (!error_fmt && (ctx->flags & FLAG_NO_EXEC) && rr->content_type
797c945,947
< 	    /* see the Kludge in send_parsed_file for why */
---
> 	    /* See the Kludge in send_parsed_file for why */
>             /* Basically, it puts a bread crumb in here, then looks */
>             /*   for the crumb later to see if its been here.       */
813c963
<                 ap_rputs(error, r);
---
>                 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
816c966
< 	    /* destroy the sub request if it's not a nested include */
---
> 	    /* destroy the sub request if it's not a nested include (crumb) */
823,825d972
<         else if (!strcmp(tag, "done")) {
<             return 0;
<         }
830c977
<             ap_rputs(error, r);
---
>             CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
951c1098
<         rc = ap_os_create_privileged_process(r, procnew, s, argv, ap_create_environment(r->pool, env), procattr, r->pool);
---
>         rc = apr_create_process(procnew, s, argv, ap_create_environment(r->pool, env), procattr, r->pool);
982,983c1129,1130
< static int handle_exec(ap_bucket *in, request_rec *r, const char *error,
<                        ap_filter_t *next)
---
> static int handle_exec(include_ctx_t *ctx, request_rec *r, ap_filter_t *next,
>                        ap_bucket *head_ptr, ap_bucket **inserted_head)
985,986c1132,1133
<     char tag[MAX_STRING_LEN];
<     char *tag_val;
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
987a1135
>     ap_bucket  *tmp_buck;
989d1136
<     apr_off_t offset = strlen("exec ") + strlen(STARTING_SEQUENCE);
990a1138
>     *inserted_head = NULL;
992,993c1140,1146
<         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) {
<             return 1;
---
>         if (!(tag_val = get_tag_and_value(ctx, &tag, 1))) {
>             if (tag == NULL) {
>                 return (0);
>             }
>             else {
>                 return 1;
>             }
1000,1002c1153,1154
<                             "to tag exec in file %s",
<                             tag, r->filename);
<                 ap_rputs(error, r);
---
>                             "to tag exec in file %s", tag, r->filename);
>                 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
1009c1161
<             if (include_cgi(parsed_string, r, next) == -1) {
---
>             if (include_cgi(parsed_string, r, next, head_ptr, inserted_head) == -1) {
1012c1164
<                 ap_rputs(error, r);
---
>                 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
1016,1018d1167
<         else if (!strcmp(tag, "done")) {
<             return 0;
<         }
1021,1023c1170,1171
<                         "unknown parameter \"%s\" to tag exec in %s",
<                         tag, file);
<             ap_rputs(error, r);
---
>                         "unknown parameter \"%s\" to tag exec in %s", tag, file);
>             CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
1029c1177,1178
< static int handle_echo(ap_bucket *in, request_rec *r, const char *error)
---
> static int handle_echo(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                            ap_bucket **inserted_head)
1031,1032c1180,1184
<     char tag[MAX_STRING_LEN];
<     char *tag_val;
---
>     char       *tag       = NULL;
>     char       *tag_val   = NULL;
>     const char *echo_text = NULL;
>     ap_bucket  *tmp_buck;
>     apr_ssize_t e_len;
1034d1185
<     apr_off_t offset = strlen("echo ") + strlen(STARTING_SEQUENCE);
1037a1189
>     *inserted_head = NULL;
1039,1040c1191,1197
<         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) {
<             return 1;
---
>         if (!(tag_val = get_tag_and_value(ctx, &tag, 1))) {
>             if (tag != NULL) {
>                 return (1);
>             }
>             else {
>                 return (0);
>             }
1043a1201
>             int b_copy = 0;
1046,1053c1204,1207
< 		if (encode == E_NONE) {
< 		    ap_rputs(val, r);
< 		}
< 		else if (encode == E_URL) {
< 		    ap_rputs(ap_escape_uri(r->pool, val), r);
< 		}
< 		else if (encode == E_ENTITY) {
< 		    ap_rputs(ap_escape_html(r->pool, val), r);
---
>                 switch(encode) {
>                 case E_NONE:   echo_text = val;  b_copy = 1;             break;
>                 case E_URL:    echo_text = ap_escape_uri(r->pool, val);  break;
>                 case E_ENTITY: echo_text = ap_escape_html(r->pool, val); break;
1054a1209,1211
> 
>                 e_len = strlen(echo_text);
>                 tmp_buck = ap_bucket_create_transient(echo_text, e_len);
1057c1214,1218
<                 ap_rputs("(none)", r);
---
>                 tmp_buck = ap_bucket_create_immortal("(none)", sizeof("none"));
>             }
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
>             if (*inserted_head == NULL) {
>                 *inserted_head = tmp_buck;
1059,1061d1219
<         }
<         else if (!strcmp(tag, "done")) {
<             return 0;
1070,1072c1228,1229
< 			    "tag echo in %s",
< 			    tag_val, r->filename);
< 		ap_rputs(error, r);
---
> 			    "tag echo in %s", tag_val, r->filename);
>                 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
1075d1231
< 
1078c1234
<                         "unknown parameter \"%s\" to tag echo in %s",
---
>                         "unknown parameter \"%s\" in tag echo of %s",
1080c1236
<             ap_rputs(error, r);
---
>             CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
1081a1238
> 
1086c1243
< static int handle_perl(ap_bucket *in, request_rec *r, const char *error)
---
> static int handle_perl(include_ctx_t *ctx, request_rec *r)
1088c1245,1246
<     char tag[MAX_STRING_LEN];
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
1090d1247
<     char *tag_val;
1093d1249
<     apr_off_t offset = strlen("perl ") + strlen(STARTING_SEQUENCE);
1095c1251
<     if (ap_allow_options(r) & OPT_INCNOEXEC) {
---
>     if (ctx->flags & FLAG_NO_EXEC) {
1099c1255
<         return DECLINED;
---
>         return (DECLINED);
1100a1257
> 
1102c1259
<         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) {
---
>         if (!(tag_val = get_tag_and_value(ctx, &tag, 1))) {
1112,1114d1268
<         else if (strnEQ(tag, "done", 4)) {
<             break;
<         }
1115a1270
> 
1119c1274,1275
<     return OK;
---
> 
>     return (OK);
1126,1127c1282,1283
< static int handle_config(ap_bucket *in, request_rec *r, char *error, char *tf,
<                          int *sizefmt)
---
> static int handle_config(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                            ap_bucket **inserted_head)
1129,1130c1285,1286
<     char tag[MAX_STRING_LEN];
<     char *tag_val;
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
1133d1288
<     apr_off_t offset = strlen("config ") + strlen(STARTING_SEQUENCE);
1134a1290
>     *inserted_head = NULL;
1136,1137c1292,1298
<         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0, &offset))) {
<             return 1;
---
>         if (!(tag_val = get_tag_and_value(ctx, &tag, 0))) {
>             if (tag == NULL) {
>                 return 0;  /* Reached the end of the string. */
>             }
>             else {
>                 return 1;  /* tags must have values. */
>             }
1140c1301,1302
<             parse_string(r, tag_val, error, MAX_STRING_LEN, 0);
---
>             parse_string(r, tag_val, ctx->error_str, MAX_STRING_LEN, 0);
>             ctx->error_length = strlen(ctx->error_str);
1145,1147c1307,1309
<             parse_string(r, tag_val, tf, MAX_STRING_LEN, 0);
<             apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, tf, 0));
<             apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, tf, 1));
---
>             parse_string(r, tag_val, ctx->time_str, MAX_STRING_LEN, 0);
>             apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, ctx->time_str, 0));
>             apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, ctx->time_str, 1));
1149c1311
<                       ap_ht_time(r->pool, r->finfo.mtime, tf, 0));
---
>                            ap_ht_time(r->pool, r->finfo.mtime, ctx->time_str, 0));
1155c1317
<                 *sizefmt = SIZEFMT_BYTES;
---
>                 ctx->flags |= FLAG_SIZE_IN_BYTES;
1158c1320
<                 *sizefmt = SIZEFMT_KMG;
---
>                 ctx->flags &= FLAG_SIZE_ABBREV;
1161,1163d1322
<         else if (!strcmp(tag, "done")) {
<             return 0;
<         }
1164a1324,1325
>             ap_bucket  *tmp_buck;
> 
1168c1329
<             ap_rputs(error, r);
---
>             CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
1175c1336
<                      char *tag_val, apr_finfo_t *finfo, const char *error)
---
>                      char *tag_val, apr_finfo_t *finfo)
1211d1371
<             ap_rputs(error, r);
1232d1391
<             ap_rputs(error, r);
1241d1399
<         ap_rputs(error, r);
1245a1404,1429
> #define NEG_SIGN  "    -"
> #define ZERO_K    "   0k"
> #define ONE_K     "   1k"
> 
> static void generate_size(apr_ssize_t size, char *buff, apr_ssize_t buff_size)
> {
>     /* XXX: this -1 thing is a gross hack */
>     if (size == (apr_ssize_t)-1) {
> 	memcpy (buff, NEG_SIGN, sizeof(NEG_SIGN)+1);
>     }
>     else if (!size) {
> 	memcpy (buff, ZERO_K, sizeof(ZERO_K)+1);
>     }
>     else if (size < 1024) {
> 	memcpy (buff, ONE_K, sizeof(ONE_K)+1);
>     }
>     else if (size < 1048576) {
>         apr_snprintf(buff, buff_size, "%4" APR_SSIZE_T_FMT "k", (size + 512) / 1024);
>     }
>     else if (size < 103809024) {
>         apr_snprintf(buff, buff_size, "%4.1fM", size / 1048576.0);
>     }
>     else {
>         apr_snprintf(buff, buff_size, "%4" APR_SSIZE_T_FMT "M", (size + 524288) / 1048576);
>     }
> }
1247c1431,1432
< static int handle_fsize(ap_bucket *in, request_rec *r, const char *error, int sizefmt)
---
> static int handle_fsize(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                         ap_bucket **inserted_head)
1249,1251c1434,1438
<     char tag[MAX_STRING_LEN];
<     char *tag_val;
<     apr_finfo_t finfo;
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
>     apr_finfo_t  finfo;
>     apr_ssize_t  s_len, s_wrt;
>     ap_bucket   *tmp_buck;
1253d1439
<     apr_off_t offset = strlen("fsize ") + strlen(STARTING_SEQUENCE);
1254a1441
>     *inserted_head = NULL;
1256,1260c1443,1449
<         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) {
<             return 1;
<         }
<         else if (!strcmp(tag, "done")) {
<             return 0;
---
>         if (!(tag_val = get_tag_and_value(ctx, &tag, 1))) {
>             if (tag == NULL) {
>                 return 0;
>             }
>             else {
>                 return 1;
>             }
1264,1266c1453,1458
<             if (!find_file(r, "fsize", tag, parsed_string, &finfo, error)) {
<                 if (sizefmt == SIZEFMT_KMG) {
<                     ap_send_size(finfo.size, r);
---
>             if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
>                 char buff[50];
> 
>                 if (!(ctx->flags & FLAG_SIZE_IN_BYTES)) {
>                     generate_size(finfo.size, buff, sizeof(buff));
>                     s_len = strlen (buff);
1269,1271c1461,1465
<                     int l, x;
<                     apr_snprintf(tag, sizeof(tag), "%" APR_OFF_T_FMT, finfo.size);
<                     l = strlen(tag);    /* grrr */
---
>                     int l, x, pos = 0;
>                     char tmp_buff[50];
> 
>                     apr_snprintf(tmp_buff, sizeof(tmp_buff), "%" APR_OFF_T_FMT, finfo.size);
>                     l = strlen(tmp_buff);    /* grrr */
1274c1468
<                             ap_rputc(',', r);
---
>                             buff[pos++] = ',';
1276c1470
<                         ap_rputc(tag[x], r);
---
>                         buff[pos++] = tmp_buff[x];
1277a1472,1473
>                     buff[pos] = '\0';
>                     s_len = pos;
1278a1475,1483
> 
>                 tmp_buck = ap_bucket_create_heap(buff, s_len, 1, &s_wrt);
>                 AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
>                 if (*inserted_head == NULL) {
>                     *inserted_head = tmp_buck;
>                 }
>             }
>             else {
>                 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
1284c1489,1490
< static int handle_flastmod(ap_bucket *in, request_rec *r, const char *error, const char *tf)
---
> static int handle_flastmod(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                            ap_bucket **inserted_head)
1286,1288c1492,1496
<     char tag[MAX_STRING_LEN];
<     char *tag_val;
<     apr_finfo_t finfo;
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
>     apr_finfo_t  finfo;
>     apr_ssize_t  t_len;
>     ap_bucket   *tmp_buck;
1290d1497
<     apr_off_t offset = strlen("flastmod ") + strlen(STARTING_SEQUENCE);
1291a1499
>     *inserted_head = NULL;
1293,1297c1501,1507
<         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) {
<             return 1;
<         }
<         else if (!strcmp(tag, "done")) {
<             return 0;
---
>         if (!(tag_val = get_tag_and_value(ctx, &tag, 1))) {
>             if (tag == NULL) {
>                 return 0;
>             }
>             else {
>                 return 1;
>             }
1301,1302c1511,1524
<             if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) {
<                 ap_rputs(ap_ht_time(r->pool, finfo.mtime, tf, 0), r);
---
>             if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
>                 char *t_val;
> 
>                 t_val = ap_ht_time(r->pool, finfo.mtime, ctx->time_str, 0);
>                 t_len = strlen(t_val);
> 
>                 tmp_buck = ap_bucket_create_transient(t_val, t_len);
>                 AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
>                 if (*inserted_head == NULL) {
>                     *inserted_head = tmp_buck;
>                 }
>             }
>             else {
>                 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
1342a1565
>     int tkn_fnd = 0;
1429c1652
<     for (ch = *string; ch != '\0'; ch = *++string) {
---
>     for (ch = *string; ((ch != '\0') && (!tkn_fnd)); ch = *++string) {
1432c1655,1658
<                 goto TOKEN_DONE;
---
>                 tkn_fnd = 1;
>             }
>             else {
>                 token->value[next++] = ch;
1434,1435d1659
<             token->value[next++] = ch;
<             continue;
1437,1452c1661,1664
<         if (!qs) {
<             if (apr_isspace(ch)) {
<                 goto TOKEN_DONE;
<             }
<             switch (ch) {
<             case '(':
<                 goto TOKEN_DONE;
<             case ')':
<                 goto TOKEN_DONE;
<             case '=':
<                 goto TOKEN_DONE;
<             case '!':
<                 goto TOKEN_DONE;
<             case '|':
<                 if (*(string + 1) == '|') {
<                     goto TOKEN_DONE;
---
>         else {
>             if (!qs) {
>                 if (apr_isspace(ch)) {
>                     tkn_fnd = 1;
1454,1457c1666,1689
<                 break;
<             case '&':
<                 if (*(string + 1) == '&') {
<                     goto TOKEN_DONE;
---
>                 else {
>                     switch (ch) {
>                     case '(':
>                     case ')':
>                     case '=':
>                     case '!':
>                     case '<':
>                     case '>':
>                         tkn_fnd = 1;
>                         break;
>                     case '|':
>                         if (*(string + 1) == '|') {
>                             tkn_fnd = 1;
>                         }
>                         break;
>                     case '&':
>                         if (*(string + 1) == '&') {
>                             tkn_fnd = 1;
>                         }
>                         break;
>                     }
>                     if (!tkn_fnd) {
>                         token->value[next++] = ch;
>                     }
1459,1463d1690
<                 break;
<             case '<':
<                 goto TOKEN_DONE;
<             case '>':
<                 goto TOKEN_DONE;
1465,1471c1692,1700
<             token->value[next++] = ch;
<         }
<         else {
<             if (ch == '\'') {
<                 qs = 0;
<                 ++string;
<                 goto TOKEN_DONE;
---
>             else {
>                 if (ch == '\'') {
>                     qs = 0;
>                     ++string;
>                     tkn_fnd = 1;
>                 }
>                 else {
>                     token->value[next++] = ch;
>                 }
1473d1701
<             token->value[next++] = ch;
1476c1704
<   TOKEN_DONE:
---
> 
2045c2273
< 			"bad token type");
---
>                           "bad token type");
2057,2063c2285,2307
< static int handle_if(ap_bucket *in, request_rec *r, const char *error,
<                      int *conditional_status, int *printing)
< {
<     char tag[MAX_STRING_LEN];
<     char *tag_val;
<     char *expr;
<     apr_off_t offset = strlen("if ") + strlen(STARTING_SEQUENCE);
---
> #define LOG_COND_STATUS(cntx, t_buck, h_ptr, ins_head, tag_text)             \
>             char *cond_txt = "**** X     conditional_status=\"0\"\n";        \
>                                                                              \
>             if (cntx->flags & FLAG_COND_TRUE) {                              \
>                 cont_txt[31] = '1';                                          \
>             }                                                                \
>             memcpy(&cond_txt[5], tag_text, sizeof(tag_text));                \
>             t_buck = ap_bucket_create_transient(cond_txt, sizeof(cond_txt)); \
>             AP_BUCKET_INSERT_BEFORE(h_ptr, t_buck);                          \
>                                                                              \
>             if (ins_head == NULL) {                                          \
>                 ins_head = t_buck;                                           \
>             }
>             
> 
> /* pjr - These seem to allow expr="fred" expr="joe" where joe overwrites fred. */
> static int handle_if(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                      ap_bucket **inserted_head)
> {
>     char *tag     = NULL;
>     char *tag_val = NULL;
>     char *expr    = NULL;
>     ap_bucket *tmp_buck;
2065c2309
<     expr = NULL;
---
>     *inserted_head = NULL;
2067,2071c2311,2312
<         tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0, &offset);
<         if (*tag == '\0') {
<             return 1;
<         }
<         else if (!strcmp(tag, "done")) {
---
>         tag_val = get_tag_and_value(ctx, &tag, 0);
>         if (tag == NULL) {
2074,2076c2315,2317
< 			    "missing expr in if statement: %s",
< 			    r->filename);
< 		ap_rputs(error, r);
---
> 			    "missing expr in if statement: %s", r->filename);
> 
>                 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
2079c2320,2325
<             *printing = *conditional_status = parse_expr(r, expr, error);
---
>             if (parse_expr(r, expr, ctx->error_str)) {
>                 ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE);
>             }
>             else {
>                 ctx->flags &= FLAG_CLEAR_PRINT_COND;
>             }
2081,2082c2327
<             ap_rvputs(r, "**** if conditional_status=\"",
<                    *conditional_status ? "1" : "0", "\"\n", NULL);
---
>             LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, "   if");
2089c2334,2344
<             ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
---
>             ap_bucket *tmp2_buck;
>             tmp_buck = ap_bucket_create_immortal("**** if expr=\"", 15);
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
>             tmp2_buck = ap_bucket_create_transient(expr, strlen(expr));
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
>             tmp2_buck = ap_bucket_create_immortal("\"\n", sizeof("\"\n");
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
> 
>             if (*inserted_head == NULL) {
>                 *inserted_head = tmp_buck;
>             }
2094,2096c2349,2350
<                         "unknown parameter \"%s\" to tag if in %s",
<                         tag, r->filename);
<             ap_rputs(error, r);
---
>                         "unknown parameter \"%s\" to tag if in %s", tag, r->filename);
>             CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
2097a2352
> 
2101,2102c2356,2357
< static int handle_elif(ap_bucket *in, request_rec *r, const char *error,
<                        int *conditional_status, int *printing)
---
> static int handle_elif(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                        ap_bucket **inserted_head)
2104,2107c2359,2362
<     char tag[MAX_STRING_LEN];
<     char *tag_val;
<     char *expr;
<     apr_off_t offset = strlen("elif ") + strlen(STARTING_SEQUENCE);
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
>     char *expr    = NULL;
>     ap_bucket *tmp_buck;
2109c2364
<     expr = NULL;
---
>     *inserted_head = NULL;
2111,2115c2366,2367
<         tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0, &offset);
<         if (*tag == '\0') {
<             return 1;
<         }
<         else if (!strcmp(tag, "done")) {
---
>         tag_val = get_tag_and_value(ctx, &tag, 0);
>         if (tag == '\0') {
2117,2118c2369
<             ap_rvputs(r, "**** elif conditional_status=\"",
<                    *conditional_status ? "1" : "0", "\"\n", NULL);
---
>             LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, " elif");
2120,2121c2371,2372
<             if (*conditional_status) {
<                 *printing = 0;
---
>             if (ctx->flags & FLAG_COND_TRUE) {
>                 ctx->flags &= FLAG_CLEAR_PRINTING;
2126,2129c2377,2379
< 			    "missing expr in elif statement: %s",
< 			    r->filename);
< 		ap_rputs(error, r);
< 		return 1;
---
> 			    "missing expr in elif statement: %s", r->filename);
>                 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
> 		return (1);
2131c2381,2386
<             *printing = *conditional_status = parse_expr(r, expr, error);
---
>             if (parse_expr(r, expr, ctx->error_str)) {
>                 ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE);
>             }
>             else {
>                 ctx->flags &= FLAG_CLEAR_PRINT_COND;
>             }
2133,2134c2388
<             ap_rvputs(r, "**** elif conditional_status=\"",
<                    *conditional_status ? "1" : "0", "\"\n", NULL);
---
>             LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, " elif");
2136c2390
<             return 0;
---
>             return (0);
2141c2395,2405
<             ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
---
>             ap_bucket *tmp2_buck;
>             tmp_buck = ap_bucket_create_immortal("**** elif expr=\"", 17);
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
>             tmp2_buck = ap_bucket_create_transient(expr, strlen(expr));
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
>             tmp2_buck = ap_bucket_create_immortal("\"\n", sizeof("\"\n");
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
> 
>             if (*inserted_head == NULL) {
>                 *inserted_head = tmp_buck;
>             }
2146,2148c2410,2411
<                         "unknown parameter \"%s\" to tag if in %s",
<                         tag, r->filename);
<             ap_rputs(error, r);
---
>                         "unknown parameter \"%s\" to tag if in %s", tag, r->filename);
>             CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
2153,2154c2416,2417
< static int handle_else(ap_bucket *in, request_rec *r, const char *error,
<                        int *conditional_status, int *printing)
---
> static int handle_else(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                        ap_bucket **inserted_head)
2156,2157c2419,2421
<     char tag[MAX_STRING_LEN];
<     apr_off_t offset = strlen("else ") + strlen(STARTING_SEQUENCE);
---
>     char *tag = NULL;
>     char *tag_val = NULL;
>     ap_bucket *tmp_buck;
2159,2160c2423,2431
<     if (!get_tag(r->pool, in, tag, sizeof(tag), 1, &offset)) {
<         return 1;
---
>     *inserted_head = NULL;
>     tag_val = get_tag_and_value(ctx, &tag, 1);
>     if ((tag != NULL) || (tag_val != NULL)) {
>         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
>                     "else directive does not take tags in %s", r->filename);
>         if (ctx->flags & FLAG_PRINTING) {
>             CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
>         }
>         return -1;
2162c2433
<     else if (!strcmp(tag, "done")) {
---
>     else {
2164,2165c2435
<         ap_rvputs(r, "**** else conditional_status=\"",
<                *conditional_status ? "1" : "0", "\"\n", NULL);
---
>         LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, " else");
2167,2176c2437,2438
<         *printing = !(*conditional_status);
<         *conditional_status = 1;
<         return 0;
<     }
<     else {
<         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
<                     "else directive does not take tags in %s",
< 		    r->filename);
<         if (*printing) {
<             ap_rputs(error, r);
---
>         if (ctx->flags & FLAG_COND_TRUE) {
>             ctx->flags &= FLAG_CLEAR_PRINTING;
2178c2440,2443
<         return -1;
---
>         else {
>             ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE);
>         }
>         return 0;
2182,2183c2447,2448
< static int handle_endif(ap_bucket *in, request_rec *r, const char *error,
<                         int *conditional_status, int *printing)
---
> static int handle_endif(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                         ap_bucket **inserted_head)
2185,2189c2450,2460
<     char tag[MAX_STRING_LEN];
<     apr_off_t offset = strlen("endif ") + strlen(STARTING_SEQUENCE);
< 
<     if (!get_tag(r->pool, in, tag, sizeof(tag), 1, &offset)) {
<         return 1;
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
>     ap_bucket *tmp_buck;
> 
>     *inserted_head = NULL;
>     tag_val = get_tag_and_value(ctx, &tag, 1);
>     if ((tag != NULL) || (tag_val != NULL)) {
>         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
>                     "endif directive does not take tags in %s", r->filename);
>         CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
>         return -1;
2191c2462
<     else if (!strcmp(tag, "done")) {
---
>     else {
2193,2194c2464
<         ap_rvputs(r, "**** endif conditional_status=\"",
<                *conditional_status ? "1" : "0", "\"\n", NULL);
---
>         LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, "endif");
2196,2197c2466
<         *printing = 1;
<         *conditional_status = 1;
---
>         ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE);
2200,2206d2468
<     else {
<         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
<                     "endif directive does not take tags in %s",
< 		    r->filename);
<         ap_rputs(error, r);
<         return -1;
<     }
2209c2471,2472
< static int handle_set(ap_bucket *in, request_rec *r, const char *error)
---
> static int handle_set(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                         ap_bucket **inserted_head)
2211c2474,2477
<     char tag[MAX_STRING_LEN];
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
>     char *var     = NULL;
>     ap_bucket *tmp_buck;
2213,2215d2478
<     char *tag_val;
<     char *var;
<     apr_off_t offset = strlen("set ") + strlen(STARTING_SEQUENCE);
2217c2480
<     var = (char *) NULL;
---
>     *inserted_head = NULL;
2219,2222c2482,2483
<         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) {
<             return 1;
<         }
<         else if (!strcmp(tag, "done")) {
---
>         tag_val = get_tag_and_value(ctx, &tag, 1);
>         if ((tag == NULL) && (tag_val == NULL)) {
2224a2486,2488
>         else if (tag_val == NULL) {
>             return 1;
>         }
2233,2234c2497,2498
<                 ap_rputs(error, r);
<                 return -1;
---
>                 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
>                 return (-1);
2237c2501,2502
<             apr_table_setn(r->subprocess_env, var, apr_pstrdup(r->pool, parsed_string));
---
>             apr_table_setn(r->subprocess_env, apr_pstrdup(r->pool, var),
>                            apr_pstrdup(r->pool, parsed_string));
2242c2507
<             ap_rputs(error, r);
---
>             CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
2248c2513,2514
< static int handle_printenv(ap_bucket *in, request_rec *r, const char *error)
---
> static int handle_printenv(include_ctx_t *ctx, request_rec *r, ap_bucket *head_ptr,
>                            ap_bucket **inserted_head)
2250,2255c2516,2526
<     char tag[MAX_STRING_LEN];
<     char *tag_val;
<     apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
<     apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts;
<     int i;
<     apr_off_t offset = strlen("printenv ") + strlen(STARTING_SEQUENCE);
---
>     char *tag     = NULL;
>     char *tag_val = NULL;
>     ap_bucket *tmp_buck;
> 
>     tag_val = get_tag_and_value(ctx, &tag, 1);
>     if ((tag == NULL) && (tag_val == NULL)) {
>         apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
>         apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts;
>         int i;
>         char *key_text, *val_text;
>         apr_ssize_t   k_len, v_len;
2257,2260c2528
<     if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1, &offset))) {
<         return 1;
<     }
<     else if (!strcmp(tag, "done")) {
---
>         *inserted_head = NULL;
2262,2263c2530,2549
<             ap_rvputs(r, ap_escape_html(r->pool, elts[i].key), "=", 
< 		ap_escape_html(r->pool, elts[i].val), "\n", NULL);
---
>             key_text = ap_escape_html(r->pool, elts[i].key);
>             val_text = ap_escape_html(r->pool, elts[i].val);
>             k_len = strlen(key_text);
>             v_len = strlen(val_text);
> 
>             /*  Key_text                                               */
>             tmp_buck = ap_bucket_create_transient(key_text, k_len);
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
>             if (*inserted_head == NULL) {
>                 *inserted_head = tmp_buck;
>             }
>             /*            =                                            */
>             tmp_buck = ap_bucket_create_immortal("=", 1);
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
>             /*              Value_text                                 */
>             tmp_buck = ap_bucket_create_transient(val_text, v_len);
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
>             /*                        newline...                       */
>             tmp_buck = ap_bucket_create_immortal("\n", 1);
>             AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
2269,2271c2555,2556
<                     "printenv directive does not take tags in %s",
< 		    r->filename);
<         ap_rputs(error, r);
---
>                     "printenv directive does not take tags in %s", r->filename);
>         CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
2276a2562,2569
> #define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt)                           \
> if ((AP_BRIGADE_EMPTY(cntxt->ssi_tag_brigade)) && (cntxt->head_start_bucket != NULL)) { \
>     ap_bucket_brigade *tag_plus;                                             \
>                                                                              \
>     tag_plus = ap_brigade_split(brgd, cntxt->head_start_bucket);             \
>     ap_pass_brigade(f->next, brgd);                                          \
>     brgd = tag_plus;                                                         \
> }                                                                            \
2278d2570
< /* -------------------------- The main function --------------------------- */
2280c2572
< /* This is a stub which parses a file descriptor. */
---
> /* -------------------------- The main function --------------------------- */
2282,2284d2573
< typedef struct include_ctx {
<     ap_bucket_brigade *bb;
< } include_ctx;
2288,2294c2577,2578
<     char directive[MAX_STRING_LEN], error[MAX_STRING_LEN];
<     char timefmt[MAX_STRING_LEN];
<     int noexec = ap_allow_options(r) & OPT_INCNOEXEC;
<     int sizefmt;
<     int if_nesting;
<     int printing;
<     int conditional_status;
---
>     include_ctx_t *ctx = f->ctx;
>     int is_buffer_allocated = 0;
2296,2297c2580
<     ap_bucket *tagbuck, *dptr2;
<     ap_bucket *endsec;
---
>     ap_bucket *tmp_dptr, *ctx_end, *bb_end;
2299d2581
<     include_ctx *ctx;
2302,2309d2583
<     apr_cpystrn(error, DEFAULT_ERROR_MSG, sizeof(error));
<     apr_cpystrn(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt));
<     sizefmt = SIZEFMT_KMG;
< 
< /*  Turn printing on */
<     printing = conditional_status = 1;
<     if_nesting = 0;
< 
2320,2327c2594,2613
<     if (!f->ctx) {
<         f->ctx = ctx = apr_pcalloc(r->pool, sizeof(f->ctx));
<         ctx->bb = ap_brigade_create(r->pool);
<     }
<     else {
<         ctx = f->ctx;
<         AP_BRIGADE_CONCAT(*bb, ctx->bb);
<     }
---
>     while (dptr != AP_BRIGADE_SENTINEL(*bb)) {
>         /* State to check for the STARTING_SEQUENCE. */
>         if ((ctx->state == PRE_HEAD) || (ctx->state == PARSE_HEAD)) {
>             int do_cleanup = 0;
>             apr_ssize_t cleanup_bytes = ctx->parse_pos;
> 
>             tmp_dptr = find_start_sequence(dptr, ctx, AP_BRIGADE_LAST(*bb), &do_cleanup);
> 
>             if ((do_cleanup) && (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade))) {
>                 ap_bucket *tmp_bkt;
> 
>                 tmp_bkt = ap_bucket_create_immortal(STARTING_SEQUENCE, cleanup_bytes);
>                 AP_BRIGADE_INSERT_HEAD(*bb, tmp_bkt);
> 
>                 while (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
>                     tmp_bkt = AP_BRIGADE_FIRST(ctx->ssi_tag_brigade);
>                     AP_BUCKET_REMOVE(tmp_bkt);
>                     ap_bucket_destroy(tmp_bkt);
>                 }
>             }
2329,2335c2615,2625
<     AP_BRIGADE_FOREACH(dptr, *bb) {
<         if ((tagbuck = find_string(dptr, STARTING_SEQUENCE, AP_BRIGADE_LAST(*bb))) != NULL) {
<             dptr2 = tagbuck;
<             dptr = tagbuck;
<             endsec = find_string(dptr2, ENDING_SEQUENCE, AP_BRIGADE_LAST(*bb));
<             if (endsec == NULL) {
<                 ap_save_brigade(f, &ctx->bb, bb);
---
>             /* If I am inside a conditional (if, elif, else) that is false
>              *   then I need to throw away anything contained in it.
>              */
>             if ((!(ctx->flags & FLAG_PRINTING)) && (tmp_dptr != NULL)) {
>                 while (dptr != tmp_dptr) {
>                     ap_bucket *free_bucket = dptr;
> 
>                     dptr = AP_BUCKET_NEXT (dptr);
>                     AP_BUCKET_REMOVE(free_bucket);
>                     ap_bucket_destroy(free_bucket);
>                 }
2337,2338c2627,2664
<              
<             /* At this point, everything between tagbuck and endsec is an SSI
---
> 
>             /* Adjust the current bucket position based on what was found... */
>             if ((tmp_dptr != NULL) && (ctx->state == PARSE_TAG)) {
>                 if (ctx->tag_start_bucket != NULL) {
>                     dptr = ctx->tag_start_bucket;
>                 }
>                 else {
>                     dptr = AP_BRIGADE_SENTINEL(*bb);
>                 }
>             }
>             else if (tmp_dptr == NULL) { /* There was no possible SSI tag in the */
>                 dptr = AP_BRIGADE_SENTINEL(*bb);  /* remainder of this brigade...    */
>             }
>         }
> 
>         /* State to check for the ENDING_SEQUENCE. */
>         if (((ctx->state == PARSE_TAG) || (ctx->state == PARSE_TAIL)) &&
>             (dptr != AP_BRIGADE_SENTINEL(*bb))) {
>             tmp_dptr = find_end_sequence(dptr, ctx, AP_BRIGADE_LAST(*bb));
> 
>             if (tmp_dptr != NULL) {
>                 dptr = tmp_dptr;  /* Adjust bucket pos... */
>             }
>             else {
>                 dptr = AP_BRIGADE_SENTINEL(*bb);  /* remainder of this brigade...    */
>             }
>         }
> 
>         /* State to processed the directive... */
>         if (ctx->state == PARSED) {
>             ap_bucket    *content_head = NULL, *tmp_bkt;
>             char          tmp_buf[TMP_BUF_SIZE];
>             char         *directive_str = NULL;
>             dir_token_id  directive_token;
>             int           done = 0;
> 
>             /* At this point, everything between ctx->head_start_bucket and
>              * ctx->tail_start_bucket is an SSI
2341c2667,2668
<             if (get_directive(tagbuck, directive, sizeof(directive), r->pool)) {
---
>             if (get_combined_directive (ctx, *bb, &is_buffer_allocated,
>                                         tmp_buf, TMP_BUF_SIZE) != APR_SUCCESS) {
2343c2670
< 			    "mod_include: error reading directive in %s",
---
> 			    "mod_include: error copying directive in %s",
2345c2672,2702
< 		ap_rputs(error, r);
---
>                 CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head);
> 
>                 /* DO CLEANUP HERE!!!!! */
>                 tmp_dptr = ctx->head_start_bucket;
>                 if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
>                     ctx_end  = AP_BRIGADE_LAST(ctx->ssi_tag_brigade);
>                 }
>                 else {
>                     ctx_end  = NULL;
>                 }
>                 bb_end = AP_BRIGADE_LAST(*bb);
> 
>                 do {
>                     if (tmp_dptr == dptr) {
>                         done = 1;
>                     }
>                     else {
>                         ap_bucket *free_bucket;
> 
>                         free_bucket = tmp_dptr;
>                         tmp_dptr    = AP_BUCKET_NEXT (tmp_dptr);
> 
>                         if ((ctx_end != NULL) && (AP_BUCKET_PREV(tmp_dptr) == ctx_end)) {
>                             tmp_dptr = AP_BRIGADE_FIRST(*bb);
>                         }
> 
>                         AP_BUCKET_REMOVE(free_bucket);
>                         ap_bucket_destroy(free_bucket);
>                     }
>                 } while ((!done) && (AP_BUCKET_PREV(tmp_dptr) != bb_end));
> 
2348,2353c2705,2715
<             tag_and_after = ap_brigade_split(*bb, dptr);
<             ap_pass_brigade(f->next, *bb); /* process what came before the tag */
<             *bb = tag_and_after;
<             if (!strcmp(directive, "if")) {
<                 if (!printing) {
<                     if_nesting++;
---
> 
>             /* Can't destroy the tag buckets until I'm done processing
>              *  because the combined_tag might just be pointing to
>              *  the contents of a single bucket!
>              */
>             directive_str = get_directive(ctx, &directive_token);
> 
>             switch (directive_token) {
>             case TOK_IF:
>                 if (!ctx->flags & FLAG_PRINTING) {
>                     ctx->if_nesting_level++;
2356,2379c2718,2719
<                     ret = handle_if(tagbuck, r, error, &conditional_status,
<                                     &printing);
<                     if_nesting = 0;
<                 }
<                 continue;
<             }
<             else if (!strcmp(directive, "else")) {
<                 if (!if_nesting) {
<                     ret = handle_else(tagbuck, r, error, &conditional_status,
<                                       &printing);
<                 }
<                 continue;
<             }
<             else if (!strcmp(directive, "elif")) {
<                 if (!if_nesting) {
<                     ret = handle_elif(tagbuck, r, error, &conditional_status,
<                                       &printing);
<                 }
<                 continue;
<             }
<             else if (!strcmp(directive, "endif")) {
<                 if (!if_nesting) {
<                     ret = handle_endif(tagbuck, r, error, &conditional_status,
<                                        &printing);
---
>                     ret = handle_if(ctx, r, dptr, &content_head);
>                     ctx->if_nesting_level = 0;
2381,2382c2721,2725
<                 else {
<                     if_nesting--;
---
>                 break;
> 
>             case TOK_ELSE:
>                 if (!ctx->if_nesting_level) {
>                     ret = handle_else(ctx, r, dptr, &content_head);
2384,2396c2727,2737
<                 continue;
<             }
<             if (!printing) {
<                 continue;
<             }
<             if (!strcmp(directive, "exec")) {
<                 if (noexec) {
<                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
< 				  "exec used but not allowed in %s",
< 				  r->filename);
<                     if (printing) {
<                         ap_rputs(error, r);
<                     }
---
>                 break;
> 
>             case TOK_ELIF:
>                 if (!ctx->if_nesting_level) {
>                     ret = handle_elif(ctx, r, dptr, &content_head);
>                 }
>                 break;
> 
>             case TOK_ENDIF:
>                 if (!ctx->if_nesting_level) {
>                     ret = handle_endif(ctx, r, dptr, &content_head);
2399c2740
<                     ret = handle_exec(tagbuck, r, error, f->next);
---
>                     ctx->if_nesting_level--;
2400a2742,2744
>                 break;
>             default:
>                 break;
2402,2406c2746,2801
<             else if (!strcmp(directive, "config")) {
<                 ret = handle_config(tagbuck, r, error, timefmt, &sizefmt);
<             }
<             else if (!strcmp(directive, "set")) {
<                 ret = handle_set(tagbuck, r, error);
---
> 
>             if (ctx->flags & FLAG_PRINTING) {
>                 switch (directive_token) {
>                 case TOK_EXEC:
>                     if (ctx->flags & FLAG_NO_EXEC) {
>                         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
>     				  "exec used but not allowed in %s", r->filename);
>                         CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head);
>                     }
>                     else {
>                         SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx);
>                         ret = handle_exec(ctx, r, f->next, dptr, &content_head);
>                     }
>                     break;
> 
>                 case TOK_INCLUDE:
>                     SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx);
>                     ret = handle_include(ctx, r, f->next, dptr, &content_head);
>                     break;
> 
> #ifdef USE_PERL_SSI
>                 case TOK_PERL:
>                     SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx);
>                     ret = handle_perl(ctx, r);
>                     break;
> #endif
>                 case TOK_SET:    /* Might generate an error... */
>                     ret = handle_set(ctx, r, dptr, &content_head);
>                     break;
>                 case TOK_ECHO:
>                     ret = handle_echo(ctx, r, dptr, &content_head);
>                     break;
>                 case TOK_FSIZE:
>                     ret = handle_fsize(ctx, r, dptr, &content_head);
>                     break;
>                 case TOK_CONFIG:   /* Might generate an error... */
>                     ret = handle_config(ctx, r, dptr, &content_head);
>                     break;
>                 case TOK_FLASTMOD:
>                     ret = handle_flastmod(ctx, r, dptr, &content_head);
>                     break;
>                 case TOK_PRINTENV:
>                     ret = handle_printenv(ctx, r, dptr, &content_head);
>                     break;
> 
>                 default:
>                     if ((directive_token != TOK_IF) &&
>                         (directive_token != TOK_ELSE) &&
>                         (directive_token != TOK_ELIF) &&
>                         (directive_token != TOK_ENDIF)) {
>                         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
>                                       "unknown directive \"%s\" in parsed doc %s",
>                                       directive_str, r->filename);
>                         CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head);
>                     }
>                 }
2408,2409c2803,2809
<             else if (!strcmp(directive, "include")) {
<                 ret = handle_include(tagbuck, r, f->next, error, noexec);
---
>             else {
>                 if (directive_token == TOK_UNKNOWN) {
>                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
>                                   "unknown directive \"%s\" in parsed doc %s",
>                                   directive_str, r->filename);
>                     CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head);
>                 }
2411,2412c2811,2831
<             else if (!strcmp(directive, "echo")) {
<                 ret = handle_echo(tagbuck, r, error);
---
> 
>             /* This chunk of code starts at the first bucket in the chain
>              * of tag buckets (assuming that by this point the bucket for
>              * the STARTING_SEQUENCE has been split) and loops through to
>              * the end of the tag buckets freeing them all.
>              *
>              * Remember that some part of this may have been set aside
>              * into the ssi_tag_brigade and the remainder (possibly as
>              * little as one byte) will be in the current brigade.
>              *
>              * The value of dptr should have been set during the
>              * PARSE_TAIL state to the first bucket after the
>              * ENDING_SEQUENCE.
>              *
>              * The value of content_head may have been set during processing
>              * of the directive. If so, the content was inserted in front
>              * of the dptr bucket. The inserted buckets should not be thrown
>              * away here, but they should also not be parsed later.
>              */
>             if (content_head == NULL) {
>                 content_head = dptr;
2414,2415c2833,2835
<             else if (!strcmp(directive, "fsize")) {
<                 ret = handle_fsize(tagbuck, r, error, sizefmt);
---
>             tmp_dptr = ctx->head_start_bucket;
>             if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
>                 ctx_end  = AP_BRIGADE_LAST(ctx->ssi_tag_brigade);
2417,2418c2837,2838
<             else if (!strcmp(directive, "flastmod")) {
<                 ret = handle_flastmod(tagbuck, r, error, timefmt);
---
>             else {
>                 ctx_end  = NULL;
2420,2421c2840,2864
<             else if (!strcmp(directive, "printenv")) {
<                 ret = handle_printenv(tagbuck, r, error);
---
>             bb_end   = AP_BRIGADE_LAST(*bb);
> 
>             do {
>                 if (tmp_dptr == content_head) {
>                     done = 1;
>                 }
>                 else {
>                     ap_bucket *free_bucket;
> 
>                     free_bucket = tmp_dptr;
>                     tmp_dptr    = AP_BUCKET_NEXT (tmp_dptr);
> 
>                     if ((ctx_end != NULL) && (AP_BUCKET_PREV(tmp_dptr) == ctx_end)) {
>                         tmp_dptr = AP_BRIGADE_FIRST(*bb);
>                     }
> 
>                     AP_BUCKET_REMOVE(free_bucket);
>                     ap_bucket_destroy(free_bucket);
>                 }
>             } while ((!done) && (AP_BUCKET_PREV(tmp_dptr) != bb_end));
> 
>             if (is_buffer_allocated) {
>                 free (ctx->combined_tag);
>                 ctx->combined_tag = NULL;
>                 is_buffer_allocated = 0;
2423,2425c2866,2868
< #ifdef USE_PERL_SSI
<             else if (!strcmp(directive, "perl")) {
<                 ret = handle_perl(tagbuck, r, error);
---
>             else if (ctx->combined_tag != NULL) {
>                 memset (ctx->combined_tag, '\0', ctx->tag_length);
>                 ctx->combined_tag = NULL;
2427,2434c2870,2886
< #endif
<             else {
<                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
< 			      "unknown directive \"%s\" "
< 			      "in parsed doc %s",
< 			      directive, r->filename);
<                 if (printing) {
<                     ap_rputs(error, r);
---
> 
>             /* Don't reset the flags or the nesting level!!! */
>             ctx->parse_pos         = 0;
>             ctx->head_start_bucket = NULL;
>             ctx->head_start_index  = 0;
>             ctx->tag_start_bucket  = NULL;
>             ctx->tag_start_index   = 0;
>             ctx->tail_start_bucket = NULL;
>             ctx->tail_start_index  = 0;
>             ctx->curr_tag_pos      = NULL;
>             ctx->tag_length        = 0;
> 
>             if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
>                 while (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
>                     tmp_bkt = AP_BRIGADE_FIRST(ctx->ssi_tag_brigade);
>                     AP_BUCKET_REMOVE(tmp_bkt);
>                     ap_bucket_destroy(tmp_bkt);
2437,2438c2889,2890
<             *bb = ap_brigade_split(tag_and_after, endsec); 
<             dptr = AP_BUCKET_PREV(endsec);
---
> 
>             ctx->state     = PRE_HEAD;
2440,2441c2892,2943
<         else {
<             return;
---
>     }
> 
>     /* If I am in the middle of parsing an SSI tag then I need to set aside
>      *   the pertinent trailing buckets and pass on the initial part of the
>      *   brigade. The pertinent parts of the next brigades will be added to
>      *   these set aside buckets to form the whole tag and will be processed
>      *   once the whole tag has been found.
>      */
>     if (ctx->state == PRE_HEAD) {
>         /* Inside a false conditional (if, elif, else), so toss it all... */
>         if (!(ctx->flags & FLAG_PRINTING)) {
>             int done = 0;
>             ap_bucket *last_bkt;
> 
>             last_bkt = AP_BRIGADE_LAST(*bb);
>             do {
>                 ap_bucket *free_bucket = dptr;
> 
>                 if (dptr == last_bkt) {
>                     done = 1;
>                 }
>                 else {
>                     dptr = AP_BUCKET_NEXT (dptr);
>                     AP_BUCKET_REMOVE(free_bucket);
>                     ap_bucket_destroy(free_bucket);
>                 }
>             } while (!done);
>         }
>         else { /* Otherwise pass it along... */
>             ap_pass_brigade(f->next, *bb);  /* No SSI tags in this brigade... */
>         }
>     }
>     else if (ctx->state == PARSED) {     /* Invalid internal condition... */
>         ap_bucket *content_head, *tmp_bkt;
>         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
>                       "Invalid mod_include state during file %s", r->filename);
>         CREATE_ERROR_BUCKET(ctx, tmp_bkt, AP_BRIGADE_FIRST(*bb), content_head);
>     }
>     else {                 /* Entire brigade is middle chunk of SSI tag... */
>         if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
>             AP_BRIGADE_CONCAT(ctx->ssi_tag_brigade, *bb);
>         }
>         else {             /* End of brigade contains part of SSI tag... */
>             if (ctx->head_start_index > 0) {
>                 ap_bucket_split(ctx->head_start_bucket, ctx->head_start_index);
>                 ctx->head_start_bucket = AP_BUCKET_NEXT(ctx->head_start_bucket);
>                 ctx->head_start_index  = 0;
>             }
>                            /* Set aside tag, pass pre-tag... */
>             tag_and_after = ap_brigade_split(*bb, ctx->head_start_bucket);
>             ap_save_brigade(f, &ctx->ssi_tag_brigade, &tag_and_after);
>             ap_pass_brigade(f->next, *bb);
2492a2995
>     include_ctx_t *ctx = f->ctx;
2496a3000,3004
> #ifdef FREDDY_KRUGER_VEGAMATIC_MODE
>     ap_bucket_brigade *rest;
>     ap_bucket *tmp_buck;
> #endif
> 
2504a3013,3032
>     if (!f->ctx) {
>         f->ctx    = ctx      = apr_pcalloc(f->c->pool, sizeof(*ctx));
>         if (ctx != NULL) {
>             ctx->state           = PRE_HEAD;
>             ctx->flags           = (FLAG_PRINTING | FLAG_COND_TRUE);
>             if (ap_allow_options(r) & OPT_INCNOEXEC) {
>                 ctx->flags |= FLAG_NO_EXEC;
>             }
>             ctx->ssi_tag_brigade = ap_brigade_create(f->c->pool);
> 
>             apr_cpystrn(ctx->error_str, DEFAULT_ERROR_MSG,   sizeof(ctx->error_str));
>             apr_cpystrn(ctx->time_str,  DEFAULT_TIME_FORMAT, sizeof(ctx->time_str));
>             ctx->error_length = strlen(ctx->error_str);
>         }
>         else {
>             ap_pass_brigade(f->next, b);
>             return APR_ENOMEM;
>         }
>     }
> 
2540a3069,3091
> 
> #ifdef FREDDY_KRUGER_VEGAMATIC_MODE
>     tmp_buck = AP_BRIGADE_FIRST(b);
>     while (!AP_BUCKET_IS_EOS(tmp_buck)) {
>         const char *buf;
>         apr_ssize_t len;
> 
>         ap_bucket_read(tmp_buck, &buf, &len, 0);
> 
>         if (len > 1) {
>             ap_bucket_split(tmp_buck, 1);
>         }
>         tmp_buck = AP_BUCKET_NEXT(tmp_buck);
>         rest = ap_brigade_split(b, tmp_buck);
> 
>         send_parsed_content(&b, r, f);
> 
>         b = rest;
>     }
>     if (AP_BUCKET_IS_EOS(tmp_buck)) {
>         send_parsed_content(&b, r, f);
>     }
> #else
2542c3093
<     ap_pass_brigade(f->next, b);
---
> #endif

-------------------------------------------------------------------------
-------------------------------------------------------------------------


/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000 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 acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" 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 name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * 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/>.
 *
 * Portions of this software are based upon public domain software
 * originally written at the National Center for Supercomputing Applications,
 * University of Illinois, Urbana-Champaign.
 */

#ifndef _MOD_INCLUDE_H
#define _MOD_INCLUDE_H 1



#define STARTING_SEQUENCE "<!--#"
#define ENDING_SEQUENCE "-->"

#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
#define SIZEFMT_BYTES 0
#define SIZEFMT_KMG 1
#define TMP_BUF_SIZE 1024
#ifdef CHARSET_EBCDIC
#define RAW_ASCII_CHAR(ch)  apr_xlate_conv_byte(ap_hdrs_from_ascii, (unsigned char)ch)
#else /*CHARSET_EBCDIC*/
#define RAW_ASCII_CHAR(ch)  (ch)
#endif /*CHARSET_EBCDIC*/

module AP_MODULE_DECLARE_DATA includes_module;

/* just need some arbitrary non-NULL pointer which can't also be a request_rec */
#define NESTED_INCLUDE_MAGIC	(&includes_module)

/* TODO: changing directory should be handled by CreateProcess */
#define ap_chdir_file(x) do {} while(0)


/****************************************************************************
 * Used to keep context information during parsing of a request for SSI tags.
 * This is especially useful if the tag stretches across multiple buckets or
 * brigades. This keeps track of which buckets need to be replaced with the
 * content generated by the SSI tag.
 *
 * state: PRE_HEAD - State prior to finding the first character of the 
 *                   STARTING_SEQUENCE. Next state is PARSE_HEAD.
 *        PARSE_HEAD - State entered once the first character of the
 *                     STARTING_SEQUENCE is found and exited when the
 *                     the full STARTING_SEQUENCE has been matched or
 *                     a match failure occurs. Next state is PRE_HEAD
 *                     or PARSE_TAG.
 *        PARSE_TAG - State entered once the STARTING sequence has been
 *                    matched. It is exited when the first character in
 *                    ENDING_SEQUENCE is found. Next state is PARSE_TAIL.
 *        PARSE_TAIL - State entered from PARSE_TAG state when the first
 *                     character in ENDING_SEQUENCE is encountered. This
 *                     state is exited when the ENDING_SEQUENCE has been
 *                     completely matched, or when a match failure occurs.
 *                     Next state is PARSE_TAG or PARSED.
 *        PARSED - State entered from PARSE_TAIL once the complete 
 *                 ENDING_SEQUENCE has been matched. The SSI tag is
 *                 processed and the SSI buckets are replaced with the
 *                 SSI content during this state.
 * parse_pos: Current matched position within the STARTING_SEQUENCE or
 *            ENDING_SEQUENCE during the PARSE_HEAD and PARSE_TAIL states.
 *            This is especially useful when the sequence spans brigades.
 * X_start_bucket: These point to the buckets containing the first character
 *                 of the STARTING_SEQUENCE, the first non-whitespace
 *                 character of the tag, and the first character in the
 *                 ENDING_SEQUENCE (head_, tag_, and tail_ respectively).
 *                 The buckets are kept intact until the PARSED state is
 *                 reached, at which time the tag is consolidated and the
 *                 buckets are released. The buckets that these point to
 *                 have all been set aside in the ssi_tag_brigade (along
 *                 with all of the intervening buckets).
 * X_start_index: The index points within the specified bucket contents
 *                where the first character of the STARTING_SEQUENCE,
 *                the first non-whitespace character of the tag, and the
 *                first character in the ENDING_SEQUENCE can be found
 *                (head_, tag_, and tail_ respectively).
 * combined_tag: Once the PARSED state is reached the tag is collected from
 *               the bucket(s) in the ssi_tag_brigade into this contiguous
 *               buffer. The buckets in the ssi_tag_brigade are released
 *               and the tag is processed.
 * curr_tag_pos: Ptr to the combined_tag buffer indicating the current
 *               parse position.
 * tag_length: The number of bytes in the actual tag (excluding the
 *             STARTING_SEQUENCE, leading and trailing whitespace,
 *             and ENDING_SEQUENCE). This length is computed as the
 *             buckets are parsed and set aside during the PARSE_TAG state.
 * ssi_tag_brigade: The temporary brigade used by this filter to set aside
 *                  the buckets containing parts of the ssi tag and headers.
 */
typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_TAG, PARSE_TAIL, PARSED} states;
typedef struct include_filter_ctx {
    states       state;
    long         flags;    /* See the FLAG_XXXXX definitions. */
    int          if_nesting_level;
    apr_ssize_t  parse_pos;
    
    ap_bucket   *head_start_bucket;
    apr_ssize_t  head_start_index;

    ap_bucket   *tag_start_bucket;
    apr_ssize_t  tag_start_index;

    ap_bucket   *tail_start_bucket;
    apr_ssize_t  tail_start_index;

    char        *combined_tag;
    char        *curr_tag_pos;
    apr_ssize_t  tag_length;

    apr_ssize_t  error_length;
    char         error_str[MAX_STRING_LEN];
    char         time_str[MAX_STRING_LEN];

    ap_bucket_brigade *ssi_tag_brigade;
} include_ctx_t;

/* These flags are used to set flag bits. */
#define FLAG_PRINTING         0x00000001  /* Printing conditional lines. */
#define FLAG_COND_TRUE        0x00000002  /* Conditional eval'd to true. */
#define FLAG_SIZE_IN_BYTES    0x00000004  /* Sizes displayed in bytes.   */
#define FLAG_NO_EXEC          0x00000008  /* No Exec in current context. */

/* These flags are used to clear flag bits. */
#define FLAG_SIZE_ABBREV      0xFFFFFFFB  /* Reset SIZE_IN_BYTES bit.    */
#define FLAG_CLEAR_PRINT_COND 0xFFFFFFFC  /* Reset PRINTING and COND_TRUE*/
#define FLAG_CLEAR_PRINTING   0xFFFFFFFE  /* Reset just PRINTING bit.    */

typedef enum {TOK_UNKNOWN, TOK_IF, TOK_SET, TOK_ECHO, TOK_ELIF, TOK_ELSE,
              TOK_EXEC, TOK_PERL, TOK_ENDIF, TOK_FSIZE, TOK_CONFIG,
              TOK_INCLUDE, TOK_FLASTMOD, TOK_PRINTENV} dir_token_id;

#define CREATE_ERROR_BUCKET(cntx, t_buck, h_ptr, ins_head)              \
                t_buck = ap_bucket_create_transient(cntx->error_str,    \
                                                    ctx->error_length); \
                AP_BUCKET_INSERT_BEFORE(h_ptr, t_buck);                 \
                                                                        \
                if (ins_head == NULL) {                                 \
                    ins_head = t_buck;                                  \
                }

#endif /* _MOD_INCLUDE_H */

Mime
View raw message