source: bin/varnishd/cache_esi_parse.c @ 6f038c

Revision 6f038c, 28.7 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

Remove $Id$ from the source code, git does not support it.

  • 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#include "stevedore.h"
42
43//#define Debug(fmt, ...) printf(fmt, __VA_ARGS__)
44#define Debug(fmt, ...) /**/
45
46struct vep_state;
47
48enum dowhat {DO_ATTR, DO_TAG};
49typedef void dostuff_f(struct vep_state *, enum dowhat);
50
51struct vep_match {
52        const char      *match;
53        const char      * const *state;
54};
55
56enum vep_mark { VERBATIM = 0, SKIP };
57
58struct vep_state {
59        unsigned                magic;
60#define VEP_MAGIC               0x55cb9b82
61        struct vsb              *vsb;
62
63        const struct sess       *sp;
64        int                     dogzip;
65        vep_callback_t          *cb;
66
67        /* Internal Counter for default call-back function */
68        ssize_t                 cb_x;
69
70        /* parser state */
71        const char              *state;
72        unsigned                startup;
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_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_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 += l;                         \
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->esicmt = NULL;
659                                        vep->esicmt_p = NULL;
660                                        /*
661                                         * The end of the esicmt
662                                         * should not be emitted.
663                                         * But the stuff before should
664                                         */
665                                        vep_mark_skip(vep, p);
666                                }
667                        }
668                        if (p < e) {
669                                if (!vep->remove)
670                                        vep_mark_verbatim(vep, p);
671                                assert(*p == '<');
672                                p++;
673                                vep->state = VEP_STARTTAG;
674                        } else if (vep->esicmt_p == vep->esicmt && !vep->remove)
675                                vep_mark_verbatim(vep, p);
676
677                /******************************************************
678                 * SECTION C
679                 */
680
681                } else if (vep->state == VEP_STARTTAG) {
682                        /*
683                         * Start of tag, set up match table
684                         */
685                        if (p < e) {
686                                if (*p == '/') {
687                                        vep->endtag = 1;
688                                        p++;
689                                }
690                                vep->match = vep_match_starttag;
691                                vep->state = VEP_MATCH;
692                        }
693                } else if (vep->state == VEP_COMMENT) {
694                        /*
695                         * We are in a comment, find out if it is an
696                         * ESI comment or a regular comment
697                         */
698                        if (vep->esicmt == NULL)
699                                vep->esicmt_p = vep->esicmt = "esi";
700                        while (p < e) {
701                                if (*p != *vep->esicmt_p) {
702                                        vep->esicmt_p = vep->esicmt = NULL;
703                                        vep->until_p = vep->until = "-->";
704                                        vep->until_s = VEP_NEXTTAG;
705                                        vep->state = VEP_UNTIL;
706                                        vep_mark_verbatim(vep, p);
707                                        break;
708                                }
709                                p++;
710                                if (*++vep->esicmt_p != '\0')
711                                        continue;
712                                if (vep->remove)
713                                        vep_error(vep,
714                                            "ESI 1.0 Nested <!--esi"
715                                            " element in <esi:remove>");
716                                vep->esicmt_p = vep->esicmt = "-->";
717                                vep->state = VEP_NEXTTAG;
718                                vep_mark_skip(vep, p);
719                                break;
720                        }
721                } else if (vep->state == VEP_CDATA) {
722                        /*
723                         * Easy: just look for the end of CDATA
724                         */
725                        vep->until_p = vep->until = "]]>";
726                        vep->until_s = VEP_NEXTTAG;
727                        vep->state = VEP_UNTIL;
728                } else if (vep->state == VEP_ESITAG) {
729                        vep->in_esi_tag = 1;
730                        vep_mark_skip(vep, p);
731                        vep->match = vep_match_esi;
732                        vep->state = VEP_MATCH;
733                } else if (vep->state == VEP_ESIINCLUDE) {
734                        if (vep->remove) {
735                                vep_error(vep,
736                                    "ESI 1.0 <esi:include> element"
737                                    " nested in <esi:remove>");
738                                vep->state = VEP_TAGERROR;
739                        } else if (vep->endtag) {
740                                vep_error(vep,
741                                    "ESI 1.0 </esi:include> illegal end-tag");
742                                vep->state = VEP_TAGERROR;
743                        } else {
744                                vep->dostuff = vep_do_include;
745                                vep->state = VEP_INTAG;
746                                vep->attr = vep_match_attr_include;
747                        }
748                } else if (vep->state == VEP_ESIREMOVE) {
749                        vep->dostuff = vep_do_remove;
750                        vep->state = VEP_INTAG;
751                } else if (vep->state == VEP_ESICOMMENT) {
752                        if (vep->remove) {
753                                vep_error(vep,
754                                    "ESI 1.0 <esi:comment> element"
755                                    " nested in <esi:remove>");
756                                vep->state = VEP_TAGERROR;
757                        } else if (vep->endtag) {
758                                vep_error(vep,
759                                    "ESI 1.0 </esi:comment> illegal end-tag");
760                                vep->state = VEP_TAGERROR;
761                        } else {
762                                vep->dostuff = vep_do_comment;
763                                vep->state = VEP_INTAG;
764                        }
765                } else if (vep->state == VEP_ESIBOGON) {
766                        vep_error(vep,
767                            "ESI 1.0 <esi:bogus> element");
768                        vep->state = VEP_TAGERROR;
769
770                /******************************************************
771                 * SECTION D
772                 */
773
774                } else if (vep->state == VEP_INTAG) {
775                        vep->tag_i = 0;
776                        while (p < e && vct_islws(*p) && !vep->emptytag) {
777                                p++;
778                                vep->canattr = 1;
779                        }
780                        if (p < e && *p == '/' && !vep->emptytag) {
781                                p++;
782                                vep->emptytag = 1;
783                                vep->canattr = 0;
784                        }
785                        if (p < e && *p == '>') {
786                                p++;
787                                AN(vep->dostuff);
788                                vep_mark_skip(vep, p);
789                                vep->dostuff(vep, DO_TAG);
790                                vep->in_esi_tag = 0;
791                                vep->state = VEP_NEXTTAG;
792                        } else if (p < e && vep->emptytag) {
793                                vep_error(vep,
794                                    "XML 1.0 '>' does not follow '/' in tag");
795                                vep->state = VEP_TAGERROR;
796                        } else if (p < e && vep->canattr &&
797                            vct_isxmlnamestart(*p)) {
798                                vep->state = VEP_ATTR;
799                        } else if (p < e) {
800                                vep_error(vep,
801                                    "XML 1.0 Illegal attribute tart char");
802                                vep->state = VEP_TAGERROR;
803                        }
804                } else if (vep->state == VEP_TAGERROR) {
805                        while (p < e && *p != '>')
806                                p++;
807                        if (p < e) {
808                                p++;
809                                vep_mark_skip(vep, p);
810                                vep->in_esi_tag = 0;
811                                vep->state = VEP_NEXTTAG;
812                        }
813
814                /******************************************************
815                 * SECTION E
816                 */
817
818                } else if (vep->state == VEP_ATTR) {
819                        AZ(vep->attr_delim);
820                        if (vep->attr == NULL) {
821                                p++;
822                                AZ(vep->attr_vsb);
823                                vep->state = VEP_SKIPATTR;
824                        } else {
825                                vep->match = vep->attr;
826                                vep->state = VEP_MATCH;
827                        }
828                } else if (vep->state == VEP_SKIPATTR) {
829                        while (p < e && vct_isxmlname(*p))
830                                p++;
831                        if (p < e && *p == '=') {
832                                p++;
833                                vep->state = VEP_ATTRDELIM;
834                        } else if (p < e && *p == '>') {
835                                vep->state = VEP_INTAG;
836                        } else if (p < e && *p == '/') {
837                                vep->state = VEP_INTAG;
838                        } else if (p < e && vct_issp(*p)) {
839                                vep->state = VEP_INTAG;
840                        } else if (p < e) {
841                                vep_error(vep,
842                                    "XML 1.0 Illegal attr char");
843                                vep->state = VEP_TAGERROR;
844                        }
845                } else if (vep->state == VEP_ATTRGETVAL) {
846                        vep->attr_vsb = vsb_new_auto();
847                        vep->state = VEP_ATTRDELIM;
848                } else if (vep->state == VEP_ATTRDELIM) {
849                        AZ(vep->attr_delim);
850                        if (*p == '"' || *p == '\'') {
851                                vep->attr_delim = *p++;
852                                vep->state = VEP_ATTRVAL;
853                        } else if (!vct_issp(*p)) {
854                                vep->attr_delim = ' ';
855                                vep->state = VEP_ATTRVAL;
856                        } else {
857                                vep_error(vep,
858                                    "XML 1.0 Illegal attribute delimiter");
859                                vep->state = VEP_TAGERROR;
860                        }
861
862                } else if (vep->state == VEP_ATTRVAL) {
863                        while (p < e && *p != '>' && *p != vep->attr_delim &&
864                           (vep->attr_delim != ' ' || !vct_issp(*p))) {
865                                if (vep->attr_vsb != NULL)
866                                        vsb_bcat(vep->attr_vsb, p, 1);
867                                p++;
868                        }
869                        if (p < e && *p == '>') {
870                                vep_error(vep,
871                                    "XML 1.0 Missing end attribute delimiter");
872                                vep->state = VEP_TAGERROR;
873                                vep->attr_delim = 0;
874                                if (vep->attr_vsb != NULL) {
875                                        AZ(vsb_finish(vep->attr_vsb));
876                                        vsb_delete(vep->attr_vsb);
877                                        vep->attr_vsb = NULL;
878                                }
879                        } else if (p < e) {
880                                vep->attr_delim = 0;
881                                p++;
882                                vep->state = VEP_INTAG;
883                                if (vep->attr_vsb != NULL) {
884                                        AZ(vsb_finish(vep->attr_vsb));
885                                        AN(vep->dostuff);
886                                        vep->dostuff(vep, DO_ATTR);
887                                        vep->attr_vsb = NULL;
888                                }
889                        }
890
891                /******************************************************
892                 * Utility Section
893                 */
894
895                } else if (vep->state == VEP_MATCH) {
896                        /*
897                         * Match against a table
898                         */
899                        vm = vep_match(vep, p, e);
900                        vep->match_hit = vm;
901                        if (vm != NULL) {
902                                if (vm->match != NULL)
903                                        p += strlen(vm->match);
904                                vep->state = *vm->state;
905                                vep->match = NULL;
906                                vep->tag_i = 0;
907                        } else {
908                                memcpy(vep->tag, p, e - p);
909                                vep->tag_i = e - p;
910                                vep->state = VEP_MATCHBUF;
911                                p = e;
912                        }
913                } else if (vep->state == VEP_MATCHBUF) {
914                        /*
915                         * Match against a table while split over input
916                         * sections.
917                         */
918                        do {
919                                if (*p == '>') {
920                                        for (vm = vep->match;
921                                            vm->match != NULL; vm++)
922                                                continue;
923                                        AZ(vm->match);
924                                } else {
925                                        vep->tag[vep->tag_i++] = *p++;
926                                        vm = vep_match(vep,
927                                            vep->tag, vep->tag + vep->tag_i);
928                                        if (vm && vm->match == NULL) {
929                                                vep->tag_i--;
930                                                p--;
931                                        }
932                                }
933                        } while (vm == NULL && p < e);
934                        vep->match_hit = vm;
935                        if (vm == NULL) {
936                                assert(p == e);
937                        } else {
938                                vep->state = *vm->state;
939                                vep->match = NULL;
940                        }
941                } else if (vep->state == VEP_UNTIL) {
942                        /*
943                         * Skip until we see magic string
944                         */
945                        while (p < e) {
946                                if (*p++ != *vep->until_p++) {
947                                        vep->until_p = vep->until;
948                                } else if (*vep->until_p == '\0') {
949                                        vep->state = vep->until_s;
950                                        break;
951                                }
952                        }
953                        if (p == e && !vep->remove)
954                                vep_mark_verbatim(vep, p);
955                } else {
956                        Debug("*** Unknown state %s\n", vep->state);
957                        INCOMPL();
958                }
959        }
960        /*
961         * We must always mark up the storage we got, try to do so
962         * in the most efficient way, in particular with respect to
963         * minimizing and limiting use of pending.
964         */
965        if (p == vep->ver_p)
966                ;
967        else if (vep->in_esi_tag)
968                vep_mark_skip(vep, p);
969        else if (vep->remove)
970                vep_mark_skip(vep, p);
971        else
972                vep_mark_pending(vep, p);
973}
974
975/*---------------------------------------------------------------------
976 */
977
978static ssize_t
979vep_default_cb(const struct sess *sp, ssize_t l, enum vgz_flag flg)
980{
981
982        (void)flg;
983        AN(sp->wrk->vep);
984        sp->wrk->vep->cb_x += l;
985Debug("CB(%jd,%d) = %jd\n", (intmax_t)l, flg, (intmax_t)sp->wrk->vep->cb_x);
986        return (sp->wrk->vep->cb_x);
987}
988
989/*---------------------------------------------------------------------
990 */
991
992void
993VEP_Init(const struct sess *sp, vep_callback_t *cb)
994{
995        struct vep_state *vep;
996
997        AZ(sp->wrk->vep);
998        sp->wrk->vep = (void*)WS_Alloc(sp->wrk->ws, sizeof *vep);
999        AN(sp->wrk->vep);
1000
1001        vep = sp->wrk->vep;
1002        memset(vep, 0, sizeof *vep);
1003        vep->magic = VEP_MAGIC;
1004        vep->sp = sp;
1005        vep->vsb = vsb_new_auto();
1006        AN(vep->vsb);
1007
1008        if (cb != NULL) {
1009                vep->dogzip = 1;
1010                /* XXX */
1011                vsb_printf(vep->vsb, "%c", VEC_GZ);
1012                vep->cb = cb;
1013        } else {
1014                vep->cb = vep_default_cb;
1015        }
1016
1017        vep->state = VEP_START;
1018        vep->crc = crc32(0L, Z_NULL, 0);
1019        vep->crcp = crc32(0L, Z_NULL, 0);
1020
1021        /*
1022         * We must force the GZIP header out as a SKIP string, otherwise
1023         * an object starting with <esi:include would have its GZIP header
1024         * appear after the included object (e000026.vtc)
1025         */
1026        vep->startup = 1;
1027        vep->ver_p = "";
1028        vep->last_mark = SKIP;
1029        vep_mark_common(vep, vep->ver_p, VERBATIM);
1030        vep->startup = 0;
1031}
1032
1033/*---------------------------------------------------------------------
1034 */
1035
1036struct vsb *
1037VEP_Finish(const struct sess *sp)
1038{
1039        struct vep_state *vep;
1040        ssize_t l, lcb;
1041
1042        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1043        vep = sp->wrk->vep;
1044        CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
1045
1046        if (vep->o_pending)
1047                vep_mark_common(vep, vep->ver_p, vep->last_mark);
1048        if (vep->o_wait > 0) {
1049                lcb = vep->cb(vep->sp, 0, VGZ_ALIGN);
1050                vep_emit_common(vep, lcb - vep->o_last, vep->last_mark);
1051        }
1052        (void)vep->cb(vep->sp, 0, VGZ_FINISH);
1053
1054        sp->wrk->vep = NULL;
1055
1056        AZ(vsb_finish(vep->vsb));
1057        l = vsb_len(vep->vsb);
1058        if (vep->state != VEP_NOTXML && l > 0)
1059                return (vep->vsb);
1060        vsb_delete(vep->vsb);
1061        return (NULL);
1062}
1063
1064#if 0
1065
1066digraph xml {
1067        rankdir="LR"
1068        size="7,10"
1069#################################################################
1070# SECTION A
1071#
1072
1073START           [shape=ellipse]
1074TESTXML         [shape=ellipse]
1075NOTXML          [shape=ellipse]
1076NEXTTAGa        [shape=hexagon, label="NEXTTAG"]
1077STARTTAGa       [shape=hexagon, label="STARTTAG"]
1078START           -> TESTXML
1079START           -> NEXTTAGa     [style=dotted, label="syntax:1"]
1080TESTXML         -> TESTXML      [label="lws"]
1081TESTXML         -> NOTXML
1082TESTXML         -> STARTTAGa    [label="'<'"]
1083
1084#################################################################
1085# SECTION B
1086
1087NOTMYTAG        [shape=ellipse]
1088NEXTTAG         [shape=ellipse]
1089NOTMYTAG        -> NEXTTAG      [style=dotted, label="syntax:2"]
1090STARTTAGb       [shape=hexagon, label="STARTTAG"]
1091NOTMYTAG        -> NEXTTAG      [label="'>'"]
1092NOTMYTAG        -> NOTMYTAG     [label="*"]
1093NEXTTAG         -> NEXTTAG      [label="'-->'"]
1094NEXTTAG         -> NEXTTAG      [label="*"]
1095NEXTTAG         -> STARTTAGb    [label="'<'"]
1096
1097#################################################################
1098# SECTION C
1099
1100STARTTAG        [shape=ellipse]
1101COMMENT         [shape=ellipse]
1102CDATA           [shape=ellipse]
1103ESITAG          [shape=ellipse]
1104ESIETAG         [shape=ellipse]
1105ESIINCLUDE      [shape=ellipse]
1106ESIREMOVE       [shape=ellipse]
1107ESICOMMENT      [shape=ellipse]
1108ESIBOGON        [shape=ellipse]
1109INTAGc          [shape=hexagon, label="INTAG"]
1110NOTMYTAGc       [shape=hexagon, label="NOTMYTAG"]
1111NEXTTAGc        [shape=hexagon, label="NEXTTAG"]
1112TAGERRORc       [shape=hexagon, label="TAGERROR"]
1113C1              [shape=circle,label=""]
1114STARTTAG        -> COMMENT      [label="'<!--'"]
1115STARTTAG        -> ESITAG       [label="'<esi'"]
1116STARTTAG        -> CDATA        [label="'<![CDATA['"]
1117STARTTAG        -> NOTMYTAGc    [label="'*'"]
1118COMMENT         -> NEXTTAGc     [label="'esi'"]
1119COMMENT         -> C1           [label="*"]
1120C1              -> C1           [label="*"]
1121C1              -> NEXTTAGc     [label="-->"]
1122CDATA           -> CDATA        [label="*"]
1123CDATA           -> NEXTTAGc     [label="]]>"]
1124ESITAG          -> ESIINCLUDE   [label="'include'"]
1125ESITAG          -> ESIREMOVE    [label="'remove'"]
1126ESITAG          -> ESICOMMENT   [label="'comment'"]
1127ESITAG          -> ESIBOGON     [label="*"]
1128ESICOMMENT      -> INTAGc
1129ESICOMMENT      -> TAGERRORc
1130ESICOMMENT      -> TAGERRORc    [style=dotted, label="nested\nin\nremove"]
1131ESIREMOVE       -> INTAGc
1132ESIREMOVE       -> TAGERRORc
1133ESIINCLUDE      -> INTAGc
1134ESIINCLUDE      -> TAGERRORc
1135ESIINCLUDE      -> TAGERRORc    [style=dotted, label="nested\nin\nremove"]
1136ESIBOGON        -> TAGERRORc
1137
1138#################################################################
1139# SECTION D
1140
1141INTAG           [shape=ellipse]
1142TAGERROR        [shape=ellipse]
1143NEXTTAGd        [shape=hexagon, label="NEXTTAG"]
1144ATTRd           [shape=hexagon, label="ATTR"]
1145D1              [shape=circle, label=""]
1146D2              [shape=circle, label=""]
1147INTAG           -> D1           [label="lws"]
1148D1              -> D2           [label="/"]
1149INTAG           -> D2           [label="/"]
1150INTAG           -> NEXTTAGd     [label=">"]
1151D1              -> NEXTTAGd     [label=">"]
1152D2              -> NEXTTAGd     [label=">"]
1153D1              -> ATTRd        [label="XMLstartchar"]
1154D1              -> TAGERROR     [label="*"]
1155D2              -> TAGERROR     [label="*"]
1156TAGERROR        -> TAGERROR     [label="*"]
1157TAGERROR        -> NEXTTAGd     [label="'>'"]
1158
1159#################################################################
1160# SECTION E
1161
1162ATTR            [shape=ellipse]
1163SKIPATTR        [shape=ellipse]
1164ATTRGETVAL      [shape=ellipse]
1165ATTRDELIM       [shape=ellipse]
1166ATTRVAL         [shape=ellipse]
1167TAGERRORe       [shape=hexagon, label="TAGERROR"]
1168INTAGe          [shape=hexagon, label="INTAG"]
1169ATTR            -> SKIPATTR     [label="*"]
1170ATTR            -> ATTRGETVAL   [label="wanted attr"]
1171SKIPATTR        -> SKIPATTR     [label="XMLname"]
1172SKIPATTR        -> ATTRDELIM    [label="'='"]
1173SKIPATTR        -> TAGERRORe    [label="*"]
1174ATTRGETVAL      -> ATTRDELIM
1175ATTRDELIM       -> ATTRVAL      [label="\""]
1176ATTRDELIM       -> ATTRVAL      [label="\'"]
1177ATTRDELIM       -> ATTRVAL      [label="*"]
1178ATTRDELIM       -> TAGERRORe    [label="lws"]
1179ATTRVAL         -> TAGERRORe    [label="'>'"]
1180ATTRVAL         -> INTAGe       [label="delim"]
1181ATTRVAL         -> ATTRVAL      [label="*"]
1182
1183}
1184
1185#endif
Note: See TracBrowser for help on using the repository browser.