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