]> mj.ucw.cz Git - libucw.git/blob - sherlock/xml/common.c
XML: Split to several files, revised part of iface and
[libucw.git] / sherlock / xml / common.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 "lib/lib.h"
13 #include "lib/mempool.h"
14 #include "lib/fastbuf.h"
15 #include "lib/ff-unicode.h"
16 #include "lib/ff-binary.h"
17 #include "lib/chartype.h"
18 #include "lib/unicode.h"
19 #include "lib/hashfunc.h"
20 #include "lib/stkstring.h"
21 #include "lib/unaligned.h"
22 #include "charset/charconv.h"
23 #include "charset/fb-charconv.h"
24 #include "sherlock/xml/xml.h"
25 #include "sherlock/xml/dtd.h"
26 #include "sherlock/xml/common.h"
27
28 #include <setjmp.h>
29
30 /*** Error handling ***/
31
32 void NONRET
33 xml_throw(struct xml_context *ctx)
34 {
35   ASSERT(ctx->err_code && ctx->throw_buf);
36   longjmp(*(jmp_buf *)ctx->throw_buf, ctx->err_code);
37 }
38
39 void
40 xml_warn(struct xml_context *ctx, const char *format, ...)
41 {
42   if (ctx->h_warn)
43     {
44       va_list args;
45       va_start(args, format);
46       ctx->err_msg = stk_vprintf(format, args);
47       ctx->err_code = XML_ERR_WARN;
48       va_end(args);
49       ctx->h_warn(ctx);
50       ctx->err_msg = NULL;
51       ctx->err_code = XML_ERR_OK;
52     }
53 }
54
55 void
56 xml_error(struct xml_context *ctx, const char *format, ...)
57 {
58   if (ctx->h_error)
59     {
60       va_list args;
61       va_start(args, format);
62       ctx->err_msg = stk_vprintf(format, args);
63       ctx->err_code = XML_ERR_ERROR;
64       va_end(args);
65       ctx->h_error(ctx);
66       ctx->err_msg = NULL;
67       ctx->err_code = XML_ERR_OK;
68     }
69 }
70
71 void NONRET
72 xml_fatal(struct xml_context *ctx, const char *format, ...)
73 {
74   va_list args;
75   va_start(args, format);
76   ctx->err_msg = mp_vprintf(ctx->stack, format, args);
77   ctx->err_code = XML_ERR_FATAL;
78   ctx->state = XML_STATE_FATAL;
79   va_end(args);
80   if (ctx->h_fatal)
81     ctx->h_fatal(ctx);
82   xml_throw(ctx);
83 }
84
85 /*** Charecter categorization ***/
86
87 #include "obj/sherlock/xml/unicat.c"
88
89 /*** Memory management ***/
90
91 void NONRET
92 xml_fatal_nested(struct xml_context *ctx)
93 {
94   xml_fatal(ctx, "Entity not nested correctly");
95 }
96
97 void *
98 xml_hash_new(struct mempool *pool, uns size)
99 {
100   void *tab = mp_alloc_zero(pool, size + XML_HASH_HDR_SIZE);
101   *(void **)tab = pool;
102   return tab + XML_HASH_HDR_SIZE;
103 }
104
105 /*** Reading of document/external entities ***/
106
107 static void NONRET
108 xml_eof(struct xml_context *ctx)
109 {
110   ctx->err_msg = "Unexpected EOF";
111   ctx->err_code = XML_ERR_EOF;
112   xml_throw(ctx);
113 }
114
115 static inline void
116 xml_add_char(u32 **bstop, uns c)
117 {
118   *(*bstop)++ = c;
119   *(*bstop)++ = xml_char_cat(c);
120 }
121
122 struct xml_source *
123 xml_push_source(struct xml_context *ctx, uns flags)
124 {
125   xml_push(ctx);
126   struct xml_source *src = ctx->src;
127   if (src)
128     {
129       src->bptr = ctx->bptr;
130       src->bstop = ctx->bstop;
131     }
132   src = mp_alloc_zero(ctx->stack, sizeof(*src));
133   src->next = ctx->src;
134   src->saved_depth = ctx->depth;
135   ctx->src = src;
136   ctx->flags = (ctx->flags & ~(XML_FLAG_SRC_EOF | XML_FLAG_SRC_EXPECTED_DECL | XML_FLAG_SRC_NEW_LINE | XML_FLAG_SRC_SURROUND | XML_FLAG_SRC_DOCUMENT)) | flags;
137   ctx->bstop = ctx->bptr = src->buf;
138   ctx->depth = 0;
139   if (flags & XML_FLAG_SRC_SURROUND)
140     xml_add_char(&ctx->bstop, 0x20);
141   return src;
142 }
143
144 static void
145 xml_pop_source(struct xml_context *ctx)
146 {
147   TRACE(ctx, "pop_source");
148   if (unlikely(ctx->depth != 0))
149     xml_fatal_nested(ctx);
150   struct xml_source *src = ctx->src;
151   ASSERT(src);
152   bclose(src->fb);
153   ctx->depth = src->saved_depth;
154   ctx->src = src = src->next;
155   if (src)
156     {
157       ctx->bptr = src->bptr;
158       ctx->bstop = src->bstop;
159     }
160   xml_pop(ctx);
161   if (unlikely(!src))
162     xml_eof(ctx);
163 }
164
165 static void xml_refill_utf8(struct xml_context *ctx);
166
167 void
168 xml_push_entity(struct xml_context *ctx, struct xml_dtd_ent *ent)
169 {
170   TRACE(ctx, "xml_push_entity");
171   uns cat1 = ctx->src->refill_cat1;
172   uns cat2 = ctx->src->refill_cat2;
173   struct xml_source *src = xml_push_source(ctx, 0);
174   src->refill_cat1 = cat1;
175   src->refill_cat2 = cat2;
176   if (ent->flags & XML_DTD_ENT_EXTERNAL)
177     xml_fatal(ctx, "External entities not implemented"); // FIXME
178   else
179     {
180       fbbuf_init_read(src->fb = &src->wrap_fb, ent->text, ent->len, 0);
181       src->refill = xml_refill_utf8;
182     }
183 }
184
185 void
186 xml_set_source(struct xml_context *ctx, struct fastbuf *fb)
187 {
188   TRACE(ctx, "xml_set_source");
189   ASSERT(!ctx->src);
190   struct xml_source *src = xml_push_source(ctx, XML_FLAG_SRC_DOCUMENT | XML_FLAG_SRC_EXPECTED_DECL);
191   src->fb = fb;
192 }
193
194 static uns
195 xml_error_restricted(struct xml_context *ctx, uns c)
196 {
197   if (c == ~1U)
198     xml_error(ctx, "Corrupted encoding");
199   else
200     xml_error(ctx, "Restricted char U+%04X", c);
201   return UNI_REPLACEMENT;
202 }
203
204 void xml_parse_decl(struct xml_context *ctx);
205
206 #define REFILL(ctx, func, params...)                                                    \
207   struct xml_source *src = ctx->src;                                                    \
208   struct fastbuf *fb = src->fb;                                                         \
209   if (ctx->bptr == ctx->bstop)                                                          \
210     ctx->bptr = ctx->bstop = src->buf;                                                  \
211   uns f = ctx->flags, c, t1 = src->refill_cat1, t2 = src->refill_cat2, row = src->row;  \
212   u32 *bend = src->buf + ARRAY_SIZE(src->buf), *bstop = ctx->bstop,                     \
213       *last_0xd = (f & XML_FLAG_SRC_NEW_LINE) ? bstop : bend;                           \
214   do                                                                                    \
215     {                                                                                   \
216       c = func(fb, ##params);                                                           \
217       uns t = xml_char_cat(c);                                                          \
218       if (t & t1)                                                                       \
219         /* Typical branch */                                                            \
220         *bstop++ = c, *bstop++ = t;                                                     \
221       else if (t & t2)                                                                  \
222         {                                                                               \
223           /* New line */                                                                \
224           /* XML 1.0: 0xA | 0xD | 0xD 0xA */                                            \
225           /* XML 1.1: 0xA | 0xD | 0xD 0xA | 0x85 | 0xD 0x85 | 0x2028 */                 \
226           if (c == 0xd)                                                                 \
227             last_0xd = bstop + 2;                                                       \
228           else if (c != 0x2028 && last_0xd == bstop)                                    \
229             {                                                                           \
230               last_0xd = bend;                                                          \
231               continue;                                                                 \
232             }                                                                           \
233           xml_add_char(&bstop, 0xa), row++;                                             \
234         }                                                                               \
235       else if (c == '>')                                                                \
236         {                                                                               \
237           /* Used only in XML/TextDecl to switch the encoding */                        \
238           *bstop++ = c, *bstop++ = t;                                                   \
239           break;                                                                        \
240         }                                                                               \
241       else if (~c)                                                                      \
242         /* Restricted character */                                                      \
243         xml_add_char(&bstop, xml_error_restricted(ctx, c));                             \
244       else                                                                              \
245         {                                                                               \
246           /* EOF */                                                                     \
247           if (f & XML_FLAG_SRC_SURROUND)                                                \
248             xml_add_char(&bstop, 0x20);                                                 \
249           f |= XML_FLAG_SRC_EOF;                                                        \
250           break;                                                                        \
251         }                                                                               \
252     }                                                                                   \
253   while (bstop < bend);                                                                 \
254   ctx->flags = (last_0xd == bstop) ? f | XML_FLAG_SRC_NEW_LINE : f & ~XML_FLAG_SRC_NEW_LINE; \
255   ctx->bstop = bstop;                                                                   \
256   src->row = row;
257
258 static void
259 xml_refill_utf8(struct xml_context *ctx)
260 {
261   REFILL(ctx, bget_utf8_repl, ~1U);
262 }
263
264 static void
265 xml_refill_utf16_le(struct xml_context *ctx)
266 {
267   REFILL(ctx, bget_utf16_le_repl, ~1U);
268 }
269
270 static void
271 xml_refill_utf16_be(struct xml_context *ctx)
272 {
273   REFILL(ctx, bget_utf16_be_repl, ~1U);
274 }
275
276 #if 0
277 static inline uns
278 xml_refill_libcharset_bget(struct fastbuf *fb, unsigned short int *in_to_x)
279 {
280   // FIXME: slow
281   int c;
282   return (unlikely(c = bgetc(fb) < 0)) ? c : (int)conv_x_to_ucs(in_to_x[c]);
283 }
284
285 static void
286 xml_refill_libcharset(struct xml_context *ctx)
287 {
288   unsigned short int *in_to_x = ctx->src->refill_in_to_x;
289   REFILL(ctx, xml_refill_libcharset_bget, in_to_x);
290 }
291 #endif
292
293 #undef REFILL
294
295 void
296 xml_refill(struct xml_context *ctx)
297 {
298   do
299     {
300       if (ctx->flags & XML_FLAG_SRC_EOF)
301         xml_pop_source(ctx);
302       else if (ctx->flags & XML_FLAG_SRC_EXPECTED_DECL)
303         xml_parse_decl(ctx);
304       else
305         {
306           ctx->src->refill(ctx);
307           TRACE(ctx, "refilled %u characters", (uns)((ctx->bstop - ctx->bptr) / 2));
308         }
309     }
310   while (ctx->bptr == ctx->bstop);
311 }
312
313 uns
314 xml_row(struct xml_context *ctx)
315 {
316   struct xml_source *src = ctx->src;
317   if (!src)
318     return 0;
319   uns row = src->row;
320   for (u32 *p = ctx->bstop; p != ctx->bptr; p -= 2)
321     if (p[-1] & src->refill_cat2)
322       row--;
323   return row + 1;
324 }
325
326 /*** Basic parsing ***/
327
328 void NONRET
329 xml_fatal_expected(struct xml_context *ctx, uns c)
330 {
331   xml_fatal(ctx, "Expected '%c'", c);
332 }
333
334 void NONRET
335 xml_fatal_expected_white(struct xml_context *ctx)
336 {
337   xml_fatal(ctx, "Expected a white space");
338 }
339
340 void NONRET
341 xml_fatal_expected_quot(struct xml_context *ctx)
342 {
343   xml_fatal(ctx, "Expected a quotation mark");
344 }
345
346 void
347 xml_parse_eq(struct xml_context *ctx)
348 {
349   /* Eq ::= S? '=' S? */
350   xml_parse_white(ctx, 0);
351   xml_parse_char(ctx, '=');
352   xml_parse_white(ctx, 0);
353 }
354
355 /* Names and nmtokens */
356
357 static char *
358 xml_parse_string(struct xml_context *ctx, struct mempool *pool, uns first_cat, uns next_cat, char *err)
359 {
360   char *p = mp_start_noalign(pool, 1);
361   if (unlikely(!(xml_peek_cat(ctx) & first_cat)))
362     xml_fatal(ctx, "%s", err);
363   do
364     {
365       p = mp_spread(pool, p, 5);
366       p = utf8_32_put(p, xml_skip_char(ctx));
367     }
368   while (xml_peek_cat(ctx) & next_cat);
369   *p++ = 0;
370   return mp_end(pool, p);
371 }
372
373 static void
374 xml_skip_string(struct xml_context *ctx, uns first_cat, uns next_cat, char *err)
375 {
376   if (unlikely(!(xml_get_cat(ctx) & first_cat)))
377     xml_fatal(ctx, "%s", err);
378   while (xml_peek_cat(ctx) & next_cat)
379     xml_skip_char(ctx);
380 }
381
382 char *
383 xml_parse_name(struct xml_context *ctx, struct mempool *pool)
384 {
385   /* Name ::= NameStartChar (NameChar)* */
386   return xml_parse_string(ctx, pool,
387     !(ctx->flags & XML_FLAG_VERSION_1_1) ? XML_CHAR_SNAME_1_0 : XML_CHAR_SNAME_1_1,
388     !(ctx->flags & XML_FLAG_VERSION_1_1) ? XML_CHAR_NAME_1_0 : XML_CHAR_NAME_1_1,
389     "Expected a name");
390 }
391
392 void
393 xml_skip_name(struct xml_context *ctx)
394 {
395   xml_skip_string(ctx,
396     !(ctx->flags & XML_FLAG_VERSION_1_1) ? XML_CHAR_SNAME_1_0 : XML_CHAR_SNAME_1_1,
397     !(ctx->flags & XML_FLAG_VERSION_1_1) ? XML_CHAR_NAME_1_0 : XML_CHAR_NAME_1_1,
398     "Expected a name");
399 }
400
401 char *
402 xml_parse_nmtoken(struct xml_context *ctx, struct mempool *pool)
403 {
404   /* Nmtoken ::= (NameChar)+ */
405   uns cat = !(ctx->flags & XML_FLAG_VERSION_1_1) ? XML_CHAR_NAME_1_0 : XML_CHAR_NAME_1_1;
406   return xml_parse_string(ctx, pool, cat, cat, "Expected a nmtoken");
407 }
408
409 /* Simple literals */
410
411 char *
412 xml_parse_system_literal(struct xml_context *ctx, struct mempool *pool)
413 {
414   /* SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") */
415   char *p = mp_start_noalign(pool, 1);
416   uns q = xml_parse_quote(ctx), c;
417   while ((c = xml_get_char(ctx)) != q)
418     {
419       p = mp_spread(pool, p, 5);
420       p = utf8_32_put(p, c);
421     }
422   *p++ = 0;
423   return mp_end(pool, p);
424 }
425
426 char *
427 xml_parse_pubid_literal(struct xml_context *ctx, struct mempool *pool)
428 {
429   /* PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'" */
430   char *p = mp_start_noalign(pool, 1);
431   uns q = xml_parse_quote(ctx), c;
432   while ((c = xml_get_char(ctx)) != q)
433     {
434       if (unlikely(!(xml_last_cat(ctx) & XML_CHAR_PUBID)))
435         xml_fatal(ctx, "Expected a pubid character");
436       p = mp_spread(pool, p, 2);
437       *p++ = c;
438     }
439   *p++ = 0;
440   return mp_end(pool, p);
441 }
442
443 static char *
444 xml_parse_encoding_name(struct xml_context *ctx)
445 {
446   /* EncName ::= '"' [A-Za-z] ([A-Za-z0-9._] | '-')* '"' | "'" [A-Za-z] ([A-Za-z0-9._] | '-')* "'" */
447   char *p = mp_start_noalign(ctx->pool, 1);
448   uns q = xml_parse_quote(ctx);
449   if (unlikely(!(xml_get_cat(ctx) & XML_CHAR_ENC_SNAME)))
450     xml_fatal(ctx, "Invalid character in the encoding name");
451   while (1)
452     {
453       p = mp_spread(ctx->pool, p, 2);
454       *p++ = xml_last_char(ctx);
455       if (xml_get_char(ctx) == q)
456         break;
457       if (unlikely(!(xml_last_cat(ctx) & XML_CHAR_ENC_NAME)))
458         xml_fatal(ctx, "Invalid character in the encoding name");
459     }
460   *p++ = 0;
461   return mp_end(ctx->pool, p);
462 }
463
464 /* Document/external entity header */
465
466 static inline void
467 xml_init_cats(struct xml_context *ctx, uns mask)
468 {
469   if (!(ctx->flags & XML_FLAG_VERSION_1_1))
470     {
471       ctx->src->refill_cat1 = XML_CHAR_VALID_1_0 & ~XML_CHAR_NEW_LINE_1_0 & ~mask;
472       ctx->src->refill_cat2 = XML_CHAR_NEW_LINE_1_0;
473     }
474   else
475     {
476       ctx->src->refill_cat1 = XML_CHAR_UNRESTRICTED_1_1 & ~XML_CHAR_NEW_LINE_1_1 & ~mask;
477       ctx->src->refill_cat2 = XML_CHAR_NEW_LINE_1_1;
478     }
479 }
480
481 static void
482 xml_init_charconv(struct xml_context *ctx, int cs)
483 {
484   // FIXME: hack
485   struct xml_source *src = ctx->src;
486   TRACE(ctx, "wrapping charset %s", charset_name(cs));
487 #if 0
488   struct conv_context conv;
489   conv_set_charset(&conv, cs, CONV_CHARSET_UTF8);
490   src->refill = xml_refill_libcharset;
491   src->refill_in_to_x = conv.in_to_x;
492 #else
493   src->fb = fb_wrap_charconv_in(src->fb, cs, CONV_CHARSET_UTF8);
494   // FIXME: memory leak
495 #endif
496 }
497
498 void
499 xml_parse_decl(struct xml_context *ctx)
500 {
501   TRACE(ctx, "xml_parse_decl");
502   struct xml_source *src = ctx->src;
503   ctx->flags &= ~XML_FLAG_SRC_EXPECTED_DECL;
504
505   /* Setup valid Unicode ranges and force the reader to abort refill() after each '>', where we can switch encoding or XML version */
506   xml_init_cats(ctx, XML_CHAR_GT);
507
508   /* Initialize the supplied charset (if any) or try to guess it */
509   char *expected_encoding = src->expected_encoding ? : src->fb_encoding;
510   src->refill = xml_refill_utf8;
511   int bom = bpeekc(src->fb);
512   if (bom < 0)
513     ctx->flags |= XML_FLAG_SRC_EOF;
514   if (!src->fb_encoding)
515     {
516       if (bom == 0xfe)
517         src->refill = xml_refill_utf16_be;
518       else if (bom == 0xff)
519         src->refill = xml_refill_utf16_le;
520     }
521   else
522     {
523       int cs = find_charset_by_name(src->fb_encoding);
524       if (cs == CONV_CHARSET_UTF8)
525         {}
526       else if (cs >= 0)
527         {
528           xml_init_charconv(ctx, cs);
529           bom = 0;
530         }
531       else if (strcasecmp(src->fb_encoding, "UTF-16"))
532         {
533           src->refill = xml_refill_utf16_be;
534           if (bom == 0xff)
535             src->refill = xml_refill_utf16_le;
536           if (!src->expected_encoding)
537             expected_encoding = (bom == 0xff) ? "UTF-16LE" : "UTF-16BE";
538         }
539       else if (strcasecmp(src->fb_encoding, "UTF-16BE"))
540         src->refill = xml_refill_utf16_be;
541       else if (strcasecmp(src->fb_encoding, "UTF-16LE"))
542         src->refill = xml_refill_utf16_le;
543       else
544         {
545           xml_error(ctx, "Unknown encoding '%s'", src->fb_encoding);
546           expected_encoding = NULL;
547         }
548     }
549   uns utf16 = src->refill == xml_refill_utf16_le || src->refill == xml_refill_utf16_be;
550   if (bom > 0 && xml_peek_char(ctx) == 0xfeff)
551     xml_skip_char(ctx);
552   else if (utf16)
553     xml_error(ctx, "Missing or corrupted BOM");
554
555   /* Look ahead for presence of XMLDecl or optional TextDecl */
556   if (!(ctx->flags & XML_FLAG_SRC_EOF) && ctx->bstop != src->buf + ARRAY_SIZE(src->buf))
557     xml_refill(ctx);
558   uns doc = ctx->flags & XML_FLAG_SRC_DOCUMENT;
559   u32 *bptr = ctx->bptr;
560   uns have_decl = (12 <= ctx->bstop - ctx->bptr && (bptr[11] & XML_CHAR_WHITE) &&
561     bptr[0] == '<' && bptr[2] == '?' && (bptr[4] & 0xdf) == 'X' && (bptr[6] & 0xdf) == 'M' && (bptr[8] & 0xdf) == 'L');
562   if (!have_decl)
563     {
564       if (doc)
565         xml_fatal(ctx, "Missing or corrupted XML header");
566       else if (expected_encoding && strcasecmp(src->expected_encoding, "UTF-8") && !utf16)
567         xml_error(ctx, "Missing or corrupted entity header");
568       goto exit;
569     }
570   ctx->bptr = bptr + 12;
571   xml_parse_white(ctx, 0);
572
573   /* Parse version string (mandatory in XMLDecl, optional in TextDecl) */
574   if (xml_peek_char(ctx) == 'v')
575     {
576       xml_parse_seq(ctx, "version");
577       xml_parse_eq(ctx);
578       char *version = xml_parse_pubid_literal(ctx, ctx->pool);
579       TRACE(ctx, "version=%s", version);
580       uns v = 0;
581       if (!strcmp(version, "1.1"))
582         v = XML_FLAG_VERSION_1_1;
583       else if (strcmp(version, "1.0"))
584         {
585           xml_error(ctx, "Unknown XML version string '%s'", version);
586           version = "1.0";
587         }
588       if (doc)
589         {
590           ctx->version_str = version;
591           ctx->flags |= v;
592         }
593       else if (v > (ctx->flags & XML_FLAG_VERSION_1_1))
594         xml_error(ctx, "XML 1.1 external entity included from XML 1.0 document");
595       if (!xml_parse_white(ctx, !doc))
596         goto end;
597     }
598   else if (doc)
599     {
600       xml_error(ctx, "Expected XML version");
601       ctx->version_str = "1.0";
602     }
603
604   /* Parse encoding string (optional in XMLDecl, mandatory in TextDecl) */
605   if (xml_peek_char(ctx) == 'e')
606     {
607       xml_parse_seq(ctx, "encoding");
608       xml_parse_eq(ctx);
609       src->decl_encoding = xml_parse_encoding_name(ctx);
610       TRACE(ctx, "encoding=%s", src->decl_encoding);
611       if (!xml_parse_white(ctx, 0))
612         goto end;
613     }
614   else if (!doc)
615     xml_error(ctx, "Expected XML encoding");
616
617   /* Parse whether the document is standalone (optional in XMLDecl) */
618   if (doc && xml_peek_char(ctx) == 's')
619     {
620       xml_parse_seq(ctx, "standalone");
621       xml_parse_eq(ctx);
622       uns c = xml_parse_quote(ctx);
623       if (ctx->standalone = (xml_peek_char(ctx) == 'y'))
624         xml_parse_seq(ctx, "yes");
625       else
626         xml_parse_seq(ctx, "no");
627       xml_parse_char(ctx, c);
628       TRACE(ctx, "standalone=%d", ctx->standalone);
629       xml_parse_white(ctx, 0);
630     }
631 end:
632   xml_parse_seq(ctx, "?>");
633
634   /* Switch to the final encoding */
635   if (src->decl_encoding)
636     {
637       int cs = find_charset_by_name(src->decl_encoding);
638       if (cs < 0 && !expected_encoding)
639         xml_error(ctx, "Unknown encoding '%s'", src->decl_encoding);
640       else if (!src->fb_encoding && cs >= 0 && cs != CONV_CHARSET_UTF8)
641         xml_init_charconv(ctx, cs);
642       else if (expected_encoding && strcasecmp(src->decl_encoding, expected_encoding) && (!utf16 ||
643         !(!strcasecmp(src->decl_encoding, "UTF-16") ||
644          (!strcasecmp(src->decl_encoding, "UTF-16BE") && strcasecmp(expected_encoding, "UTF-16LE")) ||
645          (!strcasecmp(src->decl_encoding, "UTF-16LE") && strcasecmp(expected_encoding, "UTF-16BE")))))
646         xml_error(ctx, "The header contains encoding '%s' instead of expected '%s'", src->decl_encoding, expected_encoding);
647     }
648
649 exit:
650   /* Update valid Unicode ranges */
651   xml_init_cats(ctx, 0);
652 }