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 787
vep_error(const struct vep_state *vep, const char *p)
204
{
205 787
        VSC_C_main->esi_errors++;
206 1574
        VSLb(vep->vc->wrk->vsl, SLT_ESI_xmlerror, "ERR: after %zd %s",
207 787
             vep->o_last, p);
208 787
}
209
210
/*--------------------------------------------------------------------
211
 * Report a parsing warning
212
 */
213
214
static void
215 126
vep_warn(const struct vep_state *vep, const char *p)
216
{
217 126
        VSC_C_main->esi_warnings++;
218 252
        VSLb(vep->vc->wrk->vsl, SLT_ESI_xmlerror, "WARN: after %zd %s",
219 126
             vep->o_last, p);
220 126
}
221
222
/*---------------------------------------------------------------------
223
 * return match or NULL if more input needed.
224
 */
225
226
static struct vep_match *
227 259133
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 259133
        AN(vep->match);
233 1683919
        for (vm = vep->match; vm->match != NULL; vm++) {
234 1454099
                assert(vstrlen(vm->match) <= sizeof (vep->tag));
235 1454099
                r = b;
236 1594955
                for (q = vm->match; *q != '\0' && r < e; q++, r++)
237 1565642
                        if (*q != *r)
238 1424786
                                break;
239 1454099
                if (*q == '\0')
240 24213
                        break;
241 1429886
                if (r == e)
242 5100
                        return (NULL);
243 1424786
        }
244 254033
        return (vm);
245 259133
}
246
247
/*---------------------------------------------------------------------
248
 *
249
 */
250
251
static void
252 20769
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 20769
        assert(l > 0);
257 20769
        if (l < 256) {
258 20412
                buf[0] = (uint8_t)m8;
259 20412
                buf[1] = (uint8_t)l;
260 20412
                assert((ssize_t)buf[1] == l);
261 20412
                VSB_bcat(vep->vsb, buf, 2);
262 20769
        } else if (l < 65536) {
263 231
                buf[0] = (uint8_t)m16;
264 231
                vbe16enc(buf + 1, (uint16_t)l);
265 231
                assert((ssize_t)vbe16dec(buf + 1) == l);
266 231
                VSB_bcat(vep->vsb, buf, 3);
267 231
        } else {
268 126
                buf[0] = (uint8_t)m64;
269 126
                vbe64enc(buf + 1, l);
270 126
                assert((ssize_t)vbe64dec(buf + 1) == l);
271 126
                VSB_bcat(vep->vsb, buf, 9);
272
        }
273 20769
}
274
275
static void
276 9429
vep_emit_skip(const struct vep_state *vep, ssize_t l)
277
{
278
279 9429
        vep_emit_len(vep, l, VEC_S1, VEC_S2, VEC_S8);
280 9429
}
281
282
static void
283 9219
vep_emit_verbatim(const struct vep_state *vep, ssize_t l, ssize_t l_crc)
284
{
285
        uint8_t buf[4];
286
287 9219
        vep_emit_len(vep, l, VEC_V1, VEC_V2, VEC_V8);
288 9219
        if (vep->dogzip) {
289 2121
                vep_emit_len(vep, l_crc, VEC_C1, VEC_C2, VEC_C8);
290 2121
                vbe32enc(buf, vep->crc);
291 2121
                VSB_bcat(vep->vsb, buf, sizeof buf);
292 2121
        }
293 9219
}
294
295
static void
296 21672
vep_emit_common(struct vep_state *vep, ssize_t l, enum vep_mark mark)
297
{
298
299 21672
        assert(l >= 0);
300 21672
        if (l == 0)
301 3024
                return;
302 18648
        assert(mark == SKIP || mark == VERBATIM);
303 18648
        if (mark == SKIP)
304 9429
                vep_emit_skip(vep, l);
305
        else
306 9219
                vep_emit_verbatim(vep, l, vep->o_crc);
307
308 18648
        vep->crc = crc32(0L, Z_NULL, 0);
309 18648
        vep->o_crc = 0;
310 18648
        vep->o_total += l;
311 21672
}
312
313
/*---------------------------------------------------------------------
314
 *
315
 */
