source: bin/varnishd/cache_esi_parse.c @ 0e6355

Revision 0e6355, 28.8 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

First sweep to remove needless #includes.

This is based on output from some scripts and basic sanity testing.

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2011 Varnish Software AS
3 * All rights reserved.
4 *
5 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * VEP Varnish Esi Parsing
29 */
30
31#include "config.h"
32
33#include <stdio.h>
34#include <stdlib.h>
35
36#include "cache.h"
37#include "cache_esi.h"
38#include "vend.h"
39#include "vct.h"
40#include "vgz.h"
41
42//#define Debug(fmt, ...) printf(fmt, __VA_ARGS__)
43#define Debug(fmt, ...) /**/
44
45struct vep_state;
46
47enum dowhat {DO_ATTR, DO_TAG};
48typedef void dostuff_f(struct vep_state *, enum dowhat);
49
50struct vep_match {
51        const char      *match;
52        const char      * const *state;
53};
54
55enum vep_mark { VERBATIM = 0, SKIP };
56
57struct vep_state {
58        unsigned                magic;
59#define VEP_MAGIC               0x55cb9b82
60        struct vsb              *vsb;
61
62        const struct sess       *sp;
63        int                     dogzip;
64        vep_callback_t          *cb;
65
66        /* Internal Counter for default call-back function */
67        ssize_t                 cb_x;
68
69        /* parser state */
70        const char              *state;
71        unsigned                startup;
72        unsigned                esi_found;
73
74        unsigned                endtag;
75        unsigned                emptytag;
76        unsigned                canattr;
77
78        unsigned                remove;
79
80        ssize_t                 o_wait;
81        ssize_t                 o_pending;
82        ssize_t                 o_total;
83        uint32_t                crc;
84        ssize_t                 o_crc;
85        uint32_t                crcp;
86        ssize_t                 o_last;
87
88const char              *hack_p;
89        const char              *ver_p;
90
91        const char              *until;
92        const char              *until_p;
93        const char              *until_s;
94
95        int                     in_esi_tag;
96
97        const char              *esicmt;
98        const char              *esicmt_p;
99
100        struct vep_match        *attr;
101        struct vsb              *attr_vsb;
102        int                     attr_delim;
103
104        struct vep_match        *match;
105        struct vep_match        *match_hit;
106
107        char                    tag[10];
108        int                     tag_i;
109
110        dostuff_f               *dostuff;
111
112        struct vsb              *include_src;
113
114        unsigned                nm_skip;
115        unsigned                nm_verbatim;
116        unsigned                nm_pending;
117        enum vep_mark           last_mark;
118};
119
120/*---------------------------------------------------------------------*/
121
122static const char * const VEP_START =           "[Start]";
123static const char * const VEP_TESTXML =         "[TestXml]";
124static const char * const VEP_NOTXML =          "[NotXml]";
125
126static const char * const VEP_NEXTTAG =         "[NxtTag]";
127static const char * const VEP_NOTMYTAG =        "[NotMyTag]";
128
129static const char * const VEP_STARTTAG =        "[StartTag]";
130static const char * const VEP_COMMENT =         "[Comment]";
131static const char * const VEP_CDATA =           "[CDATA]";
132static const char * const VEP_ESITAG =          "[ESITag]";
133
134static const char * const VEP_ESIREMOVE =       "[ESI:Remove]";
135static const char * const VEP_ESIINCLUDE =      "[ESI:Include]";
136static const char * const VEP_ESICOMMENT =      "[ESI:Comment]";
137static const char * const VEP_ESIBOGON =        "[ESI:Bogon]";
138
139static const char * const VEP_INTAG =           "[InTag]";
140static const char * const VEP_TAGERROR =        "[TagError]";
141
142static const char * const VEP_ATTR =            "[Attribute]";
143static const char * const VEP_SKIPATTR =        "[SkipAttribute]";
144static const char * const VEP_ATTRDELIM =       "[AttrDelim]";
145static const char * const VEP_ATTRGETVAL =      "[AttrGetValue]";
146static const char * const VEP_ATTRVAL =         "[AttrValue]";
147
148static const char * const VEP_UNTIL =           "[Until]";
149static const char * const VEP_MATCHBUF =        "[MatchBuf]";
150static const char * const VEP_MATCH =           "[Match]";
151
152/*---------------------------------------------------------------------*/
153
154static struct vep_match vep_match_starttag[] = {
155        { "!--",        &VEP_COMMENT },
156        { "esi:",       &VEP_ESITAG },
157        { "![CDATA[",   &VEP_CDATA },
158        { NULL,         &VEP_NOTMYTAG }
159};
160
161/*---------------------------------------------------------------------*/
162
163static struct vep_match vep_match_esi[] = {
164        { "include",    &VEP_ESIINCLUDE },
165        { "remove",     &VEP_ESIREMOVE },
166        { "comment",    &VEP_ESICOMMENT },
167        { NULL,         &VEP_ESIBOGON }
168};
169
170/*---------------------------------------------------------------------*/
171
172static struct vep_match vep_match_attr_include[] = {
173        { "src=",       &VEP_ATTRGETVAL },
174        { NULL,         &VEP_SKIPATTR }
175};
176
177/*--------------------------------------------------------------------
178 * Report a parsing error
179 */
180
181static void
182vep_error(const struct vep_state *vep, const char *p)
183{
184        intmax_t l;
185
186        VSC_C_main->esi_errors++;
187        l = (intmax_t)(vep->ver_p - vep->hack_p);
188        WSP(vep->sp, SLT_ESI_xmlerror, "ERR at %jd %s", l, p);
189
190}
191
192/*--------------------------------------------------------------------
193 * Report a parsing warning
194 */
195
196static void
197vep_warn(const struct vep_state *vep, const char *p)
198{
199        intmax_t l;
200
201        VSC_C_main->esi_warnings++;
202        l = (intmax_t)(vep->ver_p - vep->hack_p);
203        printf("WARNING at %jd %s\n", l, p);
204        WSP(vep->sp, SLT_ESI_xmlerror, "WARN at %jd %s", l, p);
205
206}
207
208/*---------------------------------------------------------------------
209 * return match or NULL if more input needed.
210 */
211
212static struct vep_match *
213vep_match(struct vep_state *vep, const char *b, const char *e)
214{
215        struct vep_match *vm;
216        const char *q, *r;
217        ssize_t l;
218
219        for (vm = vep->match; vm->match; vm++) {
220                r = b;
221                for (q = vm->match; *q && r < e; q++, r++)
222                        if (*q != *r)
223                                break;
224                if (*q != '\0' && r == e) {
225                        if (b != vep->tag) {
226                                l = e - b;
227                                assert(l < sizeof vep->tag);
228                                memmove(vep->tag, b, l);
229                                vep->tag_i = l;
230                        }
231                        return (NULL);
232                }
233                if (*q == '\0')
234                        return (vm);
235        }
236        return (vm);
237}
238
239/*---------------------------------------------------------------------
240 *
241 */
242
243static void
244vep_emit_len(const struct vep_state *vep, ssize_t l, int m8, int m16, int m64)
245{
246        uint8_t buf[9];
247
248        assert(l > 0);
249        if (l < 256) {
250                buf[0] = (uint8_t)m8;
251                buf[1] = (uint8_t)l;
252                assert((ssize_t)buf[1] == l);
253                VSB_bcat(vep->vsb, buf, 2);
254        } else if (l < 65536) {
255                buf[0] = (uint8_t)m16;
256                vbe16enc(buf + 1, (uint16_t)l);
257                assert((ssize_t)vbe16dec(buf + 1) == l);
258                VSB_bcat(vep->vsb, buf, 3);
259        } else {
260                buf[0] = (uint8_t)m64;
261                vbe64enc(buf + 1, l);
262                assert((ssize_t)vbe64dec(buf + 1) == l);
263                VSB_bcat(vep->vsb, buf, 9);
264        }
265}
266
267static void
268vep_emit_skip(const struct vep_state *vep, ssize_t l)
269{
270
271        if (params->esi_syntax & 0x20) {
272                Debug("---> SKIP(%jd)\n", (intmax_t)l);
273        }
274        vep_emit_len(vep, l, VEC_S1, VEC_S2, VEC_S8);
275}
276
277static void
278vep_emit_verbatim(const struct vep_state *vep, ssize_t l, ssize_t l_crc)
279{
280        uint8_t buf[4];
281
282        if (params->esi_syntax & 0x20) {
283                Debug("---> VERBATIM(%jd)\n", (intmax_t)l);
284        }
285        vep_emit_len(vep, l, VEC_V1, VEC_V2, VEC_V8);
286        if (vep->dogzip) {
287                vep_emit_len(vep, l_crc, VEC_C1, VEC_C2, VEC_C8);
288                vbe32enc(buf, vep->crc);
289                VSB_bcat(vep->vsb, buf, sizeof buf);
290        }
291}
292
293static void
294vep_emit_common(struct vep_state *vep, ssize_t l, enum vep_mark mark)
295{
296
297        assert(l > 0);
298        assert(mark == SKIP || mark == VERBATIM);
299        if (mark == SKIP)
300                vep_emit_skip(vep, l);
301        else
302                vep_emit_verbatim(vep, l, vep->o_crc);
303
304        vep->crc = crc32(0L, Z_NULL, 0);
305        vep->o_crc = 0;
306        vep->o_total += l;
307}
308
309/*---------------------------------------------------------------------
310 *
311 */
312
313static void
314vep_mark_common(struct vep_state *vep, const char *p, enum vep_mark mark)
315{
316        ssize_t l, lcb;
317
318        assert(mark == SKIP || mark == VERBATIM);
319
320        /* The NO-OP case, no data, no pending data & no change of mode */
321        if (vep->last_mark == mark && p == vep->ver_p && vep->o_pending == 0)
322                return;
323
324        /*
325         * If we changed mode, emit whatever the opposite mode
326         * assembled before the pending bytes.
327         */
328
329        if (vep->last_mark != mark && (vep->o_wait > 0 || vep->startup)) {
330                lcb = vep->cb(vep->sp, 0,
331                    mark == VERBATIM ? VGZ_RESET : VGZ_ALIGN);
332                if (lcb - vep->o_last > 0)
333                        vep_emit_common(vep, lcb - vep->o_last, vep->last_mark);
334                vep->o_last = lcb;
335                vep->o_wait = 0;
336        }
337
338        /* Transfer pending bytes CRC into active mode CRC */
339        if (vep->o_pending) {
340                (void)vep->cb(vep->sp, vep->o_pending, VGZ_NORMAL);
341                if (vep->o_crc == 0) {
342                        vep->crc = vep->crcp;
343                        vep->o_crc = vep->o_pending;
344                } else {
345                        vep->crc = crc32_combine(vep->crc,
346                            vep->crcp, vep->o_pending);
347                        vep->o_crc += vep->o_pending;
348                }
349                vep->crcp = crc32(0L, Z_NULL, 0);
350                vep->o_wait += vep->o_pending;
351                vep->o_pending = 0;
352        }
353
354        /* * Process this bit of input */
355        AN(vep->ver_p);
356        l = p - vep->ver_p;
357        assert(l >= 0);
358        vep->crc = crc32(vep->crc, (const void*)vep->ver_p, l);
359        vep->o_crc += l;
360        vep->ver_p = p;
361
362        vep->o_wait += l;
363        vep->last_mark = mark;
364        (void)vep->cb(vep->sp, l, VGZ_NORMAL);
365}
366
367static void
368vep_mark_verbatim(struct vep_state *vep, const char *p)
369{
370
371        vep_mark_common(vep, p, VERBATIM);
372        vep->nm_verbatim++;
373}
374
375static void
376vep_mark_skip(struct vep_state *vep, const char *p)
377{
378
379        vep_mark_common(vep, p, SKIP);
380        vep->nm_skip++;
381}
382
383static void
384vep_mark_pending(struct vep_state *vep, const char *p)
385{
386        ssize_t l;
387
388        AN(vep->ver_p);
389        l = p - vep->ver_p;
390        assert(l > 0);
391        assert(l >= 0);
392        vep->crcp = crc32(vep->crcp, (const void *)vep->ver_p, l);
393        vep->ver_p = p;
394
395        vep->o_pending += l;
396        vep->nm_pending++;
397}
398
399/*---------------------------------------------------------------------
400 */
401
402static void __match_proto__()
403vep_do_comment(struct vep_state *vep, enum dowhat what)
404{
405        Debug("DO_COMMENT(%d)\n", what);
406        assert(what == DO_TAG);
407        if (!vep->emptytag)
408                vep_error(vep, "ESI 1.0 <esi:comment> needs final '/'");
409}
410
411/*---------------------------------------------------------------------
412 */
413
414static void __match_proto__()
415vep_do_remove(struct vep_state *vep, enum dowhat what)
416{
417        Debug("DO_REMOVE(%d, end %d empty %d remove %d)\n",
418            what, vep->endtag, vep->emptytag, vep->remove);
419        assert(what == DO_TAG);
420        if (vep->emptytag) {
421                vep_error(vep,
422                    "ESI 1.0 <esi:remove/> not legal");
423        } else {
424                if (vep->remove && !vep->endtag)
425                        vep_error(vep,
426                            "ESI 1.0 <esi:remove> already open");
427                else if (!vep->remove && vep->endtag)
428                        vep_error(vep,
429                            "ESI 1.0 <esi:remove> not open");
430                else
431                        vep->remove = !vep->endtag;
432        }
433}
434
435/*---------------------------------------------------------------------
436 */
437
438static void __match_proto__()
439vep_do_include(struct vep_state *vep, enum dowhat what)
440{
441        char *p, *q, *h;
442        ssize_t l;
443        txt url;
444
445        Debug("DO_INCLUDE(%d)\n", what);
446        if (what == DO_ATTR) {
447                Debug("ATTR (%s) (%s)\n", vep->match_hit->match,
448                        VSB_data(vep->attr_vsb));
449                if (vep->include_src != NULL) {
450                        vep_error(vep,
451                            "ESI 1.0 <esi:include> "
452                            "has multiple src= attributes");
453                        vep->state = VEP_TAGERROR;
454                        VSB_delete(vep->attr_vsb);
455                        VSB_delete(vep->include_src);
456                        vep->attr_vsb = NULL;
457                        vep->include_src = NULL;
458                        return;
459                }
460                XXXAZ(vep->include_src);        /* multiple src= */
461                vep->include_src = vep->attr_vsb;
462                return;
463        }
464        assert(what == DO_TAG);
465        if (!vep->emptytag)
466                vep_warn(vep,
467                    "ESI 1.0 <esi:include> lacks final '/'");
468        if (vep->include_src == NULL) {
469                vep_error(vep,
470                    "ESI 1.0 <esi:include> lacks src attr");
471                return;
472        }
473
474        /*
475         * Strictly speaking, we ought to spit out any piled up skip before
476         * emitting the VEC for the include, but objectively that makes no
477         * difference and robs us of a chance to collapse another skip into
478         * this on so we don't do that.
479         * However, we cannot tolerate any verbatim stuff piling up.
480         * The mark_skip() before calling dostuff should have taken
481         * care of that.  Make sure.
482         */
483        assert(vep->o_wait == 0 || vep->last_mark == SKIP);
484        /* XXX: what if it contains NUL bytes ?? */
485        p = VSB_data(vep->include_src);
486        l = VSB_len(vep->include_src);
487        h = 0;
488
489        VSB_printf(vep->vsb, "%c", VEC_INCL);
490        if (l > 7 && !memcmp(p, "http://", 7)) {
491                h = p + 7;
492                p = strchr(h, '/');
493                AN(p);
494                Debug("HOST <%.*s> PATH <%s>\n", (int)(p-h),h, p);
495                VSB_printf(vep->vsb, "Host: %.*s%c",
496                    (int)(p-h), h, 0);
497        } else if (*p == '/') {
498                VSB_printf(vep->vsb, "%c", 0);
499        } else {
500                VSB_printf(vep->vsb, "%c", 0);
501                url = vep->sp->wrk->bereq->hd[HTTP_HDR_URL];
502                /* Look for the last / before a '?' */
503                h = NULL;
504                for (q = url.b; q < url.e && *q != '?'; q++)
505                        if (*q == '/')
506                                h = q;
507                if (h == NULL)
508                        h = q + 1;
509
510                Debug("INCL:: [%.*s]/[%s]\n",
511                    (int)(h - url.b), url.b, p);
512                VSB_printf(vep->vsb, "%.*s/", (int)(h - url.b), url.b);
513        }
514        l -= (p - VSB_data(vep->include_src));
515        for (q = p; *q != '\0'; ) {
516                if (*q == '&') {
517#define R(w,f,r)                                                        \
518                        if (q + w <= p + l && !memcmp(q, f, w)) { \
519                                VSB_printf(vep->vsb, "%c", r);  \
520                                q += w;                         \
521                                continue;                       \
522                        }
523                        R(6, "&apos;", '\'');
524                        R(6, "&quot;", '"');
525                        R(4, "&lt;", '<');
526                        R(4, "&gt;", '>');
527                        R(5, "&amp;", '&');
528                }
529                VSB_printf(vep->vsb, "%c", *q++);
530        }
531#undef R
532        VSB_printf(vep->vsb, "%c", 0);
533
534        VSB_delete(vep->include_src);
535        vep->include_src = NULL;
536}
537
538/*---------------------------------------------------------------------
539 * Lex/Parse object for ESI instructions
540 *
541 * This function is called with the input object piecemal so do not
542 * assume that we have more than one char available at at time, but
543 * optimize for getting huge chunks.
544 *
545 * NB: At the bottom of this source-file, there is a dot-diagram matching
546 * NB: the state-machine.  Please maintain it along with the code.
547 */
548
549void
550VEP_parse(const struct sess *sp, const char *p, size_t l)
551{
552        struct vep_state *vep;
553        const char *e;
554        struct vep_match *vm;
555        int i;
556
557        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
558        vep = sp->wrk->vep;
559        CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
560        assert(l > 0);
561
562        /* XXX: Really need to fix this */
563        if (vep->hack_p == NULL)
564                vep->hack_p = p;
565
566        vep->ver_p = p;
567
568        e = p + l;
569
570        while (p < e) {
571                AN(vep->state);
572                i = e - p;
573                if (i > 10)
574                        i = 10;
575                Debug("EP %s %d (%.*s) [%.*s]\n",
576                    vep->state,
577                    vep->remove,
578                    vep->tag_i, vep->tag,
579                    i, p);
580                assert(p >= vep->ver_p);
581
582                /******************************************************
583                 * SECTION A
584                 */
585
586                if (vep->state == VEP_START) {
587                        if (params->esi_syntax & 0x1)
588                                vep->state = VEP_NEXTTAG;
589                        else
590                                vep->state = VEP_TESTXML;
591                } else if (vep->state == VEP_TESTXML) {
592                        /*
593                         * If the first non-whitespace char is different
594                         * from '<' we assume this is not XML.
595                         */
596                        while (p < e && vct_islws(*p))
597                                p++;
598                        vep_mark_verbatim(vep, p);
599                        if (p < e && *p == '<') {
600                                p++;
601                                vep->state = VEP_STARTTAG;
602                        } else if (p < e) {
603                                WSP(vep->sp, SLT_ESI_xmlerror,
604                                    "No ESI processing, first char not '<'");
605                                vep->state = VEP_NOTXML;
606                        }
607                } else if (vep->state == VEP_NOTXML) {
608                        /*
609                         * This is not recognized as XML, just skip thru
610                         * vfp_esi_end() will handle the rest
611                         */
612                        p = e;
613                        vep_mark_verbatim(vep, p);
614
615                /******************************************************
616                 * SECTION B
617                 */
618
619                } else if (vep->state == VEP_NOTMYTAG) {
620                        if (params->esi_syntax & 0x2) {
621                                p++;
622                                vep->state = VEP_NEXTTAG;
623                        } else {
624                                vep->tag_i = 0;
625                                while (p < e) {
626                                        if (*p++ == '>') {
627                                                vep->state = VEP_NEXTTAG;
628                                                break;
629                                        }
630                                }
631                        }
632                        if (p == e && !vep->remove)
633                                vep_mark_verbatim(vep, p);
634                } else if (vep->state == VEP_NEXTTAG) {
635                        /*
636                         * Hunt for start of next tag and keep an eye
637                         * out for end of EsiCmt if armed.
638                         */
639                        vep->emptytag = 0;
640                        vep->endtag = 0;
641                        vep->attr = NULL;
642                        vep->dostuff = NULL;
643                        while (p < e && *p != '<') {
644                                if (vep->esicmt_p == NULL) {
645                                        p++;
646                                        continue;
647                                }
648                                if (*p != *vep->esicmt_p) {
649                                        p++;
650                                        vep->esicmt_p = vep->esicmt;
651                                        continue;
652                                }
653                                if (!vep->remove &&
654                                    vep->esicmt_p == vep->esicmt)
655                                        vep_mark_verbatim(vep, p);
656                                p++;
657                                if (*++vep->esicmt_p == '\0') {
658                                        vep->esi_found = 1;
659                                        vep->esicmt = NULL;
660                                        vep->esicmt_p = NULL;
661                                        /*
662                                         * The end of the esicmt
663                                         * should not be emitted.
664                                         * But the stuff before should
665                                         */
666                                        vep_mark_skip(vep, p);
667                                }
668                        }
669                        if (p < e) {
670                                if (!vep->remove)
671                                        vep_mark_verbatim(vep, p);
672                                assert(*p == '<');
673                                p++;
674                                vep->state = VEP_STARTTAG;
675                        } else if (vep->esicmt_p == vep->esicmt && !vep->remove)
676                                vep_mark_verbatim(vep, p);
677
678                /******************************************************
679                 * SECTION C
680                 */
681
682                } else if (vep->state == VEP_STARTTAG) {
683                        /*
684                         * Start of tag, set up match table
685                         */
686                        if (p < e) {
687                                if (*p == '/') {
688                                        vep->endtag = 1;
689                                        p++;
690                                }
691                                vep->match = vep_match_starttag;
692                                vep->state = VEP_MATCH;
693                        }
694                } else if (vep->state == VEP_COMMENT) {
695                        /*
696                         * We are in a comment, find out if it is an
697                         * ESI comment or a regular comment
698                         */
699                        if (vep->esicmt == NULL)
700                                vep->esicmt_p = vep->esicmt = "esi";
701                        while (p < e) {
702                                if (*p != *vep->esicmt_p) {
703                                        vep->esicmt_p = vep->esicmt = NULL;
704                                        vep->until_p = vep->until = "-->";
705                                        vep->until_s = VEP_NEXTTAG;
706                                        vep->state = VEP_UNTIL;
707                                        vep_mark_verbatim(vep, p);
708                                        break;
709                                }
710                                p++;
711                                if (*++vep->esicmt_p != '\0')
712                                        continue;
713                                if (vep->remove)
714                                        vep_error(vep,
715                                            "ESI 1.0 Nested <!--esi"
716                                            " element in <esi:remove>");
717                                vep->esicmt_p = vep->esicmt = "-->";
718                                vep->state = VEP_NEXTTAG;
719                                vep_mark_skip(vep, p);
720                                break;
721                        }
722                } else if (vep->state == VEP_CDATA) {
723                        /*
724                         * Easy: just look for the end of CDATA
725                         */
726                        vep->until_p = vep->until = "]]>";
727                        vep->until_s = VEP_NEXTTAG;
728                        vep->state = VEP_UNTIL;
729                } else if (vep->state == VEP_ESITAG) {
730                        vep->in_esi_tag = 1;
731                        vep->esi_found = 1;
732                        vep_mark_skip(vep, p);
733                        vep->match = vep_match_esi;
734                        vep->state = VEP_MATCH;
735                } else if (vep->state == VEP_ESIINCLUDE) {
736                        if (vep->remove) {
737                                vep_error(vep,
738                                    "ESI 1.0 <esi:include> element"
739                                    " nested in <esi:remove>");
740                                vep->state = VEP_TAGERROR;
741                        } else if (vep->endtag) {
742                                vep_error(vep,
743                                    "ESI 1.0 </esi:include> illegal end-tag");
744                                vep->state = VEP_TAGERROR;
745                        } else {
746                                vep->dostuff = vep_do_include;
747                                vep->state = VEP_INTAG;
748                                vep->attr = vep_match_attr_include;
749                        }
750                } else if (vep->state == VEP_ESIREMOVE) {
751                        vep->dostuff = vep_do_remove;
752                        vep->state = VEP_INTAG;
753                } else if (vep->state == VEP_ESICOMMENT) {
754                        if (vep->remove) {
755                                vep_error(vep,
756                                    "ESI 1.0 <esi:comment> element"
757                                    " nested in <esi:remove>");
758                                vep->state = VEP_TAGERROR;
759                        } else if (vep->endtag) {
760                                vep_error(vep,
761                                    "ESI 1.0 </esi:comment> illegal end-tag");
762                                vep->state = VEP_TAGERROR;
763                        } else {
764                                vep->dostuff = vep_do_comment;
765                                vep->state = VEP_INTAG;
766                        }
767                } else if (vep->state == VEP_ESIBOGON) {
768                        vep_error(vep,
769                            "ESI 1.0 <esi:bogus> element");
770                        vep->state = VEP_TAGERROR;
771
772                /******************************************************
773                 * SECTION D
774                 */
775
776                } else if (vep->state == VEP_INTAG) {
777                        vep->tag_i = 0;
778                        while (p < e && vct_islws(*p) && !vep->emptytag) {
779                                p++;
780                                vep->canattr = 1;
781                        }
782                        if (p < e && *p == '/' && !vep->emptytag) {
783                                p++;
784                                vep->emptytag = 1;
785                                vep->canattr = 0;
786                        }
787                        if (p < e && *p == '>') {
788                                p++;
789                                AN(vep->dostuff);
790                                vep_mark_skip(vep, p);
791                                vep->dostuff(vep, DO_TAG);
792                                vep->in_esi_tag = 0;
793                                vep->state = VEP_NEXTTAG;
794                        } else if (p < e && vep->emptytag) {
795                                vep_error(vep,
796                                    "XML 1.0 '>' does not follow '/' in tag");
797                                vep->state = VEP_TAGERROR;
798                        } else if (p < e && vep->canattr &&
799                            vct_isxmlnamestart(*p)) {
800                                vep->state = VEP_ATTR;
801                        } else if (p < e) {
802                                vep_error(vep,
803                                    "XML 1.0 Illegal attribute tart char");
804                                vep->state = VEP_TAGERROR;
805                        }
806                } else if (vep->state == VEP_TAGERROR) {
807                        while (p < e && *p != '>')
808                                p++;
809                        if (p < e) {
810                                p++;
811                                vep_mark_skip(vep, p);
812                                vep->in_esi_tag = 0;
813                                vep->state = VEP_NEXTTAG;
814                        }
815
816                /******************************************************
817                 * SECTION E
818                 */
819
820                } else if (vep->state == VEP_ATTR) {
821                        AZ(vep->attr_delim);
822                        if (vep->attr == NULL) {
823                                p++;
824                                AZ(vep->attr_vsb);
825                                vep->state = VEP_SKIPATTR;
826                        } else {
827                                vep->match = vep->attr;
828                                vep->state = VEP_MATCH;
829                        }
830                } else if (vep->state == VEP_SKIPATTR) {
831                        while (p < e && vct_isxmlname(*p))
832                                p++;
833                        if (p < e && *p == '=') {
834                                p++;
835                                vep->state = VEP_ATTRDELIM;
836                        } else if (p < e && *p == '>') {
837                                vep->state = VEP_INTAG;
838                        } else if (p < e && *p == '/') {
839                                vep->state = VEP_INTAG;
840                        } else if (p < e && vct_issp(*p)) {
841                                vep->state = VEP_INTAG;
842                        } else if (p < e) {
843                                vep_error(vep,
844                                    "XML 1.0 Illegal attr char");
845                                vep->state = VEP_TAGERROR;
846                        }
847                } else if (vep->state == VEP_ATTRGETVAL) {
848                        vep->attr_vsb = VSB_new_auto();
849                        vep->state = VEP_ATTRDELIM;
850                } else if (vep->state == VEP_ATTRDELIM) {
851                        AZ(vep->attr_delim);
852                        if (*p == '"' || *p == '\'') {
853                                vep->attr_delim = *p++;
854                                vep->state = VEP_ATTRVAL;
855                        } else if (!vct_issp(*p)) {
856                                vep->attr_delim = ' ';
857                                vep->state = VEP_ATTRVAL;
858                        } else {
859                                vep_error(vep,
860                                    "XML 1.0 Illegal attribute delimiter");
861                                vep->state = VEP_TAGERROR;
862                        }
863
864                } else if (vep->state == VEP_ATTRVAL) {
865                        while (p < e && *p != '>' && *p != vep->attr_delim &&
866                           (vep->attr_delim != ' ' || !vct_issp(*p))) {
867                                if (vep->attr_vsb != NULL)
868                                        VSB_bcat(vep->attr_vsb, p, 1);
869                                p++;
870                        }
871                        if (p < e && *p == '>') {
872                                vep_error(vep,
873                                    "XML 1.0 Missing end attribute delimiter");
874                                vep->state = VEP_TAGERROR;
875                                vep->attr_delim = 0;
876                                if (vep->attr_vsb != NULL) {
877                                        AZ(VSB_finish(vep->attr_vsb));
878                                        VSB_delete(vep->attr_vsb);
879                                        vep->attr_vsb = NULL;
880                                }
881                        } else if (p < e) {
882                                vep->attr_delim = 0;
883                                p++;
884                                vep->state = VEP_INTAG;
885                                if (vep->attr_vsb != NULL) {
886                                        AZ(VSB_finish(vep->attr_vsb));
887                                        AN(vep->dostuff);
888                                        vep->dostuff(vep, DO_ATTR);
889                                        vep->attr_vsb = NULL;
890                                }
891                        }
892
893                /******************************************************
894                 * Utility Section
895                 */
896
897                } else if (vep->state == VEP_MATCH) {
898                        /*
899                         * Match against a table
900                         */
901                        vm = vep_match(vep, p, e);
902                        vep->match_hit = vm;
903                        if (vm != NULL) {
904                                if (vm->match != NULL)
905                                        p += strlen(vm->match);
906                                vep->state = *vm->state;
907                                vep->match = NULL;
908                                vep->tag_i = 0;
909                        } else {
910                                memcpy(vep->tag, p, e - p);
911                                vep->tag_i = e - p;
912                                vep->state = VEP_MATCHBUF;
913                                p = e;
914                        }
915                } else if (vep->state == VEP_MATCHBUF) {
916                        /*
917                         * Match against a table while split over input
918                         * sections.
919                         */
920                        do {
921                                if (*p == '>') {
922                                        for (vm = vep->match;
923                                            vm->match != NULL; vm++)
924                                                continue;
925                                        AZ(vm->match);
926                                } else {
927                                        vep->tag[vep->tag_i++] = *p++;
928                                        vm = vep_match(vep,
929                                            vep->tag, vep->tag + vep->tag_i);
930                                        if (vm && vm->match == NULL) {
931                                                vep->tag_i--;
932                                                p--;
933                                        }
934                                }
935                        } while (vm == NULL && p < e);
936                        vep->match_hit = vm;
937                        if (vm == NULL) {
938                                assert(p == e);
939                        } else {
940                                vep->state = *vm->state;
941                                vep->match = NULL;
942                        }
943                } else if (vep->state == VEP_UNTIL) {
944                        /*
945                         * Skip until we see magic string
946                         */
947                        while (p < e) {
948                                if (*p++ != *vep->until_p++) {
949                                        vep->until_p = vep->until;
950                                } else if (*vep->until_p == '\0') {
951                                        vep->state = vep->until_s;
952                                        break;
953                                }
954                        }
955                        if (p == e && !vep->remove)
956                                vep_mark_verbatim(vep, p);
957                } else {
958                        Debug("*** Unknown state %s\n", vep->state);
959                        INCOMPL();
960                }
961        }
962        /*
963         * We must always mark up the storage we got, try to do so
964         * in the most efficient way, in particular with respect to
965         * minimizing and limiting use of pending.
966         */
967        if (p == vep->ver_p)
968                ;
969        else if (vep->in_esi_tag)
970                vep_mark_skip(vep, p);
971        else if (vep->remove)
972                vep_mark_skip(vep, p);
973        else
974                vep_mark_pending(vep, p);
975}
976
977/*---------------------------------------------------------------------
978 */
979
980static ssize_t
981vep_default_cb(const struct sess *sp, ssize_t l, enum vgz_flag flg)
982{
983
984        (void)flg;
985        AN(sp->wrk->vep);
986        sp->wrk->vep->cb_x += l;
987Debug("CB(%jd,%d) = %jd\n", (intmax_t)l, flg, (intmax_t)sp->wrk->vep->cb_x);
988        return (sp->wrk->vep->cb_x);
989}
990
991/*---------------------------------------------------------------------
992 */
993
994void
995VEP_Init(const struct sess *sp, vep_callback_t *cb)
996{
997        struct vep_state *vep;
998
999        AZ(sp->wrk->vep);
1000        sp->wrk->vep = (void*)WS_Alloc(sp->wrk->ws, sizeof *vep);
1001        AN(sp->wrk->vep);
1002
1003        vep = sp->wrk->vep;
1004        memset(vep, 0, sizeof *vep);
1005        vep->magic = VEP_MAGIC;
1006        vep->sp = sp;
1007        vep->vsb = VSB_new_auto();
1008        AN(vep->vsb);
1009
1010        if (cb != NULL) {
1011                vep->dogzip = 1;
1012                /* XXX */
1013                VSB_printf(vep->vsb, "%c", VEC_GZ);
1014                vep->cb = cb;
1015        } else {
1016                vep->cb = vep_default_cb;
1017        }
1018
1019        vep->state = VEP_START;
1020        vep->crc = crc32(0L, Z_NULL, 0);
1021        vep->crcp = crc32(0L, Z_NULL, 0);
1022
1023        /*
1024         * We must force the GZIP header out as a SKIP string, otherwise
1025         * an object starting with <esi:include would have its GZIP header
1026         * appear after the included object (e000026.vtc)
1027         */
1028        vep->startup = 1;
1029        vep->ver_p = "";
1030        vep->last_mark = SKIP;
1031        vep_mark_common(vep, vep->ver_p, VERBATIM);
1032        vep->startup = 0;
1033}
1034
1035/*---------------------------------------------------------------------
1036 */
1037
1038struct vsb *
1039VEP_Finish(const struct sess *sp)
1040{
1041        struct vep_state *vep;
1042        ssize_t l, lcb;
1043
1044        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1045        vep = sp->wrk->vep;
1046        CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
1047
1048        if (vep->o_pending)
1049                vep_mark_common(vep, vep->ver_p, vep->last_mark);
1050        if (vep->o_wait > 0) {
1051                lcb = vep->cb(vep->sp, 0, VGZ_ALIGN);
1052                vep_emit_common(vep, lcb - vep->o_last, vep->last_mark);
1053        }
1054        (void)vep->cb(vep->sp, 0, VGZ_FINISH);
1055
1056        sp->wrk->vep = NULL;
1057
1058        AZ(VSB_finish(vep->vsb));
1059        l = VSB_len(vep->vsb);
1060        if (vep->esi_found && l > 0)
1061                return (vep->vsb);
1062        VSB_delete(vep->vsb);
1063        return (NULL);
1064}
1065
1066#if 0
1067
1068digraph xml {
1069        rankdir="LR"
1070        size="7,10"
1071#################################################################
1072# SECTION A
1073#
1074
1075START           [shape=ellipse]
1076TESTXML         [shape=ellipse]
1077NOTXML          [shape=ellipse]
1078NEXTTAGa        [shape=hexagon, label="NEXTTAG"]
1079STARTTAGa       [shape=hexagon, label="STARTTAG"]
1080START           -> TESTXML
1081START           -> NEXTTAGa     [style=dotted, label="syntax:1"]
1082TESTXML         -> TESTXML      [label="lws"]
1083TESTXML         -> NOTXML
1084TESTXML         -> STARTTAGa    [label="'<'"]
1085
1086#################################################################
1087# SECTION B
1088
1089NOTMYTAG        [shape=ellipse]
1090NEXTTAG         [shape=ellipse]
1091NOTMYTAG        -> NEXTTAG      [style=dotted, label="syntax:2"]
1092STARTTAGb       [shape=hexagon, label="STARTTAG"]
1093NOTMYTAG        -> NEXTTAG      [label="'>'"]
1094NOTMYTAG        -> NOTMYTAG     [label="*"]
1095NEXTTAG         -> NEXTTAG      [label="'-->'"]
1096NEXTTAG         -> NEXTTAG      [label="*"]
1097NEXTTAG         -> STARTTAGb    [label="'<'"]
1098
1099#################################################################
1100# SECTION C
1101
1102STARTTAG        [shape=ellipse]
1103COMMENT         [shape=ellipse]
1104CDATA           [shape=ellipse]
1105ESITAG          [shape=ellipse]
1106ESIETAG         [shape=ellipse]
1107ESIINCLUDE      [shape=ellipse]
1108ESIREMOVE       [shape=ellipse]
1109ESICOMMENT      [shape=ellipse]
1110ESIBOGON        [shape=ellipse]
1111INTAGc          [shape=hexagon, label="INTAG"]
1112NOTMYTAGc       [shape=hexagon, label="NOTMYTAG"]
1113NEXTTAGc        [shape=hexagon, label="NEXTTAG"]
1114TAGERRORc       [shape=hexagon, label="TAGERROR"]
1115C1              [shape=circle,label=""]
1116STARTTAG        -> COMMENT      [label="'<!--'"]
1117STARTTAG        -> ESITAG       [label="'<esi'"]
1118STARTTAG        -> CDATA        [label="'<![CDATA['"]
1119STARTTAG        -> NOTMYTAGc    [label="'*'"]
1120COMMENT         -> NEXTTAGc     [label="'esi'"]
1121COMMENT         -> C1           [label="*"]
1122C1              -> C1           [label="*"]
1123C1              -> NEXTTAGc     [label="-->"]
1124CDATA           -> CDATA        [label="*"]
1125CDATA           -> NEXTTAGc     [label="]]>"]
1126ESITAG          -> ESIINCLUDE   [label="'include'"]
1127ESITAG          -> ESIREMOVE    [label="'remove'"]
1128ESITAG          -> ESICOMMENT   [label="'comment'"]
1129ESITAG          -> ESIBOGON     [label="*"]
1130ESICOMMENT      -> INTAGc
1131ESICOMMENT      -> TAGERRORc
1132ESICOMMENT      -> TAGERRORc    [style=dotted, label="nested\nin\nremove"]
1133ESIREMOVE       -> INTAGc
1134ESIREMOVE       -> TAGERRORc
1135ESIINCLUDE      -> INTAGc
1136ESIINCLUDE      -> TAGERRORc
1137ESIINCLUDE      -> TAGERRORc    [style=dotted, label="nested\nin\nremove"]
1138ESIBOGON        -> TAGERRORc
1139
1140#################################################################
1141# SECTION D
1142
1143INTAG           [shape=ellipse]
1144TAGERROR        [shape=ellipse]
1145NEXTTAGd        [shape=hexagon, label="NEXTTAG"]
1146ATTRd           [shape=hexagon, label="ATTR"]
1147D1              [shape=circle, label=""]
1148D2              [shape=circle, label=""]
1149INTAG           -> D1           [label="lws"]
1150D1              -> D2           [label="/"]
1151INTAG           -> D2           [label="/"]
1152INTAG           -> NEXTTAGd     [label=">"]
1153D1              -> NEXTTAGd     [label=">"]
1154D2              -> NEXTTAGd     [label=">"]
1155D1              -> ATTRd        [label="XMLstartchar"]
1156D1              -> TAGERROR     [label="*"]
1157D2              -> TAGERROR     [label="*"]
1158TAGERROR        -> TAGERROR     [label="*"]
1159TAGERROR        -> NEXTTAGd     [label="'>'"]
1160
1161#################################################################
1162# SECTION E
1163
1164ATTR            [shape=ellipse]
1165SKIPATTR        [shape=ellipse]
1166ATTRGETVAL      [shape=ellipse]
1167ATTRDELIM       [shape=ellipse]
1168ATTRVAL         [shape=ellipse]
1169TAGERRORe       [shape=hexagon, label="TAGERROR"]
1170INTAGe          [shape=hexagon, label="INTAG"]
1171ATTR            -> SKIPATTR     [label="*"]
1172ATTR            -> ATTRGETVAL   [label="wanted attr"]
1173SKIPATTR        -> SKIPATTR     [label="XMLname"]
1174SKIPATTR        -> ATTRDELIM    [label="'='"]
1175SKIPATTR        -> TAGERRORe    [label="*"]
1176ATTRGETVAL      -> ATTRDELIM
1177ATTRDELIM       -> ATTRVAL      [label="\""]
1178ATTRDELIM       -> ATTRVAL      [label="\'"]
1179ATTRDELIM       -> ATTRVAL      [label="*"]
1180ATTRDELIM       -> TAGERRORe    [label="lws"]
1181ATTRVAL         -> TAGERRORe    [label="'>'"]
1182ATTRVAL         -> INTAGe       [label="delim"]
1183ATTRVAL         -> ATTRVAL      [label="*"]
1184
1185}
1186
1187#endif
Note: See TracBrowser for help on using the repository browser.