/[Apache-SVN]/httpd/httpd/trunk/modules/filters/mod_include.c
ViewVC logotype

Contents of /httpd/httpd/trunk/modules/filters/mod_include.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 642978 - (hide annotations)
Mon Mar 31 12:29:05 2008 UTC (19 months, 3 weeks ago) by niq
File MIME type: text/plain
File size: 104611 byte(s)
Update mod_include for ap_expr update
1 fielding 420983 /* Licensed to the Apache Software Foundation (ASF) under one or more
2     * contributor license agreements. See the NOTICE file distributed with
3     * this work for additional information regarding copyright ownership.
4     * The ASF licenses this file to You under the Apache License, Version 2.0
5     * (the "License"); you may not use this file except in compliance with
6     * the License. You may obtain a copy of the License at
7 fielding 83751 *
8 nd 102525 * http://www.apache.org/licenses/LICENSE-2.0
9 fielding 83751 *
10 nd 102525 * Unless required by applicable law or agreed to in writing, software
11     * distributed under the License is distributed on an "AS IS" BASIS,
12     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     * See the License for the specific language governing permissions and
14     * limitations under the License.
15 fielding 83751 */
16    
17 gstein 87080 #include "apr.h"
18     #include "apr_strings.h"
19     #include "apr_thread_proc.h"
20 rbb 87241 #include "apr_hash.h"
21 wrowe 87903 #include "apr_user.h"
22 gstein 88060 #include "apr_lib.h"
23 gstein 88061 #include "apr_optional.h"
24 gstein 87080
25 gstein 88061 #define APR_WANT_STRFUNC
26 nd 101048 #define APR_WANT_MEMFUNC
27 gstein 88061 #include "apr_want.h"
28    
29 rbb 84497 #include "ap_config.h"
30 rbb 86211 #include "util_filter.h"
31 fielding 83751 #include "httpd.h"
32     #include "http_config.h"
33 rbb 88417 #include "http_core.h"
34 fielding 83751 #include "http_request.h"
35     #include "http_core.h"
36     #include "http_protocol.h"
37     #include "http_log.h"
38     #include "http_main.h"
39     #include "util_script.h"
40 rbb 85449 #include "http_core.h"
41 rbb 87067 #include "mod_include.h"
42 niq 642559 #include "ap_expr.h"
43 nd 101046
44 nd 101048 /* helper for Latin1 <-> entity encoding */
45 nd 101046 #if APR_CHARSET_EBCDIC
46 trawick 85222 #include "util_ebcdic.h"
47 nd 101046 #define RAW_ASCII_CHAR(ch) apr_xlate_conv_byte(ap_hdrs_from_ascii, \
48     (unsigned char)ch)
49 nd 101049 #else /* APR_CHARSET_EBCDIC */
50 nd 101046 #define RAW_ASCII_CHAR(ch) (ch)
51 nd 101049 #endif /* !APR_CHARSET_EBCDIC */
52 fielding 83751
53 nd 101049
54     /*
55     * +-------------------------------------------------------+
56     * | |
57     * | Types and Structures
58     * | |
59     * +-------------------------------------------------------+
60 jerenkrantz 90872 */
61    
62 nd 101070 /* sll used for string expansion */
63     typedef struct result_item {
64     struct result_item *next;
65     apr_size_t len;
66     const char *string;
67     } result_item_t;
68    
69 nd 101052 typedef enum {
70     XBITHACK_OFF,
71     XBITHACK_ON,
72     XBITHACK_FULL
73     } xbithack_t;
74 jerenkrantz 90872
75 nd 101046 typedef struct {
76 nd 101052 const char *default_error_msg;
77     const char *default_time_fmt;
78 nd 101129 const char *undefined_echo;
79 nd 101052 xbithack_t xbithack;
80 minfrin 571872 const int accessenable;
81 jerenkrantz 90872 } include_dir_config;
82    
83 ianh 92660 typedef struct {
84 nd 101052 const char *default_start_tag;
85     const char *default_end_tag;
86 ianh 92660 } include_server_config;
87    
88 nd 101036 /* main parser states */
89     typedef enum {
90     PARSE_PRE_HEAD,
91     PARSE_HEAD,
92     PARSE_DIRECTIVE,
93     PARSE_DIRECTIVE_POSTNAME,
94     PARSE_DIRECTIVE_TAIL,
95     PARSE_DIRECTIVE_POSTTAIL,
96     PARSE_PRE_ARG,
97     PARSE_ARG,
98     PARSE_ARG_NAME,
99     PARSE_ARG_POSTNAME,
100     PARSE_ARG_EQ,
101     PARSE_ARG_PREVAL,
102     PARSE_ARG_VAL,
103     PARSE_ARG_VAL_ESC,
104     PARSE_ARG_POSTVAL,
105     PARSE_TAIL,
106     PARSE_TAIL_SEQ,
107     PARSE_EXECUTE
108     } parse_state_t;
109    
110 nd 101048 typedef struct arg_item {
111     struct arg_item *next;
112     char *name;
113     apr_size_t name_len;
114     char *value;
115 nd 101052 apr_size_t value_len;
116 nd 101048 } arg_item_t;
117 nd 101036
118 nd 101073 typedef struct {
119 nd 101078 unsigned int T[256];
120     unsigned int x;
121     apr_size_t pattern_len;
122     } bndm_t;
123    
124 nd 101046 struct ssi_internal_ctx {
125 nd 101036 parse_state_t state;
126     int seen_eos;
127     int error;
128     char quote; /* quote character value (or \0) */
129 nd 101052 apr_size_t parse_pos; /* parse position of partial matches */
130 nd 101046 apr_size_t bytes_read;
131 nd 101036
132     apr_bucket_brigade *tmp_bb;
133    
134 nd 101046 request_rec *r;
135     const char *start_seq;
136     bndm_t *start_seq_pat;
137     const char *end_seq;
138 nd 101036 apr_size_t end_seq_len;
139     char *directive; /* name of the current directive */
140 nd 101046 apr_size_t directive_len; /* length of the current directive name */
141 nd 101036
142 nd 101048 arg_item_t *current_arg; /* currently parsed argument */
143     arg_item_t *argv; /* all arguments */
144 nd 101036
145 nd 101073 backref_t *re; /* NULL if there wasn't a regex yet */
146 nd 101078
147 nd 101129 const char *undefined_echo;
148     apr_size_t undefined_echo_len;
149    
150 niq 642559 opt_func_t access_func; /* is using the access tests allowed? */
151 minfrin 572136
152 niq 642559 /* breadcrumb to track whether child request should have parent's env */
153     request_rec *kludge_child;
154 nd 101078 #ifdef DEBUG_INCLUDE
155     struct {
156     ap_filter_t *f;
157     apr_bucket_brigade *bb;
158     } debug;
159     #endif
160 nd 101046 };
161 nd 101036
162 nd 101049
163     /*
164     * +-------------------------------------------------------+
165     * | |
166 nd 101078 * | Debugging Utilities
167     * | |
168     * +-------------------------------------------------------+
169     */
170    
171     #ifdef DEBUG_INCLUDE
172 nd 101087
173     #define TYPE_TOKEN(token, ttype) do { \
174     (token)->type = ttype; \
175     (token)->s = #ttype; \
176     } while(0)
177    
178     #define CREATE_NODE(ctx, name) do { \
179     (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
180     (name)->parent = (name)->left = (name)->right = NULL; \
181     (name)->done = 0; \
182     (name)->dump_done = 0; \
183     } while(0)
184    
185 nd 101078 static void debug_printf(include_ctx_t *ctx, const char *fmt, ...)
186     {
187     va_list ap;
188     char *debug__str;
189    
190     va_start(ap, fmt);
191     debug__str = apr_pvsprintf(ctx->pool, fmt, ap);
192     va_end(ap);
193    
194     APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
195     debug__str, strlen(debug__str), ctx->pool,
196     ctx->intern->debug.f->c->bucket_alloc));
197     }
198    
199 nd 101087 #define DUMP__CHILD(ctx, is, node, child) if (1) { \
200     parse_node_t *d__c = node->child; \
201     if (d__c) { \
202     if (!d__c->dump_done) { \
203     if (d__c->parent != node) { \
204     debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
205     if (!d__c->parent) { \
206     debug_printf(ctx, "Parent of " #child " child node is " \
207     "NULL.\n"); \
208     } \
209     else { \
210     debug_printf(ctx, "Parent of " #child " child node " \
211     "points to another node (of type %s)!\n", \
212     d__c->parent->token.s); \
213     } \
214     return; \
215     } \
216     node = d__c; \
217     continue; \
218     } \
219     } \
220     else { \
221     debug_printf(ctx, "%s(missing)\n", is); \
222     } \
223     }
224    
225     static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
226     {
227     parse_node_t *current;
228     char *is;
229    
230     if (!root) {
231     debug_printf(ctx, " -- Parse Tree empty --\n\n");
232     return;
233     }
234    
235     debug_printf(ctx, " ----- Parse Tree -----\n");
236     current = root;
237     is = " ";
238    
239     while (current) {
240     switch (current->token.type) {
241     case TOKEN_STRING:
242     case TOKEN_RE:
243     debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
244     current->token.value);
245     current->dump_done = 1;
246     current = current->parent;
247     continue;
248    
249     case TOKEN_NOT:
250 nd 101115 case TOKEN_GROUP:
251 nd 101095 case TOKEN_RBRACE:
252     case TOKEN_LBRACE:
253 nd 101087 if (!current->dump_done) {
254     debug_printf(ctx, "%s%s\n", is, current->token.s);
255     is = apr_pstrcat(ctx->dpool, is, " ", NULL);
256     current->dump_done = 1;
257     }
258    
259     DUMP__CHILD(ctx, is, current, right)
260    
261     if (!current->right || current->right->dump_done) {
262     is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
263     if (current->right) current->right->dump_done = 0;
264     current = current->parent;
265     }
266     continue;
267    
268 nd 101095 default:
269 nd 101087 if (!current->dump_done) {
270 nd 101095 debug_printf(ctx, "%s%s\n", is, current->token.s);
271 nd 101087 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
272     current->dump_done = 1;
273     }
274    
275 nd 101095 DUMP__CHILD(ctx, is, current, left)
276 nd 101087 DUMP__CHILD(ctx, is, current, right)
277    
278 nd 101095 if ((!current->left || current->left->dump_done) &&
279     (!current->right || current->right->dump_done)) {
280    
281 nd 101087 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
282 nd 101095 if (current->left) current->left->dump_done = 0;
283 nd 101087 if (current->right) current->right->dump_done = 0;
284     current = current->parent;
285     }
286     continue;
287     }
288     }
289    
290     /* it is possible to call this function within the parser loop, to see
291     * how the tree is built. That way, we must cleanup after us to dump
292     * always the whole tree
293     */
294     root->dump_done = 0;
295     if (root->left) root->left->dump_done = 0;
296     if (root->right) root->right->dump_done = 0;
297    
298     debug_printf(ctx, " --- End Parse Tree ---\n\n");
299    
300     return;
301     }
302    
303 nd 101078 #define DEBUG_INIT(ctx, filter, brigade) do { \
304     (ctx)->intern->debug.f = filter; \
305     (ctx)->intern->debug.bb = brigade; \
306     } while(0)
307    
308     #define DEBUG_PRINTF(arg) debug_printf arg
309    
310     #define DEBUG_DUMP_TOKEN(ctx, token) do { \
311     token_t *d__t = (token); \
312     \
313     if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) { \
314 nd 101095 DEBUG_PRINTF(((ctx), " Found: %s (%s)\n", d__t->s, d__t->value)); \
315 nd 101078 } \
316     else { \
317 nd 101095 DEBUG_PRINTF((ctx, " Found: %s\n", d__t->s)); \
318 nd 101078 } \
319     } while(0)
320    
321 nd 101107 #define DEBUG_DUMP_EVAL(ctx, node) do { \
322     char c = '"'; \
323     switch ((node)->token.type) { \
324     case TOKEN_STRING: \
325     debug_printf((ctx), " Evaluate: %s (%s) -> %c\n", (node)->token.s,\
326     (node)->token.value, ((node)->value) ? '1':'0'); \
327     break; \
328     case TOKEN_AND: \
329     case TOKEN_OR: \
330     debug_printf((ctx), " Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
331     (node)->token.s, \
332     (((node)->left->done) ? ((node)->left->value ?"1":"0") \
333     : "short circuited"), \
334     (((node)->right->done) ? ((node)->right->value?"1":"0") \
335     : "short circuited"), \
336     (node)->value ? '1' : '0'); \
337     break; \
338     case TOKEN_EQ: \
339     case TOKEN_NE: \
340     case TOKEN_GT: \
341     case TOKEN_GE: \
342     case TOKEN_LT: \
343     case TOKEN_LE: \
344     if ((node)->right->token.type == TOKEN_RE) c = '/'; \
345     debug_printf((ctx), " Compare: %s (\"%s\" with %c%s%c) -> %c\n", \
346     (node)->token.s, \
347     (node)->left->token.value, \
348     c, (node)->right->token.value, c, \
349     (node)->value ? '1' : '0'); \
350     break; \
351     default: \
352     debug_printf((ctx), " Evaluate: %s -> %c\n", (node)->token.s, \
353     (node)->value ? '1' : '0'); \
354     break; \
355     } \
356     } while(0)
357    
358 nd 101081 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched) do { \
359     if (unmatched) { \
360     DEBUG_PRINTF(((ctx), " Unmatched %c\n", (char)(unmatched))); \
361     } \
362 nd 101078 } while(0)
363    
364     #define DEBUG_DUMP_COND(ctx, text) \
365     DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text), \
366     ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
367    
368 nd 101087 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
369    
370 nd 101078 #else /* DEBUG_INCLUDE */
371    
372     #define TYPE_TOKEN(token, ttype) (token)->type = ttype
373 nd 101087
374     #define CREATE_NODE(ctx, name) do { \
375     (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
376     (name)->parent = (name)->left = (name)->right = NULL; \
377     (name)->done = 0; \
378     } while(0)
379    
380 nd 101078 #define DEBUG_INIT(ctx, f, bb)
381     #define DEBUG_PRINTF(arg)
382     #define DEBUG_DUMP_TOKEN(ctx, token)
383 nd 101107 #define DEBUG_DUMP_EVAL(ctx, node)
384 nd 101078 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
385     #define DEBUG_DUMP_COND(ctx, text)
386 nd 101087 #define DEBUG_DUMP_TREE(ctx, root)
387 nd 101078
388     #endif /* !DEBUG_INCLUDE */
389    
390    
391     /*
392     * +-------------------------------------------------------+
393     * | |
394 nd 101049 * | Static Module Data
395     * | |
396     * +-------------------------------------------------------+
397     */
398    
399     /* global module structure */
400     module AP_MODULE_DECLARE_DATA include_module;
401    
402     /* function handlers for include directives */
403     static apr_hash_t *include_handlers;
404    
405     /* forward declaration of handler registry */
406     static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
407    
408     /* Sentinel value to store in subprocess_env for items that
409     * shouldn't be evaluated until/unless they're actually used
410     */
411     static const char lazy_eval_sentinel;
412     #define LAZY_VALUE (&lazy_eval_sentinel)
413    
414     /* default values */
415 nd 101048 #define DEFAULT_START_SEQUENCE "<!--#"
416     #define DEFAULT_END_SEQUENCE "-->"
417 nd 101046 #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
418     #define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
419     #define DEFAULT_UNDEFINED_ECHO "(none)"
420 nd 101036
421 jerenkrantz 90872 #ifdef XBITHACK
422 nd 101052 #define DEFAULT_XBITHACK XBITHACK_FULL
423 jerenkrantz 90872 #else
424 nd 101052 #define DEFAULT_XBITHACK XBITHACK_OFF
425 jerenkrantz 90872 #endif
426    
427 fielding 83751
428 nd 101049 /*
429     * +-------------------------------------------------------+
430     * | |
431     * | Environment/Expansion Functions
432     * | |
433     * +-------------------------------------------------------+
434 jerenkrantz 90872 */
435    
436 fielding 83751 /*
437     * decodes a string containing html entities or numeric character references.
438     * 's' is overwritten with the decoded string.
439     * If 's' is syntatically incorrect, then the followed fixups will be made:
440     * unknown entities will be left undecoded;
441     * references to unused numeric characters will be deleted.
442     * In particular, &#00; will not be decoded, but will be deleted.
443     */
444    
445     /* maximum length of any ISO-LATIN-1 HTML entity name. */
446     #define MAXENTLEN (6)
447    
448     /* The following is a shrinking transformation, therefore safe. */
449    
450     static void decodehtml(char *s)
451     {
452     int val, i, j;
453 brianp 92284 char *p;
454 fielding 83751 const char *ents;
455     static const char * const entlist[MAXENTLEN + 1] =
456     {
457 nd 101074 NULL, /* 0 */
458     NULL, /* 1 */
459     "lt\074gt\076", /* 2 */
460     "amp\046ETH\320eth\360", /* 3 */
461     "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml"
462     "\353iuml\357ouml\366uuml\374yuml\377", /* 4 */
463    
464     "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
465     "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
466     "icirc\356ocirc\364ucirc\373thorn\376", /* 5 */
467    
468     "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
469     "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
470     "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
471     "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
472     "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
473     "oslash\370ugrave\371uacute\372yacute\375" /* 6 */
474 fielding 83751 };
475    
476 brianp 92284 /* Do a fast scan through the string until we find anything
477     * that needs more complicated handling
478     */
479     for (; *s != '&'; s++) {
480     if (*s == '\0') {
481     return;
482     }
483     }
484    
485     for (p = s; *s != '\0'; s++, p++) {
486 fielding 83751 if (*s != '&') {
487     *p = *s;
488     continue;
489     }
490     /* find end of entity */
491     for (i = 1; s[i] != ';' && s[i] != '\0'; i++) {
492     continue;
493     }
494    
495     if (s[i] == '\0') { /* treat as normal data */
496     *p = *s;
497     continue;
498     }
499    
500     /* is it numeric ? */
501     if (s[1] == '#') {
502 wrowe 86008 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
503 fielding 83751 val = val * 10 + s[j] - '0';
504     }
505     s += i;
506     if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
507     (val >= 127 && val <= 160) || val >= 256) {
508     p--; /* no data to output */
509     }
510     else {
511     *p = RAW_ASCII_CHAR(val);
512     }
513     }
514     else {
515     j = i - 1;
516     if (j > MAXENTLEN || entlist[j] == NULL) {
517     /* wrong length */
518     *p = '&';
519     continue; /* skip it */
520     }
521     for (ents = entlist[j]; *ents != '\0'; ents += i) {
522     if (strncmp(s + 1, ents, j) == 0) {
523     break;
524     }
525     }
526    
527     if (*ents == '\0') {
528     *p = '&'; /* unknown */
529     }
530     else {
531     *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
532     s += i;
533     }
534     }
535     }
536    
537     *p = '\0';
538     }
539    
540 nd 101052 static void add_include_vars(request_rec *r, const char *timefmt)
541 fielding 83751 {
542 nd 101049 apr_table_t *e = r->subprocess_env;
543     char *t;
544 nd 101036
545 nd 101049 apr_table_setn(e, "DATE_LOCAL", LAZY_VALUE);
546     apr_table_setn(e, "DATE_GMT", LAZY_VALUE);
547     apr_table_setn(e, "LAST_MODIFIED", LAZY_VALUE);
548     apr_table_setn(e, "DOCUMENT_URI", r->uri);
549     if (r->path_info && *r->path_info) {
550     apr_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
551 nd 101036 }
552 nd 101049 apr_table_setn(e, "USER_NAME", LAZY_VALUE);
553 nd 101461 if (r->filename && (t = strrchr(r->filename, '/'))) {
554 nd 101049 apr_table_setn(e, "DOCUMENT_NAME", ++t);
555     }
556     else {
557     apr_table_setn(e, "DOCUMENT_NAME", r->uri);
558     }
559     if (r->args) {
560     char *arg_copy = apr_pstrdup(r->pool, r->args);
561 nd 101036
562 nd 101049 ap_unescape_url(arg_copy);
563     apr_table_setn(e, "QUERY_STRING_UNESCAPED",
564     ap_escape_shell_cmd(r->pool, arg_copy));
565     }
566     }
567 nd 101036
568 nd 101049 static const char *add_include_vars_lazy(request_rec *r, const char *var)
569     {
570     char *val;
571     if (!strcasecmp(var, "DATE_LOCAL")) {
572     include_dir_config *conf =
573     (include_dir_config *)ap_get_module_config(r->per_dir_config,
574     &include_module);
575     val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 0);
576     }
577     else if (!strcasecmp(var, "DATE_GMT")) {
578     include_dir_config *conf =
579     (include_dir_config *)ap_get_module_config(r->per_dir_config,
580     &include_module);
581     val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 1);
582     }
583     else if (!strcasecmp(var, "LAST_MODIFIED")) {
584     include_dir_config *conf =
585     (include_dir_config *)ap_get_module_config(r->per_dir_config,
586     &include_module);
587     val = ap_ht_time(r->pool, r->finfo.mtime, conf->default_time_fmt, 0);
588     }
589     else if (!strcasecmp(var, "USER_NAME")) {
590 trawick 101154 if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
591 nd 101049 val = "<unknown>";
592     }
593     }
594     else {
595     val = NULL;
596     }
597 nd 101036
598 nd 101049 if (val) {
599     apr_table_setn(r->subprocess_env, var, val);
600 nd 101036 }
601 nd 101049 return val;
602     }
603 nd 101036
604 nd 101070 static const char *get_include_var(const char *var, include_ctx_t *ctx)
605 nd 101049 {
606     const char *val;
607 nd 101073 request_rec *r = ctx->intern->r;
608    
609 nd 101049 if (apr_isdigit(*var) && !var[1]) {
610 nd 101100 apr_size_t idx = *var - '0';
611 nd 101073 backref_t *re = ctx->intern->re;
612    
613 nd 101049 /* Handle $0 .. $9 from the last regex evaluated.
614     * The choice of returning NULL strings on not-found,
615     * v.s. empty strings on an empty match is deliberate.
616     */
617 nd 101073 if (!re) {
618 fielding 101234 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
619     "regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
620     idx, r->filename);
621 nd 101049 return NULL;
622     }
623     else {
624 striker 101556 if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
625 nd 101073 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
626 fielding 101234 "regex capture $%" APR_SIZE_T_FMT
627     " is out of range (last regex was: '%s') in %s",
628     idx, re->rexp, r->filename);
629 nd 101049 return NULL;
630     }
631 nd 101073
632     if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
633     return NULL;
634     }
635    
636     val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
637     re->match[idx].rm_eo - re->match[idx].rm_so);
638 nd 101049 }
639     }
640     else {
641 nd 101073 val = apr_table_get(r->subprocess_env, var);
642 nd 101049
643 nd 101073 if (val == LAZY_VALUE) {
644     val = add_include_vars_lazy(r, var);
645     }
646 nd 101049 }
647 nd 101073
648 nd 101049 return val;
649 nd 101036 }
650    
651 fielding 83751 /*
652     * Do variable substitution on strings
653 nd 101070 *
654 brianp 92747 * (Note: If out==NULL, this function allocs a buffer for the resulting
655 nd 101070 * string from ctx->pool. The return value is always the parsed string)
656 fielding 83751 */
657 nd 101069 static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
658 brianp 92747 apr_size_t length, int leave_name)
659 fielding 83751 {
660 nd 101069 request_rec *r = ctx->intern->r;
661 nd 101070 result_item_t *result = NULL, *current = NULL;
662     apr_size_t outlen = 0, inlen, span;
663     char *ret = NULL, *eout = NULL;
664     const char *p;
665 fielding 83751
666 nd 101070 if (out) {
667     /* sanity check, out && !length is not supported */
668     ap_assert(out && length);
669    
670     ret = out;
671     eout = out + length - 1;
672     }
673    
674     span = strcspn(in, "\\$");
675     inlen = strlen(in);
676    
677     /* fast exit */
678     if (inlen == span) {
679     if (out) {
680     apr_cpystrn(out, in, length);
681 brianp 92747 }
682 nd 101070 else {
683     ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
684     ? length - 1 : inlen);
685     }
686    
687     return ret;
688 brianp 92747 }
689 nd 101070
690     /* well, actually something to do */
691     p = in + span;
692    
693     if (out) {
694     if (span) {
695     memcpy(out, in, (out+span <= eout) ? span : (eout-out));
696     out += span;
697     }
698     }
699 brianp 92747 else {
700 nd 101070 current = result = apr_palloc(ctx->dpool, sizeof(*result));
701     current->next = NULL;
702     current->string = in;
703     current->len = span;
704     outlen = span;
705 brianp 92747 }
706    
707 nd 101070 /* loop for specials */
708     do {
709     if ((out && out >= eout) || (length && outlen >= length)) {
710     break;
711     }
712 fielding 83751
713 nd 101070 /* prepare next entry */
714     if (!out && current->len) {
715     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
716     current = current->next;
717     current->next = NULL;
718     current->len = 0;
719     }
720    
721     /*
722     * escaped character
723     */
724     if (*p == '\\') {
725     if (out) {
726     *out++ = (p[1] == '$') ? *++p : *p;
727     ++p;
728     }
729     else {
730     current->len = 1;
731     current->string = (p[1] == '$') ? ++p : p;
732     ++p;
733     ++outlen;
734     }
735     }
736    
737     /*
738     * variable expansion
739     */
740     else { /* *p == '$' */
741     const char *newp = NULL, *ep, *key = NULL;
742    
743     if (*++p == '{') {
744     ep = ap_strchr_c(++p, '}');
745     if (!ep) {
746     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing '}' on "
747     "variable \"%s\" in %s", p, r->filename);
748     break;
749 brianp 92747 }
750 nd 101070
751     if (p < ep) {
752     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
753     newp = ep + 1;
754 brianp 92747 }
755 nd 101070 p -= 2;
756 jerenkrantz 90874 }
757 fielding 83751 else {
758 nd 101070 ep = p;
759     while (*ep == '_' || apr_isalnum(*ep)) {
760     ++ep;
761     }
762    
763     if (p < ep) {
764     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
765     newp = ep;
766     }
767     --p;
768 fielding 83751 }
769    
770 nd 101070 /* empty name results in a copy of '$' in the output string */
771     if (!key) {
772     if (out) {
773     *out++ = *p++;
774 jerenkrantz 90874 }
775     else {
776 nd 101070 current->len = 1;
777     current->string = p++;
778     ++outlen;
779 jerenkrantz 90874 }
780 nd 101070 }
781     else {
782     const char *val = get_include_var(key, ctx);
783     apr_size_t len = 0;
784 fielding 83751
785 nd 101070 if (val) {
786     len = strlen(val);
787 jerenkrantz 90874 }
788 nd 101070 else if (leave_name) {
789     val = p;
790     len = ep - p;
791 jerenkrantz 90874 }
792 nd 101070
793     if (val && len) {
794     if (out) {
795     memcpy(out, val, (out+len <= eout) ? len : (eout-out));
796     out += len;
797 brianp 92747 }
798 nd 101070 else {
799     current->len = len;
800     current->string = val;
801     outlen += len;
802 brianp 92747 }
803     }
804 nd 101070
805     p = newp;
806 jerenkrantz 90874 }
807 nd 101070 }
808    
809     if ((out && out >= eout) || (length && outlen >= length)) {
810 fielding 83751 break;
811     }
812 nd 101070
813     /* check the remainder */
814     if (*p && (span = strcspn(p, "\\$")) > 0) {
815     if (!out && current->len) {
816     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
817     current = current->next;
818     current->next = NULL;
819     }
820    
821     if (out) {
822     memcpy(out, p, (out+span <= eout) ? span : (eout-out));
823     out += span;
824     }
825     else {
826     current->len = span;
827     current->string = p;
828     outlen += span;
829     }
830    
831     p += span;
832     }
833     } while (p < in+inlen);
834    
835     /* assemble result */
836     if (out) {
837     if (out > eout) {
838     *eout = '\0';
839     }
840     else {
841     *out = '\0';
842     }
843 fielding 83751 }
844 nd 101070 else {
845     const char *ep;
846    
847     if (length && outlen > length) {
848     outlen = length - 1;
849     }
850    
851     ret = out = apr_palloc(ctx->pool, outlen + 1);
852     ep = ret + outlen;
853    
854     do {
855     if (result->len) {
856     memcpy(out, result->string, (out+result->len <= ep)
857     ? result->len : (ep-out));
858     out += result->len;
859     }
860     result = result->next;
861     } while (result && out < ep);
862    
863     ret[outlen] = '\0';
864     }
865    
866     return ret;
867 fielding 83751 }
868    
869 niq 642978 static const char *ssi_parse_string(request_rec *r, const char *in)
870 fielding 83751 {
871 niq 642559 include_ctx_t *ctx = ap_get_module_config(r->request_config,
872     &include_module);
873     return ap_ssi_parse_string(ctx, in, NULL, 0, SSI_EXPAND_DROP_NAME);
874 fielding 83751 }
875 niq 642978 static int ssi_access(request_rec *r, ap_parse_node_t *current,
876 niq 642559 string_func_t parse_string)
877 fielding 83751 {
878 niq 642559 request_rec *rr;
879     include_ctx_t *ctx = ap_get_module_config(r->request_config,
880     &include_module);
881 fielding 83751
882 niq 642559 /* if this arg isn't -A, just return */
883     if (current->token.type != TOKEN_ACCESS || current->token.value[0] != 'A') {
884     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
885     "Unsupported option -%s in file %s",
886     current->token.value, r->filename);
887     return 1;
888 nd 101078 }
889 niq 642559 if (current->left || !current->right ||
890     (current->right->token.type != TOKEN_STRING &&
891     current->right->token.type != TOKEN_RE)) {
892     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
893     "Invalid expression in file %s: Token '-A' must be followed by a URI string.",
894     r->filename);
895     return 1; /* was_error */
896 fielding 83751 }
897 niq 642559 current->right->token.value =
898     ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
899     SSI_EXPAND_DROP_NAME);
900     rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
901     /* 400 and higher are considered access denied */
902     if (rr->status < HTTP_BAD_REQUEST) {
903     current->value = 1;
904 fielding 83751 }
905 nd 101078 else {
906 niq 642559 current->value = 0;
907     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r,
908     "mod_include: The tested "
909     "subrequest -A \"%s\" returned an error code.",
910     current->right->token.value);
911 nd 101078 }
912 niq 642559 ap_destroy_sub_req(rr);
913     return 0;
914 nd 101078 }
915    
916 nd 101049 /*
917     * +-------------------------------------------------------+
918     * | |
919     * | Action Handlers
920     * | |
921     * +-------------------------------------------------------+
922     */
923    
924     /*
925     * Extract the next tag name and value.
926     * If there are no more tags, set the tag name to NULL.
927     * The tag value is html decoded if dodecode is non-zero.
928     * The tag value may be NULL if there is no tag value..
929     */
930     static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
931     char **tag_val, int dodecode)
932     {
933     if (!ctx->intern->argv) {
934     *tag = NULL;
935     *tag_val = NULL;
936    
937     return;
938     }
939    
940     *tag_val = ctx->intern->argv->value;
941     *tag = ctx->intern->argv->name;
942    
943     ctx->intern->argv = ctx->intern->argv->next;
944    
945     if (dodecode && *tag_val) {
946     decodehtml(*tag_val);
947     }
948    
949     return;
950     }
951    
952     static int find_file(request_rec *r, const char *directive, const char *tag,
953     char *tag_val, apr_finfo_t *finfo)
954     {
955     char *to_send = tag_val;
956     request_rec *rr = NULL;
957     int ret=0;
958     char *error_fmt = NULL;
959     apr_status_t rv = APR_SUCCESS;
960    
961     if (!strcmp(tag, "file")) {
962 nd 101080 char *newpath;
963    
964     /* be safe; only files in this directory or below allowed */
965     rv = apr_filepath_merge(&newpath, NULL, tag_val,
966     APR_FILEPATH_SECUREROOTTEST |
967     APR_FILEPATH_NOTABSOLUTE, r->pool);
968    
969 jerenkrantz 104439 if (rv != APR_SUCCESS) {
970 nd 101049 error_fmt = "unable to access file \"%s\" "
971     "in parsed file %s";
972     }
973     else {
974     /* note: it is okay to pass NULL for the "next filter" since
975     we never attempt to "run" this sub request. */
976 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, NULL);
977 nd 101049
978     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
979     to_send = rr->filename;
980 jim 332306 if ((rv = apr_stat(finfo, to_send,
981 nd 101049 APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
982     && rv != APR_INCOMPLETE) {
983     error_fmt = "unable to get information about \"%s\" "
984     "in parsed file %s";
985     }
986     }
987     else {
988     error_fmt = "unable to lookup information about \"%s\" "
989     "in parsed file %s";
990     }
991     }
992    
993     if (error_fmt) {
994     ret = -1;
995     ap_log_rerror(APLOG_MARK, APLOG_ERR,
996     rv, r, error_fmt, to_send, r->filename);
997     }
998    
999     if (rr) ap_destroy_sub_req(rr);
1000 jim 332306
1001 nd 101049 return ret;
1002     }
1003     else if (!strcmp(tag, "virtual")) {
1004     /* note: it is okay to pass NULL for the "next filter" since
1005     we never attempt to "run" this sub request. */
1006     rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
1007    
1008     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
1009     memcpy((char *) finfo, (const char *) &rr->finfo,
1010     sizeof(rr->finfo));
1011     ap_destroy_sub_req(rr);
1012     return 0;
1013     }
1014     else {
1015 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to get "
1016     "information about \"%s\" in parsed file %s",
1017     tag_val, r->filename);
1018 nd 101049 ap_destroy_sub_req(rr);
1019     return -1;
1020     }
1021     }
1022     else {
1023 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1024     "to tag %s in %s", tag, directive, r->filename);
1025 nd 101049 return -1;
1026     }
1027     }
1028    
1029 nd 101053 /*
1030     * <!--#include virtual|file="..." [virtual|file="..."] ... -->
1031     */
1032 nd 101049 static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
1033     apr_bucket_brigade *bb)
1034     {
1035     request_rec *r = f->r;
1036    
1037 nd 101053 if (!ctx->argc) {
1038     ap_log_rerror(APLOG_MARK,
1039     (ctx->flags & SSI_FLAG_PRINTING)
1040     ? APLOG_ERR : APLOG_WARNING,
1041     0, r, "missing argument for include element in %s",
1042     r->filename);
1043     }
1044 nd 101049
1045 nd 101053 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1046     return APR_SUCCESS;
1047     }
1048 nd 101049
1049 nd 101053 if (!ctx->argc) {
1050     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1051     return APR_SUCCESS;
1052     }
1053 nd 101049
1054 nd 101053 while (1) {
1055     char *tag = NULL;
1056     char *tag_val = NULL;
1057     request_rec *rr = NULL;
1058     char *error_fmt = NULL;
1059     char *parsed_string;
1060 nd 101049
1061 nd 101053 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1062     if (!tag || !tag_val) {
1063     break;
1064     }
1065 nd 101049
1066 nd 101053 if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
1067     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1068     "\"%s\" to tag include in %s", tag, r->filename);
1069     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1070     break;
1071     }
1072 nd 101049
1073 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1074 nd 101053 SSI_EXPAND_DROP_NAME);
1075     if (tag[0] == 'f') {
1076 nd 101080 char *newpath;
1077     apr_status_t rv;
1078    
1079     /* be safe; only files in this directory or below allowed */
1080 nd 102147 rv = apr_filepath_merge(&newpath, NULL, parsed_string,
1081 nd 101080 APR_FILEPATH_SECUREROOTTEST |
1082     APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
1083    
1084 jerenkrantz 104439 if (rv != APR_SUCCESS) {
1085 nd 101053 error_fmt = "unable to include file \"%s\" in parsed file %s";
1086     }
1087     else {
1088 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, f->next);
1089 nd 101053 }
1090     }
1091     else {
1092 minfrin 592951 if (r->kept_body) {
1093     rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
1094     }
1095     else {
1096     rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
1097     }
1098 nd 101053 }
1099 nd 101049
1100 nd 101053 if (!error_fmt && rr->status != HTTP_OK) {
1101     error_fmt = "unable to include \"%s\" in parsed file %s";
1102     }
1103 nd 101049
1104 nd 101053 if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
1105     rr->content_type && strncmp(rr->content_type, "text/", 5)) {
1106    
1107     error_fmt = "unable to include potential exec \"%s\" in parsed "
1108     "file %s";
1109     }
1110    
1111     /* See the Kludge in includes_filter for why.
1112     * Basically, it puts a bread crumb in here, then looks
1113     * for the crumb later to see if its been here.
1114     */
1115 niq 642559 ctx->intern->kludge_child = rr;
1116 nd 101053
1117     if (!error_fmt && ap_run_sub_req(rr)) {
1118     error_fmt = "unable to include \"%s\" in parsed file %s";
1119     }
1120    
1121     if (error_fmt) {
1122     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
1123     r->filename);
1124     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1125     }
1126    
1127 jorton 179763 /* Do *not* destroy the subrequest here; it may have allocated
1128     * variables in this r->subprocess_env in the subrequest's
1129     * r->pool, so that pool must survive as long as this request.
1130     * Yes, this is a memory leak. */
1131 nd 101053
1132     if (error_fmt) {
1133     break;
1134     }
1135 nd 101049 }
1136    
1137     return APR_SUCCESS;
1138     }
1139    
1140 nd 101054 /*
1141     * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
1142     */
1143 nd 101049 static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
1144     apr_bucket_brigade *bb)
1145     {
1146     enum {E_NONE, E_URL, E_ENTITY} encode;
1147     request_rec *r = f->r;
1148    
1149 nd 101054 if (!ctx->argc) {
1150     ap_log_rerror(APLOG_MARK,
1151     (ctx->flags & SSI_FLAG_PRINTING)
1152     ? APLOG_ERR : APLOG_WARNING,
1153     0, r, "missing argument for echo element in %s",
1154     r->filename);
1155     }
1156    
1157     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1158     return APR_SUCCESS;
1159     }
1160    
1161     if (!ctx->argc) {
1162     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1163     return APR_SUCCESS;
1164     }
1165    
1166 nd 101049 encode = E_ENTITY;
1167    
1168 nd 101054 while (1) {
1169     char *tag = NULL;
1170     char *tag_val = NULL;
1171 nd 101049
1172 nd 101054 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1173     if (!tag || !tag_val) {
1174     break;
1175     }
1176 nd 101049
1177 nd 101054 if (!strcmp(tag, "var")) {
1178     const char *val;
1179     const char *echo_text = NULL;
1180     apr_size_t e_len;
1181    
1182 nd 101070 val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
1183 nd 101079 0, SSI_EXPAND_DROP_NAME),
1184 nd 101070 ctx);
1185    
1186 nd 101054 if (val) {
1187     switch(encode) {
1188     case E_NONE:
1189     echo_text = val;
1190     break;
1191     case E_URL:
1192     echo_text = ap_escape_uri(ctx->dpool, val);
1193     break;
1194     case E_ENTITY:
1195     echo_text = ap_escape_html(ctx->dpool, val);
1196     break;
1197 nd 101049 }
1198 nd 101054
1199     e_len = strlen(echo_text);
1200     }
1201     else {
1202 nd 101129 echo_text = ctx->intern->undefined_echo;
1203     e_len = ctx->intern->undefined_echo_len;
1204 nd 101049 }
1205 nd 101054
1206     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
1207 nd 101129 apr_pmemdup(ctx->pool, echo_text, e_len),
1208 nd 101054 e_len, ctx->pool, f->c->bucket_alloc));
1209     }
1210     else if (!strcmp(tag, "encoding")) {
1211     if (!strcasecmp(tag_val, "none")) {
1212     encode = E_NONE;
1213 nd 101049 }
1214 nd 101054 else if (!strcasecmp(tag_val, "url")) {
1215     encode = E_URL;
1216     }
1217     else if (!strcasecmp(tag_val, "entity")) {
1218     encode = E_ENTITY;
1219     }
1220 nd 101049 else {
1221 nd 101054 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1222     "\"%s\" to parameter \"encoding\" of tag echo in "
1223     "%s", tag_val, r->filename);
1224 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1225 nd 101054 break;
1226 nd 101049 }
1227     }
1228 nd 101054 else {
1229     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1230     "\"%s\" in tag echo of %s", tag, r->filename);
1231     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1232     break;
1233     }
1234 nd 101049 }
1235    
1236     return APR_SUCCESS;
1237     }
1238    
1239 nd 101055 /*
1240 nd 101130 * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
1241     * [echomsg="..."] -->
1242 nd 101049 */
1243     static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
1244     apr_bucket_brigade *bb)
1245     {
1246     request_rec *r = f->r;
1247     apr_table_t *env = r->subprocess_env;
1248    
1249 nd 101055 if (!ctx->argc) {
1250     ap_log_rerror(APLOG_MARK,
1251     (ctx->flags & SSI_FLAG_PRINTING)
1252     ? APLOG_ERR : APLOG_WARNING,
1253     0, r, "missing argument for config element in %s",
1254     r->filename);
1255     }
1256    
1257     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1258     return APR_SUCCESS;
1259     }
1260    
1261     if (!ctx->argc) {
1262     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1263     return APR_SUCCESS;
1264     }
1265    
1266     while (1) {
1267     char *tag = NULL;
1268     char *tag_val = NULL;
1269    
1270     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
1271     if (!tag || !tag_val) {
1272     break;
1273     }
1274    
1275     if (!strcmp(tag, "errmsg")) {
1276 nd 101071 ctx->error_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1277     SSI_EXPAND_DROP_NAME);
1278 nd 101055 }
1279 nd 101130 else if (!strcmp(tag, "echomsg")) {
1280     ctx->intern->undefined_echo =
1281     ap_ssi_parse_string(ctx, tag_val, NULL, 0,SSI_EXPAND_DROP_NAME);
1282     ctx->intern->undefined_echo_len=strlen(ctx->intern->undefined_echo);
1283     }
1284 nd 101055 else if (!strcmp(tag, "timefmt")) {
1285     apr_time_t date = r->request_time;
1286 nd 101049
1287 nd 101071 ctx->time_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1288     SSI_EXPAND_DROP_NAME);
1289    
1290 jim 332306 apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date,
1291 nd 101055 ctx->time_str, 0));
1292 jim 332306 apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date,
1293 nd 101055 ctx->time_str, 1));
1294     apr_table_setn(env, "LAST_MODIFIED",
1295 jim 332306 ap_ht_time(r->pool, r->finfo.mtime,
1296 nd 101055 ctx->time_str, 0));
1297     }
1298     else if (!strcmp(tag, "sizefmt")) {
1299 nd 101130 char *parsed_string;
1300    
1301 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1302 nd 101055 SSI_EXPAND_DROP_NAME);
1303     if (!strcmp(parsed_string, "bytes")) {
1304     ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
1305 nd 101049 }
1306 nd 101055 else if (!strcmp(parsed_string, "abbrev")) {
1307     ctx->flags &= SSI_FLAG_SIZE_ABBREV;
1308 nd 101049 }
1309     else {
1310 nd 101055 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1311     "\"%s\" to parameter \"sizefmt\" of tag config "
1312     "in %s", parsed_string, r->filename);
1313 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1314 nd 101055 break;
1315 nd 101049 }
1316     }
1317 nd 101055 else {
1318     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1319     "\"%s\" to tag config in %s", tag, r->filename);
1320     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1321     break;
1322     }
1323 nd 101049 }
1324    
1325     return APR_SUCCESS;
1326     }
1327    
1328 nd 101057 /*
1329     * <!--#fsize virtual|file="..." [virtual|file="..."] ... -->
1330     */
1331 nd 101049 static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
1332     apr_bucket_brigade *bb)
1333     {
1334     request_rec *r = f->r;
1335    
1336 nd 101057 if (!ctx->argc) {
1337     ap_log_rerror(APLOG_MARK,
1338     (ctx->flags & SSI_FLAG_PRINTING)
1339     ? APLOG_ERR : APLOG_WARNING,
1340     0, r, "missing argument for fsize element in %s",
1341     r->filename);
1342     }
1343    
1344     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1345     return APR_SUCCESS;
1346     }
1347    
1348     if (!ctx->argc) {
1349     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1350     return APR_SUCCESS;
1351     }
1352    
1353     while (1) {
1354     char *tag = NULL;
1355     char *tag_val = NULL;
1356     apr_finfo_t finfo;
1357     char *parsed_string;
1358    
1359     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1360     if (!tag || !tag_val) {
1361     break;
1362     }
1363    
1364 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1365 nd 101057 SSI_EXPAND_DROP_NAME);
1366    
1367     if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
1368     char *buf;
1369     apr_size_t len;
1370    
1371     if (!(ctx->flags & SSI_FLAG_SIZE_IN_BYTES)) {
1372     buf = apr_strfsize(finfo.size, apr_palloc(ctx->pool, 5));
1373     len = 4; /* omit the \0 terminator */
1374 nd 101049 }
1375     else {
1376 nd 101057 apr_size_t l, x, pos;
1377     char *tmp;
1378 nd 101049
1379 nd 101057 tmp = apr_psprintf(ctx->dpool, "%" APR_OFF_T_FMT, finfo.size);
1380     len = l = strlen(tmp);
1381 nd 101049
1382 nd 101057 for (x = 0; x < l; ++x) {
1383     if (x && !((l - x) % 3)) {
1384     ++len;
1385 nd 101049 }
1386 nd 101057 }
1387 nd 101049
1388 nd 101057 if (len == l) {
1389     buf = apr_pstrmemdup(ctx->pool, tmp, len);
1390 nd 101049 }
1391     else {
1392 nd 101057 buf = apr_palloc(ctx->pool, len);
1393    
1394     for (pos = x = 0; x < l; ++x) {
1395     if (x && !((l - x) % 3)) {
1396     buf[pos++] = ',';
1397     }
1398     buf[pos++] = tmp[x];
1399     }
1400 nd 101049 }
1401     }
1402 nd 101057
1403     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buf, len,
1404     ctx->pool, f->c->bucket_alloc));
1405 nd 101049 }
1406 nd 101057 else {
1407     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1408     break;
1409     }
1410 nd 101049 }
1411    
1412     return APR_SUCCESS;
1413     }
1414    
1415 nd 101063 /*
1416     * <!--#flastmod virtual|file="..." [virtual|file="..."] ... -->
1417     */
1418 nd 101049 static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
1419     apr_bucket_brigade *bb)
1420     {
1421     request_rec *r = f->r;
1422    
1423 nd 101063 if (!ctx->argc) {
1424     ap_log_rerror(APLOG_MARK,
1425     (ctx->flags & SSI_FLAG_PRINTING)
1426     ? APLOG_ERR : APLOG_WARNING,
1427     0, r, "missing argument for flastmod element in %s",
1428     r->filename);
1429     }
1430 nd 101049
1431 nd 101063 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1432     return APR_SUCCESS;
1433     }
1434 nd 101049
1435 nd 101063 if (!ctx->argc) {
1436     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1437     return APR_SUCCESS;
1438     }
1439    
1440     while (1) {
1441     char *tag = NULL;
1442     char *tag_val = NULL;
1443     apr_finfo_t finfo;
1444     char *parsed_string;
1445    
1446     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1447     if (!tag || !tag_val) {
1448     break;
1449 nd 101049 }
1450 nd 101063
1451 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1452 nd 101063 SSI_EXPAND_DROP_NAME);
1453    
1454     if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
1455     char *t_val;
1456     apr_size_t t_len;
1457    
1458     t_val = ap_ht_time(ctx->pool, finfo.mtime, ctx->time_str, 0);
1459     t_len = strlen(t_val);
1460    
1461     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(t_val, t_len,
1462     ctx->pool, f->c->bucket_alloc));
1463     }
1464     else {
1465     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1466     break;
1467     }
1468 nd 101049 }
1469    
1470     return APR_SUCCESS;
1471     }
1472    
1473 nd 101064 /*
1474     * <!--#if expr="..." -->
1475     */
1476 nd 101046 static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
1477     apr_bucket_brigade *bb)
1478 fielding 83751 {
1479 nd 101064 char *tag = NULL;
1480     char *expr = NULL;
1481 nd 101046 request_rec *r = f->r;
1482 nd 101078 int expr_ret, was_error;
1483 fielding 83751
1484 nd 101064 if (ctx->argc != 1) {
1485     ap_log_rerror(APLOG_MARK,
1486     (ctx->flags & SSI_FLAG_PRINTING)
1487     ? APLOG_ERR : APLOG_WARNING,
1488     0, r, (ctx->argc)
1489     ? "too many arguments for if element in %s"
1490     : "missing expr argument for if element in %s",
1491     r->filename);
1492     }
1493    
1494 nd 101048 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1495 nd 101064 ++(ctx->if_nesting_level);
1496     return APR_SUCCESS;
1497 rbb 87067 }
1498 nd 101046
1499 nd 101064 if (ctx->argc != 1) {
1500     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1501     return APR_SUCCESS;
1502     }
1503    
1504     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1505    
1506     if (strcmp(tag, "expr")) {
1507     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1508     "to tag if in %s", tag, r->filename);
1509     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1510     return APR_SUCCESS;
1511     }
1512    
1513     if (!expr) {
1514     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr value for if "
1515     "element in %s", r->filename);
1516     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1517     return APR_SUCCESS;
1518     }
1519    
1520 nd 101078 DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr));
1521 rbb 87067
1522 niq 642559 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1523     ssi_parse_string, ctx->intern->access_func);
1524 nd 101064
1525     if (was_error) {
1526     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1527     return APR_SUCCESS;
1528 fielding 83751 }
1529 nd 101048
1530 nd 101064 if (expr_ret) {
1531     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1532     }
1533     else {
1534     ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1535     }
1536    
1537 nd 101078 DEBUG_DUMP_COND(ctx, " if");
1538    
1539 nd 101064 ctx->if_nesting_level = 0;
1540    
1541 nd 101046 return APR_SUCCESS;
1542 fielding 83751 }
1543    
1544 nd 101065 /*
1545     * <!--#elif expr="..." -->
1546     */
1547 nd 101046 static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
1548     apr_bucket_brigade *bb)
1549 fielding 83751 {
1550 nd 101065 char *tag = NULL;
1551     char *expr = NULL;
1552     request_rec *r = f->r;
1553 nd 101078 int expr_ret, was_error;
1554 fielding 83751
1555 nd 101065 if (ctx->argc != 1) {
1556     ap_log_rerror(APLOG_MARK,
1557     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1558     0, r, (ctx->argc)
1559     ? "too many arguments for if element in %s"
1560     : "missing expr argument for if element in %s",
1561     r->filename);
1562     }
1563    
1564     if (ctx->if_nesting_level) {
1565     return APR_SUCCESS;
1566     }
1567    
1568     if (ctx->argc != 1) {
1569     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1570     return APR_SUCCESS;
1571     }
1572    
1573     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1574    
1575     if (strcmp(tag, "expr")) {
1576     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1577     "to tag if in %s", tag, r->filename);
1578     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1579     return APR_SUCCESS;
1580     }
1581    
1582     if (!expr) {
1583     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr in elif "
1584     "statement: %s", r->filename);
1585     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1586     return APR_SUCCESS;
1587     }
1588    
1589 nd 101078 DEBUG_PRINTF((ctx, "**** elif expr=\"%s\"\n", expr));
1590     DEBUG_DUMP_COND(ctx, " elif");
1591 nd 101065
1592     if (ctx->flags & SSI_FLAG_COND_TRUE) {
1593     ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1594     return APR_SUCCESS;
1595 fielding 83751 }
1596 nd 101048
1597 niq 642559 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1598     ssi_parse_string, ctx->intern->access_func);
1599 nd 101065
1600     if (was_error) {
1601     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1602     return APR_SUCCESS;
1603     }
1604    
1605     if (expr_ret) {
1606     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1607     }
1608     else {
1609     ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1610     }
1611    
1612 nd 101078 DEBUG_DUMP_COND(ctx, " elif");
1613 nd 101065
1614 nd 101046 return APR_SUCCESS;
1615 fielding 83751 }
1616    
1617 nd 101066 /*
1618     * <!--#else -->
1619     */
1620 nd 101046 static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f,
1621     apr_bucket_brigade *bb)
1622 fielding 83751 {
1623 nd 101046 request_rec *r = f->r;
1624 fielding 83751
1625 nd 101066 if (ctx->argc) {
1626     ap_log_rerror(APLOG_MARK,
1627     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1628     0, r, "else directive does not take tags in %s",
1629     r->filename);
1630     }
1631    
1632     if (ctx->if_nesting_level) {
1633     return APR_SUCCESS;
1634     }
1635    
1636     if (ctx->argc) {
1637     if (ctx->flags & SSI_FLAG_PRINTING) {
1638     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1639 fielding 83751 }
1640 nd 101066
1641     return APR_SUCCESS;
1642     }
1643    
1644 nd 101078 DEBUG_DUMP_COND(ctx, " else");
1645 jim 332306
1646 nd 101066 if (ctx->flags & SSI_FLAG_COND_TRUE) {
1647     ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1648 fielding 83751 }
1649 nd 101066 else {
1650     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1651     }
1652 nd 101048
1653 nd 101046 return APR_SUCCESS;
1654 fielding 83751 }
1655    
1656 nd 101066 /*
1657     * <!--#endif -->
1658     */
1659 nd 101046 static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f,
1660     apr_bucket_brigade *bb)
1661 fielding 83751 {
1662 nd 101046 request_rec *r = f->r;
1663 fielding 83751
1664 nd 101066 if (ctx->argc) {
1665     ap_log_rerror(APLOG_MARK,
1666     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1667     0, r, "endif directive does not take tags in %s",
1668     r->filename);
1669 fielding 83751 }
1670 nd 101066
1671     if (ctx->if_nesting_level) {
1672     --(ctx->if_nesting_level);
1673 nd 101046 return APR_SUCCESS;
1674 fielding 83751 }
1675 nd 101066
1676     if (ctx->argc) {
1677     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1678     return APR_SUCCESS;
1679     }
1680    
1681 nd 101078 DEBUG_DUMP_COND(ctx, "endif");
1682    
1683 nd 101066 ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1684    
1685     return APR_SUCCESS;
1686 fielding 83751 }
1687    
1688 nd 101067 /*
1689     * <!--#set var="..." value="..." ... -->
1690     */
1691 nd 101046 static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
1692     apr_bucket_brigade *bb)
1693 fielding 83751 {
1694 nd 101067 char *var = NULL;
1695 nd 101046 request_rec *r = f->r;
1696 ianh 93556 request_rec *sub = r->main;
1697     apr_pool_t *p = r->pool;
1698 fielding 83751
1699 nd 101067 if (ctx->argc < 2) {
1700     ap_log_rerror(APLOG_MARK,
1701     (ctx->flags & SSI_FLAG_PRINTING)
1702     ? APLOG_ERR : APLOG_WARNING,
1703     0, r, "missing argument for set element in %s",
1704     r->filename);
1705     }
1706    
1707     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1708     return APR_SUCCESS;
1709     }
1710    
1711     if (ctx->argc < 2) {
1712     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1713     return APR_SUCCESS;
1714     }
1715    
1716 jim 332306 /* we need to use the 'main' request pool to set notes as that is
1717 ianh 93556 * a notes lifetime
1718     */
1719     while (sub) {
1720     p = sub->pool;
1721     sub = sub->main;
1722     }
1723    
1724 nd 101067 while (1) {
1725     char *tag = NULL;
1726     char *tag_val = NULL;
1727    
1728     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1729    
1730     if (!tag || !tag_val) {
1731     break;
1732     }
1733    
1734     if (!strcmp(tag, "var")) {
1735 nd 101079 var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1736 nd 101067 SSI_EXPAND_DROP_NAME);
1737     }
1738     else if (!strcmp(tag, "value")) {
1739     char *parsed_string;
1740    
1741     if (!var) {
1742     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "variable must "
1743     "precede value in set directive in %s",
1744     r->filename);
1745 nd 101046 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1746 nd 101067 break;
1747 fielding 83751 }
1748 nd 101067
1749 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1750 nd 101067 SSI_EXPAND_DROP_NAME);
1751     apr_table_setn(r->subprocess_env, apr_pstrdup(p, var),
1752     apr_pstrdup(p, parsed_string));
1753 fielding 83751 }
1754 nd 101067 else {
1755     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid tag for set "
1756     "directive in %s", r->filename);
1757     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1758     break;
1759     }
1760 fielding 83751 }
1761 nd 101048
1762 nd 101046 return APR_SUCCESS;
1763 fielding 83751 }
1764    
1765 nd 101068 /*
1766     * <!--#printenv -->
1767     */
1768 nd 101046 static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f,
1769     apr_bucket_brigade *bb)
1770 fielding 83751 {
1771 nd 101046 request_rec *r = f->r;
1772 nd 101068 const apr_array_header_t *arr;
1773     const apr_table_entry_t *elts;
1774     int i;
1775 fielding 83751
1776 nd 101068 if (ctx->argc) {
1777     ap_log_rerror(APLOG_MARK,
1778     (ctx->flags & SSI_FLAG_PRINTING)
1779     ? APLOG_ERR : APLOG_WARNING,
1780     0, r, "printenv directive does not take tags in %s",
1781     r->filename);
1782     }
1783 rbb 87067
1784 nd 101068 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1785     return APR_SUCCESS;
1786     }
1787    
1788     if (ctx->argc) {
1789     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1790     return APR_SUCCESS;
1791     }
1792    
1793     arr = apr_table_elts(r->subprocess_env);
1794     elts = (apr_table_entry_t *)arr->elts;
1795    
1796     for (i = 0; i < arr->nelts; ++i) {
1797     const char *key_text, *val_text;
1798     char *key_val, *next;
1799     apr_size_t k_len, v_len, kv_length;
1800    
1801     /* get key */
1802     key_text = ap_escape_html(ctx->dpool, elts[i].key);
1803     k_len = strlen(key_text);
1804    
1805     /* get value */
1806     val_text = elts[i].val;
1807     if (val_text == LAZY_VALUE) {
1808     val_text = add_include_vars_lazy(r, elts[i].key);
1809 fielding 83751 }
1810 nd 101068 val_text = ap_escape_html(ctx->dpool, elts[i].val);
1811     v_len = strlen(val_text);
1812    
1813     /* assemble result */
1814     kv_length = k_len + v_len + sizeof("=\n");
1815     key_val = apr_palloc(ctx->pool, kv_length);
1816     next = key_val;
1817    
1818     memcpy(next, key_text, k_len);
1819     next += k_len;
1820     *next++ = '=';
1821     memcpy(next, val_text, v_len);
1822     next += v_len;
1823     *next++ = '\n';
1824     *next = 0;
1825    
1826     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(key_val, kv_length-1,
1827     ctx->pool, f->c->bucket_alloc));
1828 fielding 83751 }
1829 nd 101048
1830 nd 101068 ctx->flush_now = 1;
1831 nd 101046 return APR_SUCCESS;
1832 fielding 83751 }
1833    
1834    
1835 nd 101036 /*
1836 nd 101049 * +-------------------------------------------------------+
1837     * | |
1838     * | Main Includes-Filter Engine
1839     * | |
1840     * +-------------------------------------------------------+
1841     */
1842    
1843     /* This is an implementation of the BNDM search algorithm.
1844     *
1845 jim 332306 * Fast and Flexible String Matching by Combining Bit-parallelism and
1846     * Suffix Automata (2001)
1847 nd 101049 * Gonzalo Navarro, Mathieu Raffinot
1848     *
1849     * http://www-igm.univ-mlv.fr/~raffinot/ftp/jea2001.ps.gz
1850     *
1851     * Initial code submitted by Sascha Schumann.
1852     */
1853 jim 332306
1854 nd 101049 /* Precompile the bndm_t data structure. */
1855 nd 101052 static bndm_t *bndm_compile(apr_pool_t *pool, const char *n, apr_size_t nl)
1856 nd 101049 {
1857     unsigned int x;
1858     const char *ne = n + nl;
1859 nd 101052 bndm_t *t = apr_palloc(pool, sizeof(*t));
1860 nd 101049
1861     memset(t->T, 0, sizeof(unsigned int) * 256);
1862 nd 101052 t->pattern_len = nl;
1863    
1864     for (x = 1; n < ne; x <<= 1) {
1865 nd 101049 t->T[(unsigned char) *n++] |= x;
1866 nd 101052 }
1867 nd 101049
1868     t->x = x - 1;
1869 nd 101052
1870     return t;
1871 nd 101049 }
1872    
1873     /* Implements the BNDM search algorithm (as described above).
1874     *
1875     * h - the string to look in
1876     * hl - length of the string to look for
1877 jim 332306 * t - precompiled bndm structure against the pattern
1878 nd 101049 *
1879     * Returns the count of character that is the first match or hl if no
1880     * match is found.
1881     */
1882 nd 101052 static apr_size_t bndm(bndm_t *t, const char *h, apr_size_t hl)
1883 nd 101049 {
1884     const char *skip;
1885     const char *he, *p, *pi;
1886     unsigned int *T, x, d;
1887 nd 101052 apr_size_t nl;
1888 nd 101049
1889     he = h + hl;
1890    
1891     T = t->T;
1892     x = t->x;
1893 nd 101052 nl = t->pattern_len;
1894 nd 101049
1895     pi = h - 1; /* pi: p initial */
1896