316
317
static void
318 1392083
vep_mark_common(struct vep_state *vep, const char *p, enum vep_mark mark)
319
{
320
        ssize_t l, lcb;
321
322 1392083
        assert(mark == SKIP || mark == VERBATIM);
323
324
        /* The NO-OP case, no data, no pending data & no change of mode */
325 1392083
        if (vep->last_mark == mark && p == vep->ver_p && vep->o_pending == 0)
326 887
                return;
327
328
        /*
329
         * If we changed mode, emit whatever the opposite mode
330
         * assembled before the pending bytes.
331
         */
332
333 1391196
        if (vep->last_mark != mark && (vep->o_wait > 0 || vep->startup)) {
334 34230
                lcb = vep->cb(vep->vc, vep->cb_priv, 0,
335 17115
                    mark == VERBATIM ? VGZ_RESET : VGZ_ALIGN);
336 17115
                vep_emit_common(vep, lcb - vep->o_last, vep->last_mark);
337 17115
                vep->o_last = lcb;
338 17115
                vep->o_wait = 0;
339 17115
        }
340
341
        /* Transfer pending bytes CRC into active mode CRC */
342 1391196
        if (vep->o_pending) {
343 1404
                (void)vep->cb(vep->vc, vep->cb_priv, vep->o_pending,
344
                     VGZ_NORMAL);
345 1404
                if (vep->o_crc == 0) {
346 1104
                        vep->crc = vep->crcp;
347 1104
                        vep->o_crc = vep->o_pending;
348 1104
                } else {
349 600
                        vep->crc = crc32_combine(vep->crc,
350 300
                            vep->crcp, vep->o_pending);
351 300
                        vep->o_crc += vep->o_pending;
352
                }
353 1404
                vep->crcp = crc32(0L, Z_NULL, 0);
354 1404
                vep->o_wait += vep->o_pending;
355 1404
                vep->o_pending = 0;
356 1404
        }
357
358
        /* * Process this bit of input */
359 1391196
        AN(vep->ver_p);
360 1391196
        l = p - vep->ver_p;
361 1391196
        assert(l >= 0);
362 1391196
        vep->crc = crc32(vep->crc, (const void*)vep->ver_p, l);
363 1391196
        vep->o_crc += l;
364 1391196
        vep->ver_p = p;
365
366 1391196
        vep->o_wait += l;
367 1391196
        vep->last_mark = mark;
368 1391196
        (void)vep->cb(vep->vc, vep->cb_priv, l, VGZ_NORMAL);
369 1392083
}
370
371
static void
372 802698
vep_mark_verbatim(struct vep_state *vep, const char *p)
373
{
374
375 802698
        vep_mark_common(vep, p, VERBATIM);
376 802698
        vep->nm_verbatim++;
377 802698
}
378
379
static void
380 584828
vep_mark_skip(struct vep_state *vep, const char *p)
381
{
382
383 584828
        vep_mark_common(vep, p, SKIP);
384 584828
        vep->nm_skip++;
385 584828
}
386
387
static void
388 2595
vep_mark_pending(struct vep_state *vep, const char *p)
389
{
390
        ssize_t l;
391
392 2595
        AN(vep->ver_p);
393 2595
        l = p - vep->ver_p;
394 2595
        assert(l > 0);
395 2595
        vep->crcp = crc32(vep->crcp, (const void *)vep->ver_p, l);
396 2595
        vep->ver_p = p;
397
398 2595
        vep->o_pending += l;
399 2595
        vep->nm_pending++;
400 2595
}
401
402
/*---------------------------------------------------------------------
403
 */
404
405
static void v_matchproto_()
406 126
vep_do_comment(struct vep_state *vep, enum dowhat what)
407
{
408
        Debug("DO_COMMENT(%d)\n", what);
409 126
        assert(what == DO_TAG);
410 126
        if (!vep->emptytag)
411 42
                vep_error(vep, "ESI 1.0 <esi:comment> needs final '/'");
412 126
}
413
414
/*---------------------------------------------------------------------
415
 */
416
417
static void v_matchproto_()
418 2982
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 2982
        assert(what == DO_TAG);
423 2982
        if (vep->emptytag)
424 21
                vep_error(vep, "ESI 1.0 <esi:remove/> not legal");
425 2961
        else if (vep->remove && !vep->endtag)
426 21
                vep_error(vep, "ESI 1.0 <esi:remove> already open");
427 2940
        else if (!vep->remove && vep->endtag)
428 21
                vep_error(vep, "ESI 1.0 <esi:remove> not open");
429
        else
430 2919
                vep->remove = !vep->endtag;
431 2982
}
432
433
/*---------------------------------------------------------------------
434
 */
