/[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 795642 - (hide annotations)
Sun Jul 19 21:54:36 2009 UTC (4 months, 1 week ago) by niq
File MIME type: text/plain
File size: 104228 byte(s)
Update r795445 patch in the light of rpluem's comments on-list
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 jorton 757376 static void add_include_vars(request_rec *r)
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 jorton 757376 static const char *add_include_vars_lazy(request_rec *r, const char *var, const char *timefmt)
569 nd 101049 {
570     char *val;
571     if (!strcasecmp(var, "DATE_LOCAL")) {
572 jorton 757376 val = ap_ht_time(r->pool, r->request_time, timefmt, 0);
573 nd 101049 }
574     else if (!strcasecmp(var, "DATE_GMT")) {
575 jorton 757376 val = ap_ht_time(r->pool, r->request_time, timefmt, 1);
576 nd 101049 }
577     else if (!strcasecmp(var, "LAST_MODIFIED")) {
578 jorton 757376 val = ap_ht_time(r->pool, r->finfo.mtime, timefmt, 0);
579 nd 101049 }
580     else if (!strcasecmp(var, "USER_NAME")) {
581 trawick 101154 if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
582 nd 101049 val = "<unknown>";
583     }
584     }
585     else {
586     val = NULL;
587     }
588 nd 101036
589 nd 101049 if (val) {
590     apr_table_setn(r->subprocess_env, var, val);
591 nd 101036 }
592 nd 101049 return val;
593     }
594 nd 101036
595 nd 101070 static const char *get_include_var(const char *var, include_ctx_t *ctx)
596 nd 101049 {
597     const char *val;
598 nd 101073 request_rec *r = ctx->intern->r;
599    
600 nd 101049 if (apr_isdigit(*var) && !var[1]) {
601 nd 101100 apr_size_t idx = *var - '0';
602 nd 101073 backref_t *re = ctx->intern->re;
603    
604 nd 101049 /* Handle $0 .. $9 from the last regex evaluated.
605     * The choice of returning NULL strings on not-found,
606     * v.s. empty strings on an empty match is deliberate.
607     */
608 niq 795445 if (!re || !re->have_match) {
609 fielding 101234 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
610     "regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
611     idx, r->filename);
612 nd 101049 return NULL;
613     }
614 niq 795642 else if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
615     ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
616     "regex capture $%" APR_SIZE_T_FMT
617     " is out of range (last regex was: '%s') in %s",
618     idx, re->rexp, r->filename);
619 niq 795445 return NULL;
620     }
621     else if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
622     /* I don't think this can happen if have_match is true.
623     * But let's not risk a regression by dropping this
624     */
625     return NULL;
626     }
627    
628 nd 101049 else {
629 nd 101073 val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
630     re->match[idx].rm_eo - re->match[idx].rm_so);
631 nd 101049 }
632     }
633     else {
634 nd 101073 val = apr_table_get(r->subprocess_env, var);
635 nd 101049
636 nd 101073 if (val == LAZY_VALUE) {
637 jorton 757376 val = add_include_vars_lazy(r, var, ctx->time_str);
638 nd 101073 }
639 nd 101049 }
640 nd 101073
641 nd 101049 return val;
642 nd 101036 }
643    
644 fielding 83751 /*
645     * Do variable substitution on strings
646 nd 101070 *
647 brianp 92747 * (Note: If out==NULL, this function allocs a buffer for the resulting
648 nd 101070 * string from ctx->pool. The return value is always the parsed string)
649 fielding 83751 */
650 nd 101069 static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
651 brianp 92747 apr_size_t length, int leave_name)
652 fielding 83751 {
653 nd 101069 request_rec *r = ctx->intern->r;
654 nd 101070 result_item_t *result = NULL, *current = NULL;
655     apr_size_t outlen = 0, inlen, span;
656     char *ret = NULL, *eout = NULL;
657     const char *p;
658 fielding 83751
659 nd 101070 if (out) {
660     /* sanity check, out && !length is not supported */
661     ap_assert(out && length);
662    
663     ret = out;
664     eout = out + length - 1;
665     }
666    
667     span = strcspn(in, "\\$");
668     inlen = strlen(in);
669    
670     /* fast exit */
671     if (inlen == span) {
672     if (out) {
673     apr_cpystrn(out, in, length);
674 brianp 92747 }
675 nd 101070 else {
676     ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
677     ? length - 1 : inlen);
678     }
679    
680     return ret;
681 brianp 92747 }
682 nd 101070
683     /* well, actually something to do */
684     p = in + span;
685    
686     if (out) {
687     if (span) {
688     memcpy(out, in, (out+span <= eout) ? span : (eout-out));
689     out += span;
690     }
691     }
692 brianp 92747 else {
693 nd 101070 current = result = apr_palloc(ctx->dpool, sizeof(*result));
694     current->next = NULL;
695     current->string = in;
696     current->len = span;
697     outlen = span;
698 brianp 92747 }
699    
700 nd 101070 /* loop for specials */
701     do {
702     if ((out && out >= eout) || (length && outlen >= length)) {
703     break;
704     }
705 fielding 83751
706 nd 101070 /* prepare next entry */
707     if (!out && current->len) {
708     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
709     current = current->next;
710     current->next = NULL;
711     current->len = 0;
712     }
713    
714     /*
715     * escaped character
716     */
717     if (*p == '\\') {
718     if (out) {
719     *out++ = (p[1] == '$') ? *++p : *p;
720     ++p;
721     }
722     else {
723     current->len = 1;
724     current->string = (p[1] == '$') ? ++p : p;
725     ++p;
726     ++outlen;
727     }
728     }
729    
730     /*
731     * variable expansion
732     */
733     else { /* *p == '$' */
734     const char *newp = NULL, *ep, *key = NULL;
735    
736     if (*++p == '{') {
737     ep = ap_strchr_c(++p, '}');
738     if (!ep) {
739     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing '}' on "
740     "variable \"%s\" in %s", p, r->filename);
741     break;
742 brianp 92747 }
743 nd 101070
744     if (p < ep) {
745     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
746     newp = ep + 1;
747 brianp 92747 }
748 nd 101070 p -= 2;
749 jerenkrantz 90874 }
750 fielding 83751 else {
751 nd 101070 ep = p;
752     while (*ep == '_' || apr_isalnum(*ep)) {
753     ++ep;
754     }
755    
756     if (p < ep) {
757     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
758     newp = ep;
759     }
760     --p;
761 fielding 83751 }
762    
763 nd 101070 /* empty name results in a copy of '$' in the output string */
764     if (!key) {
765     if (out) {
766     *out++ = *p++;
767 jerenkrantz 90874 }
768     else {
769 nd 101070 current->len = 1;
770     current->string = p++;
771     ++outlen;
772 jerenkrantz 90874 }
773 nd 101070 }
774     else {
775     const char *val = get_include_var(key, ctx);
776     apr_size_t len = 0;
777 fielding 83751
778 nd 101070 if (val) {
779     len = strlen(val);
780 jerenkrantz 90874 }
781 nd 101070 else if (leave_name) {
782     val = p;
783     len = ep - p;
784 jerenkrantz 90874 }
785 nd 101070
786     if (val && len) {
787     if (out) {
788     memcpy(out, val, (out+len <= eout) ? len : (eout-out));
789     out += len;
790 brianp 92747 }
791 nd 101070 else {
792     current->len = len;
793     current->string = val;
794     outlen += len;
795 brianp 92747 }
796     }
797 nd 101070
798     p = newp;
799 jerenkrantz 90874 }
800 nd 101070 }
801    
802     if ((out && out >= eout) || (length && outlen >= length)) {
803 fielding 83751 break;
804     }
805 nd 101070
806     /* check the remainder */
807     if (*p && (span = strcspn(p, "\\$")) > 0) {
808     if (!out && current->len) {
809     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
810     current = current->next;
811     current->next = NULL;
812     }
813    
814     if (out) {
815     memcpy(out, p, (out+span <= eout) ? span : (eout-out));
816     out += span;
817     }
818     else {
819     current->len = span;
820     current->string = p;
821     outlen += span;
822     }
823    
824     p += span;
825     }
826     } while (p < in+inlen);
827    
828     /* assemble result */
829     if (out) {
830     if (out > eout) {
831     *eout = '\0';
832     }
833     else {
834     *out = '\0';
835     }
836 fielding 83751 }
837 nd 101070 else {
838     const char *ep;
839    
840     if (length && outlen > length) {
841     outlen = length - 1;
842     }
843    
844     ret = out = apr_palloc(ctx->pool, outlen + 1);
845     ep = ret + outlen;
846    
847     do {
848     if (result->len) {
849     memcpy(out, result->string, (out+result->len <= ep)
850     ? result->len : (ep-out));
851     out += result->len;
852     }
853     result = result->next;
854     } while (result && out < ep);
855    
856     ret[outlen] = '\0';
857     }
858    
859     return ret;
860 fielding 83751 }
861    
862 niq 642978 static const char *ssi_parse_string(request_rec *r, const char *in)
863 fielding 83751 {
864 niq 642559 include_ctx_t *ctx = ap_get_module_config(r->request_config,
865     &include_module);
866     return ap_ssi_parse_string(ctx, in, NULL, 0, SSI_EXPAND_DROP_NAME);
867 fielding 83751 }
868 niq 642978 static int ssi_access(request_rec *r, ap_parse_node_t *current,
869 niq 642559 string_func_t parse_string)
870 fielding 83751 {
871 niq 642559 request_rec *rr;
872     include_ctx_t *ctx = ap_get_module_config(r->request_config,
873     &include_module);
874 fielding 83751
875 niq 642559 /* if this arg isn't -A, just return */
876     if (current->token.type != TOKEN_ACCESS || current->token.value[0] != 'A') {
877     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
878     "Unsupported option -%s in file %s",
879     current->token.value, r->filename);
880     return 1;
881 nd 101078 }
882 niq 642559 if (current->left || !current->right ||
883     (current->right->token.type != TOKEN_STRING &&
884     current->right->token.type != TOKEN_RE)) {
885     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
886     "Invalid expression in file %s: Token '-A' must be followed by a URI string.",
887     r->filename);
888     return 1; /* was_error */
889 fielding 83751 }
890 niq 642559 current->right->token.value =
891     ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
892     SSI_EXPAND_DROP_NAME);
893     rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
894     /* 400 and higher are considered access denied */
895     if (rr->status < HTTP_BAD_REQUEST) {
896     current->value = 1;
897 fielding 83751 }
898 nd 101078 else {
899 niq 642559 current->value = 0;
900     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r,
901     "mod_include: The tested "
902     "subrequest -A \"%s\" returned an error code.",
903     current->right->token.value);
904 nd 101078 }
905 niq 642559 ap_destroy_sub_req(rr);
906     return 0;
907 nd 101078 }
908    
909 nd 101049 /*
910     * +-------------------------------------------------------+
911     * | |
912     * | Action Handlers
913     * | |
914     * +-------------------------------------------------------+
915     */
916    
917     /*
918     * Extract the next tag name and value.
919     * If there are no more tags, set the tag name to NULL.
920     * The tag value is html decoded if dodecode is non-zero.
921     * The tag value may be NULL if there is no tag value..
922     */
923     static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
924     char **tag_val, int dodecode)
925     {
926     if (!ctx->intern->argv) {
927     *tag = NULL;
928     *tag_val = NULL;
929    
930     return;
931     }
932    
933     *tag_val = ctx->intern->argv->value;
934     *tag = ctx->intern->argv->name;
935    
936     ctx->intern->argv = ctx->intern->argv->next;
937    
938     if (dodecode && *tag_val) {
939     decodehtml(*tag_val);
940     }
941    
942     return;
943     }
944    
945     static int find_file(request_rec *r, const char *directive, const char *tag,
946     char *tag_val, apr_finfo_t *finfo)
947     {
948     char *to_send = tag_val;
949     request_rec *rr = NULL;
950     int ret=0;
951     char *error_fmt = NULL;
952     apr_status_t rv = APR_SUCCESS;
953    
954     if (!strcmp(tag, "file")) {
955 nd 101080 char *newpath;
956    
957     /* be safe; only files in this directory or below allowed */
958     rv = apr_filepath_merge(&newpath, NULL, tag_val,
959     APR_FILEPATH_SECUREROOTTEST |
960     APR_FILEPATH_NOTABSOLUTE, r->pool);
961    
962 jerenkrantz 104439 if (rv != APR_SUCCESS) {
963 nd 101049 error_fmt = "unable to access file \"%s\" "
964     "in parsed file %s";
965     }
966     else {
967     /* note: it is okay to pass NULL for the "next filter" since
968     we never attempt to "run" this sub request. */
969 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, NULL);
970 nd 101049
971     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
972     to_send = rr->filename;
973 jim 332306 if ((rv = apr_stat(finfo, to_send,
974 nd 101049 APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
975     && rv != APR_INCOMPLETE) {
976     error_fmt = "unable to get information about \"%s\" "
977     "in parsed file %s";
978     }
979     }
980     else {
981     error_fmt = "unable to lookup information about \"%s\" "
982     "in parsed file %s";
983     }
984     }
985    
986     if (error_fmt) {
987     ret = -1;
988     ap_log_rerror(APLOG_MARK, APLOG_ERR,
989     rv, r, error_fmt, to_send, r->filename);
990     }
991    
992     if (rr) ap_destroy_sub_req(rr);
993 jim 332306
994 nd 101049 return ret;
995     }
996     else if (!strcmp(tag, "virtual")) {
997     /* note: it is okay to pass NULL for the "next filter" since
998     we never attempt to "run" this sub request. */
999     rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
1000    
1001     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
1002     memcpy((char *) finfo, (const char *) &rr->finfo,
1003     sizeof(rr->finfo));
1004     ap_destroy_sub_req(rr);
1005     return 0;
1006     }
1007     else {
1008 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to get "
1009     "information about \"%s\" in parsed file %s",
1010     tag_val, r->filename);
1011 nd 101049 ap_destroy_sub_req(rr);
1012     return -1;
1013     }
1014     }
1015     else {
1016 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1017     "to tag %s in %s", tag, directive, r->filename);
1018 nd 101049 return -1;
1019     }
1020     }
1021    
1022 nd 101053 /*
1023     * <!--#include virtual|file="..." [virtual|file="..."] ... -->
1024     */
1025 nd 101049 static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
1026     apr_bucket_brigade *bb)
1027     {
1028     request_rec *r = f->r;
1029    
1030 nd 101053 if (!ctx->argc) {
1031     ap_log_rerror(APLOG_MARK,
1032     (ctx->flags & SSI_FLAG_PRINTING)
1033     ? APLOG_ERR : APLOG_WARNING,
1034     0, r, "missing argument for include element in %s",
1035     r->filename);
1036     }
1037 nd 101049
1038 nd 101053 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1039     return APR_SUCCESS;
1040     }
1041 nd 101049
1042 nd 101053 if (!ctx->argc) {
1043     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1044     return APR_SUCCESS;
1045     }
1046 nd 101049
1047 nd 101053 while (1) {
1048     char *tag = NULL;
1049     char *tag_val = NULL;
1050     request_rec *rr = NULL;
1051     char *error_fmt = NULL;
1052     char *parsed_string;
1053 nd 101049
1054 nd 101053 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1055     if (!tag || !tag_val) {
1056     break;
1057     }
1058 nd 101049
1059 nd 101053 if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
1060     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1061     "\"%s\" to tag include in %s", tag, r->filename);
1062     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1063     break;
1064     }
1065 nd 101049
1066 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1067 nd 101053 SSI_EXPAND_DROP_NAME);
1068     if (tag[0] == 'f') {
1069 nd 101080 char *newpath;
1070     apr_status_t rv;
1071    
1072     /* be safe; only files in this directory or below allowed */
1073 nd 102147 rv = apr_filepath_merge(&newpath, NULL, parsed_string,
1074 nd 101080 APR_FILEPATH_SECUREROOTTEST |
1075     APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
1076    
1077 jerenkrantz 104439 if (rv != APR_SUCCESS) {
1078 nd 101053 error_fmt = "unable to include file \"%s\" in parsed file %s";
1079     }
1080     else {
1081 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, f->next);
1082 nd 101053 }
1083     }
1084     else {
1085 minfrin 592951 if (r->kept_body) {
1086     rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
1087     }
1088     else {
1089     rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
1090     }
1091 nd 101053 }
1092 nd 101049
1093 nd 101053 if (!error_fmt && rr->status != HTTP_OK) {
1094     error_fmt = "unable to include \"%s\" in parsed file %s";
1095     }
1096 nd 101049
1097 nd 101053 if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
1098     rr->content_type && strncmp(rr->content_type, "text/", 5)) {
1099    
1100     error_fmt = "unable to include potential exec \"%s\" in parsed "
1101     "file %s";
1102     }
1103    
1104     /* See the Kludge in includes_filter for why.
1105     * Basically, it puts a bread crumb in here, then looks
1106     * for the crumb later to see if its been here.
1107     */
1108 niq 642559 ctx->intern->kludge_child = rr;
1109 nd 101053
1110     if (!error_fmt && ap_run_sub_req(rr)) {
1111     error_fmt = "unable to include \"%s\" in parsed file %s";
1112     }
1113    
1114     if (error_fmt) {
1115     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
1116     r->filename);
1117     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1118     }
1119    
1120 jorton 179763 /* Do *not* destroy the subrequest here; it may have allocated
1121     * variables in this r->subprocess_env in the subrequest's
1122     * r->pool, so that pool must survive as long as this request.
1123     * Yes, this is a memory leak. */
1124 nd 101053
1125     if (error_fmt) {
1126     break;
1127     }
1128 nd 101049 }
1129    
1130     return APR_SUCCESS;
1131     }
1132    
1133 nd 101054 /*
1134     * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
1135     */
1136 nd 101049 static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
1137     apr_bucket_brigade *bb)
1138     {
1139     enum {E_NONE, E_URL, E_ENTITY} encode;
1140     request_rec *r = f->r;
1141    
1142 nd 101054 if (!ctx->argc) {
1143     ap_log_rerror(APLOG_MARK,
1144     (ctx->flags & SSI_FLAG_PRINTING)
1145     ? APLOG_ERR : APLOG_WARNING,
1146     0, r, "missing argument for echo element in %s",
1147     r->filename);
1148     }
1149    
1150     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1151     return APR_SUCCESS;
1152     }
1153    
1154     if (!ctx->argc) {
1155     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1156     return APR_SUCCESS;
1157     }
1158    
1159 nd 101049 encode = E_ENTITY;
1160    
1161 nd 101054 while (1) {
1162     char *tag = NULL;
1163     char *tag_val = NULL;
1164 nd 101049
1165 nd 101054 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1166     if (!tag || !tag_val) {
1167     break;
1168     }
1169 nd 101049
1170 nd 101054 if (!strcmp(tag, "var")) {
1171     const char *val;
1172     const char *echo_text = NULL;
1173     apr_size_t e_len;
1174    
1175 nd 101070 val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
1176 nd 101079 0, SSI_EXPAND_DROP_NAME),
1177 nd 101070 ctx);
1178    
1179 nd 101054 if (val) {
1180     switch(encode) {
1181     case E_NONE:
1182     echo_text = val;
1183     break;
1184     case E_URL:
1185     echo_text = ap_escape_uri(ctx->dpool, val);
1186     break;
1187     case E_ENTITY:
1188 niq 730296 /* PR#25202: escape anything non-ascii here */
1189     echo_text = ap_escape_html2(ctx->dpool, val, 1);
1190 nd 101054 break;
1191 nd 101049 }
1192 nd 101054
1193     e_len = strlen(echo_text);
1194     }
1195     else {
1196 nd 101129 echo_text = ctx->intern->undefined_echo;
1197     e_len = ctx->intern->undefined_echo_len;
1198 nd 101049 }
1199 nd 101054
1200     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
1201 nd 101129 apr_pmemdup(ctx->pool, echo_text, e_len),
1202 nd 101054 e_len, ctx->pool, f->c->bucket_alloc));
1203     }
1204     else if (!strcmp(tag, "encoding")) {
1205     if (!strcasecmp(tag_val, "none")) {
1206     encode = E_NONE;
1207 nd 101049 }
1208 nd 101054 else if (!strcasecmp(tag_val, "url")) {
1209     encode = E_URL;
1210     }
1211     else if (!strcasecmp(tag_val, "entity")) {
1212     encode = E_ENTITY;
1213     }
1214 nd 101049 else {
1215 nd 101054 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1216     "\"%s\" to parameter \"encoding\" of tag echo in "
1217     "%s", tag_val, r->filename);
1218 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1219 nd 101054 break;
1220 nd 101049 }
1221     }
1222 nd 101054 else {
1223     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1224     "\"%s\" in tag echo of %s", tag, r->filename);
1225     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1226     break;
1227     }
1228 nd 101049 }
1229    
1230     return APR_SUCCESS;
1231     }
1232    
1233 nd 101055 /*
1234 nd 101130 * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
1235     * [echomsg="..."] -->
1236 nd 101049 */
1237     static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
1238     apr_bucket_brigade *bb)
1239     {
1240     request_rec *r = f->r;
1241     apr_table_t *env = r->subprocess_env;
1242    
1243 nd 101055 if (!ctx->argc) {
1244     ap_log_rerror(APLOG_MARK,
1245     (ctx->flags & SSI_FLAG_PRINTING)
1246     ? APLOG_ERR : APLOG_WARNING,
1247     0, r, "missing argument for config element in %s",
1248     r->filename);
1249     }
1250    
1251     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1252     return APR_SUCCESS;
1253     }
1254    
1255     if (!ctx->argc) {
1256     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1257     return APR_SUCCESS;
1258     }
1259    
1260     while (1) {
1261     char *tag = NULL;
1262     char *tag_val = NULL;
1263    
1264     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
1265     if (!tag || !tag_val) {
1266     break;
1267     }
1268    
1269     if (!strcmp(tag, "errmsg")) {
1270 nd 101071 ctx->error_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1271     SSI_EXPAND_DROP_NAME);
1272 nd 101055 }
1273 nd 101130 else if (!strcmp(tag, "echomsg")) {
1274     ctx->intern->undefined_echo =
1275     ap_ssi_parse_string(ctx, tag_val, NULL, 0,SSI_EXPAND_DROP_NAME);
1276     ctx->intern->undefined_echo_len=strlen(ctx->intern->undefined_echo);
1277     }
1278 nd 101055 else if (!strcmp(tag, "timefmt")) {
1279     apr_time_t date = r->request_time;
1280 nd 101049
1281 nd 101071 ctx->time_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1282     SSI_EXPAND_DROP_NAME);
1283    
1284 jim 332306 apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date,
1285 nd 101055 ctx->time_str, 0));
1286 jim 332306 apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date,
1287 nd 101055 ctx->time_str, 1));
1288     apr_table_setn(env, "LAST_MODIFIED",
1289 jim 332306 ap_ht_time(r->pool, r->finfo.mtime,
1290 nd 101055 ctx->time_str, 0));
1291     }
1292     else if (!strcmp(tag, "sizefmt")) {
1293 nd 101130 char *parsed_string;
1294    
1295 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1296 nd 101055 SSI_EXPAND_DROP_NAME);
1297     if (!strcmp(parsed_string, "bytes")) {
1298     ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
1299 nd 101049 }
1300 nd 101055 else if (!strcmp(parsed_string, "abbrev")) {
1301     ctx->flags &= SSI_FLAG_SIZE_ABBREV;
1302 nd 101049 }
1303     else {
1304 nd 101055 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1305     "\"%s\" to parameter \"sizefmt\" of tag config "
1306     "in %s", parsed_string, r->filename);
1307 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1308 nd 101055 break;
1309 nd 101049 }
1310     }
1311 nd 101055 else {
1312     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1313     "\"%s\" to tag config in %s", tag, r->filename);
1314     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1315     break;
1316     }
1317 nd 101049 }
1318    
1319     return APR_SUCCESS;
1320     }
1321    
1322 nd 101057 /*
1323     * <!--#fsize virtual|file="..." [virtual|file="..."] ... -->
1324     */
1325 nd 101049 static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
1326     apr_bucket_brigade *bb)
1327     {
1328     request_rec *r = f->r;
1329    
1330 nd 101057 if (!ctx->argc) {
1331     ap_log_rerror(APLOG_MARK,
1332     (ctx->flags & SSI_FLAG_PRINTING)
1333     ? APLOG_ERR : APLOG_WARNING,
1334     0, r, "missing argument for fsize element in %s",
1335     r->filename);
1336     }
1337    
1338     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1339     return APR_SUCCESS;
1340     }
1341    
1342     if (!ctx->argc) {
1343     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1344     return APR_SUCCESS;
1345     }
1346    
1347     while (1) {
1348     char *tag = NULL;
1349     char *tag_val = NULL;
1350     apr_finfo_t finfo;
1351     char *parsed_string;
1352    
1353     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1354     if (!tag || !tag_val) {
1355     break;
1356     }
1357    
1358 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1359 nd 101057 SSI_EXPAND_DROP_NAME);
1360    
1361     if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
1362     char *buf;
1363     apr_size_t len;
1364    
1365     if (!(ctx->flags & SSI_FLAG_SIZE_IN_BYTES)) {
1366     buf = apr_strfsize(finfo.size, apr_palloc(ctx->pool, 5));
1367     len = 4; /* omit the \0 terminator */
1368 nd 101049 }
1369     else {
1370 nd 101057 apr_size_t l, x, pos;
1371     char *tmp;
1372 nd 101049
1373 nd 101057 tmp = apr_psprintf(ctx->dpool, "%" APR_OFF_T_FMT, finfo.size);
1374     len = l = strlen(tmp);
1375 nd 101049
1376 nd 101057 for (x = 0; x < l; ++x) {
1377     if (x && !((l - x) % 3)) {
1378     ++len;
1379 nd 101049 }
1380 nd 101057 }
1381 nd 101049
1382 nd 101057 if (len == l) {
1383     buf = apr_pstrmemdup(ctx->pool, tmp, len);
1384 nd 101049 }
1385     else {
1386 nd 101057 buf = apr_palloc(ctx->pool, len);
1387    
1388     for (pos = x = 0; x < l; ++x) {
1389     if (x && !((l - x) % 3)) {
1390     buf[pos++] = ',';
1391     }
1392     buf[pos++] = tmp[x];
1393     }
1394 nd 101049 }
1395     }
1396 nd 101057
1397     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buf, len,
1398     ctx->pool, f->c->bucket_alloc));
1399 nd 101049 }
1400 nd 101057 else {
1401     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1402     break;
1403     }
1404 nd 101049 }
1405    
1406     return APR_SUCCESS;
1407     }
1408    
1409 nd 101063 /*
1410     * <!--#flastmod virtual|file="..." [virtual|file="..."] ... -->
1411     */
1412 nd 101049 static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
1413     apr_bucket_brigade *bb)
1414     {
1415     request_rec *r = f->r;
1416    
1417 nd 101063 if (!ctx->argc) {
1418     ap_log_rerror(APLOG_MARK,
1419     (ctx->flags & SSI_FLAG_PRINTING)
1420     ? APLOG_ERR : APLOG_WARNING,
1421     0, r, "missing argument for flastmod element in %s",
1422     r->filename);
1423     }
1424 nd 101049
1425 nd 101063 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1426     return APR_SUCCESS;
1427     }
1428 nd 101049
1429 nd 101063 if (!ctx->argc) {
1430     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1431     return APR_SUCCESS;
1432     }
1433    
1434     while (1) {
1435     char *tag = NULL;
1436     char *tag_val = NULL;
1437     apr_finfo_t finfo;
1438     char *parsed_string;
1439    
1440     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1441     if (!tag || !tag_val) {
1442     break;
1443 nd 101049 }
1444 nd 101063
1445 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1446 nd 101063 SSI_EXPAND_DROP_NAME);
1447    
1448     if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
1449     char *t_val;
1450     apr_size_t t_len;
1451    
1452     t_val = ap_ht_time(ctx->pool, finfo.mtime, ctx->time_str, 0);
1453     t_len = strlen(t_val);
1454    
1455     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(t_val, t_len,
1456     ctx->pool, f->c->bucket_alloc));
1457     }
1458     else {
1459     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1460     break;
1461     }
1462 nd 101049 }
1463    
1464     return APR_SUCCESS;
1465     }
1466    
1467 nd 101064 /*
1468     * <!--#if expr="..." -->
1469     */
1470 nd 101046 static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
1471     apr_bucket_brigade *bb)
1472 fielding 83751 {
1473 nd 101064 char *tag = NULL;
1474     char *expr = NULL;
1475 nd 101046 request_rec *r = f->r;
1476 nd 101078 int expr_ret, was_error;
1477 fielding 83751
1478 nd 101064 if (ctx->argc != 1) {
1479     ap_log_rerror(APLOG_MARK,
1480     (ctx->flags & SSI_FLAG_PRINTING)
1481     ? APLOG_ERR : APLOG_WARNING,
1482     0, r, (ctx->argc)
1483     ? "too many arguments for if element in %s"
1484     : "missing expr argument for if element in %s",
1485     r->filename);
1486     }
1487    
1488 nd 101048 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1489 nd 101064 ++(ctx->if_nesting_level);
1490     return APR_SUCCESS;
1491 rbb 87067 }
1492 nd 101046
1493 nd 101064 if (ctx->argc != 1) {
1494     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1495     return APR_SUCCESS;
1496     }
1497    
1498     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1499    
1500     if (strcmp(tag, "expr")) {
1501     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1502     "to tag if in %s", tag, r->filename);
1503     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1504     return APR_SUCCESS;
1505     }
1506    
1507     if (!expr) {
1508     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr value for if "
1509     "element in %s", r->filename);
1510     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1511     return APR_SUCCESS;
1512     }
1513    
1514 nd 101078 DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr));
1515 rbb 87067
1516 niq 642559 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1517     ssi_parse_string, ctx->intern->access_func);
1518 nd 101064
1519     if (was_error) {
1520     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1521     return APR_SUCCESS;
1522 fielding 83751 }
1523 nd 101048
1524 nd 101064 if (expr_ret) {
1525     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1526     }
1527     else {
1528     ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1529     }
1530    
1531 nd 101078 DEBUG_DUMP_COND(ctx, " if");
1532    
1533 nd 101064 ctx->if_nesting_level = 0;
1534    
1535 nd 101046 return APR_SUCCESS;
1536 fielding 83751 }
1537    
1538 nd 101065 /*
1539     * <!--#elif expr="..." -->
1540     */
1541 nd 101046 static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
1542     apr_bucket_brigade *bb)
1543 fielding 83751 {
1544 nd 101065 char *tag = NULL;
1545     char *expr = NULL;
1546     request_rec *r = f->r;
1547 nd 101078 int expr_ret, was_error;
1548 fielding 83751
1549 nd 101065 if (ctx->argc != 1) {
1550     ap_log_rerror(APLOG_MARK,
1551     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1552     0, r, (ctx->argc)
1553     ? "too many arguments for if element in %s"
1554     : "missing expr argument for if element in %s",
1555     r->filename);
1556     }
1557    
1558     if (ctx->if_nesting_level) {
1559     return APR_SUCCESS;
1560     }
1561    
1562     if (ctx->argc != 1) {
1563     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1564     return APR_SUCCESS;
1565     }
1566    
1567     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1568    
1569     if (strcmp(tag, "expr")) {
1570     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1571     "to tag if in %s", tag, r->filename);
1572     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1573     return APR_SUCCESS;
1574     }
1575    
1576     if (!expr) {
1577     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr in elif "
1578     "statement: %s", r->filename);
1579     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1580     return APR_SUCCESS;
1581     }
1582    
1583 nd 101078 DEBUG_PRINTF((ctx, "**** elif expr=\"%s\"\n", expr));
1584     DEBUG_DUMP_COND(ctx, " elif");
1585 nd 101065
1586     if (ctx->flags & SSI_FLAG_COND_TRUE) {
1587     ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1588     return APR_SUCCESS;
1589 fielding 83751 }
1590 nd 101048
1591 niq 642559 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1592     ssi_parse_string, ctx->intern->access_func);
1593 nd 101065
1594     if (was_error) {
1595     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1596     return APR_SUCCESS;
1597     }
1598    
1599     if (expr_ret) {
1600     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1601     }
1602     else {
1603     ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1604     }
1605    
1606 nd 101078 DEBUG_DUMP_COND(ctx, " elif");
1607 nd 101065
1608 nd 101046 return APR_SUCCESS;
1609 fielding 83751 }
1610    
1611 nd 101066 /*
1612     * <!--#else -->
1613     */
1614 nd 101046 static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f,
1615     apr_bucket_brigade *bb)
1616 fielding 83751 {
1617 nd 101046 request_rec *r = f->r;
1618 fielding 83751
1619 nd 101066 if (ctx->argc) {
1620     ap_log_rerror(APLOG_MARK,
1621     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1622     0, r, "else directive does not take tags in %s",
1623     r->filename);
1624     }
1625    
1626     if (ctx->if_nesting_level) {
1627     return APR_SUCCESS;
1628     }
1629    
1630     if (ctx->argc) {
1631     if (ctx->flags & SSI_FLAG_PRINTING) {
1632     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1633 fielding 83751 }
1634 nd 101066
1635     return APR_SUCCESS;
1636     }
1637    
1638 nd 101078 DEBUG_DUMP_COND(ctx, " else");
1639 jim 332306
1640 nd 101066 if (ctx->flags & SSI_FLAG_COND_TRUE) {
1641     ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1642 fielding 83751 }
1643 nd 101066 else {
1644     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1645     }
1646 nd 101048
1647 nd 101046 return APR_SUCCESS;
1648 fielding 83751 }
1649    
1650 nd 101066 /*
1651     * <!--#endif -->
1652     */
1653 nd 101046 static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f,
1654     apr_bucket_brigade *bb)
1655 fielding 83751 {
1656 nd 101046 request_rec *r = f->r;
1657 fielding 83751
1658 nd 101066 if (ctx->argc) {
1659     ap_log_rerror(APLOG_MARK,
1660     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1661     0, r, "endif directive does not take tags in %s",
1662     r->filename);
1663 fielding 83751 }
1664 nd 101066
1665     if (ctx->if_nesting_level) {
1666     --(ctx->if_nesting_level);
1667 nd 101046 return APR_SUCCESS;
1668 fielding 83751 }
1669 nd 101066
1670     if (ctx->argc) {
1671     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1672     return APR_SUCCESS;
1673     }
1674    
1675 nd 101078 DEBUG_DUMP_COND(ctx, "endif");
1676    
1677 nd 101066 ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1678    
1679     return APR_SUCCESS;
1680 fielding 83751 }
1681    
1682 nd 101067 /*
1683     * <!--#set var="..." value="..." ... -->
1684     */
1685 nd 101046 static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
1686     apr_bucket_brigade *bb)
1687 fielding 83751 {
1688 nd 101067 char *var = NULL;
1689 nd 101046 request_rec *r = f->r;
1690 ianh 93556 request_rec *sub = r->main;
1691     apr_pool_t *p = r->pool;
1692 fielding 83751
1693 nd 101067 if (ctx->argc < 2) {
1694     ap_log_rerror(APLOG_MARK,
1695     (ctx->flags & SSI_FLAG_PRINTING)
1696     ? APLOG_ERR : APLOG_WARNING,
1697     0, r, "missing argument for set element in %s",
1698     r->filename);
1699     }
1700    
1701     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1702     return APR_SUCCESS;
1703     }
1704    
1705     if (ctx->argc < 2) {
1706     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1707     return APR_SUCCESS;
1708     }
1709    
1710 jim 332306 /* we need to use the 'main' request pool to set notes as that is
1711 ianh 93556 * a notes lifetime
1712     */
1713     while (sub) {
1714     p = sub->pool;
1715     sub = sub->main;
1716     }
1717    
1718 nd 101067 while (1) {
1719     char *tag = NULL;
1720     char *tag_val = NULL;
1721    
1722     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1723    
1724     if (!tag || !tag_val) {
1725     break;
1726     }
1727    
1728     if (!strcmp(tag, "var")) {
1729 nd 101079 var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1730 nd 101067 SSI_EXPAND_DROP_NAME);
1731     }
1732     else if (!strcmp(tag, "value")) {
1733     char *parsed_string;
1734    
1735     if (!var) {
1736     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "variable must "
1737     "precede value in set directive in %s",
1738     r->filename);
1739 nd 101046 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1740 nd 101067 break;
1741 fielding 83751 }
1742 nd 101067
1743 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1744 nd 101067 SSI_EXPAND_DROP_NAME);
1745     apr_table_setn(r->subprocess_env, apr_pstrdup(p, var),
1746     apr_pstrdup(p, parsed_string));
1747 fielding 83751 }
1748 nd 101067 else {
1749     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid tag for set "
1750     "directive in %s", r->filename);
1751     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1752     break;
1753     }
1754 fielding 83751 }
1755 nd 101048
1756 nd 101046 return APR_SUCCESS;
1757 fielding 83751 }
1758    
1759 nd 101068 /*
1760     * <!--#printenv -->
1761     */
1762 nd 101046 static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f,
1763     apr_bucket_brigade *bb)
1764 fielding 83751 {
1765 nd 101046 request_rec *r = f->r;
1766 nd 101068 const apr_array_header_t *arr;
1767     const apr_table_entry_t *elts;
1768     int i;
1769 fielding 83751
1770 nd 101068 if (ctx->argc) {
1771     ap_log_rerror(APLOG_MARK,
1772     (ctx->flags & SSI_FLAG_PRINTING)
1773     ? APLOG_ERR : APLOG_WARNING,
1774     0, r, "printenv directive does not take tags in %s",
1775     r->filename);
1776     }
1777 rbb 87067
1778 nd 101068 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1779     return APR_SUCCESS;
1780     }
1781    
1782     if (ctx->argc) {
1783     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1784     return APR_SUCCESS;
1785     }
1786    
1787     arr = apr_table_elts(r->subprocess_env);
1788     elts = (apr_table_entry_t *)arr->elts;
1789    
1790     for (i = 0; i < arr->nelts; ++i) {
1791     const char *key_text, *val_text;
1792     char *key_val, *next;
1793     apr_size_t k_len, v_len, kv_length;
1794    
1795     /* get key */
1796     key_text = ap_escape_html(ctx->dpool, elts[i].key);
1797     k_len = strlen(key_text);
1798    
1799     /* get value */
1800     val_text = elts[i].val;
1801     if (val_text == LAZY_VALUE) {
1802 jorton 757376 val_text = add_include_vars_lazy(r, elts[i].key, ctx->time_str);
1803 fielding 83751 }
1804 nd 101068 val_text = ap_escape_html(ctx->dpool, elts[i].val);
1805     v_len = strlen(val_text);
1806    
1807     /* assemble result */
1808     kv_length = k_len + v_len + sizeof("=\n");
1809     key_val = apr_palloc(ctx->pool, kv_length);
1810     next = key_val;
1811    
1812     memcpy(next, key_text, k_len);
1813     next += k_len;
1814     *next++ = '=';
1815     memcpy(next, val_text, v_len);
1816     next += v_len;
1817     *next++ = '\n';
1818     *next = 0;
1819    
1820     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(key_val, kv_length-1,
1821     ctx->pool, f->c->bucket_alloc));
1822 fielding 83751 }
1823 nd 101048
1824 nd 101068 ctx->flush_now = 1;
1825 nd 101046 return APR_SUCCESS;
1826 fielding 83751 }
1827    
1828    
1829 nd 101036 /*
1830 nd 101049 * +-------------------------------------------------------+
1831     * | |
1832     * | Main Includes-Filter Engine
1833     * | |
1834     * +-------------------------------------------------------+
1835     */
1836    
1837     /* This is an implementation of the BNDM search algorithm.
1838     *
1839 jim 332306 * Fast and Flexible String Matching by Combining Bit-parallelism and
1840     * Suffix Automata (2001)
1841 nd 101049 * Gonzalo Navarro, Mathieu Raffinot
1842     *
1843     * http://www-igm.univ-mlv.fr/~raffinot/ftp/jea2001.ps.gz
1844     *
1845     * Initial code submitted by Sascha Schumann.
1846     */
1847 jim 332306
1848 nd 101049 /* Precompile the bndm_t data structure. */
1849 nd 101052 static bndm_t *bndm_compile(apr_pool_t *pool, const char *n, apr_size_t nl)
1850 nd 101049 {
1851     unsigned int x;
1852     const char *ne = n + nl;
1853 nd 101052 bndm_t *t = apr_palloc(pool, sizeof(*t));
1854 nd 101049
1855     memset(t->T, 0, sizeof(unsigned int) * 256);
1856 nd 101052 t->pattern_len = nl;
1857    
1858     for (x = 1; n < ne; x <<= 1) {
1859 nd 101049 t->T[(unsigned char) *n++] |= x;
1860 nd 101052 }
1861 nd 101049
1862     t->x = x - 1;
1863 nd 101052
1864     return t;
1865 nd 101049 }
1866    
1867     /* Implements the BNDM search algorithm (as described above).
1868     *
1869     * h - the string to look in
1870     * hl - length of the string to look for
1871 jim 332306 * t - precompiled bndm structure against the pattern
1872 nd 101049 *
1873     * Returns the count of character that is the first match or hl if no
1874     * match is found.
1875     */
1876 nd 101052 static apr_size_t bndm(bndm_t *t, const char *h, apr_size_t hl)
1877 nd 101049 {
1878     const char *skip;
1879     const char *he, *p, *pi;
1880     unsigned int *T, x, d;
1881 nd 101052 apr_size_t nl;
1882 nd 101049
1883     he = h + hl;
1884    
1885     T = t->T;
1886     x = t->x;
1887 nd 101052 nl = t->pattern_len;
1888 nd 101049
1889     pi = h - 1; /* pi: p initial */