]> mj.ucw.cz Git - libucw.git/blob - shxml/dtd.c
README: Trying variable substitution
[libucw.git] / shxml / dtd.c
1 /*
2  *      Sherlock Library -- A simple XML parser
3  *
4  *      (c) 2007--2008 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 #undef LOCAL_DEBUG
11
12 #include <ucw/lib.h>
13 #include <shxml/xml.h>
14 #include <shxml/dtd.h>
15 #include <shxml/internals.h>
16 #include <ucw/fastbuf.h>
17 #include <ucw/ff-unicode.h>
18 #include <ucw/unicode.h>
19
20 /* Notations */
21
22 #define HASH_PREFIX(x) xml_dtd_notns_##x
23 #define HASH_NODE struct xml_dtd_notn
24 #define HASH_KEY_STRING name
25 #define HASH_ZERO_FILL
26 #define HASH_TABLE_DYNAMIC
27 #define HASH_WANT_LOOKUP
28 #define HASH_WANT_FIND
29 #define HASH_GIVE_ALLOC
30 #define HASH_TABLE_ALLOC
31 XML_HASH_GIVE_ALLOC
32 #include <ucw/hashtable.h>
33
34 struct xml_dtd_notn *
35 xml_dtd_find_notn(struct xml_context *ctx, char *name)
36 {
37   struct xml_dtd *dtd = ctx->dtd;
38   struct xml_dtd_notn *notn = xml_dtd_notns_find(dtd->tab_notns, name);
39   return !notn ? NULL : (notn->flags & XML_DTD_NOTN_DECLARED) ? notn : NULL;
40 }
41
42 /* General entities */
43
44 #define HASH_PREFIX(x) xml_dtd_ents_##x
45 #define HASH_NODE struct xml_dtd_entity
46 #define HASH_KEY_STRING name
47 #define HASH_ZERO_FILL
48 #define HASH_TABLE_DYNAMIC
49 #define HASH_WANT_FIND
50 #define HASH_WANT_LOOKUP
51 #define HASH_GIVE_ALLOC
52 #define HASH_TABLE_ALLOC
53 XML_HASH_GIVE_ALLOC
54 #include <ucw/hashtable.h>
55
56 static struct xml_dtd_entity *
57 xml_dtd_declare_trivial_entity(struct xml_context *ctx, char *name, char *text)
58 {
59   struct xml_dtd *dtd = ctx->dtd;
60   struct xml_dtd_entity *ent = xml_dtd_ents_lookup(dtd->tab_ents, name);
61   if (ent->flags & XML_DTD_ENTITY_DECLARED)
62     {
63       xml_warn(ctx, "Entity &%s; already declared", name);
64       return NULL;
65     }
66   slist_add_tail(&dtd->ents, &ent->n);
67   ent->flags = XML_DTD_ENTITY_DECLARED | XML_DTD_ENTITY_TRIVIAL;
68   ent->text = text;
69   return ent;
70 }
71
72 static void
73 xml_dtd_declare_default_entities(struct xml_context *ctx)
74 {
75   xml_dtd_declare_trivial_entity(ctx, "lt", "<");
76   xml_dtd_declare_trivial_entity(ctx, "gt", ">");
77   xml_dtd_declare_trivial_entity(ctx, "amp", "&");
78   xml_dtd_declare_trivial_entity(ctx, "apos", "'");
79   xml_dtd_declare_trivial_entity(ctx, "quot", "\"");
80 }
81
82 struct xml_dtd_entity *
83 xml_def_find_entity(struct xml_context *ctx UNUSED, char *name)
84 {
85 #define ENT(n, t) ent_##n = { .name = #n, .text = t, .flags = XML_DTD_ENTITY_DECLARED | XML_DTD_ENTITY_TRIVIAL }
86   static struct xml_dtd_entity ENT(lt, "<"), ENT(gt, ">"), ENT(amp, "&"), ENT(apos, "'"), ENT(quot, "\"");
87 #undef ENT
88   switch (name[0])
89     {
90       case 'l':
91         if (!strcmp(name, "lt"))
92           return &ent_lt;
93         break;
94       case 'g':
95         if (!strcmp(name, "gt"))
96           return &ent_gt;
97         break;
98       case 'a':
99         if (!strcmp(name, "amp"))
100           return &ent_amp;
101         if (!strcmp(name, "apos"))
102           return &ent_apos;
103         break;
104       case 'q':
105         if (!strcmp(name, "quot"))
106           return &ent_quot;
107         break;
108     }
109   return NULL;
110 }
111
112 struct xml_dtd_entity *
113 xml_dtd_find_entity(struct xml_context *ctx, char *name)
114 {
115   struct xml_dtd *dtd = ctx->dtd;
116   if (ctx->h_find_entity)
117     return ctx->h_find_entity(ctx, name);
118   else if (dtd)
119     {
120       struct xml_dtd_entity *ent = xml_dtd_ents_find(dtd->tab_ents, name);
121       return !ent ? NULL : (ent->flags & XML_DTD_ENTITY_DECLARED) ? ent : NULL;
122     }
123   else
124     return xml_def_find_entity(ctx, name);
125 }
126
127 /* Parameter entities */
128
129 static struct xml_dtd_entity *
130 xml_dtd_find_pentity(struct xml_context *ctx, char *name)
131 {
132   struct xml_dtd *dtd = ctx->dtd;
133   struct xml_dtd_entity *ent = xml_dtd_ents_find(dtd->tab_pents, name);
134   return !ent ? NULL : (ent->flags & XML_DTD_ENTITY_DECLARED) ? ent : NULL;
135 }
136
137 /* Elements */
138
139 struct xml_dtd_elems_table;
140
141 static void
142 xml_dtd_elems_init_data(struct xml_dtd_elems_table *tab UNUSED, struct xml_dtd_elem *e)
143 {
144   slist_init(&e->attrs);
145 }
146
147 #define HASH_PREFIX(x) xml_dtd_elems_##x
148 #define HASH_NODE struct xml_dtd_elem
149 #define HASH_KEY_STRING name
150 #define HASH_TABLE_DYNAMIC
151 #define HASH_ZERO_FILL
152 #define HASH_WANT_FIND
153 #define HASH_WANT_LOOKUP
154 #define HASH_GIVE_ALLOC
155 #define HASH_GIVE_INIT_DATA
156 #define HASH_TABLE_ALLOC
157 XML_HASH_GIVE_ALLOC
158 #include <ucw/hashtable.h>
159
160 struct xml_dtd_elem *
161 xml_dtd_find_elem(struct xml_context *ctx, char *name)
162 {
163   return ctx->dtd ? xml_dtd_elems_find(ctx->dtd->tab_elems, name) : NULL;
164 }
165
166 /* Element sons */
167
168 struct xml_dtd_enodes_table;
169
170 static inline uns
171 xml_dtd_enodes_hash(struct xml_dtd_enodes_table *tab UNUSED, struct xml_dtd_elem_node *parent, struct xml_dtd_elem *elem)
172 {
173   return hash_pointer(parent) ^ hash_pointer(elem);
174 }
175
176 static inline int
177 xml_dtd_enodes_eq(struct xml_dtd_enodes_table *tab UNUSED, struct xml_dtd_elem_node *parent1, struct xml_dtd_elem *elem1, struct xml_dtd_elem_node *parent2, struct xml_dtd_elem *elem2)
178 {
179   return (parent1 == parent2) && (elem1 == elem2);
180 }
181
182 static inline void
183 xml_dtd_enodes_init_key(struct xml_dtd_enodes_table *tab UNUSED, struct xml_dtd_elem_node *node, struct xml_dtd_elem_node *parent, struct xml_dtd_elem *elem)
184 {
185   node->parent = parent;
186   node->elem = elem;
187 }
188
189 #define HASH_PREFIX(x) xml_dtd_enodes_##x
190 #define HASH_NODE struct xml_dtd_elem_node
191 #define HASH_KEY_COMPLEX(x) x parent, x elem
192 #define HASH_KEY_DECL struct xml_dtd_elem_node *parent, struct xml_dtd_elem *elem
193 #define HASH_GIVE_HASHFN
194 #define HASH_GIVE_EQ
195 #define HASH_GIVE_INIT_KEY
196 #define HASH_TABLE_DYNAMIC
197 #define HASH_ZERO_FILL
198 #define HASH_WANT_FIND
199 #define HASH_WANT_NEW
200 #define HASH_GIVE_ALLOC
201 #define HASH_TABLE_ALLOC
202 XML_HASH_GIVE_ALLOC
203 #include <ucw/hashtable.h>
204
205 /* Element attributes */
206
207 struct xml_dtd_attrs_table;
208
209 static inline uns
210 xml_dtd_attrs_hash(struct xml_dtd_attrs_table *tab UNUSED, struct xml_dtd_elem *elem, char *name)
211 {
212   return hash_pointer(elem) ^ hash_string(name);
213 }
214
215 static inline int
216 xml_dtd_attrs_eq(struct xml_dtd_attrs_table *tab UNUSED, struct xml_dtd_elem *elem1, char *name1, struct xml_dtd_elem *elem2, char *name2)
217 {
218   return (elem1 == elem2) && !strcmp(name1, name2);
219 }
220
221 static inline void
222 xml_dtd_attrs_init_key(struct xml_dtd_attrs_table *tab UNUSED, struct xml_dtd_attr *attr, struct xml_dtd_elem *elem, char *name)
223 {
224   attr->elem = elem;
225   attr->name = name;
226   slist_add_tail(&elem->attrs, &attr->n);
227 }
228
229 #define HASH_PREFIX(x) xml_dtd_attrs_##x
230 #define HASH_NODE struct xml_dtd_attr
231 #define HASH_ZERO_FILL
232 #define HASH_TABLE_DYNAMIC
233 #define HASH_KEY_COMPLEX(x) x elem, x name
234 #define HASH_KEY_DECL struct xml_dtd_elem *elem, char *name
235 #define HASH_GIVE_HASHFN
236 #define HASH_GIVE_EQ
237 #define HASH_GIVE_INIT_KEY
238 #define HASH_WANT_FIND
239 #define HASH_WANT_NEW
240 #define HASH_GIVE_ALLOC
241 #define HASH_TABLE_ALLOC
242 XML_HASH_GIVE_ALLOC
243 #include <ucw/hashtable.h>
244
245 struct xml_dtd_attr *
246 xml_dtd_find_attr(struct xml_context *ctx, struct xml_dtd_elem *elem, char *name)
247 {
248   return ctx->dtd ? xml_dtd_attrs_find(ctx->dtd->tab_attrs, elem, name) : NULL;
249 }
250
251 /* Enumerated attribute values */
252
253 struct xml_dtd_evals_table;
254
255 static inline uns
256 xml_dtd_evals_hash(struct xml_dtd_evals_table *tab UNUSED, struct xml_dtd_attr *attr, char *val)
257 {
258   return hash_pointer(attr) ^ hash_string(val);
259 }
260
261 static inline int
262 xml_dtd_evals_eq(struct xml_dtd_evals_table *tab UNUSED, struct xml_dtd_attr *attr1, char *val1, struct xml_dtd_attr *attr2, char *val2)
263 {
264   return (attr1 == attr2) && !strcmp(val1, val2);
265 }
266
267 static inline void
268 xml_dtd_evals_init_key(struct xml_dtd_evals_table *tab UNUSED, struct xml_dtd_eval *eval, struct xml_dtd_attr *attr, char *val)
269 {
270   eval->attr = attr;
271   eval->val = val;
272 }
273
274 #define HASH_PREFIX(x) xml_dtd_evals_##x
275 #define HASH_NODE struct xml_dtd_eval
276 #define HASH_TABLE_DYNAMIC
277 #define HASH_KEY_COMPLEX(x) x attr, x val
278 #define HASH_KEY_DECL struct xml_dtd_attr *attr, char *val
279 #define HASH_GIVE_HASHFN
280 #define HASH_GIVE_EQ
281 #define HASH_GIVE_INIT_KEY
282 #define HASH_WANT_FIND
283 #define HASH_WANT_NEW
284 #define HASH_GIVE_ALLOC
285 #define HASH_TABLE_ALLOC
286 XML_HASH_GIVE_ALLOC
287 #include <ucw/hashtable.h>
288
289 /* Enumerated attribute notations */
290
291 struct xml_dtd_enotns_table;
292
293 static inline uns
294 xml_dtd_enotns_hash(struct xml_dtd_enotns_table *tab UNUSED, struct xml_dtd_attr *attr, struct xml_dtd_notn *notn)
295 {
296   return hash_pointer(attr) ^ hash_pointer(notn);
297 }
298
299 static inline int
300 xml_dtd_enotns_eq(struct xml_dtd_enotns_table *tab UNUSED, struct xml_dtd_attr *attr1, struct xml_dtd_notn *notn1, struct xml_dtd_attr *attr2, struct xml_dtd_notn *notn2)
301 {
302   return (attr1 == attr2) && (notn1 == notn2);
303 }
304
305 static inline void
306 xml_dtd_enotns_init_key(struct xml_dtd_enotns_table *tab UNUSED, struct xml_dtd_enotn *enotn, struct xml_dtd_attr *attr, struct xml_dtd_notn *notn)
307 {
308   enotn->attr = attr;
309   enotn->notn = notn;
310 }
311
312 #define HASH_PREFIX(x) xml_dtd_enotns_##x
313 #define HASH_NODE struct xml_dtd_enotn
314 #define HASH_TABLE_DYNAMIC
315 #define HASH_KEY_COMPLEX(x) x attr, x notn
316 #define HASH_KEY_DECL struct xml_dtd_attr *attr, struct xml_dtd_notn *notn
317 #define HASH_GIVE_HASHFN
318 #define HASH_GIVE_EQ
319 #define HASH_GIVE_INIT_KEY
320 #define HASH_WANT_FIND
321 #define HASH_WANT_NEW
322 #define HASH_GIVE_ALLOC
323 #define HASH_TABLE_ALLOC
324 XML_HASH_GIVE_ALLOC
325 #include <ucw/hashtable.h>
326
327 /* DTD initialization/cleanup */
328
329 void
330 xml_dtd_init(struct xml_context *ctx)
331 {
332   if (ctx->dtd)
333     return;
334   struct mempool *pool = mp_new(4096);
335   struct xml_dtd *dtd = ctx->dtd = mp_alloc_zero(pool, sizeof(*ctx->dtd));
336   dtd->pool = pool;
337   xml_dtd_ents_init(dtd->tab_ents = xml_hash_new(pool, sizeof(struct xml_dtd_ents_table)));
338   xml_dtd_ents_init(dtd->tab_pents = xml_hash_new(pool, sizeof(struct xml_dtd_ents_table)));
339   xml_dtd_notns_init(dtd->tab_notns = xml_hash_new(pool, sizeof(struct xml_dtd_notns_table)));
340   xml_dtd_elems_init(dtd->tab_elems = xml_hash_new(pool, sizeof(struct xml_dtd_elems_table)));
341   xml_dtd_enodes_init(dtd->tab_enodes = xml_hash_new(pool, sizeof(struct xml_dtd_enodes_table)));
342   xml_dtd_attrs_init(dtd->tab_attrs = xml_hash_new(pool, sizeof(struct xml_dtd_attrs_table)));
343   xml_dtd_evals_init(dtd->tab_evals = xml_hash_new(pool, sizeof(struct xml_dtd_evals_table)));
344   xml_dtd_enotns_init(dtd->tab_enotns = xml_hash_new(pool, sizeof(struct xml_dtd_enotns_table)));
345   xml_dtd_declare_default_entities(ctx);
346 }
347
348 void
349 xml_dtd_cleanup(struct xml_context *ctx)
350 {
351   if (!ctx->dtd)
352     return;
353   mp_delete(ctx->dtd->pool);
354   ctx->dtd = NULL;
355 }
356
357 void
358 xml_dtd_finish(struct xml_context *ctx)
359 {
360   if (!ctx->dtd)
361     return;
362   // FIXME: validity checks
363 }
364
365 /*** Parsing functions ***/
366
367 /* References to parameter entities */
368
369 void
370 xml_parse_pe_ref(struct xml_context *ctx)
371 {
372   /* PEReference ::= '%' Name ';'
373    * Already parsed: '%' */
374   struct mempool_state state;
375   mp_save(ctx->stack, &state);
376   char *name = xml_parse_name(ctx, ctx->stack);
377   xml_parse_char(ctx, ';');
378   struct xml_dtd_entity *ent = xml_dtd_find_pentity(ctx, name);
379   if (!ent)
380     xml_error(ctx, "Unknown entity %%%s;", name);
381   else
382     {
383       TRACE(ctx, "Pushed entity %%%s;", name);
384       mp_restore(ctx->stack, &state);
385       xml_dec(ctx);
386       xml_push_entity(ctx, ent);
387       return;
388     }
389   mp_restore(ctx->stack, &state);
390   xml_dec(ctx);
391 }
392
393 static uns
394 xml_parse_dtd_pe(struct xml_context *ctx, uns entity_decl)
395 {
396   /* Already parsed: '%' */
397   do
398     {
399       xml_inc(ctx);
400       if (!~entity_decl && (xml_peek_cat(ctx) & XML_CHAR_WHITE))
401         {
402           xml_dec(ctx);
403           return ~0U;
404         }
405       xml_parse_pe_ref(ctx);
406       while (xml_peek_cat(ctx) & XML_CHAR_WHITE)
407         xml_skip_char(ctx);
408     }
409   while (xml_get_char(ctx) == '%');
410   xml_unget_char(ctx);
411   return 1;
412 }
413
414 static inline uns
415 xml_parse_dtd_white(struct xml_context *ctx, uns mandatory)
416 {
417   /* Whitespace or parameter entity,
418    * mandatory==~0U has a special maening of the whitespace before the '%' character in an parameter entity declaration */
419   uns cnt = 0;
420   while (xml_peek_cat(ctx) & XML_CHAR_WHITE)
421     {
422       xml_skip_char(ctx);
423       cnt = 1;
424     }
425   if (xml_peek_char(ctx) == '%')
426     {
427       xml_skip_char(ctx);
428       return xml_parse_dtd_pe(ctx, mandatory);
429     }
430   else if (unlikely(mandatory && !cnt))
431     xml_fatal_expected_white(ctx);
432   return cnt;
433 }
434
435 static void
436 xml_dtd_parse_external_id(struct xml_context *ctx, char **system_id, char **public_id, uns allow_public)
437 {
438   struct xml_dtd *dtd = ctx->dtd;
439   uns c = xml_peek_char(ctx);
440   if (c == 'S')
441     {
442       xml_parse_seq(ctx, "SYSTEM");
443       xml_parse_dtd_white(ctx, 1);
444       *public_id = NULL;
445       *system_id = xml_parse_system_literal(ctx, dtd->pool);
446     }
447   else if (c == 'P')
448     {
449       xml_parse_seq(ctx, "PUBLIC");
450       xml_parse_dtd_white(ctx, 1);
451       *system_id = NULL;
452       *public_id = xml_parse_pubid_literal(ctx, dtd->pool);
453       if (xml_parse_dtd_white(ctx, !allow_public))
454         if ((c = xml_peek_char(ctx)) == '\'' || c == '"' || !allow_public)
455           *system_id = xml_parse_system_literal(ctx, dtd->pool);
456     }
457   else
458     xml_fatal(ctx, "Expected an external ID");
459 }
460
461 /* DTD: <!NOTATION ...> */
462
463 void
464 xml_parse_notation_decl(struct xml_context *ctx)
465 {
466   /* NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>'
467    * Already parsed: '<!NOTATION' */
468   TRACE(ctx, "parse_notation_decl");
469   struct xml_dtd *dtd = ctx->dtd;
470   xml_parse_dtd_white(ctx, 1);
471
472   struct xml_dtd_notn *notn = xml_dtd_notns_lookup(dtd->tab_notns, xml_parse_name(ctx, dtd->pool));
473   xml_parse_dtd_white(ctx, 1);
474   char *system_id, *public_id;
475   xml_dtd_parse_external_id(ctx, &system_id, &public_id, 1);
476   xml_parse_dtd_white(ctx, 0);
477   xml_parse_char(ctx, '>');
478
479   if (notn->flags & XML_DTD_NOTN_DECLARED)
480     xml_warn(ctx, "Notation %s already declared", notn->name);
481   else
482     {
483       notn->flags = XML_DTD_NOTN_DECLARED;
484       notn->system_id = system_id;
485       notn->public_id = public_id;
486       slist_add_tail(&dtd->notns, &notn->n);
487     }
488   xml_dec(ctx);
489 }
490
491 /* DTD: <!ENTITY ...> */
492
493 void
494 xml_parse_entity_decl(struct xml_context *ctx)
495 {
496   /* Already parsed: '<!ENTITY' */
497   TRACE(ctx, "parse_entity_decl");
498   struct xml_dtd *dtd = ctx->dtd;
499   uns flags = ~xml_parse_dtd_white(ctx, ~0U) ? 0 : XML_DTD_ENTITY_PARAMETER;
500   if (flags)
501     xml_parse_dtd_white(ctx, 1);
502   struct xml_dtd_entity *ent = xml_dtd_ents_lookup(flags ? dtd->tab_pents : dtd->tab_ents, xml_parse_name(ctx, dtd->pool));
503   xml_parse_dtd_white(ctx, 1);
504   slist *list = flags ? &dtd->pents : &dtd->ents;
505   if (ent->flags & XML_DTD_ENTITY_DECLARED)
506     {
507        xml_fatal(ctx, "Entity &%s; already declared, skipping not implemented", ent->name);
508        // FIXME: should be only warning
509     }
510   uns c, sep = xml_get_char(ctx);
511   if (sep == '\'' || sep == '"')
512     {
513       /* Internal entity:
514        * EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' | "'" ([^%&'] | PEReference | Reference)* "'" */
515       char *p = mp_start_noalign(dtd->pool, 1);
516       while (1)
517         {
518           if ((c = xml_get_char(ctx)) == sep)
519             break;
520           if (c == '%')
521             {
522               // FIXME
523               ASSERT(0);
524               //xml_parse_parameter_ref(ctx);
525               continue;
526             }
527           if (c == '&')
528             {
529               xml_inc(ctx);
530               if (xml_peek_char(ctx) != '#')
531                 {
532                   /* Bypass references to general entities */
533                   struct mempool_state state;
534                   mp_save(ctx->stack, &state);
535                   char *n = xml_parse_name(ctx, ctx->stack);
536                   xml_parse_char(ctx, ';');
537                   xml_dec(ctx);
538                   uns l = strlen(n);
539                   p = mp_spread(dtd->pool, p, 3 + l);
540                   *p++ = '&';
541                   memcpy(p, n, l);
542                   p += l;
543                   *p++ = ';';;
544                   mp_restore(ctx->stack, &state);
545                   continue;
546                 }
547               else
548                 {
549                   xml_skip_char(ctx);
550                   c = xml_parse_char_ref(ctx);
551                 }
552             }
553           p = mp_spread(dtd->pool, p, 5);
554           p = utf8_32_put(p, c);
555         }
556       *p = 0;
557       ent->len = p - (char *)mp_ptr(dtd->pool);
558       ent->text = mp_end(dtd->pool, p + 1);
559       slist_add_tail(list, &ent->n);
560       ent->flags = flags | XML_DTD_ENTITY_DECLARED;
561     }
562   else
563     {
564       /* External entity */
565       struct xml_dtd_notn *notn = NULL;
566       char *system_id, *public_id;
567       xml_unget_char(ctx);
568       xml_dtd_parse_external_id(ctx, &system_id, &public_id, 0);
569       if (xml_parse_dtd_white(ctx, 0) && flags && xml_peek_char(ctx) != '>')
570         {
571           /* General external unparsed entity */
572           flags |= XML_DTD_ENTITY_UNPARSED;
573           xml_parse_seq(ctx, "NDATA");
574           xml_parse_dtd_white(ctx, 1);
575           notn = xml_dtd_notns_lookup(dtd->tab_notns, xml_parse_name(ctx, dtd->pool));
576         }
577       slist_add_tail(list, &ent->n);
578       ent->flags = flags | XML_DTD_ENTITY_DECLARED | XML_DTD_ENTITY_EXTERNAL;
579       ent->system_id = system_id;
580       ent->public_id = public_id;
581       ent->notn = notn;
582     }
583   xml_parse_dtd_white(ctx, 0);
584   xml_parse_char(ctx, '>');
585   xml_dec(ctx);
586 }
587
588 /* DTD: <!ELEMENT ...> */
589
590 void
591 xml_parse_element_decl(struct xml_context *ctx)
592 {
593   /* Elementdecl ::= '<!ELEMENT' S  Name  S  contentspec  S? '>'
594    * Already parsed: '<!ELEMENT' */
595   struct xml_dtd *dtd = ctx->dtd;
596   xml_parse_dtd_white(ctx, 1);
597   char *name = xml_parse_name(ctx, dtd->pool);
598   xml_parse_dtd_white(ctx, 1);
599   struct xml_dtd_elem *elem = xml_dtd_elems_lookup(dtd->tab_elems, name);
600   if (elem->flags & XML_DTD_ELEM_DECLARED)
601     xml_fatal(ctx, "Element <%s> already declared", name);
602
603   /* contentspec ::= 'EMPTY' | 'ANY' | Mixed | children */
604   uns c = xml_peek_char(ctx);
605   if (c == 'E')
606     {
607       xml_parse_seq(ctx, "EMPTY");
608       elem->type = XML_DTD_ELEM_EMPTY;
609     }
610   else if (c == 'A')
611     {
612       xml_parse_seq(ctx, "ANY");
613       elem->type = XML_DTD_ELEM_ANY;
614     }
615   else if (c == '(')
616     {
617       xml_skip_char(ctx);
618       xml_inc(ctx);
619       xml_parse_dtd_white(ctx, 0);
620       struct xml_dtd_elem_node *parent = elem->node = mp_alloc_zero(dtd->pool, sizeof(*parent));
621       if (xml_peek_char(ctx) == '#')
622         {
623           /* Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')' */
624           xml_skip_char(ctx);
625           xml_parse_seq(ctx, "PCDATA");
626           elem->type = XML_DTD_ELEM_MIXED;
627           parent->type = XML_DTD_ELEM_PCDATA;
628           while (1)
629             {
630               xml_parse_dtd_white(ctx, 0);
631               if ((c = xml_get_char(ctx)) == ')')
632                 break;
633               else if (c != '|')
634                 xml_fatal_expected(ctx, ')');
635               xml_parse_dtd_white(ctx, 0);
636               struct xml_dtd_elem *son_elem = xml_dtd_elems_lookup(dtd->tab_elems, xml_parse_name(ctx, dtd->pool));
637               if (xml_dtd_enodes_find(dtd->tab_enodes, parent, son_elem))
638                 xml_error(ctx, "Duplicate content '%s'", son_elem->name);
639               else
640                 {
641                   struct xml_dtd_elem_node *son = xml_dtd_enodes_new(dtd->tab_enodes, parent, son_elem);
642                   slist_add_tail(&parent->sons, &son->n);
643                 }
644             }
645           xml_dec(ctx);
646           if (xml_peek_char(ctx) == '*')
647             {
648               xml_skip_char(ctx);
649               parent->occur = XML_DTD_ELEM_OCCUR_MULT;
650             }
651           else if (!slist_head(&parent->sons))
652             parent->occur = XML_DTD_ELEM_OCCUR_ONCE;
653           else
654             xml_fatal_expected(ctx, '*');
655         }
656       else
657         {
658           /* children ::= (choice | seq) ('?' | '*' | '+')?
659            * cp ::= (Name | choice | seq) ('?' | '*' | '+')?
660            * choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')'
661            * seq ::= '(' S? cp ( S? ',' S? cp )* S? ')' */
662
663           elem->type = XML_DTD_ELEM_CHILDREN;
664           parent->type = XML_DTD_ELEM_PCDATA;
665           uns c;
666           goto first;
667
668           while (1)
669             {
670               /* After name */
671               xml_parse_dtd_white(ctx, 0);
672               if ((c = xml_get_char(ctx)) ==  ')')
673                 {
674                   xml_dec(ctx);
675                   if (parent->type == XML_DTD_ELEM_PCDATA)
676                     parent->type = XML_DTD_ELEM_SEQ;
677                   if ((c = xml_get_char(ctx)) == '?')
678                     parent->occur = XML_DTD_ELEM_OCCUR_OPT;
679                   else if (c == '*')
680                     parent->occur = XML_DTD_ELEM_OCCUR_MULT;
681                   else if (c == '+')
682                     parent->occur = XML_DTD_ELEM_OCCUR_PLUS;
683                   else
684                     {
685                       xml_unget_char(ctx);
686                       parent->occur = XML_DTD_ELEM_OCCUR_ONCE;
687                     }
688                   if (!parent->parent)
689                     break;
690                   parent = parent->parent;
691                   continue;
692                 }
693               else if (c == '|')
694                 {
695                   if (parent->type == XML_DTD_ELEM_PCDATA)
696                     parent->type = XML_DTD_ELEM_OR;
697                   else if (parent->type != XML_DTD_ELEM_OR)
698                     xml_fatal(ctx, "Mixed operators in the list of element children");
699                 }
700               else if (c == ',')
701                 {
702                   if (parent->type == XML_DTD_ELEM_PCDATA)
703                     parent->type = XML_DTD_ELEM_SEQ;
704                   else if (parent->type != XML_DTD_ELEM_SEQ)
705                     xml_fatal(ctx, "Mixed operators in the list of element children");
706                 }
707               else if (c == '(')
708                 {
709                   xml_inc(ctx);
710                   struct xml_dtd_elem_node *son = mp_alloc_zero(dtd->pool, sizeof(*son));
711                   son->parent = parent;
712                   slist_add_tail(&parent->sons, &son->n);
713                   parent = son->parent;
714                   son->type = XML_DTD_ELEM_MIXED;
715                 }
716               else
717                 xml_unget_char(ctx);
718
719               /* Before name */
720               xml_parse_dtd_white(ctx, 0);
721 first:;
722               struct xml_dtd_elem *son_elem = xml_dtd_elems_lookup(dtd->tab_elems, xml_parse_name(ctx, dtd->pool));
723               // FIXME: duplicates, occurance
724               //struct xml_dtd_elem_node *son = xml_dtd_enodes_new(dtd->tab_enodes, parent, son_elem);
725               struct xml_dtd_elem_node *son = mp_alloc_zero(dtd->pool, sizeof(*son));
726               son->parent = parent;
727               son->elem = son_elem;
728               slist_add_tail(&parent->sons, &son->n);
729             }
730         }
731     }
732   else
733     xml_fatal(ctx, "Expected element content specification");
734
735   xml_parse_dtd_white(ctx, 0);
736   xml_parse_char(ctx, '>');
737   xml_dec(ctx);
738 }
739
740 void
741 xml_parse_attr_list_decl(struct xml_context *ctx)
742 {
743   /* AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
744    * AttDef ::= S Name S AttType S DefaultDecl
745    * Already parsed: '<!ATTLIST' */
746   struct xml_dtd *dtd = ctx->dtd;
747   xml_parse_dtd_white(ctx, 1);
748   struct xml_dtd_elem *elem = xml_dtd_elems_lookup(ctx->dtd->tab_elems, xml_parse_name(ctx, dtd->pool));
749
750   while (xml_parse_dtd_white(ctx, 0) && xml_peek_char(ctx) != '>')
751     {
752       char *name = xml_parse_name(ctx, dtd->pool);
753       struct xml_dtd_attr *attr = xml_dtd_attrs_find(dtd->tab_attrs, elem, name);
754       uns ignored = 0;
755       if (attr)
756         {
757           xml_warn(ctx, "Duplicate attribute definition");
758           ignored++;
759         }
760       else
761         attr = xml_dtd_attrs_new(ctx->dtd->tab_attrs, elem, name);
762       xml_parse_dtd_white(ctx, 1);
763       if (xml_peek_char(ctx) == '(')
764         {
765           xml_skip_char(ctx); // FIXME: xml_inc/dec ?
766           if (!ignored)
767             attr->type = XML_ATTR_ENUM;
768           do
769             {
770               xml_parse_dtd_white(ctx, 0);
771               char *value = xml_parse_nmtoken(ctx, dtd->pool);
772               if (!ignored)
773                 if (xml_dtd_evals_find(ctx->dtd->tab_evals, attr, value))
774                   xml_error(ctx, "Duplicate enumeration value");
775                 else
776                   xml_dtd_evals_new(ctx->dtd->tab_evals, attr, value);
777               xml_parse_dtd_white(ctx, 0);
778             }
779           while (xml_get_char(ctx) == '|');
780           xml_unget_char(ctx);
781           xml_parse_char(ctx, ')');
782         }
783       else
784         {
785           char *type = xml_parse_name(ctx, dtd->pool);
786           enum xml_dtd_attr_type t = XML_ATTR_CDATA;
787           if (!strcmp(type, "CDATA"))
788             t = XML_ATTR_CDATA;
789           else if (!strcmp(type, "ID"))
790             t = XML_ATTR_ID;
791           else if (!strcmp(type, "IDREF"))
792             t = XML_ATTR_IDREF;
793           else if (!strcmp(type, "IDREFS"))
794             t = XML_ATTR_IDREFS;
795           else if (!strcmp(type, "ENTITY"))
796             t = XML_ATTR_ENTITY;
797           else if (!strcmp(type, "ENTITIES"))
798             t = XML_ATTR_ENTITIES;
799           else if (!strcmp(type, "NMTOKEN"))
800             t = XML_ATTR_NMTOKEN;
801           else if (!strcmp(type, "NMTOKENS"))
802             t = XML_ATTR_NMTOKENS;
803           else if (!strcmp(type, "NOTATION"))
804             {
805               if (elem->type == XML_DTD_ELEM_EMPTY)
806                 xml_fatal(ctx, "Empty element must not have notation attribute");
807               // FIXME: An element type MUST NOT have more than one NOTATION attribute specified.
808               t = XML_ATTR_NOTATION;
809               xml_parse_dtd_white(ctx, 1);
810               xml_parse_char(ctx, '(');
811               do
812                 {
813                   xml_parse_dtd_white(ctx, 0);
814                   struct xml_dtd_notn *n = xml_dtd_notns_lookup(ctx->dtd->tab_notns, xml_parse_name(ctx, dtd->pool));
815                   if (!ignored)
816                     if (xml_dtd_enotns_find(ctx->dtd->tab_enotns, attr, n))
817                       xml_error(ctx, "Duplicate enumerated notation");
818                     else
819                       xml_dtd_enotns_new(ctx->dtd->tab_enotns, attr, n);
820                   xml_parse_dtd_white(ctx, 0);
821                 }
822               while (xml_get_char(ctx) == '|');
823               xml_unget_char(ctx);
824               xml_parse_char(ctx, ')');
825             }
826           else
827             xml_fatal(ctx, "Unknown attribute type");
828           if (!ignored)
829             attr->type = t;
830         }
831       xml_parse_dtd_white(ctx, 1);
832       enum xml_dtd_attr_default def = XML_ATTR_NONE;
833       if (xml_get_char(ctx) == '#')
834         switch (xml_peek_char(ctx))
835           {
836             case 'R':
837               xml_parse_seq(ctx, "REQUIRED");
838               def = XML_ATTR_REQUIRED;
839               break;
840             case 'I':
841               xml_parse_seq(ctx, "IMPLIED");
842               def = XML_ATTR_IMPLIED;
843               break;
844             case 'F':
845               xml_parse_seq(ctx, "FIXED");
846               def = XML_ATTR_FIXED;
847               xml_parse_dtd_white(ctx, 1);
848               break;
849             default:
850               xml_fatal(ctx, "Expected a modifier for default attribute value");
851           }
852       else
853         xml_unget_char(ctx);
854       if (def != XML_ATTR_REQUIRED && def != XML_ATTR_IMPLIED)
855         {
856           char *v = xml_parse_attr_value(ctx, attr);
857           if (!ignored)
858             attr->default_value = v;
859         }
860       if (!ignored)
861         attr->default_mode = def;
862     }
863   xml_skip_char(ctx);
864   xml_dec(ctx);
865 }
866
867 void
868 xml_skip_internal_subset(struct xml_context *ctx)
869 {
870   TRACE(ctx, "skip_internal_subset");
871   /* AlreadyParsed: '[' */
872   uns c;
873   while ((c = xml_get_char(ctx)) != ']')
874     {
875       if (c != '<')
876         continue;
877       if ((c = xml_get_char(ctx)) == '?')
878         {
879           xml_inc(ctx);
880           xml_skip_pi(ctx);
881         }
882       else if (c != '!')
883         xml_dec(ctx);
884       else if (xml_get_char(ctx) == '-')
885         {
886           xml_inc(ctx);
887           xml_skip_comment(ctx);
888         }
889       else
890         while ((c = xml_get_char(ctx)) != '>')
891           if (c == '\'' || c == '"')
892             while (xml_get_char(ctx) != c);
893     }
894   xml_dec(ctx);
895 }
896
897 /*** Validation of attribute values ***/
898
899 static uns
900 xml_check_tokens(char *value, uns first_cat, uns next_cat, uns seq)
901 {
902   char *p = value;
903   uns u;
904   while (1)
905     {
906       p = utf8_32_get(p, &u);
907       if (!(xml_char_cat(u) & first_cat))
908         return 0;
909       while (*p & ~0x20)
910         {
911           p = utf8_32_get(p, &u);
912           if (!(xml_char_cat(u) & next_cat))
913             return 0;
914         }
915       if (!*p)
916         return 1;
917       if (!seq)
918         return 0;
919       p++;
920     }
921 }
922
923 static uns
924 xml_is_name(struct xml_context *ctx, char *value)
925 {
926   /* Name ::= NameStartChar (NameChar)* */
927   return xml_check_tokens(value, ctx->cat_sname, ctx->cat_name, 0);
928 }
929
930 static uns
931 xml_is_names(struct xml_context *ctx, char *value)
932 {
933   /* Names ::= Name (#x20 Name)* */
934   return xml_check_tokens(value, ctx->cat_sname, ctx->cat_name, 1);
935 }
936
937 static uns
938 xml_is_nmtoken(struct xml_context *ctx, char *value)
939 {
940   /* Nmtoken ::= (NameChar)+ */
941   return xml_check_tokens(value, ctx->cat_name, ctx->cat_name, 0);
942 }
943
944 static uns
945 xml_is_nmtokens(struct xml_context *ctx, char *value)
946 {
947   /* Nmtokens ::= Nmtoken (#x20 Nmtoken)* */
948   return xml_check_tokens(value, ctx->cat_name, ctx->cat_name, 1);
949 }
950
951 static void
952 xml_err_attr_format(struct xml_context *ctx, struct xml_dtd_attr *dtd, char *type)
953 {
954   xml_error(ctx, "Attribute %s in <%s> does not match the production of %s", dtd->name, dtd->elem->name, type);
955 }
956
957 void
958 xml_validate_attr(struct xml_context *ctx, struct xml_dtd_attr *dtd, char *value)
959 {
960   if (dtd->type == XML_ATTR_CDATA)
961     return;
962   xml_normalize_white(ctx, value);
963   switch (dtd->type)
964     {
965       case XML_ATTR_ID:
966         if (!xml_is_name(ctx, value))
967           xml_err_attr_format(ctx, dtd, "NAME");
968         //FIXME: add to a hash table
969         break;
970       case XML_ATTR_IDREF:
971         if (!xml_is_name(ctx, value))
972           xml_err_attr_format(ctx, dtd, "NAME");
973         // FIXME: find in hash table (beware forward references)
974         break;
975       case XML_ATTR_IDREFS:
976         if (!xml_is_names(ctx, value))
977           xml_err_attr_format(ctx, dtd, "NAMES");
978         // FIXME: find
979         break;
980       case XML_ATTR_ENTITY:
981         // FIXME
982         break;
983       case XML_ATTR_ENTITIES:
984         // FIXME
985         break;
986       case XML_ATTR_NMTOKEN:
987         if (!xml_is_nmtoken(ctx, value))
988           xml_err_attr_format(ctx, dtd, "NMTOKEN");
989         break;
990       case XML_ATTR_NMTOKENS:
991         if (!xml_is_nmtokens(ctx, value))
992           xml_err_attr_format(ctx, dtd, "NMTOKENS");
993         break;
994       case XML_ATTR_ENUM:
995         if (!xml_dtd_evals_find(ctx->dtd->tab_evals, dtd, value))
996           xml_error(ctx, "Attribute %s in <%s> contains an undefined enumeration value", dtd->name, dtd->elem->name);
997         break;
998       case XML_ATTR_NOTATION:
999         if (!xml_dtd_find_notn(ctx, value))
1000           xml_error(ctx, "Attribute %s in <%s> contains an undefined notation", dtd->name, dtd->elem->name);
1001         break;
1002     }
1003 }