435
436
static void
437 5481
include_attr_src(struct vep_state *vep)
438
{
439
        const char *p;
440
441 5481
        if (vep->include_src != NULL) {
442 21
                vep_error(vep,
443
                    "ESI 1.0 <esi:include> "
444
                    "has multiple src= attributes");
445 21
                vep->state = VEP_TAGERROR;
446 21
                VSB_destroy(&vep->attr_vsb);
447 21
                VSB_destroy(&vep->include_src);
448 21
                return;
449
        }
450 194985
        for (p = VSB_data(vep->attr_vsb); *p != '\0'; p++)
451 189546
                if (vct_islws(*p))
452 21
                        break;
453 5460
        if (*p != '\0') {
454 21
                vep_error(vep,
455
                    "ESI 1.0 <esi:include> "
456
                    "has whitespace in src= attribute");
457 21
                vep->state = VEP_TAGERROR;
458 21
                VSB_destroy(&vep->attr_vsb);
459 21
                if (vep->include_src != NULL)
460 0
                        VSB_destroy(&vep->include_src);
461 21
                return;
462
        }
463 5439
        vep->include_src = vep->attr_vsb;
464 5439
        vep->attr_vsb = NULL;
465 5481
}
466
467
static void
468 189
include_attr_onerror(struct vep_state *vep)
469
{
470
471 189
        vep->include_continue = !vstrcmp("continue", VSB_data(vep->attr_vsb));
472 189
        VSB_destroy(&vep->attr_vsb);
473 189
}
474
475
static void v_matchproto_()
476 11130
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 11130
        if (what == DO_ATTR) {
484
                Debug("ATTR (%s) (%s)\n", vep->match_hit->match,
485
                        VSB_data(vep->attr_vsb));
486 5670
                if (!vstrcmp("src=", vep->match_hit->match)) {
487 5481
                        include_attr_src(vep);
488 5481
                        return;
489
                }
490 189
                if (!vstrcmp("onerror=", vep->match_hit->match)) {
491 189
                        include_attr_onerror(vep);
492 189
                        return;
493
                }
494 0
                WRONG("Unhandled <esi:include> attribute");
495 0
        }
496 5460
        assert(what == DO_TAG);
497 5460
        if (!vep->emptytag)
498 42
                vep_warn(vep, "ESI 1.0 <esi:include> lacks final '/'");
499 5460
        if (vep->include_src == NULL) {
500 42
                vep_error(vep, "ESI 1.0 <esi:include> lacks src attr");
501 42
                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 5418
        assert(vep->o_wait == 0 || vep->last_mark == SKIP);
514
        /* XXX: what if it contains NUL bytes ?? */
515 5418
        p = VSB_data(vep->include_src);
516 5418
        l = VSB_len(vep->include_src);
517 5418
        h = 0;
518
519 5418
        incl = vep->include_continue ? VEC_IC : VEC_IA;
520
521 5418
        if (l > 7 && !vmemcmp(p, "http://", 7)) {
522 63
                h = p + 7;
523 63
                p = strchr(h, '/');
524 63
                if (p == NULL) {
525 42
                        vep_error(vep,
526
                            "ESI 1.0 <esi:include> invalid src= URL");
527 42
                        vep->state = VEP_TAGERROR;
528 42
                        AZ(vep->attr_vsb);
529 42
                        VSB_destroy(&vep->include_src);
530 42
                        return;
531
                }
532
                Debug("HOST <%.*s> PATH <%s>\n", (int)(p-h),h, p);
533 21
                VSB_printf(vep->vsb, "%c", incl);
534 21
                VSB_printf(vep->vsb, "Host: %.*s%c", (int)(p-h), h, 0);
535 5376
        } else if (l > 8 && !vmemcmp(p, "https://", 8)) {
536 84
                if (!vep->flags.esi_ignore_https) {
537 21
                        vep_warn(vep,
538
                            "ESI 1.0 <esi:include> with https:// ignored");
539 21
                        vep->state = VEP_TAGERROR;
540 21
                        AZ(vep->attr_vsb);
541 21
                        VSB_destroy(&vep->include_src);
542 21
                        return;
543
                }
544 63
                vep_warn(vep,
545
                    "ESI 1.0 <esi:include> https:// treated as http://");
546 63
                h = p + 8;
547 63
                p = strchr(h, '/');
548 63
                if (p == NULL) {
549 42
                        vep_error(vep,
550
                            "ESI 1.0 <esi:include> invalid src= URL");
551 42
                        vep->state = VEP_TAGERROR;
552 42
                        AZ(vep->attr_vsb);
553 42
                        VSB_destroy(&vep->include_src);
554 42
                        return;
555
                }
556 21
                VSB_printf(vep->vsb, "%c", incl);
557 21
                VSB_printf(vep->vsb, "Host: %.*s%c", (int)(p-h), h, 0);
558 5292
        } else if (*p == '/') {
559 4641
                VSB_printf(vep->vsb, "%c", incl);
560 4641
                VSB_printf(vep->vsb, "%c", 0);
561 4641
        } else {
562 630
                VSB_printf(vep->vsb, "%c", incl);
563 630
                VSB_printf(vep->vsb, "%c", 0);
564
                /* Look for the last / before a '?' */
565 630
                h = NULL;
566 2016
                for (q = vep->url; *q && *q != '?'; q++)
567 2121
                        if (*q == '/')
568 735
                                h = q;
569 630
                if (h == NULL)
570 0
                        h = q + 1;
571
572
                Debug("INCL:: [%.*s]/[%s]\n",
573
                    (int)(h - vep->url), vep->url, p);
574 630
                VSB_printf(vep->vsb, "%.*s/", (int)(h - vep->url), vep->url);
575
        }
576 5313
        l -= (p - VSB_data(vep->include_src));
577 191898
        for (q = p; *q != '\0'; ) {
578 186585
                if (*q == '&') {
579
#define R(w,f,r)                                                        \
580
                        if (q + w <= p + l && !vmemcmp(q, f, w)) { \
581
                                VSB_printf(vep->vsb, "%c", r);  \
582
                                q += w;                         \
583
                                continue;                       \
584
                        }
585 210
                        R(6, "&apos;", '\'');
586 168
                        R(6, "&quot;", '"');
587 126
                        R(4, "&lt;", '<');
588 84
                        R(4, "&gt;", '>');
589 42
                        R(5, "&amp;", '&');
590 0
                }
591 186375
                VSB_printf(vep->vsb, "%c", *q++);
592
        }
593
#undef R
594 5313
        VSB_printf(vep->vsb, "%c", 0);
595 5313
        VSB_destroy(&vep->include_src);
596 5313
        vep->include_continue = 0;
597 11130
}
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 1130358
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 1130358
        CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
