Re: XML XInclude support

Daniel Jacobowitz
2007-01-31T18:33:55Z

On Mon, Jan 29, 2007 at 04:32:30PM -0500, Daniel Jacobowitz wrote:
> 2007-01-29 Daniel Jacobowitz <dan@codesourcery.com>

Whoops. Noticed today that I'd left one of the new test supporting
files out of this patch - here's a complete version.

--
Daniel Jacobowitz
CodeSourcery

2007-01-29 Daniel Jacobowitz <dan@codesourcery.com>

* Makefile.in (XMLFILES): New.
(COMMON_OBS): Add xml-builtin.o.
(xml-builtin.c, stamp-xml): New rules.
(xml-tdesc.o): Update.
* features/feature_to_c.sh: New file.
* xml-support.c (MAX_XINCLUDE_DEPTH): Define.
(struct gdb_xml_parser): Add dtd_name and is_xinclude.
(gdb_xml_start_element): Initialize scope after possibly reallocating
scopes. Move cleanup later. Handle the XInclude description
specially.
(gdb_xml_end_element): Only parse the body if there is a current element.
Call XML_DefaultCurrent if there is no element.
(gdb_xml_fetch_external_entity, gdb_xml_use_dtd): New.
(struct xinclude_parsing_data, xinclude_start_include)
(xinclude_end_include, xml_xinclude_default)
(xml_xinclude_start_doctype, xml_xinclude_end_doctype)
(xml_xinclude_xml_decl, xml_xinclude_cleanup, xinclude_attributes)
(xinclude_elements, xml_process_xincludes, fetch_xml_builtin): New.
* xml-support.h (xml_fetch_another, xml_process_xincludes)
(fetch_xml_builtin, xml_builtin, gdb_xml_use_dtd): New declarations.
* xml-tdesc.c (tdesc_parse_xml): Add fetcher_baton argument. Expand
XInclude directives. Use the compiled in DTD.
(fetch_xml_from_file): Add baton argument. Treat it as a containing
directory name. Do not warn here.
(file_read_description_xml): Update call. Warn here instead. Pass
a dirname as baton.
(fetch_available_features_from_target): New.
(target_read_description_xml): Use it.
* features/gdb-target.dtd: Add copyright notice. Use xinclude.dtd
to handle XInclude.
* features/xinclude.dtd: New file.

* gdb.xml/bad-include.xml, gdb.xml/inc-2.xml, gdb.xml/inc-body.xml,
gdb.xml/includes.xml, gdb.xml/loop.xml,
gdb.xml/tdesc-xinclude.exp: New files.

* gdb.texinfo (Target Description Format): Add section on XInclude.

