This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: XML XInclude support


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;
 }
+
+
+/* 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;
+}
+
+
+/* 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 */
 
 
@@ -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);
+}
+
+
 /* 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"/>


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]