]> mj.ucw.cz Git - libucw.git/blob - ucw-xml/parse.c
XML: Changed internal representation of attributes
[libucw.git] / ucw-xml / parse.c
1 /*
2  *      UCW Library -- A simple XML parser
3  *
4  *      (c) 2007--2008 Pavel Charvat <pchar@ucw.cz>
5  *      (c) 2015 Martin Mares <mj@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #undef LOCAL_DEBUG
12
13 #include <ucw/lib.h>
14 #include <ucw-xml/xml.h>
15 #include <ucw-xml/dtd.h>
16 #include <ucw-xml/internals.h>
17 #include <ucw/fastbuf.h>
18 #include <ucw/ff-unicode.h>
19 #include <ucw/unicode.h>
20 #include <ucw/chartype.h>
21 #include <ucw/hashfunc.h>
22
23 #include <setjmp.h>
24
25 /*** Basic parsing ***/
26
27 void NONRET
28 xml_fatal_expected(struct xml_context *ctx, uint c)
29 {
30   if (c >= 32 && c < 127)
31     xml_fatal(ctx, "Expected '%c'", c);
32   else
33     xml_fatal(ctx, "Expected U+%04x", c);
34 }
35
36 void NONRET
37 xml_fatal_expected_white(struct xml_context *ctx)
38 {
39   xml_fatal(ctx, "Expected a white space");
40 }
41
42 void NONRET
43 xml_fatal_expected_quot(struct xml_context *ctx)
44 {
45   xml_fatal(ctx, "Expected a quotation mark");
46 }
47
48 void
49 xml_parse_eq(struct xml_context *ctx)
50 {
51   /* Eq ::= S? '=' S? */
52   xml_parse_white(ctx, 0);
53   xml_parse_char(ctx, '=');
54   xml_parse_white(ctx, 0);
55 }
56
57 /*** Names and nmtokens ***/
58
59 static char *
60 xml_parse_string(struct xml_context *ctx, struct mempool *pool, uint first_cat, uint next_cat, char *err)
61 {
62   char *p = mp_start_noalign(pool, 2);
63   *p++ = '<';           /* We always prepend a '<', so we can seek backwards in the string */
64   if (unlikely(!(xml_peek_cat(ctx) & first_cat)))
65     xml_fatal(ctx, "%s", err);
66   do
67     {
68       p = mp_spread(pool, p, 5);
69       p = utf8_32_put(p, xml_skip_char(ctx));
70     }
71   while (xml_peek_cat(ctx) & next_cat);
72   *p++ = 0;
73   return mp_end(pool, p) + 1;
74 }
75
76 static void
77 xml_skip_string(struct xml_context *ctx, uint first_cat, uint next_cat, char *err)
78 {
79   if (unlikely(!(xml_get_cat(ctx) & first_cat)))
80     xml_fatal(ctx, "%s", err);
81   while (xml_peek_cat(ctx) & next_cat)
82     xml_skip_char(ctx);
83 }
84
85 char *
86 xml_parse_name(struct xml_context *ctx, struct mempool *pool)
87 {
88   /* Name ::= NameStartChar (NameChar)* */
89   return xml_parse_string(ctx, pool, ctx->cat_sname, ctx->cat_name, "Expected a name");
90 }
91
92 void
93 xml_skip_name(struct xml_context *ctx)
94 {
95   xml_skip_string(ctx, ctx->cat_sname, ctx->cat_name, "Expected a name");
96 }
97
98 char *
99 xml_parse_nmtoken(struct xml_context *ctx, struct mempool *pool)
100 {
101   /* Nmtoken ::= (NameChar)+ */
102   return xml_parse_string(ctx, pool, ctx->cat_name, ctx->cat_name, "Expected a nmtoken");
103 }
104
105 /*** Simple literals ***/
106
107 char *
108 xml_parse_system_literal(struct xml_context *ctx, struct mempool *pool)
109 {
110   /* SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") */
111   char *p = mp_start_noalign(pool, 1);
112   uint q = xml_parse_quote(ctx), c;
113   while ((c = xml_get_char(ctx)) != q)
114     {
115       p = mp_spread(pool, p, 5);
116       p = utf8_32_put(p, c);
117     }
118   *p++ = 0;
119   return mp_end(pool, p);
120 }
121
122 char *
123 xml_parse_pubid_literal(struct xml_context *ctx, struct mempool *pool)
124 {
125   /* PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'" */
126   char *p = mp_start_noalign(pool, 1);
127   uint q = xml_parse_quote(ctx), c;
128   while ((c = xml_get_char(ctx)) != q)
129     {
130       if (unlikely(!(xml_last_cat(ctx) & XML_CHAR_PUBID)))
131         xml_fatal(ctx, "Expected a pubid character");
132       p = mp_spread(pool, p, 2);
133       *p++ = c;
134     }
135   *p++ = 0;
136   return mp_end(pool, p);
137 }
138
139 /*** Comments ***/
140
141 void
142 xml_push_comment(struct xml_context *ctx)
143 {
144   TRACE(ctx, "push_comment");
145   /* Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
146    * Already parsed: '<!-' */
147   xml_parse_char(ctx, '-');
148   struct xml_node *n = xml_push_dom(ctx, NULL);
149   n->type = XML_NODE_COMMENT;
150   char *p = mp_start_noalign(ctx->pool, 6);
151   while (1)
152     {
153       if (xml_get_char(ctx) == '-')
154         if (xml_get_char(ctx) == '-')
155           break;
156         else
157           *p++ = '-';
158       p = utf8_32_put(p, xml_last_char(ctx));
159       p = mp_spread(ctx->pool, p, 6);
160     }
161   xml_parse_char(ctx, '>');
162   *p = 0;
163   n->len = p - (char *)mp_ptr(ctx->pool);
164   n->text = mp_end(ctx->pool, p + 1);
165   if ((ctx->flags & XML_REPORT_COMMENTS) && ctx->h_comment)
166     ctx->h_comment(ctx);
167 }
168
169 void
170 xml_pop_comment(struct xml_context *ctx)
171 {
172   xml_pop_dom(ctx, !(ctx->flags & XML_ALLOC_COMMENTS));
173   xml_dec(ctx);
174   TRACE(ctx, "pop_comment");
175 }
176
177 void
178 xml_skip_comment(struct xml_context *ctx)
179 {
180   TRACE(ctx, "skip_comment");
181   xml_parse_char(ctx, '-');
182   while (xml_get_char(ctx) != '-' || xml_get_char(ctx) != '-');
183   xml_parse_char(ctx, '>');
184   xml_dec(ctx);
185 }
186
187 /*** Processing instructions ***/
188
189 void
190 xml_push_pi(struct xml_context *ctx)
191 {
192   TRACE(ctx, "push_pi");
193   /* Parses a PI to ctx->value and ctx->name:
194    *   PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
195    *   PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
196    * Already parsed: '<?' */
197   struct xml_node *n = xml_push_dom(ctx, NULL);
198   n->type = XML_NODE_PI;
199   n->name = xml_parse_name(ctx, ctx->pool);
200   if (unlikely(!strcasecmp(n->name, "xml")))
201     xml_error(ctx, "Reserved PI target");
202   char *p = mp_start_noalign(ctx->pool, 5);
203   if (!xml_parse_white(ctx, 0))
204     xml_parse_seq(ctx, "?>");
205   else
206     while (1)
207       {
208         if (xml_get_char(ctx) == '?')
209           if (xml_peek_char(ctx) == '>')
210             {
211               xml_skip_char(ctx);
212               break;
213             }
214           else
215             *p++ = '?';
216         else
217           p = utf8_32_put(p, xml_last_char(ctx));
218         p = mp_spread(ctx->pool, p, 5);
219       }
220   *p = 0;
221   n->len = p - (char *)mp_ptr(ctx->pool);
222   n->text = mp_end(ctx->pool, p + 1);
223   if ((ctx->flags & XML_REPORT_PIS) && ctx->h_pi)
224     ctx->h_pi(ctx);
225 }
226
227 void
228 xml_pop_pi(struct xml_context *ctx)
229 {
230   xml_pop_dom(ctx, !(ctx->flags & XML_ALLOC_PIS));
231   xml_dec(ctx);
232   TRACE(ctx, "pop_pi");
233 }
234
235 void
236 xml_skip_pi(struct xml_context *ctx)
237 {
238   TRACE(ctx, "skip_pi");
239   if (ctx->flags & XML_VALIDATING)
240     {
241       struct mempool_state state;
242       mp_save(ctx->stack, &state);
243       if (unlikely(!strcasecmp(xml_parse_name(ctx, ctx->stack), "xml")))
244         xml_error(ctx, "Reserved PI target");
245       mp_restore(ctx->stack, &state);
246       if (!xml_parse_white(ctx, 0))
247         {
248           xml_parse_seq(ctx, "?>");
249           xml_dec(ctx);
250           return;
251         }
252     }
253   while (1)
254     if (xml_get_char(ctx) == '?')
255       if (xml_peek_char(ctx) == '>')
256         break;
257   xml_skip_char(ctx);
258   xml_dec(ctx);
259 }
260
261 /*** Character references ***/
262
263 uint
264 xml_parse_char_ref(struct xml_context *ctx)
265 {
266   TRACE(ctx, "parse_char_ref");
267   /* CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
268    * Already parsed: '&#' */
269   uint v = 0;
270   if (xml_get_char(ctx) == 'x')
271     {
272       if (!(xml_get_cat(ctx) & XML_CHAR_XDIGIT))
273         {
274           xml_error(ctx, "Expected a hexadecimal value of character reference");
275           goto recover;
276         }
277       do
278         {
279           v = (v << 4) + Cxvalue(xml_last_char(ctx));
280         }
281       while (v < 0x110000 && (xml_get_cat(ctx) & XML_CHAR_XDIGIT));
282     }
283   else
284     {
285       if (!(xml_last_cat(ctx) & XML_CHAR_DIGIT))
286         {
287           xml_error(ctx, "Expected a numeric value of character reference");
288           goto recover;
289         }
290       do
291         {
292           v = v * 10 + xml_last_char(ctx) - '0';
293         }
294       while (v < 0x110000 && (xml_get_cat(ctx) & XML_CHAR_DIGIT));
295     }
296   uint cat = xml_char_cat(v);
297   if (!(cat & ctx->cat_unrestricted))
298     {
299       xml_error(ctx, "Character reference out of range");
300       goto recover;
301     }
302   if (xml_last_char(ctx) == ';')
303     {
304       xml_dec(ctx);
305       return v;
306     }
307   xml_error(ctx, "Expected ';'");
308 recover:
309   while (xml_last_char(ctx) != ';')
310     xml_get_char(ctx);
311   xml_dec(ctx);
312   return UNI_REPLACEMENT;
313 }
314
315 /*** References to general entities ***/
316
317 static void
318 xml_parse_ref(struct xml_context *ctx)
319 {
320   /* Reference ::= EntityRef | CharRef
321    * EntityRef ::= '&' Name ';'
322    * Already parsed: '&' */
323   struct fastbuf *out = &ctx->chars;
324   if (xml_peek_char(ctx) == '#')
325     {
326       xml_skip_char(ctx);
327       bput_utf8_32(out, xml_parse_char_ref(ctx));
328     }
329   else
330     {
331       TRACE(ctx, "parse_ge_ref");
332       struct mempool_state state;
333       mp_save(ctx->stack, &state);
334       char *name = xml_parse_name(ctx, ctx->stack);
335       xml_parse_char(ctx, ';');
336       struct xml_dtd_entity *ent = xml_dtd_find_entity(ctx, name);
337       if (!ent)
338         {
339           xml_error(ctx, "Unknown entity &%s;", name);
340           bputc(out, '&');
341           bputs(out, name);
342           bputc(out, ';');
343         }
344       else if (ent->flags & XML_DTD_ENTITY_TRIVIAL)
345         {
346           TRACE(ctx, "Trivial entity &%s;", name);
347           bputs(out, ent->text);
348         }
349       else
350         {
351           TRACE(ctx, "Pushed entity &%s;", name);
352           mp_restore(ctx->stack, &state);
353           xml_dec(ctx);
354           xml_push_entity(ctx, ent);
355           return;
356         }
357       mp_restore(ctx->stack, &state);
358       xml_dec(ctx);
359     }
360 }
361
362 /*** Character data ***/
363
364 void
365 xml_spout_chars(struct fastbuf *fb)
366 {
367   if (fb->bptr < fb->bufend)
368     return;
369   struct xml_context *ctx = SKIP_BACK(struct xml_context, chars, fb);
370   struct mempool *pool = ctx->pool;
371   if (fb->bufend != fb->buffer)
372     {
373       TRACE(ctx, "growing chars");
374       uint len = fb->bufend - fb->buffer;
375       uint reported = fb->bstop - fb->buffer;
376       fb->buffer = mp_expand(pool);
377       fb->bufend = fb->buffer + mp_avail(pool);
378       fb->bptr = fb->buffer + len;
379       fb->bstop = fb->buffer + reported;
380     }
381   else
382     {
383       TRACE(ctx, "starting chars");
384       mp_save(pool, &ctx->chars_state);
385       fb->bptr = fb->buffer = fb->bstop = mp_start_noalign(pool, 2);
386       fb->bufend = fb->buffer + mp_avail(pool) - 1;
387     }
388 }
389
390 static inline uint
391 xml_end_chars(struct xml_context *ctx, char **out)
392 {
393   struct fastbuf *fb = &ctx->chars;
394   uint len = fb->bptr - fb->buffer;
395   if (len)
396     {
397       TRACE(ctx, "ending chars");
398       *fb->bptr = 0;
399       *out = mp_end(ctx->pool, fb->bptr + 1);
400       fb->bufend = fb->bstop = fb->bptr = fb->buffer;
401     }
402   return len;
403 }
404
405 static inline uint
406 xml_report_chars(struct xml_context *ctx, char **out)
407 {
408   struct fastbuf *fb = &ctx->chars;
409   uint len = fb->bptr - fb->buffer;
410   if (len)
411     {
412       *fb->bptr = 0;
413       *out = fb->bstop;
414       fb->bstop = fb->bptr;
415     }
416   return len;
417 }
418
419 static inline uint
420 xml_flush_chars(struct xml_context *ctx)
421 {
422   char *text, *rtext;
423   uint len = xml_end_chars(ctx, &text), rlen;
424   if (len)
425     {
426       if (ctx->flags & XML_NO_CHARS)
427         {
428           if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_ignorable)
429             ctx->h_ignorable(ctx, text, len);
430           mp_restore(ctx->pool, &ctx->chars_state);
431           return 0;
432         }
433       if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_block && (rlen = xml_report_chars(ctx, &rtext)))
434         ctx->h_block(ctx, rtext, rlen);
435       if (!(ctx->flags & XML_ALLOC_CHARS) && !(ctx->flags & XML_REPORT_CHARS))
436         {
437           mp_restore(ctx->pool, &ctx->chars_state);
438           return 0;
439         }
440       struct xml_node *n = xml_push_dom(ctx, &ctx->chars_state);
441       n->type = XML_NODE_CHARS;
442       n->text = text;
443       n->len = len;
444       if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_chars)
445         ctx->h_chars(ctx);
446     }
447   return len;
448 }
449
450 static inline void
451 xml_pop_chars(struct xml_context *ctx)
452 {
453   xml_pop_dom(ctx, !(ctx->flags & XML_ALLOC_CHARS));
454   TRACE(ctx, "pop_chars");
455 }
456
457 static inline void
458 xml_append_chars(struct xml_context *ctx)
459 {
460   TRACE(ctx, "append_chars");
461   struct fastbuf *out = &ctx->chars;
462   if (ctx->flags & XML_NO_CHARS)
463     while (xml_get_char(ctx) != '<')
464       if (xml_last_cat(ctx) & XML_CHAR_WHITE)
465         bput_utf8_32(out, xml_last_char(ctx));
466       else
467         {
468           xml_error(ctx, "This element must not contain character data");
469           while (xml_get_char(ctx) != '<');
470           break;
471         }
472   else
473     while (xml_get_char(ctx) != '<')
474       if (xml_last_char(ctx) == '&')
475         {
476           xml_inc(ctx);
477           xml_parse_ref(ctx);
478         }
479       else
480         bput_utf8_32(out, xml_last_char(ctx));
481   xml_unget_char(ctx);
482 }
483
484 /*** CDATA sections ***/
485
486 static void
487 xml_skip_cdata(struct xml_context *ctx)
488 {
489   TRACE(ctx, "skip_cdata");
490   xml_parse_seq(ctx, "CDATA[");
491   while (xml_get_char(ctx) != ']' || xml_get_char(ctx) != ']' || xml_get_char(ctx) != '>');
492   xml_dec(ctx);
493 }
494
495 static void
496 xml_append_cdata(struct xml_context *ctx)
497 {
498   /* CDSect :== '<![CDATA[' (Char* - (Char* ']]>' Char*)) ']]>'
499    * Already parsed: '<![' */
500   TRACE(ctx, "append_cdata");
501   if (ctx->flags & XML_NO_CHARS)
502     {
503       xml_error(ctx, "This element must not contain CDATA");
504       xml_skip_cdata(ctx);
505       return;
506     }
507   xml_parse_seq(ctx, "CDATA[");
508   struct fastbuf *out = &ctx->chars;
509   uint rlen;
510   char *rtext;
511   if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_block && (rlen = xml_report_chars(ctx, &rtext)))
512     ctx->h_block(ctx, rtext, rlen);
513   while (1)
514     {
515       if (xml_get_char(ctx) == ']')
516         {
517           if (xml_get_char(ctx) == ']')
518             if (xml_get_char(ctx) == '>')
519               break;
520             else
521               bputc(out, ']');
522           bputc(out, ']');
523         }
524       bput_utf8_32(out, xml_last_char(ctx));
525     }
526   if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_cdata && (rlen = xml_report_chars(ctx, &rtext)))
527     ctx->h_cdata(ctx, rtext, rlen);
528   xml_dec(ctx);
529 }
530
531 /*** Attribute values ***/
532
533 char *
534 xml_parse_attr_value(struct xml_context *ctx, struct xml_dtd_attr *attr UNUSED)
535 {
536   TRACE(ctx, "parse_attr_value");
537   /* AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'" */
538   /* FIXME: -- check value constrains / normalize leading/trailing WS and repeated WS */
539   struct mempool_state state;
540   uint quote = xml_parse_quote(ctx);
541   mp_save(ctx->stack, &state);
542   struct fastbuf *out = &ctx->chars;
543   struct xml_source *src = ctx->src;
544   while (1)
545     {
546       uint c = xml_get_char(ctx);
547       if (c == '&')
548         {
549           xml_inc(ctx);
550           xml_parse_ref(ctx);
551         }
552       else if (c == quote && src == ctx->src)
553         break;
554       else if (c == '<')
555         xml_error(ctx, "Attribute value must not contain '<'");
556       else if (xml_last_cat(ctx) & XML_CHAR_WHITE)
557         bputc(out, ' ');
558       else
559         bput_utf8_32(out, c);
560     }
561   mp_restore(ctx->stack, &state);
562   char *text;
563   return xml_end_chars(ctx, &text) ? text : "";
564 }
565
566 uint
567 xml_normalize_white(struct xml_context *ctx UNUSED, char *text)
568 {
569   char *s = text, *d = text;
570   while (*s == 0x20)
571     s++;
572   while (1)
573     {
574       while (*s & ~0x20)
575         *d++ = *s++;
576       if (!*s)
577         break;
578       while (*++s == 0x20);
579       *d++ = 0x20;
580     }
581   if (d != text && d[-1] == 0x20)
582     d--;
583   *d = 0;
584   return d - text;
585 }
586
587 /*** Attributes ***/
588
589 static void
590 xml_raw_add_attr(struct xml_context *ctx, struct xml_node *e, char *name, char *value)
591 {
592   struct xml_attr *a = mp_alloc(ctx->pool, sizeof(*a));
593   a->elem = e;
594   a->ns = 0;            /* Namespaces will be resolved later */
595   a->name = name;
596   a->val = value;
597   a->dtd = NULL;
598   a->user = NULL;
599   /* a->hash will be calculated later */
600   slist_add_tail(&e->attrs, &a->n);
601 }
602
603 static inline uint
604 xml_attr_hash(uint ns, char *name)
605 {
606   return hash_string(name) ^ hash_u32(ns);
607 }
608
609 static void
610 xml_parse_attr(struct xml_context *ctx)
611 {
612   TRACE(ctx, "parse_attr");
613   /* Attribute ::= Name Eq AttValue */
614   struct xml_node *e = ctx->node;
615   char *n = xml_parse_name(ctx, ctx->pool);
616   xml_parse_eq(ctx);
617   char *v = xml_parse_attr_value(ctx, NULL);
618   xml_raw_add_attr(ctx, e, n, v);
619 }
620
621 static void
622 xml_process_attr(struct xml_context *ctx, struct xml_attr *a)
623 {
624   struct xml_node *e = a->elem;
625   a->hash = xml_attr_hash(a->ns, a->name);
626
627   XML_ATTR_FOR_EACH(a2, e)
628     {
629       if (a2 == a)
630         break;
631       if (a2->hash == a->hash && a2->ns == a->ns && !strcmp(a2->name, a->name))
632         xml_error(ctx, "Attribute %s is not unique in element <%s>", xml_attr_qname(ctx, a), xml_node_qname(ctx, e));
633     }
634 }
635
636 struct xml_attr *
637 xml_attr_find(struct xml_context *ctx, struct xml_node *node, char *name)
638 {
639   return xml_attr_find_ns(ctx, node, 0, name);
640 }
641
642 struct xml_attr *
643 xml_attr_find_ns(struct xml_context *ctx UNUSED, struct xml_node *node, uint ns, char *name)
644 {
645   ASSERT(node->type == XML_NODE_ELEM);
646   uint hash = xml_attr_hash(ns, name);
647   XML_ATTR_FOR_EACH(a, node)
648     if (a->hash == hash && a->ns == ns && !strcmp(a->name, name))
649       return a;
650   return NULL;
651 }
652
653 char *
654 xml_attr_value_ns(struct xml_context *ctx, struct xml_node *node, uint ns, char *name)
655 {
656   struct xml_attr *attr = xml_attr_find_ns(ctx, node, ns, name);
657   if (attr)
658     return attr->val;
659   if (!node->dtd)
660     return NULL;
661   if (ns)                       /* So far, our DTD support is not namespace-aware */
662     return NULL;
663   struct xml_dtd_attr *dtd = xml_dtd_find_attr(ctx, node->dtd, name);
664   return dtd ? dtd->default_value : NULL;
665 }
666
667 char *
668 xml_attr_value(struct xml_context *ctx, struct xml_node *node, char *name)
669 {
670   return xml_attr_value_ns(ctx, node, 0, name);
671 }
672
673 char *
674 xml_attr_qname(struct xml_context *ctx UNUSED, struct xml_attr *attr)
675 {
676   char *n = attr->name;
677   while (n[-1] != '<')
678     n--;
679   return n;
680 }
681
682 /*** Elements ***/
683
684 static uint
685 xml_validate_element(struct xml_dtd_elem_node *root, struct xml_dtd_elem *elem)
686 {
687   if (root->elem)
688     return elem == root->elem;
689   else
690     SLIST_FOR_EACH(struct xml_dtd_elem_node *, son, root->sons)
691       if (xml_validate_element(son, elem))
692         return 1;
693   return 0;
694 }
695
696 static void
697 xml_push_element(struct xml_context *ctx)
698 {
699   TRACE(ctx, "push_element");
700   /* EmptyElemTag | STag
701    * EmptyElemTag ::= '<' Name (S  Attribute)* S? '/>'
702    * STag ::= '<' Name (S  Attribute)* S? '>'
703    * Already parsed: '<' */
704   struct xml_node *e = xml_push_dom(ctx, NULL);
705   clist_init(&e->sons);
706   e->type = XML_NODE_ELEM;
707   e->name = xml_parse_name(ctx, ctx->pool);
708   slist_init(&e->attrs);
709
710   if (!e->parent)
711     {
712       ctx->dom = e;
713       if (ctx->doctype && strcmp(e->name, ctx->doctype))
714         xml_error(ctx, "The root element <%s> does not match the document type <%s>", e->name, ctx->doctype);
715     }
716
717   if (!ctx->dtd)
718     e->dtd = NULL;
719   else if (!(e->dtd = xml_dtd_find_elem(ctx, e->name)))
720     xml_error(ctx, "Undefined element <%s>", e->name);
721   else
722     {
723       struct xml_dtd_elem *dtd = e->dtd, *parent_dtd = e->parent ? e->parent->dtd : NULL;
724       if (dtd->type == XML_DTD_ELEM_MIXED)
725         ctx->flags &= ~XML_NO_CHARS;
726       else
727         ctx->flags |= XML_NO_CHARS;
728       if (parent_dtd)
729         if (parent_dtd->type == XML_DTD_ELEM_EMPTY)
730           xml_error(ctx, "Empty element must not contain children");
731         else if (parent_dtd->type != XML_DTD_ELEM_ANY)
732           {
733             // FIXME: validate regular expressions
734             if (!xml_validate_element(parent_dtd->node, dtd))
735               xml_error(ctx, "Unexpected element <%s>", e->name);
736           }
737     }
738
739   /* Parse attributes */
740   while (1)
741     {
742       uint white = xml_parse_white(ctx, 0);
743       uint c = xml_get_char(ctx);
744       if (c == '/')
745         {
746           xml_parse_char(ctx, '>');
747           ctx->flags |= XML_EMPTY_ELEM_TAG;
748           break;
749         }
750       else if (c == '>')
751         break;
752       else if (!white)
753         xml_fatal_expected_white(ctx);
754       xml_unget_char(ctx);
755       xml_parse_attr(ctx);
756     }
757
758   /* Resolve namespaces */
759   xml_ns_push_element(ctx);
760
761   /* Once we have namespaces, hash attribute names */
762   XML_ATTR_FOR_EACH(a, e)
763     xml_process_attr(ctx, a);
764
765   /* FIXME: DTD logic is not namespace-aware */
766   if (e->dtd)
767     {
768       XML_ATTR_FOR_EACH(a, e)
769         {
770           if (!(a->dtd = xml_dtd_find_attr(ctx, e->dtd, a->name)))
771             xml_error(ctx, "Undefined attribute %s in element <%s>", a->name, e->name);
772           else
773             xml_validate_attr(ctx, a->dtd, a->val);
774         }
775       SLIST_FOR_EACH(struct xml_dtd_attr *, a, e->dtd->attrs)
776         {
777           if (a->default_mode == XML_ATTR_REQUIRED)
778             {
779               if (!xml_attr_find(ctx, e, a->name))
780                 xml_error(ctx, "Missing required attribute %s in element <%s>", a->name, e->name);
781             }
782           else if (a->default_mode != XML_ATTR_IMPLIED && ctx->flags & XML_ALLOC_DEFAULT_ATTRS)
783             {
784               if (!xml_attr_find(ctx, e, a->name))
785                 xml_raw_add_attr(ctx, e, a->name, a->default_value);
786             }
787         }
788     }
789
790   if ((ctx->flags & XML_REPORT_TAGS) && ctx->h_stag)
791     ctx->h_stag(ctx);
792 }
793
794 static void
795 xml_pop_element(struct xml_context *ctx)
796 {
797   TRACE(ctx, "pop_element");
798   if ((ctx->flags & XML_REPORT_TAGS) && ctx->h_etag)
799     ctx->h_etag(ctx);
800
801   xml_ns_pop_element(ctx);
802
803   struct xml_node *e = ctx->node;
804   uint free = !(ctx->flags & XML_ALLOC_TAGS);
805   if (free)
806     {
807       if (!e->parent)
808         ctx->dom = NULL;
809 #if 0
810       /*
811        *  With the current data structures, freeing of attributes is not necessary,
812        *  but it might be if we switch to a global hash table of large elements.
813        */
814       SLIST_FOR_EACH(struct xml_attr *, a, e->attrs)
815         xml_attrs_remove(ctx->tab_attrs, a);
816       struct xml_node *n;
817       while (n = clist_head(&e->sons))
818         {
819           if (n->type == XML_NODE_ELEM)
820             {
821               SLIST_FOR_EACH(struct xml_attr *, a, n->attrs)
822                 xml_attrs_remove(ctx->tab_attrs, a);
823               clist_insert_list_after(&n->sons, &n->n);
824             }
825           clist_remove(&n->n);
826         }
827 #endif
828     }
829
830   xml_pop_dom(ctx, free);
831   xml_dec(ctx);
832 }
833
834 static void
835 xml_parse_etag(struct xml_context *ctx)
836 {
837  /* ETag ::= '</' Name S? '>'
838   * Already parsed: '<' */
839   struct xml_node *e = ctx->node;
840   ASSERT(e);
841   char *n = xml_node_qname(ctx, e);
842   while (*n)
843     {
844       uint c;
845       n = utf8_32_get(n, &c);
846       if (xml_get_char(ctx) != c)
847         goto recover;
848     }
849   xml_parse_white(ctx, 0);
850   if (xml_get_char(ctx) != '>')
851     {
852 recover:
853       xml_error(ctx, "Invalid ETag, expected </%s>", e->name);
854       while (xml_get_char(ctx) != '>');
855     }
856   xml_dec(ctx);
857 }
858
859 char *
860 xml_node_qname(struct xml_context *ctx UNUSED, struct xml_node *node)
861 {
862   ASSERT(node->type == XML_NODE_ELEM);
863   char *n = node->name;
864   while (n[-1] != '<')
865     n--;
866   return n;
867 }
868
869 /*** Document type declaration ***/
870
871 static void
872 xml_parse_doctype_decl(struct xml_context *ctx)
873 {
874   TRACE(ctx, "parse_doctype_decl");
875   /* doctypedecl ::= '<!DOCTYPE' S  Name (S  ExternalID)? S? ('[' intSubset ']' S?)? '>'
876    * Already parsed: '<!'
877    * Terminated before '[' or '>' */
878   if (ctx->doctype)
879     xml_fatal(ctx, "Multiple document types not allowed");
880   xml_parse_seq(ctx, "DOCTYPE");
881   xml_parse_white(ctx, 1);
882   ctx->doctype = xml_parse_name(ctx, ctx->pool);
883   TRACE(ctx, "doctype=%s", ctx->doctype);
884   uint c;
885   if (xml_parse_white(ctx, 0) && ((c = xml_peek_char(ctx)) == 'S' || c == 'P'))
886     {
887       if (c == 'S')
888         {
889           xml_parse_seq(ctx, "SYSTEM");
890           xml_parse_white(ctx, 1);
891           ctx->system_id = xml_parse_system_literal(ctx, ctx->pool);
892         }
893       else
894         {
895           xml_parse_seq(ctx, "PUBLIC");
896           xml_parse_white(ctx, 1);
897           ctx->public_id = xml_parse_pubid_literal(ctx, ctx->pool);
898           xml_parse_white(ctx, 1);
899           ctx->system_id = xml_parse_system_literal(ctx, ctx->pool);
900         }
901       xml_parse_white(ctx, 0);
902       ctx->flags |= XML_HAS_EXTERNAL_SUBSET;
903     }
904   if (xml_peek_char(ctx) == '[')
905     {
906       ctx->flags |= XML_HAS_INTERNAL_SUBSET;
907       xml_skip_char(ctx);
908       xml_inc(ctx);
909     }
910   if (ctx->h_doctype_decl)
911     ctx->h_doctype_decl(ctx);
912 }
913
914
915
916 ///////////////////////////////////////////////////////////////////////////////////////////////////////////
917
918 /* DTD: Internal subset */
919
920 static void
921 xml_parse_subset(struct xml_context *ctx, uint external)
922 {
923   // FIXME:
924   // -- comments/pi have no parent
925   // -- conditional sections in external subset
926   // -- check corectness of parameter entities
927
928   /* '[' intSubset ']'
929    * intSubset :== (markupdecl | DeclSep)
930    * Already parsed: '['
931    *
932    * extSubsetDecl ::= ( markupdecl | conditionalSect | DeclSep)*
933    */
934   while (1)
935     {
936       xml_parse_white(ctx, 0);
937       uint c = xml_get_char(ctx);
938       xml_inc(ctx);
939       if (c == '<')
940         if ((c = xml_get_char(ctx)) == '!')
941           switch (c = xml_get_char(ctx))
942             {
943               case '-':
944                 xml_push_comment(ctx);
945                 xml_pop_comment(ctx);
946                 break;
947               case 'N':
948                 xml_parse_seq(ctx, "OTATION");
949                 xml_parse_notation_decl(ctx);
950                 break;
951               case 'E':
952                 if ((c = xml_get_char(ctx)) == 'N')
953                   {
954                     xml_parse_seq(ctx, "TITY");
955                     xml_parse_entity_decl(ctx);
956                   }
957                 else if (c == 'L')
958                   {
959                     xml_parse_seq(ctx, "EMENT");
960                     xml_parse_element_decl(ctx);
961                   }
962                 else
963                   goto invalid_markup;
964                 break;
965               case 'A':
966                 xml_parse_seq(ctx, "TTLIST");
967                 xml_parse_attr_list_decl(ctx);
968                 break;
969               default:
970                 goto invalid_markup;
971             }
972         else if (c == '?')
973           {
974             xml_push_pi(ctx);
975             xml_pop_pi(ctx);
976           }
977         else
978           goto invalid_markup;
979       else if (c == '%')
980         xml_parse_pe_ref(ctx);
981       else if (c == ']' && !external)
982         {
983           break;
984         }
985       else if (c == '>' && external)
986         {
987           break;
988         }
989       else
990         goto invalid_markup;
991     }
992   xml_dec(ctx);
993   return;
994 invalid_markup: ;
995   xml_fatal(ctx, "Invalid markup in the %s subset", external ? "external" : "internal");
996 }
997
998 /*** The State Machine ***/
999
1000 uint
1001 xml_next(struct xml_context *ctx)
1002 {
1003   /* A nasty state machine */
1004
1005 #define PULL(x) do { if (ctx->pull & XML_PULL_##x) return ctx->state = XML_STATE_##x; case XML_STATE_##x: ; } while (0)
1006 #define PULL_STATE(x, s) do { if (ctx->pull & XML_PULL_##x) return ctx->state = XML_STATE_##s, XML_STATE_##x; case XML_STATE_##s: ; } while (0)
1007
1008   TRACE(ctx, "xml_next (state=%u)", ctx->state);
1009   jmp_buf throw_buf;
1010   ctx->throw_buf = &throw_buf;
1011   if (setjmp(throw_buf))
1012     {
1013 error:
1014       if (ctx->err_code == XML_ERR_EOF && ctx->h_fatal)
1015         ctx->h_fatal(ctx);
1016       TRACE(ctx, "raised fatal error");
1017       return ctx->state = XML_STATE_EOF;
1018     }
1019   uint c;
1020   switch (ctx->state)
1021     {
1022       case XML_STATE_START:
1023         TRACE(ctx, "entering prolog");
1024         ctx->flags |= XML_SRC_DOCUMENT | XML_SRC_EXPECTED_DECL;
1025         if (ctx->h_document_start)
1026           ctx->h_document_start(ctx);
1027         /* XMLDecl */
1028         xml_refill(ctx);
1029         if (ctx->h_xml_decl)
1030           ctx->h_xml_decl(ctx);
1031         PULL(XML_DECL);
1032
1033         /* Misc* (doctypedecl Misc*)? */
1034         while (1)
1035           {
1036             xml_parse_white(ctx, 0);
1037             xml_parse_char(ctx, '<');
1038             xml_inc(ctx);
1039             if ((c = xml_get_char(ctx)) == '?')
1040               /* Processing intruction */
1041               if (!(ctx->flags & XML_REPORT_PIS))
1042                 xml_skip_pi(ctx);
1043               else
1044                 {
1045                   xml_push_pi(ctx);
1046                   PULL_STATE(PI, PROLOG_PI);
1047                   xml_pop_pi(ctx);
1048                 }
1049             else if (c != '!')
1050               {
1051                 /* Found the root tag */
1052                 xml_unget_char(ctx);
1053                 goto first_tag;
1054               }
1055             else if (xml_get_char(ctx) == '-')
1056               if (!(ctx->flags & XML_REPORT_COMMENTS))
1057                 xml_skip_comment(ctx);
1058               else
1059                 {
1060                   xml_push_comment(ctx);
1061                   PULL_STATE(COMMENT, PROLOG_COMMENT);
1062                   xml_pop_comment(ctx);
1063                 }
1064             else
1065               {
1066                 /* DocTypeDecl */
1067                 xml_unget_char(ctx);
1068                 xml_parse_doctype_decl(ctx);
1069                 PULL(DOCTYPE_DECL);
1070                 if (ctx->flags & XML_HAS_DTD)
1071                   if (ctx->flags & XML_PARSE_DTD)
1072                     {
1073                       xml_dtd_init(ctx);
1074                       if (ctx->h_dtd_start)
1075                         ctx->h_dtd_start(ctx);
1076                       if (ctx->flags & XML_HAS_INTERNAL_SUBSET)
1077                         {
1078                           xml_parse_subset(ctx, 0);
1079                           xml_dec(ctx);
1080                         }
1081                       if (ctx->flags & XML_HAS_EXTERNAL_SUBSET)
1082                         {
1083                           struct xml_dtd_entity ent = {
1084                             .system_id = ctx->system_id,
1085                             .public_id = ctx->public_id,
1086                           };
1087                           xml_parse_white(ctx, 0);
1088                           xml_parse_char(ctx, '>');
1089                           xml_unget_char(ctx);
1090                           ASSERT(ctx->h_resolve_entity);
1091                           ctx->h_resolve_entity(ctx, &ent);
1092                           ctx->flags |= XML_SRC_EXPECTED_DECL;
1093                           xml_parse_subset(ctx, 1);
1094                           xml_unget_char(ctx);;
1095                         }
1096                       if (ctx->h_dtd_end)
1097                         ctx->h_dtd_end(ctx);
1098                     }
1099                   else if (ctx->flags & XML_HAS_INTERNAL_SUBSET)
1100                     xml_skip_internal_subset(ctx);
1101                 xml_parse_white(ctx, 0);
1102                 xml_parse_char(ctx, '>');
1103                 xml_dec(ctx);
1104               }
1105           }
1106
1107       case XML_STATE_CHARS:
1108
1109         while (1)
1110           {
1111             if (xml_peek_char(ctx) != '<')
1112               {
1113                 /* CharData */
1114                 xml_append_chars(ctx);
1115                 continue;
1116               }
1117             else
1118               xml_skip_char(ctx);
1119             xml_inc(ctx);
1120 first_tag:
1121
1122             if ((c = xml_get_char(ctx)) == '?')
1123               {
1124                 /* PI */
1125                 if (!(ctx->flags & (XML_REPORT_PIS | XML_ALLOC_PIS)))
1126                   xml_skip_pi(ctx);
1127                 else
1128                   {
1129                     if (xml_flush_chars(ctx))
1130                       {
1131                         PULL_STATE(CHARS, CHARS_BEFORE_PI);
1132                         xml_pop_chars(ctx);
1133                       }
1134                     xml_push_pi(ctx);
1135                     PULL(PI);
1136                     xml_pop_pi(ctx);
1137                   }
1138               }
1139
1140             else if (c == '!')
1141               if ((c = xml_get_char(ctx)) == '-')
1142                 {
1143                   /* Comment */
1144                   if (!(ctx->flags & (XML_REPORT_COMMENTS | XML_ALLOC_COMMENTS)))
1145                     xml_skip_comment(ctx);
1146                   else
1147                     {
1148                       if (xml_flush_chars(ctx))
1149                         {
1150                           PULL_STATE(CHARS, CHARS_BEFORE_COMMENT);
1151                           xml_pop_chars(ctx);
1152                         }
1153                       xml_push_comment(ctx);
1154                       PULL(COMMENT);
1155                       xml_pop_comment(ctx);
1156                     }
1157                 }
1158               else if (c == '[')
1159                 {
1160                   /* CDATA */
1161                   xml_append_cdata(ctx);
1162                 }
1163               else
1164                 xml_fatal(ctx, "Unexpected character after '<!'");
1165
1166             else if (c != '/')
1167               {
1168                 /* STag | EmptyElemTag */
1169                 xml_unget_char(ctx);
1170                 if (xml_flush_chars(ctx))
1171                   {
1172                     PULL_STATE(CHARS, CHARS_BEFORE_STAG);
1173                     xml_pop_chars(ctx);
1174                   }
1175
1176                 xml_push_element(ctx);
1177                 PULL(STAG);
1178                 if (ctx->flags & XML_EMPTY_ELEM_TAG)
1179                   goto pop_element;
1180               }
1181
1182             else
1183               {
1184                 /* ETag */
1185                 if (xml_flush_chars(ctx))
1186                   {
1187                     PULL_STATE(CHARS, CHARS_BEFORE_ETAG);
1188                     xml_pop_chars(ctx);
1189                   }
1190
1191                 xml_parse_etag(ctx);
1192 pop_element:
1193                 PULL(ETAG);
1194                 xml_pop_element(ctx);
1195                 if (!ctx->node)
1196                   goto epilog;
1197               }
1198           }
1199
1200 epilog:
1201         /* Misc* */
1202         TRACE(ctx, "entering epilog");
1203         while (1)
1204           {
1205             /* Epilog whitespace is the only place, where a valid document can reach EOF */
1206             if (setjmp(throw_buf))
1207               if (ctx->err_code == XML_ERR_EOF)
1208                 {
1209                   TRACE(ctx, "reached EOF");
1210                   ctx->state = XML_STATE_EOF;
1211                   if (ctx->h_document_end)
1212                     ctx->h_document_end(ctx);
1213       case XML_STATE_EOF:
1214                   ctx->err_code = 0;
1215                   ctx->err_msg = NULL;
1216                   return XML_STATE_EOF;
1217                 }
1218               else
1219                 goto error;
1220             xml_parse_white(ctx, 0);
1221             if (setjmp(throw_buf))
1222               goto error;
1223
1224             /* Misc */
1225             xml_parse_char(ctx, '<');
1226             xml_inc(ctx);
1227             if ((c = xml_get_char(ctx)) == '?')
1228               /* Processing instruction */
1229               if (!(ctx->flags & XML_REPORT_PIS))
1230                 xml_skip_pi(ctx);
1231               else
1232                 {
1233                   xml_push_pi(ctx);
1234                   PULL_STATE(PI, EPILOG_PI);
1235                   xml_pop_pi(ctx);
1236                 }
1237             else if (c == '!')
1238               {
1239                 xml_parse_char(ctx, '-');
1240                 /* Comment */
1241                 if (!(ctx->flags & XML_REPORT_COMMENTS))
1242                   xml_skip_comment(ctx);
1243                 else
1244                   {
1245                     xml_push_comment(ctx);
1246                     PULL_STATE(COMMENT, EPILOG_COMMENT);
1247                     xml_pop_comment(ctx);
1248                   }
1249               }
1250             else
1251               xml_fatal(ctx, "Syntax error in the epilog");
1252           }
1253
1254     }
1255   ASSERT(0);
1256 }
1257
1258 uint
1259 xml_next_state(struct xml_context *ctx, uint pull)
1260 {
1261   uint saved = ctx->pull;
1262   ctx->pull = pull;
1263   uint res = xml_next(ctx);
1264   ctx->pull = saved;
1265   return res;
1266 }
1267
1268 uint
1269 xml_skip_element(struct xml_context *ctx)
1270 {
1271   ASSERT(ctx->state == XML_STATE_STAG);
1272   struct xml_node *node = ctx->node;
1273   uint saved = ctx->pull, res;
1274   ctx->pull = XML_PULL_ETAG;
1275   while ((res = xml_next(ctx)) && ctx->node != node);
1276   ctx->pull = saved;
1277   return res;
1278 }
1279
1280 uint
1281 xml_parse(struct xml_context *ctx)
1282 {
1283   /* This cycle should run only once unless the user overrides the value of ctx->pull in a SAX handler */
1284   do
1285     {
1286       ctx->pull = 0;
1287     }
1288   while (xml_next(ctx));
1289   return ctx->err_code;
1290 }
1291
1292 char *
1293 xml_merge_chars(struct xml_context *ctx UNUSED, struct xml_node *node, struct mempool *pool)
1294 {
1295   ASSERT(node->type == XML_NODE_ELEM);
1296   char *p = mp_start_noalign(pool, 1);
1297   XML_NODE_FOR_EACH(son, node)
1298     if (son->type == XML_NODE_CHARS)
1299       {
1300         p = mp_spread(pool, p, son->len + 1);
1301         memcpy(p, son->text, son->len);
1302         p += son->len;
1303       }
1304   *p++ = 0;
1305   return mp_end(pool, p);
1306 }
1307
1308 static char *
1309 xml_append_dom_chars(char *p, struct mempool *pool, struct xml_node *node)
1310 {
1311   XML_NODE_FOR_EACH(son, node)
1312     if (son->type == XML_NODE_CHARS)
1313       {
1314         p = mp_spread(pool, p, son->len + 1);
1315         memcpy(p, son->text, son->len);
1316         p += son->len;
1317       }
1318     else if (son->type == XML_NODE_ELEM)
1319       p = xml_append_dom_chars(p, pool, son);
1320   return p;
1321 }
1322
1323 char *
1324 xml_merge_dom_chars(struct xml_context *ctx UNUSED, struct xml_node *node, struct mempool *pool)
1325 {
1326   ASSERT(node->type == XML_NODE_ELEM);
1327   char *p = mp_start_noalign(pool, 1);
1328   p = xml_append_dom_chars(p, pool, node);
1329   *p++ = 0;
1330   return mp_end(pool, p);
1331 }