---
gdb/Makefile.in | 20 +
gdb/doc/gdb.texinfo | 21 +
gdb/features/feature_to_c.sh | 77 ++++++
gdb/features/gdb-target.dtd | 11
gdb/features/xinclude.dtd | 13 +
gdb/testsuite/gdb.xml/bad-include.xml | 1
gdb/testsuite/gdb.xml/inc-2.xml | 1
gdb/testsuite/gdb.xml/inc-body.xml | 1
gdb/testsuite/gdb.xml/includes.xml | 3
gdb/testsuite/gdb.xml/loop.xml | 1
gdb/testsuite/gdb.xml/tdesc-xinclude.exp | 54 ++++
gdb/xml-support.c | 372 +++++++++++++++++++++++++++++--
gdb/xml-support.h | 34 ++
gdb/xml-tdesc.c | 98 ++++++--
14 files changed, 662 insertions(+), 45 deletions(-)

Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2007-01-31 13:31:22.000000000 -0500
+++ src/gdb/Makefile.in 2007-01-31 13:31:55.000000000 -0500
@@ -400,6 +400,9 @@ LINTFLAGS= $(GDB_CFLAGS) $(OPCODES_CFLAG
RUNTEST = runtest
RUNTESTFLAGS=

+# XML files to build in to GDB.
+XMLFILES = $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinclude.dtd
+
# This is ser-unix.o for any system which supports a v7/BSD/SYSV/POSIX
# interface to the serial port. Hopefully if get ported to OS/2, VMS,
# etc., then there will be (as part of the C library or perhaps as
@@ -972,7 +975,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
tramp-frame.o \
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
- target-descriptions.o target-memory.o xml-tdesc.o
+ target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o

TSOBS = inflow.o

@@ -1681,6 +1684,19 @@ po/$(PACKAGE).pot: force
.PRECIOUS: objc-exp.c
.PRECIOUS: p-exp.c

+# XML rules
+
+xml-builtin.c: stamp-xml; @true
+stamp-xml: $(srcdir)/features/feature_to_c.sh Makefile $(XMLFILES)
+ rm -f xml-builtin.tmp
+ AWK="$(AWK)" \
+ $(SHELL) $(srcdir)/features/feature_to_c.sh \
+ xml-builtin.tmp $(XMLFILES)
+ $(SHELL) $(srcdir)/../move-if-change xml-builtin.tmp xml-builtin.c
+ echo stamp > stamp-xml
+
+.PRECIOUS: xml-builtin.c
+
#
# gdb/ dependencies
#
@@ -2876,7 +2892,7 @@ xcoffread.o: xcoffread.c $(defs_h) $(bfd
xcoffsolib.o: xcoffsolib.c $(defs_h) $(bfd_h) $(xcoffsolib_h) $(inferior_h) \
$(gdbcmd_h) $(symfile_h) $(frame_h) $(gdb_regex_h)
xml-tdesc.o: xml-tdesc.c $(defs_h) $(target_h) $(target_descriptions_h) \
- $(xml_tdesc_h) $(xml_support_h) $(gdb_assert_h)
+ $(xml_tdesc_h) $(xml_support_h) $(filenames_h) $(gdb_assert_h)
xml-support.o: xml-support.c $(defs_h) $(xml_support_h) $(exceptions_h) \
$(gdbcmd_h) $(gdb_string_h) $(gdb_expat_h) $(safe_ctype_h)
xstormy16-tdep.o: xstormy16-tdep.c $(defs_h) $(frame_h) $(frame_base_h) \
Index: src/gdb/features/feature_to_c.sh
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/features/feature_to_c.sh 2007-01-31 13:31:55.000000000 -0500
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# Convert text files to compilable C arrays.
+#
+# Copyright (C) 2007 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
+# (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 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.
+
+output=$1
+shift
+
+if test -z "$output" || test -z "$1"; then
+ echo "Usage: $0 OUTPUTFILE INPUTFILE..."
+ exit 1
+fi
+
+if test -e "$output"; then
+ echo "Output file \"$output\" already exists; refusing to overwrite."
+ exit 1
+fi
+
+for input; do
+ arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'`
+
+ ${AWK:-awk} 'BEGIN { n = 0
+ print "static const char '$arrayname'[] = {"
+ for (i = 0; i < 255; i++)
+ _ord_[sprintf("%c", i)] = i
+ } {
+ split($0, line, "");
+ printf " "
+ for (i = 1; i <= length($0); i++) {
+ c = line[i]
+ if (c == "'\''") {
+ printf "'\''\\'\'''\'', "
+ } else if (c == "\\") {
+ printf "'\''\\\\'\'', "
+ } else if (_ord_[c] >= 32 && _ord_[c] < 127) {
+ printf "'\''" c "'\'', "
+ } else {
+ printf "'\''\\%03o'\'', ", _ord_[c]
+ }
+ if (i % 10 == 0)
+ printf "\n "
+ }
+ printf "'\''\\n'\'', \n"
+ } END {
+ print " 0 };"
+ }' < $input >> $output
+done
+
+echo >> $output
+echo "const char *const xml_builtin[][2] = {" >> $output
+
+for input; do
+ basename=`echo $input | sed 's,.*/,,'`
+ arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'`
+ echo " { \"$basename\", $arrayname }," >> $output
+done
+
+echo " { 0, 0 }" >> $output
+echo "};" >> $output
Index: src/gdb/xml-support.c
===================================================================
--- src.orig/gdb/xml-support.c 2007-01-31 13:31:22.000000000 -0500
+++ src/gdb/xml-support.c 2007-01-31 13:31:55.000000000 -0500
@@ -36,6 +36,10 @@ static int debug_xml;
#include "gdb_string.h"
#include "safe-ctype.h"

+/* The maximum depth of <xi:include> nesting. No need to be miserly,
+ we just want to avoid running out of stack on loops. */
+#define MAX_XINCLUDE_DEPTH 30
+
/* A parsing level -- used to keep track of the current element
nesting. */
struct scope_level
@@ -68,6 +72,10 @@ struct gdb_xml_parser

struct gdb_exception error; /* A thrown error, if any. */
int last_line; /* The line of the thrown error, or 0. */
+
+ const char *dtd_name; /* The name of the expected / default DTD,
+ if specified. */
+ int is_xinclude; /* Are we the special <xi:include> parser? */
};

