From 108546a0ea067d8e5da4d223fb03c6659b153bd0 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Wed, 7 Feb 2007 22:48:06 +0000 Subject: [PATCH] * 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/tdesc-xinclude.exp: New files. * gdb.texinfo (Target Description Format): Add section on XInclude. --- gdb/ChangeLog | 34 +++ gdb/Makefile.in | 20 +- gdb/doc/ChangeLog | 4 + gdb/doc/gdb.texinfo | 27 +++ gdb/features/feature_to_c.sh | 77 +++++++ gdb/features/gdb-target.dtd | 11 +- gdb/features/xinclude.dtd | 13 ++ gdb/testsuite/ChangeLog | 5 + 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 | 370 +++++++++++++++++++++++++++++-- gdb/xml-support.h | 34 +++ gdb/xml-tdesc.c | 106 +++++++-- 17 files changed, 718 insertions(+), 44 deletions(-) create mode 100644 gdb/features/feature_to_c.sh create mode 100644 gdb/features/xinclude.dtd create mode 100644 gdb/testsuite/gdb.xml/bad-include.xml create mode 100644 gdb/testsuite/gdb.xml/inc-2.xml create mode 100644 gdb/testsuite/gdb.xml/inc-body.xml create mode 100644 gdb/testsuite/gdb.xml/includes.xml create mode 100644 gdb/testsuite/gdb.xml/loop.xml create mode 100644 gdb/testsuite/gdb.xml/tdesc-xinclude.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 781377e..be2bcfe 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,37 @@ +2007-02-07 Daniel Jacobowitz + + * 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. + 2007-02-05 Daniel Jacobowitz * linux-thread-db.c (check_for_thread_db): Return early if we have diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 9567fac..f9b9827 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -400,6 +400,9 @@ LINTFLAGS= $(GDB_CFLAGS) $(OPCODES_CFLAGS) $(READLINE_CFLAGS) \ 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) $(YYOBJ) \ 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_h) $(gdb_string_h) $(gdb_stat_h) \ 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) \ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index b51fe2d..b37890e 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,7 @@ +2007-02-07 Daniel Jacobowitz + + * gdb.texinfo (Target Description Format): Add section on XInclude. + 2006-02-03 Nick Roberts * gdb.texinfo (GDB/MI Miscellaneous Commands): Describe the new diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 01eb8fa..251ed9e 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -25825,6 +25825,33 @@ The content of the @samp{} element is an architecture name, from the same selection accepted by @code{set architecture} (@pxref{Targets, ,Specifying a Debugging Target}). +@subsection Inclusion +@cindex target descriptions, inclusion +@cindex XInclude +@ifnotinfo +@cindex +@end ifnotinfo + +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 + +@end example + +@noindent +When @value{GDBN} encounters an element of this form, it will retrieve +the named XML @var{document}, and replace the inclusion directive with +the contents of that document. If the current description was read +using @samp{qXfer}, then so will be the included document; +@var{document} will be interpreted as the name of an annex. If the +current description was read from a file, @value{GDBN} will look for +@var{document} as a file in the same directory where it found the +original description. + @include gpl.texi diff --git a/gdb/features/feature_to_c.sh b/gdb/features/feature_to_c.sh new file mode 100644 index 0000000..b737d0e --- /dev/null +++ b/gdb/features/feature_to_c.sh @@ -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 diff --git a/gdb/features/gdb-target.dtd b/gdb/features/gdb-target.dtd index 83a5a3d..8bcb049 100644 --- a/gdb/features/gdb-target.dtd +++ b/gdb/features/gdb-target.dtd @@ -1,9 +1,14 @@ + + - - + +%xinclude; diff --git a/gdb/features/xinclude.dtd b/gdb/features/xinclude.dtd new file mode 100644 index 0000000..abee8d4 --- /dev/null +++ b/gdb/features/xinclude.dtd @@ -0,0 +1,13 @@ + + + + + + diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 5ca882b..d68bf71 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2007-02-07 Daniel Jacobowitz + + * gdb.xml/bad-include.xml, gdb.xml/inc-2.xml, gdb.xml/inc-body.xml, + gdb.xml/includes.xml, gdb.xml/tdesc-xinclude.exp: New files. + 2007-02-02 Denis Pilat * gdb.threads/threadapply.exp: check that frame is not changed by diff --git a/gdb/testsuite/gdb.xml/bad-include.xml b/gdb/testsuite/gdb.xml/bad-include.xml new file mode 100644 index 0000000..f7a2b72 --- /dev/null +++ b/gdb/testsuite/gdb.xml/bad-include.xml @@ -0,0 +1 @@ + diff --git a/gdb/testsuite/gdb.xml/inc-2.xml b/gdb/testsuite/gdb.xml/inc-2.xml new file mode 100644 index 0000000..b076814 --- /dev/null +++ b/gdb/testsuite/gdb.xml/inc-2.xml @@ -0,0 +1 @@ + diff --git a/gdb/testsuite/gdb.xml/inc-body.xml b/gdb/testsuite/gdb.xml/inc-body.xml new file mode 100644 index 0000000..f7d37cb --- /dev/null +++ b/gdb/testsuite/gdb.xml/inc-body.xml @@ -0,0 +1 @@ +invalid-body diff --git a/gdb/testsuite/gdb.xml/includes.xml b/gdb/testsuite/gdb.xml/includes.xml new file mode 100644 index 0000000..3a6a243 --- /dev/null +++ b/gdb/testsuite/gdb.xml/includes.xml @@ -0,0 +1,3 @@ + + + diff --git a/gdb/testsuite/gdb.xml/loop.xml b/gdb/testsuite/gdb.xml/loop.xml new file mode 100644 index 0000000..edd6d20 --- /dev/null +++ b/gdb/testsuite/gdb.xml/loop.xml @@ -0,0 +1 @@ + diff --git a/gdb/testsuite/gdb.xml/tdesc-xinclude.exp b/gdb/testsuite/gdb.xml/tdesc-xinclude.exp new file mode 100644 index 0000000..90d4fe0 --- /dev/null +++ b/gdb/testsuite/gdb.xml/tdesc-xinclude.exp @@ -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" diff --git a/gdb/xml-support.c b/gdb/xml-support.c index 58229a4..1a31db4 100644 --- a/gdb/xml-support.c +++ b/gdb/xml-support.c @@ -36,6 +36,10 @@ static int debug_xml; #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 + /* 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 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 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 *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 XML_Char *name, 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 XML_Char *name, if (element == NULL || element->name == NULL) { + /* 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 (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 XML_Char *name, 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 XML_Char *name) 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 XML_Char *name) 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); + if (scope->body == NULL) + 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 = obstack_object_size (scope->body); + obstack_1grow (scope->body, '\0'); + body = obstack_finish (scope->body); - 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])) + 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 char *name, 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_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 +{ + /* 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 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 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 */ diff --git a/gdb/xml-support.h b/gdb/xml-support.h index 8a9dd61..de8ae5b 100644 --- a/gdb/xml-support.h +++ b/gdb/xml-support.h @@ -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 + 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_parser_and_cleanup (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. diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c index f47e368..e7864bc 100644 --- a/gdb/xml-tdesc.c +++ b/gdb/xml-tdesc.c @@ -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_elements[] = { /* 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,67 @@ file_read_description_xml (const char *filename) 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 + 2); + memcpy (dirname, filename, base - filename); + + /* On DOS based file systems, convert "d:foo" to "d:.", so that + we create "d:./bar" later instead of the (different) + "d:/bar". */ + if (base - filename == 2 && IS_ABSOLUTE_PATH (base) + && !IS_DIR_SEPARATOR (filename[0])) + dirname[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 . */ + +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 +291,14 @@ target_read_description_xml (struct target_ops *ops) 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; -- 2.7.4