Coverage Report

Created: 2026-09-14 07:13

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