/* Process some body text. We accumulate the text for later use; it's
@@ -152,7 +160,7 @@ gdb_xml_start_element (void *data, const
const XML_Char **attrs)
{
struct gdb_xml_parser *parser = data;
- struct scope_level *scope = VEC_last (scope_level_s, parser->scopes);
+ struct scope_level *scope;
struct scope_level new_scope;
const struct gdb_xml_element *element;
const struct gdb_xml_attribute *attribute;
@@ -160,13 +168,13 @@ gdb_xml_start_element (void *data, const
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. */
+ VEC_reserve (scope_level_s, parser->scopes, 1);
+ scope = VEC_last (scope_level_s, parser->scopes);
memset (&new_scope, 0, sizeof (new_scope));
- VEC_safe_push (scope_level_s, parser->scopes, &new_scope);
+ VEC_quick_push (scope_level_s, parser->scopes, &new_scope);

gdb_xml_debug (parser, _("Entering element <%s>"), name);

@@ -181,8 +189,21 @@ gdb_xml_start_element (void *data, const

if (element == NULL || element->name == NULL)
{
+ /* If we're working on XInclude, <xi:include> 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 (parser->is_xinclude)
+ {
+ struct scope_level *unknown_scope;
+
+ XML_DefaultCurrent (parser->expat_parser);
+
+ unknown_scope = VEC_last (scope_level_s, parser->scopes);
+ unknown_scope->elements = scope->elements;
+ return;
+ }
+
gdb_xml_debug (parser, _("Element <%s> unknown"), name);
- do_cleanups (back_to);
return;
}

@@ -191,6 +212,8 @@ gdb_xml_start_element (void *data, const

scope->seen |= seen;

+ back_to = make_cleanup (gdb_xml_values_cleanup, &attributes);
+
for (attribute = element->attributes;
attribute != NULL && attribute->name != NULL;
attribute++)
@@ -304,7 +327,6 @@ gdb_xml_end_element (void *data, const X
struct scope_level *scope = VEC_last (scope_level_s, parser->scopes);
const struct gdb_xml_element *element;
unsigned int seen;
- char *body;

gdb_xml_debug (parser, _("Leaving element <%s>"), name);

@@ -317,26 +339,32 @@ gdb_xml_end_element (void *data, const X
element->name);

/* Call the element processor. */
- if (scope->body == NULL)
- body = "";
- else
+ if (scope->element != NULL && scope->element->end_handler)
{
- int length;
+ char *body;

- length = obstack_object_size (scope->body);
- obstack_1grow (scope->body, '\0');
- body = obstack_finish (scope->body);
-
- /* Strip leading and trailing whitespace. */
- while (length > 0 && ISSPACE (body[length-1]))
- body[--length] = '\0';
- while (*body && ISSPACE (*body))
- body++;
- }
+ if (scope->body == NULL)
+ body = "";
+ else
+ {
+ int length;

- if (scope->element != NULL && scope->element->end_handler)
- scope->element->end_handler (parser, scope->element, parser->user_data,
- body);
+ length = obstack_object_size (scope->body);
+ obstack_1grow (scope->body, '\0');
+ body = obstack_finish (scope->body);
+
+ /* Strip leading and trailing whitespace. */
+ while (length > 0 && ISSPACE (body[length-1]))
+ body[--length] = '\0';
+ while (*body && ISSPACE (*body))
+ body++;
+ }
+
+ scope->element->end_handler (parser, scope->element, parser->user_data,
+ body);
+ }
+ else if (scope->element == NULL)
+ XML_DefaultCurrent (parser->expat_parser);

/* Pop the scope level. */
if (scope->body)
@@ -435,6 +463,74 @@ gdb_xml_create_parser_and_cleanup (const
return parser;
}

+/* External entity handler. The only external entities we support
+ are those compiled into GDB (we do not fetch entities from the
+ target). */
+
+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 = XML_GetUserData (expat_parser);
+ XML_Parser entity_parser;
+ const char *text;
+ enum XML_Status status;
+
+ if (systemId == NULL)
+ {
+ 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;
+ }
+
+ entity_parser = XML_ExternalEntityParserCreate (expat_parser, context, NULL);
+
+ /* 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);
+
+ status = XML_Parse (entity_parser, text, strlen (text), 1);
+
+ XML_ParserFree (entity_parser);
+ return status;
+}
+
+/* Associate DTD_NAME, which must be the name of a compiled-in DTD,
+ with PARSER. */
+
+void
+gdb_xml_use_dtd (struct gdb_xml_parser *parser, const char *dtd_name)
+{
+ enum XML_Error err;
+
+ parser->dtd_name = dtd_name;
+
+ XML_SetParamEntityParsing (parser->expat_parser,
+ XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE);
+ XML_SetExternalEntityRefHandler (parser->expat_parser,
+ gdb_xml_fetch_external_entity);
+
+ /* Even if no DTD is provided, use the built-in DTD anyway. */
+ err = XML_UseForeignDTD (parser->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
should be NUL-terminated.

@@ -560,6 +656,236 @@ gdb_xml_parse_attr_enum (struct gdb_xml_
memcpy (ret, &enums->value, sizeof (enums->value));
return ret;
}
+\f
+
+/* 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 <!IGNORE> and <!INCLUDE> 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 <?xml> declaration because we generate
+ UTF-8, not whatever the input encoding was. */
+
+struct xinclude_parsing_data
+{
+ /* The obstack to build the output in. */
+ struct obstack obstack;
+
+ /* 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 <xi:include> 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, VEC(gdb_xml_value_s) *attributes)
+{
+ struct xinclude_parsing_data *data = user_data;
+ char *href = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ struct cleanup *back_to;
+ char *text, *output;
+ int ret;
+
+ 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);
+
+ text = data->fetcher (href, data->fetcher_baton);
+ if (text == NULL)
+ gdb_xml_error (parser, _("Could not load XML document \"%s\""), href);
+ back_to = make_cleanup (xfree, text);
+
+ output = xml_process_xincludes (parser->name, text, data->fetcher,
+ data->fetcher_baton,
+ data->include_depth + 1);
+ if (output == NULL)
+ gdb_xml_error (parser, _("Parsing \"%s\" failed"), href);
+
+ obstack_grow (&data->obstack, output, strlen (output));
+ xfree (output);
+
+ do_cleanups (back_to);
+
+ 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 = user_data;
+
+ data->skip_depth--;
+}
+
+static void XMLCALL
+xml_xinclude_default (void *data_, const XML_Char *s, int len)
+{
+ struct gdb_xml_parser *parser = data_;
+ struct xinclude_parsing_data *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. */
+ obstack_grow (&data->obstack, 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 = data_;
+ struct xinclude_parsing_data *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 = data_;
+ struct xinclude_parsing_data *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. */
+}
+
+static void
+xml_xinclude_cleanup (void *data_)
+{
+ struct xinclude_parsing_data *data = data_;
+
+ obstack_free (&data->obstack, NULL);
+ xfree (data);
+}
+
+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 <xi:include> processing. */
+
+char *
+xml_process_xincludes (const char *name, const char *text,
+ xml_fetch_another fetcher, void *fetcher_baton,
+ int depth)
+{
+ enum XML_Error err;
+ struct gdb_xml_parser *parser;
+ struct xinclude_parsing_data *data;
+ struct cleanup *back_to;
+ char *result = NULL;
+
+ data = XZALLOC (struct xinclude_parsing_data);
+ obstack_init (&data->obstack);
+ back_to = make_cleanup (xml_xinclude_cleanup, data);
+
+ parser = gdb_xml_create_parser_and_cleanup (name, xinclude_elements, data);
+ parser->is_xinclude = 1;
+
+ data->include_depth = depth;
+ data->fetcher = fetcher;
+ data->fetcher_baton = fetcher_baton;
+
+ 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);
+
+ gdb_xml_use_dtd (parser, "xinclude.dtd");
+
+ if (gdb_xml_parse (parser, text) == 0)
+ {
+ obstack_1grow (&data->obstack, '\0');
+ result = xstrdup (obstack_finish (&data->obstack));
+
+ if (depth == 0)
+ gdb_xml_debug (parser, _("XInclude processing succeeded:\n%s"),
+ result);
+ }
+ else
+ result = NULL;
+
+ do_cleanups (back_to);
+ return result;
+}
+\f
+
+/* 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 *(*p)[2];
+
+ for (p = xml_builtin; (*p)[0]; p++)
+ if (strcmp ((*p)[0], filename) == 0)
+ return (*p)[1];
+
+ return NULL;
+}

#endif /* HAVE_LIBEXPAT */

Index: src/gdb/xml-support.h
===================================================================
--- src.orig/gdb/xml-support.h 2007-01-31 13:31:22.000000000 -0500
+++ src/gdb/xml-support.h 2007-01-31 13:31:55.000000000 -0500
@@ -30,6 +30,35 @@ struct gdb_xml_parser;
struct gdb_xml_element;
struct gdb_xml_attribute;

+/* Support for XInclude. */
+
+/* Callback to fetch a new XML file, based on the provided HREF. */
+
+typedef char *(*xml_fetch_another) (const char *href, void *baton);
+
+/* Return a new string which is the expansion of TEXT after processing
+ <xi:include> tags. FETCHER will be called (with FETCHER_BATON) to
+ retrieve any new files. DEPTH should be zero on the initial call.
+
+ On failure, this function uses NAME in a warning and returns NULL.
+ It may throw an exception, but does not for XML parsing
+ problems. */
+
+char *xml_process_xincludes (const char *name, const char *text,
+ xml_fetch_another fetcher, void *fetcher_baton,
+ int depth);
+
+/* 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);
+
+/* The text of compiled-in XML documents, from xml-builtin.c
+ (generated). */
+extern const char *xml_builtin[][2];
+
+/* Simplified XML parser infrastructure. */
+
/* A name and value pair, used to record parsed attributes. */

struct gdb_xml_value
@@ -140,6 +169,11 @@ struct gdb_xml_parser *gdb_xml_create_pa
(const char *name, const struct gdb_xml_element *elements,
void *user_data);

+/* Associate DTD_NAME, which must be the name of a compiled-in DTD,
+ with PARSER. */
+
+void gdb_xml_use_dtd (struct gdb_xml_parser *parser, const char *dtd_name);
+
/* Invoke PARSER on BUFFER. BUFFER is the data to parse, which
should be NUL-terminated.

Index: src/gdb/xml-tdesc.c
===================================================================
--- src.orig/gdb/xml-tdesc.c 2007-01-31 13:31:22.000000000 -0500
+++ src/gdb/xml-tdesc.c 2007-01-31 13:31:55.000000000 -0500
@@ -28,6 +28,8 @@
#include "xml-support.h"
#include "xml-tdesc.h"

+#include "filenames.h"
+
#include "gdb_assert.h"

#if !defined(HAVE_LIBEXPAT)
@@ -36,7 +38,8 @@
an XML parser. */

static struct target_desc *
-tdesc_parse_xml (const char *document)
+tdesc_parse_xml (const char *document, xml_fetch_another fetcher,
+ void *fetcher_baton)
{
static int have_warned;

@@ -94,22 +97,33 @@ const struct gdb_xml_element tdesc_eleme
/* Parse DOCUMENT into a target description and return it. */

static struct target_desc *
-tdesc_parse_xml (const char *document)
+tdesc_parse_xml (const char *document, xml_fetch_another fetcher,
+ void *fetcher_baton)
{
struct cleanup *back_to, *result_cleanup;
struct gdb_xml_parser *parser;
struct tdesc_parsing_data data;
+ char *expanded_text;

- memset (&data, 0, sizeof (struct tdesc_parsing_data));
+ /* Expand all XInclude directives. */
+ expanded_text = xml_process_xincludes (_("target description"),
+ document, fetcher, fetcher_baton, 0);
+ if (expanded_text == NULL)
+ {
+ warning (_("Could not load XML target description; ignoring"));
+ return NULL;
+ }
+ back_to = make_cleanup (xfree, expanded_text);

- back_to = make_cleanup (null_cleanup, NULL);
parser = gdb_xml_create_parser_and_cleanup (_("target description"),
tdesc_elements, &data);
+ gdb_xml_use_dtd (parser, "gdb-target.dtd");

+ memset (&data, 0, sizeof (struct tdesc_parsing_data));
data.tdesc = allocate_target_description ();
result_cleanup = make_cleanup_free_target_description (data.tdesc);

- if (gdb_xml_parse (parser, document) == 0)
+ if (gdb_xml_parse (parser, expanded_text) == 0)
{
/* Parsed successfully. */
discard_cleanups (result_cleanup);
@@ -123,7 +137,6 @@ tdesc_parse_xml (const char *document)
return NULL;
}
}
-
#endif /* HAVE_LIBEXPAT */
\f

@@ -139,19 +152,28 @@ do_cleanup_fclose (void *file)
the text. If something goes wrong, return NULL and warn. */

static char *
-fetch_xml_from_file (const char *filename)
+fetch_xml_from_file (const char *filename, void *baton)
{
+ const char *dirname = baton;
FILE *file;
struct cleanup *back_to;
char *text;
size_t len, offset;

- file = fopen (filename, FOPEN_RT);
- if (file == NULL)
+ if (dirname && *dirname)
{
- warning (_("Could not open \"%s\""), filename);
- return NULL;
+ char *fullname = concat (dirname, "/", filename, NULL);
+ if (fullname == NULL)
+ nomem (0);
+ file = fopen (fullname, FOPEN_RT);
+ xfree (fullname);
}
+ else
+ file = fopen (filename, FOPEN_RT);
+
+ if (file == NULL)
+ return NULL;
+
back_to = make_cleanup (do_cleanup_fclose, file);

/* Read in the whole file, one chunk at a time. */
@@ -198,18 +220,59 @@ file_read_description_xml (const char *f
struct target_desc *tdesc;
char *tdesc_str;
struct cleanup *back_to;
+ const char *base;
+ char *dirname;

- tdesc_str = fetch_xml_from_file (filename);
+ tdesc_str = fetch_xml_from_file (filename, NULL);
if (tdesc_str == NULL)
- return NULL;
+ {
+ warning (_("Could not open \"%s\""), filename);
+ return NULL;
+ }

back_to = make_cleanup (xfree, tdesc_str);
- tdesc = tdesc_parse_xml (tdesc_str);
+
+ /* Simple, portable version of dirname that does not modify its
+ argument. */
+ base = lbasename (filename);
+ while (base > filename && IS_DIR_SEPARATOR (base[-1]))
+ --base;
+ if (base > filename)
+ {
+ dirname = xmalloc (base - filename + 1);
+ memcpy (dirname, filename, base - filename);
+ dirname[base - filename] = '\0';
+ make_cleanup (xfree, dirname);
+ }
+ else
+ dirname = NULL;
+
+ tdesc = tdesc_parse_xml (tdesc_str, fetch_xml_from_file, dirname);
do_cleanups (back_to);

return tdesc;
}

+/* Read a string representation of available features from the target,
+ using TARGET_OBJECT_AVAILABLE_FEATURES. The returned string is
+ malloc allocated and NUL-terminated. NAME should be a non-NULL
+ string identifying the XML document we want; the top level document
+ is "target.xml". Other calls may be performed for the DTD or
+ for <xi:include>. */
+
+static char *
+fetch_available_features_from_target (const char *name, void *baton_)
+{
+ struct target_ops *ops = baton_;
+
+ /* Read this object as a string. This ensures that a NUL
+ terminator is added. */
+ return target_read_stralloc (ops,
+ TARGET_OBJECT_AVAILABLE_FEATURES,
+ name);
+}
+\f
+
/* Read an XML target description using OPS. Parse it, and return the
parsed description. */

@@ -220,13 +283,14 @@ target_read_description_xml (struct targ
char *tdesc_str;
struct cleanup *back_to;

- tdesc_str = target_read_stralloc (ops, TARGET_OBJECT_AVAILABLE_FEATURES,
- "target.xml");
+ tdesc_str = fetch_available_features_from_target ("target.xml", ops);
if (tdesc_str == NULL)
return NULL;

back_to = make_cleanup (xfree, tdesc_str);
- tdesc = tdesc_parse_xml (tdesc_str);
+ tdesc = tdesc_parse_xml (tdesc_str,
+ fetch_available_features_from_target,
+ ops);
do_cleanups (back_to);

return tdesc;
Index: src/gdb/features/gdb-target.dtd
===================================================================
--- src.orig/gdb/features/gdb-target.dtd 2007-01-31 13:31:22.000000000 -0500
+++ src/gdb/features/gdb-target.dtd 2007-01-31 13:31:55.000000000 -0500
@@ -1,9 +1,14 @@
+<!-- Copyright (C) 2007 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
<!-- The root element of a GDB target description is <target>. -->

<!ELEMENT target (architecture?)>
-<!ATTLIST target
- xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude">

<!ELEMENT architecture (#PCDATA)>

-
+<!ENTITY % xinclude SYSTEM "xinclude.dtd">
+%xinclude;
Index: src/gdb/features/xinclude.dtd
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/features/xinclude.dtd 2007-01-31 13:31:55.000000000 -0500
@@ -0,0 +1,13 @@
+<!-- Copyright (C) 2007 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- GDB supports a subset of XInclude. Only whole documents can
+ be included, and only as XML. -->
+
+<!ELEMENT xi:include (EMPTY)>
+<!ATTLIST xi:include
+ xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude"
+ href CDATA #REQUIRED>
Index: src/gdb/testsuite/gdb.xml/bad-include.xml
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.xml/bad-include.xml 2007-01-31 13:31:55.000000000 -0500
@@ -0,0 +1 @@
+<xi:include href="nonexistant.xml"/>
Index: src/gdb/testsuite/gdb.xml/inc-2.xml
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.xml/inc-2.xml 2007-01-31 13:31:55.000000000 -0500
@@ -0,0 +1 @@
+<xi:include href="inc-body.xml"/>
Index: src/gdb/testsuite/gdb.xml/inc-body.xml
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.xml/inc-body.xml 2007-01-31 13:31:55.000000000 -0500
@@ -0,0 +1 @@
+<architecture>invalid-body</architecture>
Index: src/gdb/testsuite/gdb.xml/includes.xml
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.xml/includes.xml 2007-01-31 13:31:55.000000000 -0500
@@ -0,0 +1,3 @@
+<target>
+ <xi:include href="inc-2.xml"/>
+</target>
Index: src/gdb/testsuite/gdb.xml/tdesc-xinclude.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.xml/tdesc-xinclude.exp 2007-01-31 13:31:55.000000000 -0500
@@ -0,0 +1,54 @@
+# Copyright 2007 Free Software Foundation, Inc.
+
+# 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
+# (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 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.
+
+if {[gdb_skip_xml_test]} {
+ unsupported "tdesc-errors.exp"
+ return -1
+}
+
+gdb_start
+
+proc set_arch { srcfile errmsg } {
+ global gdb_prompt
+ global srcdir
+ global subdir
+
+ # Anchor the test output, so that error messages are detected.
+ set cmd "set tdesc filename $srcdir/$subdir/$srcfile"
+ set msg $cmd
+ set cmd_regex [string_to_regexp $cmd]
+ gdb_test_multiple $cmd $msg {
+ -re "^$cmd_regex\r\n$errmsg$gdb_prompt $" {
+ pass $msg
+ }
+ }
+}
+
+set common_warn "\r\nwarning: Could not load XML target description; ignoring\r\n"
+
+# This file is valid, but specifies an unknown architecture.
+# We should get a warning, if we process the includes correctly.
+set_arch "includes.xml" \
+ "warning:.*specified unknown architecture.* \"invalid-body\"$common_warn"
+
+# This file contains a missing include. We should warn the user about
+# it.
+set_arch "bad-include.xml" \
+ "warning:.*Could not load XML document \"nonexistant.xml\"$common_warn"
+
+# Make sure we detect infinite loops, eventually.
+set_arch "loop.xml" \
+ "warning:.*Maximum XInclude depth.*$common_warn"
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo 2007-01-31 13:31:22.000000000 -0500
+++ src/gdb/doc/gdb.texinfo 2007-01-31 13:31:55.000000000 -0500
@@ -25781,6 +25781,27 @@ The content of the @samp{<architecture>}
name, from the same selection accepted by @code{set architecture}
(@pxref{Targets, ,Specifying a Debugging Target}).

+@subsection Inclusion
+
+It can sometimes be valuable to split a target description up into
+several different annexes, either for organizational purposes, or to
+share files between different possible target descriptions. You can
+divide a description into multiple files by replacing any element of
+the target description with an inclusion directive of the form:
+
+@example
+<xi:include href="@var{name}"/>
+@end example
+
+@noindent
+When @value{GDBN} encounters an element of this form, it will retrieve
+the XML document named @var{name}, and replace the inclusion directive
+with the contents of that annex. If the current description was read
+using @samp{qXfer}, then the new document will be also, using
+@var{name} as the annex. If the current description was read from a
+file, @value{GDBN} will look for a file named @var{name} in the same
+directory as the original file.
+

@include gpl.texi

Index: src/gdb/testsuite/gdb.xml/loop.xml
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.xml/loop.xml 2007-01-11 16:28:03.000000000 -0500
@@ -0,0 +1 @@
+<xi:include href="loop.xml"/>