X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fxml-support.c;h=f5a142754578e18572874406258cb2e2c0d0fcc0;hb=84e098cdea4eb6a5ddc525a2145ffe66475fa2af;hp=18416c8019e25ee8ff2f5a84dc2d3fb5f06ea92e;hpb=d097fa3ec237939836cf8b28d0fdc11d140177f2;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/xml-support.c b/gdb/xml-support.c index 18416c8019..f5a1427545 100644 --- a/gdb/xml-support.c +++ b/gdb/xml-support.c @@ -1,13 +1,12 @@ /* Helper routines for parsing XML using Expat. - Copyright (C) 2006 - Free Software Foundation, Inc. + Copyright (C) 2006-2019 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -16,31 +15,42 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see . */ #include "defs.h" #include "gdbcmd.h" +#include "xml-builtin.h" +#include "xml-support.h" +#include "gdbsupport/filestuff.h" +#include "safe-ctype.h" +#include +#include /* Debugging flag. */ -static int debug_xml; +static bool debug_xml; /* The contents of this file are only useful if XML support is available. */ #ifdef HAVE_LIBEXPAT -#include "exceptions.h" -#include "xml-support.h" - #include "gdb_expat.h" -#include "gdb_string.h" -#include "safe-ctype.h" + +/* The maximum depth of nesting. No need to be miserly, + we just want to avoid running out of stack on loops. */ +#define MAX_XINCLUDE_DEPTH 30 + +/* Simplified XML parser infrastructure. */ /* A parsing level -- used to keep track of the current element nesting. */ struct scope_level { + explicit scope_level (const gdb_xml_element *elements_ = NULL) + : elements (elements_), + element (NULL), + seen (0) + {} + /* Elements we allow at this level. */ const struct gdb_xml_element *elements; @@ -52,142 +62,243 @@ struct scope_level unsigned int seen; /* Body text accumulation. */ - struct obstack *body; + std::string body; }; -typedef struct scope_level scope_level_s; -DEF_VEC_O(scope_level_s); /* The parser itself, and our additional state. */ struct gdb_xml_parser { - XML_Parser expat_parser; /* The underlying expat parser. */ + gdb_xml_parser (const char *name, + const gdb_xml_element *elements, + void *user_data); + ~gdb_xml_parser(); + + /* Associate DTD_NAME, which must be the name of a compiled-in DTD, + with the parser. */ + void use_dtd (const char *dtd_name); + + /* Return the name of the expected / default DTD, if specified. */ + const char *dtd_name () + { return m_dtd_name; } + + /* Invoke the parser on BUFFER. BUFFER is the data to parse, which + should be NUL-terminated. + + The return value is 0 for success or -1 for error. It may throw, + but only if something unexpected goes wrong during parsing; parse + errors will be caught, warned about, and reported as failure. */ + int parse (const char *buffer); + + /* Issue a debugging message. */ + void vdebug (const char *format, va_list ap) + ATTRIBUTE_PRINTF (2, 0); - const char *name; /* Name of this parser. */ - void *user_data; /* The user's callback data, for handlers. */ + /* Issue an error message, and stop parsing. */ + void verror (const char *format, va_list ap) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 0); - VEC(scope_level_s) *scopes; /* Scoping stack. */ + void body_text (const XML_Char *text, int length); + void start_element (const XML_Char *name, const XML_Char **attrs); + void end_element (const XML_Char *name); - struct gdb_exception error; /* A thrown error, if any. */ - int last_line; /* The line of the thrown error, or 0. */ + /* Return the name of this parser. */ + const char *name () + { return m_name; } + + /* Return the user's callback data, for handlers. */ + void *user_data () + { return m_user_data; }; + + /* Are we the special parser? */ + void set_is_xinclude (bool is_xinclude) + { m_is_xinclude = is_xinclude; } + + /* A thrown error, if any. */ + void set_error (gdb_exception &&error) + { + m_error = std::move (error); +#ifdef HAVE_XML_STOPPARSER + XML_StopParser (m_expat_parser, XML_FALSE); +#endif + } + + /* Return the underlying expat parser. */ + XML_Parser expat_parser () + { return m_expat_parser; } + +private: + /* The underlying expat parser. */ + XML_Parser m_expat_parser; + + /* Name of this parser. */ + const char *m_name; + + /* The user's callback data, for handlers. */ + void *m_user_data; + + /* Scoping stack. */ + std::vector m_scopes; + +/* A thrown error, if any. */ + struct gdb_exception m_error; + + /* The line of the thrown error, or 0. */ + int m_last_line; + + /* The name of the expected / default DTD, if specified. */ + const char *m_dtd_name; + + /* Are we the special parser? */ + bool m_is_xinclude; }; /* Process some body text. We accumulate the text for later use; it's wrong to do anything with it immediately, because a single block of text might be broken up into multiple calls to this function. */ +void +gdb_xml_parser::body_text (const XML_Char *text, int length) +{ + if (m_error.reason < 0) + return; + + scope_level &scope = m_scopes.back (); + scope.body.append (text, length); +} + static void gdb_xml_body_text (void *data, const XML_Char *text, int length) { - struct gdb_xml_parser *parser = data; - struct scope_level *scope = VEC_last (scope_level_s, parser->scopes); + struct gdb_xml_parser *parser = (struct gdb_xml_parser *) data; - if (scope->body == NULL) - { - scope->body = XZALLOC (struct obstack); - obstack_init (scope->body); - } - - obstack_grow (scope->body, text, length); + parser->body_text (text, length); } /* Issue a debugging message from one of PARSER's handlers. */ void -gdb_xml_debug (struct gdb_xml_parser *parser, const char *format, ...) +gdb_xml_parser::vdebug (const char *format, va_list ap) { - int line = XML_GetCurrentLineNumber (parser->expat_parser); - va_list ap; - char *message; - - if (!debug_xml) - return; + int line = XML_GetCurrentLineNumber (m_expat_parser); - va_start (ap, format); - message = xstrvprintf (format, ap); + std::string message = string_vprintf (format, ap); if (line) fprintf_unfiltered (gdb_stderr, "%s (line %d): %s\n", - parser->name, line, message); + m_name, line, message.c_str ()); else fprintf_unfiltered (gdb_stderr, "%s: %s\n", - parser->name, message); - xfree (message); + m_name, message.c_str ()); +} + +void +gdb_xml_debug (struct gdb_xml_parser *parser, const char *format, ...) +{ + if (!debug_xml) + return; + + va_list ap; + va_start (ap, format); + parser->vdebug (format, ap); + va_end (ap); } /* Issue an error message from one of PARSER's handlers, and stop parsing. */ +void +gdb_xml_parser::verror (const char *format, va_list ap) +{ + int line = XML_GetCurrentLineNumber (m_expat_parser); + + m_last_line = line; + throw_verror (XML_PARSE_ERROR, format, ap); +} + void gdb_xml_error (struct gdb_xml_parser *parser, const char *format, ...) { - int line = XML_GetCurrentLineNumber (parser->expat_parser); va_list ap; - - parser->last_line = line; va_start (ap, format); - throw_verror (XML_PARSE_ERROR, format, ap); + parser->verror (format, ap); + va_end (ap); } -/* Clean up a vector of parsed attribute values. */ +/* Find the attribute named NAME in the set of parsed attributes + ATTRIBUTES. Returns NULL if not found. */ -static void -gdb_xml_values_cleanup (void *data) +struct gdb_xml_value * +xml_find_attribute (std::vector &attributes, + const char *name) { - VEC(gdb_xml_value_s) **values = data; - struct gdb_xml_value *value; - int ix; + for (gdb_xml_value &value : attributes) + if (strcmp (value.name, name) == 0) + return &value; - for (ix = 0; VEC_iterate (gdb_xml_value_s, *values, ix, value); ix++) - xfree (value->value); - VEC_free (gdb_xml_value_s, *values); + return NULL; } -/* Handle the start of an element. DATA is our local XML parser, NAME - is the element, and ATTRS are the names and values of this - element's attributes. */ +/* Handle the start of an element. NAME is the element, and ATTRS are + the names and values of this element's attributes. */ -static void -gdb_xml_start_element (void *data, const XML_Char *name, - const XML_Char **attrs) +void +gdb_xml_parser::start_element (const XML_Char *name, + const XML_Char **attrs) { - struct gdb_xml_parser *parser = data; - struct scope_level *scope = VEC_last (scope_level_s, parser->scopes); - struct scope_level new_scope; + if (m_error.reason < 0) + return; + const struct gdb_xml_element *element; const struct gdb_xml_attribute *attribute; - VEC(gdb_xml_value_s) *attributes = NULL; unsigned int seen; - struct cleanup *back_to; - - back_to = make_cleanup (gdb_xml_values_cleanup, &attributes); /* Push an error scope. If we return or throw an exception before filling this in, it will tell us to ignore children of this - element. */ - memset (&new_scope, 0, sizeof (new_scope)); - VEC_safe_push (scope_level_s, parser->scopes, &new_scope); + element. Note we don't take a reference to the element yet + because further below we'll process the element which may recurse + back here and push more elements to the vector. When the + recursion unrolls all such elements will have been popped back + already, but if one of those pushes reallocates the vector, + previous element references will be invalidated. */ + m_scopes.emplace_back (); + + /* Get a reference to the current scope. */ + scope_level &scope = m_scopes[m_scopes.size () - 2]; - gdb_xml_debug (parser, _("Entering element <%s>"), name); + gdb_xml_debug (this, _("Entering element <%s>"), name); /* Find this element in the list of the current scope's allowed children. Record that we've seen it. */ seen = 1; - for (element = scope->elements; element && element->name; + for (element = scope.elements; element && element->name; element++, seen <<= 1) if (strcmp (element->name, name) == 0) break; if (element == NULL || element->name == NULL) { - gdb_xml_debug (parser, _("Element <%s> unknown"), name); - do_cleanups (back_to); + /* If we're working on XInclude, can be the child + of absolutely anything. Copy the previous scope's element + list into the new scope even if there was no match. */ + if (m_is_xinclude) + { + XML_DefaultCurrent (m_expat_parser); + + scope_level &unknown_scope = m_scopes.back (); + unknown_scope.elements = scope.elements; + return; + } + + gdb_xml_debug (this, _("Element <%s> unknown"), name); return; } - if (!(element->flags & GDB_XML_EF_REPEATABLE) && (seen & scope->seen)) - gdb_xml_error (parser, _("Element <%s> only expected once"), name); + if (!(element->flags & GDB_XML_EF_REPEATABLE) && (seen & scope.seen)) + gdb_xml_error (this, _("Element <%s> only expected once"), name); - scope->seen |= seen; + scope.seen |= seen; + + std::vector attributes; for (attribute = element->attributes; attribute != NULL && attribute->name != NULL; @@ -196,7 +307,6 @@ gdb_xml_start_element (void *data, const XML_Char *name, const char *val = NULL; const XML_Char **p; void *parsed_value; - struct gdb_xml_value new_value; for (p = attrs; *p != NULL; p += 2) if (!strcmp (attribute->name, p[0])) @@ -207,14 +317,14 @@ gdb_xml_start_element (void *data, const XML_Char *name, if (*p != NULL && val == NULL) { - gdb_xml_debug (parser, _("Attribute \"%s\" missing a value"), + gdb_xml_debug (this, _("Attribute \"%s\" missing a value"), attribute->name); continue; } if (*p == NULL && !(attribute->flags & GDB_XML_AF_OPTIONAL)) { - gdb_xml_error (parser, _("Required attribute \"%s\" of " + gdb_xml_error (this, _("Required attribute \"%s\" of " "<%s> not specified"), attribute->name, element->name); continue; @@ -223,17 +333,15 @@ gdb_xml_start_element (void *data, const XML_Char *name, if (*p == NULL) continue; - gdb_xml_debug (parser, _("Parsing attribute %s=\"%s\""), + gdb_xml_debug (this, _("Parsing attribute %s=\"%s\""), attribute->name, val); if (attribute->handler) - parsed_value = attribute->handler (parser, attribute, val); + parsed_value = attribute->handler (this, attribute, val); else parsed_value = xstrdup (val); - new_value.name = attribute->name; - new_value.value = parsed_value; - VEC_safe_push (gdb_xml_value_s, attributes, &new_value); + attributes.emplace_back (attribute->name, parsed_value); } /* Check for unrecognized attributes. */ @@ -250,20 +358,21 @@ gdb_xml_start_element (void *data, const XML_Char *name, break; if (attribute == NULL || attribute->name == NULL) - gdb_xml_debug (parser, _("Ignoring unknown attribute %s"), *p); + gdb_xml_debug (this, _("Ignoring unknown attribute %s"), *p); } } /* Call the element handler if there is one. */ if (element->start_handler) - element->start_handler (parser, element, parser->user_data, attributes); - - /* Fill in a new scope level. */ - scope = VEC_last (scope_level_s, parser->scopes); - scope->element = element; - scope->elements = element->children; - - do_cleanups (back_to); + element->start_handler (this, element, m_user_data, attributes); + + /* Fill in a new scope level. Note that we must delay getting a + back reference till here because above we might have recursed, + which may have reallocated the vector which invalidates + iterators/pointers/references. */ + scope_level &new_scope = m_scopes.back (); + new_scope.element = element; + new_scope.elements = element->children; } /* Wrapper for gdb_xml_start_element, to prevent throwing exceptions @@ -273,74 +382,70 @@ static void gdb_xml_start_element_wrapper (void *data, const XML_Char *name, const XML_Char **attrs) { - struct gdb_xml_parser *parser = data; - volatile struct gdb_exception ex; + struct gdb_xml_parser *parser = (struct gdb_xml_parser *) data; - if (parser->error.reason < 0) - return; - - TRY_CATCH (ex, RETURN_MASK_ALL) + try { - gdb_xml_start_element (data, name, attrs); + parser->start_element (name, attrs); } - if (ex.reason < 0) + catch (gdb_exception &ex) { - parser->error = ex; - XML_StopParser (parser->expat_parser, XML_FALSE); + parser->set_error (std::move (ex)); } } -/* Handle the end of an element. DATA is our local XML parser, and - NAME is the current element. */ +/* Handle the end of an element. NAME is the current element. */ -static void -gdb_xml_end_element (void *data, const XML_Char *name) +void +gdb_xml_parser::end_element (const XML_Char *name) { - struct gdb_xml_parser *parser = data; - struct scope_level *scope = VEC_last (scope_level_s, parser->scopes); + if (m_error.reason < 0) + return; + + struct scope_level *scope = &m_scopes.back (); const struct gdb_xml_element *element; unsigned int seen; - char *body; - gdb_xml_debug (parser, _("Leaving element <%s>"), name); + gdb_xml_debug (this, _("Leaving element <%s>"), name); for (element = scope->elements, seen = 1; element != NULL && element->name != NULL; element++, seen <<= 1) if ((scope->seen & seen) == 0 && (element->flags & GDB_XML_EF_OPTIONAL) == 0) - gdb_xml_error (parser, _("Required element <%s> is missing"), + gdb_xml_error (this, _("Required element <%s> is missing"), element->name); - /* Call the element processor. */ - if (scope->body == NULL) - body = ""; - else + /* Call the element processor. */ + if (scope->element != NULL && scope->element->end_handler) { - int length; + const char *body; - length = obstack_object_size (scope->body); - obstack_1grow (scope->body, '\0'); - body = obstack_finish (scope->body); + if (scope->body.empty ()) + body = ""; + else + { + int length; - /* Strip leading and trailing whitespace. */ - while (length > 0 && ISSPACE (body[length-1])) - body[--length] = '\0'; - while (*body && ISSPACE (*body)) - body++; - } + length = scope->body.size (); + body = scope->body.c_str (); - if (scope->element != NULL && scope->element->end_handler) - scope->element->end_handler (parser, scope->element, parser->user_data, - body); + /* Strip leading and trailing whitespace. */ + while (length > 0 && ISSPACE (body[length - 1])) + length--; + scope->body.erase (length); + while (*body && ISSPACE (*body)) + body++; + } - /* Pop the scope level. */ - if (scope->body) - { - obstack_free (scope->body, NULL); - xfree (scope->body); + scope->element->end_handler (this, scope->element, + m_user_data, body); } - VEC_pop (scope_level_s, parser->scopes); + else if (scope->element == NULL) + XML_DefaultCurrent (m_expat_parser); + + /* Pop the scope level. */ + m_scopes.pop_back (); } /* Wrapper for gdb_xml_end_element, to prevent throwing exceptions @@ -349,84 +454,119 @@ gdb_xml_end_element (void *data, const XML_Char *name) static void gdb_xml_end_element_wrapper (void *data, const XML_Char *name) { - struct gdb_xml_parser *parser = data; - volatile struct gdb_exception ex; - - if (parser->error.reason < 0) - return; + struct gdb_xml_parser *parser = (struct gdb_xml_parser *) data; - TRY_CATCH (ex, RETURN_MASK_ALL) + try { - gdb_xml_end_element (data, name); + parser->end_element (name); } - if (ex.reason < 0) + catch (gdb_exception &ex) { - parser->error = ex; - XML_StopParser (parser->expat_parser, XML_FALSE); + parser->set_error (std::move (ex)); } } /* Free a parser and all its associated state. */ -static void -gdb_xml_cleanup (void *arg) +gdb_xml_parser::~gdb_xml_parser () +{ + XML_ParserFree (m_expat_parser); +} + +/* Initialize a parser. */ + +gdb_xml_parser::gdb_xml_parser (const char *name, + const gdb_xml_element *elements, + void *user_data) + : m_name (name), + m_user_data (user_data), + m_last_line (0), + m_dtd_name (NULL), + m_is_xinclude (false) { - struct gdb_xml_parser *parser = arg; - struct scope_level *scope; - int ix; + m_expat_parser = XML_ParserCreateNS (NULL, '!'); + if (m_expat_parser == NULL) + malloc_failure (0); - XML_ParserFree (parser->expat_parser); + XML_SetUserData (m_expat_parser, this); - /* Clean up the scopes. */ - for (ix = 0; VEC_iterate (scope_level_s, parser->scopes, ix, scope); ix++) - if (scope->body) - { - obstack_free (scope->body, NULL); - xfree (scope->body); - } - VEC_free (scope_level_s, parser->scopes); + /* Set the callbacks. */ + XML_SetElementHandler (m_expat_parser, gdb_xml_start_element_wrapper, + gdb_xml_end_element_wrapper); + XML_SetCharacterDataHandler (m_expat_parser, gdb_xml_body_text); - xfree (parser); + /* Initialize the outer scope. */ + m_scopes.emplace_back (elements); } -/* Initialize and return a parser. Register a cleanup to destroy the - parser. */ +/* External entity handler. The only external entities we support + are those compiled into GDB (we do not fetch entities from the + target). */ -struct gdb_xml_parser * -gdb_xml_create_parser_and_cleanup (const char *name, - const struct gdb_xml_element *elements, - void *user_data) +static int XMLCALL +gdb_xml_fetch_external_entity (XML_Parser expat_parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId) { - struct gdb_xml_parser *parser; - struct scope_level start_scope; + XML_Parser entity_parser; + const char *text; + enum XML_Status status; - /* Initialize the parser. */ - parser = XZALLOC (struct gdb_xml_parser); - parser->expat_parser = XML_ParserCreateNS (NULL, '!'); - if (parser->expat_parser == NULL) + if (systemId == NULL) { - xfree (parser); - nomem (0); + gdb_xml_parser *parser + = (gdb_xml_parser *) XML_GetUserData (expat_parser); + + text = fetch_xml_builtin (parser->dtd_name ()); + if (text == NULL) + internal_error (__FILE__, __LINE__, + _("could not locate built-in DTD %s"), + parser->dtd_name ()); + } + else + { + text = fetch_xml_builtin (systemId); + if (text == NULL) + return XML_STATUS_ERROR; } - parser->name = name; + entity_parser = XML_ExternalEntityParserCreate (expat_parser, + context, NULL); - parser->user_data = user_data; - XML_SetUserData (parser->expat_parser, parser); + /* Don't use our handlers for the contents of the DTD. Just let expat + process it. */ + XML_SetElementHandler (entity_parser, NULL, NULL); + XML_SetDoctypeDeclHandler (entity_parser, NULL, NULL); + XML_SetXmlDeclHandler (entity_parser, NULL); + XML_SetDefaultHandler (entity_parser, NULL); + XML_SetUserData (entity_parser, NULL); - /* Set the callbacks. */ - XML_SetElementHandler (parser->expat_parser, gdb_xml_start_element_wrapper, - gdb_xml_end_element_wrapper); - XML_SetCharacterDataHandler (parser->expat_parser, gdb_xml_body_text); + status = XML_Parse (entity_parser, text, strlen (text), 1); - /* Initialize the outer scope. */ - memset (&start_scope, 0, sizeof (start_scope)); - start_scope.elements = elements; - VEC_safe_push (scope_level_s, parser->scopes, &start_scope); + XML_ParserFree (entity_parser); + return status; +} - make_cleanup (gdb_xml_cleanup, parser); +void +gdb_xml_parser::use_dtd (const char *dtd_name) +{ + enum XML_Error err; + + m_dtd_name = dtd_name; - return parser; + XML_SetParamEntityParsing (m_expat_parser, + XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE); + XML_SetExternalEntityRefHandler (m_expat_parser, + gdb_xml_fetch_external_entity); + + /* Even if no DTD is provided, use the built-in DTD anyway. */ + err = XML_UseForeignDTD (m_expat_parser, XML_TRUE); + if (err != XML_ERROR_NONE) + internal_error (__FILE__, __LINE__, + _("XML_UseForeignDTD failed: %s"), + XML_ErrorString (err)); } /* Invoke PARSER on BUFFER. BUFFER is the data to parse, which @@ -437,42 +577,56 @@ gdb_xml_create_parser_and_cleanup (const char *name, errors will be caught, warned about, and reported as failure. */ int -gdb_xml_parse (struct gdb_xml_parser *parser, const char *buffer) +gdb_xml_parser::parse (const char *buffer) { enum XML_Status status; const char *error_string; - status = XML_Parse (parser->expat_parser, buffer, strlen (buffer), 1); + gdb_xml_debug (this, _("Starting:\n%s"), buffer); + + status = XML_Parse (m_expat_parser, buffer, strlen (buffer), 1); - if (status == XML_STATUS_OK && parser->error.reason == 0) + if (status == XML_STATUS_OK && m_error.reason == 0) return 0; - if (parser->error.reason == RETURN_ERROR - && parser->error.error == XML_PARSE_ERROR) + if (m_error.reason == RETURN_ERROR + && m_error.error == XML_PARSE_ERROR) { - gdb_assert (parser->error.message != NULL); - error_string = parser->error.message; + gdb_assert (m_error.message != NULL); + error_string = m_error.what (); } else if (status == XML_STATUS_ERROR) { - enum XML_Error err = XML_GetErrorCode (parser->expat_parser); + enum XML_Error err = XML_GetErrorCode (m_expat_parser); + error_string = XML_ErrorString (err); } else { - gdb_assert (parser->error.reason < 0); - throw_exception (parser->error); + gdb_assert (m_error.reason < 0); + throw_exception (std::move (m_error)); } - if (parser->last_line != 0) - warning (_("while parsing %s (at line %d): %s"), parser->name, - parser->last_line, error_string); + if (m_last_line != 0) + warning (_("while parsing %s (at line %d): %s"), m_name, + m_last_line, error_string); else - warning (_("while parsing %s: %s"), parser->name, error_string); + warning (_("while parsing %s: %s"), m_name, error_string); return -1; } +int +gdb_xml_parse_quick (const char *name, const char *dtd_name, + const struct gdb_xml_element *elements, + const char *document, void *user_data) +{ + gdb_xml_parser parser (name, elements, user_data); + if (dtd_name != NULL) + parser.use_dtd (dtd_name); + return parser.parse (document); +} + /* Parse a field VALSTR that we expect to contain an integer value. The integer is returned in *VALP. The string is parsed with an equivalent to strtoul. @@ -524,11 +678,19 @@ gdb_xml_parse_attr_ulongest (struct gdb_xml_parser *parser, gdb_xml_error (parser, _("Can't convert %s=\"%s\" to an integer"), attribute->name, value); - ret = xmalloc (sizeof (result)); + ret = XNEW (ULONGEST); memcpy (ret, &result, sizeof (result)); return ret; } +/* A handler_data for yes/no boolean values. */ + +const struct gdb_xml_enum gdb_xml_enums_boolean[] = { + { "yes", 1 }, + { "no", 0 }, + { NULL, 0 } +}; + /* Map NAME to VALUE. A struct gdb_xml_enum * should be saved as the value of handler_data when using gdb_xml_parse_attr_enum to parse a fixed list of possible strings. The list is terminated by an entry @@ -539,11 +701,13 @@ gdb_xml_parse_attr_enum (struct gdb_xml_parser *parser, const struct gdb_xml_attribute *attribute, const char *value) { - const struct gdb_xml_enum *enums = attribute->handler_data; + const struct gdb_xml_enum *enums + = (const struct gdb_xml_enum *) attribute->handler_data; void *ret; - for (enums = attribute->handler_data; enums->name != NULL; enums++) - if (strcmp (enums->name, value) == 0) + for (enums = (const struct gdb_xml_enum *) attribute->handler_data; + enums->name != NULL; enums++) + if (strcasecmp (enums->name, value) == 0) break; if (enums->name == NULL) @@ -554,8 +718,247 @@ gdb_xml_parse_attr_enum (struct gdb_xml_parser *parser, memcpy (ret, &enums->value, sizeof (enums->value)); return ret; } + + +/* XInclude processing. This is done as a separate step from actually + parsing the document, so that we can produce a single combined XML + document - e.g. to hand to a front end or to simplify comparing two + documents. We make extensive use of XML_DefaultCurrent, to pass + input text directly into the output without reformatting or + requoting it. + + We output the DOCTYPE declaration for the first document unchanged, + if present, and discard DOCTYPEs from included documents. Only the + one we pass through here is used when we feed the result back to + expat. The XInclude standard explicitly does not discuss + validation of the result; we choose to apply the same DTD applied + to the outermost document. + + We can not simply include the external DTD subset in the document + as an internal subset, because and are valid + only in external subsets. But if we do not pass the DTD into the + output at all, default values will not be filled in. + + We don't pass through any declaration because we generate + UTF-8, not whatever the input encoding was. */ + +struct xinclude_parsing_data +{ + xinclude_parsing_data (std::string &output_, + xml_fetch_another fetcher_, void *fetcher_baton_, + int include_depth_) + : output (output_), + skip_depth (0), + include_depth (include_depth_), + fetcher (fetcher_), + fetcher_baton (fetcher_baton_) + {} + + /* Where the output goes. */ + std::string &output; + + /* A count indicating whether we are in an element whose + children should not be copied to the output, and if so, + how deep we are nested. This is used for anything inside + an xi:include, and for the DTD. */ + int skip_depth; + + /* The number of elements currently being processed, + to detect loops. */ + int include_depth; + + /* A function to call to obtain additional features, and its + baton. */ + xml_fetch_another fetcher; + void *fetcher_baton; +}; + +static void +xinclude_start_include (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + std::vector &attributes) +{ + struct xinclude_parsing_data *data + = (struct xinclude_parsing_data *) user_data; + char *href = (char *) xml_find_attribute (attributes, "href")->value.get (); + + gdb_xml_debug (parser, _("Processing XInclude of \"%s\""), href); + if (data->include_depth > MAX_XINCLUDE_DEPTH) + gdb_xml_error (parser, _("Maximum XInclude depth (%d) exceeded"), + MAX_XINCLUDE_DEPTH); + + gdb::optional text + = data->fetcher (href, data->fetcher_baton); + if (!text) + gdb_xml_error (parser, _("Could not load XML document \"%s\""), href); + + if (!xml_process_xincludes (data->output, parser->name (), + text->data (), data->fetcher, + data->fetcher_baton, + data->include_depth + 1)) + gdb_xml_error (parser, _("Parsing \"%s\" failed"), href); + + data->skip_depth++; +} + +static void +xinclude_end_include (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, const char *body_text) +{ + struct xinclude_parsing_data *data + = (struct xinclude_parsing_data *) user_data; + + data->skip_depth--; +} + +static void XMLCALL +xml_xinclude_default (void *data_, const XML_Char *s, int len) +{ + struct gdb_xml_parser *parser = (struct gdb_xml_parser *) data_; + xinclude_parsing_data *data = (xinclude_parsing_data *) parser->user_data (); + + /* If we are inside of e.g. xi:include or the DTD, don't save this + string. */ + if (data->skip_depth) + return; + + /* Otherwise just add it to the end of the document we're building + up. */ + data->output.append (s, len); +} + +static void XMLCALL +xml_xinclude_start_doctype (void *data_, const XML_Char *doctypeName, + const XML_Char *sysid, const XML_Char *pubid, + int has_internal_subset) +{ + struct gdb_xml_parser *parser = (struct gdb_xml_parser *) data_; + xinclude_parsing_data *data = (xinclude_parsing_data *) parser->user_data (); + + /* Don't print out the doctype, or the contents of the DTD internal + subset, if any. */ + data->skip_depth++; +} + +static void XMLCALL +xml_xinclude_end_doctype (void *data_) +{ + struct gdb_xml_parser *parser = (struct gdb_xml_parser *) data_; + xinclude_parsing_data *data = (xinclude_parsing_data *) parser->user_data (); + + data->skip_depth--; +} + +static void XMLCALL +xml_xinclude_xml_decl (void *data_, const XML_Char *version, + const XML_Char *encoding, int standalone) +{ + /* Do nothing - this function prevents the default handler from + being called, thus suppressing the XML declaration from the + output. */ +} + +const struct gdb_xml_attribute xinclude_attributes[] = { + { "href", GDB_XML_AF_NONE, NULL, NULL }, + { NULL, GDB_XML_AF_NONE, NULL, NULL } +}; + +const struct gdb_xml_element xinclude_elements[] = { + { "http://www.w3.org/2001/XInclude!include", xinclude_attributes, NULL, + GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, + xinclude_start_include, xinclude_end_include }, + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } +}; + +/* The main entry point for processing. */ + +bool +xml_process_xincludes (std::string &result, + const char *name, const char *text, + xml_fetch_another fetcher, void *fetcher_baton, + int depth) +{ + xinclude_parsing_data data (result, fetcher, fetcher_baton, depth); + + gdb_xml_parser parser (name, xinclude_elements, &data); + parser.set_is_xinclude (true); + + XML_SetCharacterDataHandler (parser.expat_parser (), NULL); + XML_SetDefaultHandler (parser.expat_parser (), xml_xinclude_default); + + /* Always discard the XML version declarations; the only important + thing this provides is encoding, and our result will have been + converted to UTF-8. */ + XML_SetXmlDeclHandler (parser.expat_parser (), xml_xinclude_xml_decl); + + if (depth > 0) + /* Discard the doctype for included documents. */ + XML_SetDoctypeDeclHandler (parser.expat_parser (), + xml_xinclude_start_doctype, + xml_xinclude_end_doctype); + + parser.use_dtd ("xinclude.dtd"); + + if (parser.parse (text) == 0) + { + if (depth == 0) + gdb_xml_debug (&parser, _("XInclude processing succeeded.")); + return true; + } + + return false; +} #endif /* HAVE_LIBEXPAT */ + + +/* Return an XML document which was compiled into GDB, from + the given FILENAME, or NULL if the file was not compiled in. */ + +const char * +fetch_xml_builtin (const char *filename) +{ + const char *const (*p)[2]; + + for (p = xml_builtin; (*p)[0]; p++) + if (strcmp ((*p)[0], filename) == 0) + return (*p)[1]; + + return NULL; +} + +/* A to_xfer_partial helper function which reads XML files which were + compiled into GDB. The target may call this function from its own + to_xfer_partial handler, after converting object and annex to the + appropriate filename. */ + +LONGEST +xml_builtin_xfer_partial (const char *filename, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + const char *buf; + LONGEST len_avail; + + gdb_assert (readbuf != NULL && writebuf == NULL); + gdb_assert (filename != NULL); + + buf = fetch_xml_builtin (filename); + if (buf == NULL) + return -1; + + len_avail = strlen (buf); + if (offset >= len_avail) + return 0; + + if (len > len_avail - offset) + len = len_avail - offset; + memcpy (readbuf, buf + offset, len); + return len; +} + static void show_debug_xml (struct ui_file *file, int from_tty, @@ -564,7 +967,46 @@ show_debug_xml (struct ui_file *file, int from_tty, fprintf_filtered (file, _("XML debugging is %s.\n"), value); } -void _initialize_xml_support (void); +gdb::optional +xml_fetch_content_from_file (const char *filename, void *baton) +{ + const char *dirname = (const char *) baton; + gdb_file_up file; + + if (dirname && *dirname) + { + char *fullname = concat (dirname, "/", filename, (char *) NULL); + + file = gdb_fopen_cloexec (fullname, FOPEN_RT); + xfree (fullname); + } + else + file = gdb_fopen_cloexec (filename, FOPEN_RT); + + if (file == NULL) + return {}; + + /* Read in the whole file. */ + + size_t len; + + if (fseek (file.get (), 0, SEEK_END) == -1) + perror_with_name (_("seek to end of file")); + len = ftell (file.get ()); + rewind (file.get ()); + + gdb::char_vector text (len + 1); + + if (fread (text.data (), 1, len, file.get ()) != len + || ferror (file.get ())) + { + warning (_("Read error from \"%s\""), filename); + return {}; + } + + text.back () = '\0'; + return text; +} void _initialize_xml_support (void)