618 1130358
        assert(l > 0);
619
620 1130358
        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 4557
                vep->ver_p = "";
628 4557
                vep->last_mark = SKIP;
629 4557
                vep_mark_common(vep, vep->ver_p, VERBATIM);
630 4557
                vep->startup = 0;
631 4557
                AZ(vep->hack_p);
632 4557
                vep->hack_p = p;
633 4557
        }
634
635 1130358
        vep->ver_p = p;
636
637 1130358
        e = p + l;
638
639 3285630
        while (p < e) {
640 2155272
                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 2155272
                assert(p >= vep->ver_p);
647
648
                /******************************************************
649
                 * SECTION A
650
                 */
651
652 2155272
                if (vep->state == VEP_START) {
653 4620
                        if (vep->flags.esi_remove_bom &&
654 126
                            *p == (char)0xeb) {
655 84
                                vep->match = vep_match_bom;
656 84
                                vep->state = VEP_MATCH;
657 84
                        } else
658 4536
                                vep->state = VEP_BOM;
659 2155272
                } else if (vep->state == VEP_BOM) {
660 4557
                        vep_mark_skip(vep, p);
661 4557
                        if (vep->flags.esi_disable_xml_check)
662 1176
                                vep->state = VEP_NEXTTAG;
663
                        else
664 3381
                                vep->state = VEP_TESTXML;
665 2150652
                } 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 7507
                        while (p < e && vct_islws(*p))
671 3948
                                p++;
672 3559
                        vep_mark_verbatim(vep, p);
673 3559
                        if (p < e && *p == '<') {
674 3087
                                p++;
675 3087
                                vep->state = VEP_STARTTAG;
676 3559
                        } else if (p < e && *p == (char)0xeb) {
677 42
                                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 42
                                vep->state = VEP_NOTXML;
683 472
                        } else if (p < e) {
684 231
                                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 231
                                vep->state = VEP_NOTXML;
690 231
                        }
691 2146095
                } 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 273
                        p = e;
697 273
                        vep_mark_verbatim(vep, p);
698
699
                /******************************************************
700
                 * SECTION B
701
                 */
702
703 2142536
                } else if (vep->state == VEP_NOTMYTAG) {
704 230399
                        if (vep->flags.esi_ignore_other_elements) {
705 42
                                p++;
706 42
                                vep->state = VEP_NEXTTAG;
707 42
                        } else {
708 230357
                                vep->tag_i = 0;
709 976440
                                while (p < e) {
710 975641
                                        if (*p++ == '>') {
711 229558
                                                vep->state = VEP_NEXTTAG;
712 229558
                                                break;
713
                                        }
714
                                }
715
                        }
716 230399
                        if (p == e && !vep->remove)
717 2132
                                vep_mark_verbatim(vep, p);
718 2142263
                } 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 1352580
                        vep->emptytag = 0;
