From a6d8eb6256e4b4165976318a0f058b7c51330564 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Wed, 27 Dec 2000 10:46:47 +0000 Subject: [PATCH] Finally had a bit of time to resynch both trees: - HTMLparser.[ch]: added a way to avoid adding automatically omitted tags. htmlHandleOmittedElem() allows to change the default handling. - tree.[ch] xmllint.c: added xmlDocDumpFormatMemory() and xmlDocDumpFormatMemoryEnc(), uses memory functions for output of xmllint too when using --memory flag, added a memory test suite at the Makefile level. - xpathInternals.h xpath.[ch] xpointer.c: fixed problems with namespace use when encountering QNames in XPath evalation, added xmlns() scheme in XPointer. - nanoftp.c : incorporated a fix - parser.c xmlIO.c: fixed problems raised with encoding when using the memory I/O - parserInternals.c: closed bug 25934 reported by torsten.landschoff@innominate.de - TODO: updated Daniel --- ChangeLog | 19 +++ HTMLparser.c | 40 ++++- HTMLparser.h | 1 + Makefile.am | 18 +++ TODO | 3 + aclocal.m4 | 40 ++--- include/libxml/HTMLparser.h | 1 + include/libxml/tree.h | 9 ++ include/libxml/xpath.h | 16 +- include/libxml/xpathInternals.h | 8 + nanoftp.c | 3 + parser.c | 29 ++-- parserInternals.c | 10 +- tree.c | 261 +++++++++++++++----------------- tree.h | 9 ++ xmlIO.c | 10 +- xmllint.c | 20 ++- xpath.c | 136 ++++++++++++++--- xpath.h | 16 +- xpathInternals.h | 8 + xpointer.c | 48 ++++++ 21 files changed, 497 insertions(+), 208 deletions(-) diff --git a/ChangeLog b/ChangeLog index f86f81a5..d1d2490b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +Wed Dec 27 12:35:49 CET 2000 Daniel Veillard + + * HTMLparser.[ch]: added a way to avoid adding automatically + omitted tags. htmlHandleOmittedElem() allows to change the + default handling. + * tree.[ch] xmllint.c: added xmlDocDumpFormatMemory() and + xmlDocDumpFormatMemoryEnc(), uses memory functions for output + of xmllint too when using --memory flag, added a memory test + suite at the Makefile level. + * xpathInternals.h xpath.[ch] xpointer.c: fixed problems + with namespace use when encountering QNames in XPath evalation, + added xmlns() scheme in XPointer. + * nanoftp.c : incorporated a fix + * parser.c xmlIO.c: fixed problems raised with encoding when using + the memory I/O + * parserInternals.c: closed bug 25934 reported by + torsten.landschoff@innominate.de + * TODO: updated + Sat Nov 25 11:46:27 CET 2000 Daniel Veillard * configure.in doc/html/* doc/xml.html: made a 2.2.9 release diff --git a/HTMLparser.c b/HTMLparser.c index f2831f6b..a494132d 100644 --- a/HTMLparser.c +++ b/HTMLparser.c @@ -53,6 +53,8 @@ /* #define DEBUG */ /* #define DEBUG_PUSH */ +int htmlOmittedDefaultValue = 1; + /************************************************************************ * * * Parser stacks related functions and macros * @@ -837,12 +839,14 @@ htmlIsAutoClosed(htmlDocPtr doc, htmlNodePtr elem) { * @ctxt: an HTML parser context * @newtag: The new tag name * - * The HTmL DtD allows a tag to exists only implicitely + * The HTML DtD allows a tag to exists only implicitely * called when a new tag has been detected and generates the * appropriates implicit tags if missing */ void htmlCheckImplied(htmlParserCtxtPtr ctxt, const xmlChar *newtag) { + if (!htmlOmittedDefaultValue) + return; if (xmlStrEqual(newtag, BAD_CAST"html")) return; if (ctxt->nameNr <= 0) { @@ -2787,6 +2791,10 @@ htmlCheckEncoding(htmlParserCtxtPtr ctxt, const xmlChar *attvalue) { if ((ctxt == NULL) || (attvalue == NULL)) return; + /* do not change encoding */ + if (ctxt->input->encoding != NULL) + return; + encoding = xmlStrcasestr(attvalue, BAD_CAST"charset="); if (encoding != NULL) { encoding += 8; @@ -4822,6 +4830,7 @@ htmlCreateFileParserCtxt(const char *filename, const char *encoding) htmlParserInputPtr inputStream; xmlParserInputBufferPtr buf; /* htmlCharEncoding enc; */ + xmlChar *content, *content_line = (xmlChar *) "charset="; buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE); if (buf == NULL) return(NULL); @@ -4852,6 +4861,18 @@ htmlCreateFileParserCtxt(const char *filename, const char *encoding) inputStream->free = NULL; inputPush(ctxt, inputStream); + + /* set encoding */ + if (encoding) { + content = xmlMalloc (xmlStrlen(content_line) + strlen(encoding) + 1); + if (content) { + strcpy ((char *)content, (char *)content_line); + strcat ((char *)content, (char *)encoding); + htmlCheckEncoding (ctxt, content); + xmlFree (content); + } + } + return(ctxt); } @@ -4913,4 +4934,21 @@ htmlParseFile(const char *filename, const char *encoding) { return(htmlSAXParseFile(filename, encoding, NULL, NULL)); } +/** + * htmlHandleOmittedElem: + * @val: int 0 or 1 + * + * Set and return the previous value for handling HTML omitted tags. + * + * Returns the last value for 0 for no handling, 1 for auto insertion. + */ + +int +htmlHandleOmittedElem(int val) { + int old = htmlOmittedDefaultValue; + + htmlOmittedDefaultValue = val; + return(old); +} + #endif /* LIBXML_HTML_ENABLED */ diff --git a/HTMLparser.h b/HTMLparser.h index be95357d..b70c0a12 100644 --- a/HTMLparser.h +++ b/HTMLparser.h @@ -91,6 +91,7 @@ int htmlEncodeEntities(unsigned char* out, const unsigned char* in, int *inlen, int quoteChar); int htmlIsScriptAttribute(const xmlChar *name); +int htmlHandleOmittedElem(int val); /** * Interfaces for the Push mode diff --git a/Makefile.am b/Makefile.am index 1e711fe4..1bd18c34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -179,6 +179,24 @@ XMLtests : xmllint diff result.$$name result2.$$name ; \ rm result.$$name result2.$$name ; \ fi ; fi ; done) + @echo "##" + @echo "## XML regression tests on memory" + @echo "##" + @(for i in $(srcdir)/test/* ; do \ + name=`basename $$i`; \ + if [ ! -d $$i ] ; then \ + if [ ! -f $(srcdir)/result/$$name ] ; then \ + echo New test file $$name ; \ + $(top_builddir)/xmllint --memory $$i > $(srcdir)/result/$$name ; \ + else \ + echo Testing $$name ; \ + $(top_builddir)/xmllint --memory $$i > result.$$name ; \ + grep "MORY ALLO" .memdump | grep -v "MEMORY ALLOCATED : 0";\ + diff $(srcdir)/result/$$name result.$$name ; \ + $(top_builddir)/xmllint --memory result.$$name > result2.$$name ; \ + diff result.$$name result2.$$name ; \ + rm result.$$name result2.$$name ; \ + fi ; fi ; done) XMLenttests : xmllint @echo "##" diff --git a/TODO b/TODO index 61ba935e..e83773a3 100644 --- a/TODO +++ b/TODO @@ -32,6 +32,9 @@ TODO: - jamesh suggestion: SAX like functions to save a document ie. call a function to open a new element with given attributes, write character data, close last element, etc +- htmlParseDoc has parameter encoding which is not used. + Function htmlCreateDocParserCtxt ignore it. + TODO: ===== diff --git a/aclocal.m4 b/aclocal.m4 index 869e5fcc..e3726759 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -620,31 +620,35 @@ esac ]) # AC_LIBLTDL_CONVENIENCE[(dir)] - sets LIBLTDL to the link flags for -# the libltdl convenience library, adds --enable-ltdl-convenience to -# the configure arguments. Note that LIBLTDL is not AC_SUBSTed, nor -# is AC_CONFIG_SUBDIRS called. If DIR is not provided, it is assumed -# to be `${top_builddir}/libltdl'. Make sure you start DIR with -# '${top_builddir}/' (note the single quotes!) if your package is not -# flat, and, if you're not using automake, define top_builddir as -# appropriate in the Makefiles. +# the libltdl convenience library and INCLTDL to the include flags for +# the libltdl header and adds --enable-ltdl-convenience to the +# configure arguments. Note that LIBLTDL and INCLTDL are not +# AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called. If DIR is not +# provided, it is assumed to be `libltdl'. LIBLTDL will be prefixed +# with '${top_builddir}/' and INCLTDL will be prefixed with +# '${top_srcdir}/' (note the single quotes!). If your package is not +# flat and you're not using automake, define top_builddir and +# top_srcdir appropriately in the Makefiles. AC_DEFUN(AC_LIBLTDL_CONVENIENCE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl case "$enable_ltdl_convenience" in no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; "") enable_ltdl_convenience=yes ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; esac - LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdlc.la - INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl']) + LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdlc.la + INCLTDL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) ]) # AC_LIBLTDL_INSTALLABLE[(dir)] - sets LIBLTDL to the link flags for -# the libltdl installable library, and adds --enable-ltdl-install to -# the configure arguments. Note that LIBLTDL is not AC_SUBSTed, nor -# is AC_CONFIG_SUBDIRS called. If DIR is not provided, it is assumed -# to be `${top_builddir}/libltdl'. Make sure you start DIR with -# '${top_builddir}/' (note the single quotes!) if your package is not -# flat, and, if you're not using automake, define top_builddir as -# appropriate in the Makefiles. +# the libltdl installable library and INCLTDL to the include flags for +# the libltdl header and adds --enable-ltdl-install to the configure +# arguments. Note that LIBLTDL and INCLTDL are not AC_SUBSTed, nor is +# AC_CONFIG_SUBDIRS called. If DIR is not provided and an installed +# libltdl is not found, it is assumed to be `libltdl'. LIBLTDL will +# be prefixed with '${top_builddir}/' and INCLTDL will be prefixed +# with '${top_srcdir}/' (note the single quotes!). If your package is +# not flat and you're not using automake, define top_builddir and +# top_srcdir appropriately in the Makefiles. # In the future, this macro may have to be called after AC_PROG_LIBTOOL. AC_DEFUN(AC_LIBLTDL_INSTALLABLE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl AC_CHECK_LIB(ltdl, main, @@ -657,8 +661,8 @@ AC_DEFUN(AC_LIBLTDL_INSTALLABLE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl ]) if test x"$enable_ltdl_install" = x"yes"; then ac_configure_args="$ac_configure_args --enable-ltdl-install" - LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdl.la - INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl']) + LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdl.la + INCLTDL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) else ac_configure_args="$ac_configure_args --enable-ltdl-install=no" LIBLTDL="-lltdl" diff --git a/include/libxml/HTMLparser.h b/include/libxml/HTMLparser.h index be95357d..b70c0a12 100644 --- a/include/libxml/HTMLparser.h +++ b/include/libxml/HTMLparser.h @@ -91,6 +91,7 @@ int htmlEncodeEntities(unsigned char* out, const unsigned char* in, int *inlen, int quoteChar); int htmlIsScriptAttribute(const xmlChar *name); +int htmlHandleOmittedElem(int val); /** * Interfaces for the Push mode diff --git a/include/libxml/tree.h b/include/libxml/tree.h index 7b19d090..2dc32423 100644 --- a/include/libxml/tree.h +++ b/include/libxml/tree.h @@ -634,6 +634,10 @@ int xmlReconciliateNs (xmlDocPtr doc, /* * Saving */ +void xmlDocDumpFormatMemory (xmlDocPtr cur, + xmlChar**mem, + int *size, + int format); void xmlDocDumpMemory (xmlDocPtr cur, xmlChar**mem, int *size); @@ -641,6 +645,11 @@ void xmlDocDumpMemoryEnc (xmlDocPtr out_doc, xmlChar **doc_txt_ptr, int * doc_txt_len, const char *txt_encoding); +void xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, + xmlChar **doc_txt_ptr, + int * doc_txt_len, + const char *txt_encoding, + int format); int xmlDocDump (FILE *f, xmlDocPtr cur); void xmlElemDump (FILE *f, diff --git a/include/libxml/xpath.h b/include/libxml/xpath.h index 553b3fc6..6f820d4a 100644 --- a/include/libxml/xpath.h +++ b/include/libxml/xpath.h @@ -47,7 +47,8 @@ typedef enum { XPATH_MEMORY_ERROR, XPTR_SYNTAX_ERROR, XPTR_RESOURCE_ERROR, - XPTR_SUB_RESOURCE_ERROR + XPTR_SUB_RESOURCE_ERROR, + XPATH_UNDEF_PREFIX_ERROR } xmlXPathError; /* @@ -171,6 +172,8 @@ struct _xmlXPathAxis { * - a set of variable bindings * - a function library * - the set of namespace declarations in scope for the expression + * Following the switch to hash tables, this need to be trimmed up at + * the next binary incompatible release. */ struct _xmlXPathContext { @@ -193,10 +196,10 @@ struct _xmlXPathContext { int max_axis; /* max number of axis */ xmlXPathAxisPtr axis; /* Array of defined axis */ - /* Namespace traversal should be implemented with user */ - xmlNsPtr *namespaces; /* The namespaces lookup */ - int nsNr; /* the current Namespace index */ - void *user; /* user defined extra info */ + /* the namespace nodes of the context node */ + xmlNsPtr *namespaces; /* Array of namespaces */ + int nsNr; /* number of namespace in scope */ + void *user; /* function to free */ /* extra variables */ int contextSize; /* the context size */ @@ -206,6 +209,9 @@ struct _xmlXPathContext { int xptr; /* it this an XPointer context */ xmlNodePtr here; /* for here() */ xmlNodePtr origin; /* for origin() */ + + /* the set of namespace declarations in scope for the expression */ + xmlHashTablePtr nsHash; /* The namespaces hash table */ }; /* diff --git a/include/libxml/xpathInternals.h b/include/libxml/xpathInternals.h index a18e453a..6967c90f 100644 --- a/include/libxml/xpathInternals.h +++ b/include/libxml/xpathInternals.h @@ -68,6 +68,13 @@ void xmlXPathDebugDumpObject (FILE *output, /** * Extending a context */ +int xmlXPathRegisterNs (xmlXPathContextPtr ctxt, + const xmlChar *prefix, + const xmlChar *ns_uri); +const xmlChar * xmlXPathNsLookup (xmlXPathContextPtr ctxt, + const xmlChar *ns_uri); +void xmlXPathRegisteredNsCleanup (xmlXPathContextPtr ctxt); + int xmlXPathRegisterFunc (xmlXPathContextPtr ctxt, const xmlChar *name, xmlXPathFunction f); @@ -121,6 +128,7 @@ void xmlXPathIdFunction (xmlXPathParserContextPtr ctxt, void xmlXPathRoot (xmlXPathParserContextPtr ctxt); void xmlXPathEvalExpr (xmlXPathParserContextPtr ctxt); xmlChar * xmlXPathParseName (xmlXPathParserContextPtr ctxt); +xmlChar * xmlXPathParseNCName (xmlXPathParserContextPtr ctxt); /* * Debug diff --git a/nanoftp.c b/nanoftp.c index 12073557..c7ea79af 100644 --- a/nanoftp.c +++ b/nanoftp.c @@ -650,6 +650,9 @@ get_more: * and analyzed. */ len = xmlNanoFTPGetMore(ctx); + if (len < 0) { + return(-1); + } if ((ctxt->controlBufUsed == 0) && (len == 0)) { return(-1); } diff --git a/parser.c b/parser.c index c3ea2a3c..f3f05655 100644 --- a/parser.c +++ b/parser.c @@ -8189,17 +8189,23 @@ xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size, if ((terminate) || (ctxt->input->buf->buffer->use > 80)) xmlParseTryOrFinish(ctxt, terminate); - } else if (ctxt->instate != XML_PARSER_EOF) - if ((ctxt->input != NULL) && ctxt->input->buf != NULL) { - xmlParserInputBufferPtr in = ctxt->input->buf; - int nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw); - if (nbchars < 0) { - xmlGenericError(xmlGenericErrorContext, - "xmlParseChunk: encoder error\n"); - return(XML_ERR_INVALID_ENCODING); - } - } - xmlParseTryOrFinish(ctxt, terminate); + } else if (ctxt->instate != XML_PARSER_EOF) { + if ((ctxt->input != NULL) && ctxt->input->buf != NULL) { + xmlParserInputBufferPtr in = ctxt->input->buf; + if ((in->encoder != NULL) && (in->buffer != NULL) && + (in->raw != NULL)) { + int nbchars; + + nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw); + if (nbchars < 0) { + xmlGenericError(xmlGenericErrorContext, + "xmlParseChunk: encoder error\n"); + return(XML_ERR_INVALID_ENCODING); + } + } + } + } + xmlParseTryOrFinish(ctxt, terminate); if (terminate) { /* * Check for termination @@ -9234,6 +9240,7 @@ xmlCreateEntityParserCtxt(const xmlChar *URL, const xmlChar *ID, } else { inputStream = xmlLoadExternalEntity((char *)uri, (char *)ID, ctxt); if (inputStream == NULL) { + xmlFree(uri); xmlFreeParserCtxt(ctxt); return(NULL); } diff --git a/parserInternals.c b/parserInternals.c index d9cde918..a20faf5b 100644 --- a/parserInternals.c +++ b/parserInternals.c @@ -481,7 +481,7 @@ xmlIsBaseChar(int c) { (((c) >= 0x06D0) && ((c) <= 0x06D3)) || ((c) == 0x06D5) || (((c) >= 0x06E5) && ((c) <= 0x06E6)) || - (((c) > 0x905) && ( /* accelerator */ + (((c) >= 0x905) && ( /* accelerator */ (((c) >= 0x0905) && ((c) <= 0x0939)) || ((c) == 0x093D) || (((c) >= 0x0958) && ((c) <= 0x0961)) || @@ -572,7 +572,7 @@ xmlIsBaseChar(int c) { (((c) >= 0x0EC0) && ((c) <= 0x0EC4)) || (((c) >= 0x0F40) && ((c) <= 0x0F47)) || (((c) >= 0x0F49) && ((c) <= 0x0F69)) || - (((c) > 0x10A0) && ( /* accelerator */ + (((c) >= 0x10A0) && ( /* accelerator */ (((c) >= 0x10A0) && ((c) <= 0x10C5)) || (((c) >= 0x10D0) && ((c) <= 0x10F6)) || ((c) == 0x1100) || @@ -697,7 +697,7 @@ xmlIsCombining(int c) { (((c) >= 0x06E0) && ((c) <= 0x06E4)) || (((c) >= 0x06E7) && ((c) <= 0x06E8)) || (((c) >= 0x06EA) && ((c) <= 0x06ED)) || - (((c) > 0x0901) && ( /* accelerator */ + (((c) >= 0x0901) && ( /* accelerator */ (((c) >= 0x0901) && ((c) <= 0x0903)) || ((c) == 0x093C) || (((c) >= 0x093E) && ((c) <= 0x094C)) || @@ -713,7 +713,7 @@ xmlIsCombining(int c) { (((c) >= 0x09CB) && ((c) <= 0x09CD)) || ((c) == 0x09D7) || (((c) >= 0x09E2) && ((c) <= 0x09E3)) || - (((c) > 0x0A02) && ( /* accelerator */ + (((c) >= 0x0A02) && ( /* accelerator */ ((c) == 0x0A02) || ((c) == 0x0A3C) || ((c) == 0x0A3E) || @@ -753,7 +753,7 @@ xmlIsCombining(int c) { (((c) >= 0x0D46) && ((c) <= 0x0D48)) || (((c) >= 0x0D4A) && ((c) <= 0x0D4D)) || ((c) == 0x0D57) || - (((c) > 0x0E31) && ( /* accelerator */ + (((c) >= 0x0E31) && ( /* accelerator */ ((c) == 0x0E31) || (((c) >= 0x0E34) && ((c) <= 0x0E3A)) || (((c) >= 0x0E47) && ((c) <= 0x0E4E)) || diff --git a/tree.c b/tree.c index 1d6c3a1f..8d245030 100644 --- a/tree.c +++ b/tree.c @@ -5098,44 +5098,6 @@ xmlElemDump(FILE *f, xmlDocPtr doc, xmlNodePtr cur) { xmlBufferFree(buf); } -/** - * xmlDocContentDump: - * @buf: the XML buffer output - * @cur: the document - * - * Dump an XML document. - */ -static void -xmlDocContentDump(xmlBufferPtr buf, xmlDocPtr cur) { - xmlBufferWriteChar(buf, "version != NULL) - xmlBufferWriteQuotedString(buf, cur->version); - else - xmlBufferWriteChar(buf, "\"1.0\""); - if (cur->encoding != NULL) { - xmlBufferWriteChar(buf, " encoding="); - xmlBufferWriteQuotedString(buf, cur->encoding); - } - switch (cur->standalone) { - case 0: - xmlBufferWriteChar(buf, " standalone=\"no\""); - break; - case 1: - xmlBufferWriteChar(buf, " standalone=\"yes\""); - break; - } - xmlBufferWriteChar(buf, "?>\n"); - if (cur->children != NULL) { - xmlNodePtr child = cur->children; - - while (child != NULL) { - xmlNodeDump(buf, cur, child, 0, 0); - xmlBufferWriteChar(buf, "\n"); - child = child->next; - } - } -} - /************************************************************************ * * * Dumping XML tree content to an I/O output buffer * @@ -5522,12 +5484,13 @@ xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, * @buf: the XML buffer output * @cur: the document * @encoding: an optional encoding string + * @format: should formatting spaces been added * * Dump an XML document. */ static void xmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur, - const char *encoding) { + const char *encoding, int format) { xmlOutputBufferWriteString(buf, "version != NULL) xmlBufferWriteQuotedString(buf->buffer, cur->version); @@ -5556,7 +5519,7 @@ xmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur, xmlNodePtr child = cur->children; while (child != NULL) { - xmlNodeDumpOutput(buf, cur, child, 0, 1, encoding); + xmlNodeDumpOutput(buf, cur, child, 0, format, encoding); xmlOutputBufferWriteString(buf, "\n"); child = child->next; } @@ -5569,6 +5532,105 @@ xmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur, * * ************************************************************************/ +/** + * xmlDocDumpMemoryEnc: + * @out_doc: Document to generate XML text from + * @doc_txt_ptr: Memory pointer for allocated XML text + * @doc_txt_len: Length of the generated XML text + * @txt_encoding: Character encoding to use when generating XML text + * @format: should formatting spaces been added + * + * Dump the current DOM tree into memory using the character encoding specified + * by the caller. Note it is up to the caller of this function to free the + * allocated memory. + */ + +void +xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr, + int * doc_txt_len, const char * txt_encoding, int format) { + int dummy = 0; + + xmlCharEncoding doc_charset; + xmlOutputBufferPtr out_buff = NULL; + xmlCharEncodingHandlerPtr conv_hdlr = NULL; + + if (doc_txt_len == NULL) { + doc_txt_len = &dummy; /* Continue, caller just won't get length */ + } + + if (doc_txt_ptr == NULL) { + *doc_txt_len = 0; + xmlGenericError(xmlGenericErrorContext, + "xmlDocDumpFormatMemoryEnc: Null return buffer pointer."); + return; + } + + *doc_txt_ptr = NULL; + *doc_txt_len = 0; + + if (out_doc == NULL) { + /* No document, no output */ + xmlGenericError(xmlGenericErrorContext, + "xmlDocDumpFormatMemoryEnc: Null DOM tree document pointer.\n"); + return; + } + + /* + * Validate the encoding value, if provided. + * This logic is copied from xmlSaveFileEnc. + */ + + if (txt_encoding == NULL) + txt_encoding = (const char *) out_doc->encoding; + if (txt_encoding != NULL) { + doc_charset = xmlParseCharEncoding(txt_encoding); + + if (out_doc->charset != XML_CHAR_ENCODING_UTF8) { + xmlGenericError(xmlGenericErrorContext, + "xmlDocDumpFormatMemoryEnc: Source document not in UTF8\n"); + return; + + } else if (doc_charset != XML_CHAR_ENCODING_UTF8) { + conv_hdlr = xmlFindCharEncodingHandler(txt_encoding); + if ( conv_hdlr == NULL ) { + xmlGenericError(xmlGenericErrorContext, + "%s: %s %s '%s'\n", + "xmlDocDumpFormatMemoryEnc", + "Failed to identify encoding handler for", + "character set", + txt_encoding); + return; + } + } + } + + if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) { + xmlGenericError(xmlGenericErrorContext, + "xmlDocDumpFormatMemoryEnc: Failed to allocate output buffer.\n"); + return; + } + + xmlDocContentDumpOutput(out_buff, out_doc, txt_encoding, 1); + xmlOutputBufferFlush(out_buff); + if (out_buff->conv != NULL) { + *doc_txt_len = out_buff->conv->use; + *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len); + } else { + *doc_txt_len = out_buff->buffer->use; + *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len); + } + (void)xmlOutputBufferClose(out_buff); + + if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) { + *doc_txt_len = 0; + xmlGenericError(xmlGenericErrorContext, + "xmlDocDumpFormatMemoryEnc: %s\n", + "Failed to allocate memory for document text representation."); + } + + return; +} + /** * xmlDocDumpMemory: * @cur: the document @@ -5580,27 +5642,23 @@ xmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur, */ void xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) { - xmlBufferPtr buf; + xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0); +} - if (cur == NULL) { -#ifdef DEBUG_TREE - xmlGenericError(xmlGenericErrorContext, - "xmlDocDumpMemory : document == NULL\n"); -#endif - *mem = NULL; - *size = 0; - return; - } - buf = xmlBufferCreate(); - if (buf == NULL) { - *mem = NULL; - *size = 0; - return; - } - xmlDocContentDump(buf, cur); - *mem = xmlStrndup(buf->content, buf->use); - *size = buf->use; - xmlBufferFree(buf); +/** + * xmlDocDumpFormatMemory: + * @cur: the document + * @mem: OUT: the memory pointer + * @size: OUT: the memory lenght + * @format: should formatting spaces been added + * + * + * Dump an XML document in memory and return the xmlChar * and it's size. + * It's up to the caller to free the memory. + */ +void +xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) { + xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format); } /** @@ -5618,79 +5676,8 @@ xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) { void xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr, int * doc_txt_len, const char * txt_encoding) { - int dummy = 0; - - xmlCharEncoding doc_charset; - xmlOutputBufferPtr out_buff = NULL; - xmlCharEncodingHandlerPtr conv_hdlr = NULL; - - if (doc_txt_len == NULL) { - doc_txt_len = &dummy; /* Continue, caller just won't get length */ - } - - if (doc_txt_ptr == NULL) { - *doc_txt_len = 0; - xmlGenericError(xmlGenericErrorContext, - "xmlDocDumpMemoryEnc: Null return buffer pointer."); - return; - } - - *doc_txt_ptr = NULL; - *doc_txt_len = 0; - - if (out_doc == NULL) { - /* No document, no output */ - xmlGenericError(xmlGenericErrorContext, - "xmlDocDumpMemoryEnc: Null DOM tree document pointer.\n"); - return; - } - - /* - * Validate the encoding value, if provided. - * This logic is copied from xmlSaveFileEnc. - */ - - if (txt_encoding != NULL) { - doc_charset = xmlParseCharEncoding(txt_encoding); - - if (out_doc->charset != XML_CHAR_ENCODING_UTF8) { - xmlGenericError(xmlGenericErrorContext, - "xmlDocDumpMemoryEnc: Source document not in UTF8\n"); - return; - - } else if (doc_charset != XML_CHAR_ENCODING_UTF8) { - conv_hdlr = xmlFindCharEncodingHandler(txt_encoding); - if ( conv_hdlr == NULL ) { - xmlGenericError(xmlGenericErrorContext, - "%s: %s %s '%s'\n", - "xmlDocDumpMemoryEnc", - "Failed to identify encoding handler for", - "character set", - txt_encoding); - return; - } - } - } - - if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) { - xmlGenericError(xmlGenericErrorContext, - "xmlDocDumpMemoryEnc: Failed to allocate output buffer.\n"); - return; - } - - xmlDocContentDumpOutput(out_buff, out_doc, txt_encoding); - *doc_txt_len = out_buff->buffer->use; - *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len); - (void)xmlOutputBufferClose(out_buff); - - if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) { - *doc_txt_len = 0; - xmlGenericError(xmlGenericErrorContext, - "xmlDocDumpMemoryEnc: %s\n", - "Failed to allocate memory for document text representation."); - } - - return; + xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len, + txt_encoding, 1); } /** @@ -5792,7 +5779,7 @@ xmlDocDump(FILE *f, xmlDocPtr cur) { } buf = xmlOutputBufferCreateFile(f, handler); if (buf == NULL) return(-1); - xmlDocContentDumpOutput(buf, cur, NULL); + xmlDocContentDumpOutput(buf, cur, NULL, 1); ret = xmlOutputBufferClose(buf); return(ret); @@ -5813,7 +5800,7 @@ xmlSaveFileTo(xmlOutputBuffer *buf, xmlDocPtr cur, const char *encoding) { int ret; if (buf == NULL) return(0); - xmlDocContentDumpOutput(buf, cur, encoding); + xmlDocContentDumpOutput(buf, cur, encoding, 1); ret = xmlOutputBufferClose(buf); return(ret); } @@ -5857,7 +5844,7 @@ xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) { buf = xmlOutputBufferCreateFilename(filename, handler, 0); if (buf == NULL) return(0); - xmlDocContentDumpOutput(buf, cur, encoding); + xmlDocContentDumpOutput(buf, cur, encoding, 1); ret = xmlOutputBufferClose(buf); return(ret); @@ -5912,7 +5899,7 @@ xmlSaveFile(const char *filename, xmlDocPtr cur) { buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression); if (buf == NULL) return(0); - xmlDocContentDumpOutput(buf, cur, NULL); + xmlDocContentDumpOutput(buf, cur, NULL, 1); ret = xmlOutputBufferClose(buf); return(ret); diff --git a/tree.h b/tree.h index 7b19d090..2dc32423 100644 --- a/tree.h +++ b/tree.h @@ -634,6 +634,10 @@ int xmlReconciliateNs (xmlDocPtr doc, /* * Saving */ +void xmlDocDumpFormatMemory (xmlDocPtr cur, + xmlChar**mem, + int *size, + int format); void xmlDocDumpMemory (xmlDocPtr cur, xmlChar**mem, int *size); @@ -641,6 +645,11 @@ void xmlDocDumpMemoryEnc (xmlDocPtr out_doc, xmlChar **doc_txt_ptr, int * doc_txt_len, const char *txt_encoding); +void xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, + xmlChar **doc_txt_ptr, + int * doc_txt_len, + const char *txt_encoding, + int format); int xmlDocDump (FILE *f, xmlDocPtr cur); void xmlElemDump (FILE *f, diff --git a/xmlIO.c b/xmlIO.c index ca8413d7..3a8fcf2d 100644 --- a/xmlIO.c +++ b/xmlIO.c @@ -1413,6 +1413,9 @@ xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) { if (nbchars < MINLEN) return(0); + if (!out->writecallback) + return(nbchars); + /* * second write the stuff to the I/O channel */ @@ -1477,7 +1480,7 @@ xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) { */ int xmlOutputBufferFlush(xmlOutputBufferPtr out) { - int nbchars = 0, ret; + int nbchars = 0, ret = 0; /* * first handle encoding stuff. @@ -1497,12 +1500,13 @@ xmlOutputBufferFlush(xmlOutputBufferPtr out) { /* * second flush the stuff to the I/O channel */ - if ((out->conv != NULL) && (out->encoder != NULL)) { + if ((out->conv != NULL) && (out->encoder != NULL) && + (out->writecallback != NULL)) { ret = out->writecallback(out->context, (const char *)out->conv->content, out->conv->use); if (ret >= 0) xmlBufferShrink(out->conv, ret); - } else { + } else if (out->writecallback != NULL) { ret = out->writecallback(out->context, (const char *)out->buffer->content, out->buffer->use); if (ret >= 0) diff --git a/xmllint.c b/xmllint.c index 099084b8..3dd413a6 100644 --- a/xmllint.c +++ b/xmllint.c @@ -565,15 +565,31 @@ void parseAndPrintFile(char *filename) { #ifdef LIBXML_DEBUG_ENABLED if (!debug) { #endif - if (compress) + if (memory) { + xmlChar *result; + int len; + + if (encoding != NULL) { + xmlDocDumpMemoryEnc(doc, &result, &len, encoding); + } else { + xmlDocDumpMemory(doc, &result, &len); + } + if (result == NULL) { + fprintf(stderr, "Failed to save\n"); + } else { + write(1, result, len); + xmlFree(result); + } + } else if (compress) xmlSaveFile("-", doc); else if (encoding != NULL) xmlSaveFileEnc("-", doc, encoding); else xmlDocDump(stdout, doc); #ifdef LIBXML_DEBUG_ENABLED - } else + } else { xmlDebugDumpDocument(stdout, doc); + } #endif } diff --git a/xpath.c b/xpath.c index 62fa15cf..d667bbbc 100644 --- a/xpath.c +++ b/xpath.c @@ -444,7 +444,8 @@ const char *xmlXPathErrorMessages[] = { "Memory allocation error", "Syntax error", "Resource error", - "Sub resource error" + "Sub resource error", + "Undefined namespace prefix" }; /** @@ -1064,6 +1065,70 @@ xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) { ctxt->varHash = NULL; } +/** + * xmlXPathRegisterNs: + * @ctxt: the XPath context + * @prefix: the namespace prefix + * @ns_uri: the namespace name + * + * Register a new namespace. If @ns_uri is NULL it unregisters + * the namespace + * + * Returns 0 in case of success, -1 in case of error + */ +int +xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix, + const xmlChar *ns_uri) { + if (ctxt == NULL) + return(-1); + if (prefix == NULL) + return(-1); + + if (ctxt->nsHash == NULL) + ctxt->nsHash = xmlHashCreate(10); + if (ctxt->nsHash == NULL) + return(-1); + return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri, + (xmlHashDeallocator)xmlFree)); +} + +/** + * xmlXPathNsLookup: + * @ctxt: the XPath context + * @prefix: the namespace prefix value + * + * Search in the namespace declaration array of the context for the given + * namespace name associated to the given prefix + * + * Returns the value or NULL if not found + */ +const xmlChar * +xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) { + if (ctxt == NULL) + return(NULL); + if (prefix == NULL) + return(NULL); + if (ctxt->nsHash == NULL) + return(NULL); + + return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix)); +} + +/** + * xmlXPathRegisteredVariablesCleanup: + * @ctxt: the XPath context + * + * Cleanup the XPath context data associated to registered variables + */ +void +xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) { + if (ctxt == NULL) + return; + + xmlHashFree(ctxt->nsHash, NULL); + ctxt->nsHash = NULL; +} + /************************************************************************ * * * Routines to handle Values * @@ -1285,9 +1350,8 @@ xmlXPathNewContext(xmlDocPtr doc) { ret->max_axis = 0; ret->axis = NULL; - ret->namespaces = NULL; + ret->nsHash = NULL; ret->user = NULL; - ret->nsNr = 0; ret->contextSize = -1; ret->proximityPosition = -1; @@ -1305,9 +1369,7 @@ xmlXPathNewContext(xmlDocPtr doc) { */ void xmlXPathFreeContext(xmlXPathContextPtr ctxt) { - if (ctxt->namespaces != NULL) - xmlFree(ctxt->namespaces); - + xmlXPathRegisteredNsCleanup(ctxt); xmlXPathRegisteredFuncsCleanup(ctxt); xmlXPathRegisteredVariablesCleanup(ctxt); #ifdef DEBUG @@ -2712,10 +2774,19 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, xmlXPathAxisVal axis, if ((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_DOCUMENT_NODE) || (cur->type == XML_HTML_DOCUMENT_NODE)) { + if (prefix == NULL) { #ifdef DEBUG_STEP - n++; + n++; #endif - xmlXPathNodeSetAdd(ret, cur); + xmlXPathNodeSetAdd(ret, cur); + } else if ((cur->ns != NULL) && + (xmlStrEqual(prefix, + cur->ns->href))) { +#ifdef DEBUG_STEP + n++; +#endif + xmlXPathNodeSetAdd(ret, cur); + } } } break; @@ -2726,14 +2797,25 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, xmlXPathAxisVal axis, case NODE_TEST_NAME: switch (cur->type) { case XML_ELEMENT_NODE: - if (xmlStrEqual(name, cur->name) && - (((prefix == NULL) || - ((cur->ns != NULL) && - (xmlStrEqual(prefix, cur->ns->href)))))) { + if (xmlStrEqual(name, cur->name)) { + if (prefix == NULL) { + if ((cur->ns == NULL) || + (cur->ns->prefix == NULL)) { #ifdef DEBUG_STEP - n++; + n++; #endif - xmlXPathNodeSetAdd(ret, cur); + xmlXPathNodeSetAdd(ret, cur); + } + } else { + if ((cur->ns != NULL) && + (xmlStrEqual(prefix, + cur->ns->href))) { +#ifdef DEBUG_STEP + n++; +#endif + xmlXPathNodeSetAdd(ret, cur); + } + } } break; case XML_ATTRIBUTE_NODE: { @@ -5106,7 +5188,7 @@ xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) { */ xmlChar * xmlXPathEvalNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test, - xmlXPathTypeVal *type, xmlChar **prefix, xmlChar *name) { + xmlXPathTypeVal *type, const xmlChar **prefix, xmlChar *name) { int blanks; if ((test == NULL) || (type == NULL) || (prefix == NULL)) { @@ -5148,8 +5230,11 @@ xmlXPathEvalNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test, *type = NODE_TYPE_PI; else if (xmlStrEqual(name, BAD_CAST "text")) *type = NODE_TYPE_TEXT; - else + else { + if (name != NULL) + xmlFree(name); XP_ERROR0(XPATH_EXPR_ERROR); + } *test = NODE_TEST_TYPE; @@ -5172,8 +5257,11 @@ xmlXPathEvalNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test, xmlXPathFreeObject(cur); SKIP_BLANKS; } - if (CUR != ')') + if (CUR != ')') { + if (name != NULL) + xmlFree(name); XP_ERROR0(XPATH_UNCLOSED_ERROR); + } NEXT; return(name); } @@ -5181,7 +5269,15 @@ xmlXPathEvalNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test, if ((!blanks) && (CUR == ':')) { NEXT; - *prefix = name; + /* + * get the namespace name for this prefix + */ + *prefix = xmlXPathNsLookup(ctxt->context, name); + if (name != NULL) + xmlFree(name); + if (*prefix == NULL) { + XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); + } if (CUR == '*') { /* @@ -5347,7 +5443,7 @@ xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) { SKIP_BLANKS; } else { xmlChar *name = NULL; - xmlChar *prefix = NULL; + const xmlChar *prefix = NULL; xmlXPathTestVal test; xmlXPathAxisVal axis; xmlXPathTypeVal type; @@ -5420,8 +5516,6 @@ xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) { #endif if (name != NULL) xmlFree(name); - if (prefix != NULL) - xmlFree(prefix); eval_predicates: SKIP_BLANKS; diff --git a/xpath.h b/xpath.h index 553b3fc6..6f820d4a 100644 --- a/xpath.h +++ b/xpath.h @@ -47,7 +47,8 @@ typedef enum { XPATH_MEMORY_ERROR, XPTR_SYNTAX_ERROR, XPTR_RESOURCE_ERROR, - XPTR_SUB_RESOURCE_ERROR + XPTR_SUB_RESOURCE_ERROR, + XPATH_UNDEF_PREFIX_ERROR } xmlXPathError; /* @@ -171,6 +172,8 @@ struct _xmlXPathAxis { * - a set of variable bindings * - a function library * - the set of namespace declarations in scope for the expression + * Following the switch to hash tables, this need to be trimmed up at + * the next binary incompatible release. */ struct _xmlXPathContext { @@ -193,10 +196,10 @@ struct _xmlXPathContext { int max_axis; /* max number of axis */ xmlXPathAxisPtr axis; /* Array of defined axis */ - /* Namespace traversal should be implemented with user */ - xmlNsPtr *namespaces; /* The namespaces lookup */ - int nsNr; /* the current Namespace index */ - void *user; /* user defined extra info */ + /* the namespace nodes of the context node */ + xmlNsPtr *namespaces; /* Array of namespaces */ + int nsNr; /* number of namespace in scope */ + void *user; /* function to free */ /* extra variables */ int contextSize; /* the context size */ @@ -206,6 +209,9 @@ struct _xmlXPathContext { int xptr; /* it this an XPointer context */ xmlNodePtr here; /* for here() */ xmlNodePtr origin; /* for origin() */ + + /* the set of namespace declarations in scope for the expression */ + xmlHashTablePtr nsHash; /* The namespaces hash table */ }; /* diff --git a/xpathInternals.h b/xpathInternals.h index a18e453a..6967c90f 100644 --- a/xpathInternals.h +++ b/xpathInternals.h @@ -68,6 +68,13 @@ void xmlXPathDebugDumpObject (FILE *output, /** * Extending a context */ +int xmlXPathRegisterNs (xmlXPathContextPtr ctxt, + const xmlChar *prefix, + const xmlChar *ns_uri); +const xmlChar * xmlXPathNsLookup (xmlXPathContextPtr ctxt, + const xmlChar *ns_uri); +void xmlXPathRegisteredNsCleanup (xmlXPathContextPtr ctxt); + int xmlXPathRegisterFunc (xmlXPathContextPtr ctxt, const xmlChar *name, xmlXPathFunction f); @@ -121,6 +128,7 @@ void xmlXPathIdFunction (xmlXPathParserContextPtr ctxt, void xmlXPathRoot (xmlXPathParserContextPtr ctxt); void xmlXPathEvalExpr (xmlXPathParserContextPtr ctxt); xmlChar * xmlXPathParseName (xmlXPathParserContextPtr ctxt); +xmlChar * xmlXPathParseNCName (xmlXPathParserContextPtr ctxt); /* * Debug diff --git a/xpointer.c b/xpointer.c index 376421ff..1b2efe7f 100644 --- a/xpointer.c +++ b/xpointer.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #ifdef LIBXML_DEBUG_ENABLED @@ -37,6 +38,9 @@ #ifdef LIBXML_XPTR_ENABLED +/* Add support of the xmlns() xpointer scheme to initialize the namespaces */ +#define XPTR_XMLNS_SCHEME + /* #define DEBUG_RANGES */ #define TODO \ @@ -1038,6 +1042,50 @@ xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt, xmlChar *name) { xmlXPathRoot(ctxt); xmlXPathEvalExpr(ctxt); CUR_PTR=left; +#ifdef XPTR_XMLNS_SCHEME + } else if (xmlStrEqual(name, (xmlChar *) "xmlns")) { + const xmlChar *left = CUR_PTR; + xmlChar *prefix; + xmlChar *URI; + xmlURIPtr value; + + CUR_PTR = buffer; + prefix = xmlXPathParseNCName(ctxt); + if (prefix == NULL) { + xmlFree(buffer); + xmlFree(name); + XP_ERROR(XPTR_SYNTAX_ERROR); + } + SKIP_BLANKS; + if (CUR != '=') { + xmlFree(prefix); + xmlFree(buffer); + xmlFree(name); + XP_ERROR(XPTR_SYNTAX_ERROR); + } + NEXT; + SKIP_BLANKS; + /* @@ check escaping in the XPointer WD */ + + value = xmlParseURI((const char *)ctxt->cur); + if (value == NULL) { + xmlFree(prefix); + xmlFree(buffer); + xmlFree(name); + XP_ERROR(XPTR_SYNTAX_ERROR); + } + URI = xmlSaveUri(value); + xmlFreeURI(value); + if (URI == NULL) { + xmlFree(prefix); + xmlFree(buffer); + xmlFree(name); + XP_ERROR(XPATH_MEMORY_ERROR); + } + + xmlXPathRegisterNs(ctxt->context, prefix, URI); + CUR_PTR = left; +#endif /* XPTR_XMLNS_SCHEME */ } else { xmlGenericError(xmlGenericErrorContext, "unsupported scheme '%s'\n", name);