]> mj.ucw.cz Git - libucw.git/blob - sherlock/xml/parse.c
6f2e7e008bccd5ca987438f5167c15b40f6317cf
[libucw.git] / sherlock / xml / parse.c
1 /*
2  *      Sherlock Library -- A simple XML parser
3  *
4  *      (c) 2007 Pavel Charvat <pchar@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #define LOCAL_DEBUG
11
12 #include "sherlock/sherlock.h"
13 #include "sherlock/xml/xml.h"
14 #include "sherlock/xml/dtd.h"
15 #include "sherlock/xml/common.h"
16 #include "lib/fastbuf.h"
17 #include "lib/ff-unicode.h"
18 #include "lib/unicode.h"
19 #include "lib/chartype.h"
20 #include "lib/hashfunc.h"
21
22 #include <setjmp.h>
23
24 /*** Comments ***/
25
26 void
27 xml_push_comment(struct xml_context *ctx)
28 {
29   TRACE(ctx, "push_comment");
30   /* Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
31    * Already parsed: '<!-' */
32   xml_parse_char(ctx, '-');
33   struct xml_node *n = xml_push_dom(ctx);
34   n->type = XML_NODE_COMMENT;
35   char *p = mp_start_noalign(ctx->pool, 6);
36   while (1)
37     {
38       if (xml_get_char(ctx) == '-')
39         if (xml_get_char(ctx) == '-')
40           break;
41         else
42           *p++ = '-';
43       p = utf8_32_put(p, xml_last_char(ctx));
44       p = mp_spread(ctx->pool, p, 6);
45     }
46   xml_parse_char(ctx, '>');
47   *p = 0;
48   n->len = p - (char *)mp_ptr(ctx->pool);
49   n->text = mp_end(ctx->pool, p + 1);
50   if (ctx->h_comment)
51     ctx->h_comment(ctx);
52 }
53
54 void
55 xml_pop_comment(struct xml_context *ctx)
56 {
57   xml_pop_dom(ctx);
58   xml_dec(ctx);
59   TRACE(ctx, "pop_comment");
60 }
61
62 void
63 xml_skip_comment(struct xml_context *ctx)
64 {
65   TRACE(ctx, "skip_comment");
66   xml_parse_char(ctx, '-');
67   while (xml_get_char(ctx) != '-' || xml_get_char(ctx) != '-');
68   xml_parse_char(ctx, '>');
69   xml_dec(ctx);
70 }
71
72 /*** Processing instructions ***/
73
74 void
75 xml_push_pi(struct xml_context *ctx)
76 {
77   TRACE(ctx, "push_pi");
78   /* Parses a PI to ctx->value and ctx->name:
79    *   PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
80    *   PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
81    * Already parsed: '<?' */
82   struct xml_node *n = xml_push_dom(ctx);
83   n->type = XML_NODE_PI;
84   n->name = xml_parse_name(ctx, ctx->pool);
85   if (unlikely(!strcasecmp(n->name, "xml")))
86     xml_error(ctx, "Reserved PI target");
87   char *p = mp_start_noalign(ctx->pool, 5);
88   if (!xml_parse_white(ctx, 0))
89     xml_parse_seq(ctx, "?>");
90   else
91     while (1)
92       {
93         if (xml_get_char(ctx) == '?')
94           if (xml_peek_char(ctx) == '>')
95             {
96               xml_skip_char(ctx);
97               break;
98             }
99           else
100             *p++ = '?';
101         else
102           p = utf8_32_put(p, xml_last_char(ctx));
103         p = mp_spread(ctx->pool, p, 5);
104       }
105   *p = 0;
106   n->len = p - (char *)mp_ptr(ctx->pool);
107   n->text = mp_end(ctx->pool, p + 1);
108   if (ctx->h_pi)
109     ctx->h_pi(ctx);
110 }
111
112 void
113 xml_pop_pi(struct xml_context *ctx)
114 {
115   xml_pop_dom(ctx);
116   xml_dec(ctx);
117   TRACE(ctx, "pop_pi");
118 }
119
120 void
121 xml_skip_pi(struct xml_context *ctx)
122 {
123   TRACE(ctx, "skip_pi");
124   if (ctx->flags & XML_FLAG_VALIDATING)
125     {
126       struct mempool_state state;
127       mp_save(ctx->stack, &state);
128       if (unlikely(!strcasecmp(xml_parse_name(ctx, ctx->stack), "xml")))
129         xml_error(ctx, "Reserved PI target");
130       mp_restore(ctx->stack, &state);
131       if (!xml_parse_white(ctx, 0))
132         {
133           xml_parse_seq(ctx, "?>");
134           xml_dec(ctx);
135           return;
136         }
137     }
138   while (1)
139     if (xml_get_char(ctx) == '?')
140       if (xml_peek_char(ctx) == '>')
141         break;
142   xml_skip_char(ctx);
143   xml_dec(ctx);
144 }
145
146 /*** Character data ***/
147
148 static void
149 xml_chars_spout(struct fastbuf *fb)
150 {
151   if (fb->bptr >= fb->bufend)
152     {
153       struct xml_context *ctx = SKIP_BACK(struct xml_context, chars, fb);
154       struct mempool *pool = ctx->pool;
155       if (fb->bufend != fb->buffer)
156         {
157           uns len = fb->bufend - fb->buffer;
158           TRACE(ctx, "grow_chars");
159           fb->buffer = mp_expand(pool);
160           fb->bufend = fb->buffer + mp_avail(pool);
161           fb->bstop = fb->buffer;
162           fb->bptr = fb->buffer + len;
163         }
164       else
165         {
166           TRACE(ctx, "push_chars");
167           struct xml_node *n = xml_push_dom(ctx);
168           n->type = XML_NODE_CDATA;
169           xml_start_chars(ctx);
170         }
171     }
172 }
173
174 static void
175 xml_init_chars(struct xml_context *ctx)
176 {
177   struct fastbuf *fb = &ctx->chars;
178   fb->name = "<xml-chars>";
179   fb->spout = xml_chars_spout;
180   fb->can_overwrite_buffer = 1;
181   fb->bptr = fb->bstop = fb->buffer = fb->bufend = NULL;
182 }
183
184 static inline uns
185 xml_flush_chars(struct xml_context *ctx)
186 {
187   struct fastbuf *fb = &ctx->chars;
188   if (fb->bufend == fb->buffer)
189     return 0;
190   TRACE(ctx, "flush_chars");
191   struct xml_node *n = ctx->node;
192   n->text = xml_end_chars(ctx, &n->len);
193   n->len = fb->bufend - fb->buffer;
194   if (ctx->h_chars)
195     ctx->h_chars(ctx);
196   return 1;
197 }
198
199 static inline void
200 xml_pop_chars(struct xml_context *ctx)
201 {
202   xml_pop_dom(ctx);
203   TRACE(ctx, "pop_chars");
204 }
205
206 static inline void
207 xml_append_chars(struct xml_context *ctx)
208 {
209   TRACE(ctx, "append_chars");
210   struct fastbuf *out = &ctx->chars;
211   while (xml_get_char(ctx) != '<')
212     if (xml_last_char(ctx) == '&')
213       {
214         xml_inc(ctx);
215         xml_parse_ref(ctx);
216       }
217     else
218       bput_utf8_32(out, xml_last_char(ctx));
219   xml_unget_char(ctx);
220 }
221
222 /*** CDATA sections ***/
223
224 static void
225 xml_push_cdata(struct xml_context *ctx)
226 {
227   TRACE(ctx, "push_cdata");
228   /* CDSect :== '<![CDATA[' (Char* - (Char* ']]>' Char*)) ']]>'
229    * Already parsed: '<![' */
230   xml_parse_seq(ctx, "CDATA[");
231   struct xml_node *n = xml_push_dom(ctx);
232   n->type = XML_NODE_CDATA;
233   char *p = mp_start_noalign(ctx->pool, 7);
234   while (1)
235     {
236       if (xml_get_char(ctx) == ']')
237         {
238           if (xml_get_char(ctx) == ']')
239             if (xml_get_char(ctx) == '>')
240               break;
241             else
242               *p++ = ']';
243           *p++ = ']';
244         }
245       p = utf8_32_put(p, xml_last_char(ctx));
246       p = mp_spread(ctx->pool, p, 7);
247     }
248   *p = 0;
249   n->len = p - (char *)mp_ptr(ctx->pool);
250   n->text = mp_end(ctx->pool, p + 1);
251   if (ctx->h_cdata)
252     ctx->h_cdata(ctx);
253 }
254
255 static void
256 xml_pop_cdata(struct xml_context *ctx)
257 {
258   xml_pop_dom(ctx);
259   xml_dec(ctx);
260   TRACE(ctx, "pop_cdata");
261 }
262
263 static void
264 xml_append_cdata(struct xml_context *ctx)
265 {
266   TRACE(ctx, "append_cdata");
267   xml_parse_seq(ctx, "CDATA[");
268   struct fastbuf *out = &ctx->chars;
269   while (1)
270     {
271       if (xml_get_char(ctx) == ']')
272         {
273           if (xml_get_char(ctx) == ']')
274             if (xml_get_char(ctx) == '>')
275               break;
276             else
277               bputc(out, ']');
278           bputc(out, ']');
279         }
280       bput_utf8_32(out, xml_last_char(ctx));
281     }
282   xml_dec(ctx);
283 }
284
285 static void UNUSED
286 xml_skip_cdata(struct xml_context *ctx)
287 {
288   TRACE(ctx, "skip_cdata");
289   xml_parse_seq(ctx, "CDATA[");
290   while (xml_get_char(ctx) != ']' || xml_get_char(ctx) != ']' || xml_get_char(ctx) != '>');
291   xml_dec(ctx);
292 }
293
294 /*** Character references ***/
295
296 uns
297 xml_parse_char_ref(struct xml_context *ctx)
298 {
299   TRACE(ctx, "parse_char_ref");
300   /* CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
301    * Already parsed: '&#' */
302   uns v = 0;
303   if (xml_get_char(ctx) == 'x')
304     {
305       if (!(xml_get_cat(ctx) & XML_CHAR_XDIGIT))
306         {
307           xml_error(ctx, "Expected a hexadecimal value of character reference");
308           goto recover;
309         }
310       do
311         {
312           v = (v << 4) + Cxvalue(xml_last_char(ctx));
313         }
314       while (v < 0x110000 && (xml_get_cat(ctx) & XML_CHAR_XDIGIT));
315     }
316   else
317     {
318       if (!(xml_last_cat(ctx) & XML_CHAR_DIGIT))
319         {
320           xml_error(ctx, "Expected a numeric value of character reference");
321           goto recover;
322         }
323       do
324         {
325           v = v * 10 + xml_last_char(ctx) - '0';
326         }
327       while (v < 0x110000 && (xml_get_cat(ctx) & XML_CHAR_DIGIT));
328     }
329   uns cat = xml_char_cat(v);
330   if (!(cat & XML_CHAR_UNRESTRICTED_1_1) && ((ctx->flags & XML_FLAG_VERSION_1_1) || !(cat & XML_CHAR_VALID_1_0)))
331     {
332       xml_error(ctx, "Character reference out of range");
333       goto recover;
334     }
335   if (xml_last_char(ctx) == ';')
336     {
337       xml_dec(ctx);
338       return v;
339     }
340   xml_error(ctx, "Expected ';'");
341 recover:
342   while (xml_last_char(ctx) != ';')
343     xml_get_char(ctx);
344   xml_dec(ctx);
345   return UNI_REPLACEMENT;
346 }
347
348 /*** References to general entities ***/
349
350 void
351 xml_parse_ref(struct xml_context *ctx)
352 {
353   /* Reference ::= EntityRef | CharRef
354    * EntityRef ::= '&' Name ';'
355    * Already parsed: '&' */
356   struct fastbuf *out = &ctx->chars;
357   if (xml_peek_char(ctx) == '#')
358     {
359       xml_skip_char(ctx);
360       bput_utf8_32(out, xml_parse_char_ref(ctx));
361     }
362   else
363     {
364       TRACE(ctx, "parse_ge_ref");
365       struct mempool_state state;
366       mp_save(ctx->stack, &state);
367       char *name = xml_parse_name(ctx, ctx->stack);
368       xml_parse_char(ctx, ';');
369       struct xml_dtd_ent *ent = xml_dtd_find_gent(ctx, name);
370       if (!ent)
371         {
372           xml_error(ctx, "Unknown entity &%s;", name);
373           bputc(out, '&');
374           bputs(out, name);
375           bputc(out, ';');
376         }
377       else if (ent->flags & XML_DTD_ENT_TRIVIAL)
378         {
379           TRACE(ctx, "Trivial entity &%s;", name);
380           bwrite(out, ent->text, ent->len);
381         }
382       else
383         {
384           TRACE(ctx, "Pushed entity &%s;", name);
385           mp_restore(ctx->stack, &state);
386           xml_dec(ctx);
387           xml_push_entity(ctx, ent);
388           return;
389         }
390       mp_restore(ctx->stack, &state);
391       xml_dec(ctx);
392     }
393 }
394
395 /*** Attribute values ***/
396
397 char *
398 xml_parse_attr_value(struct xml_context *ctx, struct xml_dtd_attr *attr UNUSED)
399 {
400   TRACE(ctx, "parse_attr_value");
401   /* AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'" */
402   /* FIXME:
403    * -- copying from ctx->chars to ctx->pool is not necessary, we could directly write to ctx->pool
404    * -- berare quotes inside parased entities
405    * -- check value constrains / normalize value */
406   struct mempool_state state;
407   uns quote = xml_parse_quote(ctx);
408   mp_save(ctx->stack, &state);
409   xml_start_chars(ctx);
410   struct fastbuf *out = &ctx->chars;
411   while (1)
412     {
413       uns c = xml_get_char(ctx);
414       if (c == '&')
415         {
416           xml_inc(ctx);
417           xml_parse_ref(ctx);
418         }
419       else if (c == quote) // FIXME: beware quotes inside parsed entities
420         break;
421       else if (c == '<')
422         xml_error(ctx, "Attribute value must not contain '<'");
423       else if (xml_last_cat(ctx) & XML_CHAR_WHITE)
424         bputc(out, ' ');
425       else
426         bput_utf8_32(out, c);
427     }
428   mp_restore(ctx->stack, &state);
429   uns len;
430   return xml_end_chars(ctx, &len);
431 }
432
433 /*** Attributes ***/
434
435 struct xml_attrs_table;
436
437 static inline uns
438 xml_attrs_hash(struct xml_attrs_table *t UNUSED, struct xml_node *e, char *n)
439 {
440   return hash_pointer(e) ^ hash_string(n);
441 }
442
443 static inline int
444 xml_attrs_eq(struct xml_attrs_table *t UNUSED, struct xml_node *e1, char *n1, struct xml_node *e2, char *n2)
445 {
446   return (e1 == e2) && !strcmp(n1, n2);
447 }
448
449 static inline void
450 xml_attrs_init_key(struct xml_attrs_table *t UNUSED, struct xml_attr *a, struct xml_node *e, char *name)
451 {
452   a->elem = e;
453   a->name = name;
454   a->val = NULL;
455   slist_add_tail(&e->attrs, &a->n);
456 }
457
458 #define HASH_PREFIX(x) xml_attrs_##x
459 #define HASH_NODE struct xml_attr
460 #define HASH_KEY_COMPLEX(x) x elem, x name
461 #define HASH_KEY_DECL struct xml_node *elem, char *name
462 #define HASH_TABLE_DYNAMIC
463 #define HASH_GIVE_EQ
464 #define HASH_GIVE_HASHFN
465 #define HASH_GIVE_INIT_KEY
466 #define HASH_WANT_CLEANUP
467 #define HASH_WANT_REMOVE
468 #define HASH_WANT_LOOKUP
469 #define HASH_WANT_FIND
470 #define HASH_GIVE_ALLOC
471 XML_HASH_GIVE_ALLOC
472 #include "lib/hashtable.h"
473
474 static void
475 xml_parse_attr(struct xml_context *ctx)
476 {
477   TRACE(ctx, "parse_attr");
478   /* Attribute ::= Name Eq AttValue */
479   /* FIXME:
480    * -- memory management
481    * -- DTD */
482   struct xml_node *e = ctx->node;
483   char *n = xml_parse_name(ctx, ctx->pool);
484   struct xml_attr *a = xml_attrs_lookup(ctx->tab_attrs, e, n);
485   xml_parse_eq(ctx);
486   char *v = xml_parse_attr_value(ctx, NULL);
487   if (a->val)
488     xml_error(ctx, "Attribute %s is not unique", n);
489   else
490     a->val = v;
491 }
492
493 /*** Elements ***/
494
495 static void
496 xml_push_element(struct xml_context *ctx)
497 {
498   TRACE(ctx, "push_element");
499   /* EmptyElemTag | STag
500    * EmptyElemTag ::= '<' Name (S  Attribute)* S? '/>'
501    * STag ::= '<' Name (S  Attribute)* S? '>'
502    * Already parsed: '<' */
503   struct xml_node *e = xml_push_dom(ctx);
504   clist_init(&e->sons);
505   e->type = XML_NODE_ELEM;
506   e->name = xml_parse_name(ctx, ctx->pool);
507   slist_init(&e->attrs);
508   if (!e->parent)
509     {
510       ctx->root = e;
511       if (ctx->document_type && strcmp(e->name, ctx->document_type))
512         xml_error(ctx, "The root element %s does not match the document type %s", e->name, ctx->document_type);
513     }
514   while (1)
515     {
516       uns white = xml_parse_white(ctx, 0);
517       uns c = xml_get_char(ctx);
518       if (c == '/')
519         {
520           xml_parse_char(ctx, '>');
521           ctx->flags |= XML_FLAG_EMPTY_ELEM;
522           break;
523         }
524       else if (c == '>')
525         break;
526       else if (!white)
527         xml_fatal_expected_white(ctx);
528       xml_unget_char(ctx);
529       xml_parse_attr(ctx);
530     }
531   if (ctx->h_element_start)
532     ctx->h_element_start(ctx);
533 }
534
535 static void
536 xml_pop_element(struct xml_context *ctx)
537 {
538   TRACE(ctx, "pop_element");
539   if (ctx->h_element_end)
540     ctx->h_element_end(ctx);
541   struct xml_node *e = ctx->node;
542   if (ctx->flags & XML_DOM_FREE)
543     {
544       if (!e->parent)
545         ctx->root = NULL;
546       /* Restore hash table of attributes */
547       SLIST_FOR_EACH(struct xml_attr *, a, e->attrs)
548         xml_attrs_remove(ctx->tab_attrs, a);
549       struct xml_node *n;
550       while (n = clist_head(&e->sons))
551         {
552           if (n->type == XML_NODE_ELEM)
553             {
554               SLIST_FOR_EACH(struct xml_attr *, a, n->attrs)
555                 xml_attrs_remove(ctx->tab_attrs, a);
556               clist_insert_list_after(&n->sons, &n->n);
557             }
558           clist_remove(&n->n);
559         }
560     }
561   xml_pop_dom(ctx);
562   xml_dec(ctx);
563 }
564
565 static void
566 xml_parse_etag(struct xml_context *ctx)
567 {
568  /* ETag ::= '</' Name S? '>'
569   * Already parsed: '<' */
570   struct xml_node *e = ctx->node;
571   ASSERT(e);
572   char *n = e->name;
573   while (*n)
574     {
575       uns c;
576       n = utf8_32_get(n, &c);
577       if (xml_get_char(ctx) != c)
578         goto recover;
579     }
580   xml_parse_white(ctx, 0);
581   if (xml_get_char(ctx) != '>')
582     {
583 recover:
584       xml_error(ctx, "Invalid ETag, expected </%s>", e->name);
585       while (xml_get_char(ctx) != '>');
586     }
587   xml_dec(ctx);
588 }
589
590 /*** Document type declaration ***/
591
592 static void
593 xml_parse_doctype_decl(struct xml_context *ctx)
594 {
595   TRACE(ctx, "parse_doctype_decl");
596   /* doctypedecl ::= '<!DOCTYPE' S  Name (S  ExternalID)? S? ('[' intSubset ']' S?)? '>'
597    * Already parsed: '<!'
598    * Terminated before '[' or '>' */
599   if (ctx->document_type)
600     xml_fatal(ctx, "Multiple document types not allowed");
601   xml_parse_seq(ctx, "DOCTYPE");
602   xml_parse_white(ctx, 1);
603   ctx->document_type = xml_parse_name(ctx, ctx->pool);
604   TRACE(ctx, "doctyype=%s", ctx->document_type);
605   uns c;
606   if (xml_parse_white(ctx, 0) && ((c = xml_peek_char(ctx)) == 'S' || c == 'P'))
607     {
608       if (c == 'S')
609         {
610           xml_parse_seq(ctx, "SYSTEM");
611           xml_parse_white(ctx, 1);
612           ctx->eid.system_id = xml_parse_system_literal(ctx, ctx->pool);
613         }
614       else
615         {
616           xml_parse_seq(ctx, "PUBLIC");
617           xml_parse_white(ctx, 1);
618           ctx->eid.public_id = xml_parse_pubid_literal(ctx, ctx->pool);
619           xml_parse_white(ctx, 1);
620           ctx->eid.system_id = xml_parse_system_literal(ctx, ctx->pool);
621         }
622       xml_parse_white(ctx, 0);
623       ctx->flags |= XML_FLAG_HAS_EXTERNAL_SUBSET;
624     }
625   if (xml_peek_char(ctx) == '[')
626     ctx->flags |= XML_FLAG_HAS_INTERNAL_SUBSET;
627   if (ctx->h_doctype_decl)
628     ctx->h_doctype_decl(ctx);
629 }
630
631
632
633 ///////////////////////////////////////////////////////////////////////////////////////////////////////////
634
635 /* DTD: Internal subset */
636
637 static void
638 xml_parse_internal_subset(struct xml_context *ctx)
639 {
640   // FIXME: comments/pi have no parent
641   /* '[' intSubset ']'
642    * intSubset :== (markupdecl | DeclSep)
643    * Already parsed: ']' */
644   while (1)
645     {
646       xml_parse_white(ctx, 0);
647       uns c = xml_get_char(ctx);
648       xml_inc(ctx);
649       if (c == '<')
650         if ((c = xml_get_char(ctx)) == '!')
651           switch (c = xml_get_char(ctx))
652             {
653               case '-':
654                 xml_push_comment(ctx);
655                 xml_pop_comment(ctx);
656                 break;
657               case 'N':
658                 xml_parse_seq(ctx, "OTATION");
659                 xml_parse_notation_decl(ctx);
660                 break;
661               case 'E':
662                 if ((c = xml_get_char(ctx)) == 'N')
663                   {
664                     xml_parse_seq(ctx, "TITY");
665                     xml_parse_entity_decl(ctx);
666                   }
667                 else if (c == 'L')
668                   {
669                     xml_parse_seq(ctx, "EMENT");
670                     xml_parse_element_decl(ctx);
671                   }
672                 else
673                   goto invalid_markup;
674                 break;
675               case 'A':
676                 xml_parse_seq(ctx, "TTLIST");
677                 xml_parse_attr_list_decl(ctx);
678                 break;
679               default:
680                 goto invalid_markup;
681             }
682         else if (c == '?')
683           {
684             xml_push_pi(ctx);
685             xml_pop_pi(ctx);
686           }
687         else
688           goto invalid_markup;
689       else if (c == '%')
690         xml_parse_pe_ref(ctx);
691       else if (c == ']')
692         break;
693       else
694         goto invalid_markup;
695     }
696   xml_dec(ctx);
697   xml_dec(ctx);
698   return;
699 invalid_markup:
700   xml_fatal(ctx, "Invalid markup in the internal subset");
701 }
702
703
704 /*----------------------------------------------*/
705
706 void
707 xml_init(struct xml_context *ctx)
708 {
709   bzero(ctx, sizeof(*ctx));
710   ctx->pool = mp_new(65536);
711   ctx->stack = mp_new(65536);
712   ctx->flags = XML_DOM_FREE;
713   xml_init_chars(ctx);
714   xml_dtd_init(ctx);
715   xml_attrs_init(ctx->tab_attrs = xml_hash_new(ctx->pool, sizeof(struct xml_attrs_table)));
716 }
717
718 void
719 xml_cleanup(struct xml_context *ctx)
720 {
721   xml_attrs_cleanup(ctx->tab_attrs);
722   xml_dtd_cleanup(ctx);
723   mp_delete(ctx->pool);
724   mp_delete(ctx->stack);
725 }
726
727 int
728 xml_next(struct xml_context *ctx)
729 {
730   /* A nasty state machine */
731
732   TRACE(ctx, "xml_next (state=%u)", ctx->state);
733   jmp_buf throw_buf;
734   ctx->throw_buf = &throw_buf;
735   if (setjmp(throw_buf))
736     {
737 error:
738       if (ctx->err_code == XML_ERR_EOF && ctx->h_fatal)
739         ctx->h_fatal(ctx);
740       ctx->state = XML_STATE_FATAL;
741       TRACE(ctx, "raised fatal error");
742       return -1;
743     }
744   uns c;
745   switch (ctx->state)
746     {
747       case XML_STATE_FATAL:
748         return -1;
749
750       case XML_STATE_START:
751         TRACE(ctx, "entering prolog");
752         if (ctx->h_document_start)
753           ctx->h_document_start(ctx);
754         /* XMLDecl */
755         xml_refill(ctx);
756         if (ctx->h_xml_decl)
757           ctx->h_xml_decl(ctx);
758         if (ctx->want & XML_WANT_DECL)
759           return ctx->state = XML_STATE_DECL;
760       case XML_STATE_DECL:
761
762         /* Misc* (doctypedecl Misc*)? */
763         while (1)
764           {
765             xml_parse_white(ctx, 0);
766             xml_parse_char(ctx, '<');
767             if ((c = xml_get_char(ctx)) == '?')
768               /* Processing intruction */
769               if (!(ctx->want & XML_WANT_PI))
770                 xml_skip_pi(ctx);
771               else
772                 {
773                   xml_push_pi(ctx);
774                   ctx->state = XML_STATE_PROLOG_PI;
775                   return XML_STATE_PI;
776       case XML_STATE_PROLOG_PI:
777                   xml_pop_pi(ctx);
778                 }
779             else if (c != '!')
780               {
781                 /* Found the root tag */
782                 xml_unget_char(ctx);
783                 goto first_tag;
784               }
785             else if (xml_get_char(ctx) == '-')
786               if (!(ctx->want & XML_WANT_COMMENT))
787                 xml_skip_comment(ctx);
788               else
789                 {
790                   xml_push_comment(ctx);
791                   ctx->state = XML_STATE_PROLOG_COMMENT;
792                   return XML_STATE_COMMENT;
793       case XML_STATE_PROLOG_COMMENT:
794                   xml_pop_comment(ctx);
795                 }
796             else
797               {
798                 /* DocTypeDecl */
799                 xml_unget_char(ctx);
800                 xml_parse_doctype_decl(ctx);
801                 if (ctx->want & XML_WANT_DOCUMENT_TYPE)
802                   return ctx->state = XML_STATE_DOCUMENT_TYPE;
803       case XML_STATE_DOCUMENT_TYPE:
804                 if (xml_peek_char(ctx) == '[')
805                   {
806                     xml_skip_char(ctx);
807                     xml_inc(ctx);
808                     xml_parse_internal_subset(ctx);
809                     xml_parse_white(ctx, 0);
810                   }
811                 xml_parse_char(ctx, '>');
812               }
813           }
814
815       case XML_STATE_CHARS:
816
817         while (1)
818           {
819             if (xml_peek_char(ctx) != '<')
820               {
821                 /* CharData */
822                 xml_append_chars(ctx);
823                 continue;
824               }
825             else
826               xml_skip_char(ctx);
827 first_tag: ;
828
829             xml_inc(ctx);
830             if ((c = xml_get_char(ctx)) == '?')
831               {
832                 /* PI */
833                 if (!(ctx->want & XML_WANT_PI))
834                   xml_skip_pi(ctx);
835                 else
836                   {
837                     if (xml_flush_chars(ctx))
838                       {
839                         if (ctx->want & XML_WANT_CHARS)
840                           {
841                             ctx->state = XML_STATE_CHARS_BEFORE_PI;
842                             return XML_STATE_CHARS;
843                           }
844       case XML_STATE_CHARS_BEFORE_PI:
845                         xml_pop_chars(ctx);
846                       }
847                     xml_push_pi(ctx);
848                     return ctx->state = XML_STATE_PI;
849       case XML_STATE_PI:
850                     xml_pop_pi(ctx);
851                   }
852               }
853
854             else if (c == '!')
855               if ((c = xml_get_char(ctx)) == '-')
856                 {
857                   /* Comment */
858                   if (!(ctx->want & XML_WANT_COMMENT))
859                     xml_skip_comment(ctx);
860                   else
861                     {
862                       if (xml_flush_chars(ctx))
863                         {
864                           if (ctx->want & XML_WANT_CHARS)
865                             {
866                               ctx->state = XML_STATE_CHARS_BEFORE_COMMENT;
867                               return XML_STATE_CHARS;
868                             }
869       case XML_STATE_CHARS_BEFORE_COMMENT:
870                           xml_pop_chars(ctx);
871                         }
872                       xml_push_comment(ctx);
873                       return ctx->state = XML_STATE_COMMENT;
874       case XML_STATE_COMMENT:
875                       xml_pop_comment(ctx);
876                     }
877                 }
878               else if (c == '[')
879                 {
880                   /* CDATA */
881                   if (!(ctx->want & XML_WANT_CDATA))
882                     xml_append_cdata(ctx);
883                   else
884                     {
885                       if (xml_flush_chars(ctx))
886                         {
887                           if (ctx->want & XML_WANT_CHARS)
888                             {
889                               ctx->state = XML_STATE_CHARS_BEFORE_CDATA;
890                               return XML_STATE_CHARS;
891                             }
892       case XML_STATE_CHARS_BEFORE_CDATA:
893                           xml_pop_chars(ctx);
894                         }
895                       xml_push_cdata(ctx);
896                       return ctx->state = XML_STATE_CDATA;
897       case XML_STATE_CDATA:
898                       xml_pop_cdata(ctx);
899                     }
900                 }
901               else
902                 xml_fatal(ctx, "Unexpected character after '<!'");
903
904             else if (c != '/')
905               {
906                 /* STag | EmptyElemTag */
907                 xml_unget_char(ctx);
908                 if (xml_flush_chars(ctx))
909                   {
910                     if (ctx->want & XML_WANT_CHARS)
911                       {
912                         ctx->state = XML_STATE_CHARS_BEFORE_STAG;
913                         return XML_STATE_CHARS;
914                       }
915       case XML_STATE_CHARS_BEFORE_STAG:
916                     xml_pop_chars(ctx);
917                   }
918
919                 xml_push_element(ctx);
920                 if (ctx->want & XML_WANT_STAG)
921                   return ctx->state = XML_STATE_STAG;
922       case XML_STATE_STAG:
923                 if (ctx->flags & XML_FLAG_EMPTY_ELEM)
924                   goto pop_element;
925               }
926
927             else
928               {
929                 /* ETag */
930                 if (xml_flush_chars(ctx))
931                   {
932                     if (ctx->want & XML_WANT_CHARS)
933                       {
934                         ctx->state = XML_STATE_CHARS_BEFORE_ETAG;
935                         return XML_STATE_CHARS;
936                       }
937       case XML_STATE_CHARS_BEFORE_ETAG:
938                     xml_pop_chars(ctx);
939                   }
940
941                 xml_parse_etag(ctx);
942 pop_element:
943                 if (ctx->want & XML_WANT_ETAG)
944                   return ctx->state = XML_STATE_ETAG;
945       case XML_STATE_ETAG:
946                 xml_pop_element(ctx);
947                 if (!ctx->node)
948                   goto epilog;
949               }
950           }
951
952 epilog:
953         /* Misc* */
954         TRACE(ctx, "entering epilog");
955         while (1)
956           {
957             /* Epilog whitespace is the only place, where a valid document can reach EOF */
958             if (setjmp(throw_buf))
959               if (ctx->err_code == XML_ERR_EOF)
960                 {
961                   TRACE(ctx, "reached EOF");
962                   ctx->state = XML_STATE_EOF;
963                   if (ctx->h_document_end)
964                     ctx->h_document_end(ctx);
965       case XML_STATE_EOF:
966                   return XML_STATE_EOF;
967                 }
968               else
969                 goto error;
970             xml_parse_white(ctx, 0);
971             if (setjmp(throw_buf))
972               goto error;
973
974             /* Misc */
975             xml_parse_char(ctx, '<');
976             if ((c = xml_get_char(ctx)) == '?')
977               /* Processing instruction */
978               if (!(ctx->want & XML_WANT_PI))
979                 xml_skip_pi(ctx);
980               else
981                 {
982                   xml_push_pi(ctx);
983                   return ctx->state = XML_STATE_EPILOG_PI, XML_STATE_PI;
984       case XML_STATE_EPILOG_PI:
985                   xml_pop_pi(ctx);
986                 }
987             else if (c == '!')
988               /* Comment */
989               if (!(ctx->want & XML_WANT_COMMENT))
990                 xml_skip_comment(ctx);
991               else
992                 {
993                   xml_push_comment(ctx);
994                   return ctx->state = XML_STATE_EPILOG_COMMENT, XML_STATE_COMMENT;
995       case XML_STATE_EPILOG_COMMENT:
996                   xml_pop_comment(ctx);
997                 }
998             else
999               xml_fatal(ctx, "Syntax error in the epilog");
1000           }
1001
1002     }
1003   return -1;
1004 }