724 1352580
                        vep->attr = NULL;
725 1352580
                        vep->dostuff = NULL;
726 46767092
                        while (p < e && *p != '<') {
727 45414512
                                if (vep->esicmt_p == NULL) {
728 45412076
                                        p++;
729 45412076
                                        continue;
730
                                }
731 2436
                                if (*p != *vep->esicmt_p) {
732 1386
                                        p++;
733 1386
                                        vep->esicmt_p = vep->esicmt;
734 1386
                                        continue;
735
                                }
736 1050
                                if (!vep->remove && vep->esicmt_p == vep->esicmt)
737 252
                                        vep_mark_verbatim(vep, p);
738 1050
                                p++;
739 1050
                                if (*++vep->esicmt_p == '\0') {
740 336
                                        vep->esi_found = 1;
741 336
                                        vep->esicmt = NULL;
742 336
                                        vep->esicmt_p = NULL;
743
                                        /*
744
                                         * The end of the esicmt
745
                                         * should not be emitted.
746
                                         * But the stuff before should
747
                                         */
748 336
                                        vep_mark_skip(vep, p);
749 336
                                }
750
                        }
751 1352580
                        if (p < e) {
752 236057
                                if (!vep->remove)
753 234314
                                        vep_mark_verbatim(vep, p);
754 236057
                                assert(*p == '<');
755 236057
                                p++;
756 236057
                                vep->state = VEP_STARTTAG;
757 1352580
                        } else if (vep->esicmt_p == vep->esicmt && !vep->remove)
758 561781
                                vep_mark_verbatim(vep, p);
759
760
                /******************************************************
761
                 * SECTION C
762
                 */
