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