tests: Fix a GCC 9.2.1 warning about NULL %s value
[libisds.git] / src / physxml.c
blobdc6fc75357ea8dd850436136b572712537025ecd
1 #include "isds_priv.h"
2 #include "physxml.h"
3 #include "utils.h"
4 #include "system.h"
6 #include <string.h>
7 #include <expat.h>
8 #include <inttypes.h>
10 #define NS_CHAR_SEPARATOR '>'
12 struct expat_data {
13 XML_Parser parser;
14 const XML_Char **elements; /* NULL terminated array of elements */
15 _Bool found;
16 size_t *start;
17 size_t *end;
18 int depth; /* Current parser depth, root element is 0 */
19 int element_depth; /* elements[element_depth] we are in,
20 -1 if we are not in any (root mismatch)*/
24 /* Check for expat compile-time configuration
25 * @current_version is static string describing current expat version */
26 _hidden isds_error _isds_init_expat(const char **current_version) {
27 XML_Expat_Version current;
28 const int min_major = 2;
29 const int min_minor = 0;
30 const int min_micro = 0;
31 const XML_Feature *features; /* Static array stored in expat BSS */
32 _Bool ns_supported = 0;
34 if (current_version) *current_version = XML_ExpatVersion();
37 * Max(XML_Size) <= Max(size_t)
38 * XML_Char is char, not a wchar_t
39 * XML_UNICODE is undefined (i.e. strings in UTF-8)
40 * */
42 /* Check minimal expat version */
43 current = XML_ExpatVersionInfo();
44 if ( (current.major < min_major) ||
45 (current.major == min_major && current.minor < min_minor) ||
46 (current.major == min_major && current.minor == min_minor &&
47 current.micro < min_micro) ) {
48 isds_log(ILF_ISDS, ILL_CRIT,
49 _("Minimal %d.%d.%d Expat version required. "
50 "Current version is %d.%d.%d\n"),
51 min_major, min_minor, min_micro,
52 current.major, current.minor, current.micro);
53 return IE_ERROR;
56 /* XML_Char must be char, not a wchar_t */
57 features = XML_GetFeatureList();
58 while (features->feature != XML_FEATURE_END) {
59 switch (features->feature) {
60 case XML_FEATURE_UNICODE_WCHAR_T:
61 case XML_FEATURE_UNICODE:
62 isds_log(ILF_ISDS, ILL_CRIT,
63 _("Expat compiled with UTF-16 (wide) characters\n"));
64 return IE_ERROR;
65 break;
66 case XML_FEATURE_SIZEOF_XML_CHAR:
67 if (features->value != sizeof(char)) {
68 isds_log(ILF_ISDS, ILL_CRIT,
69 "Expat compiled with XML_Chars incompatible "
70 "with chars\n");
71 return IE_ERROR;
73 break;
74 case XML_FEATURE_NS:
75 ns_supported = 1;
76 default:
77 break;
79 features++;
82 if (!ns_supported) {
83 isds_log(ILF_ISDS, ILL_CRIT,
84 _("Expat not compiled with name space support\n"));
85 return IE_ERROR;
88 return IE_SUCCESS;
92 /* Breaks element path address into NULL terminated array of elements in
93 * preserved order. Zeroth array element will be first path element.
94 * @path element address, content will be damaged
95 * @return array of elements, NULL in case of error */
96 static const XML_Char **path2elements(XML_Char *path) {
97 const XML_Char **elements = NULL;
98 XML_Char *tmp_path;
99 char *saveptr = NULL;
100 XML_Char *element;
101 unsigned int depth = 0;
103 if (!path) return NULL;
105 elements = malloc(sizeof(elements[0]) * (strlen(path) + 1));
106 if (!elements) return NULL;
108 elements[0] = NULL;
110 tmp_path = path;
111 while ((element = (XML_Char *) strtok_r(tmp_path,
112 PHYSXML_ELEMENT_SEPARATOR, &saveptr))) {
113 tmp_path = NULL;
114 elements[depth++] = element;
117 elements[depth] = NULL;
118 return elements;
122 /* Examine start and empty element tag.
123 * @name is expanded name */
124 static void XMLCALL element_start(void *userData, const XML_Char *name,
125 const XML_Char **atts) {
126 struct expat_data *data = (struct expat_data *) userData;
127 data->depth++;
129 const XML_Index index = XML_GetCurrentByteIndex(data->parser);
130 /* XXX: Because document length is stored as size_t, index always fits
131 * size_t. */
132 const size_t boundary = index;
134 /* Silent warning about unused argument.
135 * This protype is expat's XML_StartElementHandler type. */
136 (void)atts;
138 isds_log(ILF_XML, ILL_DEBUG, _("Start: name=%s, depth=%zd, offset=%#jx "
139 "=> boundary=%#zx\n"),
140 name, data->depth, (uintmax_t)index, boundary);
142 if ((!data->found) &&
143 (data->depth == data->element_depth + 1) &&
144 (!strcmp(data->elements[data->element_depth + 1], name))) {
145 data->element_depth++;
147 isds_log(ILF_XML, ILL_DEBUG,
148 _("\tStart tag for element `%s' found\n"),
149 data->elements[data->element_depth]);
151 if (!data->elements[data->element_depth + 1]) {
152 data->found = 1;
153 *data->start = boundary;
159 /* Examine end and empty element tag.
160 * @name is expanded name */
161 static void XMLCALL element_end(void *userData, const XML_Char *name) {
163 struct expat_data *data = (struct expat_data *) userData;
164 enum XML_Status xerr;
166 const XML_Index index = (uintmax_t) XML_GetCurrentByteIndex(data->parser);
167 const int count = XML_GetCurrentByteCount(data->parser);
168 /* XXX: Because document length is stored as size_t, index + count always
169 * fits size_t. */
170 const size_t boundary = index + count - 1;
172 isds_log(ILF_XML, ILL_DEBUG, _("End: name=%s, depth=%zd, offset=%#jx "
173 "count=%u => boundary=%#zx\n"),
174 name, data->depth, (uintmax_t)index, count, boundary);
176 if (data->element_depth == data->depth) {
177 if (data->found) {
178 isds_log(ILF_XML, ILL_DEBUG,
179 _("\tEnd tag for element `%s' found\n"),
180 data->elements[data->element_depth]);
181 *data->end = boundary;
183 /* Here we can stop parser
184 * XXX: requires Expat 1.95.8 */
185 xerr = XML_StopParser(data->parser, XML_FALSE);
186 if (xerr != XML_STATUS_OK) {
187 PANIC("Error while stopping parser");
191 data->element_depth--;
194 data->depth--;
198 /* Locate element specified by element path in XML stream.
199 * TODO: Support other encodings than UTF-8
200 * @document is XML document as bit stream
201 * @length is size of @document in bytes. Zero length is forbidden.
202 * @path is special path (e.g. "|html|head|title",
203 * qualified element names are specified as
204 * NSURI '>' LOCALNAME, ommit NSURI and '>' separator if no namespace
205 * should be addressed (i.e. use only locale name)
206 * You can use PHYSXML_ELEMENT_SEPARATOR and PHYSXML_NS_SEPARATOR string
207 * macros.
208 * @start outputs start of the element location in @document (inclusive,
209 * counts from 0)
210 * @end outputs end of element (inclusive, counts from 0)
211 * @return 0 if element found */
212 _hidden isds_error _isds_find_element_boundary(void *document, size_t length,
213 char *path, size_t *start, size_t *end) {
215 XML_Parser parser;
216 enum XML_Status xerr;
217 struct expat_data user_data;
219 if (!document || !path || !start || !end || length <= 0)
220 return IE_INVAL;
222 isds_log(ILF_XML, ILL_DEBUG, _("Searching boundary of element: %s\n"),
223 path);
225 /* Parse XPath */
226 user_data.elements = path2elements(path);
227 if (!user_data.elements) return IE_NOMEM;
229 /* No element means whole document */
230 if (!user_data.elements[0]) {
231 free(user_data.elements);
232 *start = 0;
233 *end = length - 1;
234 return IE_SUCCESS;
237 /* Create parser*/
238 parser = XML_ParserCreateNS(NULL, NS_CHAR_SEPARATOR);
240 XML_SetStartElementHandler(parser, element_start);
241 XML_SetEndElementHandler(parser, element_end);
243 user_data.parser = parser;
244 user_data.found = 0;
245 user_data.start = start;
246 user_data.end = end;
247 user_data.depth = -1;
248 user_data.element_depth = -1;
249 XML_SetUserData(parser, &user_data);
251 /* Parse it */
252 xerr = XML_Parse(parser, (const char *) document, length, 1);
253 if (xerr != XML_STATUS_OK &&
254 !( (xerr == XML_STATUS_ERROR &&
255 XML_GetErrorCode(parser) == XML_ERROR_ABORTED))) {
256 free(user_data.elements);
257 isds_log(ILF_ISDS, ILL_CRIT, _("XML_Parse failed\n"));
258 return IE_ERROR;
260 free(user_data.elements);
262 XML_ParserFree(parser);
263 if (user_data.found) return IE_SUCCESS;
264 else return IE_NOEXIST;