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