763
764 1911864
                } else if (vep->state == VEP_STARTTAG) {
765
                        /* Start of tag, set up match table */
766 239144
                        vep->endtag = 0;
767 239144
                        vep->match = vep_match_starttag;
768 239144
                        vep->state = VEP_MATCH;
769 559284
                } else if (vep->state == VEP_COMMENT) {
770 105
                        vep->esicmt_p = vep->esicmt = NULL;
771 105
                        vep->until_p = vep->until = "-->";
772 105
                        vep->until_s = VEP_NEXTTAG;
773 105
                        vep->state = VEP_UNTIL;
774 320140
                } else if (vep->state == VEP_COMMENTESI) {
775 357
                        if (vep->remove)
776 105
                                vep_error(vep,
777
                                    "ESI 1.0 Nested <!--esi"
778
                                    " element in <esi:remove>");
779 357
                        vep->esicmt_p = vep->esicmt = "-->";
780 357
                        vep->state = VEP_NEXTTAG;
781 357
                        vep_mark_skip(vep, p);
782 320035
                } else if (vep->state == VEP_CDATA) {
783
                        /*
784
                         * Easy: just look for the end of CDATA
785
                         */
786 84
                        vep->until_p = vep->until = "]]>";
787 84
                        vep->until_s = VEP_NEXTTAG;
788 84
                        vep->state = VEP_UNTIL;
789 319678
                } else if (vep->state == VEP_ESIENDTAG) {
790 1533
                        vep->endtag = 1;
791 1533
                        vep->state = VEP_ESITAG;
792 319594
                } else if (vep->state == VEP_ESITAG) {
793 8946
                        vep->in_esi_tag = 1;
794 8946
                        vep->esi_found = 1;
795 8946
                        vep_mark_skip(vep, p);
796 8946
                        vep->match = vep_match_esi;
797 8946
                        vep->state = VEP_MATCH;
798 318061
                } else if (vep->state == VEP_ESIINCLUDE) {
799 5712
                        if (vep->remove) {
800 84
                                vep_error(vep,
801
                                    "ESI 1.0 <esi:include> element"
802
                                    " nested in <esi:remove>");
803 84
                                vep->state = VEP_TAGERROR;
804 5712
                        } else if (vep->endtag) {
805 21
                                vep_error(vep,
806
                                    "ESI 1.0 </esi:include> illegal end-tag");
807 21
                                vep->state = VEP_TAGERROR;
808 21
                        } else {
809 5607
                                vep->dostuff = vep_do_include;
810 5607
                                vep->state = VEP_INTAG;
811 5607
                                vep->attr = vep_match_attr_include;
812
                        }
813 309115
                } else if (vep->state == VEP_ESIREMOVE) {
814 2982
                        vep->dostuff = vep_do_remove;
815 2982
                        vep->state = VEP_INTAG;
816 303403
                } else if (vep->state == VEP_ESICOMMENT) {
817 210
                        if (vep->remove) {
818 21
                                vep_error(vep,
819
                                    "ESI 1.0 <esi:comment> element"
820
                                    " nested in <esi:remove>");
821 21
                                vep->state = VEP_TAGERROR;
822 210
                        } else if (vep->endtag) {
823 42
                                vep_error(vep,
824
                                    "ESI 1.0 </esi:comment> illegal end-tag");
825 42
                                vep->state = VEP_TAGERROR;
826 42
                        } else {
827 147
                                vep->dostuff = vep_do_comment;
828 147
                                vep->state = VEP_INTAG;
829
                        }
830 300421
                } else if (vep->state == VEP_ESIBOGON) {
831 42
                        vep_error(vep,
832
                            "ESI 1.0 <esi:bogus> element");
833 42
                        vep->state = VEP_TAGERROR;
834
835
                /******************************************************
836
                 * SECTION D
837
                 */
838
839 300211
                } else if (vep->state == VEP_INTAG) {
840 15077
                        vep->tag_i = 0;
841 23330
                        while (p < e && vct_islws(*p) && !vep->emptytag) {
842 8253
                                p++;
843 8253
                                vep->canattr = 1;
844
                        }
845 15077
                        if (p < e && *p == '/' && !vep->emptytag) {
846 5544
                                p++;
847 5544
                                vep->emptytag = 1;
848 5544
                                vep->canattr = 0;
849 5544
                        }
850 15077
                        if (p < e && *p == '>') {
851 8568
                                p++;
852 8568
                                AN(vep->dostuff);
853 8568
                                vep_mark_skip(vep, p);
854 8568
                                vep->dostuff(vep, DO_TAG);
855 8568
                                vep->in_esi_tag = 0;
856 8568
                                vep->state = VEP_NEXTTAG;
857 15077
                        } else if (p < e && vep->emptytag) {
858 21
                                vep_error(vep,
859
                                    "XML 1.0 '>' does not follow '/' in tag");
860 21
                                vep->state = VEP_TAGERROR;
861 6509
                        } else if (p < e && vep->canattr &&
862 5943
                            vct_isxmlnamestart(*p)) {
863 5922
                                vep->state = VEP_ATTR;
864 6488
                        } else if (p < e) {
865 21
                                vep_error(vep,
866
                                    "XML 1.0 Illegal attribute start char");
867 21
                                vep->state = VEP_TAGERROR;
868 21
                        }
869 300169
                } else if (vep->state == VEP_TAGERROR) {
870 3881
                        while (p < e && *p != '>')
871 2759
                                p++;
872 1122
                        if (p < e) {
873 378
                                p++;
874 378
                                vep_mark_skip(vep, p);
875 378
                                vep->in_esi_tag = 0;
876 378
                                vep->state = VEP_NEXTTAG;
877 378
                                if (vep->attr_vsb)
878 21
                                        VSB_destroy(&vep->attr_vsb);
879 378
                        }
880
881
                /******************************************************
882
                 * SECTION E
883
                 */
884
885 285092
                } else if (vep->state == VEP_ATTR) {
886 5922
                        AZ(vep->attr_delim);
887 5922
                        if (vep->attr == NULL) {
888 63
                                p++;
889 63
                                AZ(vep->attr_vsb);
890 63
                                vep->state = VEP_SKIPATTR;
891 63
                        } else {
892 5859
                                vep->match = vep->attr;
893 5859
                                vep->state = VEP_MATCH;
894
                        }
895 283970
                } else if (vep->state == VEP_SKIPATTR) {
896 686
                        while (p < e && vct_isxmlname(*p))
897 383
                                p++;
898 303
                        if (p < e && *p == '=') {
899 84
                                p++;
900 84
                                vep->state = VEP_ATTRDELIM;
901 303
                        } else if (p < e && *p == '>') {
902 21
                                vep->state = VEP_INTAG;
903 219
                        } else if (p < e && *p == '/') {
904 21
                                vep->state = VEP_INTAG;
905 198
                        } else if (p < e && vct_issp(*p)) {
906 42
                                vep->state = VEP_INTAG;
907 177
                        } else if (p < e) {
908 21
                                vep_error(vep,
909
                                    "XML 1.0 Illegal attr char");
910 21
                                vep->state = VEP_TAGERROR;
911 21
                        }
912 278048
                } else if (vep->state == VEP_ATTRGETVAL) {
913 5733
                        AZ(vep->attr_vsb);
914 5733
                        vep->attr_vsb = VSB_new_auto();
915 5733
                        vep->state = VEP_ATTRDELIM;
916 277745
                } else if (vep->state == VEP_ATTRDELIM) {
917 5817
                        AZ(vep->attr_delim);
918 5817
                        if (*p == '"' || *p == '\'') {
919 5733
                                vep->attr_delim = *p++;
920 5733
                                vep->state = VEP_ATTRVAL;
921 5817
                        } else if (!vct_issp(*p)) {
922 63
                                vep->attr_delim = ' ';
923 63
                                vep->state = VEP_ATTRVAL;
924 63
                        } else {
925 21
                                vep_error(vep,
926
                                    "XML 1.0 Illegal attribute delimiter");
927 21
                                vep->state = VEP_TAGERROR;
928
                        }
929
930 272012
                } else if (vep->state == VEP_ATTRVAL) {
931 389410
                        while (p < e && *p != '>' && *p != vep->attr_delim &&
932 191415
                           (vep->attr_delim != ' ' || !vct_issp(*p))) {
933 191415
                                if (vep->attr_vsb != NULL)
934 191163
                                        VSB_putc(vep->attr_vsb, *p);
935 191415
                                p++;
936
                        }
937 6580
                        if (p < e && *p == '>') {
938 42
                                vep_error(vep,
939
                                    "XML 1.0 Missing end attribute delimiter");
940 42
                                vep->state = VEP_TAGERROR;
941 42
                                vep->attr_delim = 0;
942 42
                                if (vep->attr_vsb != NULL) {
943 42
                                        AZ(VSB_finish(vep->attr_vsb));
944 42
                                        VSB_destroy(&vep->attr_vsb);
945 42
                                }
946 6580
                        } else if (p < e) {
947 5754
                                vep->attr_delim = 0;
948 5754
                                p++;
949 5754
                                vep->state = VEP_INTAG;
950 5754
                                if (vep->attr_vsb != NULL) {
951 5670
                                        AZ(VSB_finish(vep->attr_vsb));
952 5670
                                        AN(vep->dostuff);
953 5670
                                        vep->dostuff(vep, DO_ATTR);
954 5670
                                        vep->attr_vsb = NULL;
955 5670
                                }
956 5754
                        }
957
958
                /******************************************************
959
                 * Utility Section
960
                 */
961
962 266195
                } else if (vep->state == VEP_MATCH) {
963
                        /*
964
                         * Match against a table
965
                         */
966 254033
                        vm = vep_match(vep, p, e);
967 254033
                        vep->match_hit = vm;
968 254033
                        if (vm != NULL) {
969 251099
                                if (vm->match != NULL)
970 21412
                                        p += vstrlen(vm->match);
971 251099
                                vep->state = *vm->state;
972 251099
                                vep->match = NULL;
973 251099
                                vep->tag_i = 0;
974 251099
                        } else {
975 2934
                                assert(p + sizeof(vep->tag) >= e);
976 2934
                                vmemcpy(vep->tag, p, e - p);
977 2934
                                vep->tag_i = e - p;
978 2934
                                vep->state = VEP_MATCHBUF;
979 2934
                                p = e;
980
                        }
981 259615
                } else if (vep->state == VEP_MATCHBUF) {
982
                        /*
983
                         * Match against a table while split over input
984
                         * sections.
985
                         */
986 5100
                        AN(vep->match);
987 5100
                        i = sizeof(vep->tag) - vep->tag_i;
988 5100
                        if (i > e - p)
989 4438
                                i = e - p;
990 5100
                        vmemcpy(vep->tag + vep->tag_i, p, i);
991 10200
                        vm = vep_match(vep, vep->tag,
992 5100
                            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 5100
                        if (vm == NULL) {
1001 2166
                                vep->tag_i += i;
1002 2166
                                p += i;
1003 2166
                                assert(p == e);
1004 2166
                        } else {
1005 2934
                                vep->match_hit = vm;
1006 2934
                                vep->state = *vm->state;
1007 2934
                                if (vm->match != NULL) {
1008 2801
                                        i = vstrlen(vm->match);
1009 2801
                                        if (i > vep->tag_i)
1010 2773
                                                p += i - vep->tag_i;
1011 2801
                                }
1012 2934
                                vep->match = NULL;
1013 2934
                                vep->tag_i = 0;
1014
                        }
1015 5582
                } else if (vep->state == VEP_UNTIL) {
1016
                        /*
1017
                         * Skip until we see magic string
1018
                         */
1019 3884
                        while (p < e) {
1020 3549
                                if (*p++ != *vep->until_p++) {
1021 3066
                                        vep->until_p = vep->until;
1022 3549
                                } else if (*vep->until_p == '\0') {
1023 147
                                        vep->state = vep->until_s;
1024 147
                                        break;
1025
                                }
1026
                        }
1027 482
                        if (p == e && !vep->remove)
1028 387
                                vep_mark_verbatim(vep, p);
1029 482
                } 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 1130358
        if (p == vep->ver_p)
1040
                ;
1041 564281
        else if (vep->in_esi_tag)
1042 6273
                vep_mark_skip(vep, p);
1043 558008
        else if (vep->remove)
1044 555413
                vep_mark_skip(vep, p);
1045
        else
1046 2595
                vep_mark_pending(vep, p);
1047 1130358
}
1048
1049
/*---------------------------------------------------------------------
1050
 */
