dummy_fe: add dummy frontend server code. [1/1]

PD#SWPL-158421

Problem:
Needs dummy frontend.

Solution:
support dummy fe.
support xml and json config file.

Verify:
SC2/S7

Change-Id: I4a70700a39019747a343a8cdbd0da8f5568e4393
Signed-off-by: Yahui Han <yahui.han@amlogic.com>
diff --git a/dummy_fe/Android.bp b/dummy_fe/Android.bp
new file mode 100644
index 0000000..568b57f
--- /dev/null
+++ b/dummy_fe/Android.bp
@@ -0,0 +1,33 @@
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+    ],
+}
+
+cc_binary {
+    name: "dummy_fe_server",
+    vendor: true,
+
+    srcs: [
+        "dummy_fe_server.c",
+        "libcJSON/cJSON.c",
+        "libexpat/xmlparse.c",
+        "libexpat/xmltok.c",
+        "libexpat/xmlrole.c",
+	],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-DHAVE_EXPAT_CONFIG_H",
+        "-UWIN32_LEAN_AND_MEAN",
+        "-include stdio.h",
+    ],
+
+    local_include_dirs: ["libcJSON", "libexpat"],
+    shared_libs: [
+        "libcutils",
+        "libc",
+    ],
+}
diff --git a/dummy_fe/dmx.h b/dummy_fe/dmx.h
new file mode 100644
index 0000000..39fa47d
--- /dev/null
+++ b/dummy_fe/dmx.h
@@ -0,0 +1,549 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * dmx.h
+ *
+ * Copyright (C) 2000 Marcus Metzler <marcus@convergence.de>
+ *                  & Ralph  Metzler <ralph@convergence.de>
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _UAPI_DVBDMX_H_
+#define _UAPI_DVBDMX_H_
+
+#include <linux/types.h>
+#include <asm/ioctl.h>
+#ifndef __KERNEL__
+#include <time.h>
+#endif
+
+#define CONFIG_AMLOGIC_DVB_COMPAT
+#define DMX_FILTER_SIZE 16
+
+/**
+ * enum dmx_output - Output for the demux.
+ *
+ * @DMX_OUT_DECODER:
+ *	Streaming directly to decoder.
+ * @DMX_OUT_TAP:
+ *	Output going to a memory buffer (to be retrieved via the read command).
+ *	Delivers the stream output to the demux device on which the ioctl
+ *	is called.
+ * @DMX_OUT_TS_TAP:
+ *	Output multiplexed into a new TS (to be retrieved by reading from the
+ *	logical DVR device). Routes output to the logical DVR device
+ *	``/dev/dvb/adapter?/dvr?``, which delivers a TS multiplexed from all
+ *	filters for which @DMX_OUT_TS_TAP was specified.
+ * @DMX_OUT_TSDEMUX_TAP:
+ *	Like @DMX_OUT_TS_TAP but retrieved from the DMX device.
+ */
+enum dmx_output {
+	DMX_OUT_DECODER,
+	DMX_OUT_TAP,
+	DMX_OUT_TS_TAP,
+	DMX_OUT_TSDEMUX_TAP
+};
+
+
+/**
+ * enum dmx_input - Input from the demux.
+ *
+ * @DMX_IN_FRONTEND:	Input from a front-end device.
+ * @DMX_IN_DVR:		Input from the logical DVR device.
+ */
+enum dmx_input {
+	DMX_IN_FRONTEND,
+	DMX_IN_DVR
+};
+
+/**
+ * enum dmx_ts_pes - type of the PES filter.
+ *
+ * @DMX_PES_AUDIO0:	first audio PID. Also referred as @DMX_PES_AUDIO.
+ * @DMX_PES_VIDEO0:	first video PID. Also referred as @DMX_PES_VIDEO.
+ * @DMX_PES_TELETEXT0:	first teletext PID. Also referred as @DMX_PES_TELETEXT.
+ * @DMX_PES_SUBTITLE0:	first subtitle PID. Also referred as @DMX_PES_SUBTITLE.
+ * @DMX_PES_PCR0:	first Program Clock Reference PID.
+ *			Also referred as @DMX_PES_PCR.
+ *
+ * @DMX_PES_AUDIO1:	second audio PID.
+ * @DMX_PES_VIDEO1:	second video PID.
+ * @DMX_PES_TELETEXT1:	second teletext PID.
+ * @DMX_PES_SUBTITLE1:	second subtitle PID.
+ * @DMX_PES_PCR1:	second Program Clock Reference PID.
+ *
+ * @DMX_PES_AUDIO2:	third audio PID.
+ * @DMX_PES_VIDEO2:	third video PID.
+ * @DMX_PES_TELETEXT2:	third teletext PID.
+ * @DMX_PES_SUBTITLE2:	third subtitle PID.
+ * @DMX_PES_PCR2:	third Program Clock Reference PID.
+ *
+ * @DMX_PES_AUDIO3:	fourth audio PID.
+ * @DMX_PES_VIDEO3:	fourth video PID.
+ * @DMX_PES_TELETEXT3:	fourth teletext PID.
+ * @DMX_PES_SUBTITLE3:	fourth subtitle PID.
+ * @DMX_PES_PCR3:	fourth Program Clock Reference PID.
+ *
+ * @DMX_PES_OTHER:	any other PID.
+ */
+
+enum dmx_ts_pes {
+	DMX_PES_AUDIO0,
+	DMX_PES_VIDEO0,
+	DMX_PES_TELETEXT0,
+	DMX_PES_SUBTITLE0,
+	DMX_PES_PCR0,
+
+	DMX_PES_AUDIO1,
+	DMX_PES_VIDEO1,
+	DMX_PES_TELETEXT1,
+	DMX_PES_SUBTITLE1,
+	DMX_PES_PCR1,
+
+	DMX_PES_AUDIO2,
+	DMX_PES_VIDEO2,
+	DMX_PES_TELETEXT2,
+	DMX_PES_SUBTITLE2,
+	DMX_PES_PCR2,
+
+	DMX_PES_AUDIO3,
+	DMX_PES_VIDEO3,
+	DMX_PES_TELETEXT3,
+	DMX_PES_SUBTITLE3,
+	DMX_PES_PCR3,
+
+	DMX_PES_OTHER
+};
+
+#define DMX_PES_AUDIO    DMX_PES_AUDIO0
+#define DMX_PES_VIDEO    DMX_PES_VIDEO0
+#define DMX_PES_TELETEXT DMX_PES_TELETEXT0
+#define DMX_PES_SUBTITLE DMX_PES_SUBTITLE0
+#define DMX_PES_PCR      DMX_PES_PCR0
+
+
+
+/**
+ * struct dmx_filter - Specifies a section header filter.
+ *
+ * @filter: bit array with bits to be matched at the section header.
+ * @mask: bits that are valid at the filter bit array.
+ * @mode: mode of match: if bit is zero, it will match if equal (positive
+ *	  match); if bit is one, it will match if the bit is negated.
+ *
+ * Note: All arrays in this struct have a size of DMX_FILTER_SIZE (16 bytes).
+ */
+struct dmx_filter {
+	__u8  filter[DMX_FILTER_SIZE];
+	__u8  mask[DMX_FILTER_SIZE];
+	__u8  mode[DMX_FILTER_SIZE];
+};
+
+/**
+ * struct dmx_sct_filter_params - Specifies a section filter.
+ *
+ * @pid: PID to be filtered.
+ * @filter: section header filter, as defined by &struct dmx_filter.
+ * @timeout: maximum time to filter, in milliseconds.
+ * @flags: extra flags for the section filter.
+ *
+ * Carries the configuration for a MPEG-TS section filter.
+ *
+ * The @flags can be:
+ *
+ *	- %DMX_CHECK_CRC - only deliver sections where the CRC check succeeded;
+ *	- %DMX_ONESHOT - disable the section filter after one section
+ *	  has been delivered;
+ *	- %DMX_IMMEDIATE_START - Start filter immediately without requiring a
+ *	  :ref:`DMX_START`.
+ */
+struct dmx_sct_filter_params {
+	__u16             pid;
+	struct dmx_filter filter;
+	__u32             timeout;
+	__u32             flags;
+#define DMX_CHECK_CRC       1
+#define DMX_ONESHOT         2
+#define DMX_IMMEDIATE_START 4
+#ifdef CONFIG_AMLOGIC_DVB_COMPAT
+#define DMX_USE_SWFILTER    0x100
+
+/*bit 8~15 for mem sec_level*/
+#define DMX_MEM_SEC_LEVEL1   (1 << 10)
+#define DMX_MEM_SEC_LEVEL2   (2 << 10)
+#define DMX_MEM_SEC_LEVEL3   (3 << 10)
+#define DMX_MEM_SEC_LEVEL4   (4 << 10)
+#define DMX_MEM_SEC_LEVEL5   (5 << 10)
+#define DMX_MEM_SEC_LEVEL6   (6 << 10)
+#define DMX_MEM_SEC_LEVEL7   (7 << 10)
+#endif
+};
+
+#ifdef CONFIG_AMLOGIC_DVB_COMPAT
+
+enum dmx_input_source {
+	INPUT_DEMOD,
+	INPUT_LOCAL,
+	INPUT_LOCAL_SEC
+};
+
+/**
+ * struct dmx_non_sec_es_header - non-sec Elementary Stream (ES) Header
+ *
+ * @pts_dts_flag:[1:0], 10:pts valid, 01:dts valid
+ * @pts_dts_flag:[3:2], 10:scb is scrambled, 01:pscp invalid
+ * @pts:	pts value
+ * @dts:	dts value
+ * @len:	data len
+ */
+struct dmx_non_sec_es_header {
+	__u8 pts_dts_flag;
+	__u64 pts;
+	__u64 dts;
+	__u32 len;
+};
+
+/**
+ * struct dmx_sec_es_data - sec Elementary Stream (ES)
+ *
+ * @pts_dts_flag:[1:0], 10:pts valid, 01:dts valid
+ * @pts_dts_flag:[3:2], 10:scb is scrambled, 01:pscp invalid
+ * @pts:	pts value
+ * @dts:	dts value
+ * @buf_start:	buf start addr
+ * @buf_end:	buf end addr
+ * @data_start: data start addr
+ * @data_end: data end addr
+ */
+struct dmx_sec_es_data {
+	__u8 pts_dts_flag;
+	__u64 pts;
+	__u64 dts;
+	__u32 buf_start;
+	__u32 buf_end;
+	__u32 data_start;
+	__u32 data_end;
+};
+
+struct dmx_sec_ts_data {
+	__u32 buf_start;
+	__u32 buf_end;
+	__u32 data_start;
+	__u32 data_end;
+};
+
+struct dmx_temi_data {
+	__u8 pts_dts_flag;
+	__u64 pts;
+	__u64 dts;
+	__u8 temi[188];
+};
+
+enum dmx_audio_format {
+	AUDIO_UNKNOWN = 0,	/* unknown media */
+	AUDIO_MPX = 1,		/* mpeg audio MP2/MP3 */
+	AUDIO_AC3 = 2,		/* Dolby AC3/EAC3 */
+	AUDIO_AAC_ADTS = 3,	/* AAC-ADTS */
+	AUDIO_AAC_LOAS = 4,	/* AAC-LOAS */
+	AUDIO_DTS = 5,		/* DTS */
+	AUDIO_MAX
+};
+
+struct dmx_mem_info {
+	__u32 dmx_total_size;
+	__u32 dmx_buf_phy_start;
+	__u32 dmx_free_size;
+	__u32 dvb_core_total_size;
+	__u32 dvb_core_free_size;
+	__u32 wp_offset;
+	__u64 newest_pts;
+};
+
+struct dmx_sec_mem {
+	__u32 buff;
+	__u32 size;
+};
+#endif
+
+/**
+ * struct dmx_pes_filter_params - Specifies Packetized Elementary Stream (PES)
+ *	filter parameters.
+ *
+ * @pid:	PID to be filtered.
+ * @input:	Demux input, as specified by &enum dmx_input.
+ * @output:	Demux output, as specified by &enum dmx_output.
+ * @pes_type:	Type of the pes filter, as specified by &enum dmx_pes_type.
+ * @flags:	Demux PES flags.
+ */
+struct dmx_pes_filter_params {
+	__u16           pid;
+	enum dmx_input  input;
+	enum dmx_output output;
+	enum dmx_ts_pes pes_type;
+	__u32           flags;
+#ifdef CONFIG_AMLOGIC_DVB_COMPAT
+/*bit 8~15 for mem sec_level*/
+#define DMX_MEM_SEC_LEVEL1   (1 << 10)
+#define DMX_MEM_SEC_LEVEL2   (2 << 10)
+#define DMX_MEM_SEC_LEVEL3   (3 << 10)
+#define DMX_MEM_SEC_LEVEL4   (4 << 10)
+#define DMX_MEM_SEC_LEVEL5   (5 << 10)
+#define DMX_MEM_SEC_LEVEL6   (6 << 10)
+#define DMX_MEM_SEC_LEVEL7   (7 << 10)
+
+/*bit 16~23 for output */
+#define DMX_ES_OUTPUT        (1 << 16)
+/*set raw mode, it will send the struct dmx_sec_es_data, not es data*/
+#define DMX_OUTPUT_RAW_MODE	 (1 << 17)
+#define DMX_TEMI_FLAGS       (1 << 18)
+
+/*24~31 one byte for audio type, dmx_audio_format_t*/
+#define DMX_AUDIO_FORMAT_BIT 24
+
+#endif
+};
+
+typedef struct dmx_caps {
+    __u32 caps;
+    int num_decoders;
+} dmx_caps_t;
+
+typedef enum dmx_source {
+    DMX_SOURCE_FRONT0 = 0,
+    DMX_SOURCE_FRONT1,
+    DMX_SOURCE_FRONT2,
+    DMX_SOURCE_FRONT3,
+    DMX_SOURCE_DVR0   = 16,
+    DMX_SOURCE_DVR1,
+    DMX_SOURCE_DVR2,
+    DMX_SOURCE_DVR3,
+
+#ifdef CONFIG_AMLOGIC_DVB_COMPAT
+    DMX_SOURCE_FRONT0_OFFSET = 100,
+    DMX_SOURCE_FRONT1_OFFSET,
+    DMX_SOURCE_FRONT2_OFFSET
+#endif
+} dmx_source_t;
+
+/**
+ * struct dmx_stc - Stores System Time Counter (STC) information.
+ *
+ * @num: input data: number of the STC, from 0 to N.
+ * @base: output: divisor for STC to get 90 kHz clock.
+ * @stc: output: stc in @base * 90 kHz units.
+ */
+struct dmx_stc {
+	unsigned int num;
+	unsigned int base;
+	__u64 stc;
+};
+
+/**
+ * enum dmx_buffer_flags - DMX memory-mapped buffer flags
+ *
+ * @DMX_BUFFER_FLAG_HAD_CRC32_DISCARD:
+ *	Indicates that the Kernel discarded one or more frames due to wrong
+ *	CRC32 checksum.
+ * @DMX_BUFFER_FLAG_TEI:
+ *	Indicates that the Kernel has detected a Transport Error indicator
+ *	(TEI) on a filtered pid.
+ * @DMX_BUFFER_PKT_COUNTER_MISMATCH:
+ *	Indicates that the Kernel has detected a packet counter mismatch
+ *	on a filtered pid.
+ * @DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED:
+ *	Indicates that the Kernel has detected one or more frame discontinuity.
+ * @DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR:
+ *	Received at least one packet with a frame discontinuity indicator.
+ */
+
+enum dmx_buffer_flags {
+	DMX_BUFFER_FLAG_HAD_CRC32_DISCARD		= 1 << 0,
+	DMX_BUFFER_FLAG_TEI				= 1 << 1,
+	DMX_BUFFER_PKT_COUNTER_MISMATCH			= 1 << 2,
+	DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED		= 1 << 3,
+	DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR		= 1 << 4,
+};
+
+/**
+ * struct dmx_buffer - dmx buffer info
+ *
+ * @index:	id number of the buffer
+ * @bytesused:	number of bytes occupied by data in the buffer (payload);
+ * @offset:	for buffers with memory == DMX_MEMORY_MMAP;
+ *		offset from the start of the device memory for this plane,
+ *		(or a "cookie" that should be passed to mmap() as offset)
+ * @length:	size in bytes of the buffer
+ * @flags:	bit array of buffer flags as defined by &enum dmx_buffer_flags.
+ *		Filled only at &DMX_DQBUF.
+ * @count:	monotonic counter for filled buffers. Helps to identify
+ *		data stream loses. Filled only at &DMX_DQBUF.
+ *
+ * Contains data exchanged by application and driver using one of the streaming
+ * I/O methods.
+ *
+ * Please notice that, for &DMX_QBUF, only @index should be filled.
+ * On &DMX_DQBUF calls, all fields will be filled by the Kernel.
+ */
+struct dmx_buffer {
+	__u32			index;
+	__u32			bytesused;
+	__u32			offset;
+	__u32			length;
+	__u32			flags;
+	__u32			count;
+};
+
+/**
+ * struct dmx_requestbuffers - request dmx buffer information
+ *
+ * @count:	number of requested buffers,
+ * @size:	size in bytes of the requested buffer
+ *
+ * Contains data used for requesting a dmx buffer.
+ * All reserved fields must be set to zero.
+ */
+struct dmx_requestbuffers {
+	__u32			count;
+	__u32			size;
+};
+
+/**
+ * struct dmx_exportbuffer - export of dmx buffer as DMABUF file descriptor
+ *
+ * @index:	id number of the buffer
+ * @flags:	flags for newly created file, currently only O_CLOEXEC is
+ *		supported, refer to manual of open syscall for more details
+ * @fd:		file descriptor associated with DMABUF (set by driver)
+ *
+ * Contains data used for exporting a dmx buffer as DMABUF file descriptor.
+ * The buffer is identified by a 'cookie' returned by DMX_QUERYBUF
+ * (identical to the cookie used to mmap() the buffer to userspace). All
+ * reserved fields must be set to zero. The field reserved0 is expected to
+ * become a structure 'type' allowing an alternative layout of the structure
+ * content. Therefore this field should not be used for any other extensions.
+ */
+struct dmx_exportbuffer {
+	__u32		index;
+	__u32		flags;
+	__s32		fd;
+};
+
+#ifdef CONFIG_AMLOGIC_DVB_COMPAT
+enum {
+	DMA_0 = 0,
+	DMA_1,
+	DMA_2,
+	DMA_3,
+	DMA_4,
+	DMA_5,
+	DMA_6,
+	DMA_7,
+	FRONTEND_TS0 = 32,
+	FRONTEND_TS1,
+	FRONTEND_TS2,
+	FRONTEND_TS3,
+	FRONTEND_TS4,
+	FRONTEND_TS5,
+	FRONTEND_TS6,
+	FRONTEND_TS7,
+	DMA_0_1 = 64,
+	DMA_1_1,
+	DMA_2_1,
+	DMA_3_1,
+	DMA_4_1,
+	DMA_5_1,
+	DMA_6_1,
+	DMA_7_1,
+	FRONTEND_TS0_1 = 96,
+	FRONTEND_TS1_1,
+	FRONTEND_TS2_1,
+	FRONTEND_TS3_1,
+	FRONTEND_TS4_1,
+	FRONTEND_TS5_1,
+	FRONTEND_TS6_1,
+	FRONTEND_TS7_1,
+};
+
+/*define filter mem_info type*/
+enum {
+	DMX_VIDEO_TYPE = 0,
+	DMX_AUDIO_TYPE,
+	DMX_SUBTITLE_TYPE,
+	DMX_TELETEXT_TYPE,
+	DMX_SECTION_TYPE,
+};
+
+struct filter_mem_info {
+	__u32 type;
+	__u32 pid;
+	struct dmx_mem_info	filter_info;
+};
+
+struct dmx_filter_mem_info {
+	__u32 filter_num;
+	struct filter_mem_info info[40];
+};
+
+struct dvr_mem_info {
+	__u32 wp_offset;
+};
+
+struct decoder_mem_info {
+	__u32 rp_phy;
+};
+#endif
+
+#define DMX_START                _IO('o', 41)
+#define DMX_STOP                 _IO('o', 42)
+#define DMX_SET_FILTER           _IOW('o', 43, struct dmx_sct_filter_params)
+#define DMX_SET_PES_FILTER       _IOW('o', 44, struct dmx_pes_filter_params)
+#define DMX_SET_BUFFER_SIZE      _IO('o', 45)
+#define DMX_GET_PES_PIDS         _IOR('o', 47, __u16[5])
+#define DMX_GET_CAPS             _IOR('o', 48, dmx_caps_t)
+#define DMX_SET_SOURCE           _IOW('o', 49, dmx_source_t)
+#define DMX_GET_STC              _IOWR('o', 50, struct dmx_stc)
+#define DMX_ADD_PID              _IOW('o', 51, __u16)
+#define DMX_REMOVE_PID           _IOW('o', 52, __u16)
+#if !defined(__KERNEL__)
+
+/* This is needed for legacy userspace support */
+typedef enum dmx_output dmx_output_t;
+typedef enum dmx_input dmx_input_t;
+typedef enum dmx_ts_pes dmx_pes_type_t;
+typedef struct dmx_filter dmx_filter_t;
+
+#endif
+
+#define DMX_REQBUFS              _IOWR('o', 60, struct dmx_requestbuffers)
+#define DMX_QUERYBUF             _IOWR('o', 61, struct dmx_buffer)
+#define DMX_EXPBUF               _IOWR('o', 62, struct dmx_exportbuffer)
+#define DMX_QBUF                 _IOWR('o', 63, struct dmx_buffer)
+#define DMX_DQBUF                _IOWR('o', 64, struct dmx_buffer)
+
+#ifdef CONFIG_AMLOGIC_DVB_COMPAT
+#define DMX_SET_INPUT           _IO('o', 80)
+#define DMX_GET_MEM_INFO        _IOR('o', 81, struct dmx_mem_info)
+#define DMX_SET_HW_SOURCE       _IO('o', 82)
+#define DMX_GET_HW_SOURCE       _IOR('o', 83, int)
+#define DMX_GET_FILTER_MEM_INFO _IOR('o', 84, struct dmx_filter_mem_info)
+/*just for dvr sec mem, please call before DMX_SET_PES_FILTER*/
+#define DMX_SET_SEC_MEM			_IOW('o', 85, struct dmx_sec_mem)
+#define DMX_GET_DVR_MEM			_IOR('o', 86, struct dvr_mem_info)
+#define DMX_REMAP_PID			_IOR('o', 87, __u16[2])
+#define DMX_SET_DECODE_INFO     _IOW('o', 88, struct decoder_mem_info)
+#endif
+#endif /* _DVBDMX_H_ */
diff --git a/dummy_fe/dummy_fe_server.c b/dummy_fe/dummy_fe_server.c
new file mode 100644
index 0000000..8e62ff4
--- /dev/null
+++ b/dummy_fe/dummy_fe_server.c
@@ -0,0 +1,753 @@
+/**
+ * \page dummy_fe_server
+ * \section Introduction
+ * local ts server based on dummy frontend
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <expat.h>
+
+#include <linux/dvb/frontend.h>
+#include <getopt.h>
+#include "dummy_fe_wrapper.h"
+#include "cJSON.h"
+#include "dmx.h"
+
+#define INF(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
+#define ERR(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
+
+#define DIFF_THRESHOLD 20*1000*1000
+#define MAX_PCR_PIDS 100
+#define DUMMY_MAX_TP_NUM 16
+#define DUMMY_BLOCK_SIZE (188*1024)
+
+typedef struct {
+	int delivery_system;
+	int freqM;
+	char ts_file[512];
+} Dummy_TPInfo_t;
+
+typedef struct {
+	int id;
+	int inject_fd;
+	int filter_fd;
+	Dummy_TPInfo_t tp_list[DUMMY_MAX_TP_NUM];
+	int running;
+} Dummy_FrontendInfo_t;
+
+typedef struct {
+	uint16_t pcr_pid;
+	uint64_t last_pcr;
+	uint64_t start_time;
+} PidPCR;
+
+static int tp_cnt = 0;
+static PidPCR pcr_pids[MAX_PCR_PIDS];
+static int pcr_pid_count = 0;
+
+static int running = 1;
+static int dummy_fe_fd = -1;
+static pthread_t fe_thread_handle = (pthread_t) -1;
+static Dummy_FrontendInfo_t dummy_fe_info;
+
+static int dummy_dvr_device_open(int dvr_dev_id, int rw)
+{
+	int fd;
+	int flags = 0;
+	char dev_name[32];
+
+	memset(dev_name, 0, sizeof(dev_name));
+	snprintf(dev_name, sizeof(dev_name), "/dev/dvb0.dvr%d", dvr_dev_id);
+	if (rw)
+		flags = O_RDONLY;
+	else
+		flags = O_WRONLY;
+
+	fd = open(dev_name, flags);
+	if (fd == -1) {
+		ERR("%s cannot open \"%s\" (%s)\n", __func__, dev_name, strerror(errno));
+		return fd;
+	}
+
+	INF("%s open %s succeed, fd: %d, rw: %d\n", __func__, dev_name, fd, rw);
+	if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK, 0) < 0) {
+		ERR("%s set nonblock flag failed \"%s\"\n", __func__, strerror(errno));
+	}
+
+	return fd;
+}
+
+static int dummy_set_demux_source(int dmx_id)
+{
+	int r = 0;
+	char node[32] = {0};
+
+	snprintf(node, sizeof(node), "/dev/dvb0.demux%d", dmx_id);
+	int fd = open(node, O_RDWR);
+	if (fd < 0) {
+		ERR("%s open demux%d failed!\n", __func__, dmx_id);
+		return -1;
+	}
+
+	struct dmx_sct_filter_params filter_param;
+	memset(&filter_param, 0, sizeof(filter_param));
+	filter_param.pid = 0;
+	filter_param.filter.filter[0] = 0;
+	filter_param.filter.mask[0] = 0xff;
+	filter_param.flags = 1;
+	r = ioctl(fd, DMX_SET_FILTER, &filter_param);
+	r |= ioctl(fd, DMX_SET_BUFFER_SIZE, 32 * 1024);
+	r |= ioctl(fd, DMX_START);
+	if (r) {
+		ERR("%s create filter failed: %d\n", __func__, r);
+		return -1;
+	}
+
+	int source = DMA_0 + dmx_id;
+	int input = INPUT_LOCAL;
+
+	if (ioctl(fd, DMX_SET_INPUT, input) == -1) {
+		ERR("%s set input failed. error: %d\n", __func__, errno);
+		r = -1;
+	}
+
+	if (ioctl(fd, DMX_SET_HW_SOURCE, source) == -1) {
+		ERR("%s set hw source failed. error: %d\n", __func__, errno);
+		r = -1;
+	}
+
+	dummy_fe_info.filter_fd = fd;
+
+	INF("%s set dmx%d to local mode and source is DMA_%d\n", __func__, dmx_id, dmx_id);
+	return r;
+}
+
+static int dummy_inject_data(uint8_t *buf, int len)
+{
+	int ret;
+
+	if (dummy_fe_info.inject_fd < 0) {
+		ERR("invalid inject fd\n");
+		return -1;
+	}
+
+	ret = write(dummy_fe_info.inject_fd, buf, len);
+	if (ret <= 0) {
+		ERR("failed to inject, inject fd: %d, err: %d\n",
+				dummy_fe_info.inject_fd, errno);
+		return -1;
+	}
+	//INF("inject len: %#x, actual injected len: %#x\n", len, ret);
+	return ret;
+}
+
+enum DUMMY_FE_FILE_TYPE {
+	DUMMY_FE_FILE_XML,
+	DUMMY_FE_FILE_JSON,
+	DUMMY_FE_FILE_INVALID,
+};
+
+static enum DUMMY_FE_FILE_TYPE check_file_type(FILE *fp)
+{
+	#define BUFFER_SIZE 1024
+	enum DUMMY_FE_FILE_TYPE type = DUMMY_FE_FILE_INVALID;
+	char buf[BUFFER_SIZE];
+
+	size_t bytes_read = fread(buf, 1, BUFFER_SIZE, fp);
+	if (bytes_read == 0) {
+		return type;
+	}
+
+	int i = 0;
+	while (i < bytes_read && isspace((unsigned char) buf[i])) {
+		i++;
+	}
+
+	if (buf[i] == '<') {
+		type = DUMMY_FE_FILE_XML;
+	} else if (buf[i] == '{' || buf[i] == '[') {
+		type = DUMMY_FE_FILE_JSON;
+	} else {
+		ERR("invalid config file\n");
+	}
+
+	INF("config file type: %d\n", type);
+	return type;
+}
+
+static void start_element(void *data, const char *element, const char **attr) {
+	Dummy_FrontendInfo_t *fe = &dummy_fe_info;
+
+	INF("Start element: %s\n", element);
+	if (strcmp(element, "freq")) {
+		INF("not freq\n");
+		return;
+	}
+
+	for (int i = 0; attr[i]; i+=2) {
+		INF(" Attribute: %s = %s\n", attr[i], attr[i+1]);
+		if (!strcmp(attr[i], "modulation")) {
+			if (!strcmp(attr[i+1], "DVBC")) {
+				fe->tp_list[tp_cnt].delivery_system = SYS_DVBC_ANNEX_A;
+			} else if (!strcmp(attr[i+1], "DVBT")) {
+				fe->tp_list[tp_cnt].delivery_system = SYS_DVBT;
+			} else if (!strcmp(attr[i+1], "DVBT2")) {
+				fe->tp_list[tp_cnt].delivery_system = SYS_DVBT2;
+			} else if (!strcmp(attr[i+1], "DVBS")) {
+				fe->tp_list[tp_cnt].delivery_system = SYS_DVBS;
+			} else if (!strcmp(attr[i+1], "DVBS2")) {
+				fe->tp_list[tp_cnt].delivery_system = SYS_DVBS2;
+			} else {
+				ERR("unsupported delivery system, %s\n", attr[i+1]);
+			}
+		} else if (!strcmp(attr[i], "frequency")) {
+			fe->tp_list[tp_cnt].freqM = atoi(attr[i+1]) / 1000 / 1000;
+		} else if (!strcmp(attr[i], "filename")) {
+			strcpy(&fe->tp_list[tp_cnt].ts_file[0], attr[i+1]);
+		} else {
+			INF(" Ignor attribute: %s = %s\n", attr[i], attr[i+1]);
+		}
+	}
+
+	tp_cnt++;
+}
+
+static void end_element(void *data, const char *element) {
+	INF("End element: %s\n", element);
+}
+
+static void handle_data(void *data, const char *content, int length) {
+	INF("Character data: %.*s\n", length, content);
+}
+
+static int dummy_fe_xml_parse(FILE *fp)
+{
+	char buffer[128];
+	int done;
+	int len;
+
+	fseek(fp, 0, SEEK_SET);
+
+	// Create XML Parser
+	XML_Parser parser = XML_ParserCreate(NULL);
+
+	// Set callback
+	XML_SetElementHandler(parser, start_element, end_element);
+	XML_SetCharacterDataHandler(parser, handle_data);
+
+	// Read a block and parser
+	do {
+		len = fread(buffer, 1, sizeof(buffer), fp);
+		if (ferror(fp)) {
+			ERR("read file error\n");
+			break;
+		}
+
+		done = feof(fp);
+
+		// Parser data block
+		if (XML_Parse(parser, buffer, len, done) == XML_STATUS_ERROR) {
+			ERR("xml parser error: %s at line %lu\n",
+				XML_ErrorString(XML_GetErrorCode(parser)),
+				XML_GetCurrentLineNumber(parser));
+			break;
+		}
+	} while (!done);
+
+	// Free XML Parser
+	XML_ParserFree(parser);
+
+	INF("TP list:\n");
+	Dummy_FrontendInfo_t *fe = &dummy_fe_info;
+	for (int i = 0; i < tp_cnt; i++) {
+		INF("freq: %d, delivery_system: %d, stream: %s\n",
+			fe->tp_list[i].freqM,
+			fe->tp_list[i].delivery_system,
+			fe->tp_list[i].ts_file);
+	}
+
+	return 0;
+}
+
+static int dummy_fe_json_parse(FILE *fp)
+{
+	uint8_t *buf = NULL;
+	int len;
+
+	fseek(fp, 0, SEEK_END);
+	len = ftell(fp);
+	fseek(fp, 0, SEEK_SET);
+	buf = malloc(len);
+	if (buf) {
+		if (fread(buf, 1, len, fp) != len)
+			return -1;
+	}
+
+	cJSON *json = cJSON_Parse((const char *)buf);
+	free(buf);
+	if (!json) {
+		ERR("fe parse failed, (%s)\n", cJSON_GetErrorPtr());
+		return -1;
+	}
+
+	// parse json object
+	cJSON *frontend = cJSON_GetObjectItem(json, "frontend");
+	cJSON *tp_list = cJSON_GetObjectItem(frontend, "tp_list");
+	int array_size = cJSON_GetArraySize(tp_list);
+	Dummy_FrontendInfo_t *fe = &dummy_fe_info;
+
+
+	INF("TP list:\n");
+	for (int i = 0; i < array_size && i < DUMMY_MAX_TP_NUM; i++) {
+		cJSON *tp = cJSON_GetArrayItem(tp_list, i);
+
+		cJSON *delivery_system = cJSON_GetObjectItem(tp, "delivery_system");
+		char *delivery = delivery_system->valuestring;
+		if (!strcmp(delivery, "DVBC")) {
+			fe->tp_list[i].delivery_system = SYS_DVBC_ANNEX_A;
+		} else if (!strcmp(delivery, "DVBT")) {
+			fe->tp_list[i].delivery_system = SYS_DVBT;
+		} else if (!strcmp(delivery, "DVBT2")) {
+			fe->tp_list[i].delivery_system = SYS_DVBT2;
+		} else if (!strcmp(delivery, "DVBS")) {
+			fe->tp_list[i].delivery_system = SYS_DVBS;
+		} else if (!strcmp(delivery, "DVBS2")) {
+			fe->tp_list[i].delivery_system = SYS_DVBS2;
+		} else {
+			ERR("unsupported delivery system, %s\n", delivery_system->valuestring);
+			continue;
+		}
+
+		INF("delivery: %s, value: %d\n", delivery, fe->tp_list[i].delivery_system);
+		fe->tp_list[i].freqM = cJSON_GetObjectItem(tp, "freqM")->valueint;
+		strcpy(&fe->tp_list[i].ts_file[0], cJSON_GetObjectItem(tp, "stream")->valuestring);
+		INF("TP[%d] freq: %d, stream: %s\n", i, fe->tp_list[i].freqM, &fe->tp_list[i].ts_file[0]);
+	}
+
+	cJSON_Delete(json);
+
+	return 0;
+}
+
+static char* dummy_fe_get_stream(int delivery_system, int freqM)
+{
+	int i;
+	Dummy_FrontendInfo_t *fe = &dummy_fe_info;
+
+	if (freqM <= 0)
+		return NULL;
+
+	for (i = 0; i < DUMMY_MAX_TP_NUM; i++) {
+		if (fe->tp_list[i].delivery_system == delivery_system &&
+			fe->tp_list[i].freqM == freqM) {
+			return &fe->tp_list[i].ts_file[0];
+		}
+	}
+
+	ERR("%s failed, delivery_system: %d, freqM: %d\n",
+			__func__, delivery_system, freqM);
+
+	return NULL;
+}
+
+// Get current system time in microseconds
+uint64_t get_current_time_us()
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	return tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+// Extract PCR values from TS packet
+uint64_t extract_pcr(const uint8_t *packet)
+{
+	uint64_t pcr_base = ((uint64_t)(packet[6] & 0xFF) << 25) |
+						((uint64_t)(packet[7] & 0xFF) << 17) |
+						((uint64_t)(packet[8] & 0xFF) << 9) |
+						((uint64_t)(packet[9] & 0xFF) << 1) |
+						((uint64_t)(packet[10] & 0x80) >> 7);
+	return pcr_base;  // Ignor PCR extension
+}
+
+// Record the PID which contains PCR
+void record_pcr_pid(uint16_t pid)
+{
+	for (int i = 0; i < pcr_pid_count; i++) {
+		if (pcr_pids[i].pcr_pid == pid) {
+			return;
+		}
+	}
+
+	if (pcr_pid_count < MAX_PCR_PIDS) {
+		pcr_pids[pcr_pid_count].pcr_pid = pid;
+		pcr_pids[pcr_pid_count].last_pcr = 0;
+		pcr_pid_count ++;
+		INF("%s record pid: %#x, cnt: %d\n", __func__, pid, pcr_pid_count);
+	}
+}
+
+// Check if the TS packet contains PCR and record its PID
+void check_and_record_pcr_pid(const uint8_t *packet)
+{
+	if (packet[3] & 0x20) {	// Adaptation field control
+		int adp_field_len = packet[4];
+		if (adp_field_len > 0 && (packet[5] & 0x10)) {	// PCR flag
+			uint16_t pid = ((packet[1] & 0x1f) << 8) | packet[2];
+			record_pcr_pid(pid);
+		}
+	}
+}
+
+void *dummy_inject_thread(void *arg)
+{
+	char *stream = (char *)arg;
+	uint8_t *buf;
+
+	INF("inject %s\n", stream);
+	FILE *file = fopen(stream, "rb");
+	if (!file) {
+		ERR("Error opening input file %s\n", stream);
+		return NULL;
+	}
+
+	buf = (uint8_t *)malloc(DUMMY_BLOCK_SIZE);
+	if (!buf) {
+		ERR("no heap memory\n");
+		return NULL;
+	}
+
+	while (dummy_fe_info.running) {
+		uint64_t start_time;
+		int data_size = DUMMY_BLOCK_SIZE;
+
+		pcr_pid_count = 0;
+		memset(&pcr_pids[0], 0, sizeof(PidPCR) * MAX_PCR_PIDS);
+
+		INF("loop\n");
+		start_time = get_current_time_us();
+		fseek(file, 0, SEEK_SET);
+		while (fread(buf, 1, data_size, file) == data_size) {
+			int left = data_size;
+			uint8_t *packet = buf;
+			//INF("%s %#x bytes read\n", __func__, data_size);
+			if (!dummy_fe_info.running)
+				break;
+
+			while (left >= 188) {
+				if (packet[0] != 0x47) {
+					ERR("invalid sync byte\n");
+					packet++;
+					left--;
+					continue;
+				}
+
+				// Check and record pids that contain PCR
+				check_and_record_pcr_pid(packet);
+
+				// Process TS packets containing PCR
+				if (packet[3] & 0x20) { // adaptation field control
+					int adp_field_len = packet[4];
+					//INF("%s adp_field_len: %d, packet5: %#x\n",
+								//__func__, adp_field_len, packet[5]);
+					if (adp_field_len > 0 && (packet[5] & 0x10)) { // PCR flag
+						uint16_t pid = ((packet[1] & 0x1f) << 8) | packet[2];
+						uint64_t current_pcr = extract_pcr(packet);
+
+						for (int i = 0; i < pcr_pid_count; i++) {
+							if (pcr_pids[i].pcr_pid ==  pid) {
+								uint64_t last_pcr = pcr_pids[i].last_pcr;
+								if (pcr_pids[i].start_time == 0)
+									pcr_pids[i].start_time = start_time;
+								if (last_pcr != 0) {
+									uint64_t pcr_diff = current_pcr - last_pcr;
+
+									// PCR is a 90MHz clock, 1 PCR tick = 1/90000000 second
+									uint64_t pcr_time_us = pcr_diff / 90 * 1000;
+									//INF("pid: %#x, pcr_time_us: %lld\n", pid, pcr_time_us);
+
+									uint64_t current_time = get_current_time_us();
+									int elapsed_time_us = current_time - pcr_pids[i].start_time;
+
+									//INF("pid: %#x, current_time: %llu, start_time: %llu, elapsed_time_us: %d\n",
+											//pid, current_time, pcr_pids[i].start_time, elapsed_time_us);
+									if (elapsed_time_us > 0 &&
+											elapsed_time_us < pcr_time_us &&
+											(pcr_time_us - elapsed_time_us) < DIFF_THRESHOLD) {
+										INF("usleep %lld\n", pcr_time_us - elapsed_time_us);
+										usleep(pcr_time_us - elapsed_time_us);
+										pcr_pids[i].start_time = get_current_time_us();
+										//INF("PCR: %llx, Last_PCR: %llx\n", current_pcr, pcr_pids[i].last_pcr);
+										pcr_pids[i].last_pcr = current_pcr;
+									}
+								} else {
+									pcr_pids[i].last_pcr = current_pcr;
+								}
+								break;
+							}
+						}
+					}
+				}
+				packet += 188;
+				left -= 188;
+			}
+
+			dummy_inject_data(buf, data_size);
+		}
+	}
+
+	if (file)
+		fclose(file);
+	if (buf)
+		free(buf);
+
+	INF("exit %s\n", __func__);
+	return NULL;
+}
+
+static void dummy_fe_term(void)
+{
+
+	dummy_fe_info.running = 0;
+
+	if (!pthread_equal(fe_thread_handle, (pthread_t) -1) && running) {
+		running = 0;
+		pthread_join(fe_thread_handle, NULL);
+	}
+
+	if (dummy_fe_fd != -1)
+		close(dummy_fe_fd);
+
+	if (dummy_fe_info.inject_fd >= 0)
+		close(dummy_fe_info.inject_fd);
+
+	if (dummy_fe_info.filter_fd >= 0)
+		close(dummy_fe_info.filter_fd);
+	exit(0);
+}
+
+static void handle_signal(int signal)
+{
+	dummy_fe_term();
+}
+
+static void init_signal_handler(void)
+{
+	struct sigaction act;
+
+	memset(&act, 0, sizeof(struct sigaction));
+	act.sa_handler = handle_signal;
+	sigaction(SIGINT, &act, NULL);
+}
+
+static struct option long_options[] = {
+	{"help", no_argument, 0, 'h'},
+	{"config", required_argument, 0, 'f'},
+	{"device", required_argument, 0, 'd'},
+	{0, 0, 0, 0}
+};
+
+static void usage(char *argv[])
+{
+	INF("Usage: %s [option]\n", argv[0]);
+	INF("	-h --help		Show this help message\n");
+	INF("	-f --config		Specify configure file\n");
+	INF("	-d --device		Specify FE device\n");
+	INF("	-i --dmxdevice	Specify Demux device\n");
+}
+
+int main(int argc, char **argv)
+{
+	int ret;
+	int opt;
+	int opt_index = 0;
+	int fe_id = -1;
+	int dmx_id = -1;
+	char fe_file_path[512];
+
+	init_signal_handler();
+	memset(&fe_file_path[0], 0, sizeof(fe_file_path));
+	while ((opt = getopt_long(argc, argv, "hf:d:i:", long_options, &opt_index)) != -1) {
+		switch (opt) {
+			case 'h':
+				usage(argv);
+				exit(0);
+			case 'f':
+				strcpy(&fe_file_path[0], optarg);
+				INF("FE config file: %s\n", optarg);
+				break;
+			case 'd':
+				fe_id = strtol(optarg, NULL, 10);
+				INF("FE id: %d\n", fe_id);
+				break;
+			case 'i':
+				dmx_id = strtol(optarg, NULL, 10);
+				INF("Demux id: %d\n", dmx_id);
+				break;
+			default:
+				usage(argv);
+				abort();
+		}
+	}
+
+	if (fe_id == -1 || dmx_id == -1) {
+		usage(argv);
+		exit(0);
+	}
+
+	FILE *fe_fp = fopen(fe_file_path, "rb");
+	if (fe_fp == NULL) {
+		ERR("open %s failed!\n", fe_file_path);
+		return -1;
+	}
+
+	// parse and save frontend info from the frontend file
+	memset(&dummy_fe_info, 0, sizeof(Dummy_FrontendInfo_t));
+	dummy_fe_info.id = fe_id;
+	dummy_fe_info.inject_fd = -1;
+	dummy_fe_info.filter_fd = -1;
+
+	// check file type. xml or json ?
+	enum DUMMY_FE_FILE_TYPE type = check_file_type(fe_fp);
+
+	if (type == DUMMY_FE_FILE_XML) {
+		ret = dummy_fe_xml_parse(fe_fp);
+	} else if (type == DUMMY_FE_FILE_JSON) {
+		ret = dummy_fe_json_parse(fe_fp);
+	} else {
+		ret= -1;
+	}
+
+	fclose(fe_fp);
+	if (ret) {
+		ERR("fe parse type:%d failed\n", type);
+		return -1;
+	}
+
+	dummy_fe_info.inject_fd = dummy_dvr_device_open(dmx_id, 0);
+	if (dummy_fe_info.inject_fd < 0) {
+		ERR("open dvr device failed\n");
+		return -1;
+	}
+
+	// Set demux source to DMA0 + dmx_id for ts inject
+	dummy_set_demux_source(dmx_id);
+
+	// open dummy fe device
+	char *dev_name = "/dev/dummy_fe";
+	dummy_fe_fd = open(dev_name, O_RDWR);
+	if (dummy_fe_fd == -1) {
+		ERR("cannot open \"%s\" (%s)", dev_name, strerror(errno));
+		return -1;
+	}
+
+	struct dummy_fe_property prop;
+	memset(&prop, 0, sizeof(struct dummy_fe_property));
+
+	prop.cmd = DUMMY_FE_ID;
+	prop.data = dummy_fe_info.id;
+	INF("set prop, cmd: %d, data: %d\n", prop.cmd, prop.data);
+	ret = ioctl(dummy_fe_fd, DUMMY_FE_SET_PROPERTY, &prop);
+	if (ret) {
+		INF("set fe id[%d] failed, ret: %#x, %s\n", prop.data, ret, strerror(errno));
+		return -1;
+	}
+
+	dummy_fe_info.running = 1;
+
+	char *stream;
+	struct pollfd poll_fd;
+	poll_fd.fd = dummy_fe_fd;
+	poll_fd.events = POLLIN | POLLERR;
+	while (running) {
+		ret = poll(&poll_fd, 1, 100);
+		if (ret < 0) {
+			INF("poll failed, (%s)", strerror(errno));
+			continue;
+		}
+
+		if (!(poll_fd.revents & POLLIN))
+			continue;
+		INF("poll revents: %#x\n", poll_fd.revents);
+
+		prop.cmd = DUMMY_FE_STATE;
+		ret = ioctl(dummy_fe_fd, DUMMY_FE_GET_PROPERTY, &prop);
+		if (ret) {
+			INF("get state failed, ret: %#x\n", ret);
+			continue;
+		}
+		INF("dummy fe state: %d\n", prop.data);
+		if (prop.data != DUMMY_FE_LOCK) {
+			continue;
+		}
+
+		prop.cmd = DUMMY_FE_DELIVERY_SYSTEM;
+		ret = ioctl(dummy_fe_fd, DUMMY_FE_GET_PROPERTY, &prop);
+		if (ret) {
+			INF("get delivery system failed, ret: %#x\n", ret);
+			continue;
+		}
+		int delivery_system = prop.data;
+		INF("dummy fe delivery system: %d\n", delivery_system);
+
+		prop.cmd = DUMMY_FE_FREQUENCY;
+		ret = ioctl(dummy_fe_fd, DUMMY_FE_GET_PROPERTY, &prop);
+		if (ret) {
+			INF("get frequency failed, ret: %#x\n", ret);
+			continue;
+		}
+		int freqM = prop.data / 1000 / 1000;
+		INF("dummy fe frequency: %d\n", freqM);
+
+		// get ts stream from the dummy frontend information according to
+		// the delivery system and frequency
+		stream = dummy_fe_get_stream(delivery_system, freqM);
+		if (stream == NULL) {
+			ERR("cannot find delivery:%d, freqM:%d from FE config file\n",
+					delivery_system, freqM);
+			prop.cmd = DUMMY_FE_STATE;
+			prop.data = DUMMY_FE_UNLOCKED;
+			ret = ioctl(dummy_fe_fd, DUMMY_FE_SET_PROPERTY, &prop);
+			if (ret) {
+				ERR("set property failed, ret: %d\n", ret);
+			}
+			continue;
+		} else {
+			prop.cmd = DUMMY_FE_STATE;
+			prop.data = DUMMY_FE_LOCKED;
+			ret = ioctl(dummy_fe_fd, DUMMY_FE_SET_PROPERTY, &prop);
+			if (ret) {
+				ERR("set property failed, ret: %d\n", ret);
+			}
+		}
+
+		if (!pthread_equal(fe_thread_handle, (pthread_t) -1) && dummy_fe_info.running) {
+			dummy_fe_info.running = 0;
+			pthread_join(fe_thread_handle, NULL);
+			fe_thread_handle = (pthread_t) -1;
+		}
+
+		// create thread to read TS file and inject to hardware demux
+		dummy_fe_info.running = 1;
+		pthread_create(&fe_thread_handle, NULL, dummy_inject_thread, stream);
+	}
+
+	dummy_fe_term();
+
+	return 0;
+}
diff --git a/dummy_fe/dummy_fe_wrapper.h b/dummy_fe/dummy_fe_wrapper.h
new file mode 100644
index 0000000..1fb6e3f
--- /dev/null
+++ b/dummy_fe/dummy_fe_wrapper.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
+ */
+
+#ifndef _DUMMY_FE_WRAPPER_H_
+#define _DUMMY_FE_WRAPPER_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* Dummy Frontend Commands */
+#define DUMMY_FE_ID 0
+#define DUMMY_FE_STATE 1
+#define DUMMY_FE_STATUS 2
+#define DUMMY_FE_FREQUENCY 3
+#define DUMMY_FE_MODULATION 4
+#define DUMMY_FE_SYMBOL_RATE 5
+#define DUMMY_FE_BANDWIDTH_HZ 6
+#define DUMMY_FE_DELIVERY_SYSTEM 7
+
+/* Define dummy fe state machine */
+enum dummy_fe_state_t {
+	DUMMY_FE_INIT,
+	DUMMY_FE_LOCK,
+	DUMMY_FE_LOCKED,
+	DUMMY_FE_UNLOCKED,
+	DUMMY_FE_INVALID
+};
+
+/* Define dummy fe demod status */
+struct dummy_fe_property {
+	__u32 cmd;
+	__u32 data;
+} __packed;
+
+#define DUMMY_FE_SET_PROPERTY		_IOW('f', 0, struct dummy_fe_property)
+#define DUMMY_FE_GET_PROPERTY		_IOR('f', 1, struct dummy_fe_property)
+#endif
diff --git a/dummy_fe/libcJSON/cJSON.c b/dummy_fe/libcJSON/cJSON.c
new file mode 100644
index 0000000..f5f04d4
--- /dev/null
+++ b/dummy_fe/libcJSON/cJSON.c
@@ -0,0 +1,3088 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <float.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#define NAN 0.0/0.0
+#endif
+
+typedef struct {
+    const unsigned char *json;
+    size_t position;
+} error;
+static error global_error = { NULL, 0 };
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+    return (const char*) (global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
+{
+    if (!cJSON_IsString(item))
+    {
+        return NULL;
+    }
+
+    return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
+{
+    if (!cJSON_IsNumber(item))
+    {
+        return NAN;
+    }
+
+    return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-paste from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 13)
+    #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void)
+{
+    static char version[15];
+    sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+    return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
+{
+    if ((string1 == NULL) || (string2 == NULL))
+    {
+        return 1;
+    }
+
+    if (string1 == string2)
+    {
+        return 0;
+    }
+
+    for (; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+    {
+        if (*string1 == '\0')
+        {
+            return 0;
+        }
+    }
+
+    return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks
+{
+    void *(CJSON_CDECL *allocate)(size_t size);
+    void (CJSON_CDECL *deallocate)(void *pointer);
+    void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
+static void * CJSON_CDECL internal_malloc(size_t size)
+{
+    return malloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+    free(pointer);
+}
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+    return realloc(pointer, size);
+}
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
+
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
+{
+    size_t length = 0;
+    unsigned char *copy = NULL;
+
+    if (string == NULL)
+    {
+        return NULL;
+    }
+
+    length = strlen((const char*)string) + sizeof("");
+    copy = (unsigned char*)hooks->allocate(length);
+    if (copy == NULL)
+    {
+        return NULL;
+    }
+    memcpy(copy, string, length);
+
+    return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+    if (hooks == NULL)
+    {
+        /* Reset hooks */
+        global_hooks.allocate = malloc;
+        global_hooks.deallocate = free;
+        global_hooks.reallocate = realloc;
+        return;
+    }
+
+    global_hooks.allocate = malloc;
+    if (hooks->malloc_fn != NULL)
+    {
+        global_hooks.allocate = hooks->malloc_fn;
+    }
+
+    global_hooks.deallocate = free;
+    if (hooks->free_fn != NULL)
+    {
+        global_hooks.deallocate = hooks->free_fn;
+    }
+
+    /* use realloc only if both free and malloc are used */
+    global_hooks.reallocate = NULL;
+    if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
+    {
+        global_hooks.reallocate = realloc;
+    }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
+{
+    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+    if (node)
+    {
+        memset(node, '\0', sizeof(cJSON));
+    }
+
+    return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+    cJSON *next = NULL;
+    while (item != NULL)
+    {
+        next = item->next;
+        if (!(item->type & cJSON_IsReference) && (item->child != NULL))
+        {
+            cJSON_Delete(item->child);
+        }
+        if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
+        {
+            global_hooks.deallocate(item->valuestring);
+        }
+        if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+        {
+            global_hooks.deallocate(item->string);
+        }
+        global_hooks.deallocate(item);
+        item = next;
+    }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+    struct lconv *lconv = localeconv();
+    return (unsigned char) lconv->decimal_point[0];
+#else
+    return '.';
+#endif
+}
+
+typedef struct
+{
+    const unsigned char *content;
+    size_t length;
+    size_t offset;
+    size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+    internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
+{
+    double number = 0;
+    unsigned char *after_end = NULL;
+    unsigned char number_c_string[64];
+    unsigned char decimal_point = get_decimal_point();
+    size_t i = 0;
+
+    if ((input_buffer == NULL) || (input_buffer->content == NULL))
+    {
+        return false;
+    }
+
+    /* copy the number into a temporary buffer and replace '.' with the decimal point
+     * of the current locale (for strtod)
+     * This also takes care of '\0' not necessarily being available for marking the end of the input */
+    for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+    {
+        switch (buffer_at_offset(input_buffer)[i])
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case '+':
+            case '-':
+            case 'e':
+            case 'E':
+                number_c_string[i] = buffer_at_offset(input_buffer)[i];
+                break;
+
+            case '.':
+                number_c_string[i] = decimal_point;
+                break;
+
+            default:
+                goto loop_end;
+        }
+    }
+loop_end:
+    number_c_string[i] = '\0';
+
+    number = strtod((const char*)number_c_string, (char**)&after_end);
+    if (number_c_string == after_end)
+    {
+        return false; /* parse_error */
+    }
+
+    item->valuedouble = number;
+
+    /* use saturation in case of overflow */
+    if (number >= INT_MAX)
+    {
+        item->valueint = INT_MAX;
+    }
+    else if (number <= (double)INT_MIN)
+    {
+        item->valueint = INT_MIN;
+    }
+    else
+    {
+        item->valueint = (int)number;
+    }
+
+    item->type = cJSON_Number;
+
+    input_buffer->offset += (size_t)(after_end - number_c_string);
+    return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+    if (number >= INT_MAX)
+    {
+        object->valueint = INT_MAX;
+    }
+    else if (number <= (double)INT_MIN)
+    {
+        object->valueint = INT_MIN;
+    }
+    else
+    {
+        object->valueint = (int)number;
+    }
+
+    return object->valuedouble = number;
+}
+
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+    char *copy = NULL;
+    /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+    if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference) || (object->valuestring == NULL))
+    {
+        return NULL;
+    }
+    if (strlen(valuestring) <= strlen(object->valuestring))
+    {
+        strcpy(object->valuestring, valuestring);
+        return object->valuestring;
+    }
+    copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
+    if (copy == NULL)
+    {
+        return NULL;
+    }
+    cJSON_free(object->valuestring);
+    object->valuestring = copy;
+
+    return copy;
+}
+
+typedef struct
+{
+    unsigned char *buffer;
+    size_t length;
+    size_t offset;
+    size_t depth; /* current nesting depth (for formatted printing) */
+    cJSON_bool noalloc;
+    cJSON_bool format; /* is this print a formatted print */
+    internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer * const p, size_t needed)
+{
+    unsigned char *newbuffer = NULL;
+    size_t newsize = 0;
+
+    if ((p == NULL) || (p->buffer == NULL))
+    {
+        return NULL;
+    }
+
+    if ((p->length > 0) && (p->offset >= p->length))
+    {
+        /* make sure that offset is valid */
+        return NULL;
+    }
+
+    if (needed > INT_MAX)
+    {
+        /* sizes bigger than INT_MAX are currently not supported */
+        return NULL;
+    }
+
+    needed += p->offset + 1;
+    if (needed <= p->length)
+    {
+        return p->buffer + p->offset;
+    }
+
+    if (p->noalloc) {
+        return NULL;
+    }
+
+    /* calculate new buffer size */
+    if (needed > (INT_MAX / 2))
+    {
+        /* overflow of int, use INT_MAX if possible */
+        if (needed <= INT_MAX)
+        {
+            newsize = INT_MAX;
+        }
+        else
+        {
+            return NULL;
+        }
+    }
+    else
+    {
+        newsize = needed * 2;
+    }
+
+    if (p->hooks.reallocate != NULL)
+    {
+        /* reallocate with realloc if available */
+        newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+        if (newbuffer == NULL)
+        {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+    }
+    else
+    {
+        /* otherwise reallocate manually */
+        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+        if (!newbuffer)
+        {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+        if (newbuffer)
+        {
+            memcpy(newbuffer, p->buffer, p->offset + 1);
+        }
+        p->hooks.deallocate(p->buffer);
+    }
+    p->length = newsize;
+    p->buffer = newbuffer;
+
+    return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+    const unsigned char *buffer_pointer = NULL;
+    if ((buffer == NULL) || (buffer->buffer == NULL))
+    {
+        return;
+    }
+    buffer_pointer = buffer->buffer + buffer->offset;
+
+    buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+    double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+    return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    double d = item->valuedouble;
+    int length = 0;
+    size_t i = 0;
+    unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
+    unsigned char decimal_point = get_decimal_point();
+    double test = 0.0;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* This checks for NaN and Infinity */
+    if (isnan(d) || isinf(d))
+    {
+        length = sprintf((char*)number_buffer, "null");
+    }
+    else
+    {
+        /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+        length = sprintf((char*)number_buffer, "%1.15g", d);
+
+        /* Check whether the original double can be recovered */
+        if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
+        {
+            /* If not, print with 17 decimal places of precision */
+            length = sprintf((char*)number_buffer, "%1.17g", d);
+        }
+    }
+
+    /* sprintf failed or buffer overrun occurred */
+    if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
+    {
+        return false;
+    }
+
+    /* reserve appropriate space in the output */
+    output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    /* copy the printed number to the output and replace locale
+     * dependent decimal point with '.' */
+    for (i = 0; i < ((size_t)length); i++)
+    {
+        if (number_buffer[i] == decimal_point)
+        {
+            output_pointer[i] = '.';
+            continue;
+        }
+
+        output_pointer[i] = number_buffer[i];
+    }
+    output_pointer[i] = '\0';
+
+    output_buffer->offset += (size_t)length;
+
+    return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char * const input)
+{
+    unsigned int h = 0;
+    size_t i = 0;
+
+    for (i = 0; i < 4; i++)
+    {
+        /* parse digit */
+        if ((input[i] >= '0') && (input[i] <= '9'))
+        {
+            h += (unsigned int) input[i] - '0';
+        }
+        else if ((input[i] >= 'A') && (input[i] <= 'F'))
+        {
+            h += (unsigned int) 10 + input[i] - 'A';
+        }
+        else if ((input[i] >= 'a') && (input[i] <= 'f'))
+        {
+            h += (unsigned int) 10 + input[i] - 'a';
+        }
+        else /* invalid */
+        {
+            return 0;
+        }
+
+        if (i < 3)
+        {
+            /* shift left to make place for the next nibble */
+            h = h << 4;
+        }
+    }
+
+    return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
+{
+    long unsigned int codepoint = 0;
+    unsigned int first_code = 0;
+    const unsigned char *first_sequence = input_pointer;
+    unsigned char utf8_length = 0;
+    unsigned char utf8_position = 0;
+    unsigned char sequence_length = 0;
+    unsigned char first_byte_mark = 0;
+
+    if ((input_end - first_sequence) < 6)
+    {
+        /* input ends unexpectedly */
+        goto fail;
+    }
+
+    /* get the first utf16 sequence */
+    first_code = parse_hex4(first_sequence + 2);
+
+    /* check that the code is valid */
+    if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
+    {
+        goto fail;
+    }
+
+    /* UTF16 surrogate pair */
+    if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
+    {
+        const unsigned char *second_sequence = first_sequence + 6;
+        unsigned int second_code = 0;
+        sequence_length = 12; /* \uXXXX\uXXXX */
+
+        if ((input_end - second_sequence) < 6)
+        {
+            /* input ends unexpectedly */
+            goto fail;
+        }
+
+        if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
+        {
+            /* missing second half of the surrogate pair */
+            goto fail;
+        }
+
+        /* get the second utf16 sequence */
+        second_code = parse_hex4(second_sequence + 2);
+        /* check that the code is valid */
+        if ((second_code < 0xDC00) || (second_code > 0xDFFF))
+        {
+            /* invalid second half of the surrogate pair */
+            goto fail;
+        }
+
+
+        /* calculate the unicode codepoint from the surrogate pair */
+        codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+    }
+    else
+    {
+        sequence_length = 6; /* \uXXXX */
+        codepoint = first_code;
+    }
+
+    /* encode as UTF-8
+     * takes at maximum 4 bytes to encode:
+     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+    if (codepoint < 0x80)
+    {
+        /* normal ascii, encoding 0xxxxxxx */
+        utf8_length = 1;
+    }
+    else if (codepoint < 0x800)
+    {
+        /* two bytes, encoding 110xxxxx 10xxxxxx */
+        utf8_length = 2;
+        first_byte_mark = 0xC0; /* 11000000 */
+    }
+    else if (codepoint < 0x10000)
+    {
+        /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 3;
+        first_byte_mark = 0xE0; /* 11100000 */
+    }
+    else if (codepoint <= 0x10FFFF)
+    {
+        /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 4;
+        first_byte_mark = 0xF0; /* 11110000 */
+    }
+    else
+    {
+        /* invalid unicode codepoint */
+        goto fail;
+    }
+
+    /* encode as utf8 */
+    for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
+    {
+        /* 10xxxxxx */
+        (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+        codepoint >>= 6;
+    }
+    /* encode first byte */
+    if (utf8_length > 1)
+    {
+        (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+    }
+    else
+    {
+        (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+    }
+
+    *output_pointer += utf8_length;
+
+    return sequence_length;
+
+fail:
+    return 0;
+}
+
+/* Parse the input text into an unescaped input, and populate item. */
+static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
+{
+    const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+    const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+    unsigned char *output_pointer = NULL;
+    unsigned char *output = NULL;
+
+    /* not a string */
+    if (buffer_at_offset(input_buffer)[0] != '\"')
+    {
+        goto fail;
+    }
+
+    {
+        /* calculate approximate size of the output (overestimate) */
+        size_t allocation_length = 0;
+        size_t skipped_bytes = 0;
+        while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
+        {
+            /* is escape sequence */
+            if (input_end[0] == '\\')
+            {
+                if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
+                {
+                    /* prevent buffer overflow when last input character is a backslash */
+                    goto fail;
+                }
+                skipped_bytes++;
+                input_end++;
+            }
+            input_end++;
+        }
+        if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
+        {
+            goto fail; /* string ended unexpectedly */
+        }
+
+        /* This is at most how much we need for the output */
+        allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+        output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+        if (output == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+    }
+
+    output_pointer = output;
+    /* loop through the string literal */
+    while (input_pointer < input_end)
+    {
+        if (*input_pointer != '\\')
+        {
+            *output_pointer++ = *input_pointer++;
+        }
+        /* escape sequence */
+        else
+        {
+            unsigned char sequence_length = 2;
+            if ((input_end - input_pointer) < 1)
+            {
+                goto fail;
+            }
+
+            switch (input_pointer[1])
+            {
+                case 'b':
+                    *output_pointer++ = '\b';
+                    break;
+                case 'f':
+                    *output_pointer++ = '\f';
+                    break;
+                case 'n':
+                    *output_pointer++ = '\n';
+                    break;
+                case 'r':
+                    *output_pointer++ = '\r';
+                    break;
+                case 't':
+                    *output_pointer++ = '\t';
+                    break;
+                case '\"':
+                case '\\':
+                case '/':
+                    *output_pointer++ = input_pointer[1];
+                    break;
+
+                /* UTF-16 literal */
+                case 'u':
+                    sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+                    if (sequence_length == 0)
+                    {
+                        /* failed to convert UTF16-literal to UTF-8 */
+                        goto fail;
+                    }
+                    break;
+
+                default:
+                    goto fail;
+            }
+            input_pointer += sequence_length;
+        }
+    }
+
+    /* zero terminate the output */
+    *output_pointer = '\0';
+
+    item->type = cJSON_String;
+    item->valuestring = (char*)output;
+
+    input_buffer->offset = (size_t) (input_end - input_buffer->content);
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if (output != NULL)
+    {
+        input_buffer->hooks.deallocate(output);
+    }
+
+    if (input_pointer != NULL)
+    {
+        input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+    }
+
+    return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
+{
+    const unsigned char *input_pointer = NULL;
+    unsigned char *output = NULL;
+    unsigned char *output_pointer = NULL;
+    size_t output_length = 0;
+    /* numbers of additional characters needed for escaping */
+    size_t escape_characters = 0;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* empty string */
+    if (input == NULL)
+    {
+        output = ensure(output_buffer, sizeof("\"\""));
+        if (output == NULL)
+        {
+            return false;
+        }
+        strcpy((char*)output, "\"\"");
+
+        return true;
+    }
+
+    /* set "flag" to 1 if something needs to be escaped */
+    for (input_pointer = input; *input_pointer; input_pointer++)
+    {
+        switch (*input_pointer)
+        {
+            case '\"':
+            case '\\':
+            case '\b':
+            case '\f':
+            case '\n':
+            case '\r':
+            case '\t':
+                /* one character escape sequence */
+                escape_characters++;
+                break;
+            default:
+                if (*input_pointer < 32)
+                {
+                    /* UTF-16 escape sequence uXXXX */
+                    escape_characters += 5;
+                }
+                break;
+        }
+    }
+    output_length = (size_t)(input_pointer - input) + escape_characters;
+
+    output = ensure(output_buffer, output_length + sizeof("\"\""));
+    if (output == NULL)
+    {
+        return false;
+    }
+
+    /* no characters have to be escaped */
+    if (escape_characters == 0)
+    {
+        output[0] = '\"';
+        memcpy(output + 1, input, output_length);
+        output[output_length + 1] = '\"';
+        output[output_length + 2] = '\0';
+
+        return true;
+    }
+
+    output[0] = '\"';
+    output_pointer = output + 1;
+    /* copy the string */
+    for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
+    {
+        if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
+        {
+            /* normal character, copy */
+            *output_pointer = *input_pointer;
+        }
+        else
+        {
+            /* character needs to be escaped */
+            *output_pointer++ = '\\';
+            switch (*input_pointer)
+            {
+                case '\\':
+                    *output_pointer = '\\';
+                    break;
+                case '\"':
+                    *output_pointer = '\"';
+                    break;
+                case '\b':
+                    *output_pointer = 'b';
+                    break;
+                case '\f':
+                    *output_pointer = 'f';
+                    break;
+                case '\n':
+                    *output_pointer = 'n';
+                    break;
+                case '\r':
+                    *output_pointer = 'r';
+                    break;
+                case '\t':
+                    *output_pointer = 't';
+                    break;
+                default:
+                    /* escape and print as unicode codepoint */
+                    sprintf((char*)output_pointer, "u%04x", *input_pointer);
+                    output_pointer += 4;
+                    break;
+            }
+        }
+    }
+    output[output_length + 1] = '\"';
+    output[output_length + 2] = '\0';
+
+    return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
+{
+    return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+    if ((buffer == NULL) || (buffer->content == NULL))
+    {
+        return NULL;
+    }
+
+    if (cannot_access_at_index(buffer, 0))
+    {
+        return buffer;
+    }
+
+    while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
+    {
+       buffer->offset++;
+    }
+
+    if (buffer->offset == buffer->length)
+    {
+        buffer->offset--;
+    }
+
+    return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
+{
+    if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+    {
+        return NULL;
+    }
+
+    if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+    {
+        buffer->offset += 3;
+    }
+
+    return buffer;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+    size_t buffer_length;
+
+    if (NULL == value)
+    {
+        return NULL;
+    }
+
+    /* Adding null character size due to require_null_terminated. */
+    buffer_length = strlen(value) + sizeof("");
+
+    return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+    parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
+    cJSON *item = NULL;
+
+    /* reset error position */
+    global_error.json = NULL;
+    global_error.position = 0;
+
+    if (value == NULL || 0 == buffer_length)
+    {
+        goto fail;
+    }
+
+    buffer.content = (const unsigned char*)value;
+    buffer.length = buffer_length;
+    buffer.offset = 0;
+    buffer.hooks = global_hooks;
+
+    item = cJSON_New_Item(&global_hooks);
+    if (item == NULL) /* memory fail */
+    {
+        goto fail;
+    }
+
+    if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
+    {
+        /* parse failure. ep is set. */
+        goto fail;
+    }
+
+    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+    if (require_null_terminated)
+    {
+        buffer_skip_whitespace(&buffer);
+        if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
+        {
+            goto fail;
+        }
+    }
+    if (return_parse_end)
+    {
+        *return_parse_end = (const char*)buffer_at_offset(&buffer);
+    }
+
+    return item;
+
+fail:
+    if (item != NULL)
+    {
+        cJSON_Delete(item);
+    }
+
+    if (value != NULL)
+    {
+        error local_error;
+        local_error.json = (const unsigned char*)value;
+        local_error.position = 0;
+
+        if (buffer.offset < buffer.length)
+        {
+            local_error.position = buffer.offset;
+        }
+        else if (buffer.length > 0)
+        {
+            local_error.position = buffer.length - 1;
+        }
+
+        if (return_parse_end != NULL)
+        {
+            *return_parse_end = (const char*)local_error.json + local_error.position;
+        }
+
+        global_error = local_error;
+    }
+
+    return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+    return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
+{
+    return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
+{
+    static const size_t default_buffer_size = 256;
+    printbuffer buffer[1];
+    unsigned char *printed = NULL;
+
+    memset(buffer, 0, sizeof(buffer));
+
+    /* create buffer */
+    buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
+    buffer->length = default_buffer_size;
+    buffer->format = format;
+    buffer->hooks = *hooks;
+    if (buffer->buffer == NULL)
+    {
+        goto fail;
+    }
+
+    /* print the value */
+    if (!print_value(item, buffer))
+    {
+        goto fail;
+    }
+    update_offset(buffer);
+
+    /* check if reallocate is available */
+    if (hooks->reallocate != NULL)
+    {
+        printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+        if (printed == NULL) {
+            goto fail;
+        }
+        buffer->buffer = NULL;
+    }
+    else /* otherwise copy the JSON over to a new buffer */
+    {
+        printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+        if (printed == NULL)
+        {
+            goto fail;
+        }
+        memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+        printed[buffer->offset] = '\0'; /* just to be sure */
+
+        /* free the buffer */
+        hooks->deallocate(buffer->buffer);
+    }
+
+    return printed;
+
+fail:
+    if (buffer->buffer != NULL)
+    {
+        hooks->deallocate(buffer->buffer);
+    }
+
+    return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+    return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+    return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+    if (prebuffer < 0)
+    {
+        return NULL;
+    }
+
+    p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+    if (!p.buffer)
+    {
+        return NULL;
+    }
+
+    p.length = (size_t)prebuffer;
+    p.offset = 0;
+    p.noalloc = false;
+    p.format = fmt;
+    p.hooks = global_hooks;
+
+    if (!print_value(item, &p))
+    {
+        global_hooks.deallocate(p.buffer);
+        return NULL;
+    }
+
+    return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
+{
+    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+    if ((length < 0) || (buffer == NULL))
+    {
+        return false;
+    }
+
+    p.buffer = (unsigned char*)buffer;
+    p.length = (size_t)length;
+    p.offset = 0;
+    p.noalloc = true;
+    p.format = format;
+    p.hooks = global_hooks;
+
+    return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
+{
+    if ((input_buffer == NULL) || (input_buffer->content == NULL))
+    {
+        return false; /* no input */
+    }
+
+    /* parse the different types of values */
+    /* null */
+    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
+    {
+        item->type = cJSON_NULL;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* false */
+    if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
+    {
+        item->type = cJSON_False;
+        input_buffer->offset += 5;
+        return true;
+    }
+    /* true */
+    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
+    {
+        item->type = cJSON_True;
+        item->valueint = 1;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* string */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
+    {
+        return parse_string(item, input_buffer);
+    }
+    /* number */
+    if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
+    {
+        return parse_number(item, input_buffer);
+    }
+    /* array */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
+    {
+        return parse_array(item, input_buffer);
+    }
+    /* object */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
+    {
+        return parse_object(item, input_buffer);
+    }
+
+    return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output = NULL;
+
+    if ((item == NULL) || (output_buffer == NULL))
+    {
+        return false;
+    }
+
+    switch ((item->type) & 0xFF)
+    {
+        case cJSON_NULL:
+            output = ensure(output_buffer, 5);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "null");
+            return true;
+
+        case cJSON_False:
+            output = ensure(output_buffer, 6);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "false");
+            return true;
+
+        case cJSON_True:
+            output = ensure(output_buffer, 5);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "true");
+            return true;
+
+        case cJSON_Number:
+            return print_number(item, output_buffer);
+
+        case cJSON_Raw:
+        {
+            size_t raw_length = 0;
+            if (item->valuestring == NULL)
+            {
+                return false;
+            }
+
+            raw_length = strlen(item->valuestring) + sizeof("");
+            output = ensure(output_buffer, raw_length);
+            if (output == NULL)
+            {
+                return false;
+            }
+            memcpy(output, item->valuestring, raw_length);
+            return true;
+        }
+
+        case cJSON_String:
+            return print_string(item, output_buffer);
+
+        case cJSON_Array:
+            return print_array(item, output_buffer);
+
+        case cJSON_Object:
+            return print_object(item, output_buffer);
+
+        default:
+            return false;
+    }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
+{
+    cJSON *head = NULL; /* head of the linked list */
+    cJSON *current_item = NULL;
+
+    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+    {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if (buffer_at_offset(input_buffer)[0] != '[')
+    {
+        /* not an array */
+        goto fail;
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
+    {
+        /* empty array */
+        goto success;
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if (cannot_access_at_index(input_buffer, 0))
+    {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do
+    {
+        /* allocate next item */
+        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if (new_item == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if (head == NULL)
+        {
+            /* start the linked list */
+            current_item = head = new_item;
+        }
+        else
+        {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse next value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_value(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    }
+    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+    {
+        goto fail; /* expected end of array */
+    }
+
+success:
+    input_buffer->depth--;
+
+    if (head != NULL) {
+        head->prev = current_item;
+    }
+
+    item->type = cJSON_Array;
+    item->child = head;
+
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if (head != NULL)
+    {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    size_t length = 0;
+    cJSON *current_element = item->child;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* Compose the output array. */
+    /* opening square bracket */
+    output_pointer = ensure(output_buffer, 1);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    *output_pointer = '[';
+    output_buffer->offset++;
+    output_buffer->depth++;
+
+    while (current_element != NULL)
+    {
+        if (!print_value(current_element, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+        if (current_element->next)
+        {
+            length = (size_t) (output_buffer->format ? 2 : 1);
+            output_pointer = ensure(output_buffer, length + 1);
+            if (output_pointer == NULL)
+            {
+                return false;
+            }
+            *output_pointer++ = ',';
+            if (output_buffer->format)
+            {
+                *output_pointer++ = ' ';
+            }
+            *output_pointer = '\0';
+            output_buffer->offset += length;
+        }
+        current_element = current_element->next;
+    }
+
+    output_pointer = ensure(output_buffer, 2);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+    *output_pointer++ = ']';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
+{
+    cJSON *head = NULL; /* linked list head */
+    cJSON *current_item = NULL;
+
+    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+    {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
+    {
+        goto fail; /* not an object */
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
+    {
+        goto success; /* empty object */
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if (cannot_access_at_index(input_buffer, 0))
+    {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do
+    {
+        /* allocate next item */
+        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if (new_item == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if (head == NULL)
+        {
+            /* start the linked list */
+            current_item = head = new_item;
+        }
+        else
+        {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse the name of the child */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_string(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse name */
+        }
+        buffer_skip_whitespace(input_buffer);
+
+        /* swap valuestring and string, because we parsed the name */
+        current_item->string = current_item->valuestring;
+        current_item->valuestring = NULL;
+
+        if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
+        {
+            goto fail; /* invalid object */
+        }
+
+        /* parse the value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_value(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    }
+    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+    {
+        goto fail; /* expected end of object */
+    }
+
+success:
+    input_buffer->depth--;
+
+    if (head != NULL) {
+        head->prev = current_item;
+    }
+
+    item->type = cJSON_Object;
+    item->child = head;
+
+    input_buffer->offset++;
+    return true;
+
+fail:
+    if (head != NULL)
+    {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    size_t length = 0;
+    cJSON *current_item = item->child;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* Compose the output: */
+    length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
+    output_pointer = ensure(output_buffer, length + 1);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    *output_pointer++ = '{';
+    output_buffer->depth++;
+    if (output_buffer->format)
+    {
+        *output_pointer++ = '\n';
+    }
+    output_buffer->offset += length;
+
+    while (current_item)
+    {
+        if (output_buffer->format)
+        {
+            size_t i;
+            output_pointer = ensure(output_buffer, output_buffer->depth);
+            if (output_pointer == NULL)
+            {
+                return false;
+            }
+            for (i = 0; i < output_buffer->depth; i++)
+            {
+                *output_pointer++ = '\t';
+            }
+            output_buffer->offset += output_buffer->depth;
+        }
+
+        /* print key */
+        if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        length = (size_t) (output_buffer->format ? 2 : 1);
+        output_pointer = ensure(output_buffer, length);
+        if (output_pointer == NULL)
+        {
+            return false;
+        }
+        *output_pointer++ = ':';
+        if (output_buffer->format)
+        {
+            *output_pointer++ = '\t';
+        }
+        output_buffer->offset += length;
+
+        /* print value */
+        if (!print_value(current_item, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        /* print comma if not last */
+        length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+        output_pointer = ensure(output_buffer, length + 1);
+        if (output_pointer == NULL)
+        {
+            return false;
+        }
+        if (current_item->next)
+        {
+            *output_pointer++ = ',';
+        }
+
+        if (output_buffer->format)
+        {
+            *output_pointer++ = '\n';
+        }
+        *output_pointer = '\0';
+        output_buffer->offset += length;
+
+        current_item = current_item->next;
+    }
+
+    output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+    if (output_buffer->format)
+    {
+        size_t i;
+        for (i = 0; i < (output_buffer->depth - 1); i++)
+        {
+            *output_pointer++ = '\t';
+        }
+    }
+    *output_pointer++ = '}';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+    cJSON *child = NULL;
+    size_t size = 0;
+
+    if (array == NULL)
+    {
+        return 0;
+    }
+
+    child = array->child;
+
+    while (child != NULL)
+    {
+        size++;
+        child = child->next;
+    }
+
+    /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+    return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON *array, size_t index)
+{
+    cJSON *current_child = NULL;
+
+    if (array == NULL)
+    {
+        return NULL;
+    }
+
+    current_child = array->child;
+    while ((current_child != NULL) && (index > 0))
+    {
+        index--;
+        current_child = current_child->next;
+    }
+
+    return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+    if (index < 0)
+    {
+        return NULL;
+    }
+
+    return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
+{
+    cJSON *current_element = NULL;
+
+    if ((object == NULL) || (name == NULL))
+    {
+        return NULL;
+    }
+
+    current_element = object->child;
+    if (case_sensitive)
+    {
+        while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
+        {
+            current_element = current_element->next;
+        }
+    }
+    else
+    {
+        while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
+        {
+            current_element = current_element->next;
+        }
+    }
+
+    if ((current_element == NULL) || (current_element->string == NULL)) {
+        return NULL;
+    }
+
+    return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
+{
+    return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+    return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+    return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+    prev->next = item;
+    item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
+{
+    cJSON *reference = NULL;
+    if (item == NULL)
+    {
+        return NULL;
+    }
+
+    reference = cJSON_New_Item(hooks);
+    if (reference == NULL)
+    {
+        return NULL;
+    }
+
+    memcpy(reference, item, sizeof(cJSON));
+    reference->string = NULL;
+    reference->type |= cJSON_IsReference;
+    reference->next = reference->prev = NULL;
+    return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+    cJSON *child = NULL;
+
+    if ((item == NULL) || (array == NULL) || (array == item))
+    {
+        return false;
+    }
+
+    child = array->child;
+    /*
+     * To find the last item in array quickly, we use prev in array
+     */
+    if (child == NULL)
+    {
+        /* list is empty, start new one */
+        array->child = item;
+        item->prev = item;
+        item->next = NULL;
+    }
+    else
+    {
+        /* append to the end */
+        if (child->prev)
+        {
+            suffix_object(child->prev, item);
+            array->child->prev = item;
+        }
+        else
+        {
+            while (child->next)
+            {
+                child = child->next;
+            }
+            suffix_object(child, item);
+            array->child->prev = item;
+        }
+    }
+
+    return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+    return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+    #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string)
+{
+    return (void*)string;
+}
+#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+    #pragma GCC diagnostic pop
+#endif
+
+
+static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
+{
+    char *new_key = NULL;
+    int new_type = cJSON_Invalid;
+
+    if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
+    {
+        return false;
+    }
+
+    if (constant_key)
+    {
+        new_key = (char*)cast_away_const(string);
+        new_type = item->type | cJSON_StringIsConst;
+    }
+    else
+    {
+        new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+        if (new_key == NULL)
+        {
+            return false;
+        }
+
+        new_type = item->type & ~cJSON_StringIsConst;
+    }
+
+    if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+    {
+        hooks->deallocate(item->string);
+    }
+
+    item->string = new_key;
+    item->type = new_type;
+
+    return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+    return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+    return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+    if (array == NULL)
+    {
+        return false;
+    }
+
+    return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+    if ((object == NULL) || (string == NULL))
+    {
+        return false;
+    }
+
+    return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
+{
+    cJSON *null = cJSON_CreateNull();
+    if (add_item_to_object(object, name, null, &global_hooks, false))
+    {
+        return null;
+    }
+
+    cJSON_Delete(null);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
+{
+    cJSON *true_item = cJSON_CreateTrue();
+    if (add_item_to_object(object, name, true_item, &global_hooks, false))
+    {
+        return true_item;
+    }
+
+    cJSON_Delete(true_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
+{
+    cJSON *false_item = cJSON_CreateFalse();
+    if (add_item_to_object(object, name, false_item, &global_hooks, false))
+    {
+        return false_item;
+    }
+
+    cJSON_Delete(false_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
+{
+    cJSON *bool_item = cJSON_CreateBool(boolean);
+    if (add_item_to_object(object, name, bool_item, &global_hooks, false))
+    {
+        return bool_item;
+    }
+
+    cJSON_Delete(bool_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
+{
+    cJSON *number_item = cJSON_CreateNumber(number);
+    if (add_item_to_object(object, name, number_item, &global_hooks, false))
+    {
+        return number_item;
+    }
+
+    cJSON_Delete(number_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
+{
+    cJSON *string_item = cJSON_CreateString(string);
+    if (add_item_to_object(object, name, string_item, &global_hooks, false))
+    {
+        return string_item;
+    }
+
+    cJSON_Delete(string_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
+{
+    cJSON *raw_item = cJSON_CreateRaw(raw);
+    if (add_item_to_object(object, name, raw_item, &global_hooks, false))
+    {
+        return raw_item;
+    }
+
+    cJSON_Delete(raw_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
+{
+    cJSON *object_item = cJSON_CreateObject();
+    if (add_item_to_object(object, name, object_item, &global_hooks, false))
+    {
+        return object_item;
+    }
+
+    cJSON_Delete(object_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
+{
+    cJSON *array = cJSON_CreateArray();
+    if (add_item_to_object(object, name, array, &global_hooks, false))
+    {
+        return array;
+    }
+
+    cJSON_Delete(array);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+    if ((parent == NULL) || (item == NULL))
+    {
+        return NULL;
+    }
+
+    if (item != parent->child)
+    {
+        /* not the first element */
+        item->prev->next = item->next;
+    }
+    if (item->next != NULL)
+    {
+        /* not the last element */
+        item->next->prev = item->prev;
+    }
+
+    if (item == parent->child)
+    {
+        /* first element */
+        parent->child = item->next;
+    }
+    else if (item->next == NULL)
+    {
+        /* last element */
+        parent->child->prev = item->prev;
+    }
+
+    /* make sure the detached item doesn't point anywhere anymore */
+    item->prev = NULL;
+    item->next = NULL;
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+    if (which < 0)
+    {
+        return NULL;
+    }
+
+    return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+    cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+    cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+    cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+    cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+    cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    cJSON *after_inserted = NULL;
+
+    if (which < 0)
+    {
+        return false;
+    }
+
+    after_inserted = get_array_item(array, (size_t)which);
+    if (after_inserted == NULL)
+    {
+        return add_item_to_array(array, newitem);
+    }
+
+    newitem->next = after_inserted;
+    newitem->prev = after_inserted->prev;
+    after_inserted->prev = newitem;
+    if (after_inserted == array->child)
+    {
+        array->child = newitem;
+    }
+    else
+    {
+        newitem->prev->next = newitem;
+    }
+    return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
+{
+    if ((parent == NULL) || (replacement == NULL) || (item == NULL))
+    {
+        return false;
+    }
+
+    if (replacement == item)
+    {
+        return true;
+    }
+
+    replacement->next = item->next;
+    replacement->prev = item->prev;
+
+    if (replacement->next != NULL)
+    {
+        replacement->next->prev = replacement;
+    }
+    if (parent->child == item)
+    {
+        if (parent->child->prev == parent->child)
+        {
+            replacement->prev = replacement;
+        }
+        parent->child = replacement;
+    }
+    else
+    {   /*
+         * To find the last item in array quickly, we use prev in array.
+         * We can't modify the last item's next pointer where this item was the parent's child
+         */
+        if (replacement->prev != NULL)
+        {
+            replacement->prev->next = replacement;
+        }
+        if (replacement->next == NULL)
+        {
+            parent->child->prev = replacement;
+        }
+    }
+
+    item->next = NULL;
+    item->prev = NULL;
+    cJSON_Delete(item);
+
+    return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    if (which < 0)
+    {
+        return false;
+    }
+
+    return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
+{
+    if ((replacement == NULL) || (string == NULL))
+    {
+        return false;
+    }
+
+    /* replace the name in the replacement */
+    if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
+    {
+        cJSON_free(replacement->string);
+    }
+    replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+    replacement->type &= ~cJSON_StringIsConst;
+
+    return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+    return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+    return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_NULL;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_True;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = boolean ? cJSON_True : cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_Number;
+        item->valuedouble = num;
+
+        /* use saturation in case of overflow */
+        if (num >= INT_MAX)
+        {
+            item->valueint = INT_MAX;
+        }
+        else if (num <= (double)INT_MIN)
+        {
+            item->valueint = INT_MIN;
+        }
+        else
+        {
+            item->valueint = (int)num;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_String;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+        if (!item->valuestring)
+        {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL)
+    {
+        item->type = cJSON_String | cJSON_IsReference;
+        item->valuestring = (char*)cast_away_const(string);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL) {
+        item->type = cJSON_Object | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL) {
+        item->type = cJSON_Array | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_Raw;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+        if (!item->valuestring)
+        {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type=cJSON_Array;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_Object;
+    }
+
+    return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+    for(i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber(numbers[i]);
+        if (!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for (i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber((double)numbers[i]);
+        if (!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if (!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for (i = 0;a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber(numbers[i]);
+        if (!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if (!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (strings == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for (i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateString(strings[i]);
+        if (!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if (!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p,n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+    cJSON *newitem = NULL;
+    cJSON *child = NULL;
+    cJSON *next = NULL;
+    cJSON *newchild = NULL;
+
+    /* Bail on bad ptr */
+    if (!item)
+    {
+        goto fail;
+    }
+    /* Create new item */
+    newitem = cJSON_New_Item(&global_hooks);
+    if (!newitem)
+    {
+        goto fail;
+    }
+    /* Copy over all vars */
+    newitem->type = item->type & (~cJSON_IsReference);
+    newitem->valueint = item->valueint;
+    newitem->valuedouble = item->valuedouble;
+    if (item->valuestring)
+    {
+        newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+        if (!newitem->valuestring)
+        {
+            goto fail;
+        }
+    }
+    if (item->string)
+    {
+        newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+        if (!newitem->string)
+        {
+            goto fail;
+        }
+    }
+    /* If non-recursive, then we're done! */
+    if (!recurse)
+    {
+        return newitem;
+    }
+    /* Walk the ->next chain for the child. */
+    child = item->child;
+    while (child != NULL)
+    {
+        newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
+        if (!newchild)
+        {
+            goto fail;
+        }
+        if (next != NULL)
+        {
+            /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+            next->next = newchild;
+            newchild->prev = next;
+            next = newchild;
+        }
+        else
+        {
+            /* Set newitem->child and move to it */
+            newitem->child = newchild;
+            next = newchild;
+        }
+        child = child->next;
+    }
+
+    return newitem;
+
+fail:
+    if (newitem != NULL)
+    {
+        cJSON_Delete(newitem);
+    }
+
+    return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+    *input += static_strlen("//");
+
+    for (; (*input)[0] != '\0'; ++(*input))
+    {
+        if ((*input)[0] == '\n') {
+            *input += static_strlen("\n");
+            return;
+        }
+    }
+}
+
+static void skip_multiline_comment(char **input)
+{
+    *input += static_strlen("/*");
+
+    for (; (*input)[0] != '\0'; ++(*input))
+    {
+        if (((*input)[0] == '*') && ((*input)[1] == '/'))
+        {
+            *input += static_strlen("*/");
+            return;
+        }
+    }
+}
+
+static void minify_string(char **input, char **output) {
+    (*output)[0] = (*input)[0];
+    *input += static_strlen("\"");
+    *output += static_strlen("\"");
+
+
+    for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+        (*output)[0] = (*input)[0];
+
+        if ((*input)[0] == '\"') {
+            (*output)[0] = '\"';
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+            return;
+        } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+            (*output)[1] = (*input)[1];
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+        }
+    }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+    char *into = json;
+
+    if (json == NULL)
+    {
+        return;
+    }
+
+    while (json[0] != '\0')
+    {
+        switch (json[0])
+        {
+            case ' ':
+            case '\t':
+            case '\r':
+            case '\n':
+                json++;
+                break;
+
+            case '/':
+                if (json[1] == '/')
+                {
+                    skip_oneline_comment(&json);
+                }
+                else if (json[1] == '*')
+                {
+                    skip_multiline_comment(&json);
+                } else {
+                    json++;
+                }
+                break;
+
+            case '\"':
+                minify_string(&json, (char**)&into);
+                break;
+
+            default:
+                into[0] = json[0];
+                json++;
+                into++;
+        }
+    }
+
+    /* and null-terminate. */
+    *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xff) == cJSON_True;
+}
+
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
+{
+    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))
+    {
+        return false;
+    }
+
+    /* check if type is valid */
+    switch (a->type & 0xFF)
+    {
+        case cJSON_False:
+        case cJSON_True:
+        case cJSON_NULL:
+        case cJSON_Number:
+        case cJSON_String:
+        case cJSON_Raw:
+        case cJSON_Array:
+        case cJSON_Object:
+            break;
+
+        default:
+            return false;
+    }
+
+    /* identical objects are equal */
+    if (a == b)
+    {
+        return true;
+    }
+
+    switch (a->type & 0xFF)
+    {
+        /* in these cases and equal type is enough */
+        case cJSON_False:
+        case cJSON_True:
+        case cJSON_NULL:
+            return true;
+
+        case cJSON_Number:
+            if (compare_double(a->valuedouble, b->valuedouble))
+            {
+                return true;
+            }
+            return false;
+
+        case cJSON_String:
+        case cJSON_Raw:
+            if ((a->valuestring == NULL) || (b->valuestring == NULL))
+            {
+                return false;
+            }
+            if (strcmp(a->valuestring, b->valuestring) == 0)
+            {
+                return true;
+            }
+
+            return false;
+
+        case cJSON_Array:
+        {
+            cJSON *a_element = a->child;
+            cJSON *b_element = b->child;
+
+            for (; (a_element != NULL) && (b_element != NULL);)
+            {
+                if (!cJSON_Compare(a_element, b_element, case_sensitive))
+                {
+                    return false;
+                }
+
+                a_element = a_element->next;
+                b_element = b_element->next;
+            }
+
+            /* one of the arrays is longer than the other */
+            if (a_element != b_element) {
+                return false;
+            }
+
+            return true;
+        }
+
+        case cJSON_Object:
+        {
+            cJSON *a_element = NULL;
+            cJSON *b_element = NULL;
+            cJSON_ArrayForEach(a_element, a)
+            {
+                /* TODO This has O(n^2) runtime, which is horrible! */
+                b_element = get_object_item(b, a_element->string, case_sensitive);
+                if (b_element == NULL)
+                {
+                    return false;
+                }
+
+                if (!cJSON_Compare(a_element, b_element, case_sensitive))
+                {
+                    return false;
+                }
+            }
+
+            /* doing this twice, once on a and b to prevent true comparison if a subset of b
+             * TODO: Do this the proper way, this is just a fix for now */
+            cJSON_ArrayForEach(b_element, b)
+            {
+                a_element = get_object_item(a, b_element->string, case_sensitive);
+                if (a_element == NULL)
+                {
+                    return false;
+                }
+
+                if (!cJSON_Compare(b_element, a_element, case_sensitive))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        default:
+            return false;
+    }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+    return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+    global_hooks.deallocate(object);
+}
diff --git a/dummy_fe/libcJSON/cJSON.h b/dummy_fe/libcJSON/cJSON.h
new file mode 100644
index 0000000..88d8933
--- /dev/null
+++ b/dummy_fe/libcJSON/cJSON.h
@@ -0,0 +1,293 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type)   type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type)   __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 13
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False  (1 << 0)
+#define cJSON_True   (1 << 1)
+#define cJSON_NULL   (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array  (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw    (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+    struct cJSON *next;
+    struct cJSON *prev;
+    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+    struct cJSON *child;
+
+    /* The type of the item, as above. */
+    int type;
+
+    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
+    char *valuestring;
+    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+    int valueint;
+    /* The item's number, if type==cJSON_Number */
+    double valuedouble;
+
+    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+    char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+      /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+      void *(CJSON_CDECL *malloc_fn)(size_t sz);
+      void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant,
+ * but should point to a readable and writable address area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/dummy_fe/libexpat/ascii.h b/dummy_fe/libexpat/ascii.h
new file mode 100644
index 0000000..1f594d2
--- /dev/null
+++ b/dummy_fe/libexpat/ascii.h
@@ -0,0 +1,123 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1999-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2007      Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#define ASCII_A 0x41
+#define ASCII_B 0x42
+#define ASCII_C 0x43
+#define ASCII_D 0x44
+#define ASCII_E 0x45
+#define ASCII_F 0x46
+#define ASCII_G 0x47
+#define ASCII_H 0x48
+#define ASCII_I 0x49
+#define ASCII_J 0x4A
+#define ASCII_K 0x4B
+#define ASCII_L 0x4C
+#define ASCII_M 0x4D
+#define ASCII_N 0x4E
+#define ASCII_O 0x4F
+#define ASCII_P 0x50
+#define ASCII_Q 0x51
+#define ASCII_R 0x52
+#define ASCII_S 0x53
+#define ASCII_T 0x54
+#define ASCII_U 0x55
+#define ASCII_V 0x56
+#define ASCII_W 0x57
+#define ASCII_X 0x58
+#define ASCII_Y 0x59
+#define ASCII_Z 0x5A
+
+#define ASCII_a 0x61
+#define ASCII_b 0x62
+#define ASCII_c 0x63
+#define ASCII_d 0x64
+#define ASCII_e 0x65
+#define ASCII_f 0x66
+#define ASCII_g 0x67
+#define ASCII_h 0x68
+#define ASCII_i 0x69
+#define ASCII_j 0x6A
+#define ASCII_k 0x6B
+#define ASCII_l 0x6C
+#define ASCII_m 0x6D
+#define ASCII_n 0x6E
+#define ASCII_o 0x6F
+#define ASCII_p 0x70
+#define ASCII_q 0x71
+#define ASCII_r 0x72
+#define ASCII_s 0x73
+#define ASCII_t 0x74
+#define ASCII_u 0x75
+#define ASCII_v 0x76
+#define ASCII_w 0x77
+#define ASCII_x 0x78
+#define ASCII_y 0x79
+#define ASCII_z 0x7A
+
+#define ASCII_0 0x30
+#define ASCII_1 0x31
+#define ASCII_2 0x32
+#define ASCII_3 0x33
+#define ASCII_4 0x34
+#define ASCII_5 0x35
+#define ASCII_6 0x36
+#define ASCII_7 0x37
+#define ASCII_8 0x38
+#define ASCII_9 0x39
+
+#define ASCII_TAB 0x09
+#define ASCII_SPACE 0x20
+#define ASCII_EXCL 0x21
+#define ASCII_QUOT 0x22
+#define ASCII_AMP 0x26
+#define ASCII_APOS 0x27
+#define ASCII_MINUS 0x2D
+#define ASCII_PERIOD 0x2E
+#define ASCII_COLON 0x3A
+#define ASCII_SEMI 0x3B
+#define ASCII_LT 0x3C
+#define ASCII_EQUALS 0x3D
+#define ASCII_GT 0x3E
+#define ASCII_LSQB 0x5B
+#define ASCII_RSQB 0x5D
+#define ASCII_UNDERSCORE 0x5F
+#define ASCII_LPAREN 0x28
+#define ASCII_RPAREN 0x29
+#define ASCII_FF 0x0C
+#define ASCII_SLASH 0x2F
+#define ASCII_HASH 0x23
+#define ASCII_PIPE 0x7C
+#define ASCII_COMMA 0x2C
diff --git a/dummy_fe/libexpat/asciitab.h b/dummy_fe/libexpat/asciitab.h
new file mode 100644
index 0000000..af766fb
--- /dev/null
+++ b/dummy_fe/libexpat/asciitab.h
@@ -0,0 +1,66 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+    /* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
+    /* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+    /* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+    /* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+    /* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+    /* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+    /* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+    /* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+    /* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+    /* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+    /* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+    /* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+    /* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+    /* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+    /* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+    /* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+    /* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/dummy_fe/libexpat/expat.h b/dummy_fe/libexpat/expat.h
new file mode 100644
index 0000000..1c83563
--- /dev/null
+++ b/dummy_fe/libexpat/expat.h
@@ -0,0 +1,1064 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016      Cristian Rodríguez <crrodriguez@opensuse.org>
+   Copyright (c) 2016      Thomas Beutlich <tc@tbeu.de>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2022      Thijs Schreijer <thijs@thijsschreijer.nl>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef Expat_INCLUDED
+#define Expat_INCLUDED 1
+
+#include <stdlib.h>
+#include "expat_external.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct XML_ParserStruct;
+typedef struct XML_ParserStruct *XML_Parser;
+
+typedef unsigned char XML_Bool;
+#define XML_TRUE ((XML_Bool)1)
+#define XML_FALSE ((XML_Bool)0)
+
+/* The XML_Status enum gives the possible return values for several
+   API functions.  The preprocessor #defines are included so this
+   stanza can be added to code that still needs to support older
+   versions of Expat 1.95.x:
+
+   #ifndef XML_STATUS_OK
+   #define XML_STATUS_OK    1
+   #define XML_STATUS_ERROR 0
+   #endif
+
+   Otherwise, the #define hackery is quite ugly and would have been
+   dropped.
+*/
+enum XML_Status {
+  XML_STATUS_ERROR = 0,
+#define XML_STATUS_ERROR XML_STATUS_ERROR
+  XML_STATUS_OK = 1,
+#define XML_STATUS_OK XML_STATUS_OK
+  XML_STATUS_SUSPENDED = 2
+#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED
+};
+
+enum XML_Error {
+  XML_ERROR_NONE,
+  XML_ERROR_NO_MEMORY,
+  XML_ERROR_SYNTAX,
+  XML_ERROR_NO_ELEMENTS,
+  XML_ERROR_INVALID_TOKEN,
+  XML_ERROR_UNCLOSED_TOKEN,
+  XML_ERROR_PARTIAL_CHAR,
+  XML_ERROR_TAG_MISMATCH,
+  XML_ERROR_DUPLICATE_ATTRIBUTE,
+  XML_ERROR_JUNK_AFTER_DOC_ELEMENT,
+  XML_ERROR_PARAM_ENTITY_REF,
+  XML_ERROR_UNDEFINED_ENTITY,
+  XML_ERROR_RECURSIVE_ENTITY_REF,
+  XML_ERROR_ASYNC_ENTITY,
+  XML_ERROR_BAD_CHAR_REF,
+  XML_ERROR_BINARY_ENTITY_REF,
+  XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF,
+  XML_ERROR_MISPLACED_XML_PI,
+  XML_ERROR_UNKNOWN_ENCODING,
+  XML_ERROR_INCORRECT_ENCODING,
+  XML_ERROR_UNCLOSED_CDATA_SECTION,
+  XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+  XML_ERROR_NOT_STANDALONE,
+  XML_ERROR_UNEXPECTED_STATE,
+  XML_ERROR_ENTITY_DECLARED_IN_PE,
+  XML_ERROR_FEATURE_REQUIRES_XML_DTD,
+  XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING,
+  /* Added in 1.95.7. */
+  XML_ERROR_UNBOUND_PREFIX,
+  /* Added in 1.95.8. */
+  XML_ERROR_UNDECLARING_PREFIX,
+  XML_ERROR_INCOMPLETE_PE,
+  XML_ERROR_XML_DECL,
+  XML_ERROR_TEXT_DECL,
+  XML_ERROR_PUBLICID,
+  XML_ERROR_SUSPENDED,
+  XML_ERROR_NOT_SUSPENDED,
+  XML_ERROR_ABORTED,
+  XML_ERROR_FINISHED,
+  XML_ERROR_SUSPEND_PE,
+  /* Added in 2.0. */
+  XML_ERROR_RESERVED_PREFIX_XML,
+  XML_ERROR_RESERVED_PREFIX_XMLNS,
+  XML_ERROR_RESERVED_NAMESPACE_URI,
+  /* Added in 2.2.1. */
+  XML_ERROR_INVALID_ARGUMENT,
+  /* Added in 2.3.0. */
+  XML_ERROR_NO_BUFFER,
+  /* Added in 2.4.0. */
+  XML_ERROR_AMPLIFICATION_LIMIT_BREACH
+};
+
+enum XML_Content_Type {
+  XML_CTYPE_EMPTY = 1,
+  XML_CTYPE_ANY,
+  XML_CTYPE_MIXED,
+  XML_CTYPE_NAME,
+  XML_CTYPE_CHOICE,
+  XML_CTYPE_SEQ
+};
+
+enum XML_Content_Quant {
+  XML_CQUANT_NONE,
+  XML_CQUANT_OPT,
+  XML_CQUANT_REP,
+  XML_CQUANT_PLUS
+};
+
+/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be
+   XML_CQUANT_NONE, and the other fields will be zero or NULL.
+   If type == XML_CTYPE_MIXED, then quant will be NONE or REP and
+   numchildren will contain number of elements that may be mixed in
+   and children point to an array of XML_Content cells that will be
+   all of XML_CTYPE_NAME type with no quantification.
+
+   If type == XML_CTYPE_NAME, then the name points to the name, and
+   the numchildren field will be zero and children will be NULL. The
+   quant fields indicates any quantifiers placed on the name.
+
+   CHOICE and SEQ will have name NULL, the number of children in
+   numchildren and children will point, recursively, to an array
+   of XML_Content cells.
+
+   The EMPTY, ANY, and MIXED types will only occur at top level.
+*/
+
+typedef struct XML_cp XML_Content;
+
+struct XML_cp {
+  enum XML_Content_Type type;
+  enum XML_Content_Quant quant;
+  XML_Char *name;
+  unsigned int numchildren;
+  XML_Content *children;
+};
+
+/* This is called for an element declaration. See above for
+   description of the model argument. It's the user code's responsibility
+   to free model when finished with it. See XML_FreeContentModel.
+   There is no need to free the model from the handler, it can be kept
+   around and freed at a later stage.
+*/
+typedef void(XMLCALL *XML_ElementDeclHandler)(void *userData,
+                                              const XML_Char *name,
+                                              XML_Content *model);
+
+XMLPARSEAPI(void)
+XML_SetElementDeclHandler(XML_Parser parser, XML_ElementDeclHandler eldecl);
+
+/* The Attlist declaration handler is called for *each* attribute. So
+   a single Attlist declaration with multiple attributes declared will
+   generate multiple calls to this handler. The "default" parameter
+   may be NULL in the case of the "#IMPLIED" or "#REQUIRED"
+   keyword. The "isrequired" parameter will be true and the default
+   value will be NULL in the case of "#REQUIRED". If "isrequired" is
+   true and default is non-NULL, then this is a "#FIXED" default.
+*/
+typedef void(XMLCALL *XML_AttlistDeclHandler)(
+    void *userData, const XML_Char *elname, const XML_Char *attname,
+    const XML_Char *att_type, const XML_Char *dflt, int isrequired);
+
+XMLPARSEAPI(void)
+XML_SetAttlistDeclHandler(XML_Parser parser, XML_AttlistDeclHandler attdecl);
+
+/* The XML declaration handler is called for *both* XML declarations
+   and text declarations. The way to distinguish is that the version
+   parameter will be NULL for text declarations. The encoding
+   parameter may be NULL for XML declarations. The standalone
+   parameter will be -1, 0, or 1 indicating respectively that there
+   was no standalone parameter in the declaration, that it was given
+   as no, or that it was given as yes.
+*/
+typedef void(XMLCALL *XML_XmlDeclHandler)(void *userData,
+                                          const XML_Char *version,
+                                          const XML_Char *encoding,
+                                          int standalone);
+
+XMLPARSEAPI(void)
+XML_SetXmlDeclHandler(XML_Parser parser, XML_XmlDeclHandler xmldecl);
+
+typedef struct {
+  void *(*malloc_fcn)(size_t size);
+  void *(*realloc_fcn)(void *ptr, size_t size);
+  void (*free_fcn)(void *ptr);
+} XML_Memory_Handling_Suite;
+
+/* Constructs a new parser; encoding is the encoding specified by the
+   external protocol or NULL if there is none specified.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ParserCreate(const XML_Char *encoding);
+
+/* Constructs a new parser and namespace processor.  Element type
+   names and attribute names that belong to a namespace will be
+   expanded; unprefixed attribute names are never expanded; unprefixed
+   element type names are expanded only if there is a default
+   namespace. The expanded name is the concatenation of the namespace
+   URI, the namespace separator character, and the local part of the
+   name.  If the namespace separator is '\0' then the namespace URI
+   and the local part will be concatenated without any separator.
+   It is a programming error to use the separator '\0' with namespace
+   triplets (see XML_SetReturnNSTriplet).
+   If a namespace separator is chosen that can be part of a URI or
+   part of an XML name, splitting an expanded name back into its
+   1, 2 or 3 original parts on application level in the element handler
+   may end up vulnerable, so these are advised against;  sane choices for
+   a namespace separator are e.g. '\n' (line feed) and '|' (pipe).
+
+   Note that Expat does not validate namespace URIs (beyond encoding)
+   against RFC 3986 today (and is not required to do so with regard to
+   the XML 1.0 namespaces specification) but it may start doing that
+   in future releases.  Before that, an application using Expat must
+   be ready to receive namespace URIs containing non-URI characters.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator);
+
+/* Constructs a new parser using the memory management suite referred to
+   by memsuite. If memsuite is NULL, then use the standard library memory
+   suite. If namespaceSeparator is non-NULL it creates a parser with
+   namespace processing as described above. The character pointed at
+   will serve as the namespace separator.
+
+   All further memory operations used for the created parser will come from
+   the given suite.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ParserCreate_MM(const XML_Char *encoding,
+                    const XML_Memory_Handling_Suite *memsuite,
+                    const XML_Char *namespaceSeparator);
+
+/* Prepare a parser object to be re-used.  This is particularly
+   valuable when memory allocation overhead is disproportionately high,
+   such as when a large number of small documnents need to be parsed.
+   All handlers are cleared from the parser, except for the
+   unknownEncodingHandler. The parser's external state is re-initialized
+   except for the values of ns and ns_triplets.
+
+   Added in Expat 1.95.3.
+*/
+XMLPARSEAPI(XML_Bool)
+XML_ParserReset(XML_Parser parser, const XML_Char *encoding);
+
+/* atts is array of name/value pairs, terminated by 0;
+   names and values are 0 terminated.
+*/
+typedef void(XMLCALL *XML_StartElementHandler)(void *userData,
+                                               const XML_Char *name,
+                                               const XML_Char **atts);
+
+typedef void(XMLCALL *XML_EndElementHandler)(void *userData,
+                                             const XML_Char *name);
+
+/* s is not 0 terminated. */
+typedef void(XMLCALL *XML_CharacterDataHandler)(void *userData,
+                                                const XML_Char *s, int len);
+
+/* target and data are 0 terminated */
+typedef void(XMLCALL *XML_ProcessingInstructionHandler)(void *userData,
+                                                        const XML_Char *target,
+                                                        const XML_Char *data);
+
+/* data is 0 terminated */
+typedef void(XMLCALL *XML_CommentHandler)(void *userData, const XML_Char *data);
+
+typedef void(XMLCALL *XML_StartCdataSectionHandler)(void *userData);
+typedef void(XMLCALL *XML_EndCdataSectionHandler)(void *userData);
+
+/* This is called for any characters in the XML document for which
+   there is no applicable handler.  This includes both characters that
+   are part of markup which is of a kind that is not reported
+   (comments, markup declarations), or characters that are part of a
+   construct which could be reported but for which no handler has been
+   supplied. The characters are passed exactly as they were in the XML
+   document except that they will be encoded in UTF-8 or UTF-16.
+   Line boundaries are not normalized. Note that a byte order mark
+   character is not passed to the default handler. There are no
+   guarantees about how characters are divided between calls to the
+   default handler: for example, a comment might be split between
+   multiple calls.
+*/
+typedef void(XMLCALL *XML_DefaultHandler)(void *userData, const XML_Char *s,
+                                          int len);
+
+/* This is called for the start of the DOCTYPE declaration, before
+   any DTD or internal subset is parsed.
+*/
+typedef void(XMLCALL *XML_StartDoctypeDeclHandler)(void *userData,
+                                                   const XML_Char *doctypeName,
+                                                   const XML_Char *sysid,
+                                                   const XML_Char *pubid,
+                                                   int has_internal_subset);
+
+/* This is called for the end of the DOCTYPE declaration when the
+   closing > is encountered, but after processing any external
+   subset.
+*/
+typedef void(XMLCALL *XML_EndDoctypeDeclHandler)(void *userData);
+
+/* This is called for entity declarations. The is_parameter_entity
+   argument will be non-zero if the entity is a parameter entity, zero
+   otherwise.
+
+   For internal entities (<!ENTITY foo "bar">), value will
+   be non-NULL and systemId, publicID, and notationName will be NULL.
+   The value string is NOT null-terminated; the length is provided in
+   the value_length argument. Since it is legal to have zero-length
+   values, do not use this argument to test for internal entities.
+
+   For external entities, value will be NULL and systemId will be
+   non-NULL. The publicId argument will be NULL unless a public
+   identifier was provided. The notationName argument will have a
+   non-NULL value only for unparsed entity declarations.
+
+   Note that is_parameter_entity can't be changed to XML_Bool, since
+   that would break binary compatibility.
+*/
+typedef void(XMLCALL *XML_EntityDeclHandler)(
+    void *userData, const XML_Char *entityName, int is_parameter_entity,
+    const XML_Char *value, int value_length, const XML_Char *base,
+    const XML_Char *systemId, const XML_Char *publicId,
+    const XML_Char *notationName);
+
+XMLPARSEAPI(void)
+XML_SetEntityDeclHandler(XML_Parser parser, XML_EntityDeclHandler handler);
+
+/* OBSOLETE -- OBSOLETE -- OBSOLETE
+   This handler has been superseded by the EntityDeclHandler above.
+   It is provided here for backward compatibility.
+
+   This is called for a declaration of an unparsed (NDATA) entity.
+   The base argument is whatever was set by XML_SetBase. The
+   entityName, systemId and notationName arguments will never be
+   NULL. The other arguments may be.
+*/
+typedef void(XMLCALL *XML_UnparsedEntityDeclHandler)(
+    void *userData, const XML_Char *entityName, const XML_Char *base,
+    const XML_Char *systemId, const XML_Char *publicId,
+    const XML_Char *notationName);
+
+/* This is called for a declaration of notation.  The base argument is
+   whatever was set by XML_SetBase. The notationName will never be
+   NULL.  The other arguments can be.
+*/
+typedef void(XMLCALL *XML_NotationDeclHandler)(void *userData,
+                                               const XML_Char *notationName,
+                                               const XML_Char *base,
+                                               const XML_Char *systemId,
+                                               const XML_Char *publicId);
+
+/* When namespace processing is enabled, these are called once for
+   each namespace declaration. The call to the start and end element
+   handlers occur between the calls to the start and end namespace
+   declaration handlers. For an xmlns attribute, prefix will be
+   NULL.  For an xmlns="" attribute, uri will be NULL.
+*/
+typedef void(XMLCALL *XML_StartNamespaceDeclHandler)(void *userData,
+                                                     const XML_Char *prefix,
+                                                     const XML_Char *uri);
+
+typedef void(XMLCALL *XML_EndNamespaceDeclHandler)(void *userData,
+                                                   const XML_Char *prefix);
+
+/* This is called if the document is not standalone, that is, it has an
+   external subset or a reference to a parameter entity, but does not
+   have standalone="yes". If this handler returns XML_STATUS_ERROR,
+   then processing will not continue, and the parser will return a
+   XML_ERROR_NOT_STANDALONE error.
+   If parameter entity parsing is enabled, then in addition to the
+   conditions above this handler will only be called if the referenced
+   entity was actually read.
+*/
+typedef int(XMLCALL *XML_NotStandaloneHandler)(void *userData);
+
+/* This is called for a reference to an external parsed general
+   entity.  The referenced entity is not automatically parsed.  The
+   application can parse it immediately or later using
+   XML_ExternalEntityParserCreate.
+
+   The parser argument is the parser parsing the entity containing the
+   reference; it can be passed as the parser argument to
+   XML_ExternalEntityParserCreate.  The systemId argument is the
+   system identifier as specified in the entity declaration; it will
+   not be NULL.
+
+   The base argument is the system identifier that should be used as
+   the base for resolving systemId if systemId was relative; this is
+   set by XML_SetBase; it may be NULL.
+
+   The publicId argument is the public identifier as specified in the
+   entity declaration, or NULL if none was specified; the whitespace
+   in the public identifier will have been normalized as required by
+   the XML spec.
+
+   The context argument specifies the parsing context in the format
+   expected by the context argument to XML_ExternalEntityParserCreate;
+   context is valid only until the handler returns, so if the
+   referenced entity is to be parsed later, it must be copied.
+   context is NULL only when the entity is a parameter entity.
+
+   The handler should return XML_STATUS_ERROR if processing should not
+   continue because of a fatal error in the handling of the external
+   entity.  In this case the calling parser will return an
+   XML_ERROR_EXTERNAL_ENTITY_HANDLING error.
+
+   Note that unlike other handlers the first argument is the parser,
+   not userData.
+*/
+typedef int(XMLCALL *XML_ExternalEntityRefHandler)(XML_Parser parser,
+                                                   const XML_Char *context,
+                                                   const XML_Char *base,
+                                                   const XML_Char *systemId,
+                                                   const XML_Char *publicId);
+
+/* This is called in two situations:
+   1) An entity reference is encountered for which no declaration
+      has been read *and* this is not an error.
+   2) An internal entity reference is read, but not expanded, because
+      XML_SetDefaultHandler has been called.
+   Note: skipped parameter entities in declarations and skipped general
+         entities in attribute values cannot be reported, because
+         the event would be out of sync with the reporting of the
+         declarations or attribute values
+*/
+typedef void(XMLCALL *XML_SkippedEntityHandler)(void *userData,
+                                                const XML_Char *entityName,
+                                                int is_parameter_entity);
+
+/* This structure is filled in by the XML_UnknownEncodingHandler to
+   provide information to the parser about encodings that are unknown
+   to the parser.
+
+   The map[b] member gives information about byte sequences whose
+   first byte is b.
+
+   If map[b] is c where c is >= 0, then b by itself encodes the
+   Unicode scalar value c.
+
+   If map[b] is -1, then the byte sequence is malformed.
+
+   If map[b] is -n, where n >= 2, then b is the first byte of an
+   n-byte sequence that encodes a single Unicode scalar value.
+
+   The data member will be passed as the first argument to the convert
+   function.
+
+   The convert function is used to convert multibyte sequences; s will
+   point to a n-byte sequence where map[(unsigned char)*s] == -n.  The
+   convert function must return the Unicode scalar value represented
+   by this byte sequence or -1 if the byte sequence is malformed.
+
+   The convert function may be NULL if the encoding is a single-byte
+   encoding, that is if map[b] >= -1 for all bytes b.
+
+   When the parser is finished with the encoding, then if release is
+   not NULL, it will call release passing it the data member; once
+   release has been called, the convert function will not be called
+   again.
+
+   Expat places certain restrictions on the encodings that are supported
+   using this mechanism.
+
+   1. Every ASCII character that can appear in a well-formed XML document,
+      other than the characters
+
+      $@\^`{}~
+
+      must be represented by a single byte, and that byte must be the
+      same byte that represents that character in ASCII.
+
+   2. No character may require more than 4 bytes to encode.
+
+   3. All characters encoded must have Unicode scalar values <=
+      0xFFFF, (i.e., characters that would be encoded by surrogates in
+      UTF-16 are  not allowed).  Note that this restriction doesn't
+      apply to the built-in support for UTF-8 and UTF-16.
+
+   4. No Unicode character may be encoded by more than one distinct
+      sequence of bytes.
+*/
+typedef struct {
+  int map[256];
+  void *data;
+  int(XMLCALL *convert)(void *data, const char *s);
+  void(XMLCALL *release)(void *data);
+} XML_Encoding;
+
+/* This is called for an encoding that is unknown to the parser.
+
+   The encodingHandlerData argument is that which was passed as the
+   second argument to XML_SetUnknownEncodingHandler.
+
+   The name argument gives the name of the encoding as specified in
+   the encoding declaration.
+
+   If the callback can provide information about the encoding, it must
+   fill in the XML_Encoding structure, and return XML_STATUS_OK.
+   Otherwise it must return XML_STATUS_ERROR.
+
+   If info does not describe a suitable encoding, then the parser will
+   return an XML_ERROR_UNKNOWN_ENCODING error.
+*/
+typedef int(XMLCALL *XML_UnknownEncodingHandler)(void *encodingHandlerData,
+                                                 const XML_Char *name,
+                                                 XML_Encoding *info);
+
+XMLPARSEAPI(void)
+XML_SetElementHandler(XML_Parser parser, XML_StartElementHandler start,
+                      XML_EndElementHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartElementHandler(XML_Parser parser, XML_StartElementHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetEndElementHandler(XML_Parser parser, XML_EndElementHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetCharacterDataHandler(XML_Parser parser,
+                            XML_CharacterDataHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetProcessingInstructionHandler(XML_Parser parser,
+                                    XML_ProcessingInstructionHandler handler);
+XMLPARSEAPI(void)
+XML_SetCommentHandler(XML_Parser parser, XML_CommentHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetCdataSectionHandler(XML_Parser parser,
+                           XML_StartCdataSectionHandler start,
+                           XML_EndCdataSectionHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartCdataSectionHandler(XML_Parser parser,
+                                XML_StartCdataSectionHandler start);
+
+XMLPARSEAPI(void)
+XML_SetEndCdataSectionHandler(XML_Parser parser,
+                              XML_EndCdataSectionHandler end);
+
+/* This sets the default handler and also inhibits expansion of
+   internal entities. These entity references will be passed to the
+   default handler, or to the skipped entity handler, if one is set.
+*/
+XMLPARSEAPI(void)
+XML_SetDefaultHandler(XML_Parser parser, XML_DefaultHandler handler);
+
+/* This sets the default handler but does not inhibit expansion of
+   internal entities.  The entity reference will not be passed to the
+   default handler.
+*/
+XMLPARSEAPI(void)
+XML_SetDefaultHandlerExpand(XML_Parser parser, XML_DefaultHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start,
+                          XML_EndDoctypeDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartDoctypeDeclHandler(XML_Parser parser,
+                               XML_StartDoctypeDeclHandler start);
+
+XMLPARSEAPI(void)
+XML_SetEndDoctypeDeclHandler(XML_Parser parser, XML_EndDoctypeDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+                                 XML_UnparsedEntityDeclHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetNotationDeclHandler(XML_Parser parser, XML_NotationDeclHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetNamespaceDeclHandler(XML_Parser parser,
+                            XML_StartNamespaceDeclHandler start,
+                            XML_EndNamespaceDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartNamespaceDeclHandler(XML_Parser parser,
+                                 XML_StartNamespaceDeclHandler start);
+
+XMLPARSEAPI(void)
+XML_SetEndNamespaceDeclHandler(XML_Parser parser,
+                               XML_EndNamespaceDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetNotStandaloneHandler(XML_Parser parser,
+                            XML_NotStandaloneHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetExternalEntityRefHandler(XML_Parser parser,
+                                XML_ExternalEntityRefHandler handler);
+
+/* If a non-NULL value for arg is specified here, then it will be
+   passed as the first argument to the external entity ref handler
+   instead of the parser object.
+*/
+XMLPARSEAPI(void)
+XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg);
+
+XMLPARSEAPI(void)
+XML_SetSkippedEntityHandler(XML_Parser parser,
+                            XML_SkippedEntityHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetUnknownEncodingHandler(XML_Parser parser,
+                              XML_UnknownEncodingHandler handler,
+                              void *encodingHandlerData);
+
+/* This can be called within a handler for a start element, end
+   element, processing instruction or character data.  It causes the
+   corresponding markup to be passed to the default handler.
+*/
+XMLPARSEAPI(void)
+XML_DefaultCurrent(XML_Parser parser);
+
+/* If do_nst is non-zero, and namespace processing is in effect, and
+   a name has a prefix (i.e. an explicit namespace qualifier) then
+   that name is returned as a triplet in a single string separated by
+   the separator character specified when the parser was created: URI
+   + sep + local_name + sep + prefix.
+
+   If do_nst is zero, then namespace information is returned in the
+   default manner (URI + sep + local_name) whether or not the name
+   has a prefix.
+
+   Note: Calling XML_SetReturnNSTriplet after XML_Parse or
+     XML_ParseBuffer has no effect.
+*/
+
+XMLPARSEAPI(void)
+XML_SetReturnNSTriplet(XML_Parser parser, int do_nst);
+
+/* This value is passed as the userData argument to callbacks. */
+XMLPARSEAPI(void)
+XML_SetUserData(XML_Parser parser, void *userData);
+
+/* Returns the last value set by XML_SetUserData or NULL. */
+#define XML_GetUserData(parser) (*(void **)(parser))
+
+/* This is equivalent to supplying an encoding argument to
+   XML_ParserCreate. On success XML_SetEncoding returns non-zero,
+   zero otherwise.
+   Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer
+     has no effect and returns XML_STATUS_ERROR.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_SetEncoding(XML_Parser parser, const XML_Char *encoding);
+
+/* If this function is called, then the parser will be passed as the
+   first argument to callbacks instead of userData.  The userData will
+   still be accessible using XML_GetUserData.
+*/
+XMLPARSEAPI(void)
+XML_UseParserAsHandlerArg(XML_Parser parser);
+
+/* If useDTD == XML_TRUE is passed to this function, then the parser
+   will assume that there is an external subset, even if none is
+   specified in the document. In such a case the parser will call the
+   externalEntityRefHandler with a value of NULL for the systemId
+   argument (the publicId and context arguments will be NULL as well).
+   Note: For the purpose of checking WFC: Entity Declared, passing
+     useDTD == XML_TRUE will make the parser behave as if the document
+     had a DTD with an external subset.
+   Note: If this function is called, then this must be done before
+     the first call to XML_Parse or XML_ParseBuffer, since it will
+     have no effect after that.  Returns
+     XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING.
+   Note: If the document does not have a DOCTYPE declaration at all,
+     then startDoctypeDeclHandler and endDoctypeDeclHandler will not
+     be called, despite an external subset being parsed.
+   Note: If XML_DTD is not defined when Expat is compiled, returns
+     XML_ERROR_FEATURE_REQUIRES_XML_DTD.
+   Note: If parser == NULL, returns XML_ERROR_INVALID_ARGUMENT.
+*/
+XMLPARSEAPI(enum XML_Error)
+XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD);
+
+/* Sets the base to be used for resolving relative URIs in system
+   identifiers in declarations.  Resolving relative identifiers is
+   left to the application: this value will be passed through as the
+   base argument to the XML_ExternalEntityRefHandler,
+   XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base
+   argument will be copied.  Returns XML_STATUS_ERROR if out of memory,
+   XML_STATUS_OK otherwise.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_SetBase(XML_Parser parser, const XML_Char *base);
+
+XMLPARSEAPI(const XML_Char *)
+XML_GetBase(XML_Parser parser);
+
+/* Returns the number of the attribute/value pairs passed in last call
+   to the XML_StartElementHandler that were specified in the start-tag
+   rather than defaulted. Each attribute/value pair counts as 2; thus
+   this corresponds to an index into the atts array passed to the
+   XML_StartElementHandler.  Returns -1 if parser == NULL.
+*/
+XMLPARSEAPI(int)
+XML_GetSpecifiedAttributeCount(XML_Parser parser);
+
+/* Returns the index of the ID attribute passed in the last call to
+   XML_StartElementHandler, or -1 if there is no ID attribute or
+   parser == NULL.  Each attribute/value pair counts as 2; thus this
+   corresponds to an index into the atts array passed to the
+   XML_StartElementHandler.
+*/
+XMLPARSEAPI(int)
+XML_GetIdAttributeIndex(XML_Parser parser);
+
+#ifdef XML_ATTR_INFO
+/* Source file byte offsets for the start and end of attribute names and values.
+   The value indices are exclusive of surrounding quotes; thus in a UTF-8 source
+   file an attribute value of "blah" will yield:
+   info->valueEnd - info->valueStart = 4 bytes.
+*/
+typedef struct {
+  XML_Index nameStart;  /* Offset to beginning of the attribute name. */
+  XML_Index nameEnd;    /* Offset after the attribute name's last byte. */
+  XML_Index valueStart; /* Offset to beginning of the attribute value. */
+  XML_Index valueEnd;   /* Offset after the attribute value's last byte. */
+} XML_AttrInfo;
+
+/* Returns an array of XML_AttrInfo structures for the attribute/value pairs
+   passed in last call to the XML_StartElementHandler that were specified
+   in the start-tag rather than defaulted. Each attribute/value pair counts
+   as 1; thus the number of entries in the array is
+   XML_GetSpecifiedAttributeCount(parser) / 2.
+*/
+XMLPARSEAPI(const XML_AttrInfo *)
+XML_GetAttributeInfo(XML_Parser parser);
+#endif
+
+/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is
+   detected.  The last call to XML_Parse must have isFinal true; len
+   may be zero for this call (or any other).
+
+   Though the return values for these functions has always been
+   described as a Boolean value, the implementation, at least for the
+   1.95.x series, has always returned exactly one of the XML_Status
+   values.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_Parse(XML_Parser parser, const char *s, int len, int isFinal);
+
+XMLPARSEAPI(void *)
+XML_GetBuffer(XML_Parser parser, int len);
+
+XMLPARSEAPI(enum XML_Status)
+XML_ParseBuffer(XML_Parser parser, int len, int isFinal);
+
+/* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return.
+   Must be called from within a call-back handler, except when aborting
+   (resumable = 0) an already suspended parser. Some call-backs may
+   still follow because they would otherwise get lost. Examples:
+   - endElementHandler() for empty elements when stopped in
+     startElementHandler(),
+   - endNameSpaceDeclHandler() when stopped in endElementHandler(),
+   and possibly others.
+
+   Can be called from most handlers, including DTD related call-backs,
+   except when parsing an external parameter entity and resumable != 0.
+   Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise.
+   Possible error codes:
+   - XML_ERROR_SUSPENDED: when suspending an already suspended parser.
+   - XML_ERROR_FINISHED: when the parser has already finished.
+   - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE.
+
+   When resumable != 0 (true) then parsing is suspended, that is,
+   XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED.
+   Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer()
+   return XML_STATUS_ERROR with error code XML_ERROR_ABORTED.
+
+   *Note*:
+   This will be applied to the current parser instance only, that is, if
+   there is a parent parser then it will continue parsing when the
+   externalEntityRefHandler() returns. It is up to the implementation of
+   the externalEntityRefHandler() to call XML_StopParser() on the parent
+   parser (recursively), if one wants to stop parsing altogether.
+
+   When suspended, parsing can be resumed by calling XML_ResumeParser().
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_StopParser(XML_Parser parser, XML_Bool resumable);
+
+/* Resumes parsing after it has been suspended with XML_StopParser().
+   Must not be called from within a handler call-back. Returns same
+   status codes as XML_Parse() or XML_ParseBuffer().
+   Additional error code XML_ERROR_NOT_SUSPENDED possible.
+
+   *Note*:
+   This must be called on the most deeply nested child parser instance
+   first, and on its parent parser only after the child parser has finished,
+   to be applied recursively until the document entity's parser is restarted.
+   That is, the parent parser will not resume by itself and it is up to the
+   application to call XML_ResumeParser() on it at the appropriate moment.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_ResumeParser(XML_Parser parser);
+
+enum XML_Parsing { XML_INITIALIZED, XML_PARSING, XML_FINISHED, XML_SUSPENDED };
+
+typedef struct {
+  enum XML_Parsing parsing;
+  XML_Bool finalBuffer;
+} XML_ParsingStatus;
+
+/* Returns status of parser with respect to being initialized, parsing,
+   finished, or suspended and processing the final buffer.
+   XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus,
+   XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED
+*/
+XMLPARSEAPI(void)
+XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status);
+
+/* Creates an XML_Parser object that can parse an external general
+   entity; context is a '\0'-terminated string specifying the parse
+   context; encoding is a '\0'-terminated string giving the name of
+   the externally specified encoding, or NULL if there is no
+   externally specified encoding.  The context string consists of a
+   sequence of tokens separated by formfeeds (\f); a token consisting
+   of a name specifies that the general entity of the name is open; a
+   token of the form prefix=uri specifies the namespace for a
+   particular prefix; a token of the form =uri specifies the default
+   namespace.  This can be called at any point after the first call to
+   an ExternalEntityRefHandler so longer as the parser has not yet
+   been freed.  The new parser is completely independent and may
+   safely be used in a separate thread.  The handlers and userData are
+   initialized from the parser argument.  Returns NULL if out of memory.
+   Otherwise returns a new XML_Parser object.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ExternalEntityParserCreate(XML_Parser parser, const XML_Char *context,
+                               const XML_Char *encoding);
+
+enum XML_ParamEntityParsing {
+  XML_PARAM_ENTITY_PARSING_NEVER,
+  XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE,
+  XML_PARAM_ENTITY_PARSING_ALWAYS
+};
+
+/* Controls parsing of parameter entities (including the external DTD
+   subset). If parsing of parameter entities is enabled, then
+   references to external parameter entities (including the external
+   DTD subset) will be passed to the handler set with
+   XML_SetExternalEntityRefHandler.  The context passed will be 0.
+
+   Unlike external general entities, external parameter entities can
+   only be parsed synchronously.  If the external parameter entity is
+   to be parsed, it must be parsed during the call to the external
+   entity ref handler: the complete sequence of
+   XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and
+   XML_ParserFree calls must be made during this call.  After
+   XML_ExternalEntityParserCreate has been called to create the parser
+   for the external parameter entity (context must be 0 for this
+   call), it is illegal to make any calls on the old parser until
+   XML_ParserFree has been called on the newly created parser.
+   If the library has been compiled without support for parameter
+   entity parsing (ie without XML_DTD being defined), then
+   XML_SetParamEntityParsing will return 0 if parsing of parameter
+   entities is requested; otherwise it will return non-zero.
+   Note: If XML_SetParamEntityParsing is called after XML_Parse or
+      XML_ParseBuffer, then it has no effect and will always return 0.
+   Note: If parser == NULL, the function will do nothing and return 0.
+*/
+XMLPARSEAPI(int)
+XML_SetParamEntityParsing(XML_Parser parser,
+                          enum XML_ParamEntityParsing parsing);
+
+/* Sets the hash salt to use for internal hash calculations.
+   Helps in preventing DoS attacks based on predicting hash
+   function behavior. This must be called before parsing is started.
+   Returns 1 if successful, 0 when called after parsing has started.
+   Note: If parser == NULL, the function will do nothing and return 0.
+*/
+XMLPARSEAPI(int)
+XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt);
+
+/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then
+   XML_GetErrorCode returns information about the error.
+*/
+XMLPARSEAPI(enum XML_Error)
+XML_GetErrorCode(XML_Parser parser);
+
+/* These functions return information about the current parse
+   location.  They may be called from any callback called to report
+   some parse event; in this case the location is the location of the
+   first of the sequence of characters that generated the event.  When
+   called from callbacks generated by declarations in the document
+   prologue, the location identified isn't as neatly defined, but will
+   be within the relevant markup.  When called outside of the callback
+   functions, the position indicated will be just past the last parse
+   event (regardless of whether there was an associated callback).
+
+   They may also be called after returning from a call to XML_Parse
+   or XML_ParseBuffer.  If the return value is XML_STATUS_ERROR then
+   the location is the location of the character at which the error
+   was detected; otherwise the location is the location of the last
+   parse event, as described above.
+
+   Note: XML_GetCurrentLineNumber and XML_GetCurrentColumnNumber
+   return 0 to indicate an error.
+   Note: XML_GetCurrentByteIndex returns -1 to indicate an error.
+*/
+XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser);
+XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser);
+XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser);
+
+/* Return the number of bytes in the current event.
+   Returns 0 if the event is in an internal entity.
+*/
+XMLPARSEAPI(int)
+XML_GetCurrentByteCount(XML_Parser parser);
+
+/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets
+   the integer pointed to by offset to the offset within this buffer
+   of the current parse position, and sets the integer pointed to by size
+   to the size of this buffer (the number of input bytes). Otherwise
+   returns a NULL pointer. Also returns a NULL pointer if a parse isn't
+   active.
+
+   NOTE: The character pointer returned should not be used outside
+   the handler that makes the call.
+*/
+XMLPARSEAPI(const char *)
+XML_GetInputContext(XML_Parser parser, int *offset, int *size);
+
+/* For backwards compatibility with previous versions. */
+#define XML_GetErrorLineNumber XML_GetCurrentLineNumber
+#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber
+#define XML_GetErrorByteIndex XML_GetCurrentByteIndex
+
+/* Frees the content model passed to the element declaration handler */
+XMLPARSEAPI(void)
+XML_FreeContentModel(XML_Parser parser, XML_Content *model);
+
+/* Exposing the memory handling functions used in Expat */
+XMLPARSEAPI(void *)
+XML_ATTR_MALLOC
+XML_ATTR_ALLOC_SIZE(2)
+XML_MemMalloc(XML_Parser parser, size_t size);
+
+XMLPARSEAPI(void *)
+XML_ATTR_ALLOC_SIZE(3)
+XML_MemRealloc(XML_Parser parser, void *ptr, size_t size);
+
+XMLPARSEAPI(void)
+XML_MemFree(XML_Parser parser, void *ptr);
+
+/* Frees memory used by the parser. */
+XMLPARSEAPI(void)
+XML_ParserFree(XML_Parser parser);
+
+/* Returns a string describing the error. */
+XMLPARSEAPI(const XML_LChar *)
+XML_ErrorString(enum XML_Error code);
+
+/* Return a string containing the version number of this expat */
+XMLPARSEAPI(const XML_LChar *)
+XML_ExpatVersion(void);
+
+typedef struct {
+  int major;
+  int minor;
+  int micro;
+} XML_Expat_Version;
+
+/* Return an XML_Expat_Version structure containing numeric version
+   number information for this version of expat.
+*/
+XMLPARSEAPI(XML_Expat_Version)
+XML_ExpatVersionInfo(void);
+
+/* Added in Expat 1.95.5. */
+enum XML_FeatureEnum {
+  XML_FEATURE_END = 0,
+  XML_FEATURE_UNICODE,
+  XML_FEATURE_UNICODE_WCHAR_T,
+  XML_FEATURE_DTD,
+  XML_FEATURE_CONTEXT_BYTES,
+  XML_FEATURE_MIN_SIZE,
+  XML_FEATURE_SIZEOF_XML_CHAR,
+  XML_FEATURE_SIZEOF_XML_LCHAR,
+  XML_FEATURE_NS,
+  XML_FEATURE_LARGE_SIZE,
+  XML_FEATURE_ATTR_INFO,
+  /* Added in Expat 2.4.0. */
+  XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
+  XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT
+  /* Additional features must be added to the end of this enum. */
+};
+
+typedef struct {
+  enum XML_FeatureEnum feature;
+  const XML_LChar *name;
+  long int value;
+} XML_Feature;
+
+XMLPARSEAPI(const XML_Feature *)
+XML_GetFeatureList(void);
+
+#ifdef XML_DTD
+/* Added in Expat 2.4.0. */
+XMLPARSEAPI(XML_Bool)
+XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+    XML_Parser parser, float maximumAmplificationFactor);
+
+/* Added in Expat 2.4.0. */
+XMLPARSEAPI(XML_Bool)
+XML_SetBillionLaughsAttackProtectionActivationThreshold(
+    XML_Parser parser, unsigned long long activationThresholdBytes);
+#endif
+
+/* Expat follows the semantic versioning convention.
+   See http://semver.org.
+*/
+#define XML_MAJOR_VERSION 2
+#define XML_MINOR_VERSION 5
+#define XML_MICRO_VERSION 0
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not Expat_INCLUDED */
diff --git a/dummy_fe/libexpat/expat_config.h b/dummy_fe/libexpat/expat_config.h
new file mode 100644
index 0000000..06281b7
--- /dev/null
+++ b/dummy_fe/libexpat/expat_config.h
@@ -0,0 +1,145 @@
+/* expat_config.h.  Generated from expat_config.h.in by configure.  */
+/* expat_config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* 1234 = LILENDIAN, 4321 = BIGENDIAN */
+#define BYTEORDER 1234
+
+/* Define to 1 if you have the `arc4random' function. */
+#if defined(__APPLE__) || defined(__BIONIC__)
+#define HAVE_ARC4RANDOM 1
+#endif
+
+/* Define to 1 if you have the `arc4random_buf' function. */
+#if defined(__APPLE__) || defined(__BIONIC__)
+#define HAVE_ARC4RANDOM_BUF 1
+#endif
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define to 1 if you have the `getrandom' function. */
+#if defined(__BIONIC__) || defined(ANDROID_HOST_MUSL)
+#define HAVE_GETRANDOM 1
+#endif
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `bsd' library (-lbsd). */
+/* #undef HAVE_LIBBSD */
+
+/* Define to 1 if you have a working `mmap' system call. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have `syscall' and `SYS_getrandom'. */
+#if 0 /* We only get here for glibc and Windows (where the better choices aren't available) and they don't have this fallback either. */
+#define HAVE_SYSCALL_GETRANDOM 1
+#endif
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "expat"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "expat-bugs@libexpat.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "expat"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "expat 2.4.8"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "expat"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.4.8"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "2.4.8"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* #  undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Define to allow retrieving the byte offsets for attribute names and values.
+   */
+/* #undef XML_ATTR_INFO */
+
+/* Define to specify how much context to retain around the current parse
+   point. */
+#define XML_CONTEXT_BYTES 1024
+
+/* Define to include code reading entropy from `/dev/urandom'. */
+#if defined(__GLIBC__)
+#define XML_DEV_URANDOM 1
+#endif
+
+/* Define to make parameter entity parsing functionality available. */
+#define XML_DTD 1
+
+/* Define to make XML Namespaces functionality available. */
+#define XML_NS 1
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `long int' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
diff --git a/dummy_fe/libexpat/expat_external.h b/dummy_fe/libexpat/expat_external.h
new file mode 100644
index 0000000..8829f77
--- /dev/null
+++ b/dummy_fe/libexpat/expat_external.h
@@ -0,0 +1,165 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016      Cristian Rodríguez <crrodriguez@opensuse.org>
+   Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2018      Yury Gribov <tetra2005@gmail.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef Expat_External_INCLUDED
+#define Expat_External_INCLUDED 1
+
+/* External API definitions */
+
+/* Expat tries very hard to make the API boundary very specifically
+   defined.  There are two macros defined to control this boundary;
+   each of these can be defined before including this header to
+   achieve some different behavior, but doing so it not recommended or
+   tested frequently.
+
+   XMLCALL    - The calling convention to use for all calls across the
+                "library boundary."  This will default to cdecl, and
+                try really hard to tell the compiler that's what we
+                want.
+
+   XMLIMPORT  - Whatever magic is needed to note that a function is
+                to be imported from a dynamically loaded library
+                (.dll, .so, or .sl, depending on your platform).
+
+   The XMLCALL macro was added in Expat 1.95.7.  The only one which is
+   expected to be directly useful in client code is XMLCALL.
+
+   Note that on at least some Unix versions, the Expat library must be
+   compiled with the cdecl calling convention as the default since
+   system headers may assume the cdecl convention.
+*/
+#ifndef XMLCALL
+#  if defined(_MSC_VER)
+#    define XMLCALL __cdecl
+#  elif defined(__GNUC__) && defined(__i386) && ! defined(__INTEL_COMPILER)
+#    define XMLCALL __attribute__((cdecl))
+#  else
+/* For any platform which uses this definition and supports more than
+   one calling convention, we need to extend this definition to
+   declare the convention used on that platform, if it's possible to
+   do so.
+
+   If this is the case for your platform, please file a bug report
+   with information on how to identify your platform via the C
+   pre-processor and how to specify the same calling convention as the
+   platform's malloc() implementation.
+*/
+#    define XMLCALL
+#  endif
+#endif /* not defined XMLCALL */
+
+#if ! defined(XML_STATIC) && ! defined(XMLIMPORT)
+#  ifndef XML_BUILDING_EXPAT
+/* using Expat from an application */
+
+#    if defined(_MSC_EXTENSIONS) && ! defined(__BEOS__) && ! defined(__CYGWIN__)
+#      define XMLIMPORT __declspec(dllimport)
+#    endif
+
+#  endif
+#endif /* not defined XML_STATIC */
+
+#ifndef XML_ENABLE_VISIBILITY
+#  define XML_ENABLE_VISIBILITY 0
+#endif
+
+#if ! defined(XMLIMPORT) && XML_ENABLE_VISIBILITY
+#  define XMLIMPORT __attribute__((visibility("default")))
+#endif
+
+/* If we didn't define it above, define it away: */
+#ifndef XMLIMPORT
+#  define XMLIMPORT
+#endif
+
+#if defined(__GNUC__)                                                          \
+    && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96))
+#  define XML_ATTR_MALLOC __attribute__((__malloc__))
+#else
+#  define XML_ATTR_MALLOC
+#endif
+
+#if defined(__GNUC__)                                                          \
+    && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+#  define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
+#else
+#  define XML_ATTR_ALLOC_SIZE(x)
+#endif
+
+#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef XML_UNICODE_WCHAR_T
+#  ifndef XML_UNICODE
+#    define XML_UNICODE
+#  endif
+#  if defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__ != 2)
+#    error "sizeof(wchar_t) != 2; Need -fshort-wchar for both Expat and libc"
+#  endif
+#endif
+
+#ifdef XML_UNICODE /* Information is UTF-16 encoded. */
+#  ifdef XML_UNICODE_WCHAR_T
+typedef wchar_t XML_Char;
+typedef wchar_t XML_LChar;
+#  else
+typedef unsigned short XML_Char;
+typedef char XML_LChar;
+#  endif /* XML_UNICODE_WCHAR_T */
+#else    /* Information is UTF-8 encoded. */
+typedef char XML_Char;
+typedef char XML_LChar;
+#endif   /* XML_UNICODE */
+
+#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */
+typedef long long XML_Index;
+typedef unsigned long long XML_Size;
+#else
+typedef long XML_Index;
+typedef unsigned long XML_Size;
+#endif /* XML_LARGE_SIZE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not Expat_External_INCLUDED */
diff --git a/dummy_fe/libexpat/iasciitab.h b/dummy_fe/libexpat/iasciitab.h
new file mode 100644
index 0000000..5d8646f
--- /dev/null
+++ b/dummy_fe/libexpat/iasciitab.h
@@ -0,0 +1,67 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+    /* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML,
+    /* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+    /* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+    /* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+    /* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+    /* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+    /* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+    /* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+    /* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+    /* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+    /* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+    /* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+    /* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+    /* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+    /* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+    /* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+    /* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/dummy_fe/libexpat/internal.h b/dummy_fe/libexpat/internal.h
new file mode 100644
index 0000000..e09f533
--- /dev/null
+++ b/dummy_fe/libexpat/internal.h
@@ -0,0 +1,165 @@
+/* internal.h
+
+   Internal definitions used by Expat.  This is not needed to compile
+   client code.
+
+   The following calling convention macros are defined for frequently
+   called functions:
+
+   FASTCALL    - Used for those internal functions that have a simple
+                 body and a low number of arguments and local variables.
+
+   PTRCALL     - Used for functions called though function pointers.
+
+   PTRFASTCALL - Like PTRCALL, but for low number of arguments.
+
+   inline      - Used for selected internal functions for which inlining
+                 may improve performance on some platforms.
+
+   Note: Use of these macros is based on judgement, not hard rules,
+         and therefore subject to change.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2018      Yury Gribov <tetra2005@gmail.com>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#if defined(__GNUC__) && defined(__i386__) && ! defined(__MINGW32__)
+/* We'll use this version by default only where we know it helps.
+
+   regparm() generates warnings on Solaris boxes.   See SF bug #692878.
+
+   Instability reported with egcs on a RedHat Linux 7.3.
+   Let's comment out:
+   #define FASTCALL __attribute__((stdcall, regparm(3)))
+   and let's try this:
+*/
+#  define FASTCALL __attribute__((regparm(3)))
+#  define PTRFASTCALL __attribute__((regparm(3)))
+#endif
+
+/* Using __fastcall seems to have an unexpected negative effect under
+   MS VC++, especially for function pointers, so we won't use it for
+   now on that platform. It may be reconsidered for a future release
+   if it can be made more effective.
+   Likely reason: __fastcall on Windows is like stdcall, therefore
+   the compiler cannot perform stack optimizations for call clusters.
+*/
+
+/* Make sure all of these are defined if they aren't already. */
+
+#ifndef FASTCALL
+#  define FASTCALL
+#endif
+
+#ifndef PTRCALL
+#  define PTRCALL
+#endif
+
+#ifndef PTRFASTCALL
+#  define PTRFASTCALL
+#endif
+
+#ifndef XML_MIN_SIZE
+#  if ! defined(__cplusplus) && ! defined(inline)
+#    ifdef __GNUC__
+#      define inline __inline
+#    endif /* __GNUC__ */
+#  endif
+#endif /* XML_MIN_SIZE */
+
+#ifdef __cplusplus
+#  define inline inline
+#else
+#  ifndef inline
+#    define inline
+#  endif
+#endif
+
+#include <limits.h> // ULONG_MAX
+
+#if defined(_WIN32)                                                            \
+    && (! defined(__USE_MINGW_ANSI_STDIO)                                      \
+        || (1 - __USE_MINGW_ANSI_STDIO - 1 == 0))
+#  define EXPAT_FMT_ULL(midpart) "%" midpart "I64u"
+#  if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW
+#    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d"
+#    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "I64u"
+#  else
+#    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
+#    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
+#  endif
+#else
+#  define EXPAT_FMT_ULL(midpart) "%" midpart "llu"
+#  if ! defined(ULONG_MAX)
+#    error Compiler did not define ULONG_MAX for us
+#  elif ULONG_MAX == 18446744073709551615u // 2^64-1
+#    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
+#    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "lu"
+#  else
+#    define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
+#    define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
+#  endif
+#endif
+
+#ifndef UNUSED_P
+#  define UNUSED_P(p) (void)p
+#endif
+
+/* NOTE BEGIN If you ever patch these defaults to greater values
+              for non-attack XML payload in your environment,
+              please file a bug report with libexpat.  Thank you!
+*/
+#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT   \
+  100.0f
+#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT    \
+  8388608 // 8 MiB, 2^23
+/* NOTE END */
+
+#include "expat.h" // so we can use type XML_Parser below
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void _INTERNAL_trim_to_complete_utf8_characters(const char *from,
+                                                const char **fromLimRef);
+
+#if defined(XML_DTD)
+unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser);
+unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
+const char *unsignedCharToPrintable(unsigned char c);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/dummy_fe/libexpat/latin1tab.h b/dummy_fe/libexpat/latin1tab.h
new file mode 100644
index 0000000..b681d27
--- /dev/null
+++ b/dummy_fe/libexpat/latin1tab.h
@@ -0,0 +1,66 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+    /* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME,
+    /* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+    /* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+    /* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+    /* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+    /* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+    /* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
diff --git a/dummy_fe/libexpat/nametab.h b/dummy_fe/libexpat/nametab.h
new file mode 100644
index 0000000..6348544
--- /dev/null
+++ b/dummy_fe/libexpat/nametab.h
@@ -0,0 +1,136 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+static const unsigned namingBitmap[] = {
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x04000000,
+    0x87FFFFFE, 0x07FFFFFE, 0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF,
+    0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFE00F, 0xFC31FFFF, 0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+    0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, 0xFFFF0003, 0xFFFFFFFF,
+    0xFFFF199F, 0x033FCFFF, 0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+    0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, 0x00000000, 0x07FFFFFE,
+    0x000007FE, 0xFFFE0000, 0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060,
+    0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, 0xFFF99FE0, 0x03C5FDFF,
+    0xB0000000, 0x00030003, 0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000,
+    0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, 0xFFF99FE0, 0x23CDFDFF,
+    0xB0000000, 0x00000003, 0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000,
+    0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, 0xFFFDDFE0, 0x03EFFDFF,
+    0x40000000, 0x00000003, 0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFE, 0x000D7FFF,
+    0x0000003F, 0x00000000, 0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000,
+    0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, 0x0007DAED, 0x50000000,
+    0x82315001, 0x002C62AB, 0x40000000, 0xF580C900, 0x00000007, 0x02010800,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0FFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0x03FFFFFF, 0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
+    0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, 0x00000000, 0x00004C40,
+    0x00000000, 0x00000000, 0x00000007, 0x00000000, 0x00000000, 0x00000000,
+    0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, 0x001FFFFF, 0xFFFFFFFE,
+    0xFFFFFFFF, 0x07FFFFFF, 0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000000F,
+    0x00000000, 0x00000000, 0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE,
+    0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, 0x00FFFFFF, 0x00000000,
+    0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000,
+    0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, 0xFFFFD7C0, 0xFFFFFFFB,
+    0x547F7FFF, 0x000FFFFD, 0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+    0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, 0x00000000, 0xFFFE0000,
+    0x027FFFFF, 0xFFFFFFFE, 0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF,
+    0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, 0xFFFFFFFF, 0x7CFFFFFF,
+    0xFFEF7FFF, 0x03FF3DFF, 0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF,
+    0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, 0xFFF987E4, 0xD36DFDFF,
+    0x5E003987, 0x001FFFC0, 0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1,
+    0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, 0xD63DC7EC, 0xC3BFC718,
+    0x00803DC7, 0x0000FF80, 0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3,
+    0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, 0xFFFDDFEC, 0xC3FFFDFF,
+    0x00803DCF, 0x0000FFC3, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, 0xFEF02596, 0x3BFF6CAE,
+    0x03FF3F5F, 0x00000000, 0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF,
+    0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1FFF0000, 0x00000002,
+    0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, 0x661FFFFF, 0xFFFFFFFE,
+    0xFFFFFFFF, 0x77FFFFFF,
+};
+static const unsigned char nmstrtPages[] = {
+    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x09, 0x0A, 0x0B,
+    0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, 0x00, 0x14, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+};
+static const unsigned char namePages[] = {
+    0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, 0x00, 0x1F, 0x20, 0x21,
+    0x22, 0x23, 0x24, 0x25, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, 0x26, 0x14, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+};
diff --git a/dummy_fe/libexpat/siphash.h b/dummy_fe/libexpat/siphash.h
new file mode 100644
index 0000000..303283a
--- /dev/null
+++ b/dummy_fe/libexpat/siphash.h
@@ -0,0 +1,393 @@
+/* ==========================================================================
+ * siphash.h - SipHash-2-4 in a single header file
+ * --------------------------------------------------------------------------
+ * Derived by William Ahern from the reference implementation[1] published[2]
+ * by Jean-Philippe Aumasson and Daniel J. Berstein.
+ * Minimal changes by Sebastian Pipping and Victor Stinner on top, see below.
+ * Licensed under the CC0 Public Domain Dedication license.
+ *
+ * 1. https://www.131002.net/siphash/siphash24.c
+ * 2. https://www.131002.net/siphash/
+ * --------------------------------------------------------------------------
+ * HISTORY:
+ *
+ * 2020-10-03  (Sebastian Pipping)
+ *   - Drop support for Visual Studio 9.0/2008 and earlier
+ *
+ * 2019-08-03  (Sebastian Pipping)
+ *   - Mark part of sip24_valid as to be excluded from clang-format
+ *   - Re-format code using clang-format 9
+ *
+ * 2018-07-08  (Anton Maklakov)
+ *   - Add "fall through" markers for GCC's -Wimplicit-fallthrough
+ *
+ * 2017-11-03  (Sebastian Pipping)
+ *   - Hide sip_tobin and sip_binof unless SIPHASH_TOBIN macro is defined
+ *
+ * 2017-07-25  (Vadim Zeitlin)
+ *   - Fix use of SIPHASH_MAIN macro
+ *
+ * 2017-07-05  (Sebastian Pipping)
+ *   - Use _SIP_ULL macro to not require a C++11 compiler if compiled as C++
+ *   - Add const qualifiers at two places
+ *   - Ensure <=80 characters line length (assuming tab width 4)
+ *
+ * 2017-06-23  (Victor Stinner)
+ *   - Address Win64 compile warnings
+ *
+ * 2017-06-18  (Sebastian Pipping)
+ *   - Clarify license note in the header
+ *   - Address C89 issues:
+ *     - Stop using inline keyword (and let compiler decide)
+ *     - Replace _Bool by int
+ *     - Turn macro siphash24 into a function
+ *     - Address invalid conversion (void pointer) by explicit cast
+ *   - Address lack of stdint.h for Visual Studio 2003 to 2008
+ *   - Always expose sip24_valid (for self-tests)
+ *
+ * 2012-11-04 - Born.  (William Ahern)
+ * --------------------------------------------------------------------------
+ * USAGE:
+ *
+ * SipHash-2-4 takes as input two 64-bit words as the key, some number of
+ * message bytes, and outputs a 64-bit word as the message digest. This
+ * implementation employs two data structures: a struct sipkey for
+ * representing the key, and a struct siphash for representing the hash
+ * state.
+ *
+ * For converting a 16-byte unsigned char array to a key, use either the
+ * macro sip_keyof or the routine sip_tokey. The former instantiates a
+ * compound literal key, while the latter requires a key object as a
+ * parameter.
+ *
+ * 	unsigned char secret[16];
+ * 	arc4random_buf(secret, sizeof secret);
+ * 	struct sipkey *key = sip_keyof(secret);
+ *
+ * For hashing a message, use either the convenience macro siphash24 or the
+ * routines sip24_init, sip24_update, and sip24_final.
+ *
+ * 	struct siphash state;
+ * 	void *msg;
+ * 	size_t len;
+ * 	uint64_t hash;
+ *
+ * 	sip24_init(&state, key);
+ * 	sip24_update(&state, msg, len);
+ * 	hash = sip24_final(&state);
+ *
+ * or
+ *
+ * 	hash = siphash24(msg, len, key);
+ *
+ * To convert the 64-bit hash value to a canonical 8-byte little-endian
+ * binary representation, use either the macro sip_binof or the routine
+ * sip_tobin. The former instantiates and returns a compound literal array,
+ * while the latter requires an array object as a parameter.
+ * --------------------------------------------------------------------------
+ * NOTES:
+ *
+ * o Neither sip_keyof, sip_binof, nor siphash24 will work with compilers
+ *   lacking compound literal support. Instead, you must use the lower-level
+ *   interfaces which take as parameters the temporary state objects.
+ *
+ * o Uppercase macros may evaluate parameters more than once. Lowercase
+ *   macros should not exhibit any such side effects.
+ * ==========================================================================
+ */
+#ifndef SIPHASH_H
+#define SIPHASH_H
+
+#include <stddef.h> /* size_t */
+#include <stdint.h> /* uint64_t uint32_t uint8_t */
+
+/*
+ * Workaround to not require a C++11 compiler for using ULL suffix
+ * if this code is included and compiled as C++; related GCC warning is:
+ * warning: use of C++11 long long integer constant [-Wlong-long]
+ */
+#define _SIP_ULL(high, low) ((((uint64_t)high) << 32) | (low))
+
+#define SIP_ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
+
+#define SIP_U32TO8_LE(p, v)                                                    \
+  (p)[0] = (uint8_t)((v) >> 0);                                                \
+  (p)[1] = (uint8_t)((v) >> 8);                                                \
+  (p)[2] = (uint8_t)((v) >> 16);                                               \
+  (p)[3] = (uint8_t)((v) >> 24);
+
+#define SIP_U64TO8_LE(p, v)                                                    \
+  SIP_U32TO8_LE((p) + 0, (uint32_t)((v) >> 0));                                \
+  SIP_U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
+
+#define SIP_U8TO64_LE(p)                                                       \
+  (((uint64_t)((p)[0]) << 0) | ((uint64_t)((p)[1]) << 8)                       \
+   | ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24)                   \
+   | ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40)                   \
+   | ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
+
+#define SIPHASH_INITIALIZER                                                    \
+  { 0, 0, 0, 0, {0}, 0, 0 }
+
+struct siphash {
+  uint64_t v0, v1, v2, v3;
+
+  unsigned char buf[8], *p;
+  uint64_t c;
+}; /* struct siphash */
+
+#define SIP_KEYLEN 16
+
+struct sipkey {
+  uint64_t k[2];
+}; /* struct sipkey */
+
+#define sip_keyof(k) sip_tokey(&(struct sipkey){{0}}, (k))
+
+static struct sipkey *
+sip_tokey(struct sipkey *key, const void *src) {
+  key->k[0] = SIP_U8TO64_LE((const unsigned char *)src);
+  key->k[1] = SIP_U8TO64_LE((const unsigned char *)src + 8);
+  return key;
+} /* sip_tokey() */
+
+#ifdef SIPHASH_TOBIN
+
+#  define sip_binof(v) sip_tobin((unsigned char[8]){0}, (v))
+
+static void *
+sip_tobin(void *dst, uint64_t u64) {
+  SIP_U64TO8_LE((unsigned char *)dst, u64);
+  return dst;
+} /* sip_tobin() */
+
+#endif /* SIPHASH_TOBIN */
+
+static void
+sip_round(struct siphash *H, const int rounds) {
+  int i;
+
+  for (i = 0; i < rounds; i++) {
+    H->v0 += H->v1;
+    H->v1 = SIP_ROTL(H->v1, 13);
+    H->v1 ^= H->v0;
+    H->v0 = SIP_ROTL(H->v0, 32);
+
+    H->v2 += H->v3;
+    H->v3 = SIP_ROTL(H->v3, 16);
+    H->v3 ^= H->v2;
+
+    H->v0 += H->v3;
+    H->v3 = SIP_ROTL(H->v3, 21);
+    H->v3 ^= H->v0;
+
+    H->v2 += H->v1;
+    H->v1 = SIP_ROTL(H->v1, 17);
+    H->v1 ^= H->v2;
+    H->v2 = SIP_ROTL(H->v2, 32);
+  }
+} /* sip_round() */
+
+static struct siphash *
+sip24_init(struct siphash *H, const struct sipkey *key) {
+  H->v0 = _SIP_ULL(0x736f6d65U, 0x70736575U) ^ key->k[0];
+  H->v1 = _SIP_ULL(0x646f7261U, 0x6e646f6dU) ^ key->k[1];
+  H->v2 = _SIP_ULL(0x6c796765U, 0x6e657261U) ^ key->k[0];
+  H->v3 = _SIP_ULL(0x74656462U, 0x79746573U) ^ key->k[1];
+
+  H->p = H->buf;
+  H->c = 0;
+
+  return H;
+} /* sip24_init() */
+
+#define sip_endof(a) (&(a)[sizeof(a) / sizeof *(a)])
+
+static struct siphash *
+sip24_update(struct siphash *H, const void *src, size_t len) {
+  const unsigned char *p = (const unsigned char *)src, *pe = p + len;
+  uint64_t m;
+
+  do {
+    while (p < pe && H->p < sip_endof(H->buf))
+      *H->p++ = *p++;
+
+    if (H->p < sip_endof(H->buf))
+      break;
+
+    m = SIP_U8TO64_LE(H->buf);
+    H->v3 ^= m;
+    sip_round(H, 2);
+    H->v0 ^= m;
+
+    H->p = H->buf;
+    H->c += 8;
+  } while (p < pe);
+
+  return H;
+} /* sip24_update() */
+
+static uint64_t
+sip24_final(struct siphash *H) {
+  const char left = (char)(H->p - H->buf);
+  uint64_t b = (H->c + left) << 56;
+
+  switch (left) {
+  case 7:
+    b |= (uint64_t)H->buf[6] << 48;
+    /* fall through */
+  case 6:
+    b |= (uint64_t)H->buf[5] << 40;
+    /* fall through */
+  case 5:
+    b |= (uint64_t)H->buf[4] << 32;
+    /* fall through */
+  case 4:
+    b |= (uint64_t)H->buf[3] << 24;
+    /* fall through */
+  case 3:
+    b |= (uint64_t)H->buf[2] << 16;
+    /* fall through */
+  case 2:
+    b |= (uint64_t)H->buf[1] << 8;
+    /* fall through */
+  case 1:
+    b |= (uint64_t)H->buf[0] << 0;
+    /* fall through */
+  case 0:
+    break;
+  }
+
+  H->v3 ^= b;
+  sip_round(H, 2);
+  H->v0 ^= b;
+  H->v2 ^= 0xff;
+  sip_round(H, 4);
+
+  return H->v0 ^ H->v1 ^ H->v2 ^ H->v3;
+} /* sip24_final() */
+
+static uint64_t
+siphash24(const void *src, size_t len, const struct sipkey *key) {
+  struct siphash state = SIPHASH_INITIALIZER;
+  return sip24_final(sip24_update(sip24_init(&state, key), src, len));
+} /* siphash24() */
+
+/*
+ * SipHash-2-4 output with
+ * k = 00 01 02 ...
+ * and
+ * in = (empty string)
+ * in = 00 (1 byte)
+ * in = 00 01 (2 bytes)
+ * in = 00 01 02 (3 bytes)
+ * ...
+ * in = 00 01 02 ... 3e (63 bytes)
+ */
+static int
+sip24_valid(void) {
+  /* clang-format off */
+  static const unsigned char vectors[64][8] = {
+    { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
+    { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
+    { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
+    { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
+    { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
+    { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
+    { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
+    { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
+    { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
+    { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
+    { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
+    { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
+    { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
+    { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
+    { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
+    { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
+    { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
+    { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
+    { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
+    { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
+    { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
+    { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
+    { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
+    { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
+    { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
+    { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
+    { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
+    { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
+    { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
+    { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
+    { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
+    { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
+    { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
+    { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
+    { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
+    { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
+    { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
+    { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
+    { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
+    { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
+    { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
+    { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
+    { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
+    { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
+    { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
+    { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
+    { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
+    { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
+    { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
+    { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
+    { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
+    { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
+    { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
+    { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
+    { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
+    { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
+    { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
+    { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
+    { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
+    { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
+    { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
+    { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
+    { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
+    { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }
+  };
+  /* clang-format on */
+
+  unsigned char in[64];
+  struct sipkey k;
+  size_t i;
+
+  sip_tokey(&k, "\000\001\002\003\004\005\006\007\010\011"
+                "\012\013\014\015\016\017");
+
+  for (i = 0; i < sizeof in; ++i) {
+    in[i] = (unsigned char)i;
+
+    if (siphash24(in, i, &k) != SIP_U8TO64_LE(vectors[i]))
+      return 0;
+  }
+
+  return 1;
+} /* sip24_valid() */
+
+#ifdef SIPHASH_MAIN
+
+#  include <stdio.h>
+
+int
+main(void) {
+  const int ok = sip24_valid();
+
+  if (ok)
+    puts("OK");
+  else
+    puts("FAIL");
+
+  return ! ok;
+} /* main() */
+
+#endif /* SIPHASH_MAIN */
+
+#endif /* SIPHASH_H */
diff --git a/dummy_fe/libexpat/utf8tab.h b/dummy_fe/libexpat/utf8tab.h
new file mode 100644
index 0000000..88efcf9
--- /dev/null
+++ b/dummy_fe/libexpat/utf8tab.h
@@ -0,0 +1,66 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+    /* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+    /* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+    /* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+    /* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+    /* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+    /* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+    /* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+    /* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+    /* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+    /* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+    /* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+    /* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+    /* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
+    /* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+    /* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM,
diff --git a/dummy_fe/libexpat/winconfig.h b/dummy_fe/libexpat/winconfig.h
new file mode 100644
index 0000000..2ecd61b
--- /dev/null
+++ b/dummy_fe/libexpat/winconfig.h
@@ -0,0 +1,45 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005      Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef WINCONFIG_H
+#define WINCONFIG_H
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+
+#include <memory.h>
+#include <string.h>
+
+#endif /* ndef WINCONFIG_H */
diff --git a/dummy_fe/libexpat/xmlparse.c b/dummy_fe/libexpat/xmlparse.c
new file mode 100644
index 0000000..b6c2eca
--- /dev/null
+++ b/dummy_fe/libexpat/xmlparse.c
@@ -0,0 +1,8404 @@
+/* 5ab094ffadd6edfc94c3eee53af44a86951f9f1f0933ada3114bbce2bfb02c99 (2.5.0+)
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2005-2009 Steven Solie <steven@solie.ca>
+   Copyright (c) 2016      Eric Rahm <erahm@mozilla.com>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016      Gaurav <g.gupta@samsung.com>
+   Copyright (c) 2016      Thomas Beutlich <tc@tbeu.de>
+   Copyright (c) 2016      Gustavo Grieco <gustavo.grieco@imag.fr>
+   Copyright (c) 2016      Pascal Cuoq <cuoq@trust-in-soft.com>
+   Copyright (c) 2016      Ed Schouten <ed@nuxi.nl>
+   Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Václav Slavík <vaclav@slavik.io>
+   Copyright (c) 2017      Viktor Szakats <commit@vsz.me>
+   Copyright (c) 2017      Chanho Park <chanho61.park@samsung.com>
+   Copyright (c) 2017      Rolf Eike Beer <eike@sf-mail.de>
+   Copyright (c) 2017      Hans Wennborg <hans@chromium.org>
+   Copyright (c) 2018      Anton Maklakov <antmak.pub@gmail.com>
+   Copyright (c) 2018      Benjamin Peterson <benjamin@python.org>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2018      Mariusz Zaborski <oshogbo@vexillium.org>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2019-2020 Ben Wagner <bungeman@chromium.org>
+   Copyright (c) 2019      Vadim Zeitlin <vadim@zeitlins.org>
+   Copyright (c) 2021      Dong-hee Na <donghee.na@python.org>
+   Copyright (c) 2022      Samanta Navarro <ferivoz@riseup.net>
+   Copyright (c) 2022      Jeffrey Walton <noloader@gmail.com>
+   Copyright (c) 2022      Jann Horn <jannh@google.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#define XML_BUILDING_EXPAT 1
+
+#include <expat_config.h>
+
+#if ! defined(_GNU_SOURCE)
+#  define _GNU_SOURCE 1 /* syscall prototype */
+#endif
+
+#ifdef _WIN32
+/* force stdlib to define rand_s() */
+#  if ! defined(_CRT_RAND_S)
+#    define _CRT_RAND_S
+#  endif
+#endif
+
+#include <stddef.h>
+#include <string.h> /* memset(), memcpy() */
+#include <assert.h>
+#include <limits.h> /* UINT_MAX */
+#include <stdio.h>  /* fprintf */
+#include <stdlib.h> /* getenv, rand_s */
+#include <stdint.h> /* uintptr_t */
+#include <math.h>   /* isnan */
+
+#ifdef _WIN32
+#  define getpid GetCurrentProcessId
+#else
+#  include <sys/time.h>  /* gettimeofday() */
+#  include <sys/types.h> /* getpid() */
+#  include <unistd.h>    /* getpid() */
+#  include <fcntl.h>     /* O_RDONLY */
+#  include <errno.h>
+#endif
+
+#ifdef _WIN32
+#  include "winconfig.h"
+#endif
+
+#include "ascii.h"
+#include "expat.h"
+#include "siphash.h"
+
+#if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)
+#  if defined(HAVE_GETRANDOM)
+#    include <sys/random.h> /* getrandom */
+#  else
+#    include <unistd.h>      /* syscall */
+#    include <sys/syscall.h> /* SYS_getrandom */
+#  endif
+#  if ! defined(GRND_NONBLOCK)
+#    define GRND_NONBLOCK 0x0001
+#  endif /* defined(GRND_NONBLOCK) */
+#endif   /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */
+
+#if defined(HAVE_LIBBSD)                                                       \
+    && (defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_ARC4RANDOM))
+#  include <bsd/stdlib.h>
+#endif
+
+#if defined(_WIN32) && ! defined(LOAD_LIBRARY_SEARCH_SYSTEM32)
+#  define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
+#endif
+
+#if ! defined(HAVE_GETRANDOM) && ! defined(HAVE_SYSCALL_GETRANDOM)             \
+    && ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM)            \
+    && ! defined(XML_DEV_URANDOM) && ! defined(_WIN32)                         \
+    && ! defined(XML_POOR_ENTROPY)
+#  error You do not have support for any sources of high quality entropy \
+    enabled.  For end user security, that is probably not what you want. \
+    \
+    Your options include: \
+      * Linux >=3.17 + glibc >=2.25 (getrandom): HAVE_GETRANDOM, \
+      * Linux >=3.17 + glibc (including <2.25) (syscall SYS_getrandom): HAVE_SYSCALL_GETRANDOM, \
+      * BSD / macOS >=10.7 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \
+      * BSD / macOS (including <10.7) (arc4random): HAVE_ARC4RANDOM, \
+      * libbsd (arc4random_buf): HAVE_ARC4RANDOM_BUF + HAVE_LIBBSD, \
+      * libbsd (arc4random): HAVE_ARC4RANDOM + HAVE_LIBBSD, \
+      * Linux (including <3.17) / BSD / macOS (including <10.7) / Solaris >=8 (/dev/urandom): XML_DEV_URANDOM, \
+      * Windows >=Vista (rand_s): _WIN32. \
+    \
+    If insist on not using any of these, bypass this error by defining \
+    XML_POOR_ENTROPY; you have been warned. \
+    \
+    If you have reasons to patch this detection code away or need changes \
+    to the build system, please open a bug.  Thank you!
+#endif
+
+#ifdef XML_UNICODE
+#  define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
+#  define XmlConvert XmlUtf16Convert
+#  define XmlGetInternalEncoding XmlGetUtf16InternalEncoding
+#  define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS
+#  define XmlEncode XmlUtf16Encode
+#  define MUST_CONVERT(enc, s) (! (enc)->isUtf16 || (((uintptr_t)(s)) & 1))
+typedef unsigned short ICHAR;
+#else
+#  define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX
+#  define XmlConvert XmlUtf8Convert
+#  define XmlGetInternalEncoding XmlGetUtf8InternalEncoding
+#  define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS
+#  define XmlEncode XmlUtf8Encode
+#  define MUST_CONVERT(enc, s) (! (enc)->isUtf8)
+typedef char ICHAR;
+#endif
+
+#ifndef XML_NS
+
+#  define XmlInitEncodingNS XmlInitEncoding
+#  define XmlInitUnknownEncodingNS XmlInitUnknownEncoding
+#  undef XmlGetInternalEncodingNS
+#  define XmlGetInternalEncodingNS XmlGetInternalEncoding
+#  define XmlParseXmlDeclNS XmlParseXmlDecl
+
+#endif
+
+#ifdef XML_UNICODE
+
+#  ifdef XML_UNICODE_WCHAR_T
+#    define XML_T(x) (const wchar_t) x
+#    define XML_L(x) L##x
+#  else
+#    define XML_T(x) (const unsigned short)x
+#    define XML_L(x) x
+#  endif
+
+#else
+
+#  define XML_T(x) x
+#  define XML_L(x) x
+
+#endif
+
+/* Round up n to be a multiple of sz, where sz is a power of 2. */
+#define ROUND_UP(n, sz) (((n) + ((sz)-1)) & ~((sz)-1))
+
+/* Do safe (NULL-aware) pointer arithmetic */
+#define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0)
+
+#include "internal.h"
+#include "xmltok.h"
+#include "xmlrole.h"
+
+typedef const XML_Char *KEY;
+
+typedef struct {
+  KEY name;
+} NAMED;
+
+typedef struct {
+  NAMED **v;
+  unsigned char power;
+  size_t size;
+  size_t used;
+  const XML_Memory_Handling_Suite *mem;
+} HASH_TABLE;
+
+static size_t keylen(KEY s);
+
+static void copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key);
+
+/* For probing (after a collision) we need a step size relative prime
+   to the hash table size, which is a power of 2. We use double-hashing,
+   since we can calculate a second hash value cheaply by taking those bits
+   of the first hash value that were discarded (masked out) when the table
+   index was calculated: index = hash & mask, where mask = table->size - 1.
+   We limit the maximum step size to table->size / 4 (mask >> 2) and make
+   it odd, since odd numbers are always relative prime to a power of 2.
+*/
+#define SECOND_HASH(hash, mask, power)                                         \
+  ((((hash) & ~(mask)) >> ((power)-1)) & ((mask) >> 2))
+#define PROBE_STEP(hash, mask, power)                                          \
+  ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1))
+
+typedef struct {
+  NAMED **p;
+  NAMED **end;
+} HASH_TABLE_ITER;
+
+#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */
+#define INIT_DATA_BUF_SIZE 1024
+#define INIT_ATTS_SIZE 16
+#define INIT_ATTS_VERSION 0xFFFFFFFF
+#define INIT_BLOCK_SIZE 1024
+#define INIT_BUFFER_SIZE 1024
+
+#define EXPAND_SPARE 24
+
+typedef struct binding {
+  struct prefix *prefix;
+  struct binding *nextTagBinding;
+  struct binding *prevPrefixBinding;
+  const struct attribute_id *attId;
+  XML_Char *uri;
+  int uriLen;
+  int uriAlloc;
+} BINDING;
+
+typedef struct prefix {
+  const XML_Char *name;
+  BINDING *binding;
+} PREFIX;
+
+typedef struct {
+  const XML_Char *str;
+  const XML_Char *localPart;
+  const XML_Char *prefix;
+  int strLen;
+  int uriLen;
+  int prefixLen;
+} TAG_NAME;
+
+/* TAG represents an open element.
+   The name of the element is stored in both the document and API
+   encodings.  The memory buffer 'buf' is a separately-allocated
+   memory area which stores the name.  During the XML_Parse()/
+   XMLParseBuffer() when the element is open, the memory for the 'raw'
+   version of the name (in the document encoding) is shared with the
+   document buffer.  If the element is open across calls to
+   XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to
+   contain the 'raw' name as well.
+
+   A parser re-uses these structures, maintaining a list of allocated
+   TAG objects in a free list.
+*/
+typedef struct tag {
+  struct tag *parent;  /* parent of this element */
+  const char *rawName; /* tagName in the original encoding */
+  int rawNameLength;
+  TAG_NAME name; /* tagName in the API encoding */
+  char *buf;     /* buffer for name components */
+  char *bufEnd;  /* end of the buffer */
+  BINDING *bindings;
+} TAG;
+
+typedef struct {
+  const XML_Char *name;
+  const XML_Char *textPtr;
+  int textLen;   /* length in XML_Chars */
+  int processed; /* # of processed bytes - when suspended */
+  const XML_Char *systemId;
+  const XML_Char *base;
+  const XML_Char *publicId;
+  const XML_Char *notation;
+  XML_Bool open;
+  XML_Bool is_param;
+  XML_Bool is_internal; /* true if declared in internal subset outside PE */
+} ENTITY;
+
+typedef struct {
+  enum XML_Content_Type type;
+  enum XML_Content_Quant quant;
+  const XML_Char *name;
+  int firstchild;
+  int lastchild;
+  int childcnt;
+  int nextsib;
+} CONTENT_SCAFFOLD;
+
+#define INIT_SCAFFOLD_ELEMENTS 32
+
+typedef struct block {
+  struct block *next;
+  int size;
+  XML_Char s[1];
+} BLOCK;
+
+typedef struct {
+  BLOCK *blocks;
+  BLOCK *freeBlocks;
+  const XML_Char *end;
+  XML_Char *ptr;
+  XML_Char *start;
+  const XML_Memory_Handling_Suite *mem;
+} STRING_POOL;
+
+/* The XML_Char before the name is used to determine whether
+   an attribute has been specified. */
+typedef struct attribute_id {
+  XML_Char *name;
+  PREFIX *prefix;
+  XML_Bool maybeTokenized;
+  XML_Bool xmlns;
+} ATTRIBUTE_ID;
+
+typedef struct {
+  const ATTRIBUTE_ID *id;
+  XML_Bool isCdata;
+  const XML_Char *value;
+} DEFAULT_ATTRIBUTE;
+
+typedef struct {
+  unsigned long version;
+  unsigned long hash;
+  const XML_Char *uriName;
+} NS_ATT;
+
+typedef struct {
+  const XML_Char *name;
+  PREFIX *prefix;
+  const ATTRIBUTE_ID *idAtt;
+  int nDefaultAtts;
+  int allocDefaultAtts;
+  DEFAULT_ATTRIBUTE *defaultAtts;
+} ELEMENT_TYPE;
+
+typedef struct {
+  HASH_TABLE generalEntities;
+  HASH_TABLE elementTypes;
+  HASH_TABLE attributeIds;
+  HASH_TABLE prefixes;
+  STRING_POOL pool;
+  STRING_POOL entityValuePool;
+  /* false once a parameter entity reference has been skipped */
+  XML_Bool keepProcessing;
+  /* true once an internal or external PE reference has been encountered;
+     this includes the reference to an external subset */
+  XML_Bool hasParamEntityRefs;
+  XML_Bool standalone;
+#ifdef XML_DTD
+  /* indicates if external PE has been read */
+  XML_Bool paramEntityRead;
+  HASH_TABLE paramEntities;
+#endif /* XML_DTD */
+  PREFIX defaultPrefix;
+  /* === scaffolding for building content model === */
+  XML_Bool in_eldecl;
+  CONTENT_SCAFFOLD *scaffold;
+  unsigned contentStringLen;
+  unsigned scaffSize;
+  unsigned scaffCount;
+  int scaffLevel;
+  int *scaffIndex;
+} DTD;
+
+typedef struct open_internal_entity {
+  const char *internalEventPtr;
+  const char *internalEventEndPtr;
+  struct open_internal_entity *next;
+  ENTITY *entity;
+  int startTagLevel;
+  XML_Bool betweenDecl; /* WFC: PE Between Declarations */
+} OPEN_INTERNAL_ENTITY;
+
+enum XML_Account {
+  XML_ACCOUNT_DIRECT,           /* bytes directly passed to the Expat parser */
+  XML_ACCOUNT_ENTITY_EXPANSION, /* intermediate bytes produced during entity
+                                   expansion */
+  XML_ACCOUNT_NONE              /* i.e. do not account, was accounted already */
+};
+
+#ifdef XML_DTD
+typedef unsigned long long XmlBigCount;
+typedef struct accounting {
+  XmlBigCount countBytesDirect;
+  XmlBigCount countBytesIndirect;
+  int debugLevel;
+  float maximumAmplificationFactor; // >=1.0
+  unsigned long long activationThresholdBytes;
+} ACCOUNTING;
+
+typedef struct entity_stats {
+  unsigned int countEverOpened;
+  unsigned int currentDepth;
+  unsigned int maximumDepthSeen;
+  int debugLevel;
+} ENTITY_STATS;
+#endif /* XML_DTD */
+
+typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start,
+                                         const char *end, const char **endPtr);
+
+static Processor prologProcessor;
+static Processor prologInitProcessor;
+static Processor contentProcessor;
+static Processor cdataSectionProcessor;
+#ifdef XML_DTD
+static Processor ignoreSectionProcessor;
+static Processor externalParEntProcessor;
+static Processor externalParEntInitProcessor;
+static Processor entityValueProcessor;
+static Processor entityValueInitProcessor;
+#endif /* XML_DTD */
+static Processor epilogProcessor;
+static Processor errorProcessor;
+static Processor externalEntityInitProcessor;
+static Processor externalEntityInitProcessor2;
+static Processor externalEntityInitProcessor3;
+static Processor externalEntityContentProcessor;
+static Processor internalEntityProcessor;
+
+static enum XML_Error handleUnknownEncoding(XML_Parser parser,
+                                            const XML_Char *encodingName);
+static enum XML_Error processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
+                                     const char *s, const char *next);
+static enum XML_Error initializeEncoding(XML_Parser parser);
+static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc,
+                               const char *s, const char *end, int tok,
+                               const char *next, const char **nextPtr,
+                               XML_Bool haveMore, XML_Bool allowClosingDoctype,
+                               enum XML_Account account);
+static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,
+                                            XML_Bool betweenDecl);
+static enum XML_Error doContent(XML_Parser parser, int startTagLevel,
+                                const ENCODING *enc, const char *start,
+                                const char *end, const char **endPtr,
+                                XML_Bool haveMore, enum XML_Account account);
+static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *,
+                                     const char **startPtr, const char *end,
+                                     const char **nextPtr, XML_Bool haveMore,
+                                     enum XML_Account account);
+#ifdef XML_DTD
+static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *,
+                                      const char **startPtr, const char *end,
+                                      const char **nextPtr, XML_Bool haveMore);
+#endif /* XML_DTD */
+
+static void freeBindings(XML_Parser parser, BINDING *bindings);
+static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *,
+                                const char *s, TAG_NAME *tagNamePtr,
+                                BINDING **bindingsPtr,
+                                enum XML_Account account);
+static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix,
+                                 const ATTRIBUTE_ID *attId, const XML_Char *uri,
+                                 BINDING **bindingsPtr);
+static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata,
+                           XML_Bool isId, const XML_Char *dfltValue,
+                           XML_Parser parser);
+static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *,
+                                          XML_Bool isCdata, const char *,
+                                          const char *, STRING_POOL *,
+                                          enum XML_Account account);
+static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *,
+                                           XML_Bool isCdata, const char *,
+                                           const char *, STRING_POOL *,
+                                           enum XML_Account account);
+static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc,
+                                    const char *start, const char *end);
+static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
+static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
+                                       const char *start, const char *end,
+                                       enum XML_Account account);
+static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
+                                       const char *start, const char *end);
+static int reportComment(XML_Parser parser, const ENCODING *enc,
+                         const char *start, const char *end);
+static void reportDefault(XML_Parser parser, const ENCODING *enc,
+                          const char *start, const char *end);
+
+static const XML_Char *getContext(XML_Parser parser);
+static XML_Bool setContext(XML_Parser parser, const XML_Char *context);
+
+static void FASTCALL normalizePublicId(XML_Char *s);
+
+static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms);
+/* do not call if m_parentParser != NULL */
+static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
+static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
+                       const XML_Memory_Handling_Suite *ms);
+static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
+                   const XML_Memory_Handling_Suite *ms);
+static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *, STRING_POOL *,
+                           const HASH_TABLE *);
+static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name,
+                     size_t createSize);
+static void FASTCALL hashTableInit(HASH_TABLE *,
+                                   const XML_Memory_Handling_Suite *ms);
+static void FASTCALL hashTableClear(HASH_TABLE *);
+static void FASTCALL hashTableDestroy(HASH_TABLE *);
+static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *);
+static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *);
+
+static void FASTCALL poolInit(STRING_POOL *,
+                              const XML_Memory_Handling_Suite *ms);
+static void FASTCALL poolClear(STRING_POOL *);
+static void FASTCALL poolDestroy(STRING_POOL *);
+static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
+                            const char *ptr, const char *end);
+static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+                                 const char *ptr, const char *end);
+static XML_Bool FASTCALL poolGrow(STRING_POOL *pool);
+static const XML_Char *FASTCALL poolCopyString(STRING_POOL *pool,
+                                               const XML_Char *s);
+static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s,
+                                       int n);
+static const XML_Char *FASTCALL poolAppendString(STRING_POOL *pool,
+                                                 const XML_Char *s);
+
+static int FASTCALL nextScaffoldPart(XML_Parser parser);
+static XML_Content *build_model(XML_Parser parser);
+static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc,
+                                    const char *ptr, const char *end);
+
+static XML_Char *copyString(const XML_Char *s,
+                            const XML_Memory_Handling_Suite *memsuite);
+
+static unsigned long generate_hash_secret_salt(XML_Parser parser);
+static XML_Bool startParsing(XML_Parser parser);
+
+static XML_Parser parserCreate(const XML_Char *encodingName,
+                               const XML_Memory_Handling_Suite *memsuite,
+                               const XML_Char *nameSep, DTD *dtd);
+
+static void parserInit(XML_Parser parser, const XML_Char *encodingName);
+
+#ifdef XML_DTD
+static float accountingGetCurrentAmplification(XML_Parser rootParser);
+static void accountingReportStats(XML_Parser originParser, const char *epilog);
+static void accountingOnAbort(XML_Parser originParser);
+static void accountingReportDiff(XML_Parser rootParser,
+                                 unsigned int levelsAwayFromRootParser,
+                                 const char *before, const char *after,
+                                 ptrdiff_t bytesMore, int source_line,
+                                 enum XML_Account account);
+static XML_Bool accountingDiffTolerated(XML_Parser originParser, int tok,
+                                        const char *before, const char *after,
+                                        int source_line,
+                                        enum XML_Account account);
+
+static void entityTrackingReportStats(XML_Parser parser, ENTITY *entity,
+                                      const char *action, int sourceLine);
+static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity,
+                                 int sourceLine);
+static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
+                                  int sourceLine);
+
+static XML_Parser getRootParserOf(XML_Parser parser,
+                                  unsigned int *outLevelDiff);
+#endif /* XML_DTD */
+
+static unsigned long getDebugLevel(const char *variableName,
+                                   unsigned long defaultDebugLevel);
+
+#define poolStart(pool) ((pool)->start)
+#define poolEnd(pool) ((pool)->ptr)
+#define poolLength(pool) ((pool)->ptr - (pool)->start)
+#define poolChop(pool) ((void)--(pool->ptr))
+#define poolLastChar(pool) (((pool)->ptr)[-1])
+#define poolDiscard(pool) ((pool)->ptr = (pool)->start)
+#define poolFinish(pool) ((pool)->start = (pool)->ptr)
+#define poolAppendChar(pool, c)                                                \
+  (((pool)->ptr == (pool)->end && ! poolGrow(pool))                            \
+       ? 0                                                                     \
+       : ((*((pool)->ptr)++ = c), 1))
+
+struct XML_ParserStruct {
+  /* The first member must be m_userData so that the XML_GetUserData
+     macro works. */
+  void *m_userData;
+  void *m_handlerArg;
+  char *m_buffer;
+  const XML_Memory_Handling_Suite m_mem;
+  /* first character to be parsed */
+  const char *m_bufferPtr;
+  /* past last character to be parsed */
+  char *m_bufferEnd;
+  /* allocated end of m_buffer */
+  const char *m_bufferLim;
+  XML_Index m_parseEndByteIndex;
+  const char *m_parseEndPtr;
+  XML_Char *m_dataBuf;
+  XML_Char *m_dataBufEnd;
+  XML_StartElementHandler m_startElementHandler;
+  XML_EndElementHandler m_endElementHandler;
+  XML_CharacterDataHandler m_characterDataHandler;
+  XML_ProcessingInstructionHandler m_processingInstructionHandler;
+  XML_CommentHandler m_commentHandler;
+  XML_StartCdataSectionHandler m_startCdataSectionHandler;
+  XML_EndCdataSectionHandler m_endCdataSectionHandler;
+  XML_DefaultHandler m_defaultHandler;
+  XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler;
+  XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler;
+  XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler;
+  XML_NotationDeclHandler m_notationDeclHandler;
+  XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler;
+  XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler;
+  XML_NotStandaloneHandler m_notStandaloneHandler;
+  XML_ExternalEntityRefHandler m_externalEntityRefHandler;
+  XML_Parser m_externalEntityRefHandlerArg;
+  XML_SkippedEntityHandler m_skippedEntityHandler;
+  XML_UnknownEncodingHandler m_unknownEncodingHandler;
+  XML_ElementDeclHandler m_elementDeclHandler;
+  XML_AttlistDeclHandler m_attlistDeclHandler;
+  XML_EntityDeclHandler m_entityDeclHandler;
+  XML_XmlDeclHandler m_xmlDeclHandler;
+  const ENCODING *m_encoding;
+  INIT_ENCODING m_initEncoding;
+  const ENCODING *m_internalEncoding;
+  const XML_Char *m_protocolEncodingName;
+  XML_Bool m_ns;
+  XML_Bool m_ns_triplets;
+  void *m_unknownEncodingMem;
+  void *m_unknownEncodingData;
+  void *m_unknownEncodingHandlerData;
+  void(XMLCALL *m_unknownEncodingRelease)(void *);
+  PROLOG_STATE m_prologState;
+  Processor *m_processor;
+  enum XML_Error m_errorCode;
+  const char *m_eventPtr;
+  const char *m_eventEndPtr;
+  const char *m_positionPtr;
+  OPEN_INTERNAL_ENTITY *m_openInternalEntities;
+  OPEN_INTERNAL_ENTITY *m_freeInternalEntities;
+  XML_Bool m_defaultExpandInternalEntities;
+  int m_tagLevel;
+  ENTITY *m_declEntity;
+  const XML_Char *m_doctypeName;
+  const XML_Char *m_doctypeSysid;
+  const XML_Char *m_doctypePubid;
+  const XML_Char *m_declAttributeType;
+  const XML_Char *m_declNotationName;
+  const XML_Char *m_declNotationPublicId;
+  ELEMENT_TYPE *m_declElementType;
+  ATTRIBUTE_ID *m_declAttributeId;
+  XML_Bool m_declAttributeIsCdata;
+  XML_Bool m_declAttributeIsId;
+  DTD *m_dtd;
+  const XML_Char *m_curBase;
+  TAG *m_tagStack;
+  TAG *m_freeTagList;
+  BINDING *m_inheritedBindings;
+  BINDING *m_freeBindingList;
+  int m_attsSize;
+  int m_nSpecifiedAtts;
+  int m_idAttIndex;
+  ATTRIBUTE *m_atts;
+  NS_ATT *m_nsAtts;
+  unsigned long m_nsAttsVersion;
+  unsigned char m_nsAttsPower;
+#ifdef XML_ATTR_INFO
+  XML_AttrInfo *m_attInfo;
+#endif
+  POSITION m_position;
+  STRING_POOL m_tempPool;
+  STRING_POOL m_temp2Pool;
+  char *m_groupConnector;
+  unsigned int m_groupSize;
+  XML_Char m_namespaceSeparator;
+  XML_Parser m_parentParser;
+  XML_ParsingStatus m_parsingStatus;
+#ifdef XML_DTD
+  XML_Bool m_isParamEntity;
+  XML_Bool m_useForeignDTD;
+  enum XML_ParamEntityParsing m_paramEntityParsing;
+#endif
+  unsigned long m_hash_secret_salt;
+#ifdef XML_DTD
+  ACCOUNTING m_accounting;
+  ENTITY_STATS m_entity_stats;
+#endif
+};
+
+#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
+#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s)))
+#define FREE(parser, p) (parser->m_mem.free_fcn((p)))
+
+XML_Parser XMLCALL
+XML_ParserCreate(const XML_Char *encodingName) {
+  return XML_ParserCreate_MM(encodingName, NULL, NULL);
+}
+
+XML_Parser XMLCALL
+XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) {
+  XML_Char tmp[2] = {nsSep, 0};
+  return XML_ParserCreate_MM(encodingName, NULL, tmp);
+}
+
+// "xml=http://www.w3.org/XML/1998/namespace"
+static const XML_Char implicitContext[]
+    = {ASCII_x,     ASCII_m,     ASCII_l,      ASCII_EQUALS, ASCII_h,
+       ASCII_t,     ASCII_t,     ASCII_p,      ASCII_COLON,  ASCII_SLASH,
+       ASCII_SLASH, ASCII_w,     ASCII_w,      ASCII_w,      ASCII_PERIOD,
+       ASCII_w,     ASCII_3,     ASCII_PERIOD, ASCII_o,      ASCII_r,
+       ASCII_g,     ASCII_SLASH, ASCII_X,      ASCII_M,      ASCII_L,
+       ASCII_SLASH, ASCII_1,     ASCII_9,      ASCII_9,      ASCII_8,
+       ASCII_SLASH, ASCII_n,     ASCII_a,      ASCII_m,      ASCII_e,
+       ASCII_s,     ASCII_p,     ASCII_a,      ASCII_c,      ASCII_e,
+       '\0'};
+
+/* To avoid warnings about unused functions: */
+#if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM)
+
+#  if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)
+
+/* Obtain entropy on Linux 3.17+ */
+static int
+writeRandomBytes_getrandom_nonblock(void *target, size_t count) {
+  int success = 0; /* full count bytes written? */
+  size_t bytesWrittenTotal = 0;
+  const unsigned int getrandomFlags = GRND_NONBLOCK;
+
+  do {
+    void *const currentTarget = (void *)((char *)target + bytesWrittenTotal);
+    const size_t bytesToWrite = count - bytesWrittenTotal;
+
+    const int bytesWrittenMore =
+#    if defined(HAVE_GETRANDOM)
+        getrandom(currentTarget, bytesToWrite, getrandomFlags);
+#    else
+        syscall(SYS_getrandom, currentTarget, bytesToWrite, getrandomFlags);
+#    endif
+
+    if (bytesWrittenMore > 0) {
+      bytesWrittenTotal += bytesWrittenMore;
+      if (bytesWrittenTotal >= count)
+        success = 1;
+    }
+  } while (! success && (errno == EINTR));
+
+  return success;
+}
+
+#  endif /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */
+
+#  if ! defined(_WIN32) && defined(XML_DEV_URANDOM)
+
+/* Extract entropy from /dev/urandom */
+static int
+writeRandomBytes_dev_urandom(void *target, size_t count) {
+  int success = 0; /* full count bytes written? */
+  size_t bytesWrittenTotal = 0;
+
+  const int fd = open("/dev/urandom", O_RDONLY);
+  if (fd < 0) {
+    return 0;
+  }
+
+  do {
+    void *const currentTarget = (void *)((char *)target + bytesWrittenTotal);
+    const size_t bytesToWrite = count - bytesWrittenTotal;
+
+    const ssize_t bytesWrittenMore = read(fd, currentTarget, bytesToWrite);
+
+    if (bytesWrittenMore > 0) {
+      bytesWrittenTotal += bytesWrittenMore;
+      if (bytesWrittenTotal >= count)
+        success = 1;
+    }
+  } while (! success && (errno == EINTR));
+
+  close(fd);
+  return success;
+}
+
+#  endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */
+
+#endif /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */
+
+#if defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF)
+
+static void
+writeRandomBytes_arc4random(void *target, size_t count) {
+  size_t bytesWrittenTotal = 0;
+
+  while (bytesWrittenTotal < count) {
+    const uint32_t random32 = arc4random();
+    size_t i = 0;
+
+    for (; (i < sizeof(random32)) && (bytesWrittenTotal < count);
+         i++, bytesWrittenTotal++) {
+      const uint8_t random8 = (uint8_t)(random32 >> (i * 8));
+      ((uint8_t *)target)[bytesWrittenTotal] = random8;
+    }
+  }
+}
+
+#endif /* defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF) */
+
+#ifdef _WIN32
+
+/* Provide declaration of rand_s() for MinGW-32 (not 64, which has it),
+   as it didn't declare it in its header prior to version 5.3.0 of its
+   runtime package (mingwrt, containing stdlib.h).  The upstream fix
+   was introduced at https://osdn.net/projects/mingw/ticket/39658 . */
+#  if defined(__MINGW32__) && defined(__MINGW32_VERSION)                       \
+      && __MINGW32_VERSION < 5003000L && ! defined(__MINGW64_VERSION_MAJOR)
+__declspec(dllimport) int rand_s(unsigned int *);
+#  endif
+
+/* Obtain entropy on Windows using the rand_s() function which
+ * generates cryptographically secure random numbers.  Internally it
+ * uses RtlGenRandom API which is present in Windows XP and later.
+ */
+static int
+writeRandomBytes_rand_s(void *target, size_t count) {
+  size_t bytesWrittenTotal = 0;
+
+  while (bytesWrittenTotal < count) {
+    unsigned int random32 = 0;
+    size_t i = 0;
+
+    if (rand_s(&random32))
+      return 0; /* failure */
+
+    for (; (i < sizeof(random32)) && (bytesWrittenTotal < count);
+         i++, bytesWrittenTotal++) {
+      const uint8_t random8 = (uint8_t)(random32 >> (i * 8));
+      ((uint8_t *)target)[bytesWrittenTotal] = random8;
+    }
+  }
+  return 1; /* success */
+}
+
+#endif /* _WIN32 */
+
+#if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM)
+
+static unsigned long
+gather_time_entropy(void) {
+#  ifdef _WIN32
+  FILETIME ft;
+  GetSystemTimeAsFileTime(&ft); /* never fails */
+  return ft.dwHighDateTime ^ ft.dwLowDateTime;
+#  else
+  struct timeval tv;
+  int gettimeofday_res;
+
+  gettimeofday_res = gettimeofday(&tv, NULL);
+
+#    if defined(NDEBUG)
+  (void)gettimeofday_res;
+#    else
+  assert(gettimeofday_res == 0);
+#    endif /* defined(NDEBUG) */
+
+  /* Microseconds time is <20 bits entropy */
+  return tv.tv_usec;
+#  endif
+}
+
+#endif /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */
+
+static unsigned long
+ENTROPY_DEBUG(const char *label, unsigned long entropy) {
+  if (getDebugLevel("EXPAT_ENTROPY_DEBUG", 0) >= 1u) {
+    fprintf(stderr, "expat: Entropy: %s --> 0x%0*lx (%lu bytes)\n", label,
+            (int)sizeof(entropy) * 2, entropy, (unsigned long)sizeof(entropy));
+  }
+  return entropy;
+}
+
+static unsigned long
+generate_hash_secret_salt(XML_Parser parser) {
+  unsigned long entropy;
+  (void)parser;
+
+  /* "Failproof" high quality providers: */
+#if defined(HAVE_ARC4RANDOM_BUF)
+  arc4random_buf(&entropy, sizeof(entropy));
+  return ENTROPY_DEBUG("arc4random_buf", entropy);
+#elif defined(HAVE_ARC4RANDOM)
+  writeRandomBytes_arc4random((void *)&entropy, sizeof(entropy));
+  return ENTROPY_DEBUG("arc4random", entropy);
+#else
+  /* Try high quality providers first .. */
+#  ifdef _WIN32
+  if (writeRandomBytes_rand_s((void *)&entropy, sizeof(entropy))) {
+    return ENTROPY_DEBUG("rand_s", entropy);
+  }
+#  elif defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)
+  if (writeRandomBytes_getrandom_nonblock((void *)&entropy, sizeof(entropy))) {
+    return ENTROPY_DEBUG("getrandom", entropy);
+  }
+#  endif
+#  if ! defined(_WIN32) && defined(XML_DEV_URANDOM)
+  if (writeRandomBytes_dev_urandom((void *)&entropy, sizeof(entropy))) {
+    return ENTROPY_DEBUG("/dev/urandom", entropy);
+  }
+#  endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */
+  /* .. and self-made low quality for backup: */
+
+  /* Process ID is 0 bits entropy if attacker has local access */
+  entropy = gather_time_entropy() ^ getpid();
+
+  /* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */
+  if (sizeof(unsigned long) == 4) {
+    return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647);
+  } else {
+    return ENTROPY_DEBUG("fallback(8)",
+                         entropy * (unsigned long)2305843009213693951ULL);
+  }
+#endif
+}
+
+static unsigned long
+get_hash_secret_salt(XML_Parser parser) {
+  if (parser->m_parentParser != NULL)
+    return get_hash_secret_salt(parser->m_parentParser);
+  return parser->m_hash_secret_salt;
+}
+
+static XML_Bool /* only valid for root parser */
+startParsing(XML_Parser parser) {
+  /* hash functions must be initialized before setContext() is called */
+  if (parser->m_hash_secret_salt == 0)
+    parser->m_hash_secret_salt = generate_hash_secret_salt(parser);
+  if (parser->m_ns) {
+    /* implicit context only set for root parser, since child
+       parsers (i.e. external entity parsers) will inherit it
+    */
+    return setContext(parser, implicitContext);
+  }
+  return XML_TRUE;
+}
+
+XML_Parser XMLCALL
+XML_ParserCreate_MM(const XML_Char *encodingName,
+                    const XML_Memory_Handling_Suite *memsuite,
+                    const XML_Char *nameSep) {
+  return parserCreate(encodingName, memsuite, nameSep, NULL);
+}
+
+static XML_Parser
+parserCreate(const XML_Char *encodingName,
+             const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep,
+             DTD *dtd) {
+  XML_Parser parser;
+
+  if (memsuite) {
+    XML_Memory_Handling_Suite *mtemp;
+    parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
+    if (parser != NULL) {
+      mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
+      mtemp->malloc_fcn = memsuite->malloc_fcn;
+      mtemp->realloc_fcn = memsuite->realloc_fcn;
+      mtemp->free_fcn = memsuite->free_fcn;
+    }
+  } else {
+    XML_Memory_Handling_Suite *mtemp;
+    parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
+    if (parser != NULL) {
+      mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
+      mtemp->malloc_fcn = malloc;
+      mtemp->realloc_fcn = realloc;
+      mtemp->free_fcn = free;
+    }
+  }
+
+  if (! parser)
+    return parser;
+
+  parser->m_buffer = NULL;
+  parser->m_bufferLim = NULL;
+
+  parser->m_attsSize = INIT_ATTS_SIZE;
+  parser->m_atts
+      = (ATTRIBUTE *)MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE));
+  if (parser->m_atts == NULL) {
+    FREE(parser, parser);
+    return NULL;
+  }
+#ifdef XML_ATTR_INFO
+  parser->m_attInfo = (XML_AttrInfo *)MALLOC(
+      parser, parser->m_attsSize * sizeof(XML_AttrInfo));
+  if (parser->m_attInfo == NULL) {
+    FREE(parser, parser->m_atts);
+    FREE(parser, parser);
+    return NULL;
+  }
+#endif
+  parser->m_dataBuf
+      = (XML_Char *)MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char));
+  if (parser->m_dataBuf == NULL) {
+    FREE(parser, parser->m_atts);
+#ifdef XML_ATTR_INFO
+    FREE(parser, parser->m_attInfo);
+#endif
+    FREE(parser, parser);
+    return NULL;
+  }
+  parser->m_dataBufEnd = parser->m_dataBuf + INIT_DATA_BUF_SIZE;
+
+  if (dtd)
+    parser->m_dtd = dtd;
+  else {
+    parser->m_dtd = dtdCreate(&parser->m_mem);
+    if (parser->m_dtd == NULL) {
+      FREE(parser, parser->m_dataBuf);
+      FREE(parser, parser->m_atts);
+#ifdef XML_ATTR_INFO
+      FREE(parser, parser->m_attInfo);
+#endif
+      FREE(parser, parser);
+      return NULL;
+    }
+  }
+
+  parser->m_freeBindingList = NULL;
+  parser->m_freeTagList = NULL;
+  parser->m_freeInternalEntities = NULL;
+
+  parser->m_groupSize = 0;
+  parser->m_groupConnector = NULL;
+
+  parser->m_unknownEncodingHandler = NULL;
+  parser->m_unknownEncodingHandlerData = NULL;
+
+  parser->m_namespaceSeparator = ASCII_EXCL;
+  parser->m_ns = XML_FALSE;
+  parser->m_ns_triplets = XML_FALSE;
+
+  parser->m_nsAtts = NULL;
+  parser->m_nsAttsVersion = 0;
+  parser->m_nsAttsPower = 0;
+
+  parser->m_protocolEncodingName = NULL;
+
+  poolInit(&parser->m_tempPool, &(parser->m_mem));
+  poolInit(&parser->m_temp2Pool, &(parser->m_mem));
+  parserInit(parser, encodingName);
+
+  if (encodingName && ! parser->m_protocolEncodingName) {
+    if (dtd) {
+      // We need to stop the upcoming call to XML_ParserFree from happily
+      // destroying parser->m_dtd because the DTD is shared with the parent
+      // parser and the only guard that keeps XML_ParserFree from destroying
+      // parser->m_dtd is parser->m_isParamEntity but it will be set to
+      // XML_TRUE only later in XML_ExternalEntityParserCreate (or not at all).
+      parser->m_dtd = NULL;
+    }
+    XML_ParserFree(parser);
+    return NULL;
+  }
+
+  if (nameSep) {
+    parser->m_ns = XML_TRUE;
+    parser->m_internalEncoding = XmlGetInternalEncodingNS();
+    parser->m_namespaceSeparator = *nameSep;
+  } else {
+    parser->m_internalEncoding = XmlGetInternalEncoding();
+  }
+
+  return parser;
+}
+
+static void
+parserInit(XML_Parser parser, const XML_Char *encodingName) {
+  parser->m_processor = prologInitProcessor;
+  XmlPrologStateInit(&parser->m_prologState);
+  if (encodingName != NULL) {
+    parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
+  }
+  parser->m_curBase = NULL;
+  XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0);
+  parser->m_userData = NULL;
+  parser->m_handlerArg = NULL;
+  parser->m_startElementHandler = NULL;
+  parser->m_endElementHandler = NULL;
+  parser->m_characterDataHandler = NULL;
+  parser->m_processingInstructionHandler = NULL;
+  parser->m_commentHandler = NULL;
+  parser->m_startCdataSectionHandler = NULL;
+  parser->m_endCdataSectionHandler = NULL;
+  parser->m_defaultHandler = NULL;
+  parser->m_startDoctypeDeclHandler = NULL;
+  parser->m_endDoctypeDeclHandler = NULL;
+  parser->m_unparsedEntityDeclHandler = NULL;
+  parser->m_notationDeclHandler = NULL;
+  parser->m_startNamespaceDeclHandler = NULL;
+  parser->m_endNamespaceDeclHandler = NULL;
+  parser->m_notStandaloneHandler = NULL;
+  parser->m_externalEntityRefHandler = NULL;
+  parser->m_externalEntityRefHandlerArg = parser;
+  parser->m_skippedEntityHandler = NULL;
+  parser->m_elementDeclHandler = NULL;
+  parser->m_attlistDeclHandler = NULL;
+  parser->m_entityDeclHandler = NULL;
+  parser->m_xmlDeclHandler = NULL;
+  parser->m_bufferPtr = parser->m_buffer;
+  parser->m_bufferEnd = parser->m_buffer;
+  parser->m_parseEndByteIndex = 0;
+  parser->m_parseEndPtr = NULL;
+  parser->m_declElementType = NULL;
+  parser->m_declAttributeId = NULL;
+  parser->m_declEntity = NULL;
+  parser->m_doctypeName = NULL;
+  parser->m_doctypeSysid = NULL;
+  parser->m_doctypePubid = NULL;
+  parser->m_declAttributeType = NULL;
+  parser->m_declNotationName = NULL;
+  parser->m_declNotationPublicId = NULL;
+  parser->m_declAttributeIsCdata = XML_FALSE;
+  parser->m_declAttributeIsId = XML_FALSE;
+  memset(&parser->m_position, 0, sizeof(POSITION));
+  parser->m_errorCode = XML_ERROR_NONE;
+  parser->m_eventPtr = NULL;
+  parser->m_eventEndPtr = NULL;
+  parser->m_positionPtr = NULL;
+  parser->m_openInternalEntities = NULL;
+  parser->m_defaultExpandInternalEntities = XML_TRUE;
+  parser->m_tagLevel = 0;
+  parser->m_tagStack = NULL;
+  parser->m_inheritedBindings = NULL;
+  parser->m_nSpecifiedAtts = 0;
+  parser->m_unknownEncodingMem = NULL;
+  parser->m_unknownEncodingRelease = NULL;
+  parser->m_unknownEncodingData = NULL;
+  parser->m_parentParser = NULL;
+  parser->m_parsingStatus.parsing = XML_INITIALIZED;
+#ifdef XML_DTD
+  parser->m_isParamEntity = XML_FALSE;
+  parser->m_useForeignDTD = XML_FALSE;
+  parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+#endif
+  parser->m_hash_secret_salt = 0;
+
+#ifdef XML_DTD
+  memset(&parser->m_accounting, 0, sizeof(ACCOUNTING));
+  parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u);
+  parser->m_accounting.maximumAmplificationFactor
+      = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT;
+  parser->m_accounting.activationThresholdBytes
+      = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT;
+
+  memset(&parser->m_entity_stats, 0, sizeof(ENTITY_STATS));
+  parser->m_entity_stats.debugLevel = getDebugLevel("EXPAT_ENTITY_DEBUG", 0u);
+#endif
+}
+
+/* moves list of bindings to m_freeBindingList */
+static void FASTCALL
+moveToFreeBindingList(XML_Parser parser, BINDING *bindings) {
+  while (bindings) {
+    BINDING *b = bindings;
+    bindings = bindings->nextTagBinding;
+    b->nextTagBinding = parser->m_freeBindingList;
+    parser->m_freeBindingList = b;
+  }
+}
+
+XML_Bool XMLCALL
+XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
+  TAG *tStk;
+  OPEN_INTERNAL_ENTITY *openEntityList;
+
+  if (parser == NULL)
+    return XML_FALSE;
+
+  if (parser->m_parentParser)
+    return XML_FALSE;
+  /* move m_tagStack to m_freeTagList */
+  tStk = parser->m_tagStack;
+  while (tStk) {
+    TAG *tag = tStk;
+    tStk = tStk->parent;
+    tag->parent = parser->m_freeTagList;
+    moveToFreeBindingList(parser, tag->bindings);
+    tag->bindings = NULL;
+    parser->m_freeTagList = tag;
+  }
+  /* move m_openInternalEntities to m_freeInternalEntities */
+  openEntityList = parser->m_openInternalEntities;
+  while (openEntityList) {
+    OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
+    openEntityList = openEntity->next;
+    openEntity->next = parser->m_freeInternalEntities;
+    parser->m_freeInternalEntities = openEntity;
+  }
+  moveToFreeBindingList(parser, parser->m_inheritedBindings);
+  FREE(parser, parser->m_unknownEncodingMem);
+  if (parser->m_unknownEncodingRelease)
+    parser->m_unknownEncodingRelease(parser->m_unknownEncodingData);
+  poolClear(&parser->m_tempPool);
+  poolClear(&parser->m_temp2Pool);
+  FREE(parser, (void *)parser->m_protocolEncodingName);
+  parser->m_protocolEncodingName = NULL;
+  parserInit(parser, encodingName);
+  dtdReset(parser->m_dtd, &parser->m_mem);
+  return XML_TRUE;
+}
+
+enum XML_Status XMLCALL
+XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) {
+  if (parser == NULL)
+    return XML_STATUS_ERROR;
+  /* Block after XML_Parse()/XML_ParseBuffer() has been called.
+     XXX There's no way for the caller to determine which of the
+     XXX possible error cases caused the XML_STATUS_ERROR return.
+  */
+  if (parser->m_parsingStatus.parsing == XML_PARSING
+      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+    return XML_STATUS_ERROR;
+
+  /* Get rid of any previous encoding name */
+  FREE(parser, (void *)parser->m_protocolEncodingName);
+
+  if (encodingName == NULL)
+    /* No new encoding name */
+    parser->m_protocolEncodingName = NULL;
+  else {
+    /* Copy the new encoding name into allocated memory */
+    parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
+    if (! parser->m_protocolEncodingName)
+      return XML_STATUS_ERROR;
+  }
+  return XML_STATUS_OK;
+}
+
+XML_Parser XMLCALL
+XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
+                               const XML_Char *encodingName) {
+  XML_Parser parser = oldParser;
+  DTD *newDtd = NULL;
+  DTD *oldDtd;
+  XML_StartElementHandler oldStartElementHandler;
+  XML_EndElementHandler oldEndElementHandler;
+  XML_CharacterDataHandler oldCharacterDataHandler;
+  XML_ProcessingInstructionHandler oldProcessingInstructionHandler;
+  XML_CommentHandler oldCommentHandler;
+  XML_StartCdataSectionHandler oldStartCdataSectionHandler;
+  XML_EndCdataSectionHandler oldEndCdataSectionHandler;
+  XML_DefaultHandler oldDefaultHandler;
+  XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler;
+  XML_NotationDeclHandler oldNotationDeclHandler;
+  XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler;
+  XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler;
+  XML_NotStandaloneHandler oldNotStandaloneHandler;
+  XML_ExternalEntityRefHandler oldExternalEntityRefHandler;
+  XML_SkippedEntityHandler oldSkippedEntityHandler;
+  XML_UnknownEncodingHandler oldUnknownEncodingHandler;
+  XML_ElementDeclHandler oldElementDeclHandler;
+  XML_AttlistDeclHandler oldAttlistDeclHandler;
+  XML_EntityDeclHandler oldEntityDeclHandler;
+  XML_XmlDeclHandler oldXmlDeclHandler;
+  ELEMENT_TYPE *oldDeclElementType;
+
+  void *oldUserData;
+  void *oldHandlerArg;
+  XML_Bool oldDefaultExpandInternalEntities;
+  XML_Parser oldExternalEntityRefHandlerArg;
+#ifdef XML_DTD
+  enum XML_ParamEntityParsing oldParamEntityParsing;
+  int oldInEntityValue;
+#endif
+  XML_Bool oldns_triplets;
+  /* Note that the new parser shares the same hash secret as the old
+     parser, so that dtdCopy and copyEntityTable can lookup values
+     from hash tables associated with either parser without us having
+     to worry which hash secrets each table has.
+  */
+  unsigned long oldhash_secret_salt;
+
+  /* Validate the oldParser parameter before we pull everything out of it */
+  if (oldParser == NULL)
+    return NULL;
+
+  /* Stash the original parser contents on the stack */
+  oldDtd = parser->m_dtd;
+  oldStartElementHandler = parser->m_startElementHandler;
+  oldEndElementHandler = parser->m_endElementHandler;
+  oldCharacterDataHandler = parser->m_characterDataHandler;
+  oldProcessingInstructionHandler = parser->m_processingInstructionHandler;
+  oldCommentHandler = parser->m_commentHandler;
+  oldStartCdataSectionHandler = parser->m_startCdataSectionHandler;
+  oldEndCdataSectionHandler = parser->m_endCdataSectionHandler;
+  oldDefaultHandler = parser->m_defaultHandler;
+  oldUnparsedEntityDeclHandler = parser->m_unparsedEntityDeclHandler;
+  oldNotationDeclHandler = parser->m_notationDeclHandler;
+  oldStartNamespaceDeclHandler = parser->m_startNamespaceDeclHandler;
+  oldEndNamespaceDeclHandler = parser->m_endNamespaceDeclHandler;
+  oldNotStandaloneHandler = parser->m_notStandaloneHandler;
+  oldExternalEntityRefHandler = parser->m_externalEntityRefHandler;
+  oldSkippedEntityHandler = parser->m_skippedEntityHandler;
+  oldUnknownEncodingHandler = parser->m_unknownEncodingHandler;
+  oldElementDeclHandler = parser->m_elementDeclHandler;
+  oldAttlistDeclHandler = parser->m_attlistDeclHandler;
+  oldEntityDeclHandler = parser->m_entityDeclHandler;
+  oldXmlDeclHandler = parser->m_xmlDeclHandler;
+  oldDeclElementType = parser->m_declElementType;
+
+  oldUserData = parser->m_userData;
+  oldHandlerArg = parser->m_handlerArg;
+  oldDefaultExpandInternalEntities = parser->m_defaultExpandInternalEntities;
+  oldExternalEntityRefHandlerArg = parser->m_externalEntityRefHandlerArg;
+#ifdef XML_DTD
+  oldParamEntityParsing = parser->m_paramEntityParsing;
+  oldInEntityValue = parser->m_prologState.inEntityValue;
+#endif
+  oldns_triplets = parser->m_ns_triplets;
+  /* Note that the new parser shares the same hash secret as the old
+     parser, so that dtdCopy and copyEntityTable can lookup values
+     from hash tables associated with either parser without us having
+     to worry which hash secrets each table has.
+  */
+  oldhash_secret_salt = parser->m_hash_secret_salt;
+
+#ifdef XML_DTD
+  if (! context)
+    newDtd = oldDtd;
+#endif /* XML_DTD */
+
+  /* Note that the magical uses of the pre-processor to make field
+     access look more like C++ require that `parser' be overwritten
+     here.  This makes this function more painful to follow than it
+     would be otherwise.
+  */
+  if (parser->m_ns) {
+    XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
+    parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
+  } else {
+    parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
+  }
+
+  if (! parser)
+    return NULL;
+
+  parser->m_startElementHandler = oldStartElementHandler;
+  parser->m_endElementHandler = oldEndElementHandler;
+  parser->m_characterDataHandler = oldCharacterDataHandler;
+  parser->m_processingInstructionHandler = oldProcessingInstructionHandler;
+  parser->m_commentHandler = oldCommentHandler;
+  parser->m_startCdataSectionHandler = oldStartCdataSectionHandler;
+  parser->m_endCdataSectionHandler = oldEndCdataSectionHandler;
+  parser->m_defaultHandler = oldDefaultHandler;
+  parser->m_unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler;
+  parser->m_notationDeclHandler = oldNotationDeclHandler;
+  parser->m_startNamespaceDeclHandler = oldStartNamespaceDeclHandler;
+  parser->m_endNamespaceDeclHandler = oldEndNamespaceDeclHandler;
+  parser->m_notStandaloneHandler = oldNotStandaloneHandler;
+  parser->m_externalEntityRefHandler = oldExternalEntityRefHandler;
+  parser->m_skippedEntityHandler = oldSkippedEntityHandler;
+  parser->m_unknownEncodingHandler = oldUnknownEncodingHandler;
+  parser->m_elementDeclHandler = oldElementDeclHandler;
+  parser->m_attlistDeclHandler = oldAttlistDeclHandler;
+  parser->m_entityDeclHandler = oldEntityDeclHandler;
+  parser->m_xmlDeclHandler = oldXmlDeclHandler;
+  parser->m_declElementType = oldDeclElementType;
+  parser->m_userData = oldUserData;
+  if (oldUserData == oldHandlerArg)
+    parser->m_handlerArg = parser->m_userData;
+  else
+    parser->m_handlerArg = parser;
+  if (oldExternalEntityRefHandlerArg != oldParser)
+    parser->m_externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg;
+  parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
+  parser->m_ns_triplets = oldns_triplets;
+  parser->m_hash_secret_salt = oldhash_secret_salt;
+  parser->m_parentParser = oldParser;
+#ifdef XML_DTD
+  parser->m_paramEntityParsing = oldParamEntityParsing;
+  parser->m_prologState.inEntityValue = oldInEntityValue;
+  if (context) {
+#endif /* XML_DTD */
+    if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem)
+        || ! setContext(parser, context)) {
+      XML_ParserFree(parser);
+      return NULL;
+    }
+    parser->m_processor = externalEntityInitProcessor;
+#ifdef XML_DTD
+  } else {
+    /* The DTD instance referenced by parser->m_dtd is shared between the
+       document's root parser and external PE parsers, therefore one does not
+       need to call setContext. In addition, one also *must* not call
+       setContext, because this would overwrite existing prefix->binding
+       pointers in parser->m_dtd with ones that get destroyed with the external
+       PE parser. This would leave those prefixes with dangling pointers.
+    */
+    parser->m_isParamEntity = XML_TRUE;
+    XmlPrologStateInitExternalEntity(&parser->m_prologState);
+    parser->m_processor = externalParEntInitProcessor;
+  }
+#endif /* XML_DTD */
+  return parser;
+}
+
+static void FASTCALL
+destroyBindings(BINDING *bindings, XML_Parser parser) {
+  for (;;) {
+    BINDING *b = bindings;
+    if (! b)
+      break;
+    bindings = b->nextTagBinding;
+    FREE(parser, b->uri);
+    FREE(parser, b);
+  }
+}
+
+void XMLCALL
+XML_ParserFree(XML_Parser parser) {
+  TAG *tagList;
+  OPEN_INTERNAL_ENTITY *entityList;
+  if (parser == NULL)
+    return;
+  /* free m_tagStack and m_freeTagList */
+  tagList = parser->m_tagStack;
+  for (;;) {
+    TAG *p;
+    if (tagList == NULL) {
+      if (parser->m_freeTagList == NULL)
+        break;
+      tagList = parser->m_freeTagList;
+      parser->m_freeTagList = NULL;
+    }
+    p = tagList;
+    tagList = tagList->parent;
+    FREE(parser, p->buf);
+    destroyBindings(p->bindings, parser);
+    FREE(parser, p);
+  }
+  /* free m_openInternalEntities and m_freeInternalEntities */
+  entityList = parser->m_openInternalEntities;
+  for (;;) {
+    OPEN_INTERNAL_ENTITY *openEntity;
+    if (entityList == NULL) {
+      if (parser->m_freeInternalEntities == NULL)
+        break;
+      entityList = parser->m_freeInternalEntities;
+      parser->m_freeInternalEntities = NULL;
+    }
+    openEntity = entityList;
+    entityList = entityList->next;
+    FREE(parser, openEntity);
+  }
+
+  destroyBindings(parser->m_freeBindingList, parser);
+  destroyBindings(parser->m_inheritedBindings, parser);
+  poolDestroy(&parser->m_tempPool);
+  poolDestroy(&parser->m_temp2Pool);
+  FREE(parser, (void *)parser->m_protocolEncodingName);
+#ifdef XML_DTD
+  /* external parameter entity parsers share the DTD structure
+     parser->m_dtd with the root parser, so we must not destroy it
+  */
+  if (! parser->m_isParamEntity && parser->m_dtd)
+#else
+  if (parser->m_dtd)
+#endif /* XML_DTD */
+    dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser,
+               &parser->m_mem);
+  FREE(parser, (void *)parser->m_atts);
+#ifdef XML_ATTR_INFO
+  FREE(parser, (void *)parser->m_attInfo);
+#endif
+  FREE(parser, parser->m_groupConnector);
+  FREE(parser, parser->m_buffer);
+  FREE(parser, parser->m_dataBuf);
+  FREE(parser, parser->m_nsAtts);
+  FREE(parser, parser->m_unknownEncodingMem);
+  if (parser->m_unknownEncodingRelease)
+    parser->m_unknownEncodingRelease(parser->m_unknownEncodingData);
+  FREE(parser, parser);
+}
+
+void XMLCALL
+XML_UseParserAsHandlerArg(XML_Parser parser) {
+  if (parser != NULL)
+    parser->m_handlerArg = parser;
+}
+
+enum XML_Error XMLCALL
+XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) {
+  if (parser == NULL)
+    return XML_ERROR_INVALID_ARGUMENT;
+#ifdef XML_DTD
+  /* block after XML_Parse()/XML_ParseBuffer() has been called */
+  if (parser->m_parsingStatus.parsing == XML_PARSING
+      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+    return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING;
+  parser->m_useForeignDTD = useDTD;
+  return XML_ERROR_NONE;
+#else
+  UNUSED_P(useDTD);
+  return XML_ERROR_FEATURE_REQUIRES_XML_DTD;
+#endif
+}
+
+void XMLCALL
+XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) {
+  if (parser == NULL)
+    return;
+  /* block after XML_Parse()/XML_ParseBuffer() has been called */
+  if (parser->m_parsingStatus.parsing == XML_PARSING
+      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+    return;
+  parser->m_ns_triplets = do_nst ? XML_TRUE : XML_FALSE;
+}
+
+void XMLCALL
+XML_SetUserData(XML_Parser parser, void *p) {
+  if (parser == NULL)
+    return;
+  if (parser->m_handlerArg == parser->m_userData)
+    parser->m_handlerArg = parser->m_userData = p;
+  else
+    parser->m_userData = p;
+}
+
+enum XML_Status XMLCALL
+XML_SetBase(XML_Parser parser, const XML_Char *p) {
+  if (parser == NULL)
+    return XML_STATUS_ERROR;
+  if (p) {
+    p = poolCopyString(&parser->m_dtd->pool, p);
+    if (! p)
+      return XML_STATUS_ERROR;
+    parser->m_curBase = p;
+  } else
+    parser->m_curBase = NULL;
+  return XML_STATUS_OK;
+}
+
+const XML_Char *XMLCALL
+XML_GetBase(XML_Parser parser) {
+  if (parser == NULL)
+    return NULL;
+  return parser->m_curBase;
+}
+
+int XMLCALL
+XML_GetSpecifiedAttributeCount(XML_Parser parser) {
+  if (parser == NULL)
+    return -1;
+  return parser->m_nSpecifiedAtts;
+}
+
+int XMLCALL
+XML_GetIdAttributeIndex(XML_Parser parser) {
+  if (parser == NULL)
+    return -1;
+  return parser->m_idAttIndex;
+}
+
+#ifdef XML_ATTR_INFO
+const XML_AttrInfo *XMLCALL
+XML_GetAttributeInfo(XML_Parser parser) {
+  if (parser == NULL)
+    return NULL;
+  return parser->m_attInfo;
+}
+#endif
+
+void XMLCALL
+XML_SetElementHandler(XML_Parser parser, XML_StartElementHandler start,
+                      XML_EndElementHandler end) {
+  if (parser == NULL)
+    return;
+  parser->m_startElementHandler = start;
+  parser->m_endElementHandler = end;
+}
+
+void XMLCALL
+XML_SetStartElementHandler(XML_Parser parser, XML_StartElementHandler start) {
+  if (parser != NULL)
+    parser->m_startElementHandler = start;
+}
+
+void XMLCALL
+XML_SetEndElementHandler(XML_Parser parser, XML_EndElementHandler end) {
+  if (parser != NULL)
+    parser->m_endElementHandler = end;
+}
+
+void XMLCALL
+XML_SetCharacterDataHandler(XML_Parser parser,
+                            XML_CharacterDataHandler handler) {
+  if (parser != NULL)
+    parser->m_characterDataHandler = handler;
+}
+
+void XMLCALL
+XML_SetProcessingInstructionHandler(XML_Parser parser,
+                                    XML_ProcessingInstructionHandler handler) {
+  if (parser != NULL)
+    parser->m_processingInstructionHandler = handler;
+}
+
+void XMLCALL
+XML_SetCommentHandler(XML_Parser parser, XML_CommentHandler handler) {
+  if (parser != NULL)
+    parser->m_commentHandler = handler;
+}
+
+void XMLCALL
+XML_SetCdataSectionHandler(XML_Parser parser,
+                           XML_StartCdataSectionHandler start,
+                           XML_EndCdataSectionHandler end) {
+  if (parser == NULL)
+    return;
+  parser->m_startCdataSectionHandler = start;
+  parser->m_endCdataSectionHandler = end;
+}
+
+void XMLCALL
+XML_SetStartCdataSectionHandler(XML_Parser parser,
+                                XML_StartCdataSectionHandler start) {
+  if (parser != NULL)
+    parser->m_startCdataSectionHandler = start;
+}
+
+void XMLCALL
+XML_SetEndCdataSectionHandler(XML_Parser parser,
+                              XML_EndCdataSectionHandler end) {
+  if (parser != NULL)
+    parser->m_endCdataSectionHandler = end;
+}
+
+void XMLCALL
+XML_SetDefaultHandler(XML_Parser parser, XML_DefaultHandler handler) {
+  if (parser == NULL)
+    return;
+  parser->m_defaultHandler = handler;
+  parser->m_defaultExpandInternalEntities = XML_FALSE;
+}
+
+void XMLCALL
+XML_SetDefaultHandlerExpand(XML_Parser parser, XML_DefaultHandler handler) {
+  if (parser == NULL)
+    return;
+  parser->m_defaultHandler = handler;
+  parser->m_defaultExpandInternalEntities = XML_TRUE;
+}
+
+void XMLCALL
+XML_SetDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start,
+                          XML_EndDoctypeDeclHandler end) {
+  if (parser == NULL)
+    return;
+  parser->m_startDoctypeDeclHandler = start;
+  parser->m_endDoctypeDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetStartDoctypeDeclHandler(XML_Parser parser,
+                               XML_StartDoctypeDeclHandler start) {
+  if (parser != NULL)
+    parser->m_startDoctypeDeclHandler = start;
+}
+
+void XMLCALL
+XML_SetEndDoctypeDeclHandler(XML_Parser parser, XML_EndDoctypeDeclHandler end) {
+  if (parser != NULL)
+    parser->m_endDoctypeDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+                                 XML_UnparsedEntityDeclHandler handler) {
+  if (parser != NULL)
+    parser->m_unparsedEntityDeclHandler = handler;
+}
+
+void XMLCALL
+XML_SetNotationDeclHandler(XML_Parser parser, XML_NotationDeclHandler handler) {
+  if (parser != NULL)
+    parser->m_notationDeclHandler = handler;
+}
+
+void XMLCALL
+XML_SetNamespaceDeclHandler(XML_Parser parser,
+                            XML_StartNamespaceDeclHandler start,
+                            XML_EndNamespaceDeclHandler end) {
+  if (parser == NULL)
+    return;
+  parser->m_startNamespaceDeclHandler = start;
+  parser->m_endNamespaceDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetStartNamespaceDeclHandler(XML_Parser parser,
+                                 XML_StartNamespaceDeclHandler start) {
+  if (parser != NULL)
+    parser->m_startNamespaceDeclHandler = start;
+}
+
+void XMLCALL
+XML_SetEndNamespaceDeclHandler(XML_Parser parser,
+                               XML_EndNamespaceDeclHandler end) {
+  if (parser != NULL)
+    parser->m_endNamespaceDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetNotStandaloneHandler(XML_Parser parser,
+                            XML_NotStandaloneHandler handler) {
+  if (parser != NULL)
+    parser->m_notStandaloneHandler = handler;
+}
+
+void XMLCALL
+XML_SetExternalEntityRefHandler(XML_Parser parser,
+                                XML_ExternalEntityRefHandler handler) {
+  if (parser != NULL)
+    parser->m_externalEntityRefHandler = handler;
+}
+
+void XMLCALL
+XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) {
+  if (parser == NULL)
+    return;
+  if (arg)
+    parser->m_externalEntityRefHandlerArg = (XML_Parser)arg;
+  else
+    parser->m_externalEntityRefHandlerArg = parser;
+}
+
+void XMLCALL
+XML_SetSkippedEntityHandler(XML_Parser parser,
+                            XML_SkippedEntityHandler handler) {
+  if (parser != NULL)
+    parser->m_skippedEntityHandler = handler;
+}
+
+void XMLCALL
+XML_SetUnknownEncodingHandler(XML_Parser parser,
+                              XML_UnknownEncodingHandler handler, void *data) {
+  if (parser == NULL)
+    return;
+  parser->m_unknownEncodingHandler = handler;
+  parser->m_unknownEncodingHandlerData = data;
+}
+
+void XMLCALL
+XML_SetElementDeclHandler(XML_Parser parser, XML_ElementDeclHandler eldecl) {
+  if (parser != NULL)
+    parser->m_elementDeclHandler = eldecl;
+}
+
+void XMLCALL
+XML_SetAttlistDeclHandler(XML_Parser parser, XML_AttlistDeclHandler attdecl) {
+  if (parser != NULL)
+    parser->m_attlistDeclHandler = attdecl;
+}
+
+void XMLCALL
+XML_SetEntityDeclHandler(XML_Parser parser, XML_EntityDeclHandler handler) {
+  if (parser != NULL)
+    parser->m_entityDeclHandler = handler;
+}
+
+void XMLCALL
+XML_SetXmlDeclHandler(XML_Parser parser, XML_XmlDeclHandler handler) {
+  if (parser != NULL)
+    parser->m_xmlDeclHandler = handler;
+}
+
+int XMLCALL
+XML_SetParamEntityParsing(XML_Parser parser,
+                          enum XML_ParamEntityParsing peParsing) {
+  if (parser == NULL)
+    return 0;
+  /* block after XML_Parse()/XML_ParseBuffer() has been called */
+  if (parser->m_parsingStatus.parsing == XML_PARSING
+      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+    return 0;
+#ifdef XML_DTD
+  parser->m_paramEntityParsing = peParsing;
+  return 1;
+#else
+  return peParsing == XML_PARAM_ENTITY_PARSING_NEVER;
+#endif
+}
+
+int XMLCALL
+XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) {
+  if (parser == NULL)
+    return 0;
+  if (parser->m_parentParser)
+    return XML_SetHashSalt(parser->m_parentParser, hash_salt);
+  /* block after XML_Parse()/XML_ParseBuffer() has been called */
+  if (parser->m_parsingStatus.parsing == XML_PARSING
+      || parser->m_parsingStatus.parsing == XML_SUSPENDED)
+    return 0;
+  parser->m_hash_secret_salt = hash_salt;
+  return 1;
+}
+
+enum XML_Status XMLCALL
+XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
+  if ((parser == NULL) || (len < 0) || ((s == NULL) && (len != 0))) {
+    if (parser != NULL)
+      parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT;
+    return XML_STATUS_ERROR;
+  }
+  switch (parser->m_parsingStatus.parsing) {
+  case XML_SUSPENDED:
+    parser->m_errorCode = XML_ERROR_SUSPENDED;
+    return XML_STATUS_ERROR;
+  case XML_FINISHED:
+    parser->m_errorCode = XML_ERROR_FINISHED;
+    return XML_STATUS_ERROR;
+  case XML_INITIALIZED:
+    if (parser->m_parentParser == NULL && ! startParsing(parser)) {
+      parser->m_errorCode = XML_ERROR_NO_MEMORY;
+      return XML_STATUS_ERROR;
+    }
+    /* fall through */
+  default:
+    parser->m_parsingStatus.parsing = XML_PARSING;
+  }
+
+  if (len == 0) {
+    parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
+    if (! isFinal)
+      return XML_STATUS_OK;
+    parser->m_positionPtr = parser->m_bufferPtr;
+    parser->m_parseEndPtr = parser->m_bufferEnd;
+
+    /* If data are left over from last buffer, and we now know that these
+       data are the final chunk of input, then we have to check them again
+       to detect errors based on that fact.
+    */
+    parser->m_errorCode
+        = parser->m_processor(parser, parser->m_bufferPtr,
+                              parser->m_parseEndPtr, &parser->m_bufferPtr);
+
+    if (parser->m_errorCode == XML_ERROR_NONE) {
+      switch (parser->m_parsingStatus.parsing) {
+      case XML_SUSPENDED:
+        /* It is hard to be certain, but it seems that this case
+         * cannot occur.  This code is cleaning up a previous parse
+         * with no new data (since len == 0).  Changing the parsing
+         * state requires getting to execute a handler function, and
+         * there doesn't seem to be an opportunity for that while in
+         * this circumstance.
+         *
+         * Given the uncertainty, we retain the code but exclude it
+         * from coverage tests.
+         *
+         * LCOV_EXCL_START
+         */
+        XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr,
+                          parser->m_bufferPtr, &parser->m_position);
+        parser->m_positionPtr = parser->m_bufferPtr;
+        return XML_STATUS_SUSPENDED;
+        /* LCOV_EXCL_STOP */
+      case XML_INITIALIZED:
+      case XML_PARSING:
+        parser->m_parsingStatus.parsing = XML_FINISHED;
+        /* fall through */
+      default:
+        return XML_STATUS_OK;
+      }
+    }
+    parser->m_eventEndPtr = parser->m_eventPtr;
+    parser->m_processor = errorProcessor;
+    return XML_STATUS_ERROR;
+  }
+#ifndef XML_CONTEXT_BYTES
+  else if (parser->m_bufferPtr == parser->m_bufferEnd) {
+    const char *end;
+    int nLeftOver;
+    enum XML_Status result;
+    /* Detect overflow (a+b > MAX <==> b > MAX-a) */
+    if ((XML_Size)len > ((XML_Size)-1) / 2 - parser->m_parseEndByteIndex) {
+      parser->m_errorCode = XML_ERROR_NO_MEMORY;
+      parser->m_eventPtr = parser->m_eventEndPtr = NULL;
+      parser->m_processor = errorProcessor;
+      return XML_STATUS_ERROR;
+    }
+    parser->m_parseEndByteIndex += len;
+    parser->m_positionPtr = s;
+    parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
+
+    parser->m_errorCode
+        = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end);
+
+    if (parser->m_errorCode != XML_ERROR_NONE) {
+      parser->m_eventEndPtr = parser->m_eventPtr;
+      parser->m_processor = errorProcessor;
+      return XML_STATUS_ERROR;
+    } else {
+      switch (parser->m_parsingStatus.parsing) {
+      case XML_SUSPENDED:
+        result = XML_STATUS_SUSPENDED;
+        break;
+      case XML_INITIALIZED:
+      case XML_PARSING:
+        if (isFinal) {
+          parser->m_parsingStatus.parsing = XML_FINISHED;
+          return XML_STATUS_OK;
+        }
+      /* fall through */
+      default:
+        result = XML_STATUS_OK;
+      }
+    }
+
+    XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, end,
+                      &parser->m_position);
+    nLeftOver = s + len - end;
+    if (nLeftOver) {
+      if (parser->m_buffer == NULL
+          || nLeftOver > parser->m_bufferLim - parser->m_buffer) {
+        /* avoid _signed_ integer overflow */
+        char *temp = NULL;
+        const int bytesToAllocate = (int)((unsigned)len * 2U);
+        if (bytesToAllocate > 0) {
+          temp = (char *)REALLOC(parser, parser->m_buffer, bytesToAllocate);
+        }
+        if (temp == NULL) {
+          parser->m_errorCode = XML_ERROR_NO_MEMORY;
+          parser->m_eventPtr = parser->m_eventEndPtr = NULL;
+          parser->m_processor = errorProcessor;
+          return XML_STATUS_ERROR;
+        }
+        parser->m_buffer = temp;
+        parser->m_bufferLim = parser->m_buffer + bytesToAllocate;
+      }
+      memcpy(parser->m_buffer, end, nLeftOver);
+    }
+    parser->m_bufferPtr = parser->m_buffer;
+    parser->m_bufferEnd = parser->m_buffer + nLeftOver;
+    parser->m_positionPtr = parser->m_bufferPtr;
+    parser->m_parseEndPtr = parser->m_bufferEnd;
+    parser->m_eventPtr = parser->m_bufferPtr;
+    parser->m_eventEndPtr = parser->m_bufferPtr;
+    return result;
+  }
+#endif /* not defined XML_CONTEXT_BYTES */
+  else {
+    void *buff = XML_GetBuffer(parser, len);
+    if (buff == NULL)
+      return XML_STATUS_ERROR;
+    else {
+      memcpy(buff, s, len);
+      return XML_ParseBuffer(parser, len, isFinal);
+    }
+  }
+}
+
+enum XML_Status XMLCALL
+XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
+  const char *start;
+  enum XML_Status result = XML_STATUS_OK;
+
+  if (parser == NULL)
+    return XML_STATUS_ERROR;
+  switch (parser->m_parsingStatus.parsing) {
+  case XML_SUSPENDED:
+    parser->m_errorCode = XML_ERROR_SUSPENDED;
+    return XML_STATUS_ERROR;
+  case XML_FINISHED:
+    parser->m_errorCode = XML_ERROR_FINISHED;
+    return XML_STATUS_ERROR;
+  case XML_INITIALIZED:
+    /* Has someone called XML_GetBuffer successfully before? */
+    if (! parser->m_bufferPtr) {
+      parser->m_errorCode = XML_ERROR_NO_BUFFER;
+      return XML_STATUS_ERROR;
+    }
+
+    if (parser->m_parentParser == NULL && ! startParsing(parser)) {
+      parser->m_errorCode = XML_ERROR_NO_MEMORY;
+      return XML_STATUS_ERROR;
+    }
+    /* fall through */
+  default:
+    parser->m_parsingStatus.parsing = XML_PARSING;
+  }
+
+  start = parser->m_bufferPtr;
+  parser->m_positionPtr = start;
+  parser->m_bufferEnd += len;
+  parser->m_parseEndPtr = parser->m_bufferEnd;
+  parser->m_parseEndByteIndex += len;
+  parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
+
+  parser->m_errorCode = parser->m_processor(
+      parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr);
+
+  if (parser->m_errorCode != XML_ERROR_NONE) {
+    parser->m_eventEndPtr = parser->m_eventPtr;
+    parser->m_processor = errorProcessor;
+    return XML_STATUS_ERROR;
+  } else {
+    switch (parser->m_parsingStatus.parsing) {
+    case XML_SUSPENDED:
+      result = XML_STATUS_SUSPENDED;
+      break;
+    case XML_INITIALIZED:
+    case XML_PARSING:
+      if (isFinal) {
+        parser->m_parsingStatus.parsing = XML_FINISHED;
+        return result;
+      }
+    default:; /* should not happen */
+    }
+  }
+
+  XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr,
+                    parser->m_bufferPtr, &parser->m_position);
+  parser->m_positionPtr = parser->m_bufferPtr;
+  return result;
+}
+
+void *XMLCALL
+XML_GetBuffer(XML_Parser parser, int len) {
+  if (parser == NULL)
+    return NULL;
+  if (len < 0) {
+    parser->m_errorCode = XML_ERROR_NO_MEMORY;
+    return NULL;
+  }
+  switch (parser->m_parsingStatus.parsing) {
+  case XML_SUSPENDED:
+    parser->m_errorCode = XML_ERROR_SUSPENDED;
+    return NULL;
+  case XML_FINISHED:
+    parser->m_errorCode = XML_ERROR_FINISHED;
+    return NULL;
+  default:;
+  }
+
+  if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) {
+#ifdef XML_CONTEXT_BYTES
+    int keep;
+#endif /* defined XML_CONTEXT_BYTES */
+    /* Do not invoke signed arithmetic overflow: */
+    int neededSize = (int)((unsigned)len
+                           + (unsigned)EXPAT_SAFE_PTR_DIFF(
+                               parser->m_bufferEnd, parser->m_bufferPtr));
+    if (neededSize < 0) {
+      parser->m_errorCode = XML_ERROR_NO_MEMORY;
+      return NULL;
+    }
+#ifdef XML_CONTEXT_BYTES
+    keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
+    if (keep > XML_CONTEXT_BYTES)
+      keep = XML_CONTEXT_BYTES;
+    /* Detect and prevent integer overflow */
+    if (keep > INT_MAX - neededSize) {
+      parser->m_errorCode = XML_ERROR_NO_MEMORY;
+      return NULL;
+    }
+    neededSize += keep;
+#endif /* defined XML_CONTEXT_BYTES */
+    if (neededSize
+        <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) {
+#ifdef XML_CONTEXT_BYTES
+      if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) {
+        int offset
+            = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)
+              - keep;
+        /* The buffer pointers cannot be NULL here; we have at least some bytes
+         * in the buffer */
+        memmove(parser->m_buffer, &parser->m_buffer[offset],
+                parser->m_bufferEnd - parser->m_bufferPtr + keep);
+        parser->m_bufferEnd -= offset;
+        parser->m_bufferPtr -= offset;
+      }
+#else
+      if (parser->m_buffer && parser->m_bufferPtr) {
+        memmove(parser->m_buffer, parser->m_bufferPtr,
+                EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
+        parser->m_bufferEnd
+            = parser->m_buffer
+              + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
+        parser->m_bufferPtr = parser->m_buffer;
+      }
+#endif /* not defined XML_CONTEXT_BYTES */
+    } else {
+      char *newBuf;
+      int bufferSize
+          = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr);
+      if (bufferSize == 0)
+        bufferSize = INIT_BUFFER_SIZE;
+      do {
+        /* Do not invoke signed arithmetic overflow: */
+        bufferSize = (int)(2U * (unsigned)bufferSize);
+      } while (bufferSize < neededSize && bufferSize > 0);
+      if (bufferSize <= 0) {
+        parser->m_errorCode = XML_ERROR_NO_MEMORY;
+        return NULL;
+      }
+      newBuf = (char *)MALLOC(parser, bufferSize);
+      if (newBuf == 0) {
+        parser->m_errorCode = XML_ERROR_NO_MEMORY;
+        return NULL;
+      }
+      parser->m_bufferLim = newBuf + bufferSize;
+#ifdef XML_CONTEXT_BYTES
+      if (parser->m_bufferPtr) {
+        memcpy(newBuf, &parser->m_bufferPtr[-keep],
+               EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
+                   + keep);
+        FREE(parser, parser->m_buffer);
+        parser->m_buffer = newBuf;
+        parser->m_bufferEnd
+            = parser->m_buffer
+              + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
+              + keep;
+        parser->m_bufferPtr = parser->m_buffer + keep;
+      } else {
+        /* This must be a brand new buffer with no data in it yet */
+        parser->m_bufferEnd = newBuf;
+        parser->m_bufferPtr = parser->m_buffer = newBuf;
+      }
+#else
+      if (parser->m_bufferPtr) {
+        memcpy(newBuf, parser->m_bufferPtr,
+               EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
+        FREE(parser, parser->m_buffer);
+        parser->m_bufferEnd
+            = newBuf
+              + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
+      } else {
+        /* This must be a brand new buffer with no data in it yet */
+        parser->m_bufferEnd = newBuf;
+      }
+      parser->m_bufferPtr = parser->m_buffer = newBuf;
+#endif /* not defined XML_CONTEXT_BYTES */
+    }
+    parser->m_eventPtr = parser->m_eventEndPtr = NULL;
+    parser->m_positionPtr = NULL;
+  }
+  return parser->m_bufferEnd;
+}
+
+enum XML_Status XMLCALL
+XML_StopParser(XML_Parser parser, XML_Bool resumable) {
+  if (parser == NULL)
+    return XML_STATUS_ERROR;
+  switch (parser->m_parsingStatus.parsing) {
+  case XML_SUSPENDED:
+    if (resumable) {
+      parser->m_errorCode = XML_ERROR_SUSPENDED;
+      return XML_STATUS_ERROR;
+    }
+    parser->m_parsingStatus.parsing = XML_FINISHED;
+    break;
+  case XML_FINISHED:
+    parser->m_errorCode = XML_ERROR_FINISHED;
+    return XML_STATUS_ERROR;
+  default:
+    if (resumable) {
+#ifdef XML_DTD
+      if (parser->m_isParamEntity) {
+        parser->m_errorCode = XML_ERROR_SUSPEND_PE;
+        return XML_STATUS_ERROR;
+      }
+#endif
+      parser->m_parsingStatus.parsing = XML_SUSPENDED;
+    } else
+      parser->m_parsingStatus.parsing = XML_FINISHED;
+  }
+  return XML_STATUS_OK;
+}
+
+enum XML_Status XMLCALL
+XML_ResumeParser(XML_Parser parser) {
+  enum XML_Status result = XML_STATUS_OK;
+
+  if (parser == NULL)
+    return XML_STATUS_ERROR;
+  if (parser->m_parsingStatus.parsing != XML_SUSPENDED) {
+    parser->m_errorCode = XML_ERROR_NOT_SUSPENDED;
+    return XML_STATUS_ERROR;
+  }
+  parser->m_parsingStatus.parsing = XML_PARSING;
+
+  parser->m_errorCode = parser->m_processor(
+      parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr);
+
+  if (parser->m_errorCode != XML_ERROR_NONE) {
+    parser->m_eventEndPtr = parser->m_eventPtr;
+    parser->m_processor = errorProcessor;
+    return XML_STATUS_ERROR;
+  } else {
+    switch (parser->m_parsingStatus.parsing) {
+    case XML_SUSPENDED:
+      result = XML_STATUS_SUSPENDED;
+      break;
+    case XML_INITIALIZED:
+    case XML_PARSING:
+      if (parser->m_parsingStatus.finalBuffer) {
+        parser->m_parsingStatus.parsing = XML_FINISHED;
+        return result;
+      }
+    default:;
+    }
+  }
+
+  XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr,
+                    parser->m_bufferPtr, &parser->m_position);
+  parser->m_positionPtr = parser->m_bufferPtr;
+  return result;
+}
+
+void XMLCALL
+XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) {
+  if (parser == NULL)
+    return;
+  assert(status != NULL);
+  *status = parser->m_parsingStatus;
+}
+
+enum XML_Error XMLCALL
+XML_GetErrorCode(XML_Parser parser) {
+  if (parser == NULL)
+    return XML_ERROR_INVALID_ARGUMENT;
+  return parser->m_errorCode;
+}
+
+XML_Index XMLCALL
+XML_GetCurrentByteIndex(XML_Parser parser) {
+  if (parser == NULL)
+    return -1;
+  if (parser->m_eventPtr)
+    return (XML_Index)(parser->m_parseEndByteIndex
+                       - (parser->m_parseEndPtr - parser->m_eventPtr));
+  return -1;
+}
+
+int XMLCALL
+XML_GetCurrentByteCount(XML_Parser parser) {
+  if (parser == NULL)
+    return 0;
+  if (parser->m_eventEndPtr && parser->m_eventPtr)
+    return (int)(parser->m_eventEndPtr - parser->m_eventPtr);
+  return 0;
+}
+
+const char *XMLCALL
+XML_GetInputContext(XML_Parser parser, int *offset, int *size) {
+#ifdef XML_CONTEXT_BYTES
+  if (parser == NULL)
+    return NULL;
+  if (parser->m_eventPtr && parser->m_buffer) {
+    if (offset != NULL)
+      *offset = (int)(parser->m_eventPtr - parser->m_buffer);
+    if (size != NULL)
+      *size = (int)(parser->m_bufferEnd - parser->m_buffer);
+    return parser->m_buffer;
+  }
+#else
+  (void)parser;
+  (void)offset;
+  (void)size;
+#endif /* defined XML_CONTEXT_BYTES */
+  return (const char *)0;
+}
+
+XML_Size XMLCALL
+XML_GetCurrentLineNumber(XML_Parser parser) {
+  if (parser == NULL)
+    return 0;
+  if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) {
+    XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr,
+                      parser->m_eventPtr, &parser->m_position);
+    parser->m_positionPtr = parser->m_eventPtr;
+  }
+  return parser->m_position.lineNumber + 1;
+}
+
+XML_Size XMLCALL
+XML_GetCurrentColumnNumber(XML_Parser parser) {
+  if (parser == NULL)
+    return 0;
+  if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) {
+    XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr,
+                      parser->m_eventPtr, &parser->m_position);
+    parser->m_positionPtr = parser->m_eventPtr;
+  }
+  return parser->m_position.columnNumber;
+}
+
+void XMLCALL
+XML_FreeContentModel(XML_Parser parser, XML_Content *model) {
+  if (parser != NULL)
+    FREE(parser, model);
+}
+
+void *XMLCALL
+XML_MemMalloc(XML_Parser parser, size_t size) {
+  if (parser == NULL)
+    return NULL;
+  return MALLOC(parser, size);
+}
+
+void *XMLCALL
+XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) {
+  if (parser == NULL)
+    return NULL;
+  return REALLOC(parser, ptr, size);
+}
+
+void XMLCALL
+XML_MemFree(XML_Parser parser, void *ptr) {
+  if (parser != NULL)
+    FREE(parser, ptr);
+}
+
+void XMLCALL
+XML_DefaultCurrent(XML_Parser parser) {
+  if (parser == NULL)
+    return;
+  if (parser->m_defaultHandler) {
+    if (parser->m_openInternalEntities)
+      reportDefault(parser, parser->m_internalEncoding,
+                    parser->m_openInternalEntities->internalEventPtr,
+                    parser->m_openInternalEntities->internalEventEndPtr);
+    else
+      reportDefault(parser, parser->m_encoding, parser->m_eventPtr,
+                    parser->m_eventEndPtr);
+  }
+}
+
+const XML_LChar *XMLCALL
+XML_ErrorString(enum XML_Error code) {
+  switch (code) {
+  case XML_ERROR_NONE:
+    return NULL;
+  case XML_ERROR_NO_MEMORY:
+    return XML_L("out of memory");
+  case XML_ERROR_SYNTAX:
+    return XML_L("syntax error");
+  case XML_ERROR_NO_ELEMENTS:
+    return XML_L("no element found");
+  case XML_ERROR_INVALID_TOKEN:
+    return XML_L("not well-formed (invalid token)");
+  case XML_ERROR_UNCLOSED_TOKEN:
+    return XML_L("unclosed token");
+  case XML_ERROR_PARTIAL_CHAR:
+    return XML_L("partial character");
+  case XML_ERROR_TAG_MISMATCH:
+    return XML_L("mismatched tag");
+  case XML_ERROR_DUPLICATE_ATTRIBUTE:
+    return XML_L("duplicate attribute");
+  case XML_ERROR_JUNK_AFTER_DOC_ELEMENT:
+    return XML_L("junk after document element");
+  case XML_ERROR_PARAM_ENTITY_REF:
+    return XML_L("illegal parameter entity reference");
+  case XML_ERROR_UNDEFINED_ENTITY:
+    return XML_L("undefined entity");
+  case XML_ERROR_RECURSIVE_ENTITY_REF:
+    return XML_L("recursive entity reference");
+  case XML_ERROR_ASYNC_ENTITY:
+    return XML_L("asynchronous entity");
+  case XML_ERROR_BAD_CHAR_REF:
+    return XML_L("reference to invalid character number");
+  case XML_ERROR_BINARY_ENTITY_REF:
+    return XML_L("reference to binary entity");
+  case XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF:
+    return XML_L("reference to external entity in attribute");
+  case XML_ERROR_MISPLACED_XML_PI:
+    return XML_L("XML or text declaration not at start of entity");
+  case XML_ERROR_UNKNOWN_ENCODING:
+    return XML_L("unknown encoding");
+  case XML_ERROR_INCORRECT_ENCODING:
+    return XML_L("encoding specified in XML declaration is incorrect");
+  case XML_ERROR_UNCLOSED_CDATA_SECTION:
+    return XML_L("unclosed CDATA section");
+  case XML_ERROR_EXTERNAL_ENTITY_HANDLING:
+    return XML_L("error in processing external entity reference");
+  case XML_ERROR_NOT_STANDALONE:
+    return XML_L("document is not standalone");
+  case XML_ERROR_UNEXPECTED_STATE:
+    return XML_L("unexpected parser state - please send a bug report");
+  case XML_ERROR_ENTITY_DECLARED_IN_PE:
+    return XML_L("entity declared in parameter entity");
+  case XML_ERROR_FEATURE_REQUIRES_XML_DTD:
+    return XML_L("requested feature requires XML_DTD support in Expat");
+  case XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING:
+    return XML_L("cannot change setting once parsing has begun");
+  /* Added in 1.95.7. */
+  case XML_ERROR_UNBOUND_PREFIX:
+    return XML_L("unbound prefix");
+  /* Added in 1.95.8. */
+  case XML_ERROR_UNDECLARING_PREFIX:
+    return XML_L("must not undeclare prefix");
+  case XML_ERROR_INCOMPLETE_PE:
+    return XML_L("incomplete markup in parameter entity");
+  case XML_ERROR_XML_DECL:
+    return XML_L("XML declaration not well-formed");
+  case XML_ERROR_TEXT_DECL:
+    return XML_L("text declaration not well-formed");
+  case XML_ERROR_PUBLICID:
+    return XML_L("illegal character(s) in public id");
+  case XML_ERROR_SUSPENDED:
+    return XML_L("parser suspended");
+  case XML_ERROR_NOT_SUSPENDED:
+    return XML_L("parser not suspended");
+  case XML_ERROR_ABORTED:
+    return XML_L("parsing aborted");
+  case XML_ERROR_FINISHED:
+    return XML_L("parsing finished");
+  case XML_ERROR_SUSPEND_PE:
+    return XML_L("cannot suspend in external parameter entity");
+  /* Added in 2.0.0. */
+  case XML_ERROR_RESERVED_PREFIX_XML:
+    return XML_L(
+        "reserved prefix (xml) must not be undeclared or bound to another namespace name");
+  case XML_ERROR_RESERVED_PREFIX_XMLNS:
+    return XML_L("reserved prefix (xmlns) must not be declared or undeclared");
+  case XML_ERROR_RESERVED_NAMESPACE_URI:
+    return XML_L(
+        "prefix must not be bound to one of the reserved namespace names");
+  /* Added in 2.2.5. */
+  case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */
+    return XML_L("invalid argument");
+    /* Added in 2.3.0. */
+  case XML_ERROR_NO_BUFFER:
+    return XML_L(
+        "a successful prior call to function XML_GetBuffer is required");
+  /* Added in 2.4.0. */
+  case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
+    return XML_L(
+        "limit on input amplification factor (from DTD and entities) breached");
+  }
+  return NULL;
+}
+
+const XML_LChar *XMLCALL
+XML_ExpatVersion(void) {
+  /* V1 is used to string-ize the version number. However, it would
+     string-ize the actual version macro *names* unless we get them
+     substituted before being passed to V1. CPP is defined to expand
+     a macro, then rescan for more expansions. Thus, we use V2 to expand
+     the version macros, then CPP will expand the resulting V1() macro
+     with the correct numerals. */
+  /* ### I'm assuming cpp is portable in this respect... */
+
+#define V1(a, b, c) XML_L(#a) XML_L(".") XML_L(#b) XML_L(".") XML_L(#c)
+#define V2(a, b, c) XML_L("expat_") V1(a, b, c)
+
+  return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION);
+
+#undef V1
+#undef V2
+}
+
+XML_Expat_Version XMLCALL
+XML_ExpatVersionInfo(void) {
+  XML_Expat_Version version;
+
+  version.major = XML_MAJOR_VERSION;
+  version.minor = XML_MINOR_VERSION;
+  version.micro = XML_MICRO_VERSION;
+
+  return version;
+}
+
+const XML_Feature *XMLCALL
+XML_GetFeatureList(void) {
+  static const XML_Feature features[] = {
+      {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
+       sizeof(XML_Char)},
+      {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
+       sizeof(XML_LChar)},
+#ifdef XML_UNICODE
+      {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
+#endif
+#ifdef XML_UNICODE_WCHAR_T
+      {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
+#endif
+#ifdef XML_DTD
+      {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
+#endif
+#ifdef XML_CONTEXT_BYTES
+      {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
+       XML_CONTEXT_BYTES},
+#endif
+#ifdef XML_MIN_SIZE
+      {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
+#endif
+#ifdef XML_NS
+      {XML_FEATURE_NS, XML_L("XML_NS"), 0},
+#endif
+#ifdef XML_LARGE_SIZE
+      {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
+#endif
+#ifdef XML_ATTR_INFO
+      {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
+#endif
+#ifdef XML_DTD
+      /* Added in Expat 2.4.0. */
+      {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
+       XML_L("XML_BLAP_MAX_AMP"),
+       (long int)
+           EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT},
+      {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
+       XML_L("XML_BLAP_ACT_THRES"),
+       EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT},
+#endif
+      {XML_FEATURE_END, NULL, 0}};
+
+  return features;
+}
+
+#ifdef XML_DTD
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+    XML_Parser parser, float maximumAmplificationFactor) {
+  if ((parser == NULL) || (parser->m_parentParser != NULL)
+      || isnan(maximumAmplificationFactor)
+      || (maximumAmplificationFactor < 1.0f)) {
+    return XML_FALSE;
+  }
+  parser->m_accounting.maximumAmplificationFactor = maximumAmplificationFactor;
+  return XML_TRUE;
+}
+
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionActivationThreshold(
+    XML_Parser parser, unsigned long long activationThresholdBytes) {
+  if ((parser == NULL) || (parser->m_parentParser != NULL)) {
+    return XML_FALSE;
+  }
+  parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
+  return XML_TRUE;
+}
+#endif /* XML_DTD */
+
+/* Initially tag->rawName always points into the parse buffer;
+   for those TAG instances opened while the current parse buffer was
+   processed, and not yet closed, we need to store tag->rawName in a more
+   permanent location, since the parse buffer is about to be discarded.
+*/
+static XML_Bool
+storeRawNames(XML_Parser parser) {
+  TAG *tag = parser->m_tagStack;
+  while (tag) {
+    int bufSize;
+    int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
+    size_t rawNameLen;
+    char *rawNameBuf = tag->buf + nameLen;
+    /* Stop if already stored.  Since m_tagStack is a stack, we can stop
+       at the first entry that has already been copied; everything
+       below it in the stack is already been accounted for in a
+       previous call to this function.
+    */
+    if (tag->rawName == rawNameBuf)
+      break;
+    /* For re-use purposes we need to ensure that the
+       size of tag->buf is a multiple of sizeof(XML_Char).
+    */
+    rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+    /* Detect and prevent integer overflow. */
+    if (rawNameLen > (size_t)INT_MAX - nameLen)
+      return XML_FALSE;
+    bufSize = nameLen + (int)rawNameLen;
+    if (bufSize > tag->bufEnd - tag->buf) {
+      char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
+      if (temp == NULL)
+        return XML_FALSE;
+      /* if tag->name.str points to tag->buf (only when namespace
+         processing is off) then we have to update it
+      */
+      if (tag->name.str == (XML_Char *)tag->buf)
+        tag->name.str = (XML_Char *)temp;
+      /* if tag->name.localPart is set (when namespace processing is on)
+         then update it as well, since it will always point into tag->buf
+      */
+      if (tag->name.localPart)
+        tag->name.localPart
+            = (XML_Char *)temp + (tag->name.localPart - (XML_Char *)tag->buf);
+      tag->buf = temp;
+      tag->bufEnd = temp + bufSize;
+      rawNameBuf = temp + nameLen;
+    }
+    memcpy(rawNameBuf, tag->rawName, tag->rawNameLength);
+    tag->rawName = rawNameBuf;
+    tag = tag->parent;
+  }
+  return XML_TRUE;
+}
+
+static enum XML_Error PTRCALL
+contentProcessor(XML_Parser parser, const char *start, const char *end,
+                 const char **endPtr) {
+  enum XML_Error result = doContent(
+      parser, 0, parser->m_encoding, start, end, endPtr,
+      (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
+  if (result == XML_ERROR_NONE) {
+    if (! storeRawNames(parser))
+      return XML_ERROR_NO_MEMORY;
+  }
+  return result;
+}
+
+static enum XML_Error PTRCALL
+externalEntityInitProcessor(XML_Parser parser, const char *start,
+                            const char *end, const char **endPtr) {
+  enum XML_Error result = initializeEncoding(parser);
+  if (result != XML_ERROR_NONE)
+    return result;
+  parser->m_processor = externalEntityInitProcessor2;
+  return externalEntityInitProcessor2(parser, start, end, endPtr);
+}
+
+static enum XML_Error PTRCALL
+externalEntityInitProcessor2(XML_Parser parser, const char *start,
+                             const char *end, const char **endPtr) {
+  const char *next = start; /* XmlContentTok doesn't always set the last arg */
+  int tok = XmlContentTok(parser->m_encoding, start, end, &next);
+  switch (tok) {
+  case XML_TOK_BOM:
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
+                                  XML_ACCOUNT_DIRECT)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif /* XML_DTD */
+
+    /* If we are at the end of the buffer, this would cause the next stage,
+       i.e. externalEntityInitProcessor3, to pass control directly to
+       doContent (by detecting XML_TOK_NONE) without processing any xml text
+       declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent.
+    */
+    if (next == end && ! parser->m_parsingStatus.finalBuffer) {
+      *endPtr = next;
+      return XML_ERROR_NONE;
+    }
+    start = next;
+    break;
+  case XML_TOK_PARTIAL:
+    if (! parser->m_parsingStatus.finalBuffer) {
+      *endPtr = start;
+      return XML_ERROR_NONE;
+    }
+    parser->m_eventPtr = start;
+    return XML_ERROR_UNCLOSED_TOKEN;
+  case XML_TOK_PARTIAL_CHAR:
+    if (! parser->m_parsingStatus.finalBuffer) {
+      *endPtr = start;
+      return XML_ERROR_NONE;
+    }
+    parser->m_eventPtr = start;
+    return XML_ERROR_PARTIAL_CHAR;
+  }
+  parser->m_processor = externalEntityInitProcessor3;
+  return externalEntityInitProcessor3(parser, start, end, endPtr);
+}
+
+static enum XML_Error PTRCALL
+externalEntityInitProcessor3(XML_Parser parser, const char *start,
+                             const char *end, const char **endPtr) {
+  int tok;
+  const char *next = start; /* XmlContentTok doesn't always set the last arg */
+  parser->m_eventPtr = start;
+  tok = XmlContentTok(parser->m_encoding, start, end, &next);
+  /* Note: These bytes are accounted later in:
+           - processXmlDecl
+           - externalEntityContentProcessor
+  */
+  parser->m_eventEndPtr = next;
+
+  switch (tok) {
+  case XML_TOK_XML_DECL: {
+    enum XML_Error result;
+    result = processXmlDecl(parser, 1, start, next);
+    if (result != XML_ERROR_NONE)
+      return result;
+    switch (parser->m_parsingStatus.parsing) {
+    case XML_SUSPENDED:
+      *endPtr = next;
+      return XML_ERROR_NONE;
+    case XML_FINISHED:
+      return XML_ERROR_ABORTED;
+    default:
+      start = next;
+    }
+  } break;
+  case XML_TOK_PARTIAL:
+    if (! parser->m_parsingStatus.finalBuffer) {
+      *endPtr = start;
+      return XML_ERROR_NONE;
+    }
+    return XML_ERROR_UNCLOSED_TOKEN;
+  case XML_TOK_PARTIAL_CHAR:
+    if (! parser->m_parsingStatus.finalBuffer) {
+      *endPtr = start;
+      return XML_ERROR_NONE;
+    }
+    return XML_ERROR_PARTIAL_CHAR;
+  }
+  parser->m_processor = externalEntityContentProcessor;
+  parser->m_tagLevel = 1;
+  return externalEntityContentProcessor(parser, start, end, endPtr);
+}
+
+static enum XML_Error PTRCALL
+externalEntityContentProcessor(XML_Parser parser, const char *start,
+                               const char *end, const char **endPtr) {
+  enum XML_Error result
+      = doContent(parser, 1, parser->m_encoding, start, end, endPtr,
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer,
+                  XML_ACCOUNT_ENTITY_EXPANSION);
+  if (result == XML_ERROR_NONE) {
+    if (! storeRawNames(parser))
+      return XML_ERROR_NO_MEMORY;
+  }
+  return result;
+}
+
+static enum XML_Error
+doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
+          const char *s, const char *end, const char **nextPtr,
+          XML_Bool haveMore, enum XML_Account account) {
+  /* save one level of indirection */
+  DTD *const dtd = parser->m_dtd;
+
+  const char **eventPP;
+  const char **eventEndPP;
+  if (enc == parser->m_encoding) {
+    eventPP = &parser->m_eventPtr;
+    eventEndPP = &parser->m_eventEndPtr;
+  } else {
+    eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+    eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
+  }
+  *eventPP = s;
+
+  for (;;) {
+    const char *next = s; /* XmlContentTok doesn't always set the last arg */
+    int tok = XmlContentTok(enc, s, end, &next);
+#ifdef XML_DTD
+    const char *accountAfter
+        = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR))
+              ? (haveMore ? s /* i.e. 0 bytes */ : end)
+              : next;
+    if (! accountingDiffTolerated(parser, tok, s, accountAfter, __LINE__,
+                                  account)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif
+    *eventEndPP = next;
+    switch (tok) {
+    case XML_TOK_TRAILING_CR:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      *eventEndPP = end;
+      if (parser->m_characterDataHandler) {
+        XML_Char c = 0xA;
+        parser->m_characterDataHandler(parser->m_handlerArg, &c, 1);
+      } else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, end);
+      /* We are at the end of the final buffer, should we check for
+         XML_SUSPENDED, XML_FINISHED?
+      */
+      if (startTagLevel == 0)
+        return XML_ERROR_NO_ELEMENTS;
+      if (parser->m_tagLevel != startTagLevel)
+        return XML_ERROR_ASYNC_ENTITY;
+      *nextPtr = end;
+      return XML_ERROR_NONE;
+    case XML_TOK_NONE:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      if (startTagLevel > 0) {
+        if (parser->m_tagLevel != startTagLevel)
+          return XML_ERROR_ASYNC_ENTITY;
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_NO_ELEMENTS;
+    case XML_TOK_INVALID:
+      *eventPP = next;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_UNCLOSED_TOKEN;
+    case XML_TOK_PARTIAL_CHAR:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_PARTIAL_CHAR;
+    case XML_TOK_ENTITY_REF: {
+      const XML_Char *name;
+      ENTITY *entity;
+      XML_Char ch = (XML_Char)XmlPredefinedEntityName(
+          enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
+      if (ch) {
+#ifdef XML_DTD
+        /* NOTE: We are replacing 4-6 characters original input for 1 character
+         *       so there is no amplification and hence recording without
+         *       protection. */
+        accountingDiffTolerated(parser, tok, (char *)&ch,
+                                ((char *)&ch) + sizeof(XML_Char), __LINE__,
+                                XML_ACCOUNT_ENTITY_EXPANSION);
+#endif /* XML_DTD */
+        if (parser->m_characterDataHandler)
+          parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
+        else if (parser->m_defaultHandler)
+          reportDefault(parser, enc, s, next);
+        break;
+      }
+      name = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar,
+                             next - enc->minBytesPerChar);
+      if (! name)
+        return XML_ERROR_NO_MEMORY;
+      entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0);
+      poolDiscard(&dtd->pool);
+      /* First, determine if a check for an existing declaration is needed;
+         if yes, check that the entity exists, and that it is internal,
+         otherwise call the skipped entity or default handler.
+      */
+      if (! dtd->hasParamEntityRefs || dtd->standalone) {
+        if (! entity)
+          return XML_ERROR_UNDEFINED_ENTITY;
+        else if (! entity->is_internal)
+          return XML_ERROR_ENTITY_DECLARED_IN_PE;
+      } else if (! entity) {
+        if (parser->m_skippedEntityHandler)
+          parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0);
+        else if (parser->m_defaultHandler)
+          reportDefault(parser, enc, s, next);
+        break;
+      }
+      if (entity->open)
+        return XML_ERROR_RECURSIVE_ENTITY_REF;
+      if (entity->notation)
+        return XML_ERROR_BINARY_ENTITY_REF;
+      if (entity->textPtr) {
+        enum XML_Error result;
+        if (! parser->m_defaultExpandInternalEntities) {
+          if (parser->m_skippedEntityHandler)
+            parser->m_skippedEntityHandler(parser->m_handlerArg, entity->name,
+                                           0);
+          else if (parser->m_defaultHandler)
+            reportDefault(parser, enc, s, next);
+          break;
+        }
+        result = processInternalEntity(parser, entity, XML_FALSE);
+        if (result != XML_ERROR_NONE)
+          return result;
+      } else if (parser->m_externalEntityRefHandler) {
+        const XML_Char *context;
+        entity->open = XML_TRUE;
+        context = getContext(parser);
+        entity->open = XML_FALSE;
+        if (! context)
+          return XML_ERROR_NO_MEMORY;
+        if (! parser->m_externalEntityRefHandler(
+                parser->m_externalEntityRefHandlerArg, context, entity->base,
+                entity->systemId, entity->publicId))
+          return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+        poolDiscard(&parser->m_tempPool);
+      } else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+      break;
+    }
+    case XML_TOK_START_TAG_NO_ATTS:
+      /* fall through */
+    case XML_TOK_START_TAG_WITH_ATTS: {
+      TAG *tag;
+      enum XML_Error result;
+      XML_Char *toPtr;
+      if (parser->m_freeTagList) {
+        tag = parser->m_freeTagList;
+        parser->m_freeTagList = parser->m_freeTagList->parent;
+      } else {
+        tag = (TAG *)MALLOC(parser, sizeof(TAG));
+        if (! tag)
+          return XML_ERROR_NO_MEMORY;
+        tag->buf = (char *)MALLOC(parser, INIT_TAG_BUF_SIZE);
+        if (! tag->buf) {
+          FREE(parser, tag);
+          return XML_ERROR_NO_MEMORY;
+        }
+        tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE;
+      }
+      tag->bindings = NULL;
+      tag->parent = parser->m_tagStack;
+      parser->m_tagStack = tag;
+      tag->name.localPart = NULL;
+      tag->name.prefix = NULL;
+      tag->rawName = s + enc->minBytesPerChar;
+      tag->rawNameLength = XmlNameLength(enc, tag->rawName);
+      ++parser->m_tagLevel;
+      {
+        const char *rawNameEnd = tag->rawName + tag->rawNameLength;
+        const char *fromPtr = tag->rawName;
+        toPtr = (XML_Char *)tag->buf;
+        for (;;) {
+          int bufSize;
+          int convLen;
+          const enum XML_Convert_Result convert_res
+              = XmlConvert(enc, &fromPtr, rawNameEnd, (ICHAR **)&toPtr,
+                           (ICHAR *)tag->bufEnd - 1);
+          convLen = (int)(toPtr - (XML_Char *)tag->buf);
+          if ((fromPtr >= rawNameEnd)
+              || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) {
+            tag->name.strLen = convLen;
+            break;
+          }
+          bufSize = (int)(tag->bufEnd - tag->buf) << 1;
+          {
+            char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
+            if (temp == NULL)
+              return XML_ERROR_NO_MEMORY;
+            tag->buf = temp;
+            tag->bufEnd = temp + bufSize;
+            toPtr = (XML_Char *)temp + convLen;
+          }
+        }
+      }
+      tag->name.str = (XML_Char *)tag->buf;
+      *toPtr = XML_T('\0');
+      result
+          = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account);
+      if (result)
+        return result;
+      if (parser->m_startElementHandler)
+        parser->m_startElementHandler(parser->m_handlerArg, tag->name.str,
+                                      (const XML_Char **)parser->m_atts);
+      else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+      poolClear(&parser->m_tempPool);
+      break;
+    }
+    case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
+      /* fall through */
+    case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: {
+      const char *rawName = s + enc->minBytesPerChar;
+      enum XML_Error result;
+      BINDING *bindings = NULL;
+      XML_Bool noElmHandlers = XML_TRUE;
+      TAG_NAME name;
+      name.str = poolStoreString(&parser->m_tempPool, enc, rawName,
+                                 rawName + XmlNameLength(enc, rawName));
+      if (! name.str)
+        return XML_ERROR_NO_MEMORY;
+      poolFinish(&parser->m_tempPool);
+      result = storeAtts(parser, enc, s, &name, &bindings,
+                         XML_ACCOUNT_NONE /* token spans whole start tag */);
+      if (result != XML_ERROR_NONE) {
+        freeBindings(parser, bindings);
+        return result;
+      }
+      poolFinish(&parser->m_tempPool);
+      if (parser->m_startElementHandler) {
+        parser->m_startElementHandler(parser->m_handlerArg, name.str,
+                                      (const XML_Char **)parser->m_atts);
+        noElmHandlers = XML_FALSE;
+      }
+      if (parser->m_endElementHandler) {
+        if (parser->m_startElementHandler)
+          *eventPP = *eventEndPP;
+        parser->m_endElementHandler(parser->m_handlerArg, name.str);
+        noElmHandlers = XML_FALSE;
+      }
+      if (noElmHandlers && parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+      poolClear(&parser->m_tempPool);
+      freeBindings(parser, bindings);
+    }
+      if ((parser->m_tagLevel == 0)
+          && (parser->m_parsingStatus.parsing != XML_FINISHED)) {
+        if (parser->m_parsingStatus.parsing == XML_SUSPENDED)
+          parser->m_processor = epilogProcessor;
+        else
+          return epilogProcessor(parser, next, end, nextPtr);
+      }
+      break;
+    case XML_TOK_END_TAG:
+      if (parser->m_tagLevel == startTagLevel)
+        return XML_ERROR_ASYNC_ENTITY;
+      else {
+        int len;
+        const char *rawName;
+        TAG *tag = parser->m_tagStack;
+        rawName = s + enc->minBytesPerChar * 2;
+        len = XmlNameLength(enc, rawName);
+        if (len != tag->rawNameLength
+            || memcmp(tag->rawName, rawName, len) != 0) {
+          *eventPP = rawName;
+          return XML_ERROR_TAG_MISMATCH;
+        }
+        parser->m_tagStack = tag->parent;
+        tag->parent = parser->m_freeTagList;
+        parser->m_freeTagList = tag;
+        --parser->m_tagLevel;
+        if (parser->m_endElementHandler) {
+          const XML_Char *localPart;
+          const XML_Char *prefix;
+          XML_Char *uri;
+          localPart = tag->name.localPart;
+          if (parser->m_ns && localPart) {
+            /* localPart and prefix may have been overwritten in
+               tag->name.str, since this points to the binding->uri
+               buffer which gets re-used; so we have to add them again
+            */
+            uri = (XML_Char *)tag->name.str + tag->name.uriLen;
+            /* don't need to check for space - already done in storeAtts() */
+            while (*localPart)
+              *uri++ = *localPart++;
+            prefix = (XML_Char *)tag->name.prefix;
+            if (parser->m_ns_triplets && prefix) {
+              *uri++ = parser->m_namespaceSeparator;
+              while (*prefix)
+                *uri++ = *prefix++;
+            }
+            *uri = XML_T('\0');
+          }
+          parser->m_endElementHandler(parser->m_handlerArg, tag->name.str);
+        } else if (parser->m_defaultHandler)
+          reportDefault(parser, enc, s, next);
+        while (tag->bindings) {
+          BINDING *b = tag->bindings;
+          if (parser->m_endNamespaceDeclHandler)
+            parser->m_endNamespaceDeclHandler(parser->m_handlerArg,
+                                              b->prefix->name);
+          tag->bindings = tag->bindings->nextTagBinding;
+          b->nextTagBinding = parser->m_freeBindingList;
+          parser->m_freeBindingList = b;
+          b->prefix->binding = b->prevPrefixBinding;
+        }
+        if ((parser->m_tagLevel == 0)
+            && (parser->m_parsingStatus.parsing != XML_FINISHED)) {
+          if (parser->m_parsingStatus.parsing == XML_SUSPENDED)
+            parser->m_processor = epilogProcessor;
+          else
+            return epilogProcessor(parser, next, end, nextPtr);
+        }
+      }
+      break;
+    case XML_TOK_CHAR_REF: {
+      int n = XmlCharRefNumber(enc, s);
+      if (n < 0)
+        return XML_ERROR_BAD_CHAR_REF;
+      if (parser->m_characterDataHandler) {
+        XML_Char buf[XML_ENCODE_MAX];
+        parser->m_characterDataHandler(parser->m_handlerArg, buf,
+                                       XmlEncode(n, (ICHAR *)buf));
+      } else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+    } break;
+    case XML_TOK_XML_DECL:
+      return XML_ERROR_MISPLACED_XML_PI;
+    case XML_TOK_DATA_NEWLINE:
+      if (parser->m_characterDataHandler) {
+        XML_Char c = 0xA;
+        parser->m_characterDataHandler(parser->m_handlerArg, &c, 1);
+      } else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+      break;
+    case XML_TOK_CDATA_SECT_OPEN: {
+      enum XML_Error result;
+      if (parser->m_startCdataSectionHandler)
+        parser->m_startCdataSectionHandler(parser->m_handlerArg);
+      /* BEGIN disabled code */
+      /* Suppose you doing a transformation on a document that involves
+         changing only the character data.  You set up a defaultHandler
+         and a characterDataHandler.  The defaultHandler simply copies
+         characters through.  The characterDataHandler does the
+         transformation and writes the characters out escaping them as
+         necessary.  This case will fail to work if we leave out the
+         following two lines (because & and < inside CDATA sections will
+         be incorrectly escaped).
+
+         However, now we have a start/endCdataSectionHandler, so it seems
+         easier to let the user deal with this.
+      */
+      else if (0 && parser->m_characterDataHandler)
+        parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf,
+                                       0);
+      /* END disabled code */
+      else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+      result
+          = doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account);
+      if (result != XML_ERROR_NONE)
+        return result;
+      else if (! next) {
+        parser->m_processor = cdataSectionProcessor;
+        return result;
+      }
+    } break;
+    case XML_TOK_TRAILING_RSQB:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      if (parser->m_characterDataHandler) {
+        if (MUST_CONVERT(enc, s)) {
+          ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf;
+          XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
+          parser->m_characterDataHandler(
+              parser->m_handlerArg, parser->m_dataBuf,
+              (int)(dataPtr - (ICHAR *)parser->m_dataBuf));
+        } else
+          parser->m_characterDataHandler(
+              parser->m_handlerArg, (XML_Char *)s,
+              (int)((XML_Char *)end - (XML_Char *)s));
+      } else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, end);
+      /* We are at the end of the final buffer, should we check for
+         XML_SUSPENDED, XML_FINISHED?
+      */
+      if (startTagLevel == 0) {
+        *eventPP = end;
+        return XML_ERROR_NO_ELEMENTS;
+      }
+      if (parser->m_tagLevel != startTagLevel) {
+        *eventPP = end;
+        return XML_ERROR_ASYNC_ENTITY;
+      }
+      *nextPtr = end;
+      return XML_ERROR_NONE;
+    case XML_TOK_DATA_CHARS: {
+      XML_CharacterDataHandler charDataHandler = parser->m_characterDataHandler;
+      if (charDataHandler) {
+        if (MUST_CONVERT(enc, s)) {
+          for (;;) {
+            ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf;
+            const enum XML_Convert_Result convert_res = XmlConvert(
+                enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
+            *eventEndPP = s;
+            charDataHandler(parser->m_handlerArg, parser->m_dataBuf,
+                            (int)(dataPtr - (ICHAR *)parser->m_dataBuf));
+            if ((convert_res == XML_CONVERT_COMPLETED)
+                || (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
+              break;
+            *eventPP = s;
+          }
+        } else
+          charDataHandler(parser->m_handlerArg, (XML_Char *)s,
+                          (int)((XML_Char *)next - (XML_Char *)s));
+      } else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+    } break;
+    case XML_TOK_PI:
+      if (! reportProcessingInstruction(parser, enc, s, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_COMMENT:
+      if (! reportComment(parser, enc, s, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    default:
+      /* All of the tokens produced by XmlContentTok() have their own
+       * explicit cases, so this default is not strictly necessary.
+       * However it is a useful safety net, so we retain the code and
+       * simply exclude it from the coverage tests.
+       *
+       * LCOV_EXCL_START
+       */
+      if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+      break;
+      /* LCOV_EXCL_STOP */
+    }
+    *eventPP = s = next;
+    switch (parser->m_parsingStatus.parsing) {
+    case XML_SUSPENDED:
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_FINISHED:
+      return XML_ERROR_ABORTED;
+    default:;
+    }
+  }
+  /* not reached */
+}
+
+/* This function does not call free() on the allocated memory, merely
+ * moving it to the parser's m_freeBindingList where it can be freed or
+ * reused as appropriate.
+ */
+static void
+freeBindings(XML_Parser parser, BINDING *bindings) {
+  while (bindings) {
+    BINDING *b = bindings;
+
+    /* m_startNamespaceDeclHandler will have been called for this
+     * binding in addBindings(), so call the end handler now.
+     */
+    if (parser->m_endNamespaceDeclHandler)
+      parser->m_endNamespaceDeclHandler(parser->m_handlerArg, b->prefix->name);
+
+    bindings = bindings->nextTagBinding;
+    b->nextTagBinding = parser->m_freeBindingList;
+    parser->m_freeBindingList = b;
+    b->prefix->binding = b->prevPrefixBinding;
+  }
+}
+
+/* Precondition: all arguments must be non-NULL;
+   Purpose:
+   - normalize attributes
+   - check attributes for well-formedness
+   - generate namespace aware attribute names (URI, prefix)
+   - build list of attributes for startElementHandler
+   - default attributes
+   - process namespace declarations (check and report them)
+   - generate namespace aware element name (URI, prefix)
+*/
+static enum XML_Error
+storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
+          TAG_NAME *tagNamePtr, BINDING **bindingsPtr,
+          enum XML_Account account) {
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+  ELEMENT_TYPE *elementType;
+  int nDefaultAtts;
+  const XML_Char **appAtts; /* the attribute list for the application */
+  int attIndex = 0;
+  int prefixLen;
+  int i;
+  int n;
+  XML_Char *uri;
+  int nPrefixes = 0;
+  BINDING *binding;
+  const XML_Char *localPart;
+
+  /* lookup the element type name */
+  elementType
+      = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str, 0);
+  if (! elementType) {
+    const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str);
+    if (! name)
+      return XML_ERROR_NO_MEMORY;
+    elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name,
+                                         sizeof(ELEMENT_TYPE));
+    if (! elementType)
+      return XML_ERROR_NO_MEMORY;
+    if (parser->m_ns && ! setElementTypePrefix(parser, elementType))
+      return XML_ERROR_NO_MEMORY;
+  }
+  nDefaultAtts = elementType->nDefaultAtts;
+
+  /* get the attributes from the tokenizer */
+  n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts);
+
+  /* Detect and prevent integer overflow */
+  if (n > INT_MAX - nDefaultAtts) {
+    return XML_ERROR_NO_MEMORY;
+  }
+
+  if (n + nDefaultAtts > parser->m_attsSize) {
+    int oldAttsSize = parser->m_attsSize;
+    ATTRIBUTE *temp;
+#ifdef XML_ATTR_INFO
+    XML_AttrInfo *temp2;
+#endif
+
+    /* Detect and prevent integer overflow */
+    if ((nDefaultAtts > INT_MAX - INIT_ATTS_SIZE)
+        || (n > INT_MAX - (nDefaultAtts + INIT_ATTS_SIZE))) {
+      return XML_ERROR_NO_MEMORY;
+    }
+
+    parser->m_attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
+
+    /* Detect and prevent integer overflow.
+     * The preprocessor guard addresses the "always false" warning
+     * from -Wtype-limits on platforms where
+     * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+    if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(ATTRIBUTE)) {
+      parser->m_attsSize = oldAttsSize;
+      return XML_ERROR_NO_MEMORY;
+    }
+#endif
+
+    temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts,
+                                parser->m_attsSize * sizeof(ATTRIBUTE));
+    if (temp == NULL) {
+      parser->m_attsSize = oldAttsSize;
+      return XML_ERROR_NO_MEMORY;
+    }
+    parser->m_atts = temp;
+#ifdef XML_ATTR_INFO
+    /* Detect and prevent integer overflow.
+     * The preprocessor guard addresses the "always false" warning
+     * from -Wtype-limits on platforms where
+     * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#  if UINT_MAX >= SIZE_MAX
+    if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(XML_AttrInfo)) {
+      parser->m_attsSize = oldAttsSize;
+      return XML_ERROR_NO_MEMORY;
+    }
+#  endif
+
+    temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo,
+                                    parser->m_attsSize * sizeof(XML_AttrInfo));
+    if (temp2 == NULL) {
+      parser->m_attsSize = oldAttsSize;
+      return XML_ERROR_NO_MEMORY;
+    }
+    parser->m_attInfo = temp2;
+#endif
+    if (n > oldAttsSize)
+      XmlGetAttributes(enc, attStr, n, parser->m_atts);
+  }
+
+  appAtts = (const XML_Char **)parser->m_atts;
+  for (i = 0; i < n; i++) {
+    ATTRIBUTE *currAtt = &parser->m_atts[i];
+#ifdef XML_ATTR_INFO
+    XML_AttrInfo *currAttInfo = &parser->m_attInfo[i];
+#endif
+    /* add the name and value to the attribute list */
+    ATTRIBUTE_ID *attId
+        = getAttributeId(parser, enc, currAtt->name,
+                         currAtt->name + XmlNameLength(enc, currAtt->name));
+    if (! attId)
+      return XML_ERROR_NO_MEMORY;
+#ifdef XML_ATTR_INFO
+    currAttInfo->nameStart
+        = parser->m_parseEndByteIndex - (parser->m_parseEndPtr - currAtt->name);
+    currAttInfo->nameEnd
+        = currAttInfo->nameStart + XmlNameLength(enc, currAtt->name);
+    currAttInfo->valueStart = parser->m_parseEndByteIndex
+                              - (parser->m_parseEndPtr - currAtt->valuePtr);
+    currAttInfo->valueEnd = parser->m_parseEndByteIndex
+                            - (parser->m_parseEndPtr - currAtt->valueEnd);
+#endif
+    /* Detect duplicate attributes by their QNames. This does not work when
+       namespace processing is turned on and different prefixes for the same
+       namespace are used. For this case we have a check further down.
+    */
+    if ((attId->name)[-1]) {
+      if (enc == parser->m_encoding)
+        parser->m_eventPtr = parser->m_atts[i].name;
+      return XML_ERROR_DUPLICATE_ATTRIBUTE;
+    }
+    (attId->name)[-1] = 1;
+    appAtts[attIndex++] = attId->name;
+    if (! parser->m_atts[i].normalized) {
+      enum XML_Error result;
+      XML_Bool isCdata = XML_TRUE;
+
+      /* figure out whether declared as other than CDATA */
+      if (attId->maybeTokenized) {
+        int j;
+        for (j = 0; j < nDefaultAtts; j++) {
+          if (attId == elementType->defaultAtts[j].id) {
+            isCdata = elementType->defaultAtts[j].isCdata;
+            break;
+          }
+        }
+      }
+
+      /* normalize the attribute value */
+      result = storeAttributeValue(
+          parser, enc, isCdata, parser->m_atts[i].valuePtr,
+          parser->m_atts[i].valueEnd, &parser->m_tempPool, account);
+      if (result)
+        return result;
+      appAtts[attIndex] = poolStart(&parser->m_tempPool);
+      poolFinish(&parser->m_tempPool);
+    } else {
+      /* the value did not need normalizing */
+      appAtts[attIndex] = poolStoreString(&parser->m_tempPool, enc,
+                                          parser->m_atts[i].valuePtr,
+                                          parser->m_atts[i].valueEnd);
+      if (appAtts[attIndex] == 0)
+        return XML_ERROR_NO_MEMORY;
+      poolFinish(&parser->m_tempPool);
+    }
+    /* handle prefixed attribute names */
+    if (attId->prefix) {
+      if (attId->xmlns) {
+        /* deal with namespace declarations here */
+        enum XML_Error result = addBinding(parser, attId->prefix, attId,
+                                           appAtts[attIndex], bindingsPtr);
+        if (result)
+          return result;
+        --attIndex;
+      } else {
+        /* deal with other prefixed names later */
+        attIndex++;
+        nPrefixes++;
+        (attId->name)[-1] = 2;
+      }
+    } else
+      attIndex++;
+  }
+
+  /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */
+  parser->m_nSpecifiedAtts = attIndex;
+  if (elementType->idAtt && (elementType->idAtt->name)[-1]) {
+    for (i = 0; i < attIndex; i += 2)
+      if (appAtts[i] == elementType->idAtt->name) {
+        parser->m_idAttIndex = i;
+        break;
+      }
+  } else
+    parser->m_idAttIndex = -1;
+
+  /* do attribute defaulting */
+  for (i = 0; i < nDefaultAtts; i++) {
+    const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i;
+    if (! (da->id->name)[-1] && da->value) {
+      if (da->id->prefix) {
+        if (da->id->xmlns) {
+          enum XML_Error result = addBinding(parser, da->id->prefix, da->id,
+                                             da->value, bindingsPtr);
+          if (result)
+            return result;
+        } else {
+          (da->id->name)[-1] = 2;
+          nPrefixes++;
+          appAtts[attIndex++] = da->id->name;
+          appAtts[attIndex++] = da->value;
+        }
+      } else {
+        (da->id->name)[-1] = 1;
+        appAtts[attIndex++] = da->id->name;
+        appAtts[attIndex++] = da->value;
+      }
+    }
+  }
+  appAtts[attIndex] = 0;
+
+  /* expand prefixed attribute names, check for duplicates,
+     and clear flags that say whether attributes were specified */
+  i = 0;
+  if (nPrefixes) {
+    int j; /* hash table index */
+    unsigned long version = parser->m_nsAttsVersion;
+
+    /* Detect and prevent invalid shift */
+    if (parser->m_nsAttsPower >= sizeof(unsigned int) * 8 /* bits per byte */) {
+      return XML_ERROR_NO_MEMORY;
+    }
+
+    unsigned int nsAttsSize = 1u << parser->m_nsAttsPower;
+    unsigned char oldNsAttsPower = parser->m_nsAttsPower;
+    /* size of hash table must be at least 2 * (# of prefixed attributes) */
+    if ((nPrefixes << 1)
+        >> parser->m_nsAttsPower) { /* true for m_nsAttsPower = 0 */
+      NS_ATT *temp;
+      /* hash table size must also be a power of 2 and >= 8 */
+      while (nPrefixes >> parser->m_nsAttsPower++)
+        ;
+      if (parser->m_nsAttsPower < 3)
+        parser->m_nsAttsPower = 3;
+
+      /* Detect and prevent invalid shift */
+      if (parser->m_nsAttsPower >= sizeof(nsAttsSize) * 8 /* bits per byte */) {
+        /* Restore actual size of memory in m_nsAtts */
+        parser->m_nsAttsPower = oldNsAttsPower;
+        return XML_ERROR_NO_MEMORY;
+      }
+
+      nsAttsSize = 1u << parser->m_nsAttsPower;
+
+      /* Detect and prevent integer overflow.
+       * The preprocessor guard addresses the "always false" warning
+       * from -Wtype-limits on platforms where
+       * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+      if (nsAttsSize > (size_t)(-1) / sizeof(NS_ATT)) {
+        /* Restore actual size of memory in m_nsAtts */
+        parser->m_nsAttsPower = oldNsAttsPower;
+        return XML_ERROR_NO_MEMORY;
+      }
+#endif
+
+      temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts,
+                               nsAttsSize * sizeof(NS_ATT));
+      if (! temp) {
+        /* Restore actual size of memory in m_nsAtts */
+        parser->m_nsAttsPower = oldNsAttsPower;
+        return XML_ERROR_NO_MEMORY;
+      }
+      parser->m_nsAtts = temp;
+      version = 0; /* force re-initialization of m_nsAtts hash table */
+    }
+    /* using a version flag saves us from initializing m_nsAtts every time */
+    if (! version) { /* initialize version flags when version wraps around */
+      version = INIT_ATTS_VERSION;
+      for (j = nsAttsSize; j != 0;)
+        parser->m_nsAtts[--j].version = version;
+    }
+    parser->m_nsAttsVersion = --version;
+
+    /* expand prefixed names and check for duplicates */
+    for (; i < attIndex; i += 2) {
+      const XML_Char *s = appAtts[i];
+      if (s[-1] == 2) { /* prefixed */
+        ATTRIBUTE_ID *id;
+        const BINDING *b;
+        unsigned long uriHash;
+        struct siphash sip_state;
+        struct sipkey sip_key;
+
+        copy_salt_to_sipkey(parser, &sip_key);
+        sip24_init(&sip_state, &sip_key);
+
+        ((XML_Char *)s)[-1] = 0; /* clear flag */
+        id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0);
+        if (! id || ! id->prefix) {
+          /* This code is walking through the appAtts array, dealing
+           * with (in this case) a prefixed attribute name.  To be in
+           * the array, the attribute must have already been bound, so
+           * has to have passed through the hash table lookup once
+           * already.  That implies that an entry for it already
+           * exists, so the lookup above will return a pointer to
+           * already allocated memory.  There is no opportunaity for
+           * the allocator to fail, so the condition above cannot be
+           * fulfilled.
+           *
+           * Since it is difficult to be certain that the above
+           * analysis is complete, we retain the test and merely
+           * remove the code from coverage tests.
+           */
+          return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */
+        }
+        b = id->prefix->binding;
+        if (! b)
+          return XML_ERROR_UNBOUND_PREFIX;
+
+        for (j = 0; j < b->uriLen; j++) {
+          const XML_Char c = b->uri[j];
+          if (! poolAppendChar(&parser->m_tempPool, c))
+            return XML_ERROR_NO_MEMORY;
+        }
+
+        sip24_update(&sip_state, b->uri, b->uriLen * sizeof(XML_Char));
+
+        while (*s++ != XML_T(ASCII_COLON))
+          ;
+
+        sip24_update(&sip_state, s, keylen(s) * sizeof(XML_Char));
+
+        do { /* copies null terminator */
+          if (! poolAppendChar(&parser->m_tempPool, *s))
+            return XML_ERROR_NO_MEMORY;
+        } while (*s++);
+
+        uriHash = (unsigned long)sip24_final(&sip_state);
+
+        { /* Check hash table for duplicate of expanded name (uriName).
+             Derived from code in lookup(parser, HASH_TABLE *table, ...).
+          */
+          unsigned char step = 0;
+          unsigned long mask = nsAttsSize - 1;
+          j = uriHash & mask; /* index into hash table */
+          while (parser->m_nsAtts[j].version == version) {
+            /* for speed we compare stored hash values first */
+            if (uriHash == parser->m_nsAtts[j].hash) {
+              const XML_Char *s1 = poolStart(&parser->m_tempPool);
+              const XML_Char *s2 = parser->m_nsAtts[j].uriName;
+              /* s1 is null terminated, but not s2 */
+              for (; *s1 == *s2 && *s1 != 0; s1++, s2++)
+                ;
+              if (*s1 == 0)
+                return XML_ERROR_DUPLICATE_ATTRIBUTE;
+            }
+            if (! step)
+              step = PROBE_STEP(uriHash, mask, parser->m_nsAttsPower);
+            j < step ? (j += nsAttsSize - step) : (j -= step);
+          }
+        }
+
+        if (parser->m_ns_triplets) { /* append namespace separator and prefix */
+          parser->m_tempPool.ptr[-1] = parser->m_namespaceSeparator;
+          s = b->prefix->name;
+          do {
+            if (! poolAppendChar(&parser->m_tempPool, *s))
+              return XML_ERROR_NO_MEMORY;
+          } while (*s++);
+        }
+
+        /* store expanded name in attribute list */
+        s = poolStart(&parser->m_tempPool);
+        poolFinish(&parser->m_tempPool);
+        appAtts[i] = s;
+
+        /* fill empty slot with new version, uriName and hash value */
+        parser->m_nsAtts[j].version = version;
+        parser->m_nsAtts[j].hash = uriHash;
+        parser->m_nsAtts[j].uriName = s;
+
+        if (! --nPrefixes) {
+          i += 2;
+          break;
+        }
+      } else                     /* not prefixed */
+        ((XML_Char *)s)[-1] = 0; /* clear flag */
+    }
+  }
+  /* clear flags for the remaining attributes */
+  for (; i < attIndex; i += 2)
+    ((XML_Char *)(appAtts[i]))[-1] = 0;
+  for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding)
+    binding->attId->name[-1] = 0;
+
+  if (! parser->m_ns)
+    return XML_ERROR_NONE;
+
+  /* expand the element type name */
+  if (elementType->prefix) {
+    binding = elementType->prefix->binding;
+    if (! binding)
+      return XML_ERROR_UNBOUND_PREFIX;
+    localPart = tagNamePtr->str;
+    while (*localPart++ != XML_T(ASCII_COLON))
+      ;
+  } else if (dtd->defaultPrefix.binding) {
+    binding = dtd->defaultPrefix.binding;
+    localPart = tagNamePtr->str;
+  } else
+    return XML_ERROR_NONE;
+  prefixLen = 0;
+  if (parser->m_ns_triplets && binding->prefix->name) {
+    for (; binding->prefix->name[prefixLen++];)
+      ; /* prefixLen includes null terminator */
+  }
+  tagNamePtr->localPart = localPart;
+  tagNamePtr->uriLen = binding->uriLen;
+  tagNamePtr->prefix = binding->prefix->name;
+  tagNamePtr->prefixLen = prefixLen;
+  for (i = 0; localPart[i++];)
+    ; /* i includes null terminator */
+
+  /* Detect and prevent integer overflow */
+  if (binding->uriLen > INT_MAX - prefixLen
+      || i > INT_MAX - (binding->uriLen + prefixLen)) {
+    return XML_ERROR_NO_MEMORY;
+  }
+
+  n = i + binding->uriLen + prefixLen;
+  if (n > binding->uriAlloc) {
+    TAG *p;
+
+    /* Detect and prevent integer overflow */
+    if (n > INT_MAX - EXPAND_SPARE) {
+      return XML_ERROR_NO_MEMORY;
+    }
+    /* Detect and prevent integer overflow.
+     * The preprocessor guard addresses the "always false" warning
+     * from -Wtype-limits on platforms where
+     * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+    if ((unsigned)(n + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+      return XML_ERROR_NO_MEMORY;
+    }
+#endif
+
+    uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
+    if (! uri)
+      return XML_ERROR_NO_MEMORY;
+    binding->uriAlloc = n + EXPAND_SPARE;
+    memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char));
+    for (p = parser->m_tagStack; p; p = p->parent)
+      if (p->name.str == binding->uri)
+        p->name.str = uri;
+    FREE(parser, binding->uri);
+    binding->uri = uri;
+  }
+  /* if m_namespaceSeparator != '\0' then uri includes it already */
+  uri = binding->uri + binding->uriLen;
+  memcpy(uri, localPart, i * sizeof(XML_Char));
+  /* we always have a namespace separator between localPart and prefix */
+  if (prefixLen) {
+    uri += i - 1;
+    *uri = parser->m_namespaceSeparator; /* replace null terminator */
+    memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char));
+  }
+  tagNamePtr->str = binding->uri;
+  return XML_ERROR_NONE;
+}
+
+static XML_Bool
+is_rfc3986_uri_char(XML_Char candidate) {
+  // For the RFC 3986 ANBF grammar see
+  // https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
+
+  switch (candidate) {
+  // From rule "ALPHA" (uppercase half)
+  case 'A':
+  case 'B':
+  case 'C':
+  case 'D':
+  case 'E':
+  case 'F':
+  case 'G':
+  case 'H':
+  case 'I':
+  case 'J':
+  case 'K':
+  case 'L':
+  case 'M':
+  case 'N':
+  case 'O':
+  case 'P':
+  case 'Q':
+  case 'R':
+  case 'S':
+  case 'T':
+  case 'U':
+  case 'V':
+  case 'W':
+  case 'X':
+  case 'Y':
+  case 'Z':
+
+  // From rule "ALPHA" (lowercase half)
+  case 'a':
+  case 'b':
+  case 'c':
+  case 'd':
+  case 'e':
+  case 'f':
+  case 'g':
+  case 'h':
+  case 'i':
+  case 'j':
+  case 'k':
+  case 'l':
+  case 'm':
+  case 'n':
+  case 'o':
+  case 'p':
+  case 'q':
+  case 'r':
+  case 's':
+  case 't':
+  case 'u':
+  case 'v':
+  case 'w':
+  case 'x':
+  case 'y':
+  case 'z':
+
+  // From rule "DIGIT"
+  case '0':
+  case '1':
+  case '2':
+  case '3':
+  case '4':
+  case '5':
+  case '6':
+  case '7':
+  case '8':
+  case '9':
+
+  // From rule "pct-encoded"
+  case '%':
+
+  // From rule "unreserved"
+  case '-':
+  case '.':
+  case '_':
+  case '~':
+
+  // From rule "gen-delims"
+  case ':':
+  case '/':
+  case '?':
+  case '#':
+  case '[':
+  case ']':
+  case '@':
+
+  // From rule "sub-delims"
+  case '!':
+  case '$':
+  case '&':
+  case '\'':
+  case '(':
+  case ')':
+  case '*':
+  case '+':
+  case ',':
+  case ';':
+  case '=':
+    return XML_TRUE;
+
+  default:
+    return XML_FALSE;
+  }
+}
+
+/* addBinding() overwrites the value of prefix->binding without checking.
+   Therefore one must keep track of the old value outside of addBinding().
+*/
+static enum XML_Error
+addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
+           const XML_Char *uri, BINDING **bindingsPtr) {
+  // "http://www.w3.org/XML/1998/namespace"
+  static const XML_Char xmlNamespace[]
+      = {ASCII_h,      ASCII_t,     ASCII_t,     ASCII_p,      ASCII_COLON,
+         ASCII_SLASH,  ASCII_SLASH, ASCII_w,     ASCII_w,      ASCII_w,
+         ASCII_PERIOD, ASCII_w,     ASCII_3,     ASCII_PERIOD, ASCII_o,
+         ASCII_r,      ASCII_g,     ASCII_SLASH, ASCII_X,      ASCII_M,
+         ASCII_L,      ASCII_SLASH, ASCII_1,     ASCII_9,      ASCII_9,
+         ASCII_8,      ASCII_SLASH, ASCII_n,     ASCII_a,      ASCII_m,
+         ASCII_e,      ASCII_s,     ASCII_p,     ASCII_a,      ASCII_c,
+         ASCII_e,      '\0'};
+  static const int xmlLen = (int)sizeof(xmlNamespace) / sizeof(XML_Char) - 1;
+  // "http://www.w3.org/2000/xmlns/"
+  static const XML_Char xmlnsNamespace[]
+      = {ASCII_h,     ASCII_t,      ASCII_t, ASCII_p, ASCII_COLON,  ASCII_SLASH,
+         ASCII_SLASH, ASCII_w,      ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w,
+         ASCII_3,     ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g,      ASCII_SLASH,
+         ASCII_2,     ASCII_0,      ASCII_0, ASCII_0, ASCII_SLASH,  ASCII_x,
+         ASCII_m,     ASCII_l,      ASCII_n, ASCII_s, ASCII_SLASH,  '\0'};
+  static const int xmlnsLen
+      = (int)sizeof(xmlnsNamespace) / sizeof(XML_Char) - 1;
+
+  XML_Bool mustBeXML = XML_FALSE;
+  XML_Bool isXML = XML_TRUE;
+  XML_Bool isXMLNS = XML_TRUE;
+
+  BINDING *b;
+  int len;
+
+  /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */
+  if (*uri == XML_T('\0') && prefix->name)
+    return XML_ERROR_UNDECLARING_PREFIX;
+
+  if (prefix->name && prefix->name[0] == XML_T(ASCII_x)
+      && prefix->name[1] == XML_T(ASCII_m)
+      && prefix->name[2] == XML_T(ASCII_l)) {
+    /* Not allowed to bind xmlns */
+    if (prefix->name[3] == XML_T(ASCII_n) && prefix->name[4] == XML_T(ASCII_s)
+        && prefix->name[5] == XML_T('\0'))
+      return XML_ERROR_RESERVED_PREFIX_XMLNS;
+
+    if (prefix->name[3] == XML_T('\0'))
+      mustBeXML = XML_TRUE;
+  }
+
+  for (len = 0; uri[len]; len++) {
+    if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len]))
+      isXML = XML_FALSE;
+
+    if (! mustBeXML && isXMLNS
+        && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
+      isXMLNS = XML_FALSE;
+
+    // NOTE: While Expat does not validate namespace URIs against RFC 3986
+    //       today (and is not REQUIRED to do so with regard to the XML 1.0
+    //       namespaces specification) we have to at least make sure, that
+    //       the application on top of Expat (that is likely splitting expanded
+    //       element names ("qualified names") of form
+    //       "[uri sep] local [sep prefix] '\0'" back into 1, 2 or 3 pieces
+    //       in its element handler code) cannot be confused by an attacker
+    //       putting additional namespace separator characters into namespace
+    //       declarations.  That would be ambiguous and not to be expected.
+    //
+    //       While the HTML API docs of function XML_ParserCreateNS have been
+    //       advising against use of a namespace separator character that can
+    //       appear in a URI for >20 years now, some widespread applications
+    //       are using URI characters (':' (colon) in particular) for a
+    //       namespace separator, in practice.  To keep these applications
+    //       functional, we only reject namespaces URIs containing the
+    //       application-chosen namespace separator if the chosen separator
+    //       is a non-URI character with regard to RFC 3986.
+    if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)
+        && ! is_rfc3986_uri_char(uri[len])) {
+      return XML_ERROR_SYNTAX;
+    }
+  }
+  isXML = isXML && len == xmlLen;
+  isXMLNS = isXMLNS && len == xmlnsLen;
+
+  if (mustBeXML != isXML)
+    return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML
+                     : XML_ERROR_RESERVED_NAMESPACE_URI;
+
+  if (isXMLNS)
+    return XML_ERROR_RESERVED_NAMESPACE_URI;
+
+  if (parser->m_namespaceSeparator)
+    len++;
+  if (parser->m_freeBindingList) {
+    b = parser->m_freeBindingList;
+    if (len > b->uriAlloc) {
+      /* Detect and prevent integer overflow */
+      if (len > INT_MAX - EXPAND_SPARE) {
+        return XML_ERROR_NO_MEMORY;
+      }
+
+      /* Detect and prevent integer overflow.
+       * The preprocessor guard addresses the "always false" warning
+       * from -Wtype-limits on platforms where
+       * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+      if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+        return XML_ERROR_NO_MEMORY;
+      }
+#endif
+
+      XML_Char *temp = (XML_Char *)REALLOC(
+          parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
+      if (temp == NULL)
+        return XML_ERROR_NO_MEMORY;
+      b->uri = temp;
+      b->uriAlloc = len + EXPAND_SPARE;
+    }
+    parser->m_freeBindingList = b->nextTagBinding;
+  } else {
+    b = (BINDING *)MALLOC(parser, sizeof(BINDING));
+    if (! b)
+      return XML_ERROR_NO_MEMORY;
+
+    /* Detect and prevent integer overflow */
+    if (len > INT_MAX - EXPAND_SPARE) {
+      return XML_ERROR_NO_MEMORY;
+    }
+    /* Detect and prevent integer overflow.
+     * The preprocessor guard addresses the "always false" warning
+     * from -Wtype-limits on platforms where
+     * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+    if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+      return XML_ERROR_NO_MEMORY;
+    }
+#endif
+
+    b->uri
+        = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
+    if (! b->uri) {
+      FREE(parser, b);
+      return XML_ERROR_NO_MEMORY;
+    }
+    b->uriAlloc = len + EXPAND_SPARE;
+  }
+  b->uriLen = len;
+  memcpy(b->uri, uri, len * sizeof(XML_Char));
+  if (parser->m_namespaceSeparator)
+    b->uri[len - 1] = parser->m_namespaceSeparator;
+  b->prefix = prefix;
+  b->attId = attId;
+  b->prevPrefixBinding = prefix->binding;
+  /* NULL binding when default namespace undeclared */
+  if (*uri == XML_T('\0') && prefix == &parser->m_dtd->defaultPrefix)
+    prefix->binding = NULL;
+  else
+    prefix->binding = b;
+  b->nextTagBinding = *bindingsPtr;
+  *bindingsPtr = b;
+  /* if attId == NULL then we are not starting a namespace scope */
+  if (attId && parser->m_startNamespaceDeclHandler)
+    parser->m_startNamespaceDeclHandler(parser->m_handlerArg, prefix->name,
+                                        prefix->binding ? uri : 0);
+  return XML_ERROR_NONE;
+}
+
+/* The idea here is to avoid using stack for each CDATA section when
+   the whole file is parsed with one call.
+*/
+static enum XML_Error PTRCALL
+cdataSectionProcessor(XML_Parser parser, const char *start, const char *end,
+                      const char **endPtr) {
+  enum XML_Error result = doCdataSection(
+      parser, parser->m_encoding, &start, end, endPtr,
+      (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
+  if (result != XML_ERROR_NONE)
+    return result;
+  if (start) {
+    if (parser->m_parentParser) { /* we are parsing an external entity */
+      parser->m_processor = externalEntityContentProcessor;
+      return externalEntityContentProcessor(parser, start, end, endPtr);
+    } else {
+      parser->m_processor = contentProcessor;
+      return contentProcessor(parser, start, end, endPtr);
+    }
+  }
+  return result;
+}
+
+/* startPtr gets set to non-null if the section is closed, and to null if
+   the section is not yet closed.
+*/
+static enum XML_Error
+doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
+               const char *end, const char **nextPtr, XML_Bool haveMore,
+               enum XML_Account account) {
+  const char *s = *startPtr;
+  const char **eventPP;
+  const char **eventEndPP;
+  if (enc == parser->m_encoding) {
+    eventPP = &parser->m_eventPtr;
+    *eventPP = s;
+    eventEndPP = &parser->m_eventEndPtr;
+  } else {
+    eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+    eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
+  }
+  *eventPP = s;
+  *startPtr = NULL;
+
+  for (;;) {
+    const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */
+    int tok = XmlCdataSectionTok(enc, s, end, &next);
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#else
+    UNUSED_P(account);
+#endif
+    *eventEndPP = next;
+    switch (tok) {
+    case XML_TOK_CDATA_SECT_CLOSE:
+      if (parser->m_endCdataSectionHandler)
+        parser->m_endCdataSectionHandler(parser->m_handlerArg);
+      /* BEGIN disabled code */
+      /* see comment under XML_TOK_CDATA_SECT_OPEN */
+      else if (0 && parser->m_characterDataHandler)
+        parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf,
+                                       0);
+      /* END disabled code */
+      else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+      *startPtr = next;
+      *nextPtr = next;
+      if (parser->m_parsingStatus.parsing == XML_FINISHED)
+        return XML_ERROR_ABORTED;
+      else
+        return XML_ERROR_NONE;
+    case XML_TOK_DATA_NEWLINE:
+      if (parser->m_characterDataHandler) {
+        XML_Char c = 0xA;
+        parser->m_characterDataHandler(parser->m_handlerArg, &c, 1);
+      } else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+      break;
+    case XML_TOK_DATA_CHARS: {
+      XML_CharacterDataHandler charDataHandler = parser->m_characterDataHandler;
+      if (charDataHandler) {
+        if (MUST_CONVERT(enc, s)) {
+          for (;;) {
+            ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf;
+            const enum XML_Convert_Result convert_res = XmlConvert(
+                enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
+            *eventEndPP = next;
+            charDataHandler(parser->m_handlerArg, parser->m_dataBuf,
+                            (int)(dataPtr - (ICHAR *)parser->m_dataBuf));
+            if ((convert_res == XML_CONVERT_COMPLETED)
+                || (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
+              break;
+            *eventPP = s;
+          }
+        } else
+          charDataHandler(parser->m_handlerArg, (XML_Char *)s,
+                          (int)((XML_Char *)next - (XML_Char *)s));
+      } else if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+    } break;
+    case XML_TOK_INVALID:
+      *eventPP = next;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL_CHAR:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_PARTIAL_CHAR;
+    case XML_TOK_PARTIAL:
+    case XML_TOK_NONE:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_UNCLOSED_CDATA_SECTION;
+    default:
+      /* Every token returned by XmlCdataSectionTok() has its own
+       * explicit case, so this default case will never be executed.
+       * We retain it as a safety net and exclude it from the coverage
+       * statistics.
+       *
+       * LCOV_EXCL_START
+       */
+      *eventPP = next;
+      return XML_ERROR_UNEXPECTED_STATE;
+      /* LCOV_EXCL_STOP */
+    }
+
+    *eventPP = s = next;
+    switch (parser->m_parsingStatus.parsing) {
+    case XML_SUSPENDED:
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_FINISHED:
+      return XML_ERROR_ABORTED;
+    default:;
+    }
+  }
+  /* not reached */
+}
+
+#ifdef XML_DTD
+
+/* The idea here is to avoid using stack for each IGNORE section when
+   the whole file is parsed with one call.
+*/
+static enum XML_Error PTRCALL
+ignoreSectionProcessor(XML_Parser parser, const char *start, const char *end,
+                       const char **endPtr) {
+  enum XML_Error result
+      = doIgnoreSection(parser, parser->m_encoding, &start, end, endPtr,
+                        (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+  if (result != XML_ERROR_NONE)
+    return result;
+  if (start) {
+    parser->m_processor = prologProcessor;
+    return prologProcessor(parser, start, end, endPtr);
+  }
+  return result;
+}
+
+/* startPtr gets set to non-null is the section is closed, and to null
+   if the section is not yet closed.
+*/
+static enum XML_Error
+doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
+                const char *end, const char **nextPtr, XML_Bool haveMore) {
+  const char *next = *startPtr; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */
+  int tok;
+  const char *s = *startPtr;
+  const char **eventPP;
+  const char **eventEndPP;
+  if (enc == parser->m_encoding) {
+    eventPP = &parser->m_eventPtr;
+    *eventPP = s;
+    eventEndPP = &parser->m_eventEndPtr;
+  } else {
+    /* It's not entirely clear, but it seems the following two lines
+     * of code cannot be executed.  The only occasions on which 'enc'
+     * is not 'encoding' are when this function is called
+     * from the internal entity processing, and IGNORE sections are an
+     * error in internal entities.
+     *
+     * Since it really isn't clear that this is true, we keep the code
+     * and just remove it from our coverage tests.
+     *
+     * LCOV_EXCL_START
+     */
+    eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+    eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
+    /* LCOV_EXCL_STOP */
+  }
+  *eventPP = s;
+  *startPtr = NULL;
+  tok = XmlIgnoreSectionTok(enc, s, end, &next);
+#  ifdef XML_DTD
+  if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                XML_ACCOUNT_DIRECT)) {
+    accountingOnAbort(parser);
+    return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+  }
+#  endif
+  *eventEndPP = next;
+  switch (tok) {
+  case XML_TOK_IGNORE_SECT:
+    if (parser->m_defaultHandler)
+      reportDefault(parser, enc, s, next);
+    *startPtr = next;
+    *nextPtr = next;
+    if (parser->m_parsingStatus.parsing == XML_FINISHED)
+      return XML_ERROR_ABORTED;
+    else
+      return XML_ERROR_NONE;
+  case XML_TOK_INVALID:
+    *eventPP = next;
+    return XML_ERROR_INVALID_TOKEN;
+  case XML_TOK_PARTIAL_CHAR:
+    if (haveMore) {
+      *nextPtr = s;
+      return XML_ERROR_NONE;
+    }
+    return XML_ERROR_PARTIAL_CHAR;
+  case XML_TOK_PARTIAL:
+  case XML_TOK_NONE:
+    if (haveMore) {
+      *nextPtr = s;
+      return XML_ERROR_NONE;
+    }
+    return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */
+  default:
+    /* All of the tokens that XmlIgnoreSectionTok() returns have
+     * explicit cases to handle them, so this default case is never
+     * executed.  We keep it as a safety net anyway, and remove it
+     * from our test coverage statistics.
+     *
+     * LCOV_EXCL_START
+     */
+    *eventPP = next;
+    return XML_ERROR_UNEXPECTED_STATE;
+    /* LCOV_EXCL_STOP */
+  }
+  /* not reached */
+}
+
+#endif /* XML_DTD */
+
+static enum XML_Error
+initializeEncoding(XML_Parser parser) {
+  const char *s;
+#ifdef XML_UNICODE
+  char encodingBuf[128];
+  /* See comments about `protocolEncodingName` in parserInit() */
+  if (! parser->m_protocolEncodingName)
+    s = NULL;
+  else {
+    int i;
+    for (i = 0; parser->m_protocolEncodingName[i]; i++) {
+      if (i == sizeof(encodingBuf) - 1
+          || (parser->m_protocolEncodingName[i] & ~0x7f) != 0) {
+        encodingBuf[0] = '\0';
+        break;
+      }
+      encodingBuf[i] = (char)parser->m_protocolEncodingName[i];
+    }
+    encodingBuf[i] = '\0';
+    s = encodingBuf;
+  }
+#else
+  s = parser->m_protocolEncodingName;
+#endif
+  if ((parser->m_ns ? XmlInitEncodingNS : XmlInitEncoding)(
+          &parser->m_initEncoding, &parser->m_encoding, s))
+    return XML_ERROR_NONE;
+  return handleUnknownEncoding(parser, parser->m_protocolEncodingName);
+}
+
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s,
+               const char *next) {
+  const char *encodingName = NULL;
+  const XML_Char *storedEncName = NULL;
+  const ENCODING *newEncoding = NULL;
+  const char *version = NULL;
+  const char *versionend = NULL;
+  const XML_Char *storedversion = NULL;
+  int standalone = -1;
+
+#ifdef XML_DTD
+  if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__,
+                                XML_ACCOUNT_DIRECT)) {
+    accountingOnAbort(parser);
+    return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+  }
+#endif
+
+  if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)(
+          isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr,
+          &version, &versionend, &encodingName, &newEncoding, &standalone)) {
+    if (isGeneralTextEntity)
+      return XML_ERROR_TEXT_DECL;
+    else
+      return XML_ERROR_XML_DECL;
+  }
+  if (! isGeneralTextEntity && standalone == 1) {
+    parser->m_dtd->standalone = XML_TRUE;
+#ifdef XML_DTD
+    if (parser->m_paramEntityParsing
+        == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE)
+      parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+#endif /* XML_DTD */
+  }
+  if (parser->m_xmlDeclHandler) {
+    if (encodingName != NULL) {
+      storedEncName = poolStoreString(
+          &parser->m_temp2Pool, parser->m_encoding, encodingName,
+          encodingName + XmlNameLength(parser->m_encoding, encodingName));
+      if (! storedEncName)
+        return XML_ERROR_NO_MEMORY;
+      poolFinish(&parser->m_temp2Pool);
+    }
+    if (version) {
+      storedversion
+          = poolStoreString(&parser->m_temp2Pool, parser->m_encoding, version,
+                            versionend - parser->m_encoding->minBytesPerChar);
+      if (! storedversion)
+        return XML_ERROR_NO_MEMORY;
+    }
+    parser->m_xmlDeclHandler(parser->m_handlerArg, storedversion, storedEncName,
+                             standalone);
+  } else if (parser->m_defaultHandler)
+    reportDefault(parser, parser->m_encoding, s, next);
+  if (parser->m_protocolEncodingName == NULL) {
+    if (newEncoding) {
+      /* Check that the specified encoding does not conflict with what
+       * the parser has already deduced.  Do we have the same number
+       * of bytes in the smallest representation of a character?  If
+       * this is UTF-16, is it the same endianness?
+       */
+      if (newEncoding->minBytesPerChar != parser->m_encoding->minBytesPerChar
+          || (newEncoding->minBytesPerChar == 2
+              && newEncoding != parser->m_encoding)) {
+        parser->m_eventPtr = encodingName;
+        return XML_ERROR_INCORRECT_ENCODING;
+      }
+      parser->m_encoding = newEncoding;
+    } else if (encodingName) {
+      enum XML_Error result;
+      if (! storedEncName) {
+        storedEncName = poolStoreString(
+            &parser->m_temp2Pool, parser->m_encoding, encodingName,
+            encodingName + XmlNameLength(parser->m_encoding, encodingName));
+        if (! storedEncName)
+          return XML_ERROR_NO_MEMORY;
+      }
+      result = handleUnknownEncoding(parser, storedEncName);
+      poolClear(&parser->m_temp2Pool);
+      if (result == XML_ERROR_UNKNOWN_ENCODING)
+        parser->m_eventPtr = encodingName;
+      return result;
+    }
+  }
+
+  if (storedEncName || storedversion)
+    poolClear(&parser->m_temp2Pool);
+
+  return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) {
+  if (parser->m_unknownEncodingHandler) {
+    XML_Encoding info;
+    int i;
+    for (i = 0; i < 256; i++)
+      info.map[i] = -1;
+    info.convert = NULL;
+    info.data = NULL;
+    info.release = NULL;
+    if (parser->m_unknownEncodingHandler(parser->m_unknownEncodingHandlerData,
+                                         encodingName, &info)) {
+      ENCODING *enc;
+      parser->m_unknownEncodingMem = MALLOC(parser, XmlSizeOfUnknownEncoding());
+      if (! parser->m_unknownEncodingMem) {
+        if (info.release)
+          info.release(info.data);
+        return XML_ERROR_NO_MEMORY;
+      }
+      enc = (parser->m_ns ? XmlInitUnknownEncodingNS : XmlInitUnknownEncoding)(
+          parser->m_unknownEncodingMem, info.map, info.convert, info.data);
+      if (enc) {
+        parser->m_unknownEncodingData = info.data;
+        parser->m_unknownEncodingRelease = info.release;
+        parser->m_encoding = enc;
+        return XML_ERROR_NONE;
+      }
+    }
+    if (info.release != NULL)
+      info.release(info.data);
+  }
+  return XML_ERROR_UNKNOWN_ENCODING;
+}
+
+static enum XML_Error PTRCALL
+prologInitProcessor(XML_Parser parser, const char *s, const char *end,
+                    const char **nextPtr) {
+  enum XML_Error result = initializeEncoding(parser);
+  if (result != XML_ERROR_NONE)
+    return result;
+  parser->m_processor = prologProcessor;
+  return prologProcessor(parser, s, end, nextPtr);
+}
+
+#ifdef XML_DTD
+
+static enum XML_Error PTRCALL
+externalParEntInitProcessor(XML_Parser parser, const char *s, const char *end,
+                            const char **nextPtr) {
+  enum XML_Error result = initializeEncoding(parser);
+  if (result != XML_ERROR_NONE)
+    return result;
+
+  /* we know now that XML_Parse(Buffer) has been called,
+     so we consider the external parameter entity read */
+  parser->m_dtd->paramEntityRead = XML_TRUE;
+
+  if (parser->m_prologState.inEntityValue) {
+    parser->m_processor = entityValueInitProcessor;
+    return entityValueInitProcessor(parser, s, end, nextPtr);
+  } else {
+    parser->m_processor = externalParEntProcessor;
+    return externalParEntProcessor(parser, s, end, nextPtr);
+  }
+}
+
+static enum XML_Error PTRCALL
+entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
+                         const char **nextPtr) {
+  int tok;
+  const char *start = s;
+  const char *next = start;
+  parser->m_eventPtr = start;
+
+  for (;;) {
+    tok = XmlPrologTok(parser->m_encoding, start, end, &next);
+    /* Note: Except for XML_TOK_BOM below, these bytes are accounted later in:
+             - storeEntityValue
+             - processXmlDecl
+    */
+    parser->m_eventEndPtr = next;
+    if (tok <= 0) {
+      if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      switch (tok) {
+      case XML_TOK_INVALID:
+        return XML_ERROR_INVALID_TOKEN;
+      case XML_TOK_PARTIAL:
+        return XML_ERROR_UNCLOSED_TOKEN;
+      case XML_TOK_PARTIAL_CHAR:
+        return XML_ERROR_PARTIAL_CHAR;
+      case XML_TOK_NONE: /* start == end */
+      default:
+        break;
+      }
+      /* found end of entity value - can store it now */
+      return storeEntityValue(parser, parser->m_encoding, s, end,
+                              XML_ACCOUNT_DIRECT);
+    } else if (tok == XML_TOK_XML_DECL) {
+      enum XML_Error result;
+      result = processXmlDecl(parser, 0, start, next);
+      if (result != XML_ERROR_NONE)
+        return result;
+      /* At this point, m_parsingStatus.parsing cannot be XML_SUSPENDED.  For
+       * that to happen, a parameter entity parsing handler must have attempted
+       * to suspend the parser, which fails and raises an error.  The parser can
+       * be aborted, but can't be suspended.
+       */
+      if (parser->m_parsingStatus.parsing == XML_FINISHED)
+        return XML_ERROR_ABORTED;
+      *nextPtr = next;
+      /* stop scanning for text declaration - we found one */
+      parser->m_processor = entityValueProcessor;
+      return entityValueProcessor(parser, next, end, nextPtr);
+    }
+    /* If we are at the end of the buffer, this would cause XmlPrologTok to
+       return XML_TOK_NONE on the next call, which would then cause the
+       function to exit with *nextPtr set to s - that is what we want for other
+       tokens, but not for the BOM - we would rather like to skip it;
+       then, when this routine is entered the next time, XmlPrologTok will
+       return XML_TOK_INVALID, since the BOM is still in the buffer
+    */
+    else if (tok == XML_TOK_BOM && next == end
+             && ! parser->m_parsingStatus.finalBuffer) {
+#  ifdef XML_DTD
+      if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                    XML_ACCOUNT_DIRECT)) {
+        accountingOnAbort(parser);
+        return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+      }
+#  endif
+
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    }
+    /* If we get this token, we have the start of what might be a
+       normal tag, but not a declaration (i.e. it doesn't begin with
+       "<!").  In a DTD context, that isn't legal.
+    */
+    else if (tok == XML_TOK_INSTANCE_START) {
+      *nextPtr = next;
+      return XML_ERROR_SYNTAX;
+    }
+    start = next;
+    parser->m_eventPtr = start;
+  }
+}
+
+static enum XML_Error PTRCALL
+externalParEntProcessor(XML_Parser parser, const char *s, const char *end,
+                        const char **nextPtr) {
+  const char *next = s;
+  int tok;
+
+  tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+  if (tok <= 0) {
+    if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
+      *nextPtr = s;
+      return XML_ERROR_NONE;
+    }
+    switch (tok) {
+    case XML_TOK_INVALID:
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL:
+      return XML_ERROR_UNCLOSED_TOKEN;
+    case XML_TOK_PARTIAL_CHAR:
+      return XML_ERROR_PARTIAL_CHAR;
+    case XML_TOK_NONE: /* start == end */
+    default:
+      break;
+    }
+  }
+  /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM.
+     However, when parsing an external subset, doProlog will not accept a BOM
+     as valid, and report a syntax error, so we have to skip the BOM, and
+     account for the BOM bytes.
+  */
+  else if (tok == XML_TOK_BOM) {
+    if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                  XML_ACCOUNT_DIRECT)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+
+    s = next;
+    tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+  }
+
+  parser->m_processor = prologProcessor;
+  return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
+                  XML_ACCOUNT_DIRECT);
+}
+
+static enum XML_Error PTRCALL
+entityValueProcessor(XML_Parser parser, const char *s, const char *end,
+                     const char **nextPtr) {
+  const char *start = s;
+  const char *next = s;
+  const ENCODING *enc = parser->m_encoding;
+  int tok;
+
+  for (;;) {
+    tok = XmlPrologTok(enc, start, end, &next);
+    /* Note: These bytes are accounted later in:
+             - storeEntityValue
+    */
+    if (tok <= 0) {
+      if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      switch (tok) {
+      case XML_TOK_INVALID:
+        return XML_ERROR_INVALID_TOKEN;
+      case XML_TOK_PARTIAL:
+        return XML_ERROR_UNCLOSED_TOKEN;
+      case XML_TOK_PARTIAL_CHAR:
+        return XML_ERROR_PARTIAL_CHAR;
+      case XML_TOK_NONE: /* start == end */
+      default:
+        break;
+      }
+      /* found end of entity value - can store it now */
+      return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT);
+    }
+    start = next;
+  }
+}
+
+#endif /* XML_DTD */
+
+static enum XML_Error PTRCALL
+prologProcessor(XML_Parser parser, const char *s, const char *end,
+                const char **nextPtr) {
+  const char *next = s;
+  int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+  return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
+                  XML_ACCOUNT_DIRECT);
+}
+
+static enum XML_Error
+doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+         int tok, const char *next, const char **nextPtr, XML_Bool haveMore,
+         XML_Bool allowClosingDoctype, enum XML_Account account) {
+#ifdef XML_DTD
+  static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'};
+#endif /* XML_DTD */
+  static const XML_Char atypeCDATA[]
+      = {ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0'};
+  static const XML_Char atypeID[] = {ASCII_I, ASCII_D, '\0'};
+  static const XML_Char atypeIDREF[]
+      = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0'};
+  static const XML_Char atypeIDREFS[]
+      = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0'};
+  static const XML_Char atypeENTITY[]
+      = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0'};
+  static const XML_Char atypeENTITIES[]
+      = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T,
+         ASCII_I, ASCII_E, ASCII_S, '\0'};
+  static const XML_Char atypeNMTOKEN[]
+      = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0'};
+  static const XML_Char atypeNMTOKENS[]
+      = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K,
+         ASCII_E, ASCII_N, ASCII_S, '\0'};
+  static const XML_Char notationPrefix[]
+      = {ASCII_N, ASCII_O, ASCII_T, ASCII_A,      ASCII_T,
+         ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0'};
+  static const XML_Char enumValueSep[] = {ASCII_PIPE, '\0'};
+  static const XML_Char enumValueStart[] = {ASCII_LPAREN, '\0'};
+
+#ifndef XML_DTD
+  UNUSED_P(account);
+#endif
+
+  /* save one level of indirection */
+  DTD *const dtd = parser->m_dtd;
+
+  const char **eventPP;
+  const char **eventEndPP;
+  enum XML_Content_Quant quant;
+
+  if (enc == parser->m_encoding) {
+    eventPP = &parser->m_eventPtr;
+    eventEndPP = &parser->m_eventEndPtr;
+  } else {
+    eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+    eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
+  }
+
+  for (;;) {
+    int role;
+    XML_Bool handleDefault = XML_TRUE;
+    *eventPP = s;
+    *eventEndPP = next;
+    if (tok <= 0) {
+      if (haveMore && tok != XML_TOK_INVALID) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      switch (tok) {
+      case XML_TOK_INVALID:
+        *eventPP = next;
+        return XML_ERROR_INVALID_TOKEN;
+      case XML_TOK_PARTIAL:
+        return XML_ERROR_UNCLOSED_TOKEN;
+      case XML_TOK_PARTIAL_CHAR:
+        return XML_ERROR_PARTIAL_CHAR;
+      case -XML_TOK_PROLOG_S:
+        tok = -tok;
+        break;
+      case XML_TOK_NONE:
+#ifdef XML_DTD
+        /* for internal PE NOT referenced between declarations */
+        if (enc != parser->m_encoding
+            && ! parser->m_openInternalEntities->betweenDecl) {
+          *nextPtr = s;
+          return XML_ERROR_NONE;
+        }
+        /* WFC: PE Between Declarations - must check that PE contains
+           complete markup, not only for external PEs, but also for
+           internal PEs if the reference occurs between declarations.
+        */
+        if (parser->m_isParamEntity || enc != parser->m_encoding) {
+          if (XmlTokenRole(&parser->m_prologState, XML_TOK_NONE, end, end, enc)
+              == XML_ROLE_ERROR)
+            return XML_ERROR_INCOMPLETE_PE;
+          *nextPtr = s;
+          return XML_ERROR_NONE;
+        }
+#endif /* XML_DTD */
+        return XML_ERROR_NO_ELEMENTS;
+      default:
+        tok = -tok;
+        next = end;
+        break;
+      }
+    }
+    role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc);
+#ifdef XML_DTD
+    switch (role) {
+    case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor
+    case XML_ROLE_XML_DECL:       // bytes accounted in processXmlDecl
+    case XML_ROLE_TEXT_DECL:      // bytes accounted in processXmlDecl
+      break;
+    default:
+      if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
+        accountingOnAbort(parser);
+        return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+      }
+    }
+#endif
+    switch (role) {
+    case XML_ROLE_XML_DECL: {
+      enum XML_Error result = processXmlDecl(parser, 0, s, next);
+      if (result != XML_ERROR_NONE)
+        return result;
+      enc = parser->m_encoding;
+      handleDefault = XML_FALSE;
+    } break;
+    case XML_ROLE_DOCTYPE_NAME:
+      if (parser->m_startDoctypeDeclHandler) {
+        parser->m_doctypeName
+            = poolStoreString(&parser->m_tempPool, enc, s, next);
+        if (! parser->m_doctypeName)
+          return XML_ERROR_NO_MEMORY;
+        poolFinish(&parser->m_tempPool);
+        parser->m_doctypePubid = NULL;
+        handleDefault = XML_FALSE;
+      }
+      parser->m_doctypeSysid = NULL; /* always initialize to NULL */
+      break;
+    case XML_ROLE_DOCTYPE_INTERNAL_SUBSET:
+      if (parser->m_startDoctypeDeclHandler) {
+        parser->m_startDoctypeDeclHandler(
+            parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid,
+            parser->m_doctypePubid, 1);
+        parser->m_doctypeName = NULL;
+        poolClear(&parser->m_tempPool);
+        handleDefault = XML_FALSE;
+      }
+      break;
+#ifdef XML_DTD
+    case XML_ROLE_TEXT_DECL: {
+      enum XML_Error result = processXmlDecl(parser, 1, s, next);
+      if (result != XML_ERROR_NONE)
+        return result;
+      enc = parser->m_encoding;
+      handleDefault = XML_FALSE;
+    } break;
+#endif /* XML_DTD */
+    case XML_ROLE_DOCTYPE_PUBLIC_ID:
+#ifdef XML_DTD
+      parser->m_useForeignDTD = XML_FALSE;
+      parser->m_declEntity = (ENTITY *)lookup(
+          parser, &dtd->paramEntities, externalSubsetName, sizeof(ENTITY));
+      if (! parser->m_declEntity)
+        return XML_ERROR_NO_MEMORY;
+#endif /* XML_DTD */
+      dtd->hasParamEntityRefs = XML_TRUE;
+      if (parser->m_startDoctypeDeclHandler) {
+        XML_Char *pubId;
+        if (! XmlIsPublicId(enc, s, next, eventPP))
+          return XML_ERROR_PUBLICID;
+        pubId = poolStoreString(&parser->m_tempPool, enc,
+                                s + enc->minBytesPerChar,
+                                next - enc->minBytesPerChar);
+        if (! pubId)
+          return XML_ERROR_NO_MEMORY;
+        normalizePublicId(pubId);
+        poolFinish(&parser->m_tempPool);
+        parser->m_doctypePubid = pubId;
+        handleDefault = XML_FALSE;
+        goto alreadyChecked;
+      }
+      /* fall through */
+    case XML_ROLE_ENTITY_PUBLIC_ID:
+      if (! XmlIsPublicId(enc, s, next, eventPP))
+        return XML_ERROR_PUBLICID;
+    alreadyChecked:
+      if (dtd->keepProcessing && parser->m_declEntity) {
+        XML_Char *tem
+            = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar,
+                              next - enc->minBytesPerChar);
+        if (! tem)
+          return XML_ERROR_NO_MEMORY;
+        normalizePublicId(tem);
+        parser->m_declEntity->publicId = tem;
+        poolFinish(&dtd->pool);
+        /* Don't suppress the default handler if we fell through from
+         * the XML_ROLE_DOCTYPE_PUBLIC_ID case.
+         */
+        if (parser->m_entityDeclHandler && role == XML_ROLE_ENTITY_PUBLIC_ID)
+          handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_DOCTYPE_CLOSE:
+      if (allowClosingDoctype != XML_TRUE) {
+        /* Must not close doctype from within expanded parameter entities */
+        return XML_ERROR_INVALID_TOKEN;
+      }
+
+      if (parser->m_doctypeName) {
+        parser->m_startDoctypeDeclHandler(
+            parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid,
+            parser->m_doctypePubid, 0);
+        poolClear(&parser->m_tempPool);
+        handleDefault = XML_FALSE;
+      }
+      /* parser->m_doctypeSysid will be non-NULL in the case of a previous
+         XML_ROLE_DOCTYPE_SYSTEM_ID, even if parser->m_startDoctypeDeclHandler
+         was not set, indicating an external subset
+      */
+#ifdef XML_DTD
+      if (parser->m_doctypeSysid || parser->m_useForeignDTD) {
+        XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
+        dtd->hasParamEntityRefs = XML_TRUE;
+        if (parser->m_paramEntityParsing
+            && parser->m_externalEntityRefHandler) {
+          ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities,
+                                            externalSubsetName, sizeof(ENTITY));
+          if (! entity) {
+            /* The external subset name "#" will have already been
+             * inserted into the hash table at the start of the
+             * external entity parsing, so no allocation will happen
+             * and lookup() cannot fail.
+             */
+            return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */
+          }
+          if (parser->m_useForeignDTD)
+            entity->base = parser->m_curBase;
+          dtd->paramEntityRead = XML_FALSE;
+          if (! parser->m_externalEntityRefHandler(
+                  parser->m_externalEntityRefHandlerArg, 0, entity->base,
+                  entity->systemId, entity->publicId))
+            return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+          if (dtd->paramEntityRead) {
+            if (! dtd->standalone && parser->m_notStandaloneHandler
+                && ! parser->m_notStandaloneHandler(parser->m_handlerArg))
+              return XML_ERROR_NOT_STANDALONE;
+          }
+          /* if we didn't read the foreign DTD then this means that there
+             is no external subset and we must reset dtd->hasParamEntityRefs
+          */
+          else if (! parser->m_doctypeSysid)
+            dtd->hasParamEntityRefs = hadParamEntityRefs;
+          /* end of DTD - no need to update dtd->keepProcessing */
+        }
+        parser->m_useForeignDTD = XML_FALSE;
+      }
+#endif /* XML_DTD */
+      if (parser->m_endDoctypeDeclHandler) {
+        parser->m_endDoctypeDeclHandler(parser->m_handlerArg);
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_INSTANCE_START:
+#ifdef XML_DTD
+      /* if there is no DOCTYPE declaration then now is the
+         last chance to read the foreign DTD
+      */
+      if (parser->m_useForeignDTD) {
+        XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
+        dtd->hasParamEntityRefs = XML_TRUE;
+        if (parser->m_paramEntityParsing
+            && parser->m_externalEntityRefHandler) {
+          ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities,
+                                            externalSubsetName, sizeof(ENTITY));
+          if (! entity)
+            return XML_ERROR_NO_MEMORY;
+          entity->base = parser->m_curBase;
+          dtd->paramEntityRead = XML_FALSE;
+          if (! parser->m_externalEntityRefHandler(
+                  parser->m_externalEntityRefHandlerArg, 0, entity->base,
+                  entity->systemId, entity->publicId))
+            return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+          if (dtd->paramEntityRead) {
+            if (! dtd->standalone && parser->m_notStandaloneHandler
+                && ! parser->m_notStandaloneHandler(parser->m_handlerArg))
+              return XML_ERROR_NOT_STANDALONE;
+          }
+          /* if we didn't read the foreign DTD then this means that there
+             is no external subset and we must reset dtd->hasParamEntityRefs
+          */
+          else
+            dtd->hasParamEntityRefs = hadParamEntityRefs;
+          /* end of DTD - no need to update dtd->keepProcessing */
+        }
+      }
+#endif /* XML_DTD */
+      parser->m_processor = contentProcessor;
+      return contentProcessor(parser, s, end, nextPtr);
+    case XML_ROLE_ATTLIST_ELEMENT_NAME:
+      parser->m_declElementType = getElementType(parser, enc, s, next);
+      if (! parser->m_declElementType)
+        return XML_ERROR_NO_MEMORY;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_NAME:
+      parser->m_declAttributeId = getAttributeId(parser, enc, s, next);
+      if (! parser->m_declAttributeId)
+        return XML_ERROR_NO_MEMORY;
+      parser->m_declAttributeIsCdata = XML_FALSE;
+      parser->m_declAttributeType = NULL;
+      parser->m_declAttributeIsId = XML_FALSE;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_CDATA:
+      parser->m_declAttributeIsCdata = XML_TRUE;
+      parser->m_declAttributeType = atypeCDATA;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_ID:
+      parser->m_declAttributeIsId = XML_TRUE;
+      parser->m_declAttributeType = atypeID;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_IDREF:
+      parser->m_declAttributeType = atypeIDREF;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_IDREFS:
+      parser->m_declAttributeType = atypeIDREFS;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_ENTITY:
+      parser->m_declAttributeType = atypeENTITY;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES:
+      parser->m_declAttributeType = atypeENTITIES;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN:
+      parser->m_declAttributeType = atypeNMTOKEN;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS:
+      parser->m_declAttributeType = atypeNMTOKENS;
+    checkAttListDeclHandler:
+      if (dtd->keepProcessing && parser->m_attlistDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_ATTRIBUTE_ENUM_VALUE:
+    case XML_ROLE_ATTRIBUTE_NOTATION_VALUE:
+      if (dtd->keepProcessing && parser->m_attlistDeclHandler) {
+        const XML_Char *prefix;
+        if (parser->m_declAttributeType) {
+          prefix = enumValueSep;
+        } else {
+          prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE ? notationPrefix
+                                                              : enumValueStart);
+        }
+        if (! poolAppendString(&parser->m_tempPool, prefix))
+          return XML_ERROR_NO_MEMORY;
+        if (! poolAppend(&parser->m_tempPool, enc, s, next))
+          return XML_ERROR_NO_MEMORY;
+        parser->m_declAttributeType = parser->m_tempPool.start;
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE:
+    case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE:
+      if (dtd->keepProcessing) {
+        if (! defineAttribute(parser->m_declElementType,
+                              parser->m_declAttributeId,
+                              parser->m_declAttributeIsCdata,
+                              parser->m_declAttributeIsId, 0, parser))
+          return XML_ERROR_NO_MEMORY;
+        if (parser->m_attlistDeclHandler && parser->m_declAttributeType) {
+          if (*parser->m_declAttributeType == XML_T(ASCII_LPAREN)
+              || (*parser->m_declAttributeType == XML_T(ASCII_N)
+                  && parser->m_declAttributeType[1] == XML_T(ASCII_O))) {
+            /* Enumerated or Notation type */
+            if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_RPAREN))
+                || ! poolAppendChar(&parser->m_tempPool, XML_T('\0')))
+              return XML_ERROR_NO_MEMORY;
+            parser->m_declAttributeType = parser->m_tempPool.start;
+            poolFinish(&parser->m_tempPool);
+          }
+          *eventEndPP = s;
+          parser->m_attlistDeclHandler(
+              parser->m_handlerArg, parser->m_declElementType->name,
+              parser->m_declAttributeId->name, parser->m_declAttributeType, 0,
+              role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE);
+          handleDefault = XML_FALSE;
+        }
+      }
+      poolClear(&parser->m_tempPool);
+      break;
+    case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE:
+    case XML_ROLE_FIXED_ATTRIBUTE_VALUE:
+      if (dtd->keepProcessing) {
+        const XML_Char *attVal;
+        enum XML_Error result = storeAttributeValue(
+            parser, enc, parser->m_declAttributeIsCdata,
+            s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool,
+            XML_ACCOUNT_NONE);
+        if (result)
+          return result;
+        attVal = poolStart(&dtd->pool);
+        poolFinish(&dtd->pool);
+        /* ID attributes aren't allowed to have a default */
+        if (! defineAttribute(
+                parser->m_declElementType, parser->m_declAttributeId,
+                parser->m_declAttributeIsCdata, XML_FALSE, attVal, parser))
+          return XML_ERROR_NO_MEMORY;
+        if (parser->m_attlistDeclHandler && parser->m_declAttributeType) {
+          if (*parser->m_declAttributeType == XML_T(ASCII_LPAREN)
+              || (*parser->m_declAttributeType == XML_T(ASCII_N)
+                  && parser->m_declAttributeType[1] == XML_T(ASCII_O))) {
+            /* Enumerated or Notation type */
+            if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_RPAREN))
+                || ! poolAppendChar(&parser->m_tempPool, XML_T('\0')))
+              return XML_ERROR_NO_MEMORY;
+            parser->m_declAttributeType = parser->m_tempPool.start;
+            poolFinish(&parser->m_tempPool);
+          }
+          *eventEndPP = s;
+          parser->m_attlistDeclHandler(
+              parser->m_handlerArg, parser->m_declElementType->name,
+              parser->m_declAttributeId->name, parser->m_declAttributeType,
+              attVal, role == XML_ROLE_FIXED_ATTRIBUTE_VALUE);
+          poolClear(&parser->m_tempPool);
+          handleDefault = XML_FALSE;
+        }
+      }
+      break;
+    case XML_ROLE_ENTITY_VALUE:
+      if (dtd->keepProcessing) {
+        enum XML_Error result
+            = storeEntityValue(parser, enc, s + enc->minBytesPerChar,
+                               next - enc->minBytesPerChar, XML_ACCOUNT_NONE);
+        if (parser->m_declEntity) {
+          parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
+          parser->m_declEntity->textLen
+              = (int)(poolLength(&dtd->entityValuePool));
+          poolFinish(&dtd->entityValuePool);
+          if (parser->m_entityDeclHandler) {
+            *eventEndPP = s;
+            parser->m_entityDeclHandler(
+                parser->m_handlerArg, parser->m_declEntity->name,
+                parser->m_declEntity->is_param, parser->m_declEntity->textPtr,
+                parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0);
+            handleDefault = XML_FALSE;
+          }
+        } else
+          poolDiscard(&dtd->entityValuePool);
+        if (result != XML_ERROR_NONE)
+          return result;
+      }
+      break;
+    case XML_ROLE_DOCTYPE_SYSTEM_ID:
+#ifdef XML_DTD
+      parser->m_useForeignDTD = XML_FALSE;
+#endif /* XML_DTD */
+      dtd->hasParamEntityRefs = XML_TRUE;
+      if (parser->m_startDoctypeDeclHandler) {
+        parser->m_doctypeSysid = poolStoreString(&parser->m_tempPool, enc,
+                                                 s + enc->minBytesPerChar,
+                                                 next - enc->minBytesPerChar);
+        if (parser->m_doctypeSysid == NULL)
+          return XML_ERROR_NO_MEMORY;
+        poolFinish(&parser->m_tempPool);
+        handleDefault = XML_FALSE;
+      }
+#ifdef XML_DTD
+      else
+        /* use externalSubsetName to make parser->m_doctypeSysid non-NULL
+           for the case where no parser->m_startDoctypeDeclHandler is set */
+        parser->m_doctypeSysid = externalSubsetName;
+#endif /* XML_DTD */
+      if (! dtd->standalone
+#ifdef XML_DTD
+          && ! parser->m_paramEntityParsing
+#endif /* XML_DTD */
+          && parser->m_notStandaloneHandler
+          && ! parser->m_notStandaloneHandler(parser->m_handlerArg))
+        return XML_ERROR_NOT_STANDALONE;
+#ifndef XML_DTD
+      break;
+#else  /* XML_DTD */
+      if (! parser->m_declEntity) {
+        parser->m_declEntity = (ENTITY *)lookup(
+            parser, &dtd->paramEntities, externalSubsetName, sizeof(ENTITY));
+        if (! parser->m_declEntity)
+          return XML_ERROR_NO_MEMORY;
+        parser->m_declEntity->publicId = NULL;
+      }
+#endif /* XML_DTD */
+      /* fall through */
+    case XML_ROLE_ENTITY_SYSTEM_ID:
+      if (dtd->keepProcessing && parser->m_declEntity) {
+        parser->m_declEntity->systemId
+            = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar,
+                              next - enc->minBytesPerChar);
+        if (! parser->m_declEntity->systemId)
+          return XML_ERROR_NO_MEMORY;
+        parser->m_declEntity->base = parser->m_curBase;
+        poolFinish(&dtd->pool);
+        /* Don't suppress the default handler if we fell through from
+         * the XML_ROLE_DOCTYPE_SYSTEM_ID case.
+         */
+        if (parser->m_entityDeclHandler && role == XML_ROLE_ENTITY_SYSTEM_ID)
+          handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_ENTITY_COMPLETE:
+      if (dtd->keepProcessing && parser->m_declEntity
+          && parser->m_entityDeclHandler) {
+        *eventEndPP = s;
+        parser->m_entityDeclHandler(
+            parser->m_handlerArg, parser->m_declEntity->name,
+            parser->m_declEntity->is_param, 0, 0, parser->m_declEntity->base,
+            parser->m_declEntity->systemId, parser->m_declEntity->publicId, 0);
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_ENTITY_NOTATION_NAME:
+      if (dtd->keepProcessing && parser->m_declEntity) {
+        parser->m_declEntity->notation
+            = poolStoreString(&dtd->pool, enc, s, next);
+        if (! parser->m_declEntity->notation)
+          return XML_ERROR_NO_MEMORY;
+        poolFinish(&dtd->pool);
+        if (parser->m_unparsedEntityDeclHandler) {
+          *eventEndPP = s;
+          parser->m_unparsedEntityDeclHandler(
+              parser->m_handlerArg, parser->m_declEntity->name,
+              parser->m_declEntity->base, parser->m_declEntity->systemId,
+              parser->m_declEntity->publicId, parser->m_declEntity->notation);
+          handleDefault = XML_FALSE;
+        } else if (parser->m_entityDeclHandler) {
+          *eventEndPP = s;
+          parser->m_entityDeclHandler(
+              parser->m_handlerArg, parser->m_declEntity->name, 0, 0, 0,
+              parser->m_declEntity->base, parser->m_declEntity->systemId,
+              parser->m_declEntity->publicId, parser->m_declEntity->notation);
+          handleDefault = XML_FALSE;
+        }
+      }
+      break;
+    case XML_ROLE_GENERAL_ENTITY_NAME: {
+      if (XmlPredefinedEntityName(enc, s, next)) {
+        parser->m_declEntity = NULL;
+        break;
+      }
+      if (dtd->keepProcessing) {
+        const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
+        if (! name)
+          return XML_ERROR_NO_MEMORY;
+        parser->m_declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities,
+                                                name, sizeof(ENTITY));
+        if (! parser->m_declEntity)
+          return XML_ERROR_NO_MEMORY;
+        if (parser->m_declEntity->name != name) {
+          poolDiscard(&dtd->pool);
+          parser->m_declEntity = NULL;
+        } else {
+          poolFinish(&dtd->pool);
+          parser->m_declEntity->publicId = NULL;
+          parser->m_declEntity->is_param = XML_FALSE;
+          /* if we have a parent parser or are reading an internal parameter
+             entity, then the entity declaration is not considered "internal"
+          */
+          parser->m_declEntity->is_internal
+              = ! (parser->m_parentParser || parser->m_openInternalEntities);
+          if (parser->m_entityDeclHandler)
+            handleDefault = XML_FALSE;
+        }
+      } else {
+        poolDiscard(&dtd->pool);
+        parser->m_declEntity = NULL;
+      }
+    } break;
+    case XML_ROLE_PARAM_ENTITY_NAME:
+#ifdef XML_DTD
+      if (dtd->keepProcessing) {
+        const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
+        if (! name)
+          return XML_ERROR_NO_MEMORY;
+        parser->m_declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities,
+                                                name, sizeof(ENTITY));
+        if (! parser->m_declEntity)
+          return XML_ERROR_NO_MEMORY;
+        if (parser->m_declEntity->name != name) {
+          poolDiscard(&dtd->pool);
+          parser->m_declEntity = NULL;
+        } else {
+          poolFinish(&dtd->pool);
+          parser->m_declEntity->publicId = NULL;
+          parser->m_declEntity->is_param = XML_TRUE;
+          /* if we have a parent parser or are reading an internal parameter
+             entity, then the entity declaration is not considered "internal"
+          */
+          parser->m_declEntity->is_internal
+              = ! (parser->m_parentParser || parser->m_openInternalEntities);
+          if (parser->m_entityDeclHandler)
+            handleDefault = XML_FALSE;
+        }
+      } else {
+        poolDiscard(&dtd->pool);
+        parser->m_declEntity = NULL;
+      }
+#else  /* not XML_DTD */
+      parser->m_declEntity = NULL;
+#endif /* XML_DTD */
+      break;
+    case XML_ROLE_NOTATION_NAME:
+      parser->m_declNotationPublicId = NULL;
+      parser->m_declNotationName = NULL;
+      if (parser->m_notationDeclHandler) {
+        parser->m_declNotationName
+            = poolStoreString(&parser->m_tempPool, enc, s, next);
+        if (! parser->m_declNotationName)
+          return XML_ERROR_NO_MEMORY;
+        poolFinish(&parser->m_tempPool);
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_NOTATION_PUBLIC_ID:
+      if (! XmlIsPublicId(enc, s, next, eventPP))
+        return XML_ERROR_PUBLICID;
+      if (parser
+              ->m_declNotationName) { /* means m_notationDeclHandler != NULL */
+        XML_Char *tem = poolStoreString(&parser->m_tempPool, enc,
+                                        s + enc->minBytesPerChar,
+                                        next - enc->minBytesPerChar);
+        if (! tem)
+          return XML_ERROR_NO_MEMORY;
+        normalizePublicId(tem);
+        parser->m_declNotationPublicId = tem;
+        poolFinish(&parser->m_tempPool);
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_NOTATION_SYSTEM_ID:
+      if (parser->m_declNotationName && parser->m_notationDeclHandler) {
+        const XML_Char *systemId = poolStoreString(&parser->m_tempPool, enc,
+                                                   s + enc->minBytesPerChar,
+                                                   next - enc->minBytesPerChar);
+        if (! systemId)
+          return XML_ERROR_NO_MEMORY;
+        *eventEndPP = s;
+        parser->m_notationDeclHandler(
+            parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase,
+            systemId, parser->m_declNotationPublicId);
+        handleDefault = XML_FALSE;
+      }
+      poolClear(&parser->m_tempPool);
+      break;
+    case XML_ROLE_NOTATION_NO_SYSTEM_ID:
+      if (parser->m_declNotationPublicId && parser->m_notationDeclHandler) {
+        *eventEndPP = s;
+        parser->m_notationDeclHandler(
+            parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase,
+            0, parser->m_declNotationPublicId);
+        handleDefault = XML_FALSE;
+      }
+      poolClear(&parser->m_tempPool);
+      break;
+    case XML_ROLE_ERROR:
+      switch (tok) {
+      case XML_TOK_PARAM_ENTITY_REF:
+        /* PE references in internal subset are
+           not allowed within declarations. */
+        return XML_ERROR_PARAM_ENTITY_REF;
+      case XML_TOK_XML_DECL:
+        return XML_ERROR_MISPLACED_XML_PI;
+      default:
+        return XML_ERROR_SYNTAX;
+      }
+#ifdef XML_DTD
+    case XML_ROLE_IGNORE_SECT: {
+      enum XML_Error result;
+      if (parser->m_defaultHandler)
+        reportDefault(parser, enc, s, next);
+      handleDefault = XML_FALSE;
+      result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore);
+      if (result != XML_ERROR_NONE)
+        return result;
+      else if (! next) {
+        parser->m_processor = ignoreSectionProcessor;
+        return result;
+      }
+    } break;
+#endif /* XML_DTD */
+    case XML_ROLE_GROUP_OPEN:
+      if (parser->m_prologState.level >= parser->m_groupSize) {
+        if (parser->m_groupSize) {
+          {
+            /* Detect and prevent integer overflow */
+            if (parser->m_groupSize > (unsigned int)(-1) / 2u) {
+              return XML_ERROR_NO_MEMORY;
+            }
+
+            char *const new_connector = (char *)REALLOC(
+                parser, parser->m_groupConnector, parser->m_groupSize *= 2);
+            if (new_connector == NULL) {
+              parser->m_groupSize /= 2;
+              return XML_ERROR_NO_MEMORY;
+            }
+            parser->m_groupConnector = new_connector;
+          }
+
+          if (dtd->scaffIndex) {
+            /* Detect and prevent integer overflow.
+             * The preprocessor guard addresses the "always false" warning
+             * from -Wtype-limits on platforms where
+             * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+            if (parser->m_groupSize > (size_t)(-1) / sizeof(int)) {
+              return XML_ERROR_NO_MEMORY;
+            }
+#endif
+
+            int *const new_scaff_index = (int *)REALLOC(
+                parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
+            if (new_scaff_index == NULL)
+              return XML_ERROR_NO_MEMORY;
+            dtd->scaffIndex = new_scaff_index;
+          }
+        } else {
+          parser->m_groupConnector
+              = (char *)MALLOC(parser, parser->m_groupSize = 32);
+          if (! parser->m_groupConnector) {
+            parser->m_groupSize = 0;
+            return XML_ERROR_NO_MEMORY;
+          }
+        }
+      }
+      parser->m_groupConnector[parser->m_prologState.level] = 0;
+      if (dtd->in_eldecl) {
+        int myindex = nextScaffoldPart(parser);
+        if (myindex < 0)
+          return XML_ERROR_NO_MEMORY;
+        assert(dtd->scaffIndex != NULL);
+        dtd->scaffIndex[dtd->scaffLevel] = myindex;
+        dtd->scaffLevel++;
+        dtd->scaffold[myindex].type = XML_CTYPE_SEQ;
+        if (parser->m_elementDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_GROUP_SEQUENCE:
+      if (parser->m_groupConnector[parser->m_prologState.level] == ASCII_PIPE)
+        return XML_ERROR_SYNTAX;
+      parser->m_groupConnector[parser->m_prologState.level] = ASCII_COMMA;
+      if (dtd->in_eldecl && parser->m_elementDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_GROUP_CHOICE:
+      if (parser->m_groupConnector[parser->m_prologState.level] == ASCII_COMMA)
+        return XML_ERROR_SYNTAX;
+      if (dtd->in_eldecl
+          && ! parser->m_groupConnector[parser->m_prologState.level]
+          && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
+              != XML_CTYPE_MIXED)) {
+        dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
+            = XML_CTYPE_CHOICE;
+        if (parser->m_elementDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      parser->m_groupConnector[parser->m_prologState.level] = ASCII_PIPE;
+      break;
+    case XML_ROLE_PARAM_ENTITY_REF:
+#ifdef XML_DTD
+    case XML_ROLE_INNER_PARAM_ENTITY_REF:
+      dtd->hasParamEntityRefs = XML_TRUE;
+      if (! parser->m_paramEntityParsing)
+        dtd->keepProcessing = dtd->standalone;
+      else {
+        const XML_Char *name;
+        ENTITY *entity;
+        name = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar,
+                               next - enc->minBytesPerChar);
+        if (! name)
+          return XML_ERROR_NO_MEMORY;
+        entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0);
+        poolDiscard(&dtd->pool);
+        /* first, determine if a check for an existing declaration is needed;
+           if yes, check that the entity exists, and that it is internal,
+           otherwise call the skipped entity handler
+        */
+        if (parser->m_prologState.documentEntity
+            && (dtd->standalone ? ! parser->m_openInternalEntities
+                                : ! dtd->hasParamEntityRefs)) {
+          if (! entity)
+            return XML_ERROR_UNDEFINED_ENTITY;
+          else if (! entity->is_internal) {
+            /* It's hard to exhaustively search the code to be sure,
+             * but there doesn't seem to be a way of executing the
+             * following line.  There are two cases:
+             *
+             * If 'standalone' is false, the DTD must have no
+             * parameter entities or we wouldn't have passed the outer
+             * 'if' statement.  That means the only entity in the hash
+             * table is the external subset name "#" which cannot be
+             * given as a parameter entity name in XML syntax, so the
+             * lookup must have returned NULL and we don't even reach
+             * the test for an internal entity.
+             *
+             * If 'standalone' is true, it does not seem to be
+             * possible to create entities taking this code path that
+             * are not internal entities, so fail the test above.
+             *
+             * Because this analysis is very uncertain, the code is
+             * being left in place and merely removed from the
+             * coverage test statistics.
+             */
+            return XML_ERROR_ENTITY_DECLARED_IN_PE; /* LCOV_EXCL_LINE */
+          }
+        } else if (! entity) {
+          dtd->keepProcessing = dtd->standalone;
+          /* cannot report skipped entities in declarations */
+          if ((role == XML_ROLE_PARAM_ENTITY_REF)
+              && parser->m_skippedEntityHandler) {
+            parser->m_skippedEntityHandler(parser->m_handlerArg, name, 1);
+            handleDefault = XML_FALSE;
+          }
+          break;
+        }
+        if (entity->open)
+          return XML_ERROR_RECURSIVE_ENTITY_REF;
+        if (entity->textPtr) {
+          enum XML_Error result;
+          XML_Bool betweenDecl
+              = (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE);
+          result = processInternalEntity(parser, entity, betweenDecl);
+          if (result != XML_ERROR_NONE)
+            return result;
+          handleDefault = XML_FALSE;
+          break;
+        }
+        if (parser->m_externalEntityRefHandler) {
+          dtd->paramEntityRead = XML_FALSE;
+          entity->open = XML_TRUE;
+          entityTrackingOnOpen(parser, entity, __LINE__);
+          if (! parser->m_externalEntityRefHandler(
+                  parser->m_externalEntityRefHandlerArg, 0, entity->base,
+                  entity->systemId, entity->publicId)) {
+            entityTrackingOnClose(parser, entity, __LINE__);
+            entity->open = XML_FALSE;
+            return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+          }
+          entityTrackingOnClose(parser, entity, __LINE__);
+          entity->open = XML_FALSE;
+          handleDefault = XML_FALSE;
+          if (! dtd->paramEntityRead) {
+            dtd->keepProcessing = dtd->standalone;
+            break;
+          }
+        } else {
+          dtd->keepProcessing = dtd->standalone;
+          break;
+        }
+      }
+#endif /* XML_DTD */
+      if (! dtd->standalone && parser->m_notStandaloneHandler
+          && ! parser->m_notStandaloneHandler(parser->m_handlerArg))
+        return XML_ERROR_NOT_STANDALONE;
+      break;
+
+      /* Element declaration stuff */
+
+    case XML_ROLE_ELEMENT_NAME:
+      if (parser->m_elementDeclHandler) {
+        parser->m_declElementType = getElementType(parser, enc, s, next);
+        if (! parser->m_declElementType)
+          return XML_ERROR_NO_MEMORY;
+        dtd->scaffLevel = 0;
+        dtd->scaffCount = 0;
+        dtd->in_eldecl = XML_TRUE;
+        handleDefault = XML_FALSE;
+      }
+      break;
+
+    case XML_ROLE_CONTENT_ANY:
+    case XML_ROLE_CONTENT_EMPTY:
+      if (dtd->in_eldecl) {
+        if (parser->m_elementDeclHandler) {
+          XML_Content *content
+              = (XML_Content *)MALLOC(parser, sizeof(XML_Content));
+          if (! content)
+            return XML_ERROR_NO_MEMORY;
+          content->quant = XML_CQUANT_NONE;
+          content->name = NULL;
+          content->numchildren = 0;
+          content->children = NULL;
+          content->type = ((role == XML_ROLE_CONTENT_ANY) ? XML_CTYPE_ANY
+                                                          : XML_CTYPE_EMPTY);
+          *eventEndPP = s;
+          parser->m_elementDeclHandler(
+              parser->m_handlerArg, parser->m_declElementType->name, content);
+          handleDefault = XML_FALSE;
+        }
+        dtd->in_eldecl = XML_FALSE;
+      }
+      break;
+
+    case XML_ROLE_CONTENT_PCDATA:
+      if (dtd->in_eldecl) {
+        dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
+            = XML_CTYPE_MIXED;
+        if (parser->m_elementDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      break;
+
+    case XML_ROLE_CONTENT_ELEMENT:
+      quant = XML_CQUANT_NONE;
+      goto elementContent;
+    case XML_ROLE_CONTENT_ELEMENT_OPT:
+      quant = XML_CQUANT_OPT;
+      goto elementContent;
+    case XML_ROLE_CONTENT_ELEMENT_REP:
+      quant = XML_CQUANT_REP;
+      goto elementContent;
+    case XML_ROLE_CONTENT_ELEMENT_PLUS:
+      quant = XML_CQUANT_PLUS;
+    elementContent:
+      if (dtd->in_eldecl) {
+        ELEMENT_TYPE *el;
+        const XML_Char *name;
+        size_t nameLen;
+        const char *nxt
+            = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar);
+        int myindex = nextScaffoldPart(parser);
+        if (myindex < 0)
+          return XML_ERROR_NO_MEMORY;
+        dtd->scaffold[myindex].type = XML_CTYPE_NAME;
+        dtd->scaffold[myindex].quant = quant;
+        el = getElementType(parser, enc, s, nxt);
+        if (! el)
+          return XML_ERROR_NO_MEMORY;
+        name = el->name;
+        dtd->scaffold[myindex].name = name;
+        nameLen = 0;
+        for (; name[nameLen++];)
+          ;
+
+        /* Detect and prevent integer overflow */
+        if (nameLen > UINT_MAX - dtd->contentStringLen) {
+          return XML_ERROR_NO_MEMORY;
+        }
+
+        dtd->contentStringLen += (unsigned)nameLen;
+        if (parser->m_elementDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      break;
+
+    case XML_ROLE_GROUP_CLOSE:
+      quant = XML_CQUANT_NONE;
+      goto closeGroup;
+    case XML_ROLE_GROUP_CLOSE_OPT:
+      quant = XML_CQUANT_OPT;
+      goto closeGroup;
+    case XML_ROLE_GROUP_CLOSE_REP:
+      quant = XML_CQUANT_REP;
+      goto closeGroup;
+    case XML_ROLE_GROUP_CLOSE_PLUS:
+      quant = XML_CQUANT_PLUS;
+    closeGroup:
+      if (dtd->in_eldecl) {
+        if (parser->m_elementDeclHandler)
+          handleDefault = XML_FALSE;
+        dtd->scaffLevel--;
+        dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant;
+        if (dtd->scaffLevel == 0) {
+          if (! handleDefault) {
+            XML_Content *model = build_model(parser);
+            if (! model)
+              return XML_ERROR_NO_MEMORY;
+            *eventEndPP = s;
+            parser->m_elementDeclHandler(
+                parser->m_handlerArg, parser->m_declElementType->name, model);
+          }
+          dtd->in_eldecl = XML_FALSE;
+          dtd->contentStringLen = 0;
+        }
+      }
+      break;
+      /* End element declaration stuff */
+
+    case XML_ROLE_PI:
+      if (! reportProcessingInstruction(parser, enc, s, next))
+        return XML_ERROR_NO_MEMORY;
+      handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_COMMENT:
+      if (! reportComment(parser, enc, s, next))
+        return XML_ERROR_NO_MEMORY;
+      handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_NONE:
+      switch (tok) {
+      case XML_TOK_BOM:
+        handleDefault = XML_FALSE;
+        break;
+      }
+      break;
+    case XML_ROLE_DOCTYPE_NONE:
+      if (parser->m_startDoctypeDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_ENTITY_NONE:
+      if (dtd->keepProcessing && parser->m_entityDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_NOTATION_NONE:
+      if (parser->m_notationDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_ATTLIST_NONE:
+      if (dtd->keepProcessing && parser->m_attlistDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_ELEMENT_NONE:
+      if (parser->m_elementDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    } /* end of big switch */
+
+    if (handleDefault && parser->m_defaultHandler)
+      reportDefault(parser, enc, s, next);
+
+    switch (parser->m_parsingStatus.parsing) {
+    case XML_SUSPENDED:
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_FINISHED:
+      return XML_ERROR_ABORTED;
+    default:
+      s = next;
+      tok = XmlPrologTok(enc, s, end, &next);
+    }
+  }
+  /* not reached */
+}
+
+static enum XML_Error PTRCALL
+epilogProcessor(XML_Parser parser, const char *s, const char *end,
+                const char **nextPtr) {
+  parser->m_processor = epilogProcessor;
+  parser->m_eventPtr = s;
+  for (;;) {
+    const char *next = NULL;
+    int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                  XML_ACCOUNT_DIRECT)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif
+    parser->m_eventEndPtr = next;
+    switch (tok) {
+    /* report partial linebreak - it might be the last token */
+    case -XML_TOK_PROLOG_S:
+      if (parser->m_defaultHandler) {
+        reportDefault(parser, parser->m_encoding, s, next);
+        if (parser->m_parsingStatus.parsing == XML_FINISHED)
+          return XML_ERROR_ABORTED;
+      }
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_TOK_NONE:
+      *nextPtr = s;
+      return XML_ERROR_NONE;
+    case XML_TOK_PROLOG_S:
+      if (parser->m_defaultHandler)
+        reportDefault(parser, parser->m_encoding, s, next);
+      break;
+    case XML_TOK_PI:
+      if (! reportProcessingInstruction(parser, parser->m_encoding, s, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_COMMENT:
+      if (! reportComment(parser, parser->m_encoding, s, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_INVALID:
+      parser->m_eventPtr = next;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL:
+      if (! parser->m_parsingStatus.finalBuffer) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_UNCLOSED_TOKEN;
+    case XML_TOK_PARTIAL_CHAR:
+      if (! parser->m_parsingStatus.finalBuffer) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_PARTIAL_CHAR;
+    default:
+      return XML_ERROR_JUNK_AFTER_DOC_ELEMENT;
+    }
+    parser->m_eventPtr = s = next;
+    switch (parser->m_parsingStatus.parsing) {
+    case XML_SUSPENDED:
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_FINISHED:
+      return XML_ERROR_ABORTED;
+    default:;
+    }
+  }
+}
+
+static enum XML_Error
+processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
+  const char *textStart, *textEnd;
+  const char *next;
+  enum XML_Error result;
+  OPEN_INTERNAL_ENTITY *openEntity;
+
+  if (parser->m_freeInternalEntities) {
+    openEntity = parser->m_freeInternalEntities;
+    parser->m_freeInternalEntities = openEntity->next;
+  } else {
+    openEntity
+        = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
+    if (! openEntity)
+      return XML_ERROR_NO_MEMORY;
+  }
+  entity->open = XML_TRUE;
+#ifdef XML_DTD
+  entityTrackingOnOpen(parser, entity, __LINE__);
+#endif
+  entity->processed = 0;
+  openEntity->next = parser->m_openInternalEntities;
+  parser->m_openInternalEntities = openEntity;
+  openEntity->entity = entity;
+  openEntity->startTagLevel = parser->m_tagLevel;
+  openEntity->betweenDecl = betweenDecl;
+  openEntity->internalEventPtr = NULL;
+  openEntity->internalEventEndPtr = NULL;
+  textStart = (const char *)entity->textPtr;
+  textEnd = (const char *)(entity->textPtr + entity->textLen);
+  /* Set a safe default value in case 'next' does not get set */
+  next = textStart;
+
+#ifdef XML_DTD
+  if (entity->is_param) {
+    int tok
+        = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
+    result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
+                      tok, next, &next, XML_FALSE, XML_FALSE,
+                      XML_ACCOUNT_ENTITY_EXPANSION);
+  } else
+#endif /* XML_DTD */
+    result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding,
+                       textStart, textEnd, &next, XML_FALSE,
+                       XML_ACCOUNT_ENTITY_EXPANSION);
+
+  if (result == XML_ERROR_NONE) {
+    if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
+      entity->processed = (int)(next - textStart);
+      parser->m_processor = internalEntityProcessor;
+    } else {
+#ifdef XML_DTD
+      entityTrackingOnClose(parser, entity, __LINE__);
+#endif /* XML_DTD */
+      entity->open = XML_FALSE;
+      parser->m_openInternalEntities = openEntity->next;
+      /* put openEntity back in list of free instances */
+      openEntity->next = parser->m_freeInternalEntities;
+      parser->m_freeInternalEntities = openEntity;
+    }
+  }
+  return result;
+}
+
+static enum XML_Error PTRCALL
+internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
+                        const char **nextPtr) {
+  ENTITY *entity;
+  const char *textStart, *textEnd;
+  const char *next;
+  enum XML_Error result;
+  OPEN_INTERNAL_ENTITY *openEntity = parser->m_openInternalEntities;
+  if (! openEntity)
+    return XML_ERROR_UNEXPECTED_STATE;
+
+  entity = openEntity->entity;
+  textStart = ((const char *)entity->textPtr) + entity->processed;
+  textEnd = (const char *)(entity->textPtr + entity->textLen);
+  /* Set a safe default value in case 'next' does not get set */
+  next = textStart;
+
+#ifdef XML_DTD
+  if (entity->is_param) {
+    int tok
+        = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
+    result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
+                      tok, next, &next, XML_FALSE, XML_TRUE,
+                      XML_ACCOUNT_ENTITY_EXPANSION);
+  } else
+#endif /* XML_DTD */
+    result = doContent(parser, openEntity->startTagLevel,
+                       parser->m_internalEncoding, textStart, textEnd, &next,
+                       XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);
+
+  if (result != XML_ERROR_NONE)
+    return result;
+
+  if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
+    entity->processed = (int)(next - (const char *)entity->textPtr);
+    return result;
+  }
+
+#ifdef XML_DTD
+  entityTrackingOnClose(parser, entity, __LINE__);
+#endif
+  entity->open = XML_FALSE;
+  parser->m_openInternalEntities = openEntity->next;
+  /* put openEntity back in list of free instances */
+  openEntity->next = parser->m_freeInternalEntities;
+  parser->m_freeInternalEntities = openEntity;
+
+  // If there are more open entities we want to stop right here and have the
+  // upcoming call to XML_ResumeParser continue with entity content, or it would
+  // be ignored altogether.
+  if (parser->m_openInternalEntities != NULL
+      && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
+    return XML_ERROR_NONE;
+  }
+
+#ifdef XML_DTD
+  if (entity->is_param) {
+    int tok;
+    parser->m_processor = prologProcessor;
+    tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+    return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
+                    (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
+                    XML_ACCOUNT_DIRECT);
+  } else
+#endif /* XML_DTD */
+  {
+    parser->m_processor = contentProcessor;
+    /* see externalEntityContentProcessor vs contentProcessor */
+    result = doContent(parser, parser->m_parentParser ? 1 : 0,
+                       parser->m_encoding, s, end, nextPtr,
+                       (XML_Bool)! parser->m_parsingStatus.finalBuffer,
+                       XML_ACCOUNT_DIRECT);
+    if (result == XML_ERROR_NONE) {
+      if (! storeRawNames(parser))
+        return XML_ERROR_NO_MEMORY;
+    }
+    return result;
+  }
+}
+
+static enum XML_Error PTRCALL
+errorProcessor(XML_Parser parser, const char *s, const char *end,
+               const char **nextPtr) {
+  UNUSED_P(s);
+  UNUSED_P(end);
+  UNUSED_P(nextPtr);
+  return parser->m_errorCode;
+}
+
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+                    const char *ptr, const char *end, STRING_POOL *pool,
+                    enum XML_Account account) {
+  enum XML_Error result
+      = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account);
+  if (result)
+    return result;
+  if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
+    poolChop(pool);
+  if (! poolAppendChar(pool, XML_T('\0')))
+    return XML_ERROR_NO_MEMORY;
+  return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+                     const char *ptr, const char *end, STRING_POOL *pool,
+                     enum XML_Account account) {
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+#ifndef XML_DTD
+  UNUSED_P(account);
+#endif
+
+  for (;;) {
+    const char *next
+        = ptr; /* XmlAttributeValueTok doesn't always set the last arg */
+    int tok = XmlAttributeValueTok(enc, ptr, end, &next);
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif
+    switch (tok) {
+    case XML_TOK_NONE:
+      return XML_ERROR_NONE;
+    case XML_TOK_INVALID:
+      if (enc == parser->m_encoding)
+        parser->m_eventPtr = next;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL:
+      if (enc == parser->m_encoding)
+        parser->m_eventPtr = ptr;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_CHAR_REF: {
+      XML_Char buf[XML_ENCODE_MAX];
+      int i;
+      int n = XmlCharRefNumber(enc, ptr);
+      if (n < 0) {
+        if (enc == parser->m_encoding)
+          parser->m_eventPtr = ptr;
+        return XML_ERROR_BAD_CHAR_REF;
+      }
+      if (! isCdata && n == 0x20 /* space */
+          && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+        break;
+      n = XmlEncode(n, (ICHAR *)buf);
+      /* The XmlEncode() functions can never return 0 here.  That
+       * error return happens if the code point passed in is either
+       * negative or greater than or equal to 0x110000.  The
+       * XmlCharRefNumber() functions will all return a number
+       * strictly less than 0x110000 or a negative value if an error
+       * occurred.  The negative value is intercepted above, so
+       * XmlEncode() is never passed a value it might return an
+       * error for.
+       */
+      for (i = 0; i < n; i++) {
+        if (! poolAppendChar(pool, buf[i]))
+          return XML_ERROR_NO_MEMORY;
+      }
+    } break;
+    case XML_TOK_DATA_CHARS:
+      if (! poolAppend(pool, enc, ptr, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_TRAILING_CR:
+      next = ptr + enc->minBytesPerChar;
+      /* fall through */
+    case XML_TOK_ATTRIBUTE_VALUE_S:
+    case XML_TOK_DATA_NEWLINE:
+      if (! isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+        break;
+      if (! poolAppendChar(pool, 0x20))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_ENTITY_REF: {
+      const XML_Char *name;
+      ENTITY *entity;
+      char checkEntityDecl;
+      XML_Char ch = (XML_Char)XmlPredefinedEntityName(
+          enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar);
+      if (ch) {
+#ifdef XML_DTD
+        /* NOTE: We are replacing 4-6 characters original input for 1 character
+         *       so there is no amplification and hence recording without
+         *       protection. */
+        accountingDiffTolerated(parser, tok, (char *)&ch,
+                                ((char *)&ch) + sizeof(XML_Char), __LINE__,
+                                XML_ACCOUNT_ENTITY_EXPANSION);
+#endif /* XML_DTD */
+        if (! poolAppendChar(pool, ch))
+          return XML_ERROR_NO_MEMORY;
+        break;
+      }
+      name = poolStoreString(&parser->m_temp2Pool, enc,
+                             ptr + enc->minBytesPerChar,
+                             next - enc->minBytesPerChar);
+      if (! name)
+        return XML_ERROR_NO_MEMORY;
+      entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0);
+      poolDiscard(&parser->m_temp2Pool);
+      /* First, determine if a check for an existing declaration is needed;
+         if yes, check that the entity exists, and that it is internal.
+      */
+      if (pool == &dtd->pool) /* are we called from prolog? */
+        checkEntityDecl =
+#ifdef XML_DTD
+            parser->m_prologState.documentEntity &&
+#endif /* XML_DTD */
+            (dtd->standalone ? ! parser->m_openInternalEntities
+                             : ! dtd->hasParamEntityRefs);
+      else /* if (pool == &parser->m_tempPool): we are called from content */
+        checkEntityDecl = ! dtd->hasParamEntityRefs || dtd->standalone;
+      if (checkEntityDecl) {
+        if (! entity)
+          return XML_ERROR_UNDEFINED_ENTITY;
+        else if (! entity->is_internal)
+          return XML_ERROR_ENTITY_DECLARED_IN_PE;
+      } else if (! entity) {
+        /* Cannot report skipped entity here - see comments on
+           parser->m_skippedEntityHandler.
+        if (parser->m_skippedEntityHandler)
+          parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0);
+        */
+        /* Cannot call the default handler because this would be
+           out of sync with the call to the startElementHandler.
+        if ((pool == &parser->m_tempPool) && parser->m_defaultHandler)
+          reportDefault(parser, enc, ptr, next);
+        */
+        break;
+      }
+      if (entity->open) {
+        if (enc == parser->m_encoding) {
+          /* It does not appear that this line can be executed.
+           *
+           * The "if (entity->open)" check catches recursive entity
+           * definitions.  In order to be called with an open
+           * entity, it must have gone through this code before and
+           * been through the recursive call to
+           * appendAttributeValue() some lines below.  That call
+           * sets the local encoding ("enc") to the parser's
+           * internal encoding (internal_utf8 or internal_utf16),
+           * which can never be the same as the principle encoding.
+           * It doesn't appear there is another code path that gets
+           * here with entity->open being TRUE.
+           *
+           * Since it is not certain that this logic is watertight,
+           * we keep the line and merely exclude it from coverage
+           * tests.
+           */
+          parser->m_eventPtr = ptr; /* LCOV_EXCL_LINE */
+        }
+        return XML_ERROR_RECURSIVE_ENTITY_REF;
+      }
+      if (entity->notation) {
+        if (enc == parser->m_encoding)
+          parser->m_eventPtr = ptr;
+        return XML_ERROR_BINARY_ENTITY_REF;
+      }
+      if (! entity->textPtr) {
+        if (enc == parser->m_encoding)
+          parser->m_eventPtr = ptr;
+        return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF;
+      } else {
+        enum XML_Error result;
+        const XML_Char *textEnd = entity->textPtr + entity->textLen;
+        entity->open = XML_TRUE;
+#ifdef XML_DTD
+        entityTrackingOnOpen(parser, entity, __LINE__);
+#endif
+        result = appendAttributeValue(parser, parser->m_internalEncoding,
+                                      isCdata, (const char *)entity->textPtr,
+                                      (const char *)textEnd, pool,
+                                      XML_ACCOUNT_ENTITY_EXPANSION);
+#ifdef XML_DTD
+        entityTrackingOnClose(parser, entity, __LINE__);
+#endif
+        entity->open = XML_FALSE;
+        if (result)
+          return result;
+      }
+    } break;
+    default:
+      /* The only token returned by XmlAttributeValueTok() that does
+       * not have an explicit case here is XML_TOK_PARTIAL_CHAR.
+       * Getting that would require an entity name to contain an
+       * incomplete XML character (e.g. \xE2\x82); however previous
+       * tokenisers will have already recognised and rejected such
+       * names before XmlAttributeValueTok() gets a look-in.  This
+       * default case should be retained as a safety net, but the code
+       * excluded from coverage tests.
+       *
+       * LCOV_EXCL_START
+       */
+      if (enc == parser->m_encoding)
+        parser->m_eventPtr = ptr;
+      return XML_ERROR_UNEXPECTED_STATE;
+      /* LCOV_EXCL_STOP */
+    }
+    ptr = next;
+  }
+  /* not reached */
+}
+
+static enum XML_Error
+storeEntityValue(XML_Parser parser, const ENCODING *enc,
+                 const char *entityTextPtr, const char *entityTextEnd,
+                 enum XML_Account account) {
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+  STRING_POOL *pool = &(dtd->entityValuePool);
+  enum XML_Error result = XML_ERROR_NONE;
+#ifdef XML_DTD
+  int oldInEntityValue = parser->m_prologState.inEntityValue;
+  parser->m_prologState.inEntityValue = 1;
+#else
+  UNUSED_P(account);
+#endif /* XML_DTD */
+  /* never return Null for the value argument in EntityDeclHandler,
+     since this would indicate an external entity; therefore we
+     have to make sure that entityValuePool.start is not null */
+  if (! pool->blocks) {
+    if (! poolGrow(pool))
+      return XML_ERROR_NO_MEMORY;
+  }
+
+  for (;;) {
+    const char *next
+        = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */
+    int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
+
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__,
+                                  account)) {
+      accountingOnAbort(parser);
+      result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+      goto endEntityValue;
+    }
+#endif
+
+    switch (tok) {
+    case XML_TOK_PARAM_ENTITY_REF:
+#ifdef XML_DTD
+      if (parser->m_isParamEntity || enc != parser->m_encoding) {
+        const XML_Char *name;
+        ENTITY *entity;
+        name = poolStoreString(&parser->m_tempPool, enc,
+                               entityTextPtr + enc->minBytesPerChar,
+                               next - enc->minBytesPerChar);
+        if (! name) {
+          result = XML_ERROR_NO_MEMORY;
+          goto endEntityValue;
+        }
+        entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0);
+        poolDiscard(&parser->m_tempPool);
+        if (! entity) {
+          /* not a well-formedness error - see XML 1.0: WFC Entity Declared */
+          /* cannot report skipped entity here - see comments on
+             parser->m_skippedEntityHandler
+          if (parser->m_skippedEntityHandler)
+            parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0);
+          */
+          dtd->keepProcessing = dtd->standalone;
+          goto endEntityValue;
+        }
+        if (entity->open) {
+          if (enc == parser->m_encoding)
+            parser->m_eventPtr = entityTextPtr;
+          result = XML_ERROR_RECURSIVE_ENTITY_REF;
+          goto endEntityValue;
+        }
+        if (entity->systemId) {
+          if (parser->m_externalEntityRefHandler) {
+            dtd->paramEntityRead = XML_FALSE;
+            entity->open = XML_TRUE;
+            entityTrackingOnOpen(parser, entity, __LINE__);
+            if (! parser->m_externalEntityRefHandler(
+                    parser->m_externalEntityRefHandlerArg, 0, entity->base,
+                    entity->systemId, entity->publicId)) {
+              entityTrackingOnClose(parser, entity, __LINE__);
+              entity->open = XML_FALSE;
+              result = XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+              goto endEntityValue;
+            }
+            entityTrackingOnClose(parser, entity, __LINE__);
+            entity->open = XML_FALSE;
+            if (! dtd->paramEntityRead)
+              dtd->keepProcessing = dtd->standalone;
+          } else
+            dtd->keepProcessing = dtd->standalone;
+        } else {
+          entity->open = XML_TRUE;
+          entityTrackingOnOpen(parser, entity, __LINE__);
+          result = storeEntityValue(
+              parser, parser->m_internalEncoding, (const char *)entity->textPtr,
+              (const char *)(entity->textPtr + entity->textLen),
+              XML_ACCOUNT_ENTITY_EXPANSION);
+          entityTrackingOnClose(parser, entity, __LINE__);
+          entity->open = XML_FALSE;
+          if (result)
+            goto endEntityValue;
+        }
+        break;
+      }
+#endif /* XML_DTD */
+      /* In the internal subset, PE references are not legal
+         within markup declarations, e.g entity values in this case. */
+      parser->m_eventPtr = entityTextPtr;
+      result = XML_ERROR_PARAM_ENTITY_REF;
+      goto endEntityValue;
+    case XML_TOK_NONE:
+      result = XML_ERROR_NONE;
+      goto endEntityValue;
+    case XML_TOK_ENTITY_REF:
+    case XML_TOK_DATA_CHARS:
+      if (! poolAppend(pool, enc, entityTextPtr, next)) {
+        result = XML_ERROR_NO_MEMORY;
+        goto endEntityValue;
+      }
+      break;
+    case XML_TOK_TRAILING_CR:
+      next = entityTextPtr + enc->minBytesPerChar;
+      /* fall through */
+    case XML_TOK_DATA_NEWLINE:
+      if (pool->end == pool->ptr && ! poolGrow(pool)) {
+        result = XML_ERROR_NO_MEMORY;
+        goto endEntityValue;
+      }
+      *(pool->ptr)++ = 0xA;
+      break;
+    case XML_TOK_CHAR_REF: {
+      XML_Char buf[XML_ENCODE_MAX];
+      int i;
+      int n = XmlCharRefNumber(enc, entityTextPtr);
+      if (n < 0) {
+        if (enc == parser->m_encoding)
+          parser->m_eventPtr = entityTextPtr;
+        result = XML_ERROR_BAD_CHAR_REF;
+        goto endEntityValue;
+      }
+      n = XmlEncode(n, (ICHAR *)buf);
+      /* The XmlEncode() functions can never return 0 here.  That
+       * error return happens if the code point passed in is either
+       * negative or greater than or equal to 0x110000.  The
+       * XmlCharRefNumber() functions will all return a number
+       * strictly less than 0x110000 or a negative value if an error
+       * occurred.  The negative value is intercepted above, so
+       * XmlEncode() is never passed a value it might return an
+       * error for.
+       */
+      for (i = 0; i < n; i++) {
+        if (pool->end == pool->ptr && ! poolGrow(pool)) {
+          result = XML_ERROR_NO_MEMORY;
+          goto endEntityValue;
+        }
+        *(pool->ptr)++ = buf[i];
+      }
+    } break;
+    case XML_TOK_PARTIAL:
+      if (enc == parser->m_encoding)
+        parser->m_eventPtr = entityTextPtr;
+      result = XML_ERROR_INVALID_TOKEN;
+      goto endEntityValue;
+    case XML_TOK_INVALID:
+      if (enc == parser->m_encoding)
+        parser->m_eventPtr = next;
+      result = XML_ERROR_INVALID_TOKEN;
+      goto endEntityValue;
+    default:
+      /* This default case should be unnecessary -- all the tokens
+       * that XmlEntityValueTok() can return have their own explicit
+       * cases -- but should be retained for safety.  We do however
+       * exclude it from the coverage statistics.
+       *
+       * LCOV_EXCL_START
+       */
+      if (enc == parser->m_encoding)
+        parser->m_eventPtr = entityTextPtr;
+      result = XML_ERROR_UNEXPECTED_STATE;
+      goto endEntityValue;
+      /* LCOV_EXCL_STOP */
+    }
+    entityTextPtr = next;
+  }
+endEntityValue:
+#ifdef XML_DTD
+  parser->m_prologState.inEntityValue = oldInEntityValue;
+#endif /* XML_DTD */
+  return result;
+}
+
+static void FASTCALL
+normalizeLines(XML_Char *s) {
+  XML_Char *p;
+  for (;; s++) {
+    if (*s == XML_T('\0'))
+      return;
+    if (*s == 0xD)
+      break;
+  }
+  p = s;
+  do {
+    if (*s == 0xD) {
+      *p++ = 0xA;
+      if (*++s == 0xA)
+        s++;
+    } else
+      *p++ = *s++;
+  } while (*s);
+  *p = XML_T('\0');
+}
+
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
+                            const char *start, const char *end) {
+  const XML_Char *target;
+  XML_Char *data;
+  const char *tem;
+  if (! parser->m_processingInstructionHandler) {
+    if (parser->m_defaultHandler)
+      reportDefault(parser, enc, start, end);
+    return 1;
+  }
+  start += enc->minBytesPerChar * 2;
+  tem = start + XmlNameLength(enc, start);
+  target = poolStoreString(&parser->m_tempPool, enc, start, tem);
+  if (! target)
+    return 0;
+  poolFinish(&parser->m_tempPool);
+  data = poolStoreString(&parser->m_tempPool, enc, XmlSkipS(enc, tem),
+                         end - enc->minBytesPerChar * 2);
+  if (! data)
+    return 0;
+  normalizeLines(data);
+  parser->m_processingInstructionHandler(parser->m_handlerArg, target, data);
+  poolClear(&parser->m_tempPool);
+  return 1;
+}
+
+static int
+reportComment(XML_Parser parser, const ENCODING *enc, const char *start,
+              const char *end) {
+  XML_Char *data;
+  if (! parser->m_commentHandler) {
+    if (parser->m_defaultHandler)
+      reportDefault(parser, enc, start, end);
+    return 1;
+  }
+  data = poolStoreString(&parser->m_tempPool, enc,
+                         start + enc->minBytesPerChar * 4,
+                         end - enc->minBytesPerChar * 3);
+  if (! data)
+    return 0;
+  normalizeLines(data);
+  parser->m_commentHandler(parser->m_handlerArg, data);
+  poolClear(&parser->m_tempPool);
+  return 1;
+}
+
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc, const char *s,
+              const char *end) {
+  if (MUST_CONVERT(enc, s)) {
+    enum XML_Convert_Result convert_res;
+    const char **eventPP;
+    const char **eventEndPP;
+    if (enc == parser->m_encoding) {
+      eventPP = &parser->m_eventPtr;
+      eventEndPP = &parser->m_eventEndPtr;
+    } else {
+      /* To get here, two things must be true; the parser must be
+       * using a character encoding that is not the same as the
+       * encoding passed in, and the encoding passed in must need
+       * conversion to the internal format (UTF-8 unless XML_UNICODE
+       * is defined).  The only occasions on which the encoding passed
+       * in is not the same as the parser's encoding are when it is
+       * the internal encoding (e.g. a previously defined parameter
+       * entity, already converted to internal format).  This by
+       * definition doesn't need conversion, so the whole branch never
+       * gets executed.
+       *
+       * For safety's sake we don't delete these lines and merely
+       * exclude them from coverage statistics.
+       *
+       * LCOV_EXCL_START
+       */
+      eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+      eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
+      /* LCOV_EXCL_STOP */
+    }
+    do {
+      ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf;
+      convert_res
+          = XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
+      *eventEndPP = s;
+      parser->m_defaultHandler(parser->m_handlerArg, parser->m_dataBuf,
+                               (int)(dataPtr - (ICHAR *)parser->m_dataBuf));
+      *eventPP = s;
+    } while ((convert_res != XML_CONVERT_COMPLETED)
+             && (convert_res != XML_CONVERT_INPUT_INCOMPLETE));
+  } else
+    parser->m_defaultHandler(parser->m_handlerArg, (XML_Char *)s,
+                             (int)((XML_Char *)end - (XML_Char *)s));
+}
+
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
+                XML_Bool isId, const XML_Char *value, XML_Parser parser) {
+  DEFAULT_ATTRIBUTE *att;
+  if (value || isId) {
+    /* The handling of default attributes gets messed up if we have
+       a default which duplicates a non-default. */
+    int i;
+    for (i = 0; i < type->nDefaultAtts; i++)
+      if (attId == type->defaultAtts[i].id)
+        return 1;
+    if (isId && ! type->idAtt && ! attId->xmlns)
+      type->idAtt = attId;
+  }
+  if (type->nDefaultAtts == type->allocDefaultAtts) {
+    if (type->allocDefaultAtts == 0) {
+      type->allocDefaultAtts = 8;
+      type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(
+          parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+      if (! type->defaultAtts) {
+        type->allocDefaultAtts = 0;
+        return 0;
+      }
+    } else {
+      DEFAULT_ATTRIBUTE *temp;
+
+      /* Detect and prevent integer overflow */
+      if (type->allocDefaultAtts > INT_MAX / 2) {
+        return 0;
+      }
+
+      int count = type->allocDefaultAtts * 2;
+
+      /* Detect and prevent integer overflow.
+       * The preprocessor guard addresses the "always false" warning
+       * from -Wtype-limits on platforms where
+       * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+      if ((unsigned)count > (size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE)) {
+        return 0;
+      }
+#endif
+
+      temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts,
+                                          (count * sizeof(DEFAULT_ATTRIBUTE)));
+      if (temp == NULL)
+        return 0;
+      type->allocDefaultAtts = count;
+      type->defaultAtts = temp;
+    }
+  }
+  att = type->defaultAtts + type->nDefaultAtts;
+  att->id = attId;
+  att->value = value;
+  att->isCdata = isCdata;
+  if (! isCdata)
+    attId->maybeTokenized = XML_TRUE;
+  type->nDefaultAtts += 1;
+  return 1;
+}
+
+static int
+setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) {
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+  const XML_Char *name;
+  for (name = elementType->name; *name; name++) {
+    if (*name == XML_T(ASCII_COLON)) {
+      PREFIX *prefix;
+      const XML_Char *s;
+      for (s = elementType->name; s != name; s++) {
+        if (! poolAppendChar(&dtd->pool, *s))
+          return 0;
+      }
+      if (! poolAppendChar(&dtd->pool, XML_T('\0')))
+        return 0;
+      prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool),
+                                sizeof(PREFIX));
+      if (! prefix)
+        return 0;
+      if (prefix->name == poolStart(&dtd->pool))
+        poolFinish(&dtd->pool);
+      else
+        poolDiscard(&dtd->pool);
+      elementType->prefix = prefix;
+      break;
+    }
+  }
+  return 1;
+}
+
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start,
+               const char *end) {
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+  ATTRIBUTE_ID *id;
+  const XML_Char *name;
+  if (! poolAppendChar(&dtd->pool, XML_T('\0')))
+    return NULL;
+  name = poolStoreString(&dtd->pool, enc, start, end);
+  if (! name)
+    return NULL;
+  /* skip quotation mark - its storage will be re-used (like in name[-1]) */
+  ++name;
+  id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name,
+                              sizeof(ATTRIBUTE_ID));
+  if (! id)
+    return NULL;
+  if (id->name != name)
+    poolDiscard(&dtd->pool);
+  else {
+    poolFinish(&dtd->pool);
+    if (! parser->m_ns)
+      ;
+    else if (name[0] == XML_T(ASCII_x) && name[1] == XML_T(ASCII_m)
+             && name[2] == XML_T(ASCII_l) && name[3] == XML_T(ASCII_n)
+             && name[4] == XML_T(ASCII_s)
+             && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) {
+      if (name[5] == XML_T('\0'))
+        id->prefix = &dtd->defaultPrefix;
+      else
+        id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6,
+                                      sizeof(PREFIX));
+      id->xmlns = XML_TRUE;
+    } else {
+      int i;
+      for (i = 0; name[i]; i++) {
+        /* attributes without prefix are *not* in the default namespace */
+        if (name[i] == XML_T(ASCII_COLON)) {
+          int j;
+          for (j = 0; j < i; j++) {
+            if (! poolAppendChar(&dtd->pool, name[j]))
+              return NULL;
+          }
+          if (! poolAppendChar(&dtd->pool, XML_T('\0')))
+            return NULL;
+          id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes,
+                                        poolStart(&dtd->pool), sizeof(PREFIX));
+          if (! id->prefix)
+            return NULL;
+          if (id->prefix->name == poolStart(&dtd->pool))
+            poolFinish(&dtd->pool);
+          else
+            poolDiscard(&dtd->pool);
+          break;
+        }
+      }
+    }
+  }
+  return id;
+}
+
+#define CONTEXT_SEP XML_T(ASCII_FF)
+
+static const XML_Char *
+getContext(XML_Parser parser) {
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+  HASH_TABLE_ITER iter;
+  XML_Bool needSep = XML_FALSE;
+
+  if (dtd->defaultPrefix.binding) {
+    int i;
+    int len;
+    if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS)))
+      return NULL;
+    len = dtd->defaultPrefix.binding->uriLen;
+    if (parser->m_namespaceSeparator)
+      len--;
+    for (i = 0; i < len; i++) {
+      if (! poolAppendChar(&parser->m_tempPool,
+                           dtd->defaultPrefix.binding->uri[i])) {
+        /* Because of memory caching, I don't believe this line can be
+         * executed.
+         *
+         * This is part of a loop copying the default prefix binding
+         * URI into the parser's temporary string pool.  Previously,
+         * that URI was copied into the same string pool, with a
+         * terminating NUL character, as part of setContext().  When
+         * the pool was cleared, that leaves a block definitely big
+         * enough to hold the URI on the free block list of the pool.
+         * The URI copy in getContext() therefore cannot run out of
+         * memory.
+         *
+         * If the pool is used between the setContext() and
+         * getContext() calls, the worst it can do is leave a bigger
+         * block on the front of the free list.  Given that this is
+         * all somewhat inobvious and program logic can be changed, we
+         * don't delete the line but we do exclude it from the test
+         * coverage statistics.
+         */
+        return NULL; /* LCOV_EXCL_LINE */
+      }
+    }
+    needSep = XML_TRUE;
+  }
+
+  hashTableIterInit(&iter, &(dtd->prefixes));
+  for (;;) {
+    int i;
+    int len;
+    const XML_Char *s;
+    PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter);
+    if (! prefix)
+      break;
+    if (! prefix->binding) {
+      /* This test appears to be (justifiable) paranoia.  There does
+       * not seem to be a way of injecting a prefix without a binding
+       * that doesn't get errored long before this function is called.
+       * The test should remain for safety's sake, so we instead
+       * exclude the following line from the coverage statistics.
+       */
+      continue; /* LCOV_EXCL_LINE */
+    }
+    if (needSep && ! poolAppendChar(&parser->m_tempPool, CONTEXT_SEP))
+      return NULL;
+    for (s = prefix->name; *s; s++)
+      if (! poolAppendChar(&parser->m_tempPool, *s))
+        return NULL;
+    if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS)))
+      return NULL;
+    len = prefix->binding->uriLen;
+    if (parser->m_namespaceSeparator)
+      len--;
+    for (i = 0; i < len; i++)
+      if (! poolAppendChar(&parser->m_tempPool, prefix->binding->uri[i]))
+        return NULL;
+    needSep = XML_TRUE;
+  }
+
+  hashTableIterInit(&iter, &(dtd->generalEntities));
+  for (;;) {
+    const XML_Char *s;
+    ENTITY *e = (ENTITY *)hashTableIterNext(&iter);
+    if (! e)
+      break;
+    if (! e->open)
+      continue;
+    if (needSep && ! poolAppendChar(&parser->m_tempPool, CONTEXT_SEP))
+      return NULL;
+    for (s = e->name; *s; s++)
+      if (! poolAppendChar(&parser->m_tempPool, *s))
+        return 0;
+    needSep = XML_TRUE;
+  }
+
+  if (! poolAppendChar(&parser->m_tempPool, XML_T('\0')))
+    return NULL;
+  return parser->m_tempPool.start;
+}
+
+static XML_Bool
+setContext(XML_Parser parser, const XML_Char *context) {
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+  const XML_Char *s = context;
+
+  while (*context != XML_T('\0')) {
+    if (*s == CONTEXT_SEP || *s == XML_T('\0')) {
+      ENTITY *e;
+      if (! poolAppendChar(&parser->m_tempPool, XML_T('\0')))
+        return XML_FALSE;
+      e = (ENTITY *)lookup(parser, &dtd->generalEntities,
+                           poolStart(&parser->m_tempPool), 0);
+      if (e)
+        e->open = XML_TRUE;
+      if (*s != XML_T('\0'))
+        s++;
+      context = s;
+      poolDiscard(&parser->m_tempPool);
+    } else if (*s == XML_T(ASCII_EQUALS)) {
+      PREFIX *prefix;
+      if (poolLength(&parser->m_tempPool) == 0)
+        prefix = &dtd->defaultPrefix;
+      else {
+        if (! poolAppendChar(&parser->m_tempPool, XML_T('\0')))
+          return XML_FALSE;
+        prefix
+            = (PREFIX *)lookup(parser, &dtd->prefixes,
+                               poolStart(&parser->m_tempPool), sizeof(PREFIX));
+        if (! prefix)
+          return XML_FALSE;
+        if (prefix->name == poolStart(&parser->m_tempPool)) {
+          prefix->name = poolCopyString(&dtd->pool, prefix->name);
+          if (! prefix->name)
+            return XML_FALSE;
+        }
+        poolDiscard(&parser->m_tempPool);
+      }
+      for (context = s + 1; *context != CONTEXT_SEP && *context != XML_T('\0');
+           context++)
+        if (! poolAppendChar(&parser->m_tempPool, *context))
+          return XML_FALSE;
+      if (! poolAppendChar(&parser->m_tempPool, XML_T('\0')))
+        return XML_FALSE;
+      if (addBinding(parser, prefix, NULL, poolStart(&parser->m_tempPool),
+                     &parser->m_inheritedBindings)
+          != XML_ERROR_NONE)
+        return XML_FALSE;
+      poolDiscard(&parser->m_tempPool);
+      if (*context != XML_T('\0'))
+        ++context;
+      s = context;
+    } else {
+      if (! poolAppendChar(&parser->m_tempPool, *s))
+        return XML_FALSE;
+      s++;
+    }
+  }
+  return XML_TRUE;
+}
+
+static void FASTCALL
+normalizePublicId(XML_Char *publicId) {
+  XML_Char *p = publicId;
+  XML_Char *s;
+  for (s = publicId; *s; s++) {
+    switch (*s) {
+    case 0x20:
+    case 0xD:
+    case 0xA:
+      if (p != publicId && p[-1] != 0x20)
+        *p++ = 0x20;
+      break;
+    default:
+      *p++ = *s;
+    }
+  }
+  if (p != publicId && p[-1] == 0x20)
+    --p;
+  *p = XML_T('\0');
+}
+
+static DTD *
+dtdCreate(const XML_Memory_Handling_Suite *ms) {
+  DTD *p = ms->malloc_fcn(sizeof(DTD));
+  if (p == NULL)
+    return p;
+  poolInit(&(p->pool), ms);
+  poolInit(&(p->entityValuePool), ms);
+  hashTableInit(&(p->generalEntities), ms);
+  hashTableInit(&(p->elementTypes), ms);
+  hashTableInit(&(p->attributeIds), ms);
+  hashTableInit(&(p->prefixes), ms);
+#ifdef XML_DTD
+  p->paramEntityRead = XML_FALSE;
+  hashTableInit(&(p->paramEntities), ms);
+#endif /* XML_DTD */
+  p->defaultPrefix.name = NULL;
+  p->defaultPrefix.binding = NULL;
+
+  p->in_eldecl = XML_FALSE;
+  p->scaffIndex = NULL;
+  p->scaffold = NULL;
+  p->scaffLevel = 0;
+  p->scaffSize = 0;
+  p->scaffCount = 0;
+  p->contentStringLen = 0;
+
+  p->keepProcessing = XML_TRUE;
+  p->hasParamEntityRefs = XML_FALSE;
+  p->standalone = XML_FALSE;
+  return p;
+}
+
+static void
+dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
+  HASH_TABLE_ITER iter;
+  hashTableIterInit(&iter, &(p->elementTypes));
+  for (;;) {
+    ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+    if (! e)
+      break;
+    if (e->allocDefaultAtts != 0)
+      ms->free_fcn(e->defaultAtts);
+  }
+  hashTableClear(&(p->generalEntities));
+#ifdef XML_DTD
+  p->paramEntityRead = XML_FALSE;
+  hashTableClear(&(p->paramEntities));
+#endif /* XML_DTD */
+  hashTableClear(&(p->elementTypes));
+  hashTableClear(&(p->attributeIds));
+  hashTableClear(&(p->prefixes));
+  poolClear(&(p->pool));
+  poolClear(&(p->entityValuePool));
+  p->defaultPrefix.name = NULL;
+  p->defaultPrefix.binding = NULL;
+
+  p->in_eldecl = XML_FALSE;
+
+  ms->free_fcn(p->scaffIndex);
+  p->scaffIndex = NULL;
+  ms->free_fcn(p->scaffold);
+  p->scaffold = NULL;
+
+  p->scaffLevel = 0;
+  p->scaffSize = 0;
+  p->scaffCount = 0;
+  p->contentStringLen = 0;
+
+  p->keepProcessing = XML_TRUE;
+  p->hasParamEntityRefs = XML_FALSE;
+  p->standalone = XML_FALSE;
+}
+
+static void
+dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
+  HASH_TABLE_ITER iter;
+  hashTableIterInit(&iter, &(p->elementTypes));
+  for (;;) {
+    ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+    if (! e)
+      break;
+    if (e->allocDefaultAtts != 0)
+      ms->free_fcn(e->defaultAtts);
+  }
+  hashTableDestroy(&(p->generalEntities));
+#ifdef XML_DTD
+  hashTableDestroy(&(p->paramEntities));
+#endif /* XML_DTD */
+  hashTableDestroy(&(p->elementTypes));
+  hashTableDestroy(&(p->attributeIds));
+  hashTableDestroy(&(p->prefixes));
+  poolDestroy(&(p->pool));
+  poolDestroy(&(p->entityValuePool));
+  if (isDocEntity) {
+    ms->free_fcn(p->scaffIndex);
+    ms->free_fcn(p->scaffold);
+  }
+  ms->free_fcn(p);
+}
+
+/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise.
+   The new DTD has already been initialized.
+*/
+static int
+dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
+        const XML_Memory_Handling_Suite *ms) {
+  HASH_TABLE_ITER iter;
+
+  /* Copy the prefix table. */
+
+  hashTableIterInit(&iter, &(oldDtd->prefixes));
+  for (;;) {
+    const XML_Char *name;
+    const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter);
+    if (! oldP)
+      break;
+    name = poolCopyString(&(newDtd->pool), oldP->name);
+    if (! name)
+      return 0;
+    if (! lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX)))
+      return 0;
+  }
+
+  hashTableIterInit(&iter, &(oldDtd->attributeIds));
+
+  /* Copy the attribute id table. */
+
+  for (;;) {
+    ATTRIBUTE_ID *newA;
+    const XML_Char *name;
+    const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter);
+
+    if (! oldA)
+      break;
+    /* Remember to allocate the scratch byte before the name. */
+    if (! poolAppendChar(&(newDtd->pool), XML_T('\0')))
+      return 0;
+    name = poolCopyString(&(newDtd->pool), oldA->name);
+    if (! name)
+      return 0;
+    ++name;
+    newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name,
+                                  sizeof(ATTRIBUTE_ID));
+    if (! newA)
+      return 0;
+    newA->maybeTokenized = oldA->maybeTokenized;
+    if (oldA->prefix) {
+      newA->xmlns = oldA->xmlns;
+      if (oldA->prefix == &oldDtd->defaultPrefix)
+        newA->prefix = &newDtd->defaultPrefix;
+      else
+        newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes),
+                                        oldA->prefix->name, 0);
+    }
+  }
+
+  /* Copy the element type table. */
+
+  hashTableIterInit(&iter, &(oldDtd->elementTypes));
+
+  for (;;) {
+    int i;
+    ELEMENT_TYPE *newE;
+    const XML_Char *name;
+    const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+    if (! oldE)
+      break;
+    name = poolCopyString(&(newDtd->pool), oldE->name);
+    if (! name)
+      return 0;
+    newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name,
+                                  sizeof(ELEMENT_TYPE));
+    if (! newE)
+      return 0;
+    if (oldE->nDefaultAtts) {
+      newE->defaultAtts
+          = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+      if (! newE->defaultAtts) {
+        return 0;
+      }
+    }
+    if (oldE->idAtt)
+      newE->idAtt = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds),
+                                           oldE->idAtt->name, 0);
+    newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts;
+    if (oldE->prefix)
+      newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes),
+                                      oldE->prefix->name, 0);
+    for (i = 0; i < newE->nDefaultAtts; i++) {
+      newE->defaultAtts[i].id = (ATTRIBUTE_ID *)lookup(
+          oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0);
+      newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata;
+      if (oldE->defaultAtts[i].value) {
+        newE->defaultAtts[i].value
+            = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value);
+        if (! newE->defaultAtts[i].value)
+          return 0;
+      } else
+        newE->defaultAtts[i].value = NULL;
+    }
+  }
+
+  /* Copy the entity tables. */
+  if (! copyEntityTable(oldParser, &(newDtd->generalEntities), &(newDtd->pool),
+                        &(oldDtd->generalEntities)))
+    return 0;
+
+#ifdef XML_DTD
+  if (! copyEntityTable(oldParser, &(newDtd->paramEntities), &(newDtd->pool),
+                        &(oldDtd->paramEntities)))
+    return 0;
+  newDtd->paramEntityRead = oldDtd->paramEntityRead;
+#endif /* XML_DTD */
+
+  newDtd->keepProcessing = oldDtd->keepProcessing;
+  newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs;
+  newDtd->standalone = oldDtd->standalone;
+
+  /* Don't want deep copying for scaffolding */
+  newDtd->in_eldecl = oldDtd->in_eldecl;
+  newDtd->scaffold = oldDtd->scaffold;
+  newDtd->contentStringLen = oldDtd->contentStringLen;
+  newDtd->scaffSize = oldDtd->scaffSize;
+  newDtd->scaffLevel = oldDtd->scaffLevel;
+  newDtd->scaffIndex = oldDtd->scaffIndex;
+
+  return 1;
+} /* End dtdCopy */
+
+static int
+copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
+                STRING_POOL *newPool, const HASH_TABLE *oldTable) {
+  HASH_TABLE_ITER iter;
+  const XML_Char *cachedOldBase = NULL;
+  const XML_Char *cachedNewBase = NULL;
+
+  hashTableIterInit(&iter, oldTable);
+
+  for (;;) {
+    ENTITY *newE;
+    const XML_Char *name;
+    const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter);
+    if (! oldE)
+      break;
+    name = poolCopyString(newPool, oldE->name);
+    if (! name)
+      return 0;
+    newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY));
+    if (! newE)
+      return 0;
+    if (oldE->systemId) {
+      const XML_Char *tem = poolCopyString(newPool, oldE->systemId);
+      if (! tem)
+        return 0;
+      newE->systemId = tem;
+      if (oldE->base) {
+        if (oldE->base == cachedOldBase)
+          newE->base = cachedNewBase;
+        else {
+          cachedOldBase = oldE->base;
+          tem = poolCopyString(newPool, cachedOldBase);
+          if (! tem)
+            return 0;
+          cachedNewBase = newE->base = tem;
+        }
+      }
+      if (oldE->publicId) {
+        tem = poolCopyString(newPool, oldE->publicId);
+        if (! tem)
+          return 0;
+        newE->publicId = tem;
+      }
+    } else {
+      const XML_Char *tem
+          = poolCopyStringN(newPool, oldE->textPtr, oldE->textLen);
+      if (! tem)
+        return 0;
+      newE->textPtr = tem;
+      newE->textLen = oldE->textLen;
+    }
+    if (oldE->notation) {
+      const XML_Char *tem = poolCopyString(newPool, oldE->notation);
+      if (! tem)
+        return 0;
+      newE->notation = tem;
+    }
+    newE->is_param = oldE->is_param;
+    newE->is_internal = oldE->is_internal;
+  }
+  return 1;
+}
+
+#define INIT_POWER 6
+
+static XML_Bool FASTCALL
+keyeq(KEY s1, KEY s2) {
+  for (; *s1 == *s2; s1++, s2++)
+    if (*s1 == 0)
+      return XML_TRUE;
+  return XML_FALSE;
+}
+
+static size_t
+keylen(KEY s) {
+  size_t len = 0;
+  for (; *s; s++, len++)
+    ;
+  return len;
+}
+
+static void
+copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key) {
+  key->k[0] = 0;
+  key->k[1] = get_hash_secret_salt(parser);
+}
+
+static unsigned long FASTCALL
+hash(XML_Parser parser, KEY s) {
+  struct siphash state;
+  struct sipkey key;
+  (void)sip24_valid;
+  copy_salt_to_sipkey(parser, &key);
+  sip24_init(&state, &key);
+  sip24_update(&state, s, keylen(s) * sizeof(XML_Char));
+  return (unsigned long)sip24_final(&state);
+}
+
+static NAMED *
+lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
+  size_t i;
+  if (table->size == 0) {
+    size_t tsize;
+    if (! createSize)
+      return NULL;
+    table->power = INIT_POWER;
+    /* table->size is a power of 2 */
+    table->size = (size_t)1 << INIT_POWER;
+    tsize = table->size * sizeof(NAMED *);
+    table->v = table->mem->malloc_fcn(tsize);
+    if (! table->v) {
+      table->size = 0;
+      return NULL;
+    }
+    memset(table->v, 0, tsize);
+    i = hash(parser, name) & ((unsigned long)table->size - 1);
+  } else {
+    unsigned long h = hash(parser, name);
+    unsigned long mask = (unsigned long)table->size - 1;
+    unsigned char step = 0;
+    i = h & mask;
+    while (table->v[i]) {
+      if (keyeq(name, table->v[i]->name))
+        return table->v[i];
+      if (! step)
+        step = PROBE_STEP(h, mask, table->power);
+      i < step ? (i += table->size - step) : (i -= step);
+    }
+    if (! createSize)
+      return NULL;
+
+    /* check for overflow (table is half full) */
+    if (table->used >> (table->power - 1)) {
+      unsigned char newPower = table->power + 1;
+
+      /* Detect and prevent invalid shift */
+      if (newPower >= sizeof(unsigned long) * 8 /* bits per byte */) {
+        return NULL;
+      }
+
+      size_t newSize = (size_t)1 << newPower;
+      unsigned long newMask = (unsigned long)newSize - 1;
+
+      /* Detect and prevent integer overflow */
+      if (newSize > (size_t)(-1) / sizeof(NAMED *)) {
+        return NULL;
+      }
+
+      size_t tsize = newSize * sizeof(NAMED *);
+      NAMED **newV = table->mem->malloc_fcn(tsize);
+      if (! newV)
+        return NULL;
+      memset(newV, 0, tsize);
+      for (i = 0; i < table->size; i++)
+        if (table->v[i]) {
+          unsigned long newHash = hash(parser, table->v[i]->name);
+          size_t j = newHash & newMask;
+          step = 0;
+          while (newV[j]) {
+            if (! step)
+              step = PROBE_STEP(newHash, newMask, newPower);
+            j < step ? (j += newSize - step) : (j -= step);
+          }
+          newV[j] = table->v[i];
+        }
+      table->mem->free_fcn(table->v);
+      table->v = newV;
+      table->power = newPower;
+      table->size = newSize;
+      i = h & newMask;
+      step = 0;
+      while (table->v[i]) {
+        if (! step)
+          step = PROBE_STEP(h, newMask, newPower);
+        i < step ? (i += newSize - step) : (i -= step);
+      }
+    }
+  }
+  table->v[i] = table->mem->malloc_fcn(createSize);
+  if (! table->v[i])
+    return NULL;
+  memset(table->v[i], 0, createSize);
+  table->v[i]->name = name;
+  (table->used)++;
+  return table->v[i];
+}
+
+static void FASTCALL
+hashTableClear(HASH_TABLE *table) {
+  size_t i;
+  for (i = 0; i < table->size; i++) {
+    table->mem->free_fcn(table->v[i]);
+    table->v[i] = NULL;
+  }
+  table->used = 0;
+}
+
+static void FASTCALL
+hashTableDestroy(HASH_TABLE *table) {
+  size_t i;
+  for (i = 0; i < table->size; i++)
+    table->mem->free_fcn(table->v[i]);
+  table->mem->free_fcn(table->v);
+}
+
+static void FASTCALL
+hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) {
+  p->power = 0;
+  p->size = 0;
+  p->used = 0;
+  p->v = NULL;
+  p->mem = ms;
+}
+
+static void FASTCALL
+hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) {
+  iter->p = table->v;
+  iter->end = iter->p ? iter->p + table->size : NULL;
+}
+
+static NAMED *FASTCALL
+hashTableIterNext(HASH_TABLE_ITER *iter) {
+  while (iter->p != iter->end) {
+    NAMED *tem = *(iter->p)++;
+    if (tem)
+      return tem;
+  }
+  return NULL;
+}
+
+static void FASTCALL
+poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) {
+  pool->blocks = NULL;
+  pool->freeBlocks = NULL;
+  pool->start = NULL;
+  pool->ptr = NULL;
+  pool->end = NULL;
+  pool->mem = ms;
+}
+
+static void FASTCALL
+poolClear(STRING_POOL *pool) {
+  if (! pool->freeBlocks)
+    pool->freeBlocks = pool->blocks;
+  else {
+    BLOCK *p = pool->blocks;
+    while (p) {
+      BLOCK *tem = p->next;
+      p->next = pool->freeBlocks;
+      pool->freeBlocks = p;
+      p = tem;
+    }
+  }
+  pool->blocks = NULL;
+  pool->start = NULL;
+  pool->ptr = NULL;
+  pool->end = NULL;
+}
+
+static void FASTCALL
+poolDestroy(STRING_POOL *pool) {
+  BLOCK *p = pool->blocks;
+  while (p) {
+    BLOCK *tem = p->next;
+    pool->mem->free_fcn(p);
+    p = tem;
+  }
+  p = pool->freeBlocks;
+  while (p) {
+    BLOCK *tem = p->next;
+    pool->mem->free_fcn(p);
+    p = tem;
+  }
+}
+
+static XML_Char *
+poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr,
+           const char *end) {
+  if (! pool->ptr && ! poolGrow(pool))
+    return NULL;
+  for (;;) {
+    const enum XML_Convert_Result convert_res = XmlConvert(
+        enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end);
+    if ((convert_res == XML_CONVERT_COMPLETED)
+        || (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
+      break;
+    if (! poolGrow(pool))
+      return NULL;
+  }
+  return pool->start;
+}
+
+static const XML_Char *FASTCALL
+poolCopyString(STRING_POOL *pool, const XML_Char *s) {
+  do {
+    if (! poolAppendChar(pool, *s))
+      return NULL;
+  } while (*s++);
+  s = pool->start;
+  poolFinish(pool);
+  return s;
+}
+
+static const XML_Char *
+poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) {
+  if (! pool->ptr && ! poolGrow(pool)) {
+    /* The following line is unreachable given the current usage of
+     * poolCopyStringN().  Currently it is called from exactly one
+     * place to copy the text of a simple general entity.  By that
+     * point, the name of the entity is already stored in the pool, so
+     * pool->ptr cannot be NULL.
+     *
+     * If poolCopyStringN() is used elsewhere as it well might be,
+     * this line may well become executable again.  Regardless, this
+     * sort of check shouldn't be removed lightly, so we just exclude
+     * it from the coverage statistics.
+     */
+    return NULL; /* LCOV_EXCL_LINE */
+  }
+  for (; n > 0; --n, s++) {
+    if (! poolAppendChar(pool, *s))
+      return NULL;
+  }
+  s = pool->start;
+  poolFinish(pool);
+  return s;
+}
+
+static const XML_Char *FASTCALL
+poolAppendString(STRING_POOL *pool, const XML_Char *s) {
+  while (*s) {
+    if (! poolAppendChar(pool, *s))
+      return NULL;
+    s++;
+  }
+  return pool->start;
+}
+
+static XML_Char *
+poolStoreString(STRING_POOL *pool, const ENCODING *enc, const char *ptr,
+                const char *end) {
+  if (! poolAppend(pool, enc, ptr, end))
+    return NULL;
+  if (pool->ptr == pool->end && ! poolGrow(pool))
+    return NULL;
+  *(pool->ptr)++ = 0;
+  return pool->start;
+}
+
+static size_t
+poolBytesToAllocateFor(int blockSize) {
+  /* Unprotected math would be:
+  ** return offsetof(BLOCK, s) + blockSize * sizeof(XML_Char);
+  **
+  ** Detect overflow, avoiding _signed_ overflow undefined behavior
+  ** For a + b * c we check b * c in isolation first, so that addition of a
+  ** on top has no chance of making us accept a small non-negative number
+  */
+  const size_t stretch = sizeof(XML_Char); /* can be 4 bytes */
+
+  if (blockSize <= 0)
+    return 0;
+
+  if (blockSize > (int)(INT_MAX / stretch))
+    return 0;
+
+  {
+    const int stretchedBlockSize = blockSize * (int)stretch;
+    const int bytesToAllocate
+        = (int)(offsetof(BLOCK, s) + (unsigned)stretchedBlockSize);
+    if (bytesToAllocate < 0)
+      return 0;
+
+    return (size_t)bytesToAllocate;
+  }
+}
+
+static XML_Bool FASTCALL
+poolGrow(STRING_POOL *pool) {
+  if (pool->freeBlocks) {
+    if (pool->start == 0) {
+      pool->blocks = pool->freeBlocks;
+      pool->freeBlocks = pool->freeBlocks->next;
+      pool->blocks->next = NULL;
+      pool->start = pool->blocks->s;
+      pool->end = pool->start + pool->blocks->size;
+      pool->ptr = pool->start;
+      return XML_TRUE;
+    }
+    if (pool->end - pool->start < pool->freeBlocks->size) {
+      BLOCK *tem = pool->freeBlocks->next;
+      pool->freeBlocks->next = pool->blocks;
+      pool->blocks = pool->freeBlocks;
+      pool->freeBlocks = tem;
+      memcpy(pool->blocks->s, pool->start,
+             (pool->end - pool->start) * sizeof(XML_Char));
+      pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+      pool->start = pool->blocks->s;
+      pool->end = pool->start + pool->blocks->size;
+      return XML_TRUE;
+    }
+  }
+  if (pool->blocks && pool->start == pool->blocks->s) {
+    BLOCK *temp;
+    int blockSize = (int)((unsigned)(pool->end - pool->start) * 2U);
+    size_t bytesToAllocate;
+
+    /* NOTE: Needs to be calculated prior to calling `realloc`
+             to avoid dangling pointers: */
+    const ptrdiff_t offsetInsideBlock = pool->ptr - pool->start;
+
+    if (blockSize < 0) {
+      /* This condition traps a situation where either more than
+       * INT_MAX/2 bytes have already been allocated.  This isn't
+       * readily testable, since it is unlikely that an average
+       * machine will have that much memory, so we exclude it from the
+       * coverage statistics.
+       */
+      return XML_FALSE; /* LCOV_EXCL_LINE */
+    }
+
+    bytesToAllocate = poolBytesToAllocateFor(blockSize);
+    if (bytesToAllocate == 0)
+      return XML_FALSE;
+
+    temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks,
+                                           (unsigned)bytesToAllocate);
+    if (temp == NULL)
+      return XML_FALSE;
+    pool->blocks = temp;
+    pool->blocks->size = blockSize;
+    pool->ptr = pool->blocks->s + offsetInsideBlock;
+    pool->start = pool->blocks->s;
+    pool->end = pool->start + blockSize;
+  } else {
+    BLOCK *tem;
+    int blockSize = (int)(pool->end - pool->start);
+    size_t bytesToAllocate;
+
+    if (blockSize < 0) {
+      /* This condition traps a situation where either more than
+       * INT_MAX bytes have already been allocated (which is prevented
+       * by various pieces of program logic, not least this one, never
+       * mind the unlikelihood of actually having that much memory) or
+       * the pool control fields have been corrupted (which could
+       * conceivably happen in an extremely buggy user handler
+       * function).  Either way it isn't readily testable, so we
+       * exclude it from the coverage statistics.
+       */
+      return XML_FALSE; /* LCOV_EXCL_LINE */
+    }
+
+    if (blockSize < INIT_BLOCK_SIZE)
+      blockSize = INIT_BLOCK_SIZE;
+    else {
+      /* Detect overflow, avoiding _signed_ overflow undefined behavior */
+      if ((int)((unsigned)blockSize * 2U) < 0) {
+        return XML_FALSE;
+      }
+      blockSize *= 2;
+    }
+
+    bytesToAllocate = poolBytesToAllocateFor(blockSize);
+    if (bytesToAllocate == 0)
+      return XML_FALSE;
+
+    tem = pool->mem->malloc_fcn(bytesToAllocate);
+    if (! tem)
+      return XML_FALSE;
+    tem->size = blockSize;
+    tem->next = pool->blocks;
+    pool->blocks = tem;
+    if (pool->ptr != pool->start)
+      memcpy(tem->s, pool->start, (pool->ptr - pool->start) * sizeof(XML_Char));
+    pool->ptr = tem->s + (pool->ptr - pool->start);
+    pool->start = tem->s;
+    pool->end = tem->s + blockSize;
+  }
+  return XML_TRUE;
+}
+
+static int FASTCALL
+nextScaffoldPart(XML_Parser parser) {
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+  CONTENT_SCAFFOLD *me;
+  int next;
+
+  if (! dtd->scaffIndex) {
+    dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
+    if (! dtd->scaffIndex)
+      return -1;
+    dtd->scaffIndex[0] = 0;
+  }
+
+  if (dtd->scaffCount >= dtd->scaffSize) {
+    CONTENT_SCAFFOLD *temp;
+    if (dtd->scaffold) {
+      /* Detect and prevent integer overflow */
+      if (dtd->scaffSize > UINT_MAX / 2u) {
+        return -1;
+      }
+      /* Detect and prevent integer overflow.
+       * The preprocessor guard addresses the "always false" warning
+       * from -Wtype-limits on platforms where
+       * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+      if (dtd->scaffSize > (size_t)(-1) / 2u / sizeof(CONTENT_SCAFFOLD)) {
+        return -1;
+      }
+#endif
+
+      temp = (CONTENT_SCAFFOLD *)REALLOC(
+          parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
+      if (temp == NULL)
+        return -1;
+      dtd->scaffSize *= 2;
+    } else {
+      temp = (CONTENT_SCAFFOLD *)MALLOC(parser, INIT_SCAFFOLD_ELEMENTS
+                                                    * sizeof(CONTENT_SCAFFOLD));
+      if (temp == NULL)
+        return -1;
+      dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS;
+    }
+    dtd->scaffold = temp;
+  }
+  next = dtd->scaffCount++;
+  me = &dtd->scaffold[next];
+  if (dtd->scaffLevel) {
+    CONTENT_SCAFFOLD *parent
+        = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]];
+    if (parent->lastchild) {
+      dtd->scaffold[parent->lastchild].nextsib = next;
+    }
+    if (! parent->childcnt)
+      parent->firstchild = next;
+    parent->lastchild = next;
+    parent->childcnt++;
+  }
+  me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0;
+  return next;
+}
+
+static XML_Content *
+build_model(XML_Parser parser) {
+  /* Function build_model transforms the existing parser->m_dtd->scaffold
+   * array of CONTENT_SCAFFOLD tree nodes into a new array of
+   * XML_Content tree nodes followed by a gapless list of zero-terminated
+   * strings. */
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+  XML_Content *ret;
+  XML_Char *str; /* the current string writing location */
+
+  /* Detect and prevent integer overflow.
+   * The preprocessor guard addresses the "always false" warning
+   * from -Wtype-limits on platforms where
+   * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+  if (dtd->scaffCount > (size_t)(-1) / sizeof(XML_Content)) {
+    return NULL;
+  }
+  if (dtd->contentStringLen > (size_t)(-1) / sizeof(XML_Char)) {
+    return NULL;
+  }
+#endif
+  if (dtd->scaffCount * sizeof(XML_Content)
+      > (size_t)(-1) - dtd->contentStringLen * sizeof(XML_Char)) {
+    return NULL;
+  }
+
+  const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content)
+                            + (dtd->contentStringLen * sizeof(XML_Char)));
+
+  ret = (XML_Content *)MALLOC(parser, allocsize);
+  if (! ret)
+    return NULL;
+
+  /* What follows is an iterative implementation (of what was previously done
+   * recursively in a dedicated function called "build_node".  The old recursive
+   * build_node could be forced into stack exhaustion from input as small as a
+   * few megabyte, and so that was a security issue.  Hence, a function call
+   * stack is avoided now by resolving recursion.)
+   *
+   * The iterative approach works as follows:
+   *
+   * - We have two writing pointers, both walking up the result array; one does
+   *   the work, the other creates "jobs" for its colleague to do, and leads
+   *   the way:
+   *
+   *   - The faster one, pointer jobDest, always leads and writes "what job
+   *     to do" by the other, once they reach that place in the
+   *     array: leader "jobDest" stores the source node array index (relative
+   *     to array dtd->scaffold) in field "numchildren".
+   *
+   *   - The slower one, pointer dest, looks at the value stored in the
+   *     "numchildren" field (which actually holds a source node array index
+   *     at that time) and puts the real data from dtd->scaffold in.
+   *
+   * - Before the loop starts, jobDest writes source array index 0
+   *   (where the root node is located) so that dest will have something to do
+   *   when it starts operation.
+   *
+   * - Whenever nodes with children are encountered, jobDest appends
+   *   them as new jobs, in order.  As a result, tree node siblings are
+   *   adjacent in the resulting array, for example:
+   *
+   *     [0] root, has two children
+   *       [1] first child of 0, has three children
+   *         [3] first child of 1, does not have children
+   *         [4] second child of 1, does not have children
+   *         [5] third child of 1, does not have children
+   *       [2] second child of 0, does not have children
+   *
+   *   Or (the same data) presented in flat array view:
+   *
+   *     [0] root, has two children
+   *
+   *     [1] first child of 0, has three children
+   *     [2] second child of 0, does not have children
+   *
+   *     [3] first child of 1, does not have children
+   *     [4] second child of 1, does not have children
+   *     [5] third child of 1, does not have children
+   *
+   * - The algorithm repeats until all target array indices have been processed.
+   */
+  XML_Content *dest = ret; /* tree node writing location, moves upwards */
+  XML_Content *const destLimit = &ret[dtd->scaffCount];
+  XML_Content *jobDest = ret; /* next free writing location in target array */
+  str = (XML_Char *)&ret[dtd->scaffCount];
+
+  /* Add the starting job, the root node (index 0) of the source tree  */
+  (jobDest++)->numchildren = 0;
+
+  for (; dest < destLimit; dest++) {
+    /* Retrieve source tree array index from job storage */
+    const int src_node = (int)dest->numchildren;
+
+    /* Convert item */
+    dest->type = dtd->scaffold[src_node].type;
+    dest->quant = dtd->scaffold[src_node].quant;
+    if (dest->type == XML_CTYPE_NAME) {
+      const XML_Char *src;
+      dest->name = str;
+      src = dtd->scaffold[src_node].name;
+      for (;;) {
+        *str++ = *src;
+        if (! *src)
+          break;
+        src++;
+      }
+      dest->numchildren = 0;
+      dest->children = NULL;
+    } else {
+      unsigned int i;
+      int cn;
+      dest->name = NULL;
+      dest->numchildren = dtd->scaffold[src_node].childcnt;
+      dest->children = jobDest;
+
+      /* Append scaffold indices of children to array */
+      for (i = 0, cn = dtd->scaffold[src_node].firstchild;
+           i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib)
+        (jobDest++)->numchildren = (unsigned int)cn;
+    }
+  }
+
+  return ret;
+}
+
+static ELEMENT_TYPE *
+getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr,
+               const char *end) {
+  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+  const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end);
+  ELEMENT_TYPE *ret;
+
+  if (! name)
+    return NULL;
+  ret = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name,
+                               sizeof(ELEMENT_TYPE));
+  if (! ret)
+    return NULL;
+  if (ret->name != name)
+    poolDiscard(&dtd->pool);
+  else {
+    poolFinish(&dtd->pool);
+    if (! setElementTypePrefix(parser, ret))
+      return NULL;
+  }
+  return ret;
+}
+
+static XML_Char *
+copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
+  size_t charsRequired = 0;
+  XML_Char *result;
+
+  /* First determine how long the string is */
+  while (s[charsRequired] != 0) {
+    charsRequired++;
+  }
+  /* Include the terminator */
+  charsRequired++;
+
+  /* Now allocate space for the copy */
+  result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char));
+  if (result == NULL)
+    return NULL;
+  /* Copy the original into place */
+  memcpy(result, s, charsRequired * sizeof(XML_Char));
+  return result;
+}
+
+#ifdef XML_DTD
+
+static float
+accountingGetCurrentAmplification(XML_Parser rootParser) {
+  const XmlBigCount countBytesOutput
+      = rootParser->m_accounting.countBytesDirect
+        + rootParser->m_accounting.countBytesIndirect;
+  const float amplificationFactor
+      = rootParser->m_accounting.countBytesDirect
+            ? (countBytesOutput
+               / (float)(rootParser->m_accounting.countBytesDirect))
+            : 1.0f;
+  assert(! rootParser->m_parentParser);
+  return amplificationFactor;
+}
+
+static void
+accountingReportStats(XML_Parser originParser, const char *epilog) {
+  const XML_Parser rootParser = getRootParserOf(originParser, NULL);
+  assert(! rootParser->m_parentParser);
+
+  if (rootParser->m_accounting.debugLevel < 1) {
+    return;
+  }
+
+  const float amplificationFactor
+      = accountingGetCurrentAmplification(rootParser);
+  fprintf(stderr,
+          "expat: Accounting(%p): Direct " EXPAT_FMT_ULL(
+              "10") ", indirect " EXPAT_FMT_ULL("10") ", amplification %8.2f%s",
+          (void *)rootParser, rootParser->m_accounting.countBytesDirect,
+          rootParser->m_accounting.countBytesIndirect,
+          (double)amplificationFactor, epilog);
+}
+
+static void
+accountingOnAbort(XML_Parser originParser) {
+  accountingReportStats(originParser, " ABORTING\n");
+}
+
+static void
+accountingReportDiff(XML_Parser rootParser,
+                     unsigned int levelsAwayFromRootParser, const char *before,
+                     const char *after, ptrdiff_t bytesMore, int source_line,
+                     enum XML_Account account) {
+  assert(! rootParser->m_parentParser);
+
+  fprintf(stderr,
+          " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"",
+          bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP",
+          levelsAwayFromRootParser, source_line, 10, "");
+
+  const char ellipis[] = "[..]";
+  const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
+  const unsigned int contextLength = 10;
+
+  /* Note: Performance is of no concern here */
+  const char *walker = before;
+  if ((rootParser->m_accounting.debugLevel >= 3)
+      || (after - before)
+             <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) {
+    for (; walker < after; walker++) {
+      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
+    }
+  } else {
+    for (; walker < before + contextLength; walker++) {
+      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
+    }
+    fprintf(stderr, ellipis);
+    walker = after - contextLength;
+    for (; walker < after; walker++) {
+      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
+    }
+  }
+  fprintf(stderr, "\"\n");
+}
+
+static XML_Bool
+accountingDiffTolerated(XML_Parser originParser, int tok, const char *before,
+                        const char *after, int source_line,
+                        enum XML_Account account) {
+  /* Note: We need to check the token type *first* to be sure that
+   *       we can even access variable <after>, safely.
+   *       E.g. for XML_TOK_NONE <after> may hold an invalid pointer. */
+  switch (tok) {
+  case XML_TOK_INVALID:
+  case XML_TOK_PARTIAL:
+  case XML_TOK_PARTIAL_CHAR:
+  case XML_TOK_NONE:
+    return XML_TRUE;
+  }
+
+  if (account == XML_ACCOUNT_NONE)
+    return XML_TRUE; /* because these bytes have been accounted for, already */
+
+  unsigned int levelsAwayFromRootParser;
+  const XML_Parser rootParser
+      = getRootParserOf(originParser, &levelsAwayFromRootParser);
+  assert(! rootParser->m_parentParser);
+
+  const int isDirect
+      = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser);
+  const ptrdiff_t bytesMore = after - before;
+
+  XmlBigCount *const additionTarget
+      = isDirect ? &rootParser->m_accounting.countBytesDirect
+                 : &rootParser->m_accounting.countBytesIndirect;
+
+  /* Detect and avoid integer overflow */
+  if (*additionTarget > (XmlBigCount)(-1) - (XmlBigCount)bytesMore)
+    return XML_FALSE;
+  *additionTarget += bytesMore;
+
+  const XmlBigCount countBytesOutput
+      = rootParser->m_accounting.countBytesDirect
+        + rootParser->m_accounting.countBytesIndirect;
+  const float amplificationFactor
+      = accountingGetCurrentAmplification(rootParser);
+  const XML_Bool tolerated
+      = (countBytesOutput < rootParser->m_accounting.activationThresholdBytes)
+        || (amplificationFactor
+            <= rootParser->m_accounting.maximumAmplificationFactor);
+
+  if (rootParser->m_accounting.debugLevel >= 2) {
+    accountingReportStats(rootParser, "");
+    accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after,
+                         bytesMore, source_line, account);
+  }
+
+  return tolerated;
+}
+
+unsigned long long
+testingAccountingGetCountBytesDirect(XML_Parser parser) {
+  if (! parser)
+    return 0;
+  return parser->m_accounting.countBytesDirect;
+}
+
+unsigned long long
+testingAccountingGetCountBytesIndirect(XML_Parser parser) {
+  if (! parser)
+    return 0;
+  return parser->m_accounting.countBytesIndirect;
+}
+
+static void
+entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity,
+                          const char *action, int sourceLine) {
+  assert(! rootParser->m_parentParser);
+  if (rootParser->m_entity_stats.debugLevel < 1)
+    return;
+
+#  if defined(XML_UNICODE)
+  const char *const entityName = "[..]";
+#  else
+  const char *const entityName = entity->name;
+#  endif
+
+  fprintf(
+      stderr,
+      "expat: Entities(%p): Count %9d, depth %2d/%2d %*s%s%s; %s length %d (xmlparse.c:%d)\n",
+      (void *)rootParser, rootParser->m_entity_stats.countEverOpened,
+      rootParser->m_entity_stats.currentDepth,
+      rootParser->m_entity_stats.maximumDepthSeen,
+      (rootParser->m_entity_stats.currentDepth - 1) * 2, "",
+      entity->is_param ? "%" : "&", entityName, action, entity->textLen,
+      sourceLine);
+}
+
+static void
+entityTrackingOnOpen(XML_Parser originParser, ENTITY *entity, int sourceLine) {
+  const XML_Parser rootParser = getRootParserOf(originParser, NULL);
+  assert(! rootParser->m_parentParser);
+
+  rootParser->m_entity_stats.countEverOpened++;
+  rootParser->m_entity_stats.currentDepth++;
+  if (rootParser->m_entity_stats.currentDepth
+      > rootParser->m_entity_stats.maximumDepthSeen) {
+    rootParser->m_entity_stats.maximumDepthSeen++;
+  }
+
+  entityTrackingReportStats(rootParser, entity, "OPEN ", sourceLine);
+}
+
+static void
+entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) {
+  const XML_Parser rootParser = getRootParserOf(originParser, NULL);
+  assert(! rootParser->m_parentParser);
+
+  entityTrackingReportStats(rootParser, entity, "CLOSE", sourceLine);
+  rootParser->m_entity_stats.currentDepth--;
+}
+
+static XML_Parser
+getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) {
+  XML_Parser rootParser = parser;
+  unsigned int stepsTakenUpwards = 0;
+  while (rootParser->m_parentParser) {
+    rootParser = rootParser->m_parentParser;
+    stepsTakenUpwards++;
+  }
+  assert(! rootParser->m_parentParser);
+  if (outLevelDiff != NULL) {
+    *outLevelDiff = stepsTakenUpwards;
+  }
+  return rootParser;
+}
+
+const char *
+unsignedCharToPrintable(unsigned char c) {
+  switch (c) {
+  case 0:
+    return "\\0";
+  case 1:
+    return "\\x1";
+  case 2:
+    return "\\x2";
+  case 3:
+    return "\\x3";
+  case 4:
+    return "\\x4";
+  case 5:
+    return "\\x5";
+  case 6:
+    return "\\x6";
+  case 7:
+    return "\\x7";
+  case 8:
+    return "\\x8";
+  case 9:
+    return "\\t";
+  case 10:
+    return "\\n";
+  case 11:
+    return "\\xB";
+  case 12:
+    return "\\xC";
+  case 13:
+    return "\\r";
+  case 14:
+    return "\\xE";
+  case 15:
+    return "\\xF";
+  case 16:
+    return "\\x10";
+  case 17:
+    return "\\x11";
+  case 18:
+    return "\\x12";
+  case 19:
+    return "\\x13";
+  case 20:
+    return "\\x14";
+  case 21:
+    return "\\x15";
+  case 22:
+    return "\\x16";
+  case 23:
+    return "\\x17";
+  case 24:
+    return "\\x18";
+  case 25:
+    return "\\x19";
+  case 26:
+    return "\\x1A";
+  case 27:
+    return "\\x1B";
+  case 28:
+    return "\\x1C";
+  case 29:
+    return "\\x1D";
+  case 30:
+    return "\\x1E";
+  case 31:
+    return "\\x1F";
+  case 32:
+    return " ";
+  case 33:
+    return "!";
+  case 34:
+    return "\\\"";
+  case 35:
+    return "#";
+  case 36:
+    return "$";
+  case 37:
+    return "%";
+  case 38:
+    return "&";
+  case 39:
+    return "'";
+  case 40:
+    return "(";
+  case 41:
+    return ")";
+  case 42:
+    return "*";
+  case 43:
+    return "+";
+  case 44:
+    return ",";
+  case 45:
+    return "-";
+  case 46:
+    return ".";
+  case 47:
+    return "/";
+  case 48:
+    return "0";
+  case 49:
+    return "1";
+  case 50:
+    return "2";
+  case 51:
+    return "3";
+  case 52:
+    return "4";
+  case 53:
+    return "5";
+  case 54:
+    return "6";
+  case 55:
+    return "7";
+  case 56:
+    return "8";
+  case 57:
+    return "9";
+  case 58:
+    return ":";
+  case 59:
+    return ";";
+  case 60:
+    return "<";
+  case 61:
+    return "=";
+  case 62:
+    return ">";
+  case 63:
+    return "?";
+  case 64:
+    return "@";
+  case 65:
+    return "A";
+  case 66:
+    return "B";
+  case 67:
+    return "C";
+  case 68:
+    return "D";
+  case 69:
+    return "E";
+  case 70:
+    return "F";
+  case 71:
+    return "G";
+  case 72:
+    return "H";
+  case 73:
+    return "I";
+  case 74:
+    return "J";
+  case 75:
+    return "K";
+  case 76:
+    return "L";
+  case 77:
+    return "M";
+  case 78:
+    return "N";
+  case 79:
+    return "O";
+  case 80:
+    return "P";
+  case 81:
+    return "Q";
+  case 82:
+    return "R";
+  case 83:
+    return "S";
+  case 84:
+    return "T";
+  case 85:
+    return "U";
+  case 86:
+    return "V";
+  case 87:
+    return "W";
+  case 88:
+    return "X";
+  case 89:
+    return "Y";
+  case 90:
+    return "Z";
+  case 91:
+    return "[";
+  case 92:
+    return "\\\\";
+  case 93:
+    return "]";
+  case 94:
+    return "^";
+  case 95:
+    return "_";
+  case 96:
+    return "`";
+  case 97:
+    return "a";
+  case 98:
+    return "b";
+  case 99:
+    return "c";
+  case 100:
+    return "d";
+  case 101:
+    return "e";
+  case 102:
+    return "f";
+  case 103:
+    return "g";
+  case 104:
+    return "h";
+  case 105:
+    return "i";
+  case 106:
+    return "j";
+  case 107:
+    return "k";
+  case 108:
+    return "l";
+  case 109:
+    return "m";
+  case 110:
+    return "n";
+  case 111:
+    return "o";
+  case 112:
+    return "p";
+  case 113:
+    return "q";
+  case 114:
+    return "r";
+  case 115:
+    return "s";
+  case 116:
+    return "t";
+  case 117:
+    return "u";
+  case 118:
+    return "v";
+  case 119:
+    return "w";
+  case 120:
+    return "x";
+  case 121:
+    return "y";
+  case 122:
+    return "z";
+  case 123:
+    return "{";
+  case 124:
+    return "|";
+  case 125:
+    return "}";
+  case 126:
+    return "~";
+  case 127:
+    return "\\x7F";
+  case 128:
+    return "\\x80";
+  case 129:
+    return "\\x81";
+  case 130:
+    return "\\x82";
+  case 131:
+    return "\\x83";
+  case 132:
+    return "\\x84";
+  case 133:
+    return "\\x85";
+  case 134:
+    return "\\x86";
+  case 135:
+    return "\\x87";
+  case 136:
+    return "\\x88";
+  case 137:
+    return "\\x89";
+  case 138:
+    return "\\x8A";
+  case 139:
+    return "\\x8B";
+  case 140:
+    return "\\x8C";
+  case 141:
+    return "\\x8D";
+  case 142:
+    return "\\x8E";
+  case 143:
+    return "\\x8F";
+  case 144:
+    return "\\x90";
+  case 145:
+    return "\\x91";
+  case 146:
+    return "\\x92";
+  case 147:
+    return "\\x93";
+  case 148:
+    return "\\x94";
+  case 149:
+    return "\\x95";
+  case 150:
+    return "\\x96";
+  case 151:
+    return "\\x97";
+  case 152:
+    return "\\x98";
+  case 153:
+    return "\\x99";
+  case 154:
+    return "\\x9A";
+  case 155:
+    return "\\x9B";
+  case 156:
+    return "\\x9C";
+  case 157:
+    return "\\x9D";
+  case 158:
+    return "\\x9E";
+  case 159:
+    return "\\x9F";
+  case 160:
+    return "\\xA0";
+  case 161:
+    return "\\xA1";
+  case 162:
+    return "\\xA2";
+  case 163:
+    return "\\xA3";
+  case 164:
+    return "\\xA4";
+  case 165:
+    return "\\xA5";
+  case 166:
+    return "\\xA6";
+  case 167:
+    return "\\xA7";
+  case 168:
+    return "\\xA8";
+  case 169:
+    return "\\xA9";
+  case 170:
+    return "\\xAA";
+  case 171:
+    return "\\xAB";
+  case 172:
+    return "\\xAC";
+  case 173:
+    return "\\xAD";
+  case 174:
+    return "\\xAE";
+  case 175:
+    return "\\xAF";
+  case 176:
+    return "\\xB0";
+  case 177:
+    return "\\xB1";
+  case 178:
+    return "\\xB2";
+  case 179:
+    return "\\xB3";
+  case 180:
+    return "\\xB4";
+  case 181:
+    return "\\xB5";
+  case 182:
+    return "\\xB6";
+  case 183:
+    return "\\xB7";
+  case 184:
+    return "\\xB8";
+  case 185:
+    return "\\xB9";
+  case 186:
+    return "\\xBA";
+  case 187:
+    return "\\xBB";
+  case 188:
+    return "\\xBC";
+  case 189:
+    return "\\xBD";
+  case 190:
+    return "\\xBE";
+  case 191:
+    return "\\xBF";
+  case 192:
+    return "\\xC0";
+  case 193:
+    return "\\xC1";
+  case 194:
+    return "\\xC2";
+  case 195:
+    return "\\xC3";
+  case 196:
+    return "\\xC4";
+  case 197:
+    return "\\xC5";
+  case 198:
+    return "\\xC6";
+  case 199:
+    return "\\xC7";
+  case 200:
+    return "\\xC8";
+  case 201:
+    return "\\xC9";
+  case 202:
+    return "\\xCA";
+  case 203:
+    return "\\xCB";
+  case 204:
+    return "\\xCC";
+  case 205:
+    return "\\xCD";
+  case 206:
+    return "\\xCE";
+  case 207:
+    return "\\xCF";
+  case 208:
+    return "\\xD0";
+  case 209:
+    return "\\xD1";
+  case 210:
+    return "\\xD2";
+  case 211:
+    return "\\xD3";
+  case 212:
+    return "\\xD4";
+  case 213:
+    return "\\xD5";
+  case 214:
+    return "\\xD6";
+  case 215:
+    return "\\xD7";
+  case 216:
+    return "\\xD8";
+  case 217:
+    return "\\xD9";
+  case 218:
+    return "\\xDA";
+  case 219:
+    return "\\xDB";
+  case 220:
+    return "\\xDC";
+  case 221:
+    return "\\xDD";
+  case 222:
+    return "\\xDE";
+  case 223:
+    return "\\xDF";
+  case 224:
+    return "\\xE0";
+  case 225:
+    return "\\xE1";
+  case 226:
+    return "\\xE2";
+  case 227:
+    return "\\xE3";
+  case 228:
+    return "\\xE4";
+  case 229:
+    return "\\xE5";
+  case 230:
+    return "\\xE6";
+  case 231:
+    return "\\xE7";
+  case 232:
+    return "\\xE8";
+  case 233:
+    return "\\xE9";
+  case 234:
+    return "\\xEA";
+  case 235:
+    return "\\xEB";
+  case 236:
+    return "\\xEC";
+  case 237:
+    return "\\xED";
+  case 238:
+    return "\\xEE";
+  case 239:
+    return "\\xEF";
+  case 240:
+    return "\\xF0";
+  case 241:
+    return "\\xF1";
+  case 242:
+    return "\\xF2";
+  case 243:
+    return "\\xF3";
+  case 244:
+    return "\\xF4";
+  case 245:
+    return "\\xF5";
+  case 246:
+    return "\\xF6";
+  case 247:
+    return "\\xF7";
+  case 248:
+    return "\\xF8";
+  case 249:
+    return "\\xF9";
+  case 250:
+    return "\\xFA";
+  case 251:
+    return "\\xFB";
+  case 252:
+    return "\\xFC";
+  case 253:
+    return "\\xFD";
+  case 254:
+    return "\\xFE";
+  case 255:
+    return "\\xFF";
+  default:
+    assert(0); /* never gets here */
+    return "dead code";
+  }
+  assert(0); /* never gets here */
+}
+
+#endif /* XML_DTD */
+
+static unsigned long
+getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) {
+  const char *const valueOrNull = getenv(variableName);
+  if (valueOrNull == NULL) {
+    return defaultDebugLevel;
+  }
+  const char *const value = valueOrNull;
+
+  errno = 0;
+  char *afterValue = (char *)value;
+  unsigned long debugLevel = strtoul(value, &afterValue, 10);
+  if ((errno != 0) || (afterValue[0] != '\0')) {
+    errno = 0;
+    return defaultDebugLevel;
+  }
+
+  return debugLevel;
+}
diff --git a/dummy_fe/libexpat/xmlrole.c b/dummy_fe/libexpat/xmlrole.c
new file mode 100644
index 0000000..3f0f5c1
--- /dev/null
+++ b/dummy_fe/libexpat/xmlrole.c
@@ -0,0 +1,1255 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2005-2009 Steven Solie <steven@solie.ca>
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2021      Dong-hee Na <donghee.na@python.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <expat_config.h>
+
+#include <stddef.h>
+
+#ifdef _WIN32
+#  include "winconfig.h"
+#endif
+
+#include "expat_external.h"
+#include "internal.h"
+#include "xmlrole.h"
+#include "ascii.h"
+
+/* Doesn't check:
+
+ that ,| are not mixed in a model group
+ content of literals
+
+*/
+
+static const char KW_ANY[] = {ASCII_A, ASCII_N, ASCII_Y, '\0'};
+static const char KW_ATTLIST[]
+    = {ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0'};
+static const char KW_CDATA[]
+    = {ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0'};
+static const char KW_DOCTYPE[]
+    = {ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0'};
+static const char KW_ELEMENT[]
+    = {ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0'};
+static const char KW_EMPTY[]
+    = {ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0'};
+static const char KW_ENTITIES[] = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T,
+                                   ASCII_I, ASCII_E, ASCII_S, '\0'};
+static const char KW_ENTITY[]
+    = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0'};
+static const char KW_FIXED[]
+    = {ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0'};
+static const char KW_ID[] = {ASCII_I, ASCII_D, '\0'};
+static const char KW_IDREF[]
+    = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0'};
+static const char KW_IDREFS[]
+    = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0'};
+#ifdef XML_DTD
+static const char KW_IGNORE[]
+    = {ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0'};
+#endif
+static const char KW_IMPLIED[]
+    = {ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0'};
+#ifdef XML_DTD
+static const char KW_INCLUDE[]
+    = {ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0'};
+#endif
+static const char KW_NDATA[]
+    = {ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0'};
+static const char KW_NMTOKEN[]
+    = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0'};
+static const char KW_NMTOKENS[] = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K,
+                                   ASCII_E, ASCII_N, ASCII_S, '\0'};
+static const char KW_NOTATION[] = {ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T,
+                                   ASCII_I, ASCII_O, ASCII_N, '\0'};
+static const char KW_PCDATA[]
+    = {ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0'};
+static const char KW_PUBLIC[]
+    = {ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0'};
+static const char KW_REQUIRED[] = {ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I,
+                                   ASCII_R, ASCII_E, ASCII_D, '\0'};
+static const char KW_SYSTEM[]
+    = {ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0'};
+
+#ifndef MIN_BYTES_PER_CHAR
+#  define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar)
+#endif
+
+#ifdef XML_DTD
+#  define setTopLevel(state)                                                   \
+    ((state)->handler                                                          \
+     = ((state)->documentEntity ? internalSubset : externalSubset1))
+#else /* not XML_DTD */
+#  define setTopLevel(state) ((state)->handler = internalSubset)
+#endif /* not XML_DTD */
+
+typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, int tok,
+                                   const char *ptr, const char *end,
+                                   const ENCODING *enc);
+
+static PROLOG_HANDLER prolog0, prolog1, prolog2, doctype0, doctype1, doctype2,
+    doctype3, doctype4, doctype5, internalSubset, entity0, entity1, entity2,
+    entity3, entity4, entity5, entity6, entity7, entity8, entity9, entity10,
+    notation0, notation1, notation2, notation3, notation4, attlist0, attlist1,
+    attlist2, attlist3, attlist4, attlist5, attlist6, attlist7, attlist8,
+    attlist9, element0, element1, element2, element3, element4, element5,
+    element6, element7,
+#ifdef XML_DTD
+    externalSubset0, externalSubset1, condSect0, condSect1, condSect2,
+#endif /* XML_DTD */
+    declClose, error;
+
+static int FASTCALL common(PROLOG_STATE *state, int tok);
+
+static int PTRCALL
+prolog0(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    state->handler = prolog1;
+    return XML_ROLE_NONE;
+  case XML_TOK_XML_DECL:
+    state->handler = prolog1;
+    return XML_ROLE_XML_DECL;
+  case XML_TOK_PI:
+    state->handler = prolog1;
+    return XML_ROLE_PI;
+  case XML_TOK_COMMENT:
+    state->handler = prolog1;
+    return XML_ROLE_COMMENT;
+  case XML_TOK_BOM:
+    return XML_ROLE_NONE;
+  case XML_TOK_DECL_OPEN:
+    if (! XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end,
+                              KW_DOCTYPE))
+      break;
+    state->handler = doctype0;
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_INSTANCE_START:
+    state->handler = error;
+    return XML_ROLE_INSTANCE_START;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+prolog1(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_PI:
+    return XML_ROLE_PI;
+  case XML_TOK_COMMENT:
+    return XML_ROLE_COMMENT;
+  case XML_TOK_BOM:
+    /* This case can never arise.  To reach this role function, the
+     * parse must have passed through prolog0 and therefore have had
+     * some form of input, even if only a space.  At that point, a
+     * byte order mark is no longer a valid character (though
+     * technically it should be interpreted as a non-breaking space),
+     * so will be rejected by the tokenizing stages.
+     */
+    return XML_ROLE_NONE; /* LCOV_EXCL_LINE */
+  case XML_TOK_DECL_OPEN:
+    if (! XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end,
+                              KW_DOCTYPE))
+      break;
+    state->handler = doctype0;
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_INSTANCE_START:
+    state->handler = error;
+    return XML_ROLE_INSTANCE_START;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+prolog2(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_PI:
+    return XML_ROLE_PI;
+  case XML_TOK_COMMENT:
+    return XML_ROLE_COMMENT;
+  case XML_TOK_INSTANCE_START:
+    state->handler = error;
+    return XML_ROLE_INSTANCE_START;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype0(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = doctype1;
+    return XML_ROLE_DOCTYPE_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype1(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_OPEN_BRACKET:
+    state->handler = internalSubset;
+    return XML_ROLE_DOCTYPE_INTERNAL_SUBSET;
+  case XML_TOK_DECL_CLOSE:
+    state->handler = prolog2;
+    return XML_ROLE_DOCTYPE_CLOSE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+      state->handler = doctype3;
+      return XML_ROLE_DOCTYPE_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+      state->handler = doctype2;
+      return XML_ROLE_DOCTYPE_NONE;
+    }
+    break;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype2(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = doctype3;
+    return XML_ROLE_DOCTYPE_PUBLIC_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype3(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = doctype4;
+    return XML_ROLE_DOCTYPE_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype4(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_OPEN_BRACKET:
+    state->handler = internalSubset;
+    return XML_ROLE_DOCTYPE_INTERNAL_SUBSET;
+  case XML_TOK_DECL_CLOSE:
+    state->handler = prolog2;
+    return XML_ROLE_DOCTYPE_CLOSE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype5(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_DECL_CLOSE:
+    state->handler = prolog2;
+    return XML_ROLE_DOCTYPE_CLOSE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+internalSubset(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+               const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_DECL_OPEN:
+    if (XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end,
+                            KW_ENTITY)) {
+      state->handler = entity0;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end,
+                            KW_ATTLIST)) {
+      state->handler = attlist0;
+      return XML_ROLE_ATTLIST_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end,
+                            KW_ELEMENT)) {
+      state->handler = element0;
+      return XML_ROLE_ELEMENT_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end,
+                            KW_NOTATION)) {
+      state->handler = notation0;
+      return XML_ROLE_NOTATION_NONE;
+    }
+    break;
+  case XML_TOK_PI:
+    return XML_ROLE_PI;
+  case XML_TOK_COMMENT:
+    return XML_ROLE_COMMENT;
+  case XML_TOK_PARAM_ENTITY_REF:
+    return XML_ROLE_PARAM_ENTITY_REF;
+  case XML_TOK_CLOSE_BRACKET:
+    state->handler = doctype5;
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_NONE:
+    return XML_ROLE_NONE;
+  }
+  return common(state, tok);
+}
+
+#ifdef XML_DTD
+
+static int PTRCALL
+externalSubset0(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+                const ENCODING *enc) {
+  state->handler = externalSubset1;
+  if (tok == XML_TOK_XML_DECL)
+    return XML_ROLE_TEXT_DECL;
+  return externalSubset1(state, tok, ptr, end, enc);
+}
+
+static int PTRCALL
+externalSubset1(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+                const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_COND_SECT_OPEN:
+    state->handler = condSect0;
+    return XML_ROLE_NONE;
+  case XML_TOK_COND_SECT_CLOSE:
+    if (state->includeLevel == 0)
+      break;
+    state->includeLevel -= 1;
+    return XML_ROLE_NONE;
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_CLOSE_BRACKET:
+    break;
+  case XML_TOK_NONE:
+    if (state->includeLevel)
+      break;
+    return XML_ROLE_NONE;
+  default:
+    return internalSubset(state, tok, ptr, end, enc);
+  }
+  return common(state, tok);
+}
+
+#endif /* XML_DTD */
+
+static int PTRCALL
+entity0(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_PERCENT:
+    state->handler = entity1;
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    state->handler = entity2;
+    return XML_ROLE_GENERAL_ENTITY_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity1(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    state->handler = entity7;
+    return XML_ROLE_PARAM_ENTITY_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity2(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+      state->handler = entity4;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+      state->handler = entity3;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    break;
+  case XML_TOK_LITERAL:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ENTITY_NONE;
+    return XML_ROLE_ENTITY_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity3(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = entity4;
+    return XML_ROLE_ENTITY_PUBLIC_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity4(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = entity5;
+    return XML_ROLE_ENTITY_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity5(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return XML_ROLE_ENTITY_COMPLETE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) {
+      state->handler = entity6;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    break;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity6(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ENTITY_NONE;
+    return XML_ROLE_ENTITY_NOTATION_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity7(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+      state->handler = entity9;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+      state->handler = entity8;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    break;
+  case XML_TOK_LITERAL:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ENTITY_NONE;
+    return XML_ROLE_ENTITY_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity8(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = entity9;
+    return XML_ROLE_ENTITY_PUBLIC_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity9(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+        const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = entity10;
+    return XML_ROLE_ENTITY_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity10(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return XML_ROLE_ENTITY_COMPLETE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation0(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+          const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_NAME:
+    state->handler = notation1;
+    return XML_ROLE_NOTATION_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation1(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+          const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+      state->handler = notation3;
+      return XML_ROLE_NOTATION_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+      state->handler = notation2;
+      return XML_ROLE_NOTATION_NONE;
+    }
+    break;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation2(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+          const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = notation4;
+    return XML_ROLE_NOTATION_PUBLIC_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation3(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+          const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_NOTATION_NONE;
+    return XML_ROLE_NOTATION_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation4(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+          const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_NOTATION_NONE;
+    return XML_ROLE_NOTATION_SYSTEM_ID;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return XML_ROLE_NOTATION_NO_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist0(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = attlist1;
+    return XML_ROLE_ATTLIST_ELEMENT_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist1(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = attlist2;
+    return XML_ROLE_ATTRIBUTE_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist2(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NAME: {
+    static const char *const types[] = {
+        KW_CDATA,  KW_ID,       KW_IDREF,   KW_IDREFS,
+        KW_ENTITY, KW_ENTITIES, KW_NMTOKEN, KW_NMTOKENS,
+    };
+    int i;
+    for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i++)
+      if (XmlNameMatchesAscii(enc, ptr, end, types[i])) {
+        state->handler = attlist8;
+        return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i;
+      }
+  }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) {
+      state->handler = attlist5;
+      return XML_ROLE_ATTLIST_NONE;
+    }
+    break;
+  case XML_TOK_OPEN_PAREN:
+    state->handler = attlist3;
+    return XML_ROLE_ATTLIST_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist3(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NMTOKEN:
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = attlist4;
+    return XML_ROLE_ATTRIBUTE_ENUM_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist4(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_CLOSE_PAREN:
+    state->handler = attlist8;
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_OR:
+    state->handler = attlist3;
+    return XML_ROLE_ATTLIST_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist5(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_OPEN_PAREN:
+    state->handler = attlist6;
+    return XML_ROLE_ATTLIST_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist6(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NAME:
+    state->handler = attlist7;
+    return XML_ROLE_ATTRIBUTE_NOTATION_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist7(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_CLOSE_PAREN:
+    state->handler = attlist8;
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_OR:
+    state->handler = attlist6;
+    return XML_ROLE_ATTLIST_NONE;
+  }
+  return common(state, tok);
+}
+
+/* default value */
+static int PTRCALL
+attlist8(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_POUND_NAME:
+    if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), end,
+                            KW_IMPLIED)) {
+      state->handler = attlist1;
+      return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), end,
+                            KW_REQUIRED)) {
+      state->handler = attlist1;
+      return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), end,
+                            KW_FIXED)) {
+      state->handler = attlist9;
+      return XML_ROLE_ATTLIST_NONE;
+    }
+    break;
+  case XML_TOK_LITERAL:
+    state->handler = attlist1;
+    return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist9(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = attlist1;
+    return XML_ROLE_FIXED_ATTRIBUTE_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element0(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = element1;
+    return XML_ROLE_ELEMENT_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element1(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+      return XML_ROLE_CONTENT_EMPTY;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+      return XML_ROLE_CONTENT_ANY;
+    }
+    break;
+  case XML_TOK_OPEN_PAREN:
+    state->handler = element2;
+    state->level = 1;
+    return XML_ROLE_GROUP_OPEN;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element2(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_POUND_NAME:
+    if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), end,
+                            KW_PCDATA)) {
+      state->handler = element3;
+      return XML_ROLE_CONTENT_PCDATA;
+    }
+    break;
+  case XML_TOK_OPEN_PAREN:
+    state->level = 2;
+    state->handler = element6;
+    return XML_ROLE_GROUP_OPEN;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT;
+  case XML_TOK_NAME_QUESTION:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_OPT;
+  case XML_TOK_NAME_ASTERISK:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_REP;
+  case XML_TOK_NAME_PLUS:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_PLUS;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element3(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_CLOSE_PAREN:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ELEMENT_NONE;
+    return XML_ROLE_GROUP_CLOSE;
+  case XML_TOK_CLOSE_PAREN_ASTERISK:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ELEMENT_NONE;
+    return XML_ROLE_GROUP_CLOSE_REP;
+  case XML_TOK_OR:
+    state->handler = element4;
+    return XML_ROLE_ELEMENT_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element4(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = element5;
+    return XML_ROLE_CONTENT_ELEMENT;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element5(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_CLOSE_PAREN_ASTERISK:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ELEMENT_NONE;
+    return XML_ROLE_GROUP_CLOSE_REP;
+  case XML_TOK_OR:
+    state->handler = element4;
+    return XML_ROLE_ELEMENT_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element6(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_OPEN_PAREN:
+    state->level += 1;
+    return XML_ROLE_GROUP_OPEN;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT;
+  case XML_TOK_NAME_QUESTION:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_OPT;
+  case XML_TOK_NAME_ASTERISK:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_REP;
+  case XML_TOK_NAME_PLUS:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_PLUS;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element7(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+         const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_CLOSE_PAREN:
+    state->level -= 1;
+    if (state->level == 0) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+    }
+    return XML_ROLE_GROUP_CLOSE;
+  case XML_TOK_CLOSE_PAREN_ASTERISK:
+    state->level -= 1;
+    if (state->level == 0) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+    }
+    return XML_ROLE_GROUP_CLOSE_REP;
+  case XML_TOK_CLOSE_PAREN_QUESTION:
+    state->level -= 1;
+    if (state->level == 0) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+    }
+    return XML_ROLE_GROUP_CLOSE_OPT;
+  case XML_TOK_CLOSE_PAREN_PLUS:
+    state->level -= 1;
+    if (state->level == 0) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+    }
+    return XML_ROLE_GROUP_CLOSE_PLUS;
+  case XML_TOK_COMMA:
+    state->handler = element6;
+    return XML_ROLE_GROUP_SEQUENCE;
+  case XML_TOK_OR:
+    state->handler = element6;
+    return XML_ROLE_GROUP_CHOICE;
+  }
+  return common(state, tok);
+}
+
+#ifdef XML_DTD
+
+static int PTRCALL
+condSect0(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+          const ENCODING *enc) {
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) {
+      state->handler = condSect1;
+      return XML_ROLE_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) {
+      state->handler = condSect2;
+      return XML_ROLE_NONE;
+    }
+    break;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+condSect1(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+          const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_OPEN_BRACKET:
+    state->handler = externalSubset1;
+    state->includeLevel += 1;
+    return XML_ROLE_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+condSect2(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+          const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_OPEN_BRACKET:
+    state->handler = externalSubset1;
+    return XML_ROLE_IGNORE_SECT;
+  }
+  return common(state, tok);
+}
+
+#endif /* XML_DTD */
+
+static int PTRCALL
+declClose(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+          const ENCODING *enc) {
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return state->role_none;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return state->role_none;
+  }
+  return common(state, tok);
+}
+
+/* This function will only be invoked if the internal logic of the
+ * parser has broken down.  It is used in two cases:
+ *
+ * 1: When the XML prolog has been finished.  At this point the
+ * processor (the parser level above these role handlers) should
+ * switch from prologProcessor to contentProcessor and reinitialise
+ * the handler function.
+ *
+ * 2: When an error has been detected (via common() below).  At this
+ * point again the processor should be switched to errorProcessor,
+ * which will never call a handler.
+ *
+ * The result of this is that error() can only be called if the
+ * processor switch failed to happen, which is an internal error and
+ * therefore we shouldn't be able to provoke it simply by using the
+ * library.  It is a necessary backstop, however, so we merely exclude
+ * it from the coverage statistics.
+ *
+ * LCOV_EXCL_START
+ */
+static int PTRCALL
+error(PROLOG_STATE *state, int tok, const char *ptr, const char *end,
+      const ENCODING *enc) {
+  UNUSED_P(state);
+  UNUSED_P(tok);
+  UNUSED_P(ptr);
+  UNUSED_P(end);
+  UNUSED_P(enc);
+  return XML_ROLE_NONE;
+}
+/* LCOV_EXCL_STOP */
+
+static int FASTCALL
+common(PROLOG_STATE *state, int tok) {
+#ifdef XML_DTD
+  if (! state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF)
+    return XML_ROLE_INNER_PARAM_ENTITY_REF;
+#else
+  UNUSED_P(tok);
+#endif
+  state->handler = error;
+  return XML_ROLE_ERROR;
+}
+
+void
+XmlPrologStateInit(PROLOG_STATE *state) {
+  state->handler = prolog0;
+#ifdef XML_DTD
+  state->documentEntity = 1;
+  state->includeLevel = 0;
+  state->inEntityValue = 0;
+#endif /* XML_DTD */
+}
+
+#ifdef XML_DTD
+
+void
+XmlPrologStateInitExternalEntity(PROLOG_STATE *state) {
+  state->handler = externalSubset0;
+  state->documentEntity = 0;
+  state->includeLevel = 0;
+}
+
+#endif /* XML_DTD */
diff --git a/dummy_fe/libexpat/xmlrole.h b/dummy_fe/libexpat/xmlrole.h
new file mode 100644
index 0000000..d6e1fa1
--- /dev/null
+++ b/dummy_fe/libexpat/xmlrole.h
@@ -0,0 +1,142 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef XmlRole_INCLUDED
+#define XmlRole_INCLUDED 1
+
+#ifdef __VMS
+/*      0        1         2         3      0        1         2         3
+        1234567890123456789012345678901     1234567890123456789012345678901 */
+#  define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt
+#endif
+
+#include "xmltok.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+  XML_ROLE_ERROR = -1,
+  XML_ROLE_NONE = 0,
+  XML_ROLE_XML_DECL,
+  XML_ROLE_INSTANCE_START,
+  XML_ROLE_DOCTYPE_NONE,
+  XML_ROLE_DOCTYPE_NAME,
+  XML_ROLE_DOCTYPE_SYSTEM_ID,
+  XML_ROLE_DOCTYPE_PUBLIC_ID,
+  XML_ROLE_DOCTYPE_INTERNAL_SUBSET,
+  XML_ROLE_DOCTYPE_CLOSE,
+  XML_ROLE_GENERAL_ENTITY_NAME,
+  XML_ROLE_PARAM_ENTITY_NAME,
+  XML_ROLE_ENTITY_NONE,
+  XML_ROLE_ENTITY_VALUE,
+  XML_ROLE_ENTITY_SYSTEM_ID,
+  XML_ROLE_ENTITY_PUBLIC_ID,
+  XML_ROLE_ENTITY_COMPLETE,
+  XML_ROLE_ENTITY_NOTATION_NAME,
+  XML_ROLE_NOTATION_NONE,
+  XML_ROLE_NOTATION_NAME,
+  XML_ROLE_NOTATION_SYSTEM_ID,
+  XML_ROLE_NOTATION_NO_SYSTEM_ID,
+  XML_ROLE_NOTATION_PUBLIC_ID,
+  XML_ROLE_ATTRIBUTE_NAME,
+  XML_ROLE_ATTRIBUTE_TYPE_CDATA,
+  XML_ROLE_ATTRIBUTE_TYPE_ID,
+  XML_ROLE_ATTRIBUTE_TYPE_IDREF,
+  XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
+  XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
+  XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
+  XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
+  XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
+  XML_ROLE_ATTRIBUTE_ENUM_VALUE,
+  XML_ROLE_ATTRIBUTE_NOTATION_VALUE,
+  XML_ROLE_ATTLIST_NONE,
+  XML_ROLE_ATTLIST_ELEMENT_NAME,
+  XML_ROLE_IMPLIED_ATTRIBUTE_VALUE,
+  XML_ROLE_REQUIRED_ATTRIBUTE_VALUE,
+  XML_ROLE_DEFAULT_ATTRIBUTE_VALUE,
+  XML_ROLE_FIXED_ATTRIBUTE_VALUE,
+  XML_ROLE_ELEMENT_NONE,
+  XML_ROLE_ELEMENT_NAME,
+  XML_ROLE_CONTENT_ANY,
+  XML_ROLE_CONTENT_EMPTY,
+  XML_ROLE_CONTENT_PCDATA,
+  XML_ROLE_GROUP_OPEN,
+  XML_ROLE_GROUP_CLOSE,
+  XML_ROLE_GROUP_CLOSE_REP,
+  XML_ROLE_GROUP_CLOSE_OPT,
+  XML_ROLE_GROUP_CLOSE_PLUS,
+  XML_ROLE_GROUP_CHOICE,
+  XML_ROLE_GROUP_SEQUENCE,
+  XML_ROLE_CONTENT_ELEMENT,
+  XML_ROLE_CONTENT_ELEMENT_REP,
+  XML_ROLE_CONTENT_ELEMENT_OPT,
+  XML_ROLE_CONTENT_ELEMENT_PLUS,
+  XML_ROLE_PI,
+  XML_ROLE_COMMENT,
+#ifdef XML_DTD
+  XML_ROLE_TEXT_DECL,
+  XML_ROLE_IGNORE_SECT,
+  XML_ROLE_INNER_PARAM_ENTITY_REF,
+#endif /* XML_DTD */
+  XML_ROLE_PARAM_ENTITY_REF
+};
+
+typedef struct prolog_state {
+  int(PTRCALL *handler)(struct prolog_state *state, int tok, const char *ptr,
+                        const char *end, const ENCODING *enc);
+  unsigned level;
+  int role_none;
+#ifdef XML_DTD
+  unsigned includeLevel;
+  int documentEntity;
+  int inEntityValue;
+#endif /* XML_DTD */
+} PROLOG_STATE;
+
+void XmlPrologStateInit(PROLOG_STATE *);
+#ifdef XML_DTD
+void XmlPrologStateInitExternalEntity(PROLOG_STATE *);
+#endif /* XML_DTD */
+
+#define XmlTokenRole(state, tok, ptr, end, enc)                                \
+  (((state)->handler)(state, tok, ptr, end, enc))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlRole_INCLUDED */
diff --git a/dummy_fe/libexpat/xmltok.c b/dummy_fe/libexpat/xmltok.c
new file mode 100644
index 0000000..2b7012a
--- /dev/null
+++ b/dummy_fe/libexpat/xmltok.c
@@ -0,0 +1,1677 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2005-2009 Steven Solie <steven@solie.ca>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016      Pascal Cuoq <cuoq@trust-in-soft.com>
+   Copyright (c) 2016      Don Lewis <truckman@apache.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Alexander Bluhm <alexander.bluhm@gmx.net>
+   Copyright (c) 2017      Benbuck Nason <bnason@netflix.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2021      Dong-hee Na <donghee.na@python.org>
+   Copyright (c) 2022      Martin Ettl <ettl.martin78@googlemail.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <expat_config.h>
+
+#include <stddef.h>
+#include <string.h> /* memcpy */
+#include <stdbool.h>
+
+#ifdef _WIN32
+#  include "winconfig.h"
+#endif
+
+#include "expat_external.h"
+#include "internal.h"
+#include "xmltok.h"
+#include "nametab.h"
+
+#ifdef XML_DTD
+#  define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok)
+#else
+#  define IGNORE_SECTION_TOK_VTABLE /* as nothing */
+#endif
+
+#define VTABLE1                                                                \
+  {PREFIX(prologTok), PREFIX(contentTok),                                      \
+   PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE},                         \
+      {PREFIX(attributeValueTok), PREFIX(entityValueTok)},                     \
+      PREFIX(nameMatchesAscii), PREFIX(nameLength), PREFIX(skipS),             \
+      PREFIX(getAtts), PREFIX(charRefNumber), PREFIX(predefinedEntityName),    \
+      PREFIX(updatePosition), PREFIX(isPublicId)
+
+#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16)
+
+#define UCS2_GET_NAMING(pages, hi, lo)                                         \
+  (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1u << ((lo)&0x1F)))
+
+/* A 2 byte UTF-8 representation splits the characters 11 bits between
+   the bottom 5 and 6 bits of the bytes.  We need 8 bits to index into
+   pages, 3 bits to add to that index and 5 bits to generate the mask.
+*/
+#define UTF8_GET_NAMING2(pages, byte)                                          \
+  (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3)                         \
+                + ((((byte)[0]) & 3) << 1) + ((((byte)[1]) >> 5) & 1)]         \
+   & (1u << (((byte)[1]) & 0x1F)))
+
+/* A 3 byte UTF-8 representation splits the characters 16 bits between
+   the bottom 4, 6 and 6 bits of the bytes.  We need 8 bits to index
+   into pages, 3 bits to add to that index and 5 bits to generate the
+   mask.
+*/
+#define UTF8_GET_NAMING3(pages, byte)                                          \
+  (namingBitmap                                                                \
+       [((pages)[((((byte)[0]) & 0xF) << 4) + ((((byte)[1]) >> 2) & 0xF)]      \
+         << 3)                                                                 \
+        + ((((byte)[1]) & 3) << 1) + ((((byte)[2]) >> 5) & 1)]                 \
+   & (1u << (((byte)[2]) & 0x1F)))
+
+/* Detection of invalid UTF-8 sequences is based on Table 3.1B
+   of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
+   with the additional restriction of not allowing the Unicode
+   code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE).
+   Implementation details:
+     (A & 0x80) == 0     means A < 0x80
+   and
+     (A & 0xC0) == 0xC0  means A > 0xBF
+*/
+
+#define UTF8_INVALID2(p)                                                       \
+  ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0)
+
+#define UTF8_INVALID3(p)                                                       \
+  (((p)[2] & 0x80) == 0                                                        \
+   || ((*p) == 0xEF && (p)[1] == 0xBF ? (p)[2] > 0xBD                          \
+                                      : ((p)[2] & 0xC0) == 0xC0)               \
+   || ((*p) == 0xE0                                                            \
+           ? (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0                          \
+           : ((p)[1] & 0x80) == 0                                              \
+                 || ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0)))
+
+#define UTF8_INVALID4(p)                                                       \
+  (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 || ((p)[2] & 0x80) == 0     \
+   || ((p)[2] & 0xC0) == 0xC0                                                  \
+   || ((*p) == 0xF0                                                            \
+           ? (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0                          \
+           : ((p)[1] & 0x80) == 0                                              \
+                 || ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0)))
+
+static int PTRFASTCALL
+isNever(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  UNUSED_P(p);
+  return 0;
+}
+
+static int PTRFASTCALL
+utf8_isName2(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return UTF8_GET_NAMING2(namePages, (const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isName3(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return UTF8_GET_NAMING3(namePages, (const unsigned char *)p);
+}
+
+#define utf8_isName4 isNever
+
+static int PTRFASTCALL
+utf8_isNmstrt2(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isNmstrt3(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p);
+}
+
+#define utf8_isNmstrt4 isNever
+
+static int PTRFASTCALL
+utf8_isInvalid2(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return UTF8_INVALID2((const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isInvalid3(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return UTF8_INVALID3((const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isInvalid4(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return UTF8_INVALID4((const unsigned char *)p);
+}
+
+struct normal_encoding {
+  ENCODING enc;
+  unsigned char type[256];
+#ifdef XML_MIN_SIZE
+  int(PTRFASTCALL *byteType)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isNameMin)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *);
+  int(PTRFASTCALL *byteToAscii)(const ENCODING *, const char *);
+  int(PTRCALL *charMatches)(const ENCODING *, const char *, int);
+#endif /* XML_MIN_SIZE */
+  int(PTRFASTCALL *isName2)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isName3)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isName4)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isInvalid2)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isInvalid3)(const ENCODING *, const char *);
+  int(PTRFASTCALL *isInvalid4)(const ENCODING *, const char *);
+};
+
+#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *)(enc))
+
+#ifdef XML_MIN_SIZE
+
+#  define STANDARD_VTABLE(E)                                                   \
+    E##byteType, E##isNameMin, E##isNmstrtMin, E##byteToAscii, E##charMatches,
+
+#else
+
+#  define STANDARD_VTABLE(E) /* as nothing */
+
+#endif
+
+#define NORMAL_VTABLE(E)                                                       \
+  E##isName2, E##isName3, E##isName4, E##isNmstrt2, E##isNmstrt3,              \
+      E##isNmstrt4, E##isInvalid2, E##isInvalid3, E##isInvalid4
+
+#define NULL_VTABLE                                                            \
+  /* isName2 */ NULL, /* isName3 */ NULL, /* isName4 */ NULL,                  \
+      /* isNmstrt2 */ NULL, /* isNmstrt3 */ NULL, /* isNmstrt4 */ NULL,        \
+      /* isInvalid2 */ NULL, /* isInvalid3 */ NULL, /* isInvalid4 */ NULL
+
+static int FASTCALL checkCharRefNumber(int);
+
+#include "xmltok_impl.h"
+#include "ascii.h"
+
+#ifdef XML_MIN_SIZE
+#  define sb_isNameMin isNever
+#  define sb_isNmstrtMin isNever
+#endif
+
+#ifdef XML_MIN_SIZE
+#  define MINBPC(enc) ((enc)->minBytesPerChar)
+#else
+/* minimum bytes per character */
+#  define MINBPC(enc) 1
+#endif
+
+#define SB_BYTE_TYPE(enc, p)                                                   \
+  (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)])
+
+#ifdef XML_MIN_SIZE
+static int PTRFASTCALL
+sb_byteType(const ENCODING *enc, const char *p) {
+  return SB_BYTE_TYPE(enc, p);
+}
+#  define BYTE_TYPE(enc, p) (AS_NORMAL_ENCODING(enc)->byteType(enc, p))
+#else
+#  define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p)
+#endif
+
+#ifdef XML_MIN_SIZE
+#  define BYTE_TO_ASCII(enc, p) (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p))
+static int PTRFASTCALL
+sb_byteToAscii(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return *p;
+}
+#else
+#  define BYTE_TO_ASCII(enc, p) (*(p))
+#endif
+
+#define IS_NAME_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isName##n(enc, p))
+#define IS_NMSTRT_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isNmstrt##n(enc, p))
+#ifdef XML_MIN_SIZE
+#  define IS_INVALID_CHAR(enc, p, n)                                           \
+    (AS_NORMAL_ENCODING(enc)->isInvalid##n                                     \
+     && AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
+#else
+#  define IS_INVALID_CHAR(enc, p, n)                                           \
+    (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
+#endif
+
+#ifdef XML_MIN_SIZE
+#  define IS_NAME_CHAR_MINBPC(enc, p)                                          \
+    (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p))
+#  define IS_NMSTRT_CHAR_MINBPC(enc, p)                                        \
+    (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p))
+#else
+#  define IS_NAME_CHAR_MINBPC(enc, p) (0)
+#  define IS_NMSTRT_CHAR_MINBPC(enc, p) (0)
+#endif
+
+#ifdef XML_MIN_SIZE
+#  define CHAR_MATCHES(enc, p, c)                                              \
+    (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c))
+static int PTRCALL
+sb_charMatches(const ENCODING *enc, const char *p, int c) {
+  UNUSED_P(enc);
+  return *p == c;
+}
+#else
+/* c is an ASCII character */
+#  define CHAR_MATCHES(enc, p, c) (*(p) == (c))
+#endif
+
+#define PREFIX(ident) normal_##ident
+#define XML_TOK_IMPL_C
+#include "xmltok_impl.c"
+#undef XML_TOK_IMPL_C
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */
+       UTF8_cval1 = 0x00,
+       UTF8_cval2 = 0xc0,
+       UTF8_cval3 = 0xe0,
+       UTF8_cval4 = 0xf0
+};
+
+void
+_INTERNAL_trim_to_complete_utf8_characters(const char *from,
+                                           const char **fromLimRef) {
+  const char *fromLim = *fromLimRef;
+  size_t walked = 0;
+  for (; fromLim > from; fromLim--, walked++) {
+    const unsigned char prev = (unsigned char)fromLim[-1];
+    if ((prev & 0xf8u)
+        == 0xf0u) { /* 4-byte character, lead by 0b11110xxx byte */
+      if (walked + 1 >= 4) {
+        fromLim += 4 - 1;
+        break;
+      } else {
+        walked = 0;
+      }
+    } else if ((prev & 0xf0u)
+               == 0xe0u) { /* 3-byte character, lead by 0b1110xxxx byte */
+      if (walked + 1 >= 3) {
+        fromLim += 3 - 1;
+        break;
+      } else {
+        walked = 0;
+      }
+    } else if ((prev & 0xe0u)
+               == 0xc0u) { /* 2-byte character, lead by 0b110xxxxx byte */
+      if (walked + 1 >= 2) {
+        fromLim += 2 - 1;
+        break;
+      } else {
+        walked = 0;
+      }
+    } else if ((prev & 0x80u)
+               == 0x00u) { /* 1-byte character, matching 0b0xxxxxxx */
+      break;
+    }
+  }
+  *fromLimRef = fromLim;
+}
+
+static enum XML_Convert_Result PTRCALL
+utf8_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim,
+            char **toP, const char *toLim) {
+  bool input_incomplete = false;
+  bool output_exhausted = false;
+
+  /* Avoid copying partial characters (due to limited space). */
+  const ptrdiff_t bytesAvailable = fromLim - *fromP;
+  const ptrdiff_t bytesStorable = toLim - *toP;
+  UNUSED_P(enc);
+  if (bytesAvailable > bytesStorable) {
+    fromLim = *fromP + bytesStorable;
+    output_exhausted = true;
+  }
+
+  /* Avoid copying partial characters (from incomplete input). */
+  {
+    const char *const fromLimBefore = fromLim;
+    _INTERNAL_trim_to_complete_utf8_characters(*fromP, &fromLim);
+    if (fromLim < fromLimBefore) {
+      input_incomplete = true;
+    }
+  }
+
+  {
+    const ptrdiff_t bytesToCopy = fromLim - *fromP;
+    memcpy(*toP, *fromP, bytesToCopy);
+    *fromP += bytesToCopy;
+    *toP += bytesToCopy;
+  }
+
+  if (output_exhausted) /* needs to go first */
+    return XML_CONVERT_OUTPUT_EXHAUSTED;
+  else if (input_incomplete)
+    return XML_CONVERT_INPUT_INCOMPLETE;
+  else
+    return XML_CONVERT_COMPLETED;
+}
+
+static enum XML_Convert_Result PTRCALL
+utf8_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim,
+             unsigned short **toP, const unsigned short *toLim) {
+  enum XML_Convert_Result res = XML_CONVERT_COMPLETED;
+  unsigned short *to = *toP;
+  const char *from = *fromP;
+  while (from < fromLim && to < toLim) {
+    switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) {
+    case BT_LEAD2:
+      if (fromLim - from < 2) {
+        res = XML_CONVERT_INPUT_INCOMPLETE;
+        goto after;
+      }
+      *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f));
+      from += 2;
+      break;
+    case BT_LEAD3:
+      if (fromLim - from < 3) {
+        res = XML_CONVERT_INPUT_INCOMPLETE;
+        goto after;
+      }
+      *to++ = (unsigned short)(((from[0] & 0xf) << 12) | ((from[1] & 0x3f) << 6)
+                               | (from[2] & 0x3f));
+      from += 3;
+      break;
+    case BT_LEAD4: {
+      unsigned long n;
+      if (toLim - to < 2) {
+        res = XML_CONVERT_OUTPUT_EXHAUSTED;
+        goto after;
+      }
+      if (fromLim - from < 4) {
+        res = XML_CONVERT_INPUT_INCOMPLETE;
+        goto after;
+      }
+      n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12)
+          | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f);
+      n -= 0x10000;
+      to[0] = (unsigned short)((n >> 10) | 0xD800);
+      to[1] = (unsigned short)((n & 0x3FF) | 0xDC00);
+      to += 2;
+      from += 4;
+    } break;
+    default:
+      *to++ = *from++;
+      break;
+    }
+  }
+  if (from < fromLim)
+    res = XML_CONVERT_OUTPUT_EXHAUSTED;
+after:
+  *fromP = from;
+  *toP = to;
+  return res;
+}
+
+#ifdef XML_NS
+static const struct normal_encoding utf8_encoding_ns
+    = {{VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0},
+       {
+#  include "asciitab.h"
+#  include "utf8tab.h"
+       },
+       STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)};
+#endif
+
+static const struct normal_encoding utf8_encoding
+    = {{VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0},
+       {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+       },
+       STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)};
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_utf8_encoding_ns
+    = {{VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0},
+       {
+#  include "iasciitab.h"
+#  include "utf8tab.h"
+       },
+       STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)};
+
+#endif
+
+static const struct normal_encoding internal_utf8_encoding
+    = {{VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0},
+       {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+       },
+       STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)};
+
+static enum XML_Convert_Result PTRCALL
+latin1_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim,
+              char **toP, const char *toLim) {
+  UNUSED_P(enc);
+  for (;;) {
+    unsigned char c;
+    if (*fromP == fromLim)
+      return XML_CONVERT_COMPLETED;
+    c = (unsigned char)**fromP;
+    if (c & 0x80) {
+      if (toLim - *toP < 2)
+        return XML_CONVERT_OUTPUT_EXHAUSTED;
+      *(*toP)++ = (char)((c >> 6) | UTF8_cval2);
+      *(*toP)++ = (char)((c & 0x3f) | 0x80);
+      (*fromP)++;
+    } else {
+      if (*toP == toLim)
+        return XML_CONVERT_OUTPUT_EXHAUSTED;
+      *(*toP)++ = *(*fromP)++;
+    }
+  }
+}
+
+static enum XML_Convert_Result PTRCALL
+latin1_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim,
+               unsigned short **toP, const unsigned short *toLim) {
+  UNUSED_P(enc);
+  while (*fromP < fromLim && *toP < toLim)
+    *(*toP)++ = (unsigned char)*(*fromP)++;
+
+  if ((*toP == toLim) && (*fromP < fromLim))
+    return XML_CONVERT_OUTPUT_EXHAUSTED;
+  else
+    return XML_CONVERT_COMPLETED;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding latin1_encoding_ns
+    = {{VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0},
+       {
+#  include "asciitab.h"
+#  include "latin1tab.h"
+       },
+       STANDARD_VTABLE(sb_) NULL_VTABLE};
+
+#endif
+
+static const struct normal_encoding latin1_encoding
+    = {{VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0},
+       {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+       },
+       STANDARD_VTABLE(sb_) NULL_VTABLE};
+
+static enum XML_Convert_Result PTRCALL
+ascii_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim,
+             char **toP, const char *toLim) {
+  UNUSED_P(enc);
+  while (*fromP < fromLim && *toP < toLim)
+    *(*toP)++ = *(*fromP)++;
+
+  if ((*toP == toLim) && (*fromP < fromLim))
+    return XML_CONVERT_OUTPUT_EXHAUSTED;
+  else
+    return XML_CONVERT_COMPLETED;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding ascii_encoding_ns
+    = {{VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0},
+       {
+#  include "asciitab.h"
+           /* BT_NONXML == 0 */
+       },
+       STANDARD_VTABLE(sb_) NULL_VTABLE};
+
+#endif
+
+static const struct normal_encoding ascii_encoding
+    = {{VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0},
+       {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+           /* BT_NONXML == 0 */
+       },
+       STANDARD_VTABLE(sb_) NULL_VTABLE};
+
+static int PTRFASTCALL
+unicode_byte_type(char hi, char lo) {
+  switch ((unsigned char)hi) {
+  /* 0xD800-0xDBFF first 16-bit code unit or high surrogate (W1) */
+  case 0xD8:
+  case 0xD9:
+  case 0xDA:
+  case 0xDB:
+    return BT_LEAD4;
+  /* 0xDC00-0xDFFF second 16-bit code unit or low surrogate (W2) */
+  case 0xDC:
+  case 0xDD:
+  case 0xDE:
+  case 0xDF:
+    return BT_TRAIL;
+  case 0xFF:
+    switch ((unsigned char)lo) {
+    case 0xFF: /* noncharacter-FFFF */
+    case 0xFE: /* noncharacter-FFFE */
+      return BT_NONXML;
+    }
+    break;
+  }
+  return BT_NONASCII;
+}
+
+#define DEFINE_UTF16_TO_UTF8(E)                                                \
+  static enum XML_Convert_Result PTRCALL E##toUtf8(                            \
+      const ENCODING *enc, const char **fromP, const char *fromLim,            \
+      char **toP, const char *toLim) {                                         \
+    const char *from = *fromP;                                                 \
+    UNUSED_P(enc);                                                             \
+    fromLim = from + (((fromLim - from) >> 1) << 1); /* shrink to even */      \
+    for (; from < fromLim; from += 2) {                                        \
+      int plane;                                                               \
+      unsigned char lo2;                                                       \
+      unsigned char lo = GET_LO(from);                                         \
+      unsigned char hi = GET_HI(from);                                         \
+      switch (hi) {                                                            \
+      case 0:                                                                  \
+        if (lo < 0x80) {                                                       \
+          if (*toP == toLim) {                                                 \
+            *fromP = from;                                                     \
+            return XML_CONVERT_OUTPUT_EXHAUSTED;                               \
+          }                                                                    \
+          *(*toP)++ = lo;                                                      \
+          break;                                                               \
+        }                                                                      \
+        /* fall through */                                                     \
+      case 0x1:                                                                \
+      case 0x2:                                                                \
+      case 0x3:                                                                \
+      case 0x4:                                                                \
+      case 0x5:                                                                \
+      case 0x6:                                                                \
+      case 0x7:                                                                \
+        if (toLim - *toP < 2) {                                                \
+          *fromP = from;                                                       \
+          return XML_CONVERT_OUTPUT_EXHAUSTED;                                 \
+        }                                                                      \
+        *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2);                      \
+        *(*toP)++ = ((lo & 0x3f) | 0x80);                                      \
+        break;                                                                 \
+      default:                                                                 \
+        if (toLim - *toP < 3) {                                                \
+          *fromP = from;                                                       \
+          return XML_CONVERT_OUTPUT_EXHAUSTED;                                 \
+        }                                                                      \
+        /* 16 bits divided 4, 6, 6 amongst 3 bytes */                          \
+        *(*toP)++ = ((hi >> 4) | UTF8_cval3);                                  \
+        *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80);                    \
+        *(*toP)++ = ((lo & 0x3f) | 0x80);                                      \
+        break;                                                                 \
+      case 0xD8:                                                               \
+      case 0xD9:                                                               \
+      case 0xDA:                                                               \
+      case 0xDB:                                                               \
+        if (toLim - *toP < 4) {                                                \
+          *fromP = from;                                                       \
+          return XML_CONVERT_OUTPUT_EXHAUSTED;                                 \
+        }                                                                      \
+        if (fromLim - from < 4) {                                              \
+          *fromP = from;                                                       \
+          return XML_CONVERT_INPUT_INCOMPLETE;                                 \
+        }                                                                      \
+        plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1;                   \
+        *(*toP)++ = (char)((plane >> 2) | UTF8_cval4);                         \
+        *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80);         \
+        from += 2;                                                             \
+        lo2 = GET_LO(from);                                                    \
+        *(*toP)++ = (((lo & 0x3) << 4) | ((GET_HI(from) & 0x3) << 2)           \
+                     | (lo2 >> 6) | 0x80);                                     \
+        *(*toP)++ = ((lo2 & 0x3f) | 0x80);                                     \
+        break;                                                                 \
+      }                                                                        \
+    }                                                                          \
+    *fromP = from;                                                             \
+    if (from < fromLim)                                                        \
+      return XML_CONVERT_INPUT_INCOMPLETE;                                     \
+    else                                                                       \
+      return XML_CONVERT_COMPLETED;                                            \
+  }
+
+#define DEFINE_UTF16_TO_UTF16(E)                                               \
+  static enum XML_Convert_Result PTRCALL E##toUtf16(                           \
+      const ENCODING *enc, const char **fromP, const char *fromLim,            \
+      unsigned short **toP, const unsigned short *toLim) {                     \
+    enum XML_Convert_Result res = XML_CONVERT_COMPLETED;                       \
+    UNUSED_P(enc);                                                             \
+    fromLim = *fromP + (((fromLim - *fromP) >> 1) << 1); /* shrink to even */  \
+    /* Avoid copying first half only of surrogate */                           \
+    if (fromLim - *fromP > ((toLim - *toP) << 1)                               \
+        && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) {                             \
+      fromLim -= 2;                                                            \
+      res = XML_CONVERT_INPUT_INCOMPLETE;                                      \
+    }                                                                          \
+    for (; *fromP < fromLim && *toP < toLim; *fromP += 2)                      \
+      *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP);                      \
+    if ((*toP == toLim) && (*fromP < fromLim))                                 \
+      return XML_CONVERT_OUTPUT_EXHAUSTED;                                     \
+    else                                                                       \
+      return res;                                                              \
+  }
+
+#define SET2(ptr, ch) (((ptr)[0] = ((ch)&0xff)), ((ptr)[1] = ((ch) >> 8)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[0])
+#define GET_HI(ptr) ((unsigned char)(ptr)[1])
+
+DEFINE_UTF16_TO_UTF8(little2_)
+DEFINE_UTF16_TO_UTF16(little2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define SET2(ptr, ch) (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch)&0xFF)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[1])
+#define GET_HI(ptr) ((unsigned char)(ptr)[0])
+
+DEFINE_UTF16_TO_UTF8(big2_)
+DEFINE_UTF16_TO_UTF16(big2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define LITTLE2_BYTE_TYPE(enc, p)                                              \
+  ((p)[1] == 0 ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]  \
+               : unicode_byte_type((p)[1], (p)[0]))
+#define LITTLE2_BYTE_TO_ASCII(p) ((p)[1] == 0 ? (p)[0] : -1)
+#define LITTLE2_CHAR_MATCHES(p, c) ((p)[1] == 0 && (p)[0] == (c))
+#define LITTLE2_IS_NAME_CHAR_MINBPC(p)                                         \
+  UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0])
+#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(p)                                       \
+  UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0])
+
+#ifdef XML_MIN_SIZE
+
+static int PTRFASTCALL
+little2_byteType(const ENCODING *enc, const char *p) {
+  return LITTLE2_BYTE_TYPE(enc, p);
+}
+
+static int PTRFASTCALL
+little2_byteToAscii(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return LITTLE2_BYTE_TO_ASCII(p);
+}
+
+static int PTRCALL
+little2_charMatches(const ENCODING *enc, const char *p, int c) {
+  UNUSED_P(enc);
+  return LITTLE2_CHAR_MATCHES(p, c);
+}
+
+static int PTRFASTCALL
+little2_isNameMin(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return LITTLE2_IS_NAME_CHAR_MINBPC(p);
+}
+
+static int PTRFASTCALL
+little2_isNmstrtMin(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return LITTLE2_IS_NMSTRT_CHAR_MINBPC(p);
+}
+
+#  undef VTABLE
+#  define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#  undef PREFIX
+#  define PREFIX(ident) little2_##ident
+#  define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#  define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p)
+#  define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(p)
+#  define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(p, c)
+#  define IS_NAME_CHAR(enc, p, n) 0
+#  define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(p)
+#  define IS_NMSTRT_CHAR(enc, p, n) (0)
+#  define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(p)
+
+#  define XML_TOK_IMPL_C
+#  include "xmltok_impl.c"
+#  undef XML_TOK_IMPL_C
+
+#  undef MINBPC
+#  undef BYTE_TYPE
+#  undef BYTE_TO_ASCII
+#  undef CHAR_MATCHES
+#  undef IS_NAME_CHAR
+#  undef IS_NAME_CHAR_MINBPC
+#  undef IS_NMSTRT_CHAR
+#  undef IS_NMSTRT_CHAR_MINBPC
+#  undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding little2_encoding_ns
+    = {{VTABLE, 2, 0,
+#  if BYTEORDER == 1234
+        1
+#  else
+        0
+#  endif
+       },
+       {
+#  include "asciitab.h"
+#  include "latin1tab.h"
+       },
+       STANDARD_VTABLE(little2_) NULL_VTABLE};
+
+#endif
+
+static const struct normal_encoding little2_encoding
+    = {{VTABLE, 2, 0,
+#if BYTEORDER == 1234
+        1
+#else
+        0
+#endif
+       },
+       {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+       },
+       STANDARD_VTABLE(little2_) NULL_VTABLE};
+
+#if BYTEORDER != 4321
+
+#  ifdef XML_NS
+
+static const struct normal_encoding internal_little2_encoding_ns
+    = {{VTABLE, 2, 0, 1},
+       {
+#    include "iasciitab.h"
+#    include "latin1tab.h"
+       },
+       STANDARD_VTABLE(little2_) NULL_VTABLE};
+
+#  endif
+
+static const struct normal_encoding internal_little2_encoding
+    = {{VTABLE, 2, 0, 1},
+       {
+#  define BT_COLON BT_NMSTRT
+#  include "iasciitab.h"
+#  undef BT_COLON
+#  include "latin1tab.h"
+       },
+       STANDARD_VTABLE(little2_) NULL_VTABLE};
+
+#endif
+
+#define BIG2_BYTE_TYPE(enc, p)                                                 \
+  ((p)[0] == 0                                                                 \
+       ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]]        \
+       : unicode_byte_type((p)[0], (p)[1]))
+#define BIG2_BYTE_TO_ASCII(p) ((p)[0] == 0 ? (p)[1] : -1)
+#define BIG2_CHAR_MATCHES(p, c) ((p)[0] == 0 && (p)[1] == (c))
+#define BIG2_IS_NAME_CHAR_MINBPC(p)                                            \
+  UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1])
+#define BIG2_IS_NMSTRT_CHAR_MINBPC(p)                                          \
+  UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1])
+
+#ifdef XML_MIN_SIZE
+
+static int PTRFASTCALL
+big2_byteType(const ENCODING *enc, const char *p) {
+  return BIG2_BYTE_TYPE(enc, p);
+}
+
+static int PTRFASTCALL
+big2_byteToAscii(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return BIG2_BYTE_TO_ASCII(p);
+}
+
+static int PTRCALL
+big2_charMatches(const ENCODING *enc, const char *p, int c) {
+  UNUSED_P(enc);
+  return BIG2_CHAR_MATCHES(p, c);
+}
+
+static int PTRFASTCALL
+big2_isNameMin(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return BIG2_IS_NAME_CHAR_MINBPC(p);
+}
+
+static int PTRFASTCALL
+big2_isNmstrtMin(const ENCODING *enc, const char *p) {
+  UNUSED_P(enc);
+  return BIG2_IS_NMSTRT_CHAR_MINBPC(p);
+}
+
+#  undef VTABLE
+#  define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#  undef PREFIX
+#  define PREFIX(ident) big2_##ident
+#  define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#  define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p)
+#  define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(p)
+#  define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(p, c)
+#  define IS_NAME_CHAR(enc, p, n) 0
+#  define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(p)
+#  define IS_NMSTRT_CHAR(enc, p, n) (0)
+#  define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(p)
+
+#  define XML_TOK_IMPL_C
+#  include "xmltok_impl.c"
+#  undef XML_TOK_IMPL_C
+
+#  undef MINBPC
+#  undef BYTE_TYPE
+#  undef BYTE_TO_ASCII
+#  undef CHAR_MATCHES
+#  undef IS_NAME_CHAR
+#  undef IS_NAME_CHAR_MINBPC
+#  undef IS_NMSTRT_CHAR
+#  undef IS_NMSTRT_CHAR_MINBPC
+#  undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding big2_encoding_ns
+    = {{VTABLE, 2, 0,
+#  if BYTEORDER == 4321
+        1
+#  else
+        0
+#  endif
+       },
+       {
+#  include "asciitab.h"
+#  include "latin1tab.h"
+       },
+       STANDARD_VTABLE(big2_) NULL_VTABLE};
+
+#endif
+
+static const struct normal_encoding big2_encoding
+    = {{VTABLE, 2, 0,
+#if BYTEORDER == 4321
+        1
+#else
+        0
+#endif
+       },
+       {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+       },
+       STANDARD_VTABLE(big2_) NULL_VTABLE};
+
+#if BYTEORDER != 1234
+
+#  ifdef XML_NS
+
+static const struct normal_encoding internal_big2_encoding_ns
+    = {{VTABLE, 2, 0, 1},
+       {
+#    include "iasciitab.h"
+#    include "latin1tab.h"
+       },
+       STANDARD_VTABLE(big2_) NULL_VTABLE};
+
+#  endif
+
+static const struct normal_encoding internal_big2_encoding
+    = {{VTABLE, 2, 0, 1},
+       {
+#  define BT_COLON BT_NMSTRT
+#  include "iasciitab.h"
+#  undef BT_COLON
+#  include "latin1tab.h"
+       },
+       STANDARD_VTABLE(big2_) NULL_VTABLE};
+
+#endif
+
+#undef PREFIX
+
+static int FASTCALL
+streqci(const char *s1, const char *s2) {
+  for (;;) {
+    char c1 = *s1++;
+    char c2 = *s2++;
+    if (ASCII_a <= c1 && c1 <= ASCII_z)
+      c1 += ASCII_A - ASCII_a;
+    if (ASCII_a <= c2 && c2 <= ASCII_z)
+      /* The following line will never get executed.  streqci() is
+       * only called from two places, both of which guarantee to put
+       * upper-case strings into s2.
+       */
+      c2 += ASCII_A - ASCII_a; /* LCOV_EXCL_LINE */
+    if (c1 != c2)
+      return 0;
+    if (! c1)
+      break;
+  }
+  return 1;
+}
+
+static void PTRCALL
+initUpdatePosition(const ENCODING *enc, const char *ptr, const char *end,
+                   POSITION *pos) {
+  UNUSED_P(enc);
+  normal_updatePosition(&utf8_encoding.enc, ptr, end, pos);
+}
+
+static int
+toAscii(const ENCODING *enc, const char *ptr, const char *end) {
+  char buf[1];
+  char *p = buf;
+  XmlUtf8Convert(enc, &ptr, end, &p, p + 1);
+  if (p == buf)
+    return -1;
+  else
+    return buf[0];
+}
+
+static int FASTCALL
+isSpace(int c) {
+  switch (c) {
+  case 0x20:
+  case 0xD:
+  case 0xA:
+  case 0x9:
+    return 1;
+  }
+  return 0;
+}
+
+/* Return 1 if there's just optional white space or there's an S
+   followed by name=val.
+*/
+static int
+parsePseudoAttribute(const ENCODING *enc, const char *ptr, const char *end,
+                     const char **namePtr, const char **nameEndPtr,
+                     const char **valPtr, const char **nextTokPtr) {
+  int c;
+  char open;
+  if (ptr == end) {
+    *namePtr = NULL;
+    return 1;
+  }
+  if (! isSpace(toAscii(enc, ptr, end))) {
+    *nextTokPtr = ptr;
+    return 0;
+  }
+  do {
+    ptr += enc->minBytesPerChar;
+  } while (isSpace(toAscii(enc, ptr, end)));
+  if (ptr == end) {
+    *namePtr = NULL;
+    return 1;
+  }
+  *namePtr = ptr;
+  for (;;) {
+    c = toAscii(enc, ptr, end);
+    if (c == -1) {
+      *nextTokPtr = ptr;
+      return 0;
+    }
+    if (c == ASCII_EQUALS) {
+      *nameEndPtr = ptr;
+      break;
+    }
+    if (isSpace(c)) {
+      *nameEndPtr = ptr;
+      do {
+        ptr += enc->minBytesPerChar;
+      } while (isSpace(c = toAscii(enc, ptr, end)));
+      if (c != ASCII_EQUALS) {
+        *nextTokPtr = ptr;
+        return 0;
+      }
+      break;
+    }
+    ptr += enc->minBytesPerChar;
+  }
+  if (ptr == *namePtr) {
+    *nextTokPtr = ptr;
+    return 0;
+  }
+  ptr += enc->minBytesPerChar;
+  c = toAscii(enc, ptr, end);
+  while (isSpace(c)) {
+    ptr += enc->minBytesPerChar;
+    c = toAscii(enc, ptr, end);
+  }
+  if (c != ASCII_QUOT && c != ASCII_APOS) {
+    *nextTokPtr = ptr;
+    return 0;
+  }
+  open = (char)c;
+  ptr += enc->minBytesPerChar;
+  *valPtr = ptr;
+  for (;; ptr += enc->minBytesPerChar) {
+    c = toAscii(enc, ptr, end);
+    if (c == open)
+      break;
+    if (! (ASCII_a <= c && c <= ASCII_z) && ! (ASCII_A <= c && c <= ASCII_Z)
+        && ! (ASCII_0 <= c && c <= ASCII_9) && c != ASCII_PERIOD
+        && c != ASCII_MINUS && c != ASCII_UNDERSCORE) {
+      *nextTokPtr = ptr;
+      return 0;
+    }
+  }
+  *nextTokPtr = ptr + enc->minBytesPerChar;
+  return 1;
+}
+
+static const char KW_version[]
+    = {ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0'};
+
+static const char KW_encoding[] = {ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d,
+                                   ASCII_i, ASCII_n, ASCII_g, '\0'};
+
+static const char KW_standalone[]
+    = {ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a,
+       ASCII_l, ASCII_o, ASCII_n, ASCII_e, '\0'};
+
+static const char KW_yes[] = {ASCII_y, ASCII_e, ASCII_s, '\0'};
+
+static const char KW_no[] = {ASCII_n, ASCII_o, '\0'};
+
+static int
+doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, const char *,
+                                                 const char *),
+               int isGeneralTextEntity, const ENCODING *enc, const char *ptr,
+               const char *end, const char **badPtr, const char **versionPtr,
+               const char **versionEndPtr, const char **encodingName,
+               const ENCODING **encoding, int *standalone) {
+  const char *val = NULL;
+  const char *name = NULL;
+  const char *nameEnd = NULL;
+  ptr += 5 * enc->minBytesPerChar;
+  end -= 2 * enc->minBytesPerChar;
+  if (! parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)
+      || ! name) {
+    *badPtr = ptr;
+    return 0;
+  }
+  if (! XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) {
+    if (! isGeneralTextEntity) {
+      *badPtr = name;
+      return 0;
+    }
+  } else {
+    if (versionPtr)
+      *versionPtr = val;
+    if (versionEndPtr)
+      *versionEndPtr = ptr;
+    if (! parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) {
+      *badPtr = ptr;
+      return 0;
+    }
+    if (! name) {
+      if (isGeneralTextEntity) {
+        /* a TextDecl must have an EncodingDecl */
+        *badPtr = ptr;
+        return 0;
+      }
+      return 1;
+    }
+  }
+  if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) {
+    int c = toAscii(enc, val, end);
+    if (! (ASCII_a <= c && c <= ASCII_z) && ! (ASCII_A <= c && c <= ASCII_Z)) {
+      *badPtr = val;
+      return 0;
+    }
+    if (encodingName)
+      *encodingName = val;
+    if (encoding)
+      *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar);
+    if (! parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) {
+      *badPtr = ptr;
+      return 0;
+    }
+    if (! name)
+      return 1;
+  }
+  if (! XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone)
+      || isGeneralTextEntity) {
+    *badPtr = name;
+    return 0;
+  }
+  if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) {
+    if (standalone)
+      *standalone = 1;
+  } else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) {
+    if (standalone)
+      *standalone = 0;
+  } else {
+    *badPtr = val;
+    return 0;
+  }
+  while (isSpace(toAscii(enc, ptr, end)))
+    ptr += enc->minBytesPerChar;
+  if (ptr != end) {
+    *badPtr = ptr;
+    return 0;
+  }
+  return 1;
+}
+
+static int FASTCALL
+checkCharRefNumber(int result) {
+  switch (result >> 8) {
+  case 0xD8:
+  case 0xD9:
+  case 0xDA:
+  case 0xDB:
+  case 0xDC:
+  case 0xDD:
+  case 0xDE:
+  case 0xDF:
+    return -1;
+  case 0:
+    if (latin1_encoding.type[result] == BT_NONXML)
+      return -1;
+    break;
+  case 0xFF:
+    if (result == 0xFFFE || result == 0xFFFF)
+      return -1;
+    break;
+  }
+  return result;
+}
+
+int FASTCALL
+XmlUtf8Encode(int c, char *buf) {
+  enum {
+    /* minN is minimum legal resulting value for N byte sequence */
+    min2 = 0x80,
+    min3 = 0x800,
+    min4 = 0x10000
+  };
+
+  if (c < 0)
+    return 0; /* LCOV_EXCL_LINE: this case is always eliminated beforehand */
+  if (c < min2) {
+    buf[0] = (char)(c | UTF8_cval1);
+    return 1;
+  }
+  if (c < min3) {
+    buf[0] = (char)((c >> 6) | UTF8_cval2);
+    buf[1] = (char)((c & 0x3f) | 0x80);
+    return 2;
+  }
+  if (c < min4) {
+    buf[0] = (char)((c >> 12) | UTF8_cval3);
+    buf[1] = (char)(((c >> 6) & 0x3f) | 0x80);
+    buf[2] = (char)((c & 0x3f) | 0x80);
+    return 3;
+  }
+  if (c < 0x110000) {
+    buf[0] = (char)((c >> 18) | UTF8_cval4);
+    buf[1] = (char)(((c >> 12) & 0x3f) | 0x80);
+    buf[2] = (char)(((c >> 6) & 0x3f) | 0x80);
+    buf[3] = (char)((c & 0x3f) | 0x80);
+    return 4;
+  }
+  return 0; /* LCOV_EXCL_LINE: this case too is eliminated before calling */
+}
+
+int FASTCALL
+XmlUtf16Encode(int charNum, unsigned short *buf) {
+  if (charNum < 0)
+    return 0;
+  if (charNum < 0x10000) {
+    buf[0] = (unsigned short)charNum;
+    return 1;
+  }
+  if (charNum < 0x110000) {
+    charNum -= 0x10000;
+    buf[0] = (unsigned short)((charNum >> 10) + 0xD800);
+    buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00);
+    return 2;
+  }
+  return 0;
+}
+
+struct unknown_encoding {
+  struct normal_encoding normal;
+  CONVERTER convert;
+  void *userData;
+  unsigned short utf16[256];
+  char utf8[256][4];
+};
+
+#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *)(enc))
+
+int
+XmlSizeOfUnknownEncoding(void) {
+  return sizeof(struct unknown_encoding);
+}
+
+static int PTRFASTCALL
+unknown_isName(const ENCODING *enc, const char *p) {
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  int c = uenc->convert(uenc->userData, p);
+  if (c & ~0xFFFF)
+    return 0;
+  return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF);
+}
+
+static int PTRFASTCALL
+unknown_isNmstrt(const ENCODING *enc, const char *p) {
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  int c = uenc->convert(uenc->userData, p);
+  if (c & ~0xFFFF)
+    return 0;
+  return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF);
+}
+
+static int PTRFASTCALL
+unknown_isInvalid(const ENCODING *enc, const char *p) {
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  int c = uenc->convert(uenc->userData, p);
+  return (c & ~0xFFFF) || checkCharRefNumber(c) < 0;
+}
+
+static enum XML_Convert_Result PTRCALL
+unknown_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim,
+               char **toP, const char *toLim) {
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  char buf[XML_UTF8_ENCODE_MAX];
+  for (;;) {
+    const char *utf8;
+    int n;
+    if (*fromP == fromLim)
+      return XML_CONVERT_COMPLETED;
+    utf8 = uenc->utf8[(unsigned char)**fromP];
+    n = *utf8++;
+    if (n == 0) {
+      int c = uenc->convert(uenc->userData, *fromP);
+      n = XmlUtf8Encode(c, buf);
+      if (n > toLim - *toP)
+        return XML_CONVERT_OUTPUT_EXHAUSTED;
+      utf8 = buf;
+      *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP]
+                 - (BT_LEAD2 - 2));
+    } else {
+      if (n > toLim - *toP)
+        return XML_CONVERT_OUTPUT_EXHAUSTED;
+      (*fromP)++;
+    }
+    memcpy(*toP, utf8, n);
+    *toP += n;
+  }
+}
+
+static enum XML_Convert_Result PTRCALL
+unknown_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim,
+                unsigned short **toP, const unsigned short *toLim) {
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  while (*fromP < fromLim && *toP < toLim) {
+    unsigned short c = uenc->utf16[(unsigned char)**fromP];
+    if (c == 0) {
+      c = (unsigned short)uenc->convert(uenc->userData, *fromP);
+      *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP]
+                 - (BT_LEAD2 - 2));
+    } else
+      (*fromP)++;
+    *(*toP)++ = c;
+  }
+
+  if ((*toP == toLim) && (*fromP < fromLim))
+    return XML_CONVERT_OUTPUT_EXHAUSTED;
+  else
+    return XML_CONVERT_COMPLETED;
+}
+
+ENCODING *
+XmlInitUnknownEncoding(void *mem, int *table, CONVERTER convert,
+                       void *userData) {
+  int i;
+  struct unknown_encoding *e = (struct unknown_encoding *)mem;
+  memcpy(mem, &latin1_encoding, sizeof(struct normal_encoding));
+  for (i = 0; i < 128; i++)
+    if (latin1_encoding.type[i] != BT_OTHER
+        && latin1_encoding.type[i] != BT_NONXML && table[i] != i)
+      return 0;
+  for (i = 0; i < 256; i++) {
+    int c = table[i];
+    if (c == -1) {
+      e->normal.type[i] = BT_MALFORM;
+      /* This shouldn't really get used. */
+      e->utf16[i] = 0xFFFF;
+      e->utf8[i][0] = 1;
+      e->utf8[i][1] = 0;
+    } else if (c < 0) {
+      if (c < -4)
+        return 0;
+      /* Multi-byte sequences need a converter function */
+      if (! convert)
+        return 0;
+      e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2));
+      e->utf8[i][0] = 0;
+      e->utf16[i] = 0;
+    } else if (c < 0x80) {
+      if (latin1_encoding.type[c] != BT_OTHER
+          && latin1_encoding.type[c] != BT_NONXML && c != i)
+        return 0;
+      e->normal.type[i] = latin1_encoding.type[c];
+      e->utf8[i][0] = 1;
+      e->utf8[i][1] = (char)c;
+      e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c);
+    } else if (checkCharRefNumber(c) < 0) {
+      e->normal.type[i] = BT_NONXML;
+      /* This shouldn't really get used. */
+      e->utf16[i] = 0xFFFF;
+      e->utf8[i][0] = 1;
+      e->utf8[i][1] = 0;
+    } else {
+      if (c > 0xFFFF)
+        return 0;
+      if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff))
+        e->normal.type[i] = BT_NMSTRT;
+      else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff))
+        e->normal.type[i] = BT_NAME;
+      else
+        e->normal.type[i] = BT_OTHER;
+      e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1);
+      e->utf16[i] = (unsigned short)c;
+    }
+  }
+  e->userData = userData;
+  e->convert = convert;
+  if (convert) {
+    e->normal.isName2 = unknown_isName;
+    e->normal.isName3 = unknown_isName;
+    e->normal.isName4 = unknown_isName;
+    e->normal.isNmstrt2 = unknown_isNmstrt;
+    e->normal.isNmstrt3 = unknown_isNmstrt;
+    e->normal.isNmstrt4 = unknown_isNmstrt;
+    e->normal.isInvalid2 = unknown_isInvalid;
+    e->normal.isInvalid3 = unknown_isInvalid;
+    e->normal.isInvalid4 = unknown_isInvalid;
+  }
+  e->normal.enc.utf8Convert = unknown_toUtf8;
+  e->normal.enc.utf16Convert = unknown_toUtf16;
+  return &(e->normal.enc);
+}
+
+/* If this enumeration is changed, getEncodingIndex and encodings
+must also be changed. */
+enum {
+  UNKNOWN_ENC = -1,
+  ISO_8859_1_ENC = 0,
+  US_ASCII_ENC,
+  UTF_8_ENC,
+  UTF_16_ENC,
+  UTF_16BE_ENC,
+  UTF_16LE_ENC,
+  /* must match encodingNames up to here */
+  NO_ENC
+};
+
+static const char KW_ISO_8859_1[]
+    = {ASCII_I, ASCII_S, ASCII_O,     ASCII_MINUS, ASCII_8, ASCII_8,
+       ASCII_5, ASCII_9, ASCII_MINUS, ASCII_1,     '\0'};
+static const char KW_US_ASCII[]
+    = {ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S,
+       ASCII_C, ASCII_I, ASCII_I,     '\0'};
+static const char KW_UTF_8[]
+    = {ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0'};
+static const char KW_UTF_16[]
+    = {ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0'};
+static const char KW_UTF_16BE[]
+    = {ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1,
+       ASCII_6, ASCII_B, ASCII_E, '\0'};
+static const char KW_UTF_16LE[]
+    = {ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1,
+       ASCII_6, ASCII_L, ASCII_E, '\0'};
+
+static int FASTCALL
+getEncodingIndex(const char *name) {
+  static const char *const encodingNames[] = {
+      KW_ISO_8859_1, KW_US_ASCII, KW_UTF_8, KW_UTF_16, KW_UTF_16BE, KW_UTF_16LE,
+  };
+  int i;
+  if (name == NULL)
+    return NO_ENC;
+  for (i = 0; i < (int)(sizeof(encodingNames) / sizeof(encodingNames[0])); i++)
+    if (streqci(name, encodingNames[i]))
+      return i;
+  return UNKNOWN_ENC;
+}
+
+/* For binary compatibility, we store the index of the encoding
+   specified at initialization in the isUtf16 member.
+*/
+
+#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16)
+#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i)
+
+/* This is what detects the encoding.  encodingTable maps from
+   encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of
+   the external (protocol) specified encoding; state is
+   XML_CONTENT_STATE if we're parsing an external text entity, and
+   XML_PROLOG_STATE otherwise.
+*/
+
+static int
+initScan(const ENCODING *const *encodingTable, const INIT_ENCODING *enc,
+         int state, const char *ptr, const char *end, const char **nextTokPtr) {
+  const ENCODING **encPtr;
+
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  encPtr = enc->encPtr;
+  if (ptr + 1 == end) {
+    /* only a single byte available for auto-detection */
+#ifndef XML_DTD /* FIXME */
+    /* a well-formed document entity must have more than one byte */
+    if (state != XML_CONTENT_STATE)
+      return XML_TOK_PARTIAL;
+#endif
+    /* so we're parsing an external text entity... */
+    /* if UTF-16 was externally specified, then we need at least 2 bytes */
+    switch (INIT_ENC_INDEX(enc)) {
+    case UTF_16_ENC:
+    case UTF_16LE_ENC:
+    case UTF_16BE_ENC:
+      return XML_TOK_PARTIAL;
+    }
+    switch ((unsigned char)*ptr) {
+    case 0xFE:
+    case 0xFF:
+    case 0xEF: /* possibly first byte of UTF-8 BOM */
+      if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC && state == XML_CONTENT_STATE)
+        break;
+      /* fall through */
+    case 0x00:
+    case 0x3C:
+      return XML_TOK_PARTIAL;
+    }
+  } else {
+    switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) {
+    case 0xFEFF:
+      if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC && state == XML_CONTENT_STATE)
+        break;
+      *nextTokPtr = ptr + 2;
+      *encPtr = encodingTable[UTF_16BE_ENC];
+      return XML_TOK_BOM;
+    /* 00 3C is handled in the default case */
+    case 0x3C00:
+      if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC
+           || INIT_ENC_INDEX(enc) == UTF_16_ENC)
+          && state == XML_CONTENT_STATE)
+        break;
+      *encPtr = encodingTable[UTF_16LE_ENC];
+      return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+    case 0xFFFE:
+      if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC && state == XML_CONTENT_STATE)
+        break;
+      *nextTokPtr = ptr + 2;
+      *encPtr = encodingTable[UTF_16LE_ENC];
+      return XML_TOK_BOM;
+    case 0xEFBB:
+      /* Maybe a UTF-8 BOM (EF BB BF) */
+      /* If there's an explicitly specified (external) encoding
+         of ISO-8859-1 or some flavour of UTF-16
+         and this is an external text entity,
+         don't look for the BOM,
+         because it might be a legal data.
+      */
+      if (state == XML_CONTENT_STATE) {
+        int e = INIT_ENC_INDEX(enc);
+        if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC || e == UTF_16LE_ENC
+            || e == UTF_16_ENC)
+          break;
+      }
+      if (ptr + 2 == end)
+        return XML_TOK_PARTIAL;
+      if ((unsigned char)ptr[2] == 0xBF) {
+        *nextTokPtr = ptr + 3;
+        *encPtr = encodingTable[UTF_8_ENC];
+        return XML_TOK_BOM;
+      }
+      break;
+    default:
+      if (ptr[0] == '\0') {
+        /* 0 isn't a legal data character. Furthermore a document
+           entity can only start with ASCII characters.  So the only
+           way this can fail to be big-endian UTF-16 if it it's an
+           external parsed general entity that's labelled as
+           UTF-16LE.
+        */
+        if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC)
+          break;
+        *encPtr = encodingTable[UTF_16BE_ENC];
+        return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+      } else if (ptr[1] == '\0') {
+        /* We could recover here in the case:
+            - parsing an external entity
+            - second byte is 0
+            - no externally specified encoding
+            - no encoding declaration
+           by assuming UTF-16LE.  But we don't, because this would mean when
+           presented just with a single byte, we couldn't reliably determine
+           whether we needed further bytes.
+        */
+        if (state == XML_CONTENT_STATE)
+          break;
+        *encPtr = encodingTable[UTF_16LE_ENC];
+        return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+      }
+      break;
+    }
+  }
+  *encPtr = encodingTable[INIT_ENC_INDEX(enc)];
+  return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+}
+
+#define NS(x) x
+#define ns(x) x
+#define XML_TOK_NS_C
+#include "xmltok_ns.c"
+#undef XML_TOK_NS_C
+#undef NS
+#undef ns
+
+#ifdef XML_NS
+
+#  define NS(x) x##NS
+#  define ns(x) x##_ns
+
+#  define XML_TOK_NS_C
+#  include "xmltok_ns.c"
+#  undef XML_TOK_NS_C
+
+#  undef NS
+#  undef ns
+
+ENCODING *
+XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert,
+                         void *userData) {
+  ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData);
+  if (enc)
+    ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON;
+  return enc;
+}
+
+#endif /* XML_NS */
diff --git a/dummy_fe/libexpat/xmltok.h b/dummy_fe/libexpat/xmltok.h
new file mode 100644
index 0000000..6f630c2
--- /dev/null
+++ b/dummy_fe/libexpat/xmltok.h
@@ -0,0 +1,319 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2005 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef XmlTok_INCLUDED
+#define XmlTok_INCLUDED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following token may be returned by XmlContentTok */
+#define XML_TOK_TRAILING_RSQB                                                  \
+  -5 /* ] or ]] at the end of the scan; might be                               \
+        start of illegal ]]> sequence */
+/* The following tokens may be returned by both XmlPrologTok and
+   XmlContentTok.
+*/
+#define XML_TOK_NONE -4 /* The string to be scanned is empty */
+#define XML_TOK_TRAILING_CR                                                    \
+  -3                            /* A CR at the end of the scan;                \
+                                   might be part of CRLF sequence */
+#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
+#define XML_TOK_PARTIAL -1      /* only part of a token */
+#define XML_TOK_INVALID 0
+
+/* The following tokens are returned by XmlContentTok; some are also
+   returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok.
+*/
+#define XML_TOK_START_TAG_WITH_ATTS 1
+#define XML_TOK_START_TAG_NO_ATTS 2
+#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
+#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
+#define XML_TOK_END_TAG 5
+#define XML_TOK_DATA_CHARS 6
+#define XML_TOK_DATA_NEWLINE 7
+#define XML_TOK_CDATA_SECT_OPEN 8
+#define XML_TOK_ENTITY_REF 9
+#define XML_TOK_CHAR_REF 10 /* numeric character reference */
+
+/* The following tokens may be returned by both XmlPrologTok and
+   XmlContentTok.
+*/
+#define XML_TOK_PI 11       /* processing instruction */
+#define XML_TOK_XML_DECL 12 /* XML decl or text decl */
+#define XML_TOK_COMMENT 13
+#define XML_TOK_BOM 14 /* Byte order mark */
+
+/* The following tokens are returned only by XmlPrologTok */
+#define XML_TOK_PROLOG_S 15
+#define XML_TOK_DECL_OPEN 16  /* <!foo */
+#define XML_TOK_DECL_CLOSE 17 /* > */
+#define XML_TOK_NAME 18
+#define XML_TOK_NMTOKEN 19
+#define XML_TOK_POUND_NAME 20 /* #name */
+#define XML_TOK_OR 21         /* | */
+#define XML_TOK_PERCENT 22
+#define XML_TOK_OPEN_PAREN 23
+#define XML_TOK_CLOSE_PAREN 24
+#define XML_TOK_OPEN_BRACKET 25
+#define XML_TOK_CLOSE_BRACKET 26
+#define XML_TOK_LITERAL 27
+#define XML_TOK_PARAM_ENTITY_REF 28
+#define XML_TOK_INSTANCE_START 29
+
+/* The following occur only in element type declarations */
+#define XML_TOK_NAME_QUESTION 30        /* name? */
+#define XML_TOK_NAME_ASTERISK 31        /* name* */
+#define XML_TOK_NAME_PLUS 32            /* name+ */
+#define XML_TOK_COND_SECT_OPEN 33       /* <![ */
+#define XML_TOK_COND_SECT_CLOSE 34      /* ]]> */
+#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
+#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
+#define XML_TOK_CLOSE_PAREN_PLUS 37     /* )+ */
+#define XML_TOK_COMMA 38
+
+/* The following token is returned only by XmlAttributeValueTok */
+#define XML_TOK_ATTRIBUTE_VALUE_S 39
+
+/* The following token is returned only by XmlCdataSectionTok */
+#define XML_TOK_CDATA_SECT_CLOSE 40
+
+/* With namespace processing this is returned by XmlPrologTok for a
+   name with a colon.
+*/
+#define XML_TOK_PREFIXED_NAME 41
+
+#ifdef XML_DTD
+#  define XML_TOK_IGNORE_SECT 42
+#endif /* XML_DTD */
+
+#ifdef XML_DTD
+#  define XML_N_STATES 4
+#else /* not XML_DTD */
+#  define XML_N_STATES 3
+#endif /* not XML_DTD */
+
+#define XML_PROLOG_STATE 0
+#define XML_CONTENT_STATE 1
+#define XML_CDATA_SECTION_STATE 2
+#ifdef XML_DTD
+#  define XML_IGNORE_SECTION_STATE 3
+#endif /* XML_DTD */
+
+#define XML_N_LITERAL_TYPES 2
+#define XML_ATTRIBUTE_VALUE_LITERAL 0
+#define XML_ENTITY_VALUE_LITERAL 1
+
+/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
+#define XML_UTF8_ENCODE_MAX 4
+/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
+#define XML_UTF16_ENCODE_MAX 2
+
+typedef struct position {
+  /* first line and first column are 0 not 1 */
+  XML_Size lineNumber;
+  XML_Size columnNumber;
+} POSITION;
+
+typedef struct {
+  const char *name;
+  const char *valuePtr;
+  const char *valueEnd;
+  char normalized;
+} ATTRIBUTE;
+
+struct encoding;
+typedef struct encoding ENCODING;
+
+typedef int(PTRCALL *SCANNER)(const ENCODING *, const char *, const char *,
+                              const char **);
+
+enum XML_Convert_Result {
+  XML_CONVERT_COMPLETED = 0,
+  XML_CONVERT_INPUT_INCOMPLETE = 1,
+  XML_CONVERT_OUTPUT_EXHAUSTED
+  = 2 /* and therefore potentially input remaining as well */
+};
+
+struct encoding {
+  SCANNER scanners[XML_N_STATES];
+  SCANNER literalScanners[XML_N_LITERAL_TYPES];
+  int(PTRCALL *nameMatchesAscii)(const ENCODING *, const char *, const char *,
+                                 const char *);
+  int(PTRFASTCALL *nameLength)(const ENCODING *, const char *);
+  const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *);
+  int(PTRCALL *getAtts)(const ENCODING *enc, const char *ptr, int attsMax,
+                        ATTRIBUTE *atts);
+  int(PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr);
+  int(PTRCALL *predefinedEntityName)(const ENCODING *, const char *,
+                                     const char *);
+  void(PTRCALL *updatePosition)(const ENCODING *, const char *ptr,
+                                const char *end, POSITION *);
+  int(PTRCALL *isPublicId)(const ENCODING *enc, const char *ptr,
+                           const char *end, const char **badPtr);
+  enum XML_Convert_Result(PTRCALL *utf8Convert)(const ENCODING *enc,
+                                                const char **fromP,
+                                                const char *fromLim, char **toP,
+                                                const char *toLim);
+  enum XML_Convert_Result(PTRCALL *utf16Convert)(const ENCODING *enc,
+                                                 const char **fromP,
+                                                 const char *fromLim,
+                                                 unsigned short **toP,
+                                                 const unsigned short *toLim);
+  int minBytesPerChar;
+  char isUtf8;
+  char isUtf16;
+};
+
+/* Scan the string starting at ptr until the end of the next complete
+   token, but do not scan past eptr.  Return an integer giving the
+   type of token.
+
+   Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set.
+
+   Return XML_TOK_PARTIAL when the string does not contain a complete
+   token; nextTokPtr will not be set.
+
+   Return XML_TOK_INVALID when the string does not start a valid
+   token; nextTokPtr will be set to point to the character which made
+   the token invalid.
+
+   Otherwise the string starts with a valid token; nextTokPtr will be
+   set to point to the character following the end of that token.
+
+   Each data character counts as a single token, but adjacent data
+   characters may be returned together.  Similarly for characters in
+   the prolog outside literals, comments and processing instructions.
+*/
+
+#define XmlTok(enc, state, ptr, end, nextTokPtr)                               \
+  (((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
+
+#define XmlPrologTok(enc, ptr, end, nextTokPtr)                                \
+  XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
+
+#define XmlContentTok(enc, ptr, end, nextTokPtr)                               \
+  XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
+
+#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr)                          \
+  XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
+
+#ifdef XML_DTD
+
+#  define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr)                       \
+    XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr)
+
+#endif /* XML_DTD */
+
+/* This is used for performing a 2nd-level tokenization on the content
+   of a literal that has already been returned by XmlTok.
+*/
+#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr)                  \
+  (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
+
+#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr)                        \
+  XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlEntityValueTok(enc, ptr, end, nextTokPtr)                           \
+  XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2)                             \
+  (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
+
+#define XmlNameLength(enc, ptr) (((enc)->nameLength)(enc, ptr))
+
+#define XmlSkipS(enc, ptr) (((enc)->skipS)(enc, ptr))
+
+#define XmlGetAttributes(enc, ptr, attsMax, atts)                              \
+  (((enc)->getAtts)(enc, ptr, attsMax, atts))
+
+#define XmlCharRefNumber(enc, ptr) (((enc)->charRefNumber)(enc, ptr))
+
+#define XmlPredefinedEntityName(enc, ptr, end)                                 \
+  (((enc)->predefinedEntityName)(enc, ptr, end))
+
+#define XmlUpdatePosition(enc, ptr, end, pos)                                  \
+  (((enc)->updatePosition)(enc, ptr, end, pos))
+
+#define XmlIsPublicId(enc, ptr, end, badPtr)                                   \
+  (((enc)->isPublicId)(enc, ptr, end, badPtr))
+
+#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim)                        \
+  (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
+
+#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim)                       \
+  (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
+
+typedef struct {
+  ENCODING initEnc;
+  const ENCODING **encPtr;
+} INIT_ENCODING;
+
+int XmlParseXmlDecl(int isGeneralTextEntity, const ENCODING *enc,
+                    const char *ptr, const char *end, const char **badPtr,
+                    const char **versionPtr, const char **versionEndPtr,
+                    const char **encodingNamePtr,
+                    const ENCODING **namedEncodingPtr, int *standalonePtr);
+
+int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING *XmlGetUtf8InternalEncoding(void);
+const ENCODING *XmlGetUtf16InternalEncoding(void);
+int FASTCALL XmlUtf8Encode(int charNumber, char *buf);
+int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf);
+int XmlSizeOfUnknownEncoding(void);
+
+typedef int(XMLCALL *CONVERTER)(void *userData, const char *p);
+
+ENCODING *XmlInitUnknownEncoding(void *mem, int *table, CONVERTER convert,
+                                 void *userData);
+
+int XmlParseXmlDeclNS(int isGeneralTextEntity, const ENCODING *enc,
+                      const char *ptr, const char *end, const char **badPtr,
+                      const char **versionPtr, const char **versionEndPtr,
+                      const char **encodingNamePtr,
+                      const ENCODING **namedEncodingPtr, int *standalonePtr);
+
+int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING *XmlGetUtf8InternalEncodingNS(void);
+const ENCODING *XmlGetUtf16InternalEncodingNS(void);
+ENCODING *XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert,
+                                   void *userData);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlTok_INCLUDED */
diff --git a/dummy_fe/libexpat/xmltok_impl.c b/dummy_fe/libexpat/xmltok_impl.c
new file mode 100644
index 0000000..1971d74
--- /dev/null
+++ b/dummy_fe/libexpat/xmltok_impl.c
@@ -0,0 +1,1819 @@
+/* This file is included (from xmltok.c, 1-3 times depending on XML_MIN_SIZE)!
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2018      Benjamin Peterson <benjamin@python.org>
+   Copyright (c) 2018      Anton Maklakov <antmak.pub@gmail.com>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Boris Kolpackov <boris@codesynthesis.com>
+   Copyright (c) 2022      Martin Ettl <ettl.martin78@googlemail.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef XML_TOK_IMPL_C
+
+#  ifndef IS_INVALID_CHAR // i.e. for UTF-16 and XML_MIN_SIZE not defined
+#    define IS_INVALID_CHAR(enc, ptr, n) (0)
+#  endif
+
+#  define INVALID_LEAD_CASE(n, ptr, nextTokPtr)                                \
+  case BT_LEAD##n:                                                             \
+    if (end - ptr < n)                                                         \
+      return XML_TOK_PARTIAL_CHAR;                                             \
+    if (IS_INVALID_CHAR(enc, ptr, n)) {                                        \
+      *(nextTokPtr) = (ptr);                                                   \
+      return XML_TOK_INVALID;                                                  \
+    }                                                                          \
+    ptr += n;                                                                  \
+    break;
+
+#  define INVALID_CASES(ptr, nextTokPtr)                                       \
+    INVALID_LEAD_CASE(2, ptr, nextTokPtr)                                      \
+    INVALID_LEAD_CASE(3, ptr, nextTokPtr)                                      \
+    INVALID_LEAD_CASE(4, ptr, nextTokPtr)                                      \
+  case BT_NONXML:                                                              \
+  case BT_MALFORM:                                                             \
+  case BT_TRAIL:                                                               \
+    *(nextTokPtr) = (ptr);                                                     \
+    return XML_TOK_INVALID;
+
+#  define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr)                        \
+  case BT_LEAD##n:                                                             \
+    if (end - ptr < n)                                                         \
+      return XML_TOK_PARTIAL_CHAR;                                             \
+    if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NAME_CHAR(enc, ptr, n)) {         \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_INVALID;                                                  \
+    }                                                                          \
+    ptr += n;                                                                  \
+    break;
+
+#  define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)                          \
+  case BT_NONASCII:                                                            \
+    if (! IS_NAME_CHAR_MINBPC(enc, ptr)) {                                     \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_INVALID;                                                  \
+    }                                                                          \
+    /* fall through */                                                         \
+  case BT_NMSTRT:                                                              \
+  case BT_HEX:                                                                 \
+  case BT_DIGIT:                                                               \
+  case BT_NAME:                                                                \
+  case BT_MINUS:                                                               \
+    ptr += MINBPC(enc);                                                        \
+    break;                                                                     \
+    CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr)                              \
+    CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr)                              \
+    CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr)
+
+#  define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr)                      \
+  case BT_LEAD##n:                                                             \
+    if ((end) - (ptr) < (n))                                                   \
+      return XML_TOK_PARTIAL_CHAR;                                             \
+    if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) {       \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_INVALID;                                                  \
+    }                                                                          \
+    ptr += n;                                                                  \
+    break;
+
+#  define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)                        \
+  case BT_NONASCII:                                                            \
+    if (! IS_NMSTRT_CHAR_MINBPC(enc, ptr)) {                                   \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_INVALID;                                                  \
+    }                                                                          \
+    /* fall through */                                                         \
+  case BT_NMSTRT:                                                              \
+  case BT_HEX:                                                                 \
+    ptr += MINBPC(enc);                                                        \
+    break;                                                                     \
+    CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr)                            \
+    CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr)                            \
+    CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr)
+
+#  ifndef PREFIX
+#    define PREFIX(ident) ident
+#  endif
+
+#  define HAS_CHARS(enc, ptr, end, count)                                      \
+    ((end) - (ptr) >= ((count)*MINBPC(enc)))
+
+#  define HAS_CHAR(enc, ptr, end) HAS_CHARS(enc, ptr, end, 1)
+
+#  define REQUIRE_CHARS(enc, ptr, end, count)                                  \
+    {                                                                          \
+      if (! HAS_CHARS(enc, ptr, end, count)) {                                 \
+        return XML_TOK_PARTIAL;                                                \
+      }                                                                        \
+    }
+
+#  define REQUIRE_CHAR(enc, ptr, end) REQUIRE_CHARS(enc, ptr, end, 1)
+
+/* ptr points to character following "<!-" */
+
+static int PTRCALL
+PREFIX(scanComment)(const ENCODING *enc, const char *ptr, const char *end,
+                    const char **nextTokPtr) {
+  if (HAS_CHAR(enc, ptr, end)) {
+    if (! CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+    ptr += MINBPC(enc);
+    while (HAS_CHAR(enc, ptr, end)) {
+      switch (BYTE_TYPE(enc, ptr)) {
+        INVALID_CASES(ptr, nextTokPtr)
+      case BT_MINUS:
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        if (CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
+          ptr += MINBPC(enc);
+          REQUIRE_CHAR(enc, ptr, end);
+          if (! CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+          }
+          *nextTokPtr = ptr + MINBPC(enc);
+          return XML_TOK_COMMENT;
+        }
+        break;
+      default:
+        ptr += MINBPC(enc);
+        break;
+      }
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<!" */
+
+static int PTRCALL
+PREFIX(scanDecl)(const ENCODING *enc, const char *ptr, const char *end,
+                 const char **nextTokPtr) {
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+  case BT_MINUS:
+    return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_LSQB:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_COND_SECT_OPEN;
+  case BT_NMSTRT:
+  case BT_HEX:
+    ptr += MINBPC(enc);
+    break;
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_PERCNT:
+      REQUIRE_CHARS(enc, ptr, end, 2);
+      /* don't allow <!ENTITY% foo "whatever"> */
+      switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) {
+      case BT_S:
+      case BT_CR:
+      case BT_LF:
+      case BT_PERCNT:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      /* fall through */
+    case BT_S:
+    case BT_CR:
+    case BT_LF:
+      *nextTokPtr = ptr;
+      return XML_TOK_DECL_OPEN;
+    case BT_NMSTRT:
+    case BT_HEX:
+      ptr += MINBPC(enc);
+      break;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, const char *end,
+                      int *tokPtr) {
+  int upper = 0;
+  UNUSED_P(enc);
+  *tokPtr = XML_TOK_PI;
+  if (end - ptr != MINBPC(enc) * 3)
+    return 1;
+  switch (BYTE_TO_ASCII(enc, ptr)) {
+  case ASCII_x:
+    break;
+  case ASCII_X:
+    upper = 1;
+    break;
+  default:
+    return 1;
+  }
+  ptr += MINBPC(enc);
+  switch (BYTE_TO_ASCII(enc, ptr)) {
+  case ASCII_m:
+    break;
+  case ASCII_M:
+    upper = 1;
+    break;
+  default:
+    return 1;
+  }
+  ptr += MINBPC(enc);
+  switch (BYTE_TO_ASCII(enc, ptr)) {
+  case ASCII_l:
+    break;
+  case ASCII_L:
+    upper = 1;
+    break;
+  default:
+    return 1;
+  }
+  if (upper)
+    return 0;
+  *tokPtr = XML_TOK_XML_DECL;
+  return 1;
+}
+
+/* ptr points to character following "<?" */
+
+static int PTRCALL
+PREFIX(scanPi)(const ENCODING *enc, const char *ptr, const char *end,
+               const char **nextTokPtr) {
+  int tok;
+  const char *target = ptr;
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_S:
+    case BT_CR:
+    case BT_LF:
+      if (! PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      ptr += MINBPC(enc);
+      while (HAS_CHAR(enc, ptr, end)) {
+        switch (BYTE_TYPE(enc, ptr)) {
+          INVALID_CASES(ptr, nextTokPtr)
+        case BT_QUEST:
+          ptr += MINBPC(enc);
+          REQUIRE_CHAR(enc, ptr, end);
+          if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+            *nextTokPtr = ptr + MINBPC(enc);
+            return tok;
+          }
+          break;
+        default:
+          ptr += MINBPC(enc);
+          break;
+        }
+      }
+      return XML_TOK_PARTIAL;
+    case BT_QUEST:
+      if (! PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+        *nextTokPtr = ptr + MINBPC(enc);
+        return tok;
+      }
+      /* fall through */
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(scanCdataSection)(const ENCODING *enc, const char *ptr, const char *end,
+                         const char **nextTokPtr) {
+  static const char CDATA_LSQB[]
+      = {ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, ASCII_LSQB};
+  int i;
+  UNUSED_P(enc);
+  /* CDATA[ */
+  REQUIRE_CHARS(enc, ptr, end, 6);
+  for (i = 0; i < 6; i++, ptr += MINBPC(enc)) {
+    if (! CHAR_MATCHES(enc, ptr, CDATA_LSQB[i])) {
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_CDATA_SECT_OPEN;
+}
+
+static int PTRCALL
+PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, const char *end,
+                        const char **nextTokPtr) {
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  if (MINBPC(enc) > 1) {
+    size_t n = end - ptr;
+    if (n & (MINBPC(enc) - 1)) {
+      n &= ~(MINBPC(enc) - 1);
+      if (n == 0)
+        return XML_TOK_PARTIAL;
+      end = ptr + n;
+    }
+  }
+  switch (BYTE_TYPE(enc, ptr)) {
+  case BT_RSQB:
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
+    if (! CHAR_MATCHES(enc, ptr, ASCII_RSQB))
+      break;
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
+    if (! CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+      ptr -= MINBPC(enc);
+      break;
+    }
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_CDATA_SECT_CLOSE;
+  case BT_CR:
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
+    if (BYTE_TYPE(enc, ptr) == BT_LF)
+      ptr += MINBPC(enc);
+    *nextTokPtr = ptr;
+    return XML_TOK_DATA_NEWLINE;
+  case BT_LF:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_DATA_NEWLINE;
+    INVALID_CASES(ptr, nextTokPtr)
+  default:
+    ptr += MINBPC(enc);
+    break;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#  define LEAD_CASE(n)                                                         \
+  case BT_LEAD##n:                                                             \
+    if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) {                       \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_DATA_CHARS;                                               \
+    }                                                                          \
+    ptr += n;                                                                  \
+    break;
+      LEAD_CASE(2)
+      LEAD_CASE(3)
+      LEAD_CASE(4)
+#  undef LEAD_CASE
+    case BT_NONXML:
+    case BT_MALFORM:
+    case BT_TRAIL:
+    case BT_CR:
+    case BT_LF:
+    case BT_RSQB:
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "</" */
+
+static int PTRCALL
+PREFIX(scanEndTag)(const ENCODING *enc, const char *ptr, const char *end,
+                   const char **nextTokPtr) {
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_S:
+    case BT_CR:
+    case BT_LF:
+      for (ptr += MINBPC(enc); HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_S:
+        case BT_CR:
+        case BT_LF:
+          break;
+        case BT_GT:
+          *nextTokPtr = ptr + MINBPC(enc);
+          return XML_TOK_END_TAG;
+        default:
+          *nextTokPtr = ptr;
+          return XML_TOK_INVALID;
+        }
+      }
+      return XML_TOK_PARTIAL;
+#  ifdef XML_NS
+    case BT_COLON:
+      /* no need to check qname syntax here,
+         since end-tag must match exactly */
+      ptr += MINBPC(enc);
+      break;
+#  endif
+    case BT_GT:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_END_TAG;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#X" */
+
+static int PTRCALL
+PREFIX(scanHexCharRef)(const ENCODING *enc, const char *ptr, const char *end,
+                       const char **nextTokPtr) {
+  if (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_DIGIT:
+    case BT_HEX:
+      break;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+    for (ptr += MINBPC(enc); HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_DIGIT:
+      case BT_HEX:
+        break;
+      case BT_SEMI:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_CHAR_REF;
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#" */
+
+static int PTRCALL
+PREFIX(scanCharRef)(const ENCODING *enc, const char *ptr, const char *end,
+                    const char **nextTokPtr) {
+  if (HAS_CHAR(enc, ptr, end)) {
+    if (CHAR_MATCHES(enc, ptr, ASCII_x))
+      return PREFIX(scanHexCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_DIGIT:
+      break;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+    for (ptr += MINBPC(enc); HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_DIGIT:
+        break;
+      case BT_SEMI:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_CHAR_REF;
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&" */
+
+static int PTRCALL
+PREFIX(scanRef)(const ENCODING *enc, const char *ptr, const char *end,
+                const char **nextTokPtr) {
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  case BT_NUM:
+    return PREFIX(scanCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_SEMI:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_ENTITY_REF;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following first character of attribute name */
+
+static int PTRCALL
+PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end,
+                 const char **nextTokPtr) {
+#  ifdef XML_NS
+  int hadColon = 0;
+#  endif
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#  ifdef XML_NS
+    case BT_COLON:
+      if (hadColon) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      hadColon = 1;
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      switch (BYTE_TYPE(enc, ptr)) {
+        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      break;
+#  endif
+    case BT_S:
+    case BT_CR:
+    case BT_LF:
+      for (;;) {
+        int t;
+
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        t = BYTE_TYPE(enc, ptr);
+        if (t == BT_EQUALS)
+          break;
+        switch (t) {
+        case BT_S:
+        case BT_LF:
+        case BT_CR:
+          break;
+        default:
+          *nextTokPtr = ptr;
+          return XML_TOK_INVALID;
+        }
+      }
+      /* fall through */
+    case BT_EQUALS: {
+      int open;
+#  ifdef XML_NS
+      hadColon = 0;
+#  endif
+      for (;;) {
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        open = BYTE_TYPE(enc, ptr);
+        if (open == BT_QUOT || open == BT_APOS)
+          break;
+        switch (open) {
+        case BT_S:
+        case BT_LF:
+        case BT_CR:
+          break;
+        default:
+          *nextTokPtr = ptr;
+          return XML_TOK_INVALID;
+        }
+      }
+      ptr += MINBPC(enc);
+      /* in attribute value */
+      for (;;) {
+        int t;
+        REQUIRE_CHAR(enc, ptr, end);
+        t = BYTE_TYPE(enc, ptr);
+        if (t == open)
+          break;
+        switch (t) {
+          INVALID_CASES(ptr, nextTokPtr)
+        case BT_AMP: {
+          int tok = PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, &ptr);
+          if (tok <= 0) {
+            if (tok == XML_TOK_INVALID)
+              *nextTokPtr = ptr;
+            return tok;
+          }
+          break;
+        }
+        case BT_LT:
+          *nextTokPtr = ptr;
+          return XML_TOK_INVALID;
+        default:
+          ptr += MINBPC(enc);
+          break;
+        }
+      }
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_S:
+      case BT_CR:
+      case BT_LF:
+        break;
+      case BT_SOL:
+        goto sol;
+      case BT_GT:
+        goto gt;
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      /* ptr points to closing quote */
+      for (;;) {
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        switch (BYTE_TYPE(enc, ptr)) {
+          CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+        case BT_S:
+        case BT_CR:
+        case BT_LF:
+          continue;
+        case BT_GT:
+        gt:
+          *nextTokPtr = ptr + MINBPC(enc);
+          return XML_TOK_START_TAG_WITH_ATTS;
+        case BT_SOL:
+        sol:
+          ptr += MINBPC(enc);
+          REQUIRE_CHAR(enc, ptr, end);
+          if (! CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+          }
+          *nextTokPtr = ptr + MINBPC(enc);
+          return XML_TOK_EMPTY_ELEMENT_WITH_ATTS;
+        default:
+          *nextTokPtr = ptr;
+          return XML_TOK_INVALID;
+        }
+        break;
+      }
+      break;
+    }
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<" */
+
+static int PTRCALL
+PREFIX(scanLt)(const ENCODING *enc, const char *ptr, const char *end,
+               const char **nextTokPtr) {
+#  ifdef XML_NS
+  int hadColon;
+#  endif
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  case BT_EXCL:
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_MINUS:
+      return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_LSQB:
+      return PREFIX(scanCdataSection)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  case BT_QUEST:
+    return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_SOL:
+    return PREFIX(scanEndTag)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+#  ifdef XML_NS
+  hadColon = 0;
+#  endif
+  /* we have a start-tag */
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#  ifdef XML_NS
+    case BT_COLON:
+      if (hadColon) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      hadColon = 1;
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      switch (BYTE_TYPE(enc, ptr)) {
+        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      break;
+#  endif
+    case BT_S:
+    case BT_CR:
+    case BT_LF: {
+      ptr += MINBPC(enc);
+      while (HAS_CHAR(enc, ptr, end)) {
+        switch (BYTE_TYPE(enc, ptr)) {
+          CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+        case BT_GT:
+          goto gt;
+        case BT_SOL:
+          goto sol;
+        case BT_S:
+        case BT_CR:
+        case BT_LF:
+          ptr += MINBPC(enc);
+          continue;
+        default:
+          *nextTokPtr = ptr;
+          return XML_TOK_INVALID;
+        }
+        return PREFIX(scanAtts)(enc, ptr, end, nextTokPtr);
+      }
+      return XML_TOK_PARTIAL;
+    }
+    case BT_GT:
+    gt:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_START_TAG_NO_ATTS;
+    case BT_SOL:
+    sol:
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      if (! CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_EMPTY_ELEMENT_NO_ATTS;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end,
+                   const char **nextTokPtr) {
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  if (MINBPC(enc) > 1) {
+    size_t n = end - ptr;
+    if (n & (MINBPC(enc) - 1)) {
+      n &= ~(MINBPC(enc) - 1);
+      if (n == 0)
+        return XML_TOK_PARTIAL;
+      end = ptr + n;
+    }
+  }
+  switch (BYTE_TYPE(enc, ptr)) {
+  case BT_LT:
+    return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_AMP:
+    return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_CR:
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return XML_TOK_TRAILING_CR;
+    if (BYTE_TYPE(enc, ptr) == BT_LF)
+      ptr += MINBPC(enc);
+    *nextTokPtr = ptr;
+    return XML_TOK_DATA_NEWLINE;
+  case BT_LF:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_DATA_NEWLINE;
+  case BT_RSQB:
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return XML_TOK_TRAILING_RSQB;
+    if (! CHAR_MATCHES(enc, ptr, ASCII_RSQB))
+      break;
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return XML_TOK_TRAILING_RSQB;
+    if (! CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+      ptr -= MINBPC(enc);
+      break;
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+    INVALID_CASES(ptr, nextTokPtr)
+  default:
+    ptr += MINBPC(enc);
+    break;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#  define LEAD_CASE(n)                                                         \
+  case BT_LEAD##n:                                                             \
+    if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) {                       \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_DATA_CHARS;                                               \
+    }                                                                          \
+    ptr += n;                                                                  \
+    break;
+      LEAD_CASE(2)
+      LEAD_CASE(3)
+      LEAD_CASE(4)
+#  undef LEAD_CASE
+    case BT_RSQB:
+      if (HAS_CHARS(enc, ptr, end, 2)) {
+        if (! CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) {
+          ptr += MINBPC(enc);
+          break;
+        }
+        if (HAS_CHARS(enc, ptr, end, 3)) {
+          if (! CHAR_MATCHES(enc, ptr + 2 * MINBPC(enc), ASCII_GT)) {
+            ptr += MINBPC(enc);
+            break;
+          }
+          *nextTokPtr = ptr + 2 * MINBPC(enc);
+          return XML_TOK_INVALID;
+        }
+      }
+      /* fall through */
+    case BT_AMP:
+    case BT_LT:
+    case BT_NONXML:
+    case BT_MALFORM:
+    case BT_TRAIL:
+    case BT_CR:
+    case BT_LF:
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "%" */
+
+static int PTRCALL
+PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end,
+                    const char **nextTokPtr) {
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  case BT_S:
+  case BT_LF:
+  case BT_CR:
+  case BT_PERCNT:
+    *nextTokPtr = ptr;
+    return XML_TOK_PERCENT;
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_SEMI:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_PARAM_ENTITY_REF;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end,
+                      const char **nextTokPtr) {
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_CR:
+    case BT_LF:
+    case BT_S:
+    case BT_RPAR:
+    case BT_GT:
+    case BT_PERCNT:
+    case BT_VERBAR:
+      *nextTokPtr = ptr;
+      return XML_TOK_POUND_NAME;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return -XML_TOK_POUND_NAME;
+}
+
+static int PTRCALL
+PREFIX(scanLit)(int open, const ENCODING *enc, const char *ptr, const char *end,
+                const char **nextTokPtr) {
+  while (HAS_CHAR(enc, ptr, end)) {
+    int t = BYTE_TYPE(enc, ptr);
+    switch (t) {
+      INVALID_CASES(ptr, nextTokPtr)
+    case BT_QUOT:
+    case BT_APOS:
+      ptr += MINBPC(enc);
+      if (t != open)
+        break;
+      if (! HAS_CHAR(enc, ptr, end))
+        return -XML_TOK_LITERAL;
+      *nextTokPtr = ptr;
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_S:
+      case BT_CR:
+      case BT_LF:
+      case BT_GT:
+      case BT_PERCNT:
+      case BT_LSQB:
+        return XML_TOK_LITERAL;
+      default:
+        return XML_TOK_INVALID;
+      }
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
+                  const char **nextTokPtr) {
+  int tok;
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  if (MINBPC(enc) > 1) {
+    size_t n = end - ptr;
+    if (n & (MINBPC(enc) - 1)) {
+      n &= ~(MINBPC(enc) - 1);
+      if (n == 0)
+        return XML_TOK_PARTIAL;
+      end = ptr + n;
+    }
+  }
+  switch (BYTE_TYPE(enc, ptr)) {
+  case BT_QUOT:
+    return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_APOS:
+    return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_LT: {
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_EXCL:
+      return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_QUEST:
+      return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_NMSTRT:
+    case BT_HEX:
+    case BT_NONASCII:
+    case BT_LEAD2:
+    case BT_LEAD3:
+    case BT_LEAD4:
+      *nextTokPtr = ptr - MINBPC(enc);
+      return XML_TOK_INSTANCE_START;
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  case BT_CR:
+    if (ptr + MINBPC(enc) == end) {
+      *nextTokPtr = end;
+      /* indicate that this might be part of a CR/LF pair */
+      return -XML_TOK_PROLOG_S;
+    }
+    /* fall through */
+  case BT_S:
+  case BT_LF:
+    for (;;) {
+      ptr += MINBPC(enc);
+      if (! HAS_CHAR(enc, ptr, end))
+        break;
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_S:
+      case BT_LF:
+        break;
+      case BT_CR:
+        /* don't split CR/LF pair */
+        if (ptr + MINBPC(enc) != end)
+          break;
+        /* fall through */
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_PROLOG_S;
+      }
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_PROLOG_S;
+  case BT_PERCNT:
+    return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_COMMA:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_COMMA;
+  case BT_LSQB:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_OPEN_BRACKET;
+  case BT_RSQB:
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return -XML_TOK_CLOSE_BRACKET;
+    if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
+      REQUIRE_CHARS(enc, ptr, end, 2);
+      if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) {
+        *nextTokPtr = ptr + 2 * MINBPC(enc);
+        return XML_TOK_COND_SECT_CLOSE;
+      }
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_CLOSE_BRACKET;
+  case BT_LPAR:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_OPEN_PAREN;
+  case BT_RPAR:
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return -XML_TOK_CLOSE_PAREN;
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_AST:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_CLOSE_PAREN_ASTERISK;
+    case BT_QUEST:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_CLOSE_PAREN_QUESTION;
+    case BT_PLUS:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_CLOSE_PAREN_PLUS;
+    case BT_CR:
+    case BT_LF:
+    case BT_S:
+    case BT_GT:
+    case BT_COMMA:
+    case BT_VERBAR:
+    case BT_RPAR:
+      *nextTokPtr = ptr;
+      return XML_TOK_CLOSE_PAREN;
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  case BT_VERBAR:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_OR;
+  case BT_GT:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_DECL_CLOSE;
+  case BT_NUM:
+    return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+#  define LEAD_CASE(n)                                                         \
+  case BT_LEAD##n:                                                             \
+    if (end - ptr < n)                                                         \
+      return XML_TOK_PARTIAL_CHAR;                                             \
+    if (IS_INVALID_CHAR(enc, ptr, n)) {                                        \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_INVALID;                                                  \
+    }                                                                          \
+    if (IS_NMSTRT_CHAR(enc, ptr, n)) {                                         \
+      ptr += n;                                                                \
+      tok = XML_TOK_NAME;                                                      \
+      break;                                                                   \
+    }                                                                          \
+    if (IS_NAME_CHAR(enc, ptr, n)) {                                           \
+      ptr += n;                                                                \
+      tok = XML_TOK_NMTOKEN;                                                   \
+      break;                                                                   \
+    }                                                                          \
+    *nextTokPtr = ptr;                                                         \
+    return XML_TOK_INVALID;
+    LEAD_CASE(2)
+    LEAD_CASE(3)
+    LEAD_CASE(4)
+#  undef LEAD_CASE
+  case BT_NMSTRT:
+  case BT_HEX:
+    tok = XML_TOK_NAME;
+    ptr += MINBPC(enc);
+    break;
+  case BT_DIGIT:
+  case BT_NAME:
+  case BT_MINUS:
+#  ifdef XML_NS
+  case BT_COLON:
+#  endif
+    tok = XML_TOK_NMTOKEN;
+    ptr += MINBPC(enc);
+    break;
+  case BT_NONASCII:
+    if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) {
+      ptr += MINBPC(enc);
+      tok = XML_TOK_NAME;
+      break;
+    }
+    if (IS_NAME_CHAR_MINBPC(enc, ptr)) {
+      ptr += MINBPC(enc);
+      tok = XML_TOK_NMTOKEN;
+      break;
+    }
+    /* fall through */
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_GT:
+    case BT_RPAR:
+    case BT_COMMA:
+    case BT_VERBAR:
+    case BT_LSQB:
+    case BT_PERCNT:
+    case BT_S:
+    case BT_CR:
+    case BT_LF:
+      *nextTokPtr = ptr;
+      return tok;
+#  ifdef XML_NS
+    case BT_COLON:
+      ptr += MINBPC(enc);
+      switch (tok) {
+      case XML_TOK_NAME:
+        REQUIRE_CHAR(enc, ptr, end);
+        tok = XML_TOK_PREFIXED_NAME;
+        switch (BYTE_TYPE(enc, ptr)) {
+          CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+        default:
+          tok = XML_TOK_NMTOKEN;
+          break;
+        }
+        break;
+      case XML_TOK_PREFIXED_NAME:
+        tok = XML_TOK_NMTOKEN;
+        break;
+      }
+      break;
+#  endif
+    case BT_PLUS:
+      if (tok == XML_TOK_NMTOKEN) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_NAME_PLUS;
+    case BT_AST:
+      if (tok == XML_TOK_NMTOKEN) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_NAME_ASTERISK;
+    case BT_QUEST:
+      if (tok == XML_TOK_NMTOKEN) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_NAME_QUESTION;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return -tok;
+}
+
+static int PTRCALL
+PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end,
+                          const char **nextTokPtr) {
+  const char *start;
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  else if (! HAS_CHAR(enc, ptr, end)) {
+    /* This line cannot be executed.  The incoming data has already
+     * been tokenized once, so incomplete characters like this have
+     * already been eliminated from the input.  Retaining the paranoia
+     * check is still valuable, however.
+     */
+    return XML_TOK_PARTIAL; /* LCOV_EXCL_LINE */
+  }
+  start = ptr;
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#  define LEAD_CASE(n)                                                         \
+  case BT_LEAD##n:                                                             \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
+    break;
+      LEAD_CASE(2)
+      LEAD_CASE(3)
+      LEAD_CASE(4)
+#  undef LEAD_CASE
+    case BT_AMP:
+      if (ptr == start)
+        return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_LT:
+      /* this is for inside entity references */
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    case BT_LF:
+      if (ptr == start) {
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_DATA_NEWLINE;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_CR:
+      if (ptr == start) {
+        ptr += MINBPC(enc);
+        if (! HAS_CHAR(enc, ptr, end))
+          return XML_TOK_TRAILING_CR;
+        if (BYTE_TYPE(enc, ptr) == BT_LF)
+          ptr += MINBPC(enc);
+        *nextTokPtr = ptr;
+        return XML_TOK_DATA_NEWLINE;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_S:
+      if (ptr == start) {
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_ATTRIBUTE_VALUE_S;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_DATA_CHARS;
+}
+
+static int PTRCALL
+PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end,
+                       const char **nextTokPtr) {
+  const char *start;
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  else if (! HAS_CHAR(enc, ptr, end)) {
+    /* This line cannot be executed.  The incoming data has already
+     * been tokenized once, so incomplete characters like this have
+     * already been eliminated from the input.  Retaining the paranoia
+     * check is still valuable, however.
+     */
+    return XML_TOK_PARTIAL; /* LCOV_EXCL_LINE */
+  }
+  start = ptr;
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#  define LEAD_CASE(n)                                                         \
+  case BT_LEAD##n:                                                             \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
+    break;
+      LEAD_CASE(2)
+      LEAD_CASE(3)
+      LEAD_CASE(4)
+#  undef LEAD_CASE
+    case BT_AMP:
+      if (ptr == start)
+        return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_PERCNT:
+      if (ptr == start) {
+        int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+        return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_LF:
+      if (ptr == start) {
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_DATA_NEWLINE;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_CR:
+      if (ptr == start) {
+        ptr += MINBPC(enc);
+        if (! HAS_CHAR(enc, ptr, end))
+          return XML_TOK_TRAILING_CR;
+        if (BYTE_TYPE(enc, ptr) == BT_LF)
+          ptr += MINBPC(enc);
+        *nextTokPtr = ptr;
+        return XML_TOK_DATA_NEWLINE;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_DATA_CHARS;
+}
+
+#  ifdef XML_DTD
+
+static int PTRCALL
+PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, const char *end,
+                         const char **nextTokPtr) {
+  int level = 0;
+  if (MINBPC(enc) > 1) {
+    size_t n = end - ptr;
+    if (n & (MINBPC(enc) - 1)) {
+      n &= ~(MINBPC(enc) - 1);
+      end = ptr + n;
+    }
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+      INVALID_CASES(ptr, nextTokPtr)
+    case BT_LT:
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) {
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) {
+          ++level;
+          ptr += MINBPC(enc);
+        }
+      }
+      break;
+    case BT_RSQB:
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+          ptr += MINBPC(enc);
+          if (level == 0) {
+            *nextTokPtr = ptr;
+            return XML_TOK_IGNORE_SECT;
+          }
+          --level;
+        }
+      }
+      break;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+#  endif /* XML_DTD */
+
+static int PTRCALL
+PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
+                   const char **badPtr) {
+  ptr += MINBPC(enc);
+  end -= MINBPC(enc);
+  for (; HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_DIGIT:
+    case BT_HEX:
+    case BT_MINUS:
+    case BT_APOS:
+    case BT_LPAR:
+    case BT_RPAR:
+    case BT_PLUS:
+    case BT_COMMA:
+    case BT_SOL:
+    case BT_EQUALS:
+    case BT_QUEST:
+    case BT_CR:
+    case BT_LF:
+    case BT_SEMI:
+    case BT_EXCL:
+    case BT_AST:
+    case BT_PERCNT:
+    case BT_NUM:
+#  ifdef XML_NS
+    case BT_COLON:
+#  endif
+      break;
+    case BT_S:
+      if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) {
+        *badPtr = ptr;
+        return 0;
+      }
+      break;
+    case BT_NAME:
+    case BT_NMSTRT:
+      if (! (BYTE_TO_ASCII(enc, ptr) & ~0x7f))
+        break;
+      /* fall through */
+    default:
+      switch (BYTE_TO_ASCII(enc, ptr)) {
+      case 0x24: /* $ */
+      case 0x40: /* @ */
+        break;
+      default:
+        *badPtr = ptr;
+        return 0;
+      }
+      break;
+    }
+  }
+  return 1;
+}
+
+/* This must only be called for a well-formed start-tag or empty
+   element tag.  Returns the number of attributes.  Pointers to the
+   first attsMax attributes are stored in atts.
+*/
+
+static int PTRCALL
+PREFIX(getAtts)(const ENCODING *enc, const char *ptr, int attsMax,
+                ATTRIBUTE *atts) {
+  enum { other, inName, inValue } state = inName;
+  int nAtts = 0;
+  int open = 0; /* defined when state == inValue;
+                   initialization just to shut up compilers */
+
+  for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#  define START_NAME                                                           \
+    if (state == other) {                                                      \
+      if (nAtts < attsMax) {                                                   \
+        atts[nAtts].name = ptr;                                                \
+        atts[nAtts].normalized = 1;                                            \
+      }                                                                        \
+      state = inName;                                                          \
+    }
+#  define LEAD_CASE(n)                                                         \
+  case BT_LEAD##n: /* NOTE: The encoding has already been validated. */        \
+    START_NAME ptr += (n - MINBPC(enc));                                       \
+    break;
+      LEAD_CASE(2)
+      LEAD_CASE(3)
+      LEAD_CASE(4)
+#  undef LEAD_CASE
+    case BT_NONASCII:
+    case BT_NMSTRT:
+    case BT_HEX:
+      START_NAME
+      break;
+#  undef START_NAME
+    case BT_QUOT:
+      if (state != inValue) {
+        if (nAtts < attsMax)
+          atts[nAtts].valuePtr = ptr + MINBPC(enc);
+        state = inValue;
+        open = BT_QUOT;
+      } else if (open == BT_QUOT) {
+        state = other;
+        if (nAtts < attsMax)
+          atts[nAtts].valueEnd = ptr;
+        nAtts++;
+      }
+      break;
+    case BT_APOS:
+      if (state != inValue) {
+        if (nAtts < attsMax)
+          atts[nAtts].valuePtr = ptr + MINBPC(enc);
+        state = inValue;
+        open = BT_APOS;
+      } else if (open == BT_APOS) {
+        state = other;
+        if (nAtts < attsMax)
+          atts[nAtts].valueEnd = ptr;
+        nAtts++;
+      }
+      break;
+    case BT_AMP:
+      if (nAtts < attsMax)
+        atts[nAtts].normalized = 0;
+      break;
+    case BT_S:
+      if (state == inName)
+        state = other;
+      else if (state == inValue && nAtts < attsMax && atts[nAtts].normalized
+               && (ptr == atts[nAtts].valuePtr
+                   || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE
+                   || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE
+                   || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open))
+        atts[nAtts].normalized = 0;
+      break;
+    case BT_CR:
+    case BT_LF:
+      /* This case ensures that the first attribute name is counted
+         Apart from that we could just change state on the quote. */
+      if (state == inName)
+        state = other;
+      else if (state == inValue && nAtts < attsMax)
+        atts[nAtts].normalized = 0;
+      break;
+    case BT_GT:
+    case BT_SOL:
+      if (state != inValue)
+        return nAtts;
+      break;
+    default:
+      break;
+    }
+  }
+  /* not reached */
+}
+
+static int PTRFASTCALL
+PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) {
+  int result = 0;
+  /* skip &# */
+  UNUSED_P(enc);
+  ptr += 2 * MINBPC(enc);
+  if (CHAR_MATCHES(enc, ptr, ASCII_x)) {
+    for (ptr += MINBPC(enc); ! CHAR_MATCHES(enc, ptr, ASCII_SEMI);
+         ptr += MINBPC(enc)) {
+      int c = BYTE_TO_ASCII(enc, ptr);
+      switch (c) {
+      case ASCII_0:
+      case ASCII_1:
+      case ASCII_2:
+      case ASCII_3:
+      case ASCII_4:
+      case ASCII_5:
+      case ASCII_6:
+      case ASCII_7:
+      case ASCII_8:
+      case ASCII_9:
+        result <<= 4;
+        result |= (c - ASCII_0);
+        break;
+      case ASCII_A:
+      case ASCII_B:
+      case ASCII_C:
+      case ASCII_D:
+      case ASCII_E:
+      case ASCII_F:
+        result <<= 4;
+        result += 10 + (c - ASCII_A);
+        break;
+      case ASCII_a:
+      case ASCII_b:
+      case ASCII_c:
+      case ASCII_d:
+      case ASCII_e:
+      case ASCII_f:
+        result <<= 4;
+        result += 10 + (c - ASCII_a);
+        break;
+      }
+      if (result >= 0x110000)
+        return -1;
+    }
+  } else {
+    for (; ! CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) {
+      int c = BYTE_TO_ASCII(enc, ptr);
+      result *= 10;
+      result += (c - ASCII_0);
+      if (result >= 0x110000)
+        return -1;
+    }
+  }
+  return checkCharRefNumber(result);
+}
+
+static int PTRCALL
+PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr,
+                             const char *end) {
+  UNUSED_P(enc);
+  switch ((end - ptr) / MINBPC(enc)) {
+  case 2:
+    if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) {
+      switch (BYTE_TO_ASCII(enc, ptr)) {
+      case ASCII_l:
+        return ASCII_LT;
+      case ASCII_g:
+        return ASCII_GT;
+      }
+    }
+    break;
+  case 3:
+    if (CHAR_MATCHES(enc, ptr, ASCII_a)) {
+      ptr += MINBPC(enc);
+      if (CHAR_MATCHES(enc, ptr, ASCII_m)) {
+        ptr += MINBPC(enc);
+        if (CHAR_MATCHES(enc, ptr, ASCII_p))
+          return ASCII_AMP;
+      }
+    }
+    break;
+  case 4:
+    switch (BYTE_TO_ASCII(enc, ptr)) {
+    case ASCII_q:
+      ptr += MINBPC(enc);
+      if (CHAR_MATCHES(enc, ptr, ASCII_u)) {
+        ptr += MINBPC(enc);
+        if (CHAR_MATCHES(enc, ptr, ASCII_o)) {
+          ptr += MINBPC(enc);
+          if (CHAR_MATCHES(enc, ptr, ASCII_t))
+            return ASCII_QUOT;
+        }
+      }
+      break;
+    case ASCII_a:
+      ptr += MINBPC(enc);
+      if (CHAR_MATCHES(enc, ptr, ASCII_p)) {
+        ptr += MINBPC(enc);
+        if (CHAR_MATCHES(enc, ptr, ASCII_o)) {
+          ptr += MINBPC(enc);
+          if (CHAR_MATCHES(enc, ptr, ASCII_s))
+            return ASCII_APOS;
+        }
+      }
+      break;
+    }
+  }
+  return 0;
+}
+
+static int PTRCALL
+PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1,
+                         const char *end1, const char *ptr2) {
+  UNUSED_P(enc);
+  for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) {
+    if (end1 - ptr1 < MINBPC(enc)) {
+      /* This line cannot be executed.  The incoming data has already
+       * been tokenized once, so incomplete characters like this have
+       * already been eliminated from the input.  Retaining the
+       * paranoia check is still valuable, however.
+       */
+      return 0; /* LCOV_EXCL_LINE */
+    }
+    if (! CHAR_MATCHES(enc, ptr1, *ptr2))
+      return 0;
+  }
+  return ptr1 == end1;
+}
+
+static int PTRFASTCALL
+PREFIX(nameLength)(const ENCODING *enc, const char *ptr) {
+  const char *start = ptr;
+  for (;;) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#  define LEAD_CASE(n)                                                         \
+  case BT_LEAD##n:                                                             \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
+    break;
+      LEAD_CASE(2)
+      LEAD_CASE(3)
+      LEAD_CASE(4)
+#  undef LEAD_CASE
+    case BT_NONASCII:
+    case BT_NMSTRT:
+#  ifdef XML_NS
+    case BT_COLON:
+#  endif
+    case BT_HEX:
+    case BT_DIGIT:
+    case BT_NAME:
+    case BT_MINUS:
+      ptr += MINBPC(enc);
+      break;
+    default:
+      return (int)(ptr - start);
+    }
+  }
+}
+
+static const char *PTRFASTCALL
+PREFIX(skipS)(const ENCODING *enc, const char *ptr) {
+  for (;;) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_LF:
+    case BT_CR:
+    case BT_S:
+      ptr += MINBPC(enc);
+      break;
+    default:
+      return ptr;
+    }
+  }
+}
+
+static void PTRCALL
+PREFIX(updatePosition)(const ENCODING *enc, const char *ptr, const char *end,
+                       POSITION *pos) {
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#  define LEAD_CASE(n)                                                         \
+  case BT_LEAD##n:                                                             \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
+    pos->columnNumber++;                                                       \
+    break;
+      LEAD_CASE(2)
+      LEAD_CASE(3)
+      LEAD_CASE(4)
+#  undef LEAD_CASE
+    case BT_LF:
+      pos->columnNumber = 0;
+      pos->lineNumber++;
+      ptr += MINBPC(enc);
+      break;
+    case BT_CR:
+      pos->lineNumber++;
+      ptr += MINBPC(enc);
+      if (HAS_CHAR(enc, ptr, end) && BYTE_TYPE(enc, ptr) == BT_LF)
+        ptr += MINBPC(enc);
+      pos->columnNumber = 0;
+      break;
+    default:
+      ptr += MINBPC(enc);
+      pos->columnNumber++;
+      break;
+    }
+  }
+}
+
+#  undef DO_LEAD_CASE
+#  undef MULTIBYTE_CASES
+#  undef INVALID_CASES
+#  undef CHECK_NAME_CASE
+#  undef CHECK_NAME_CASES
+#  undef CHECK_NMSTRT_CASE
+#  undef CHECK_NMSTRT_CASES
+
+#endif /* XML_TOK_IMPL_C */
diff --git a/dummy_fe/libexpat/xmltok_impl.h b/dummy_fe/libexpat/xmltok_impl.h
new file mode 100644
index 0000000..3469c4a
--- /dev/null
+++ b/dummy_fe/libexpat/xmltok_impl.h
@@ -0,0 +1,74 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2017-2019 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+enum {
+  BT_NONXML,   /* e.g. noncharacter-FFFF */
+  BT_MALFORM,  /* illegal, with regard to encoding */
+  BT_LT,       /* less than = "<" */
+  BT_AMP,      /* ampersand = "&" */
+  BT_RSQB,     /* right square bracket = "[" */
+  BT_LEAD2,    /* lead byte of a 2-byte UTF-8 character */
+  BT_LEAD3,    /* lead byte of a 3-byte UTF-8 character */
+  BT_LEAD4,    /* lead byte of a 4-byte UTF-8 character */
+  BT_TRAIL,    /* trailing unit, e.g. second 16-bit unit of a 4-byte char. */
+  BT_CR,       /* carriage return = "\r" */
+  BT_LF,       /* line feed = "\n" */
+  BT_GT,       /* greater than = ">" */
+  BT_QUOT,     /* quotation character = "\"" */
+  BT_APOS,     /* apostrophe = "'" */
+  BT_EQUALS,   /* equal sign = "=" */
+  BT_QUEST,    /* question mark = "?" */
+  BT_EXCL,     /* exclamation mark = "!" */
+  BT_SOL,      /* solidus, slash = "/" */
+  BT_SEMI,     /* semicolon = ";" */
+  BT_NUM,      /* number sign = "#" */
+  BT_LSQB,     /* left square bracket = "[" */
+  BT_S,        /* white space, e.g. "\t", " "[, "\r"] */
+  BT_NMSTRT,   /* non-hex name start letter = "G".."Z" + "g".."z" + "_" */
+  BT_COLON,    /* colon = ":" */
+  BT_HEX,      /* hex letter = "A".."F" + "a".."f" */
+  BT_DIGIT,    /* digit = "0".."9" */
+  BT_NAME,     /* dot and middle dot = "." + chr(0xb7) */
+  BT_MINUS,    /* minus = "-" */
+  BT_OTHER,    /* known not to be a name or name start character */
+  BT_NONASCII, /* might be a name or name start character */
+  BT_PERCNT,   /* percent sign = "%" */
+  BT_LPAR,     /* left parenthesis = "(" */
+  BT_RPAR,     /* right parenthesis = "(" */
+  BT_AST,      /* asterisk = "*" */
+  BT_PLUS,     /* plus sign = "+" */
+  BT_COMMA,    /* comma = "," */
+  BT_VERBAR    /* vertical bar = "|" */
+};
+
+#include <stddef.h>
diff --git a/dummy_fe/libexpat/xmltok_ns.c b/dummy_fe/libexpat/xmltok_ns.c
new file mode 100644
index 0000000..fbdd3e3
--- /dev/null
+++ b/dummy_fe/libexpat/xmltok_ns.c
@@ -0,0 +1,122 @@
+/* This file is included!
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef XML_TOK_NS_C
+
+const ENCODING *
+NS(XmlGetUtf8InternalEncoding)(void) {
+  return &ns(internal_utf8_encoding).enc;
+}
+
+const ENCODING *
+NS(XmlGetUtf16InternalEncoding)(void) {
+#  if BYTEORDER == 1234
+  return &ns(internal_little2_encoding).enc;
+#  elif BYTEORDER == 4321
+  return &ns(internal_big2_encoding).enc;
+#  else
+  const short n = 1;
+  return (*(const char *)&n ? &ns(internal_little2_encoding).enc
+                            : &ns(internal_big2_encoding).enc);
+#  endif
+}
+
+static const ENCODING *const NS(encodings)[] = {
+    &ns(latin1_encoding).enc, &ns(ascii_encoding).enc,
+    &ns(utf8_encoding).enc,   &ns(big2_encoding).enc,
+    &ns(big2_encoding).enc,   &ns(little2_encoding).enc,
+    &ns(utf8_encoding).enc /* NO_ENC */
+};
+
+static int PTRCALL
+NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end,
+                   const char **nextTokPtr) {
+  return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE,
+                  ptr, end, nextTokPtr);
+}
+
+static int PTRCALL
+NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end,
+                    const char **nextTokPtr) {
+  return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE,
+                  ptr, end, nextTokPtr);
+}
+
+int
+NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr,
+                    const char *name) {
+  int i = getEncodingIndex(name);
+  if (i == UNKNOWN_ENC)
+    return 0;
+  SET_INIT_ENC_INDEX(p, i);
+  p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog);
+  p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent);
+  p->initEnc.updatePosition = initUpdatePosition;
+  p->encPtr = encPtr;
+  *encPtr = &(p->initEnc);
+  return 1;
+}
+
+static const ENCODING *
+NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) {
+#  define ENCODING_MAX 128
+  char buf[ENCODING_MAX] = "";
+  char *p = buf;
+  int i;
+  XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
+  if (ptr != end)
+    return 0;
+  *p = 0;
+  if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2)
+    return enc;
+  i = getEncodingIndex(buf);
+  if (i == UNKNOWN_ENC)
+    return 0;
+  return NS(encodings)[i];
+}
+
+int
+NS(XmlParseXmlDecl)(int isGeneralTextEntity, const ENCODING *enc,
+                    const char *ptr, const char *end, const char **badPtr,
+                    const char **versionPtr, const char **versionEndPtr,
+                    const char **encodingName, const ENCODING **encoding,
+                    int *standalone) {
+  return doParseXmlDecl(NS(findEncoding), isGeneralTextEntity, enc, ptr, end,
+                        badPtr, versionPtr, versionEndPtr, encodingName,
+                        encoding, standalone);
+}
+
+#endif /* XML_TOK_NS_C */