]> mj.ucw.cz Git - libucw.git/blob - ucw-xml/source.c
Build: Fixed debian/mk --archonly on debian stretch.
[libucw.git] / ucw-xml / source.c
1 /*
2  *      UCW 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 <ucw-xml/xml.h>
14 #include <ucw-xml/dtd.h>
15 #include <ucw-xml/internals.h>
16 #include <ucw/unicode.h>
17 #include <ucw/ff-unicode.h>
18 #include <charset/charconv.h>
19 #include <charset/fb-charconv.h>
20
21 /*** Character categorization ***/
22
23 #include "obj/ucw-xml/unicat.c"
24
25 static void
26 xml_init_cats(struct xml_context *ctx)
27 {
28   if (!(ctx->flags & XML_VERSION_1_1))
29     {
30       ctx->cat_chars = XML_CHAR_VALID_1_0;
31       ctx->cat_unrestricted = XML_CHAR_VALID_1_0;
32       ctx->cat_new_line = XML_CHAR_NEW_LINE_1_0;
33       ctx->cat_name = XML_CHAR_NAME_1_0;
34       ctx->cat_sname = XML_CHAR_SNAME_1_0;
35     }
36   else
37     {
38       ctx->cat_chars = XML_CHAR_VALID_1_1;
39       ctx->cat_unrestricted = XML_CHAR_UNRESTRICTED_1_1;
40       ctx->cat_new_line = XML_CHAR_NEW_LINE_1_1;
41       ctx->cat_name = XML_CHAR_NAME_1_1;
42       ctx->cat_sname = XML_CHAR_SNAME_1_1;
43     }
44 }
45
46 /*** Reading of document/external entities ***/
47
48 static void NONRET
49 xml_eof(struct xml_context *ctx)
50 {
51   ctx->err_msg = "Unexpected EOF";
52   ctx->err_code = XML_ERR_EOF;
53   xml_throw(ctx);
54 }
55
56 void NONRET
57 xml_fatal_nested(struct xml_context *ctx)
58 {
59   xml_fatal(ctx, "Entity is not nested correctly");
60 }
61
62 static inline void
63 xml_add_char(u32 **bstop, uint c)
64 {
65   *(*bstop)++ = c;
66   *(*bstop)++ = xml_char_cat(c);
67 }
68
69 struct xml_source *
70 xml_push_source(struct xml_context *ctx)
71 {
72   xml_push(ctx);
73   struct xml_source *src = ctx->src;
74   if (src)
75     {
76       src->bptr = ctx->bptr;
77       src->bstop = ctx->bstop;
78     }
79   src = mp_alloc_zero(ctx->stack, sizeof(*src));
80   src->next = ctx->src;
81   src->saved_depth = ctx->depth;
82   ctx->src = src;
83   ctx->flags &= ~(XML_SRC_EOF | XML_SRC_EXPECTED_DECL | XML_SRC_DOCUMENT);
84   ctx->bstop = ctx->bptr = src->buf;
85   ctx->depth = 0;
86   return src;
87 }
88
89 struct xml_source *
90 xml_push_fastbuf(struct xml_context *ctx, struct fastbuf *fb)
91 {
92   struct xml_source *src = xml_push_source(ctx);
93   src->fb = fb;
94   return src;
95 }
96
97 static void
98 xml_close_source(struct xml_source *src)
99 {
100   bclose(src->fb);
101   if (src->wrapped_fb)
102     bclose(src->wrapped_fb);
103 }
104
105 static void
106 xml_pop_source(struct xml_context *ctx)
107 {
108   TRACE(ctx, "pop_source");
109   if (unlikely(ctx->depth != 0))
110     xml_fatal(ctx, "Unexpected end of entity");
111   struct xml_source *src = ctx->src;
112   if (!src)
113     xml_fatal(ctx, "Undefined source");
114   xml_close_source(src);
115   ctx->depth = src->saved_depth;
116   ctx->src = src = src->next;
117   if (src)
118     {
119       ctx->bptr = src->bptr;
120       ctx->bstop = src->bstop;
121     }
122   xml_pop(ctx);
123   if (unlikely(!src))
124     xml_eof(ctx);
125 }
126
127 void
128 xml_sources_cleanup(struct xml_context *ctx)
129 {
130   struct xml_source *s;
131   while (s = ctx->src)
132     {
133       ctx->src = s->next;
134       xml_close_source(s);
135     }
136 }
137
138 static void xml_refill_utf8(struct xml_context *ctx);
139
140 void
141 xml_def_resolve_entity(struct xml_context *ctx, struct xml_dtd_entity *ent UNUSED)
142 {
143   xml_error(ctx, "References to external entities are not supported");
144 }
145
146 void
147 xml_push_entity(struct xml_context *ctx, struct xml_dtd_entity *ent)
148 {
149   TRACE(ctx, "xml_push_entity");
150   struct xml_source *src;
151   if (ent->flags & XML_DTD_ENTITY_EXTERNAL)
152     {
153       ASSERT(ctx->h_resolve_entity);
154       ctx->h_resolve_entity(ctx, ent);
155       ctx->flags |= XML_SRC_EXPECTED_DECL;
156       src = ctx->src;
157     }
158   else
159     {
160       src = xml_push_source(ctx);
161       fbbuf_init_read(src->fb = &src->wrap_fb, ent->text, strlen(ent->text), 0);
162     }
163   src->refill = xml_refill_utf8;
164   src->refill_cat1 = ctx->cat_unrestricted & ~ctx->cat_new_line;
165   src->refill_cat2 = ctx->cat_new_line;
166 }
167
168 static uint
169 xml_error_restricted(struct xml_context *ctx, uint c)
170 {
171   if (c == ~1U)
172     xml_error(ctx, "Corrupted encoding");
173   else
174     xml_error(ctx, "Restricted char U+%04X", c);
175   return UNI_REPLACEMENT;
176 }
177
178 static void xml_parse_decl(struct xml_context *ctx);
179
180 #define REFILL(ctx, func, params...)                                                    \
181   struct xml_source *src = ctx->src;                                                    \
182   struct fastbuf *fb = src->fb;                                                         \
183   if (ctx->bptr == ctx->bstop)                                                          \
184     ctx->bptr = ctx->bstop = src->buf;                                                  \
185   uint c, t1 = src->refill_cat1, t2 = src->refill_cat2, row = src->row;                 \
186   u32 *bend = src->buf + ARRAY_SIZE(src->buf), *bstop = ctx->bstop,                     \
187       *last_0xd = src->pending_0xd ? bstop : NULL;                                      \
188   do                                                                                    \
189     {                                                                                   \
190       c = func(fb, ##params);                                                           \
191       uint t = xml_char_cat(c);                                                         \
192       if (t & t1)                                                                       \
193         /* Typical branch */                                                            \
194         *bstop++ = c, *bstop++ = t;                                                     \
195       else if (t & t2)                                                                  \
196         {                                                                               \
197           /* New line */                                                                \
198           /* XML 1.0: 0xA | 0xD | 0xD 0xA */                                            \
199           /* XML 1.1: 0xA | 0xD | 0xD 0xA | 0x85 | 0xD 0x85 | 0x2028 */                 \
200           if (c == 0xd)                                                                 \
201             last_0xd = bstop + 2;                                                       \
202           else if (c != 0x2028 && last_0xd == bstop)                                    \
203             {                                                                           \
204               last_0xd = NULL;                                                          \
205               continue;                                                                 \
206             }                                                                           \
207           xml_add_char(&bstop, 0xa), row++;                                             \
208         }                                                                               \
209       else if (c == '>')                                                                \
210         {                                                                               \
211           /* Used only in XML/TextDecl to switch the encoding */                        \
212           *bstop++ = c, *bstop++ = t;                                                   \
213           break;                                                                        \
214         }                                                                               \
215       else if (~c)                                                                      \
216         /* Restricted character */                                                      \
217         xml_add_char(&bstop, xml_error_restricted(ctx, c));                             \
218       else                                                                              \
219         {                                                                               \
220           /* EOF */                                                                     \
221           ctx->flags |= XML_SRC_EOF;                                                    \
222           break;                                                                        \
223         }                                                                               \
224     }                                                                                   \
225   while (bstop < bend);                                                                 \
226   src->pending_0xd = (last_0xd == bstop);                                               \
227   ctx->bstop = bstop;                                                                   \
228   src->row = row;
229
230 static void
231 xml_refill_utf8(struct xml_context *ctx)
232 {
233   REFILL(ctx, bget_utf8_repl, ~1U);
234 }
235
236 static void
237 xml_refill_utf16_le(struct xml_context *ctx)
238 {
239   REFILL(ctx, bget_utf16_le_repl, ~1U);
240 }
241
242 static void
243 xml_refill_utf16_be(struct xml_context *ctx)
244 {
245   REFILL(ctx, bget_utf16_be_repl, ~1U);
246 }
247
248 #undef REFILL
249
250 void
251 xml_refill(struct xml_context *ctx)
252 {
253   do
254     {
255       if (ctx->flags & XML_SRC_EOF)
256         xml_pop_source(ctx);
257       else if (ctx->flags & XML_SRC_EXPECTED_DECL)
258         xml_parse_decl(ctx);
259       else
260         {
261           ctx->src->refill(ctx);
262           TRACE(ctx, "refilled %u characters", (uint)((ctx->bstop - ctx->bptr) / 2));
263         }
264     }
265   while (ctx->bptr == ctx->bstop);
266 }
267
268 static uint
269 xml_source_row(struct xml_context *ctx, struct xml_source *src)
270 {
271   uint row = src->row;
272   for (u32 *p = ctx->bstop; p != ctx->bptr; p -= 2)
273     if (p[-1] & src->refill_cat2)
274       row--;
275   return row + 1;
276 }
277
278 uint
279 xml_row(struct xml_context *ctx)
280 {
281   return ctx->src ? xml_source_row(ctx, ctx->src) : 0;
282 }
283
284 /* Document/external entity header */
285
286 static char *
287 xml_parse_encoding_name(struct xml_context *ctx)
288 {
289   /* EncName ::= '"' [A-Za-z] ([A-Za-z0-9._] | '-')* '"' | "'" [A-Za-z] ([A-Za-z0-9._] | '-')* "'" */
290   char *p = mp_start_noalign(ctx->pool, 1);
291   uint q = xml_parse_quote(ctx);
292   if (unlikely(!(xml_get_cat(ctx) & XML_CHAR_ENC_SNAME)))
293     xml_fatal(ctx, "Invalid character in the encoding name");
294   while (1)
295     {
296       p = mp_spread(ctx->pool, p, 2);
297       *p++ = xml_last_char(ctx);
298       if (xml_get_char(ctx) == q)
299         break;
300       if (unlikely(!(xml_last_cat(ctx) & XML_CHAR_ENC_NAME)))
301         xml_fatal(ctx, "Invalid character in the encoding name");
302     }
303   *p++ = 0;
304   return mp_end(ctx->pool, p);
305 }
306
307 static void
308 xml_init_charconv(struct xml_context *ctx, int cs)
309 {
310   // XXX: with a direct access to libucw-charset tables could be faster
311   struct xml_source *src = ctx->src;
312   TRACE(ctx, "wrapping charset %s", charset_name(cs));
313   src->wrapped_fb = src->fb;
314   src->fb = fb_wrap_charconv_in(src->fb, cs, CONV_CHARSET_UTF8);
315 }
316
317 static void
318 xml_parse_decl(struct xml_context *ctx)
319 {
320   TRACE(ctx, "xml_parse_decl");
321   struct xml_source *src = ctx->src;
322   ctx->flags &= ~XML_SRC_EXPECTED_DECL;
323   uint doc = ctx->flags & XML_SRC_DOCUMENT;
324
325   /* Setup valid Unicode ranges and force the reader to abort refill() after each '>', where we can switch encoding or XML version */
326   if (doc)
327     xml_init_cats(ctx);
328   src->refill_cat1 = ctx->cat_unrestricted & ~ctx->cat_new_line & ~XML_CHAR_GT;
329   src->refill_cat2 = ctx->cat_new_line;
330
331   /* Initialize the supplied charset (if any) or try to guess it */
332   char *expected_encoding = src->expected_encoding;
333   src->refill = xml_refill_utf8;
334   int bom = bpeekc(src->fb);
335   if (bom < 0)
336     ctx->flags |= XML_SRC_EOF;
337   if (!src->fb_encoding)
338     {
339       if (bom == 0xfe)
340         src->refill = xml_refill_utf16_be;
341       else if (bom == 0xff)
342         src->refill = xml_refill_utf16_le;
343     }
344   else
345     {
346       int cs = find_charset_by_name(src->fb_encoding);
347       if (cs == CONV_CHARSET_UTF8)
348         {}
349       else if (cs >= 0)
350         {
351           xml_init_charconv(ctx, cs);
352           bom = 0;
353         }
354       else if (strcasecmp(src->fb_encoding, "UTF-16"))
355         {
356           src->refill = xml_refill_utf16_be;
357           if (bom == 0xff)
358             src->refill = xml_refill_utf16_le;
359         }
360       else if (strcasecmp(src->fb_encoding, "UTF-16BE"))
361         src->refill = xml_refill_utf16_be;
362       else if (strcasecmp(src->fb_encoding, "UTF-16LE"))
363         src->refill = xml_refill_utf16_le;
364       else
365         {
366           xml_error(ctx, "Unknown encoding '%s'", src->fb_encoding);
367           expected_encoding = NULL;
368         }
369     }
370   uint utf16 = src->refill == xml_refill_utf16_le || src->refill == xml_refill_utf16_be;
371   if (utf16)
372     src->fb_encoding = (src->refill == xml_refill_utf16_be) ? "UTF-16BE" : "UTF-16LE";
373   if (!expected_encoding)
374     expected_encoding = src->fb_encoding;
375   if (bom > 0 && xml_peek_char(ctx) == 0xfeff)
376     xml_skip_char(ctx);
377   else if (utf16)
378     xml_error(ctx, "Missing or corrupted BOM");
379   TRACE(ctx, "Initial encoding=%s", src->fb_encoding ? : "?");
380
381   /* Look ahead for presence of XMLDecl or optional TextDecl */
382   if (!(ctx->flags & XML_SRC_EOF) && ctx->bstop != src->buf + ARRAY_SIZE(src->buf))
383     xml_refill(ctx);
384   u32 *bptr = ctx->bptr;
385   uint have_decl = (12 <= ctx->bstop - ctx->bptr && (bptr[11] & XML_CHAR_WHITE) &&
386     bptr[0] == '<' && bptr[2] == '?' && (bptr[4] & 0xdf) == 'X' && (bptr[6] & 0xdf) == 'M' && (bptr[8] & 0xdf) == 'L');
387   if (!have_decl)
388     {
389       if (doc)
390         xml_fatal(ctx, "Missing or corrupted XML header");
391       else if (expected_encoding && strcasecmp(src->expected_encoding, "UTF-8") && !utf16)
392         xml_error(ctx, "Missing or corrupted entity header");
393       goto exit;
394     }
395   ctx->bptr = bptr + 12;
396   xml_parse_white(ctx, 0);
397
398   /* Parse version string (mandatory in XMLDecl, optional in TextDecl) */
399   if (xml_peek_char(ctx) == 'v')
400     {
401       xml_parse_seq(ctx, "version");
402       xml_parse_eq(ctx);
403       char *version = xml_parse_pubid_literal(ctx, ctx->pool);
404       TRACE(ctx, "version=%s", version);
405       uint v = 0;
406       if (!strcmp(version, "1.1"))
407         v = XML_VERSION_1_1;
408       else if (strcmp(version, "1.0"))
409         {
410           xml_error(ctx, "Unknown XML version string '%s'", version);
411           version = "1.0";
412         }
413       if (doc)
414         {
415           ctx->version_str = version;
416           ctx->flags |= v;
417         }
418       else if (v > (ctx->flags & XML_VERSION_1_1))
419         xml_error(ctx, "XML 1.1 external entity included from XML 1.0 document");
420       if (!xml_parse_white(ctx, !doc))
421         goto end;
422     }
423   else if (doc)
424     {
425       xml_error(ctx, "Expected XML version");
426       ctx->version_str = "1.0";
427     }
428
429   /* Parse encoding string (optional in XMLDecl, mandatory in TextDecl) */
430   if (xml_peek_char(ctx) == 'e')
431     {
432       xml_parse_seq(ctx, "encoding");
433       xml_parse_eq(ctx);
434       src->decl_encoding = xml_parse_encoding_name(ctx);
435       TRACE(ctx, "encoding=%s", src->decl_encoding);
436       if (!xml_parse_white(ctx, 0))
437         goto end;
438     }
439   else if (!doc)
440     xml_error(ctx, "Expected XML encoding");
441
442   /* Parse whether the document is standalone (optional in XMLDecl) */
443   if (doc && xml_peek_char(ctx) == 's')
444     {
445       xml_parse_seq(ctx, "standalone");
446       xml_parse_eq(ctx);
447       uint c = xml_parse_quote(ctx);
448       if (ctx->standalone = (xml_peek_char(ctx) == 'y'))
449         xml_parse_seq(ctx, "yes");
450       else
451         xml_parse_seq(ctx, "no");
452       xml_parse_char(ctx, c);
453       TRACE(ctx, "standalone=%d", ctx->standalone);
454       xml_parse_white(ctx, 0);
455     }
456 end:
457   xml_parse_seq(ctx, "?>");
458
459   /* Switch to the final encoding */
460   if (src->decl_encoding)
461     {
462       int cs = find_charset_by_name(src->decl_encoding);
463       if (cs < 0 && !expected_encoding)
464         xml_error(ctx, "Unknown encoding '%s'", src->decl_encoding);
465       else if (!src->fb_encoding && cs >= 0 && cs != CONV_CHARSET_UTF8)
466         {
467           xml_init_charconv(ctx, cs);
468           src->fb_encoding = src->decl_encoding;
469         }
470       else if (expected_encoding && strcasecmp(src->decl_encoding, expected_encoding) && (!utf16 ||
471         !(!strcasecmp(src->decl_encoding, "UTF-16") ||
472          (!strcasecmp(src->decl_encoding, "UTF-16BE") && strcasecmp(expected_encoding, "UTF-16LE")) ||
473          (!strcasecmp(src->decl_encoding, "UTF-16LE") && strcasecmp(expected_encoding, "UTF-16BE")))))
474         xml_error(ctx, "The header contains encoding '%s' instead of expected '%s'", src->decl_encoding, expected_encoding);
475     }
476   if (!src->fb_encoding)
477     src->fb_encoding = "UTF-8";
478   TRACE(ctx, "Final encoding=%s", src->fb_encoding);
479
480 exit:
481   /* Update valid Unicode ranges */
482   if (doc)
483     xml_init_cats(ctx);
484   src->refill_cat1 = ctx->cat_unrestricted & ~ctx->cat_new_line;
485   src->refill_cat2 = ctx->cat_new_line;
486 }