diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2019-01-08 16:41:14 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2019-01-08 16:41:14 (GMT) |
commit | 4750a6186365f1457eea083102108b8c2a4d5936 (patch) | |
tree | 6b294117b1bce8e1643aa891d1591d6fc0271e1f /tclxml/tclxml-libxml2.c | |
parent | a3822259d9dcf8a2f80396b71f02f1bf549bf341 (diff) | |
parent | 48d306ba2a4f72d4cdb9708d57e3854aa4984a25 (diff) | |
download | blt-4750a6186365f1457eea083102108b8c2a4d5936.zip blt-4750a6186365f1457eea083102108b8c2a4d5936.tar.gz blt-4750a6186365f1457eea083102108b8c2a4d5936.tar.bz2 |
Merge commit '48d306ba2a4f72d4cdb9708d57e3854aa4984a25' as 'tclxml'
Diffstat (limited to 'tclxml/tclxml-libxml2.c')
-rwxr-xr-x | tclxml/tclxml-libxml2.c | 982 |
1 files changed, 982 insertions, 0 deletions
diff --git a/tclxml/tclxml-libxml2.c b/tclxml/tclxml-libxml2.c new file mode 100755 index 0000000..29ee985 --- /dev/null +++ b/tclxml/tclxml-libxml2.c @@ -0,0 +1,982 @@ +/* tcllibxml2.c -- + * + * A Tcl wrapper for libxml2. + * + * Copyright (c) 2005-2008 Explain. + * http://www.explain.com.au/ + * Copyright (c) 2003-2004 Zveno Pty Ltd + * http://www.zveno.com/ + * + * See the file "LICENSE" for information on usage and + * redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: tclxml-libxml2.c,v 1.1.1.1 2009/01/16 22:11:49 joye Exp $ + */ + +#include <tclxml-libxml2/tclxml-libxml2.h> +#include <libxml/globals.h> +#include <libxml/tree.h> +#include <libxml/parserInternals.h> +#include <libxml/xmlreader.h> +#include <libxml/SAX2.h> +#include <libxml/xmlerror.h> +#include <libxml/uri.h> +#include <string.h> + +#define TCL_DOES_STUBS \ + (TCL_MAJOR_VERSION > 8 || TCL_MAJOR_VERSION == 8 && (TCL_MINOR_VERSION > 1 || \ + (TCL_MINOR_VERSION == 1 && TCL_RELEASE_LEVEL == TCL_FINAL_RELEASE))) + +/* + * The structure below is used to refer to a libxml2 parser object. + */ + +typedef struct TclXMLlibxml2Info { + Tcl_Interp *interp; /* Interpreter for this instance */ + + xmlTextReaderPtr reader; /* New TextReader interface */ + + Tcl_Obj *docObjPtr; /* Result of parsing */ + TclXML_libxml2_DocumentHandling keep; /* Document handling flag */ + Tcl_Obj *preserve; /* XPath for retaining (a portion of) the document */ + Tcl_Obj *preservens; /* list of namespace declarations */ + + TclXML_Info *xmlinfo; /* Generic data structure */ + + Tcl_HashTable *scope; /* XML namespaces in scope */ + +} TclXMLlibxml2Info; + +/* + * Forward declarations for private functions. + */ + +static ClientData ReaderCreate _ANSI_ARGS_((Tcl_Interp *interp, + TclXML_Info *xmlinfo)); +static int ReaderReset _ANSI_ARGS_((ClientData clientData)); +static int TclXMLlibxml2Delete _ANSI_ARGS_((ClientData clientData)); +static int ReaderParse _ANSI_ARGS_((ClientData clientData, + char *data, int len, int final)); +static int TclXMLlibxml2Configure _ANSI_ARGS_((ClientData clientdata, + Tcl_Obj *CONST optionPtr, + Tcl_Obj *CONST valuePtr)); +static int TclXMLlibxml2Get _ANSI_ARGS_((ClientData clientData, + int objc, Tcl_Obj *CONST objv[])); + +static xmlParserInputPtr TclXMLlibxml2ExternalEntityLoader _ANSI_ARGS_((const char *URL, + const char *ID, + xmlParserCtxtPtr ctxt)); + +/* + * Externally visible functions + */ + +typedef struct ThreadSpecificData { + int initialized; + + Tcl_Interp *interp; + + /* + * Interpose on default external entity loader + */ + + TclXMLlibxml2Info *current; + xmlExternalEntityLoader defaultLoader; + +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * libxml2 is mostly thread-safe, but there are issues with error callbacks + */ + +TCL_DECLARE_MUTEX(libxml2) + +#ifndef CONST84 +#define CONST84 /* Before 8.4 no 'const' required */ +#endif + +/* + *---------------------------------------------------------------------------- + * + * Tclxml_libxml2_Init -- + * + * Initialisation routine for loadable module + * + * Results: + * None. + * + * Side effects: + * Creates commands in the interpreter, + * + *---------------------------------------------------------------------------- + */ + +int +Tclxml_libxml2_Init (interp) + Tcl_Interp *interp; /* Interpreter to initialise */ +{ + ThreadSpecificData *tsdPtr; + TclXML_ParserClassInfo *classinfo; + +#ifdef USE_TCL_STUBS + if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { + return TCL_ERROR; + } +#endif +#ifdef USE_TCLXML_STUBS + if (TclXML_InitStubs(interp, TCLXML_VERSION, 1) == NULL) { + return TCL_ERROR; + } +#endif + + classinfo = (TclXML_ParserClassInfo *) ckalloc(sizeof(TclXML_ParserClassInfo)); + classinfo->name = Tcl_NewStringObj("libxml2", -1); + classinfo->create = ReaderCreate; + classinfo->createCmd = NULL; + classinfo->createEntity = NULL; /* TclXMLlibxml2CreateEntityParser; */ + classinfo->createEntityCmd = NULL; + classinfo->parse = ReaderParse; + classinfo->parseCmd = NULL; + classinfo->configure = TclXMLlibxml2Configure; + classinfo->configureCmd = NULL; + classinfo->get = TclXMLlibxml2Get; + classinfo->getCmd = NULL; + classinfo->destroy = TclXMLlibxml2Delete; + classinfo->destroyCmd = NULL; + classinfo->reset = ReaderReset; + classinfo->resetCmd = NULL; + + if (TclXML_RegisterXMLParser(interp, classinfo) != TCL_OK) { + Tcl_SetResult(interp, "unable to register parser", NULL); + return TCL_ERROR; + } + + /* Configure the libxml2 parser */ + + Tcl_MutexLock(&libxml2); + + xmlInitParser(); + xmlSubstituteEntitiesDefault(1); + + /* + * TODO: provide configuration option for setting this value. + */ + xmlLoadExtDtdDefaultValue |= 1; + xmlLoadExtDtdDefaultValue |= XML_COMPLETE_ATTRS; + + /* + * Override default entity loader so we can implement callbacks + */ + + tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (!tsdPtr->initialized) { + tsdPtr->initialized = 1; + tsdPtr->interp = interp; + + tsdPtr->current = NULL; + tsdPtr->defaultLoader = xmlGetExternalEntityLoader(); + xmlSetExternalEntityLoader(TclXMLlibxml2ExternalEntityLoader); + } /* only need to init the library once per process */ + + /* Setting the variable is insufficient - must create namespace too. */ + if (Tcl_VarEval(interp, "namespace eval ::xml::libxml2 {variable libxml2version ", xmlParserVersion, "}\n", NULL) != TCL_OK) { + return TCL_ERROR; + } + + Tcl_MutexUnlock(&libxml2); + + TclXML_libxml2_InitDocObj(interp); + + #if (TCL_DOES_STUBS && USE_TCLXML_STUBS) + { + extern Tclxml_libxml2Stubs tclxml_libxml2Stubs; + if (Tcl_PkgProvideEx(interp, "xml::libxml2", TCLXML_VERSION, + (ClientData) &tclxml_libxml2Stubs) != TCL_OK) { + return TCL_ERROR; + } + } + #else + if (Tcl_PkgProvide(interp, "xml::libxml2", TCLXML_VERSION) != TCL_OK) { + return TCL_ERROR; + } + #endif + + return TCL_OK; +} + +/* + * TclXML/libxml2 is made safe by preventing the use of the default + * external entity loader. + */ + +int +Tclxml_libxml2_SafeInit (interp) + Tcl_Interp *interp; /* Interpreter to initialise */ +{ + return Tclxml_libxml2_Init(interp); +} + +/* + *---------------------------------------------------------------------------- + * + * TclXMLlibxml2Create -- + * + * Prepare for parsing. + * + * Results: + * Standard Tcl result. + * + * Side effects: + * This creates a Text Reader. + * + *---------------------------------------------------------------------------- + */ + +static ClientData +ReaderCreate(interp, xmlinfo) + Tcl_Interp *interp; + TclXML_Info *xmlinfo; +{ + TclXMLlibxml2Info *info; + xmlParserInputBufferPtr inputPtr; + + if (!(info = (TclXMLlibxml2Info *) Tcl_Alloc(sizeof(TclXMLlibxml2Info)))) { + Tcl_Free((char *) info); + Tcl_SetResult(interp, "unable to create parser", NULL); + return NULL; + } + info->interp = interp; + info->xmlinfo = xmlinfo; + info->preserve = NULL; + info->preservens = NULL; + + /* Create a dummy input buffer for the purpose of creating the TextReader. + * This will be replaced when we come to actually parse the document. + */ + Tcl_MutexLock(&libxml2); + inputPtr = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE); + if (inputPtr == NULL) { + Tcl_MutexUnlock(&libxml2); + + Tcl_Free((char *) info); + Tcl_SetResult(interp, "unable to create input buffer", NULL); + return NULL; + } + info->reader = xmlNewTextReader(inputPtr, NULL); + if (info->reader == NULL) { + Tcl_MutexUnlock(&libxml2); + + Tcl_Free((char *) info); + Tcl_SetResult(interp, "unable to create XML reader", NULL); + return NULL; + } + xmlTextReaderSetStructuredErrorHandler(info->reader, + (xmlStructuredErrorFunc) TclXML_libxml2_ErrorHandler, + NULL); + + Tcl_MutexUnlock(&libxml2); + + info->docObjPtr = NULL; + info->keep = TCLXML_LIBXML2_DOCUMENT_IMPLICIT; + info->scope = (Tcl_HashTable *) Tcl_Alloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(info->scope, TCL_STRING_KEYS); + + return (ClientData) info; +} + +/* + *---------------------------------------------------------------------------- + * + * TclXMLlibxml2Delete -- + * + * Destroy the libxml2 parser structure. + * + * Results: + * None. + * + * Side effects: + * Frees any memory allocated for the XML parser. + * + *---------------------------------------------------------------------------- + */ + +static int +TclXMLlibxml2Delete(clientData) + ClientData clientData; +{ + TclXMLlibxml2Info *info = (TclXMLlibxml2Info *) clientData; + + if (info->reader) { + xmlFreeTextReader(info->reader); + } + if (info->docObjPtr) { + Tcl_DecrRefCount(info->docObjPtr); + } + if (info->preserve) { + Tcl_DecrRefCount(info->preserve); + } + if (info->preservens) { + Tcl_DecrRefCount(info->preservens); + } + Tcl_DeleteHashTable(info->scope); + Tcl_Free((char *) info->scope); + Tcl_Free((char *) info); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------------- + * + * ReaderReset -- + * + * Reset the libxml2 parser structure. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static int +ReaderReset(clientData) +ClientData clientData; +{ + TclXML_Info *xmlinfo = (TclXML_Info *) clientData; + + if (xmlinfo->clientData == NULL) { + xmlinfo->clientData = (ClientData) ReaderCreate(xmlinfo->interp, xmlinfo); + if (xmlinfo->clientData == NULL) { + return TCL_ERROR; + } + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------------- + * + * ReaderParse -- + * + * Wrapper to invoke libxml2 parser and check return result. + * + * NB. Most of the logic from xmlSAXUserParseMemory is used here. + * + * Results: + * TCL_OK if no errors, TCL_ERROR otherwise. + * + * Side effects: + * Sets interpreter result as appropriate. + * + *---------------------------------------------------------------------------- + */ + +static int +ReaderParse(clientData, data, len, final) + ClientData clientData; + char *data; + int len; + int final; +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + TclXMLlibxml2Info *info = (TclXMLlibxml2Info *) clientData; + Tcl_Obj *nameObj, *nsObj, *nsdeclObj, *valueObj, *attrsObj, *errObj, *baseuriObj, *sysidObj, *extidObj; + const char *baseuri, *encoding, *name, *ns, *value; + xmlChar **preservens = NULL; + int ret, result = TCL_OK, i, listlen, options = 0, empty; + + /* not used... at present (see case XML_READER_TYPE_DOCUMENT_TYPE) + xmlNodePtr nodePtr; + */ + xmlEntityPtr entityPtr = NULL; + + if (final == 0) { + Tcl_SetResult(info->interp, "partial input not yet supported", NULL); + return TCL_ERROR; + } + + if (info->preserve && info->preservens) { + if (Tcl_ListObjLength(info->interp, info->preservens, &listlen) != TCL_OK) { + return TCL_ERROR; + } + preservens = (xmlChar **) Tcl_Alloc(len * sizeof(xmlChar *) + 1); + for (i = 0; i < listlen; i++) { + Tcl_Obj *objPtr; + const char *str; + int strlen; + + if (Tcl_ListObjIndex(info->interp, info->preservens, i, &objPtr) != TCL_OK) { + Tcl_Free((char *) preservens); + return TCL_ERROR; + } + str = Tcl_GetStringFromObj(objPtr, &strlen); + preservens[i] = xmlCharStrndup(str, strlen); + } + preservens[listlen] = NULL; + } + if (info->xmlinfo->base) { + baseuri = Tcl_GetStringFromObj(info->xmlinfo->base, NULL); + } else { + baseuri = NULL; + } + if (info->xmlinfo->encoding) { + encoding = Tcl_GetStringFromObj(info->xmlinfo->encoding, NULL); + if (strcmp(encoding, "unknown") == 0) { + encoding = NULL; + } + } else { + encoding = "utf-8"; + } + + TclXML_libxml2_ResetError(info->interp); + + options |= XML_PARSE_NOCDATA; + + tsdPtr->current = info; + + Tcl_MutexLock(&libxml2); + + if (info->xmlinfo->expandinternalentities) { + options |= XML_PARSE_NOENT; + xmlSubstituteEntitiesDefault(1); + } else { + xmlSubstituteEntitiesDefault(0); + } + if (info->xmlinfo->nowhitespace) { + options |= XML_PARSE_NOBLANKS; + } + + if (xmlReaderNewMemory(info->reader, + data, len, + baseuri, + encoding, + options) != 0) { + Tcl_MutexUnlock(&libxml2); + if (preservens) { + int i; + for (i = 0; preservens[i]; i++) { + xmlFree(preservens[i]); + } + Tcl_Free((char *) preservens); + } + tsdPtr->current = NULL; + Tcl_SetResult(info->interp, "unable to prepare parser", NULL); + return TCL_ERROR; + } + + if (info->preserve) { + int preserveret; + preserveret = xmlTextReaderPreservePattern(info->reader, + (const xmlChar *) Tcl_GetStringFromObj(info->preserve, NULL), + (const xmlChar **) preservens); + if (preserveret < 0) { + Tcl_MutexUnlock(&libxml2); + if (preservens) { + int i; + for (i = 0; preservens[i]; i++) { + xmlFree(preservens[i]); + } + Tcl_Free((char *) preservens); + } + tsdPtr->current = NULL; + Tcl_ResetResult(info->interp); + Tcl_AppendResult(info->interp, + "preparation for parsing failed: unable to preserve pattern \"", + Tcl_GetStringFromObj(info->preserve, NULL), + "\"", + NULL); + return TCL_ERROR; + } + } + + for (ret = xmlTextReaderRead(info->reader); + ret == 1; + ret = xmlTextReaderRead(info->reader)) { + result = TCL_OK; + switch (xmlTextReaderNodeType(info->reader)) { + case XML_READER_TYPE_ELEMENT: + name = (const char *) xmlTextReaderConstLocalName(info->reader); + ns = (const char *) xmlTextReaderConstNamespaceUri(info->reader); + Tcl_MutexUnlock(&libxml2); + if (name != NULL) { + nameObj = Tcl_NewStringObj(name, -1); + } else { + nameObj = Tcl_NewObj(); + } + Tcl_IncrRefCount(nameObj); + if (ns != NULL) { + nsObj = Tcl_NewStringObj(ns, -1); + } else { + nsObj = Tcl_NewObj(); + } + Tcl_IncrRefCount(nsObj); + + attrsObj = Tcl_NewObj(); + Tcl_IncrRefCount(attrsObj); + Tcl_MutexLock(&libxml2); + if (xmlTextReaderHasAttributes(info->reader)) { + if (xmlTextReaderMoveToFirstAttribute(info->reader) == 1) { + Tcl_Obj *itemObj; + + itemObj = Tcl_NewObj(); + Tcl_SetStringObj(itemObj, (CONST char *) xmlTextReaderConstLocalName(info->reader), -1); + Tcl_ListObjAppendElement(info->interp, attrsObj, itemObj); + itemObj = Tcl_NewStringObj((CONST char *) xmlTextReaderConstValue(info->reader), -1); + Tcl_ListObjAppendElement(info->interp, attrsObj, itemObj); + + while (xmlTextReaderMoveToNextAttribute(info->reader) == 1) { + itemObj = Tcl_NewStringObj((CONST char *) xmlTextReaderConstLocalName(info->reader), -1); + Tcl_ListObjAppendElement(info->interp, attrsObj, itemObj); + itemObj = Tcl_NewStringObj((CONST char *) xmlTextReaderConstValue(info->reader), -1); + Tcl_ListObjAppendElement(info->interp, attrsObj, itemObj); + } + } + } + empty = xmlTextReaderIsEmptyElement(info->reader); + Tcl_MutexUnlock(&libxml2); + + nsdeclObj = Tcl_NewObj(); + Tcl_IncrRefCount(nsdeclObj); + + TclXML_ElementStartHandler(info->xmlinfo, + nameObj, + nsObj, + attrsObj, nsdeclObj); + + Tcl_DecrRefCount(nsdeclObj); + + if (empty) { + TclXML_ElementEndHandler(info->xmlinfo, + nameObj); + } + + Tcl_DecrRefCount(nameObj); + Tcl_DecrRefCount(nsObj); + Tcl_DecrRefCount(attrsObj); + break; + + case XML_READER_TYPE_END_ELEMENT: + name = (const char *) xmlTextReaderConstLocalName(info->reader); + ns = (const char *) xmlTextReaderConstNamespaceUri(info->reader); + Tcl_MutexUnlock(&libxml2); + + if (name != NULL) { + nameObj = Tcl_NewStringObj(name, -1); + } else { + nameObj = Tcl_NewObj(); + } + Tcl_IncrRefCount(nameObj); + if (ns != NULL) { + nsObj = Tcl_NewStringObj(ns, -1); + } else { + nsObj = Tcl_NewObj(); + } + Tcl_IncrRefCount(nsObj); + + TclXML_ElementEndHandler(info->xmlinfo, + nameObj); + + Tcl_DecrRefCount(nameObj); + Tcl_DecrRefCount(nsObj); + break; + + case XML_READER_TYPE_TEXT: + case XML_READER_TYPE_CDATA: + case XML_READER_TYPE_WHITESPACE: + case XML_READER_TYPE_SIGNIFICANT_WHITESPACE: + value = (const char *) xmlTextReaderConstValue(info->reader); + Tcl_MutexUnlock(&libxml2); + if (value != NULL) { + valueObj = Tcl_NewStringObj(value, -1); + } else { + valueObj = Tcl_NewObj(); + } + Tcl_IncrRefCount(valueObj); + + TclXML_CharacterDataHandler(info->xmlinfo, + valueObj); + + Tcl_DecrRefCount(valueObj); + break; + + case XML_READER_TYPE_COMMENT: + value = (const char *) xmlTextReaderConstValue(info->reader); + Tcl_MutexUnlock(&libxml2); + if (value != NULL) { + valueObj = Tcl_NewStringObj(value, -1); + } else { + valueObj = Tcl_NewObj(); + } + + TclXML_CommentHandler(info->xmlinfo, + valueObj); + break; + + case XML_READER_TYPE_PROCESSING_INSTRUCTION: + name = (const char *) xmlTextReaderConstName(info->reader); + value = (const char *) xmlTextReaderConstValue(info->reader); + Tcl_MutexUnlock(&libxml2); + if (name != NULL) { + nameObj = Tcl_NewStringObj(name, -1); + } else { + nameObj = Tcl_NewObj(); + } + if (value != NULL) { + valueObj = Tcl_NewStringObj(value, -1); + } else { + valueObj = Tcl_NewObj(); + } + + TclXML_ProcessingInstructionHandler(info->xmlinfo, + nameObj, + valueObj); + break; + + case XML_READER_TYPE_ENTITY_REFERENCE: + name = (const char *) xmlTextReaderConstName(info->reader); + baseuri = (const char *) xmlTextReaderConstBaseUri(info->reader); + entityPtr = xmlGetDocEntity(xmlTextReaderCurrentDoc(info->reader), + (const xmlChar *) name); + Tcl_MutexUnlock(&libxml2); + + nameObj = Tcl_NewStringObj(name, -1); + Tcl_IncrRefCount(nameObj); + baseuriObj = Tcl_NewStringObj(baseuri, -1); + Tcl_IncrRefCount(baseuriObj); + sysidObj = Tcl_NewStringObj((CONST char *) entityPtr->SystemID, -1); + Tcl_IncrRefCount(sysidObj); + extidObj = Tcl_NewStringObj((CONST char *) entityPtr->ExternalID, -1); + Tcl_IncrRefCount(extidObj); + + result = TclXML_ExternalEntityRefHandler(info->xmlinfo, + nameObj, + baseuriObj, + sysidObj, + extidObj); + + Tcl_MutexLock(&libxml2); + + Tcl_DecrRefCount(nameObj); + Tcl_DecrRefCount(baseuriObj); + Tcl_DecrRefCount(sysidObj); + Tcl_DecrRefCount(extidObj); + + if (result == TCL_ERROR || result == TCL_BREAK) { + Tcl_MutexUnlock(&libxml2); + xmlTextReaderClose(info->reader); + break; + } + Tcl_MutexUnlock(&libxml2); + + break; + + case XML_READER_TYPE_DOCUMENT_TYPE: + /* these are not used... at present + name = xmlTextReaderName(info->reader); + nodePtr = xmlTextReaderCurrentNode(info->reader); + */ + Tcl_MutexUnlock(&libxml2); + + default: + break; + } + Tcl_MutexLock(&libxml2); + } + + Tcl_MutexUnlock(&libxml2); + + if (preservens) { + int i; + for (i = 0; preservens[i]; i++) { + xmlFree(preservens[i]); + } + Tcl_Free((char *) preservens); + } + + if (ret != 0 || result != TCL_OK) { + errObj = TclXML_libxml2_GetErrorObj(info->interp); + if (errObj) { + Tcl_SetObjResult(info->interp, errObj); + } else { + Tcl_SetResult(info->interp, "parsing error", NULL); + } + tsdPtr->current = NULL; + + return TCL_ERROR; + } + + info->docObjPtr = TclXML_libxml2_CreateObjFromDoc(xmlTextReaderCurrentDoc(info->reader)); + + TclXML_libxml2_DocKeep(info->docObjPtr, info->keep); + + /* TODO: errObjPtr may contain warnings, flush them through */ + + tsdPtr->current = NULL; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------------- + * + * TclXMLlibxml2Configure -- + * + * Set options for the parser. + * + * Results: + * None. + * + * Side effects: + * None (there are no options to set). + * + *---------------------------------------------------------------------------- + */ + +static int +TclXMLlibxml2Configure(clientData, optionPtr, valuePtr) + ClientData clientData; + Tcl_Obj *CONST optionPtr; + Tcl_Obj *CONST valuePtr; +{ + TclXMLlibxml2Info *info = (TclXMLlibxml2Info *) clientData; + int option, len; + char *value; + CONST84 char *Options[] = { + "-keep", + "-retainpath", + "-retainpathns", + NULL + }; + enum Options { + OPTION_KEEP, + OPTION_RETAINPATH, + OPTION_RETAINPATHNS + }; + CONST84 char *KeepOptions[] = { + "normal", + "implicit", + NULL + }; + enum KeepOptions { + OPTION_KEEP_NORMAL, + OPTION_KEEP_IMPLICIT + }; + + if (Tcl_GetIndexFromObj(info->interp, optionPtr, Options, + "option", 0, &option) != TCL_OK) { + /* + * Just ignore options we don't understand + */ + return TCL_OK; + } + + switch ((enum Options) option) { + case OPTION_KEEP: + + value = Tcl_GetStringFromObj(valuePtr, &len); + if (len == 0) { + info->keep = TCLXML_LIBXML2_DOCUMENT_KEEP; + if (info->docObjPtr) { + TclXML_libxml2_DocKeep(info->docObjPtr, TCLXML_LIBXML2_DOCUMENT_KEEP); + return TCL_BREAK; + } + } else { + if (Tcl_GetIndexFromObj(info->interp, valuePtr, KeepOptions, + "value", 0, &option) != TCL_OK) { + return TCL_ERROR; + } + switch ((enum KeepOptions) option) { + case OPTION_KEEP_NORMAL: + info->keep = TCLXML_LIBXML2_DOCUMENT_KEEP; + if (info->docObjPtr) { + TclXML_libxml2_DocKeep(info->docObjPtr, TCLXML_LIBXML2_DOCUMENT_KEEP); + } + return TCL_BREAK; + + case OPTION_KEEP_IMPLICIT: + info->keep = TCLXML_LIBXML2_DOCUMENT_IMPLICIT; + if (info->docObjPtr) { + TclXML_libxml2_DocKeep(info->docObjPtr, TCLXML_LIBXML2_DOCUMENT_IMPLICIT); + } + return TCL_BREAK; + + default: + Tcl_SetResult(info->interp, "bad value", NULL); + return TCL_ERROR; + } + } + + break; + + case OPTION_RETAINPATH: + if (info->preserve) { + Tcl_DecrRefCount(info->preserve); + } + info->preserve = valuePtr; + Tcl_IncrRefCount(valuePtr); + return TCL_BREAK; + + case OPTION_RETAINPATHNS: + if (info->preservens) { + Tcl_DecrRefCount(info->preservens); + } + info->preservens = valuePtr; + Tcl_IncrRefCount(valuePtr); + return TCL_BREAK; + + default: + + Tcl_SetResult(info->interp, "no such option", NULL); + return TCL_ERROR; + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------------- + * + * TclXMLlibxml2Get -- + * + * Retrieve data from the parser. + * + * Results: + * Depends on argument. + * + * Side effects: + * May create Tcl object. + * + *---------------------------------------------------------------------------- + */ + +static int +TclXMLlibxml2Get(clientData, objc, objv) + ClientData clientData; + int objc; + Tcl_Obj *CONST objv[]; +{ + TclXMLlibxml2Info *info = (TclXMLlibxml2Info *) clientData; + CONST84 char *methods[] = { + "document", + NULL + }; + enum methods { + TCLXML_LIBXML2_GET_DOCUMENT + }; + int option; + + if (objc != 1) { + Tcl_WrongNumArgs(info->interp, 0, objv, "method"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(info->interp, objv[0], methods, + "method", 0, &option) != TCL_OK) { + return TCL_ERROR; + } + + switch ((enum methods) option) { + case TCLXML_LIBXML2_GET_DOCUMENT: + if (info->docObjPtr) { + Tcl_SetObjResult(info->interp, info->docObjPtr); + } + + break; + + default: + Tcl_SetResult(info->interp, "unknown method", NULL); + return TCL_ERROR; + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------------- + * + * TclXMLlibxml2ExternalEntityLoader -- + * + * Retrieve an external entity, allowing interposing by the application. + * + * Results: + * External entity parsed. + * + * Side effects: + * Depends on application callback. + * + *---------------------------------------------------------------------------- + */ + +static xmlParserInputPtr +Result2ParserInput(interp, ctxt, URL) + Tcl_Interp *interp; + xmlParserCtxtPtr ctxt; + const char *URL; +{ + xmlParserInputPtr inputPtr = NULL; + + /* build our own xmlParserInput from returned data */ + inputPtr = xmlNewStringInputStream(ctxt, (const xmlChar *) Tcl_GetStringFromObj(Tcl_GetObjResult(interp), NULL)); + if (inputPtr == NULL) { + Tcl_SetResult(interp, "unable to create input stream", NULL); + Tcl_BackgroundError(interp); + return NULL; + } + inputPtr->filename = (char *) xmlCanonicPath((const xmlChar *) URL); + + return inputPtr; +} + +static xmlParserInputPtr +TclXMLlibxml2ExternalEntityLoader(URL, ID, ctxt) + const char *URL; + const char *ID; + xmlParserCtxtPtr ctxt; +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + TclXMLlibxml2Info *info; + Tcl_Interp *interp; + int result; + + info = tsdPtr->current; + + if (info) { + result = TclXML_ExternalEntityRefHandler(info->xmlinfo, NULL, NULL, + Tcl_NewStringObj(URL, -1), + Tcl_NewStringObj(ID, -1)); + interp = info->interp; + } else { + result = TclXML_ExternalEntityRefHandler(NULL, NULL, NULL, + Tcl_NewStringObj(URL, -1), + Tcl_NewStringObj(ID, -1)); + interp = tsdPtr->interp; + } + + switch (result) { + case TCL_OK: + return Result2ParserInput(interp, ctxt, URL); + + case TCL_BREAK: + return NULL; + + case TCL_CONTINUE: + break; + + case TCL_ERROR: + case TCL_RETURN: + default: + Tcl_BackgroundError(interp); + return NULL; + } + + if (Tcl_IsSafe(interp)) { + return NULL; + } else { + return (tsdPtr->defaultLoader)(URL, ID, ctxt); + } +} |