1051
1052
static ssize_t v_matchproto_(vep_callback_t)
1053 1394525
vep_default_cb(struct vfp_ctx *vc, void *priv, ssize_t l, enum vgz_flag flg)
1054
{
1055
        ssize_t *s;
1056
1057 1394525
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
1058 1394525
        AN(priv);
1059 1394525
        s = priv;
1060 1394525
        *s += l;
1061 1394525
        (void)flg;
1062 1394525
        return (*s);
1063
}
1064
1065
/*---------------------------------------------------------------------
1066
 */
1067
1068
struct vep_state *
1069 5733
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 5733
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
1075 5733
        CHECK_OBJ_NOTNULL(req, HTTP_MAGIC);
1076 5733
        vep = WS_Alloc(vc->resp->ws, sizeof *vep);
1077 5733
        if (vep == NULL) {
1078 1092
                VSLb(vc->wrk->vsl, SLT_VCL_Error,
1079
                     "VEP_Init() workspace overflow");
1080 1092
                return (NULL);
1081
        }
1082
1083 4641
        INIT_OBJ(vep, VEP_MAGIC);
1084 4641
        vep->flags = flags;
1085 4641
        vep->url = req->hd[HTTP_HDR_URL].b;
1086 4641
        vep->vc = vc;
1087 4641
        vep->vsb = VSB_new_auto();
