+/*** Character data ***/
+
+void
+xml_spout_chars(struct fastbuf *fb)
+{
+ if (fb->bptr < fb->bufend)
+ return;
+ struct xml_context *ctx = SKIP_BACK(struct xml_context, chars, fb);
+ struct mempool *pool = ctx->pool;
+ if (fb->bufend != fb->buffer)
+ {
+ TRACE(ctx, "growing chars");
+ uns len = fb->bufend - fb->buffer;
+ uns reported = fb->bstop - fb->buffer;
+ fb->buffer = mp_expand(pool);
+ fb->bufend = fb->buffer + mp_avail(pool);
+ fb->bptr = fb->buffer + len;
+ fb->bstop = fb->buffer + reported;
+ }
+ else
+ {
+ TRACE(ctx, "starting chars");
+ mp_save(pool, &ctx->chars_state);
+ fb->bptr = fb->buffer = fb->bstop = mp_start_noalign(pool, 2);
+ fb->bufend = fb->buffer + mp_avail(pool) - 1;
+ }
+}
+
+static inline uns
+xml_end_chars(struct xml_context *ctx, char **out)
+{
+ struct fastbuf *fb = &ctx->chars;
+ uns len = fb->bptr - fb->buffer;
+ if (len)
+ {
+ TRACE(ctx, "ending chars");
+ *fb->bptr = 0;
+ *out = mp_end(ctx->pool, fb->bptr + 1);
+ fb->bufend = fb->bstop = fb->bptr = fb->buffer;
+ }
+ return len;
+}
+
+static inline uns
+xml_report_chars(struct xml_context *ctx, char **out)
+{
+ struct fastbuf *fb = &ctx->chars;
+ uns len = fb->bptr - fb->buffer;
+ if (len)
+ {
+ *fb->bptr = 0;
+ *out = fb->bstop;
+ fb->bstop = fb->bptr;
+ }
+ return len;
+}
+
+static inline uns
+xml_flush_chars(struct xml_context *ctx)
+{
+ char *text, *rtext;
+ uns len = xml_end_chars(ctx, &text), rlen;
+ if (len)
+ {
+ if (ctx->flags & XML_NO_CHARS)
+ {
+ if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_ignorable)
+ ctx->h_ignorable(ctx, text, len);
+ mp_restore(ctx->pool, &ctx->chars_state);
+ return 0;
+ }
+ if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_block && (rlen = xml_report_chars(ctx, &rtext)))
+ ctx->h_block(ctx, rtext, rlen);
+ if (!(ctx->flags & XML_ALLOC_CHARS) && !(ctx->flags & XML_REPORT_CHARS))
+ {
+ mp_restore(ctx->pool, &ctx->chars_state);
+ return 0;
+ }
+ struct xml_node *n = xml_push_dom(ctx, &ctx->chars_state);
+ n->type = XML_NODE_CHARS;
+ n->text = text;
+ n->len = len;
+ if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_chars)
+ ctx->h_chars(ctx);
+ }
+ return len;
+}
+
+static inline void
+xml_pop_chars(struct xml_context *ctx)
+{
+ xml_pop_dom(ctx, !(ctx->flags & XML_ALLOC_CHARS));
+ TRACE(ctx, "pop_chars");
+}
+
+static inline void
+xml_append_chars(struct xml_context *ctx)
+{
+ TRACE(ctx, "append_chars");
+ struct fastbuf *out = &ctx->chars;
+ if (ctx->flags & XML_NO_CHARS)
+ while (xml_get_char(ctx) != '<')
+ if (xml_last_cat(ctx) & XML_CHAR_WHITE)
+ bput_utf8_32(out, xml_last_char(ctx));
+ else
+ {
+ xml_error(ctx, "This element must not contain character data");
+ while (xml_get_char(ctx) != '<');
+ break;
+ }
+ else
+ while (xml_get_char(ctx) != '<')
+ if (xml_last_char(ctx) == '&')
+ {
+ xml_inc(ctx);
+ xml_parse_ref(ctx);
+ }
+ else
+ bput_utf8_32(out, xml_last_char(ctx));
+ xml_unget_char(ctx);
+}
+
+/*** CDATA sections ***/
+
+static void
+xml_skip_cdata(struct xml_context *ctx)
+{
+ TRACE(ctx, "skip_cdata");
+ xml_parse_seq(ctx, "CDATA[");
+ while (xml_get_char(ctx) != ']' || xml_get_char(ctx) != ']' || xml_get_char(ctx) != '>');
+ xml_dec(ctx);
+}
+
+static void
+xml_append_cdata(struct xml_context *ctx)
+{
+ /* CDSect :== '<![CDATA[' (Char* - (Char* ']]>' Char*)) ']]>'
+ * Already parsed: '<![' */
+ TRACE(ctx, "append_cdata");
+ if (ctx->flags & XML_NO_CHARS)
+ {
+ xml_error(ctx, "This element must not contain CDATA");
+ xml_skip_cdata(ctx);
+ return;
+ }
+ xml_parse_seq(ctx, "CDATA[");
+ struct fastbuf *out = &ctx->chars;
+ uns rlen;
+ char *rtext;
+ if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_block && (rlen = xml_report_chars(ctx, &rtext)))
+ ctx->h_block(ctx, rtext, rlen);
+ while (1)
+ {
+ if (xml_get_char(ctx) == ']')
+ {
+ if (xml_get_char(ctx) == ']')
+ if (xml_get_char(ctx) == '>')
+ break;
+ else
+ bputc(out, ']');
+ bputc(out, ']');
+ }
+ bput_utf8_32(out, xml_last_char(ctx));
+ }
+ if ((ctx->flags & XML_REPORT_CHARS) && ctx->h_cdata && (rlen = xml_report_chars(ctx, &rtext)))
+ ctx->h_cdata(ctx, rtext, rlen);
+ xml_dec(ctx);
+}
+