test: server: Report server crash
[libisds.git] / src / physxml.c
blob22ae88c32c4c149e0f6f0dab8dc22d1300730847
1 #include "isds_priv.h"
2 #include "physxml.h"
3 #include "utils.h"
5 #include <string.h>
6 #include <expat.h>
7 #include <inttypes.h>
9 #define NS_CHAR_SEPARATOR '>'
11 struct expat_data {
12 XML_Parser parser;
13 const XML_Char **elements; /* NULL terminated array of elements */
14 _Bool found;
15 size_t *start;
16 size_t *end;
17 int depth; /* Current parser depth, root element is 0 */
18 int element_depth; /* elements[element_depth] we are in,
19 -1 if we are not in any (root mismatch)*/
23 /* Check for expat compile-time configuration
24 * @current_version is static string describing current expat version */
25 _hidden isds_error _isds_init_expat(const char **current_version) {
26 XML_Expat_Version current;
27 const int min_major = 2;
28 const int min_minor = 0;
29 const int min_micro = 0;
30 const XML_Feature *features; /* Static array stored in expat BSS */
31 _Bool ns_supported = 0;
33 if (current_version) *current_version = XML_ExpatVersion();
36 * Max(XML_Size) <= Max(size_t)
37 * XML_Char is char, not a wchar_t
38 * XML_UNICODE is undefined (i.e. strings in UTF-8)
39 * */
41 /* Check minimal expat version */
42 current = XML_ExpatVersionInfo();
43 if ( (current.major < min_major) ||
44 (current.major == min_major && current.minor < min_minor) ||
45 (current.major == min_major && current.minor == min_minor &&
46 current.micro < min_micro) ) {
47 isds_log(ILF_ISDS, ILL_CRIT,
48 _("Minimal %d.%d.%d Expat version required. "
49 "Current version is %d.%d.%d\n"),
50 min_major, min_minor, min_micro,
51 current.major, current.minor, current.micro);
52 return IE_ERROR;
55 /* XML_Char must be char, not a wchar_t */
56 features = XML_GetFeatureList();
57 while (features->feature != XML_FEATURE_END) {
58 switch (features->feature) {
59 case XML_FEATURE_UNICODE_WCHAR_T:
60 case XML_FEATURE_UNICODE:
61 isds_log(ILF_ISDS, ILL_CRIT,
62 _("Expat compiled with UTF-16 (wide) characters\n"));
63 return IE_ERROR;
64 break;
65 case XML_FEATURE_SIZEOF_XML_CHAR:
66 if (features->value != sizeof(char)) {
67 isds_log(ILF_ISDS, ILL_CRIT,
68 "Expat compiled with XML_Chars incompatible "
69 "with chars\n");
70 return IE_ERROR;
72 break;
73 case XML_FEATURE_NS:
74 ns_supported = 1;
75 default:
76 break;
78 features++;
81 if (!ns_supported) {
82 isds_log(ILF_ISDS, ILL_CRIT,
83 _("Expat not compiled with name space support\n"));
84 return IE_ERROR;
87 return IE_SUCCESS;
91 /* Breaks element path address into NULL terminated array of elements in
92 * preserved order. Zeroth array element will be first path element.
93 * @path element address, content will be damaged
94 * @return array of elements, NULL in case of error */
95 static const XML_Char **path2elements(XML_Char *path) {
96 const XML_Char **elements = NULL;
97 XML_Char *tmp_path;
98 char *saveptr = NULL;
99 XML_Char *element;
100 unsigned int depth = 0;
102 if (!path) return NULL;
104 elements = malloc(sizeof(elements[0]) * (strlen(path) + 1));
105 if (!elements) return NULL;
107 elements[0] = NULL;
109 tmp_path = path;
110 while ((element = (XML_Char *) strtok_r(tmp_path,
111 PHYSXML_ELEMENT_SEPARATOR, &saveptr))) {
112 tmp_path = NULL;
113 elements[depth++] = element;
116 elements[depth] = NULL;
117 return elements;
121 /* Examine start and empty element tag.
122 * @name is expanded name */
123 static void XMLCALL element_start(void *userData, const XML_Char *name,
124 const XML_Char **atts) {
125 struct expat_data *data = (struct expat_data *) userData;
126 data->depth++;
128 const XML_Index index = XML_GetCurrentByteIndex(data->parser);
129 /* XXX: Because document length is stored as size_t, index always fits
130 * size_t. */
131 const size_t boundary = index;
133 isds_log(ILF_XML, ILL_DEBUG, _("Start: name=%s, depth=%zd, offset=%#jx "
134 "=> boundary=%#zx\n"),
135 name, data->depth, (uintmax_t)index, boundary);
137 if ((!data->found) &&
138 (data->depth == data->element_depth + 1) &&
139 (!strcmp(data->elements[data->element_depth + 1], name))) {
140 data->element_depth++;
142 isds_log(ILF_XML, ILL_DEBUG,
143 _("\tStart tag for element `%s' found\n"),
144 data->elements[data->element_depth]);
146 if (!data->elements[data->element_depth + 1]) {
147 data->found = 1;
148 *data->start = boundary;
154 /* Examine end and empty element tag.
155 * @name is expanded name */
156 static void XMLCALL element_end(void *userData, const XML_Char *name) {
158 struct expat_data *data = (struct expat_data *) userData;
159 enum XML_Status xerr;
161 const XML_Index index = (uintmax_t) XML_GetCurrentByteIndex(data->parser);
162 const int count = XML_GetCurrentByteCount(data->parser);
163 /* XXX: Because document length is stored as size_t, index + count always
164 * fits size_t. */
165 const size_t boundary = index + count - 1;
167 isds_log(ILF_XML, ILL_DEBUG, _("End: name=%s, depth=%zd, offset=%#jx "
168 "count=%u => boundary=%#zx\n"),
169 name, data->depth, (uintmax_t)index, count, boundary);
171 if (data->element_depth == data->depth) {
172 if (data->found) {
173 isds_log(ILF_XML, ILL_DEBUG,
174 _("\tEnd tag for element `%s' found\n"),
175 data->elements[data->element_depth]);
176 *data->end = boundary;
178 /* Here we can stop parser
179 * XXX: requires Expat 1.95.8 */
180 xerr = XML_StopParser(data->parser, XML_FALSE);
181 if (xerr != XML_STATUS_OK) {
182 PANIC("Error while stopping parser");
186 data->element_depth--;
189 data->depth--;
193 /* Locate element specified by element path in XML stream.
194 * TODO: Support other encodings than UTF-8
195 * @document is XML document as bit stream
196 * @length is size of @document in bytes. Zero length is forbidden.
197 * @path is special path (e.g. "|html|head|title",
198 * qualified element names are specified as
199 * NSURI '>' LOCALNAME, ommit NSURI and '>' separator if no namespace
200 * should be addressed (i.e. use only locale name)
201 * You can use PHYSXML_ELEMENT_SEPARATOR and PHYSXML_NS_SEPARATOR string
202 * macros.
203 * @start outputs start of the element location in @document (inclusive,
204 * counts from 0)
205 * @end outputs end of element (inclusive, counts from 0)
206 * @return 0 if element found */
207 _hidden isds_error _isds_find_element_boundary(void *document, size_t length,
208 char *path, size_t *start, size_t *end) {
210 XML_Parser parser;
211 enum XML_Status xerr;
212 struct expat_data user_data;
214 if (!document || !path || !start || !end || length <= 0)
215 return IE_INVAL;
217 isds_log(ILF_XML, ILL_DEBUG, _("Searching boundary of element: %s\n"),
218 path);
220 /* Parse XPath */
221 user_data.elements = path2elements(path);
222 if (!user_data.elements) return IE_NOMEM;
224 /* No element means whole document */
225 if (!user_data.elements[0]) {
226 free(user_data.elements);
227 *start = 0;
228 *end = length - 1;
229 return IE_SUCCESS;
232 /* Create parser*/
233 parser = XML_ParserCreateNS(NULL, NS_CHAR_SEPARATOR);
235 XML_SetStartElementHandler(parser, element_start);
236 XML_SetEndElementHandler(parser, element_end);
238 user_data.parser = parser;
239 user_data.found = 0;
240 user_data.start = start;
241 user_data.end = end;
242 user_data.depth = -1;
243 user_data.element_depth = -1;
244 XML_SetUserData(parser, &user_data);
246 /* Parse it */
247 xerr = XML_Parse(parser, (const char *) document, length, 1);
248 if (xerr != XML_STATUS_OK &&
249 !( (xerr == XML_STATUS_ERROR &&
250 XML_GetErrorCode(parser) == XML_ERROR_ABORTED))) {
251 free(user_data.elements);
252 isds_log(ILF_ISDS, ILL_CRIT, _("XML_Parse failed\n"));
253 return IE_ERROR;
255 free(user_data.elements);
257 XML_ParserFree(parser);
258 if (user_data.found) return IE_SUCCESS;
259 else return IE_NOEXIST;