1088 4641
        AN(vep->vsb);
1089
1090 4641
        if (cb != NULL) {
1091 1596
                vep->dogzip = 1;
1092
                /* XXX */
1093 1596
                VSB_printf(vep->vsb, "%c", VEC_GZ);
1094 1596
                vep->cb = cb;
1095 1596
                vep->cb_priv = cb_priv;
1096 1596
        } else {
1097 3045
                vep->cb = vep_default_cb;
1098 3045
                vep->cb_priv = &vep->cb_x;
1099
        }
1100
1101 4641
        vep->state = VEP_START;
1102 4641
        vep->crc = crc32(0L, Z_NULL, 0);
1103 4641
        vep->crcp = crc32(0L, Z_NULL, 0);
1104
1105 4641
        vep->startup = 1;
1106 4641
        return (vep);
1107 5733
}
1108
1109
/*---------------------------------------------------------------------
1110
 */
1111
1112
struct vsb *
1113 4641
VEP_Finish(struct vep_state *vep)
1114
{
1115
        ssize_t l, lcb;
1116
1117 4641
        CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
1118
1119 4641
        if (vep->include_src)
1120 0
                VSB_destroy(&vep->include_src);
1121 4641
        if (vep->attr_vsb)
1122 0
                VSB_destroy(&vep->attr_vsb);
1123
1124 8904
        if (vep->state != VEP_START &&
1125 4557
            vep->state != VEP_BOM &&
1126 4557
            vep->state != VEP_TESTXML &&
1127 4536
            vep->state != VEP_NOTXML &&
1128 4263
            vep->state != VEP_NEXTTAG) {
1129 73
                vep_error(vep, "VEP ended inside a tag");
1130 73
        }
1131
1132 4641
        if (vep->o_pending)
1133 0
                vep_mark_common(vep, vep->ver_p, vep->last_mark);
1134 4641
        if (vep->o_wait > 0) {
1135 4557
                lcb = vep->cb(vep->vc, vep->cb_priv, 0, VGZ_ALIGN);
1136 4557
                vep_emit_common(vep, lcb - vep->o_last, vep->last_mark);
1137 4557
        }
1138
        // NB: We don't account for PAD+SUM+LEN in gzipped objects
1139 4641
        (void)vep->cb(vep->vc, vep->cb_priv, 0, VGZ_FINISH);
1140
1141 4641
        AZ(VSB_finish(vep->vsb));
1142 4641
        l = VSB_len(vep->vsb);
1143 4641
        if (vep->esi_found && l > 0)
1144 2541
                return (vep->vsb);
1145 2100
        VSB_destroy(&vep->vsb);
1146 2100
        return (NULL);
1147 4641
}
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