1 |
24 |
jeremybenn |
/* Helper routines for parsing XML using Expat.
|
2 |
|
|
|
3 |
|
|
Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
|
4 |
|
|
|
5 |
|
|
This file is part of GDB.
|
6 |
|
|
|
7 |
|
|
This program is free software; you can redistribute it and/or modify
|
8 |
|
|
it under the terms of the GNU General Public License as published by
|
9 |
|
|
the Free Software Foundation; either version 3 of the License, or
|
10 |
|
|
(at your option) any later version.
|
11 |
|
|
|
12 |
|
|
This program is distributed in the hope that it will be useful,
|
13 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
|
|
GNU General Public License for more details.
|
16 |
|
|
|
17 |
|
|
You should have received a copy of the GNU General Public License
|
18 |
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
19 |
|
|
|
20 |
|
|
#include "defs.h"
|
21 |
|
|
#include "gdbcmd.h"
|
22 |
|
|
#include "exceptions.h"
|
23 |
|
|
#include "xml-support.h"
|
24 |
|
|
|
25 |
|
|
#include "gdb_string.h"
|
26 |
|
|
#include "safe-ctype.h"
|
27 |
|
|
|
28 |
|
|
/* Debugging flag. */
|
29 |
|
|
static int debug_xml;
|
30 |
|
|
|
31 |
|
|
/* The contents of this file are only useful if XML support is
|
32 |
|
|
available. */
|
33 |
|
|
#ifdef HAVE_LIBEXPAT
|
34 |
|
|
|
35 |
|
|
#include "gdb_expat.h"
|
36 |
|
|
|
37 |
|
|
/* The maximum depth of <xi:include> nesting. No need to be miserly,
|
38 |
|
|
we just want to avoid running out of stack on loops. */
|
39 |
|
|
#define MAX_XINCLUDE_DEPTH 30
|
40 |
|
|
|
41 |
|
|
/* Simplified XML parser infrastructure. */
|
42 |
|
|
|
43 |
|
|
/* A parsing level -- used to keep track of the current element
|
44 |
|
|
nesting. */
|
45 |
|
|
struct scope_level
|
46 |
|
|
{
|
47 |
|
|
/* Elements we allow at this level. */
|
48 |
|
|
const struct gdb_xml_element *elements;
|
49 |
|
|
|
50 |
|
|
/* The element which we are within. */
|
51 |
|
|
const struct gdb_xml_element *element;
|
52 |
|
|
|
53 |
|
|
/* Mask of which elements we've seen at this level (used for
|
54 |
|
|
optional and repeatable checking). */
|
55 |
|
|
unsigned int seen;
|
56 |
|
|
|
57 |
|
|
/* Body text accumulation. */
|
58 |
|
|
struct obstack *body;
|
59 |
|
|
};
|
60 |
|
|
typedef struct scope_level scope_level_s;
|
61 |
|
|
DEF_VEC_O(scope_level_s);
|
62 |
|
|
|
63 |
|
|
/* The parser itself, and our additional state. */
|
64 |
|
|
struct gdb_xml_parser
|
65 |
|
|
{
|
66 |
|
|
XML_Parser expat_parser; /* The underlying expat parser. */
|
67 |
|
|
|
68 |
|
|
const char *name; /* Name of this parser. */
|
69 |
|
|
void *user_data; /* The user's callback data, for handlers. */
|
70 |
|
|
|
71 |
|
|
VEC(scope_level_s) *scopes; /* Scoping stack. */
|
72 |
|
|
|
73 |
|
|
struct gdb_exception error; /* A thrown error, if any. */
|
74 |
|
|
int last_line; /* The line of the thrown error, or 0. */
|
75 |
|
|
|
76 |
|
|
const char *dtd_name; /* The name of the expected / default DTD,
|
77 |
|
|
if specified. */
|
78 |
|
|
int is_xinclude; /* Are we the special <xi:include> parser? */
|
79 |
|
|
};
|
80 |
|
|
|
81 |
|
|
/* Process some body text. We accumulate the text for later use; it's
|
82 |
|
|
wrong to do anything with it immediately, because a single block of
|
83 |
|
|
text might be broken up into multiple calls to this function. */
|
84 |
|
|
|
85 |
|
|
static void
|
86 |
|
|
gdb_xml_body_text (void *data, const XML_Char *text, int length)
|
87 |
|
|
{
|
88 |
|
|
struct gdb_xml_parser *parser = data;
|
89 |
|
|
struct scope_level *scope = VEC_last (scope_level_s, parser->scopes);
|
90 |
|
|
|
91 |
|
|
if (parser->error.reason < 0)
|
92 |
|
|
return;
|
93 |
|
|
|
94 |
|
|
if (scope->body == NULL)
|
95 |
|
|
{
|
96 |
|
|
scope->body = XZALLOC (struct obstack);
|
97 |
|
|
obstack_init (scope->body);
|
98 |
|
|
}
|
99 |
|
|
|
100 |
|
|
obstack_grow (scope->body, text, length);
|
101 |
|
|
}
|
102 |
|
|
|
103 |
|
|
/* Issue a debugging message from one of PARSER's handlers. */
|
104 |
|
|
|
105 |
|
|
void
|
106 |
|
|
gdb_xml_debug (struct gdb_xml_parser *parser, const char *format, ...)
|
107 |
|
|
{
|
108 |
|
|
int line = XML_GetCurrentLineNumber (parser->expat_parser);
|
109 |
|
|
va_list ap;
|
110 |
|
|
char *message;
|
111 |
|
|
|
112 |
|
|
if (!debug_xml)
|
113 |
|
|
return;
|
114 |
|
|
|
115 |
|
|
va_start (ap, format);
|
116 |
|
|
message = xstrvprintf (format, ap);
|
117 |
|
|
if (line)
|
118 |
|
|
fprintf_unfiltered (gdb_stderr, "%s (line %d): %s\n",
|
119 |
|
|
parser->name, line, message);
|
120 |
|
|
else
|
121 |
|
|
fprintf_unfiltered (gdb_stderr, "%s: %s\n",
|
122 |
|
|
parser->name, message);
|
123 |
|
|
xfree (message);
|
124 |
|
|
}
|
125 |
|
|
|
126 |
|
|
/* Issue an error message from one of PARSER's handlers, and stop
|
127 |
|
|
parsing. */
|
128 |
|
|
|
129 |
|
|
void
|
130 |
|
|
gdb_xml_error (struct gdb_xml_parser *parser, const char *format, ...)
|
131 |
|
|
{
|
132 |
|
|
int line = XML_GetCurrentLineNumber (parser->expat_parser);
|
133 |
|
|
va_list ap;
|
134 |
|
|
|
135 |
|
|
parser->last_line = line;
|
136 |
|
|
va_start (ap, format);
|
137 |
|
|
throw_verror (XML_PARSE_ERROR, format, ap);
|
138 |
|
|
}
|
139 |
|
|
|
140 |
|
|
/* Clean up a vector of parsed attribute values. */
|
141 |
|
|
|
142 |
|
|
static void
|
143 |
|
|
gdb_xml_values_cleanup (void *data)
|
144 |
|
|
{
|
145 |
|
|
VEC(gdb_xml_value_s) **values = data;
|
146 |
|
|
struct gdb_xml_value *value;
|
147 |
|
|
int ix;
|
148 |
|
|
|
149 |
|
|
for (ix = 0; VEC_iterate (gdb_xml_value_s, *values, ix, value); ix++)
|
150 |
|
|
xfree (value->value);
|
151 |
|
|
VEC_free (gdb_xml_value_s, *values);
|
152 |
|
|
}
|
153 |
|
|
|
154 |
|
|
/* Handle the start of an element. DATA is our local XML parser, NAME
|
155 |
|
|
is the element, and ATTRS are the names and values of this
|
156 |
|
|
element's attributes. */
|
157 |
|
|
|
158 |
|
|
static void
|
159 |
|
|
gdb_xml_start_element (void *data, const XML_Char *name,
|
160 |
|
|
const XML_Char **attrs)
|
161 |
|
|
{
|
162 |
|
|
struct gdb_xml_parser *parser = data;
|
163 |
|
|
struct scope_level *scope;
|
164 |
|
|
struct scope_level new_scope;
|
165 |
|
|
const struct gdb_xml_element *element;
|
166 |
|
|
const struct gdb_xml_attribute *attribute;
|
167 |
|
|
VEC(gdb_xml_value_s) *attributes = NULL;
|
168 |
|
|
unsigned int seen;
|
169 |
|
|
struct cleanup *back_to;
|
170 |
|
|
|
171 |
|
|
/* Push an error scope. If we return or throw an exception before
|
172 |
|
|
filling this in, it will tell us to ignore children of this
|
173 |
|
|
element. */
|
174 |
|
|
VEC_reserve (scope_level_s, parser->scopes, 1);
|
175 |
|
|
scope = VEC_last (scope_level_s, parser->scopes);
|
176 |
|
|
memset (&new_scope, 0, sizeof (new_scope));
|
177 |
|
|
VEC_quick_push (scope_level_s, parser->scopes, &new_scope);
|
178 |
|
|
|
179 |
|
|
gdb_xml_debug (parser, _("Entering element <%s>"), name);
|
180 |
|
|
|
181 |
|
|
/* Find this element in the list of the current scope's allowed
|
182 |
|
|
children. Record that we've seen it. */
|
183 |
|
|
|
184 |
|
|
seen = 1;
|
185 |
|
|
for (element = scope->elements; element && element->name;
|
186 |
|
|
element++, seen <<= 1)
|
187 |
|
|
if (strcmp (element->name, name) == 0)
|
188 |
|
|
break;
|
189 |
|
|
|
190 |
|
|
if (element == NULL || element->name == NULL)
|
191 |
|
|
{
|
192 |
|
|
/* If we're working on XInclude, <xi:include> can be the child
|
193 |
|
|
of absolutely anything. Copy the previous scope's element
|
194 |
|
|
list into the new scope even if there was no match. */
|
195 |
|
|
if (parser->is_xinclude)
|
196 |
|
|
{
|
197 |
|
|
struct scope_level *unknown_scope;
|
198 |
|
|
|
199 |
|
|
XML_DefaultCurrent (parser->expat_parser);
|
200 |
|
|
|
201 |
|
|
unknown_scope = VEC_last (scope_level_s, parser->scopes);
|
202 |
|
|
unknown_scope->elements = scope->elements;
|
203 |
|
|
return;
|
204 |
|
|
}
|
205 |
|
|
|
206 |
|
|
gdb_xml_debug (parser, _("Element <%s> unknown"), name);
|
207 |
|
|
return;
|
208 |
|
|
}
|
209 |
|
|
|
210 |
|
|
if (!(element->flags & GDB_XML_EF_REPEATABLE) && (seen & scope->seen))
|
211 |
|
|
gdb_xml_error (parser, _("Element <%s> only expected once"), name);
|
212 |
|
|
|
213 |
|
|
scope->seen |= seen;
|
214 |
|
|
|
215 |
|
|
back_to = make_cleanup (gdb_xml_values_cleanup, &attributes);
|
216 |
|
|
|
217 |
|
|
for (attribute = element->attributes;
|
218 |
|
|
attribute != NULL && attribute->name != NULL;
|
219 |
|
|
attribute++)
|
220 |
|
|
{
|
221 |
|
|
const char *val = NULL;
|
222 |
|
|
const XML_Char **p;
|
223 |
|
|
void *parsed_value;
|
224 |
|
|
struct gdb_xml_value new_value;
|
225 |
|
|
|
226 |
|
|
for (p = attrs; *p != NULL; p += 2)
|
227 |
|
|
if (!strcmp (attribute->name, p[0]))
|
228 |
|
|
{
|
229 |
|
|
val = p[1];
|
230 |
|
|
break;
|
231 |
|
|
}
|
232 |
|
|
|
233 |
|
|
if (*p != NULL && val == NULL)
|
234 |
|
|
{
|
235 |
|
|
gdb_xml_debug (parser, _("Attribute \"%s\" missing a value"),
|
236 |
|
|
attribute->name);
|
237 |
|
|
continue;
|
238 |
|
|
}
|
239 |
|
|
|
240 |
|
|
if (*p == NULL && !(attribute->flags & GDB_XML_AF_OPTIONAL))
|
241 |
|
|
{
|
242 |
|
|
gdb_xml_error (parser, _("Required attribute \"%s\" of "
|
243 |
|
|
"<%s> not specified"),
|
244 |
|
|
attribute->name, element->name);
|
245 |
|
|
continue;
|
246 |
|
|
}
|
247 |
|
|
|
248 |
|
|
if (*p == NULL)
|
249 |
|
|
continue;
|
250 |
|
|
|
251 |
|
|
gdb_xml_debug (parser, _("Parsing attribute %s=\"%s\""),
|
252 |
|
|
attribute->name, val);
|
253 |
|
|
|
254 |
|
|
if (attribute->handler)
|
255 |
|
|
parsed_value = attribute->handler (parser, attribute, val);
|
256 |
|
|
else
|
257 |
|
|
parsed_value = xstrdup (val);
|
258 |
|
|
|
259 |
|
|
new_value.name = attribute->name;
|
260 |
|
|
new_value.value = parsed_value;
|
261 |
|
|
VEC_safe_push (gdb_xml_value_s, attributes, &new_value);
|
262 |
|
|
}
|
263 |
|
|
|
264 |
|
|
/* Check for unrecognized attributes. */
|
265 |
|
|
if (debug_xml)
|
266 |
|
|
{
|
267 |
|
|
const XML_Char **p;
|
268 |
|
|
|
269 |
|
|
for (p = attrs; *p != NULL; p += 2)
|
270 |
|
|
{
|
271 |
|
|
for (attribute = element->attributes;
|
272 |
|
|
attribute != NULL && attribute->name != NULL;
|
273 |
|
|
attribute++)
|
274 |
|
|
if (strcmp (attribute->name, *p) == 0)
|
275 |
|
|
break;
|
276 |
|
|
|
277 |
|
|
if (attribute == NULL || attribute->name == NULL)
|
278 |
|
|
gdb_xml_debug (parser, _("Ignoring unknown attribute %s"), *p);
|
279 |
|
|
}
|
280 |
|
|
}
|
281 |
|
|
|
282 |
|
|
/* Call the element handler if there is one. */
|
283 |
|
|
if (element->start_handler)
|
284 |
|
|
element->start_handler (parser, element, parser->user_data, attributes);
|
285 |
|
|
|
286 |
|
|
/* Fill in a new scope level. */
|
287 |
|
|
scope = VEC_last (scope_level_s, parser->scopes);
|
288 |
|
|
scope->element = element;
|
289 |
|
|
scope->elements = element->children;
|
290 |
|
|
|
291 |
|
|
do_cleanups (back_to);
|
292 |
|
|
}
|
293 |
|
|
|
294 |
|
|
/* Wrapper for gdb_xml_start_element, to prevent throwing exceptions
|
295 |
|
|
through expat. */
|
296 |
|
|
|
297 |
|
|
static void
|
298 |
|
|
gdb_xml_start_element_wrapper (void *data, const XML_Char *name,
|
299 |
|
|
const XML_Char **attrs)
|
300 |
|
|
{
|
301 |
|
|
struct gdb_xml_parser *parser = data;
|
302 |
|
|
volatile struct gdb_exception ex;
|
303 |
|
|
|
304 |
|
|
if (parser->error.reason < 0)
|
305 |
|
|
return;
|
306 |
|
|
|
307 |
|
|
TRY_CATCH (ex, RETURN_MASK_ALL)
|
308 |
|
|
{
|
309 |
|
|
gdb_xml_start_element (data, name, attrs);
|
310 |
|
|
}
|
311 |
|
|
if (ex.reason < 0)
|
312 |
|
|
{
|
313 |
|
|
parser->error = ex;
|
314 |
|
|
#ifdef HAVE_XML_STOPPARSER
|
315 |
|
|
XML_StopParser (parser->expat_parser, XML_FALSE);
|
316 |
|
|
#endif
|
317 |
|
|
}
|
318 |
|
|
}
|
319 |
|
|
|
320 |
|
|
/* Handle the end of an element. DATA is our local XML parser, and
|
321 |
|
|
NAME is the current element. */
|
322 |
|
|
|
323 |
|
|
static void
|
324 |
|
|
gdb_xml_end_element (void *data, const XML_Char *name)
|
325 |
|
|
{
|
326 |
|
|
struct gdb_xml_parser *parser = data;
|
327 |
|
|
struct scope_level *scope = VEC_last (scope_level_s, parser->scopes);
|
328 |
|
|
const struct gdb_xml_element *element;
|
329 |
|
|
unsigned int seen;
|
330 |
|
|
|
331 |
|
|
gdb_xml_debug (parser, _("Leaving element <%s>"), name);
|
332 |
|
|
|
333 |
|
|
for (element = scope->elements, seen = 1;
|
334 |
|
|
element != NULL && element->name != NULL;
|
335 |
|
|
element++, seen <<= 1)
|
336 |
|
|
if ((scope->seen & seen) == 0
|
337 |
|
|
&& (element->flags & GDB_XML_EF_OPTIONAL) == 0)
|
338 |
|
|
gdb_xml_error (parser, _("Required element <%s> is missing"),
|
339 |
|
|
element->name);
|
340 |
|
|
|
341 |
|
|
/* Call the element processor. */
|
342 |
|
|
if (scope->element != NULL && scope->element->end_handler)
|
343 |
|
|
{
|
344 |
|
|
char *body;
|
345 |
|
|
|
346 |
|
|
if (scope->body == NULL)
|
347 |
|
|
body = "";
|
348 |
|
|
else
|
349 |
|
|
{
|
350 |
|
|
int length;
|
351 |
|
|
|
352 |
|
|
length = obstack_object_size (scope->body);
|
353 |
|
|
obstack_1grow (scope->body, '\0');
|
354 |
|
|
body = obstack_finish (scope->body);
|
355 |
|
|
|
356 |
|
|
/* Strip leading and trailing whitespace. */
|
357 |
|
|
while (length > 0 && ISSPACE (body[length-1]))
|
358 |
|
|
body[--length] = '\0';
|
359 |
|
|
while (*body && ISSPACE (*body))
|
360 |
|
|
body++;
|
361 |
|
|
}
|
362 |
|
|
|
363 |
|
|
scope->element->end_handler (parser, scope->element, parser->user_data,
|
364 |
|
|
body);
|
365 |
|
|
}
|
366 |
|
|
else if (scope->element == NULL)
|
367 |
|
|
XML_DefaultCurrent (parser->expat_parser);
|
368 |
|
|
|
369 |
|
|
/* Pop the scope level. */
|
370 |
|
|
if (scope->body)
|
371 |
|
|
{
|
372 |
|
|
obstack_free (scope->body, NULL);
|
373 |
|
|
xfree (scope->body);
|
374 |
|
|
}
|
375 |
|
|
VEC_pop (scope_level_s, parser->scopes);
|
376 |
|
|
}
|
377 |
|
|
|
378 |
|
|
/* Wrapper for gdb_xml_end_element, to prevent throwing exceptions
|
379 |
|
|
through expat. */
|
380 |
|
|
|
381 |
|
|
static void
|
382 |
|
|
gdb_xml_end_element_wrapper (void *data, const XML_Char *name)
|
383 |
|
|
{
|
384 |
|
|
struct gdb_xml_parser *parser = data;
|
385 |
|
|
volatile struct gdb_exception ex;
|
386 |
|
|
|
387 |
|
|
if (parser->error.reason < 0)
|
388 |
|
|
return;
|
389 |
|
|
|
390 |
|
|
TRY_CATCH (ex, RETURN_MASK_ALL)
|
391 |
|
|
{
|
392 |
|
|
gdb_xml_end_element (data, name);
|
393 |
|
|
}
|
394 |
|
|
if (ex.reason < 0)
|
395 |
|
|
{
|
396 |
|
|
parser->error = ex;
|
397 |
|
|
#ifdef HAVE_XML_STOPPARSER
|
398 |
|
|
XML_StopParser (parser->expat_parser, XML_FALSE);
|
399 |
|
|
#endif
|
400 |
|
|
}
|
401 |
|
|
}
|
402 |
|
|
|
403 |
|
|
/* Free a parser and all its associated state. */
|
404 |
|
|
|
405 |
|
|
static void
|
406 |
|
|
gdb_xml_cleanup (void *arg)
|
407 |
|
|
{
|
408 |
|
|
struct gdb_xml_parser *parser = arg;
|
409 |
|
|
struct scope_level *scope;
|
410 |
|
|
int ix;
|
411 |
|
|
|
412 |
|
|
XML_ParserFree (parser->expat_parser);
|
413 |
|
|
|
414 |
|
|
/* Clean up the scopes. */
|
415 |
|
|
for (ix = 0; VEC_iterate (scope_level_s, parser->scopes, ix, scope); ix++)
|
416 |
|
|
if (scope->body)
|
417 |
|
|
{
|
418 |
|
|
obstack_free (scope->body, NULL);
|
419 |
|
|
xfree (scope->body);
|
420 |
|
|
}
|
421 |
|
|
VEC_free (scope_level_s, parser->scopes);
|
422 |
|
|
|
423 |
|
|
xfree (parser);
|
424 |
|
|
}
|
425 |
|
|
|
426 |
|
|
/* Initialize and return a parser. Register a cleanup to destroy the
|
427 |
|
|
parser. */
|
428 |
|
|
|
429 |
|
|
struct gdb_xml_parser *
|
430 |
|
|
gdb_xml_create_parser_and_cleanup (const char *name,
|
431 |
|
|
const struct gdb_xml_element *elements,
|
432 |
|
|
void *user_data)
|
433 |
|
|
{
|
434 |
|
|
struct gdb_xml_parser *parser;
|
435 |
|
|
struct scope_level start_scope;
|
436 |
|
|
|
437 |
|
|
/* Initialize the parser. */
|
438 |
|
|
parser = XZALLOC (struct gdb_xml_parser);
|
439 |
|
|
parser->expat_parser = XML_ParserCreateNS (NULL, '!');
|
440 |
|
|
if (parser->expat_parser == NULL)
|
441 |
|
|
{
|
442 |
|
|
xfree (parser);
|
443 |
|
|
nomem (0);
|
444 |
|
|
}
|
445 |
|
|
|
446 |
|
|
parser->name = name;
|
447 |
|
|
|
448 |
|
|
parser->user_data = user_data;
|
449 |
|
|
XML_SetUserData (parser->expat_parser, parser);
|
450 |
|
|
|
451 |
|
|
/* Set the callbacks. */
|
452 |
|
|
XML_SetElementHandler (parser->expat_parser, gdb_xml_start_element_wrapper,
|
453 |
|
|
gdb_xml_end_element_wrapper);
|
454 |
|
|
XML_SetCharacterDataHandler (parser->expat_parser, gdb_xml_body_text);
|
455 |
|
|
|
456 |
|
|
/* Initialize the outer scope. */
|
457 |
|
|
memset (&start_scope, 0, sizeof (start_scope));
|
458 |
|
|
start_scope.elements = elements;
|
459 |
|
|
VEC_safe_push (scope_level_s, parser->scopes, &start_scope);
|
460 |
|
|
|
461 |
|
|
make_cleanup (gdb_xml_cleanup, parser);
|
462 |
|
|
|
463 |
|
|
return parser;
|
464 |
|
|
}
|
465 |
|
|
|
466 |
|
|
/* External entity handler. The only external entities we support
|
467 |
|
|
are those compiled into GDB (we do not fetch entities from the
|
468 |
|
|
target). */
|
469 |
|
|
|
470 |
|
|
static int XMLCALL
|
471 |
|
|
gdb_xml_fetch_external_entity (XML_Parser expat_parser,
|
472 |
|
|
const XML_Char *context,
|
473 |
|
|
const XML_Char *base,
|
474 |
|
|
const XML_Char *systemId,
|
475 |
|
|
const XML_Char *publicId)
|
476 |
|
|
{
|
477 |
|
|
struct gdb_xml_parser *parser = XML_GetUserData (expat_parser);
|
478 |
|
|
XML_Parser entity_parser;
|
479 |
|
|
const char *text;
|
480 |
|
|
enum XML_Status status;
|
481 |
|
|
|
482 |
|
|
if (systemId == NULL)
|
483 |
|
|
{
|
484 |
|
|
text = fetch_xml_builtin (parser->dtd_name);
|
485 |
|
|
if (text == NULL)
|
486 |
|
|
internal_error (__FILE__, __LINE__, "could not locate built-in DTD %s",
|
487 |
|
|
parser->dtd_name);
|
488 |
|
|
}
|
489 |
|
|
else
|
490 |
|
|
{
|
491 |
|
|
text = fetch_xml_builtin (systemId);
|
492 |
|
|
if (text == NULL)
|
493 |
|
|
return XML_STATUS_ERROR;
|
494 |
|
|
}
|
495 |
|
|
|
496 |
|
|
entity_parser = XML_ExternalEntityParserCreate (expat_parser, context, NULL);
|
497 |
|
|
|
498 |
|
|
/* Don't use our handlers for the contents of the DTD. Just let expat
|
499 |
|
|
process it. */
|
500 |
|
|
XML_SetElementHandler (entity_parser, NULL, NULL);
|
501 |
|
|
XML_SetDoctypeDeclHandler (entity_parser, NULL, NULL);
|
502 |
|
|
XML_SetXmlDeclHandler (entity_parser, NULL);
|
503 |
|
|
XML_SetDefaultHandler (entity_parser, NULL);
|
504 |
|
|
XML_SetUserData (entity_parser, NULL);
|
505 |
|
|
|
506 |
|
|
status = XML_Parse (entity_parser, text, strlen (text), 1);
|
507 |
|
|
|
508 |
|
|
XML_ParserFree (entity_parser);
|
509 |
|
|
return status;
|
510 |
|
|
}
|
511 |
|
|
|
512 |
|
|
/* Associate DTD_NAME, which must be the name of a compiled-in DTD,
|
513 |
|
|
with PARSER. */
|
514 |
|
|
|
515 |
|
|
void
|
516 |
|
|
gdb_xml_use_dtd (struct gdb_xml_parser *parser, const char *dtd_name)
|
517 |
|
|
{
|
518 |
|
|
enum XML_Error err;
|
519 |
|
|
|
520 |
|
|
parser->dtd_name = dtd_name;
|
521 |
|
|
|
522 |
|
|
XML_SetParamEntityParsing (parser->expat_parser,
|
523 |
|
|
XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE);
|
524 |
|
|
XML_SetExternalEntityRefHandler (parser->expat_parser,
|
525 |
|
|
gdb_xml_fetch_external_entity);
|
526 |
|
|
|
527 |
|
|
/* Even if no DTD is provided, use the built-in DTD anyway. */
|
528 |
|
|
err = XML_UseForeignDTD (parser->expat_parser, XML_TRUE);
|
529 |
|
|
if (err != XML_ERROR_NONE)
|
530 |
|
|
internal_error (__FILE__, __LINE__,
|
531 |
|
|
"XML_UseForeignDTD failed: %s", XML_ErrorString (err));
|
532 |
|
|
}
|
533 |
|
|
|
534 |
|
|
/* Invoke PARSER on BUFFER. BUFFER is the data to parse, which
|
535 |
|
|
should be NUL-terminated.
|
536 |
|
|
|
537 |
|
|
The return value is 0 for success or -1 for error. It may throw,
|
538 |
|
|
but only if something unexpected goes wrong during parsing; parse
|
539 |
|
|
errors will be caught, warned about, and reported as failure. */
|
540 |
|
|
|
541 |
|
|
int
|
542 |
|
|
gdb_xml_parse (struct gdb_xml_parser *parser, const char *buffer)
|
543 |
|
|
{
|
544 |
|
|
enum XML_Status status;
|
545 |
|
|
const char *error_string;
|
546 |
|
|
|
547 |
|
|
gdb_xml_debug (parser, _("Starting:\n%s"), buffer);
|
548 |
|
|
|
549 |
|
|
status = XML_Parse (parser->expat_parser, buffer, strlen (buffer), 1);
|
550 |
|
|
|
551 |
|
|
if (status == XML_STATUS_OK && parser->error.reason == 0)
|
552 |
|
|
return 0;
|
553 |
|
|
|
554 |
|
|
if (parser->error.reason == RETURN_ERROR
|
555 |
|
|
&& parser->error.error == XML_PARSE_ERROR)
|
556 |
|
|
{
|
557 |
|
|
gdb_assert (parser->error.message != NULL);
|
558 |
|
|
error_string = parser->error.message;
|
559 |
|
|
}
|
560 |
|
|
else if (status == XML_STATUS_ERROR)
|
561 |
|
|
{
|
562 |
|
|
enum XML_Error err = XML_GetErrorCode (parser->expat_parser);
|
563 |
|
|
error_string = XML_ErrorString (err);
|
564 |
|
|
}
|
565 |
|
|
else
|
566 |
|
|
{
|
567 |
|
|
gdb_assert (parser->error.reason < 0);
|
568 |
|
|
throw_exception (parser->error);
|
569 |
|
|
}
|
570 |
|
|
|
571 |
|
|
if (parser->last_line != 0)
|
572 |
|
|
warning (_("while parsing %s (at line %d): %s"), parser->name,
|
573 |
|
|
parser->last_line, error_string);
|
574 |
|
|
else
|
575 |
|
|
warning (_("while parsing %s: %s"), parser->name, error_string);
|
576 |
|
|
|
577 |
|
|
return -1;
|
578 |
|
|
}
|
579 |
|
|
|
580 |
|
|
/* Parse a field VALSTR that we expect to contain an integer value.
|
581 |
|
|
The integer is returned in *VALP. The string is parsed with an
|
582 |
|
|
equivalent to strtoul.
|
583 |
|
|
|
584 |
|
|
Returns 0 for success, -1 for error. */
|
585 |
|
|
|
586 |
|
|
static int
|
587 |
|
|
xml_parse_unsigned_integer (const char *valstr, ULONGEST *valp)
|
588 |
|
|
{
|
589 |
|
|
const char *endptr;
|
590 |
|
|
ULONGEST result;
|
591 |
|
|
|
592 |
|
|
if (*valstr == '\0')
|
593 |
|
|
return -1;
|
594 |
|
|
|
595 |
|
|
result = strtoulst (valstr, &endptr, 0);
|
596 |
|
|
if (*endptr != '\0')
|
597 |
|
|
return -1;
|
598 |
|
|
|
599 |
|
|
*valp = result;
|
600 |
|
|
return 0;
|
601 |
|
|
}
|
602 |
|
|
|
603 |
|
|
/* Parse an integer string into a ULONGEST and return it, or call
|
604 |
|
|
gdb_xml_error if it could not be parsed. */
|
605 |
|
|
|
606 |
|
|
ULONGEST
|
607 |
|
|
gdb_xml_parse_ulongest (struct gdb_xml_parser *parser, const char *value)
|
608 |
|
|
{
|
609 |
|
|
ULONGEST result;
|
610 |
|
|
|
611 |
|
|
if (xml_parse_unsigned_integer (value, &result) != 0)
|
612 |
|
|
gdb_xml_error (parser, _("Can't convert \"%s\" to an integer"), value);
|
613 |
|
|
|
614 |
|
|
return result;
|
615 |
|
|
}
|
616 |
|
|
|
617 |
|
|
/* Parse an integer attribute into a ULONGEST. */
|
618 |
|
|
|
619 |
|
|
void *
|
620 |
|
|
gdb_xml_parse_attr_ulongest (struct gdb_xml_parser *parser,
|
621 |
|
|
const struct gdb_xml_attribute *attribute,
|
622 |
|
|
const char *value)
|
623 |
|
|
{
|
624 |
|
|
ULONGEST result;
|
625 |
|
|
void *ret;
|
626 |
|
|
|
627 |
|
|
if (xml_parse_unsigned_integer (value, &result) != 0)
|
628 |
|
|
gdb_xml_error (parser, _("Can't convert %s=\"%s\" to an integer"),
|
629 |
|
|
attribute->name, value);
|
630 |
|
|
|
631 |
|
|
ret = xmalloc (sizeof (result));
|
632 |
|
|
memcpy (ret, &result, sizeof (result));
|
633 |
|
|
return ret;
|
634 |
|
|
}
|
635 |
|
|
|
636 |
|
|
/* A handler_data for yes/no boolean values. */
|
637 |
|
|
|
638 |
|
|
const struct gdb_xml_enum gdb_xml_enums_boolean[] = {
|
639 |
|
|
{ "yes", 1 },
|
640 |
|
|
{ "no", 0 },
|
641 |
|
|
{ NULL, 0 }
|
642 |
|
|
};
|
643 |
|
|
|
644 |
|
|
/* Map NAME to VALUE. A struct gdb_xml_enum * should be saved as the
|
645 |
|
|
value of handler_data when using gdb_xml_parse_attr_enum to parse a
|
646 |
|
|
fixed list of possible strings. The list is terminated by an entry
|
647 |
|
|
with NAME == NULL. */
|
648 |
|
|
|
649 |
|
|
void *
|
650 |
|
|
gdb_xml_parse_attr_enum (struct gdb_xml_parser *parser,
|
651 |
|
|
const struct gdb_xml_attribute *attribute,
|
652 |
|
|
const char *value)
|
653 |
|
|
{
|
654 |
|
|
const struct gdb_xml_enum *enums = attribute->handler_data;
|
655 |
|
|
void *ret;
|
656 |
|
|
|
657 |
|
|
for (enums = attribute->handler_data; enums->name != NULL; enums++)
|
658 |
|
|
if (strcasecmp (enums->name, value) == 0)
|
659 |
|
|
break;
|
660 |
|
|
|
661 |
|
|
if (enums->name == NULL)
|
662 |
|
|
gdb_xml_error (parser, _("Unknown attribute value %s=\"%s\""),
|
663 |
|
|
attribute->name, value);
|
664 |
|
|
|
665 |
|
|
ret = xmalloc (sizeof (enums->value));
|
666 |
|
|
memcpy (ret, &enums->value, sizeof (enums->value));
|
667 |
|
|
return ret;
|
668 |
|
|
}
|
669 |
|
|
|
670 |
|
|
|
671 |
|
|
/* XInclude processing. This is done as a separate step from actually
|
672 |
|
|
parsing the document, so that we can produce a single combined XML
|
673 |
|
|
document - e.g. to hand to a front end or to simplify comparing two
|
674 |
|
|
documents. We make extensive use of XML_DefaultCurrent, to pass
|
675 |
|
|
input text directly into the output without reformatting or
|
676 |
|
|
requoting it.
|
677 |
|
|
|
678 |
|
|
We output the DOCTYPE declaration for the first document unchanged,
|
679 |
|
|
if present, and discard DOCTYPEs from included documents. Only the
|
680 |
|
|
one we pass through here is used when we feed the result back to
|
681 |
|
|
expat. The XInclude standard explicitly does not discuss
|
682 |
|
|
validation of the result; we choose to apply the same DTD applied
|
683 |
|
|
to the outermost document.
|
684 |
|
|
|
685 |
|
|
We can not simply include the external DTD subset in the document
|
686 |
|
|
as an internal subset, because <!IGNORE> and <!INCLUDE> are valid
|
687 |
|
|
only in external subsets. But if we do not pass the DTD into the
|
688 |
|
|
output at all, default values will not be filled in.
|
689 |
|
|
|
690 |
|
|
We don't pass through any <?xml> declaration because we generate
|
691 |
|
|
UTF-8, not whatever the input encoding was. */
|
692 |
|
|
|
693 |
|
|
struct xinclude_parsing_data
|
694 |
|
|
{
|
695 |
|
|
/* The obstack to build the output in. */
|
696 |
|
|
struct obstack obstack;
|
697 |
|
|
|
698 |
|
|
/* A count indicating whether we are in an element whose
|
699 |
|
|
children should not be copied to the output, and if so,
|
700 |
|
|
how deep we are nested. This is used for anything inside
|
701 |
|
|
an xi:include, and for the DTD. */
|
702 |
|
|
int skip_depth;
|
703 |
|
|
|
704 |
|
|
/* The number of <xi:include> elements currently being processed,
|
705 |
|
|
to detect loops. */
|
706 |
|
|
int include_depth;
|
707 |
|
|
|
708 |
|
|
/* A function to call to obtain additional features, and its
|
709 |
|
|
baton. */
|
710 |
|
|
xml_fetch_another fetcher;
|
711 |
|
|
void *fetcher_baton;
|
712 |
|
|
};
|
713 |
|
|
|
714 |
|
|
static void
|
715 |
|
|
xinclude_start_include (struct gdb_xml_parser *parser,
|
716 |
|
|
const struct gdb_xml_element *element,
|
717 |
|
|
void *user_data, VEC(gdb_xml_value_s) *attributes)
|
718 |
|
|
{
|
719 |
|
|
struct xinclude_parsing_data *data = user_data;
|
720 |
|
|
char *href = VEC_index (gdb_xml_value_s, attributes, 0)->value;
|
721 |
|
|
struct cleanup *back_to;
|
722 |
|
|
char *text, *output;
|
723 |
|
|
int ret;
|
724 |
|
|
|
725 |
|
|
gdb_xml_debug (parser, _("Processing XInclude of \"%s\""), href);
|
726 |
|
|
|
727 |
|
|
if (data->include_depth > MAX_XINCLUDE_DEPTH)
|
728 |
|
|
gdb_xml_error (parser, _("Maximum XInclude depth (%d) exceeded"),
|
729 |
|
|
MAX_XINCLUDE_DEPTH);
|
730 |
|
|
|
731 |
|
|
text = data->fetcher (href, data->fetcher_baton);
|
732 |
|
|
if (text == NULL)
|
733 |
|
|
gdb_xml_error (parser, _("Could not load XML document \"%s\""), href);
|
734 |
|
|
back_to = make_cleanup (xfree, text);
|
735 |
|
|
|
736 |
|
|
output = xml_process_xincludes (parser->name, text, data->fetcher,
|
737 |
|
|
data->fetcher_baton,
|
738 |
|
|
data->include_depth + 1);
|
739 |
|
|
if (output == NULL)
|
740 |
|
|
gdb_xml_error (parser, _("Parsing \"%s\" failed"), href);
|
741 |
|
|
|
742 |
|
|
obstack_grow (&data->obstack, output, strlen (output));
|
743 |
|
|
xfree (output);
|
744 |
|
|
|
745 |
|
|
do_cleanups (back_to);
|
746 |
|
|
|
747 |
|
|
data->skip_depth++;
|
748 |
|
|
}
|
749 |
|
|
|
750 |
|
|
static void
|
751 |
|
|
xinclude_end_include (struct gdb_xml_parser *parser,
|
752 |
|
|
const struct gdb_xml_element *element,
|
753 |
|
|
void *user_data, const char *body_text)
|
754 |
|
|
{
|
755 |
|
|
struct xinclude_parsing_data *data = user_data;
|
756 |
|
|
|
757 |
|
|
data->skip_depth--;
|
758 |
|
|
}
|
759 |
|
|
|
760 |
|
|
static void XMLCALL
|
761 |
|
|
xml_xinclude_default (void *data_, const XML_Char *s, int len)
|
762 |
|
|
{
|
763 |
|
|
struct gdb_xml_parser *parser = data_;
|
764 |
|
|
struct xinclude_parsing_data *data = parser->user_data;
|
765 |
|
|
|
766 |
|
|
/* If we are inside of e.g. xi:include or the DTD, don't save this
|
767 |
|
|
string. */
|
768 |
|
|
if (data->skip_depth)
|
769 |
|
|
return;
|
770 |
|
|
|
771 |
|
|
/* Otherwise just add it to the end of the document we're building
|
772 |
|
|
up. */
|
773 |
|
|
obstack_grow (&data->obstack, s, len);
|
774 |
|
|
}
|
775 |
|
|
|
776 |
|
|
static void XMLCALL
|
777 |
|
|
xml_xinclude_start_doctype (void *data_, const XML_Char *doctypeName,
|
778 |
|
|
const XML_Char *sysid, const XML_Char *pubid,
|
779 |
|
|
int has_internal_subset)
|
780 |
|
|
{
|
781 |
|
|
struct gdb_xml_parser *parser = data_;
|
782 |
|
|
struct xinclude_parsing_data *data = parser->user_data;
|
783 |
|
|
|
784 |
|
|
/* Don't print out the doctype, or the contents of the DTD internal
|
785 |
|
|
subset, if any. */
|
786 |
|
|
data->skip_depth++;
|
787 |
|
|
}
|
788 |
|
|
|
789 |
|
|
static void XMLCALL
|
790 |
|
|
xml_xinclude_end_doctype (void *data_)
|
791 |
|
|
{
|
792 |
|
|
struct gdb_xml_parser *parser = data_;
|
793 |
|
|
struct xinclude_parsing_data *data = parser->user_data;
|
794 |
|
|
|
795 |
|
|
data->skip_depth--;
|
796 |
|
|
}
|
797 |
|
|
|
798 |
|
|
static void XMLCALL
|
799 |
|
|
xml_xinclude_xml_decl (void *data_, const XML_Char *version,
|
800 |
|
|
const XML_Char *encoding, int standalone)
|
801 |
|
|
{
|
802 |
|
|
/* Do nothing - this function prevents the default handler from
|
803 |
|
|
being called, thus suppressing the XML declaration from the
|
804 |
|
|
output. */
|
805 |
|
|
}
|
806 |
|
|
|
807 |
|
|
static void
|
808 |
|
|
xml_xinclude_cleanup (void *data_)
|
809 |
|
|
{
|
810 |
|
|
struct xinclude_parsing_data *data = data_;
|
811 |
|
|
|
812 |
|
|
obstack_free (&data->obstack, NULL);
|
813 |
|
|
xfree (data);
|
814 |
|
|
}
|
815 |
|
|
|
816 |
|
|
const struct gdb_xml_attribute xinclude_attributes[] = {
|
817 |
|
|
{ "href", GDB_XML_AF_NONE, NULL, NULL },
|
818 |
|
|
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
|
819 |
|
|
};
|
820 |
|
|
|
821 |
|
|
const struct gdb_xml_element xinclude_elements[] = {
|
822 |
|
|
{ "http://www.w3.org/2001/XInclude!include", xinclude_attributes, NULL,
|
823 |
|
|
GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
|
824 |
|
|
xinclude_start_include, xinclude_end_include },
|
825 |
|
|
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
|
826 |
|
|
};
|
827 |
|
|
|
828 |
|
|
/* The main entry point for <xi:include> processing. */
|
829 |
|
|
|
830 |
|
|
char *
|
831 |
|
|
xml_process_xincludes (const char *name, const char *text,
|
832 |
|
|
xml_fetch_another fetcher, void *fetcher_baton,
|
833 |
|
|
int depth)
|
834 |
|
|
{
|
835 |
|
|
enum XML_Error err;
|
836 |
|
|
struct gdb_xml_parser *parser;
|
837 |
|
|
struct xinclude_parsing_data *data;
|
838 |
|
|
struct cleanup *back_to;
|
839 |
|
|
char *result = NULL;
|
840 |
|
|
|
841 |
|
|
data = XZALLOC (struct xinclude_parsing_data);
|
842 |
|
|
obstack_init (&data->obstack);
|
843 |
|
|
back_to = make_cleanup (xml_xinclude_cleanup, data);
|
844 |
|
|
|
845 |
|
|
parser = gdb_xml_create_parser_and_cleanup (name, xinclude_elements, data);
|
846 |
|
|
parser->is_xinclude = 1;
|
847 |
|
|
|
848 |
|
|
data->include_depth = depth;
|
849 |
|
|
data->fetcher = fetcher;
|
850 |
|
|
data->fetcher_baton = fetcher_baton;
|
851 |
|
|
|
852 |
|
|
XML_SetCharacterDataHandler (parser->expat_parser, NULL);
|
853 |
|
|
XML_SetDefaultHandler (parser->expat_parser, xml_xinclude_default);
|
854 |
|
|
|
855 |
|
|
/* Always discard the XML version declarations; the only important
|
856 |
|
|
thing this provides is encoding, and our result will have been
|
857 |
|
|
converted to UTF-8. */
|
858 |
|
|
XML_SetXmlDeclHandler (parser->expat_parser, xml_xinclude_xml_decl);
|
859 |
|
|
|
860 |
|
|
if (depth > 0)
|
861 |
|
|
/* Discard the doctype for included documents. */
|
862 |
|
|
XML_SetDoctypeDeclHandler (parser->expat_parser,
|
863 |
|
|
xml_xinclude_start_doctype,
|
864 |
|
|
xml_xinclude_end_doctype);
|
865 |
|
|
|
866 |
|
|
gdb_xml_use_dtd (parser, "xinclude.dtd");
|
867 |
|
|
|
868 |
|
|
if (gdb_xml_parse (parser, text) == 0)
|
869 |
|
|
{
|
870 |
|
|
obstack_1grow (&data->obstack, '\0');
|
871 |
|
|
result = xstrdup (obstack_finish (&data->obstack));
|
872 |
|
|
|
873 |
|
|
if (depth == 0)
|
874 |
|
|
gdb_xml_debug (parser, _("XInclude processing succeeded."));
|
875 |
|
|
}
|
876 |
|
|
else
|
877 |
|
|
result = NULL;
|
878 |
|
|
|
879 |
|
|
do_cleanups (back_to);
|
880 |
|
|
return result;
|
881 |
|
|
}
|
882 |
|
|
#endif /* HAVE_LIBEXPAT */
|
883 |
|
|
|
884 |
|
|
|
885 |
|
|
/* Return an XML document which was compiled into GDB, from
|
886 |
|
|
the given FILENAME, or NULL if the file was not compiled in. */
|
887 |
|
|
|
888 |
|
|
const char *
|
889 |
|
|
fetch_xml_builtin (const char *filename)
|
890 |
|
|
{
|
891 |
|
|
const char *(*p)[2];
|
892 |
|
|
|
893 |
|
|
for (p = xml_builtin; (*p)[0]; p++)
|
894 |
|
|
if (strcmp ((*p)[0], filename) == 0)
|
895 |
|
|
return (*p)[1];
|
896 |
|
|
|
897 |
|
|
return NULL;
|
898 |
|
|
}
|
899 |
|
|
|
900 |
|
|
/* A to_xfer_partial helper function which reads XML files which were
|
901 |
|
|
compiled into GDB. The target may call this function from its own
|
902 |
|
|
to_xfer_partial handler, after converting object and annex to the
|
903 |
|
|
appropriate filename. */
|
904 |
|
|
|
905 |
|
|
LONGEST
|
906 |
|
|
xml_builtin_xfer_partial (const char *filename,
|
907 |
|
|
gdb_byte *readbuf, const gdb_byte *writebuf,
|
908 |
|
|
ULONGEST offset, LONGEST len)
|
909 |
|
|
{
|
910 |
|
|
const char *buf;
|
911 |
|
|
LONGEST len_avail;
|
912 |
|
|
|
913 |
|
|
gdb_assert (readbuf != NULL && writebuf == NULL);
|
914 |
|
|
gdb_assert (filename != NULL);
|
915 |
|
|
|
916 |
|
|
buf = fetch_xml_builtin (filename);
|
917 |
|
|
if (buf == NULL)
|
918 |
|
|
return -1;
|
919 |
|
|
|
920 |
|
|
len_avail = strlen (buf);
|
921 |
|
|
if (offset >= len_avail)
|
922 |
|
|
return 0;
|
923 |
|
|
|
924 |
|
|
if (len > len_avail - offset)
|
925 |
|
|
len = len_avail - offset;
|
926 |
|
|
memcpy (readbuf, buf + offset, len);
|
927 |
|
|
return len;
|
928 |
|
|
}
|
929 |
|
|
|
930 |
|
|
|
931 |
|
|
static void
|
932 |
|
|
show_debug_xml (struct ui_file *file, int from_tty,
|
933 |
|
|
struct cmd_list_element *c, const char *value)
|
934 |
|
|
{
|
935 |
|
|
fprintf_filtered (file, _("XML debugging is %s.\n"), value);
|
936 |
|
|
}
|
937 |
|
|
|
938 |
|
|
/* Return a malloc allocated string with special characters from TEXT
|
939 |
|
|
replaced by entity references. */
|
940 |
|
|
|
941 |
|
|
char *
|
942 |
|
|
xml_escape_text (const char *text)
|
943 |
|
|
{
|
944 |
|
|
char *result;
|
945 |
|
|
int i, special;
|
946 |
|
|
|
947 |
|
|
/* Compute the length of the result. */
|
948 |
|
|
for (i = 0, special = 0; text[i] != '\0'; i++)
|
949 |
|
|
switch (text[i])
|
950 |
|
|
{
|
951 |
|
|
case '\'':
|
952 |
|
|
case '\"':
|
953 |
|
|
special += 5;
|
954 |
|
|
break;
|
955 |
|
|
case '&':
|
956 |
|
|
special += 4;
|
957 |
|
|
break;
|
958 |
|
|
case '<':
|
959 |
|
|
case '>':
|
960 |
|
|
special += 3;
|
961 |
|
|
break;
|
962 |
|
|
default:
|
963 |
|
|
break;
|
964 |
|
|
}
|
965 |
|
|
|
966 |
|
|
/* Expand the result. */
|
967 |
|
|
result = xmalloc (i + special + 1);
|
968 |
|
|
for (i = 0, special = 0; text[i] != '\0'; i++)
|
969 |
|
|
switch (text[i])
|
970 |
|
|
{
|
971 |
|
|
case '\'':
|
972 |
|
|
strcpy (result + i + special, "'");
|
973 |
|
|
special += 5;
|
974 |
|
|
break;
|
975 |
|
|
case '\"':
|
976 |
|
|
strcpy (result + i + special, """);
|
977 |
|
|
special += 5;
|
978 |
|
|
break;
|
979 |
|
|
case '&':
|
980 |
|
|
strcpy (result + i + special, "&");
|
981 |
|
|
special += 4;
|
982 |
|
|
break;
|
983 |
|
|
case '<':
|
984 |
|
|
strcpy (result + i + special, "<");
|
985 |
|
|
special += 3;
|
986 |
|
|
break;
|
987 |
|
|
case '>':
|
988 |
|
|
strcpy (result + i + special, ">");
|
989 |
|
|
special += 3;
|
990 |
|
|
break;
|
991 |
|
|
default:
|
992 |
|
|
result[i + special] = text[i];
|
993 |
|
|
break;
|
994 |
|
|
}
|
995 |
|
|
result[i + special] = '\0';
|
996 |
|
|
|
997 |
|
|
return result;
|
998 |
|
|
}
|
999 |
|
|
|
1000 |
|
|
void _initialize_xml_support (void);
|
1001 |
|
|
|
1002 |
|
|
void
|
1003 |
|
|
_initialize_xml_support (void)
|
1004 |
|
|
{
|
1005 |
|
|
add_setshow_boolean_cmd ("xml", class_maintenance, &debug_xml,
|
1006 |
|
|
_("Set XML parser debugging."),
|
1007 |
|
|
_("Show XML parser debugging."),
|
1008 |
|
|
_("When set, debugging messages for XML parsers "
|
1009 |
|
|
"are displayed."),
|
1010 |
|
|
NULL, show_debug_xml,
|
1011 |
|
|
&setdebuglist, &showdebuglist);
|
1012 |
|
|
}
|