/* * tclexpat.c -- * * TclXML driver for James Clark's expat XML parser * * Copyright (c) 1998-2003 Steve Ball, Zveno Pty Ltd * * Zveno Pty Ltd makes this software and associated documentation * available free of charge for any purpose. You may make copies * of the software but you must include all of this notice on any copy. * * Zveno Pty Ltd does not warrant that this software is error free * or fit for any purpose. Zveno Pty Ltd disclaims any liability for * all claims, expenses, losses, damages and costs any user may incur * as a result of using, copying or modifying the software. * * $Id: tclexpat.c,v 1.15 2003/02/25 04:09:00 balls Exp $ * */ #include #include "tclxml.h" #include #undef TCL_STORAGE_CLASS #define TCL_STORAGE_CLASS DLLEXPORT /* * The structure below is used to refer to an expat parser object. */ typedef struct TclExpatInfo { XML_Parser parser; /* The expat parser structure */ Tcl_Interp *interp; /* Interpreter for this instance */ TclXML_Info *xmlinfo; /* Generic data structure */ } TclExpatInfo; /* * Prototypes for procedures defined later in this file: */ static ClientData TclExpatCreate _ANSI_ARGS_((Tcl_Interp *interp, TclXML_Info *xmlinfo)); static ClientData TclExpatCreateEntityParser _ANSI_ARGS_(( Tcl_Interp *interp, ClientData clientData)); static int TclExpatDelete _ANSI_ARGS_((ClientData clientData)); static int TclExpatParse _ANSI_ARGS_((ClientData clientData, char *data, int len, int final)); static int TclExpatConfigure _ANSI_ARGS_((ClientData clientdata, int objc, Tcl_Obj *CONST objv[])); static int TclExpatCget _ANSI_ARGS_((ClientData clientData, int objc, Tcl_Obj *CONST objv[])); static int TclExpatGet _ANSI_ARGS_((ClientData clientData, int objc, Tcl_Obj *CONST objv[])); static void TclExpatElementStartHandler _ANSI_ARGS_(( void *userData, const XML_Char *name, const XML_Char **atts)); static void TclExpatElementEndHandler _ANSI_ARGS_(( void *userData, const XML_Char *name)); static void TclExpatCharacterDataHandler _ANSI_ARGS_(( void *userData, const XML_Char *s, int len)); static void TclExpatProcessingInstructionHandler _ANSI_ARGS_(( void *userData, const XML_Char *target, const XML_Char *data)); static void TclExpatDefaultHandler _ANSI_ARGS_(( void *userData, const XML_Char *s, int len)); static void TclExpatUnparsedDeclHandler _ANSI_ARGS_(( void *userData, const XML_Char *entityName, const XML_Char *base, const XML_Char *systemId, const XML_Char *publicId, const XML_Char *notationName)); static void TclExpatNotationDeclHandler _ANSI_ARGS_(( void *userData, const XML_Char *notationName, const XML_Char *base, const XML_Char *systemId, const XML_Char *publicId)); static void TclExpatCommentHandler _ANSI_ARGS_(( void *userData, const XML_Char *data)); static int TclExpatNotStandaloneHandler _ANSI_ARGS_(( void *userData)); static void TclExpatStartCdataSectionHandler _ANSI_ARGS_(( void *userData)); static void TclExpatEndCdataSectionHandler _ANSI_ARGS_(( void *userData)); static void TclExpatElementDeclHandler _ANSI_ARGS_(( void *userData, const XML_Char *name, XML_Char ***contentspec)); static void TclExpatAttlistDeclHandler _ANSI_ARGS_(( void *userData, const XML_Char *name, XML_Char ***attributes)); static void TclExpatStartDoctypeDeclHandler _ANSI_ARGS_(( void *userData, const XML_Char *doctypeName)); static void TclExpatEndDoctypeDeclHandler _ANSI_ARGS_(( void *userData)); static int TclExpatExternalEntityRefHandler _ANSI_ARGS_(( XML_Parser parser, const XML_Char *context, const XML_Char *base, const XML_Char *systemId, const XML_Char *publicId)); static int TclExpatUnknownEncodingHandler _ANSI_ARGS_(( void *encodingHandlerData, const XML_Char *name, XML_Encoding *info )); EXTERN int Tclexpat_Init _ANSI_ARGS_((Tcl_Interp *interp)); /* *---------------------------------------------------------------------------- * * Tclexpat_Init -- * * Initialisation routine for loadable module * * Results: * None. * * Side effects: * Registers xerces XML parser. * *---------------------------------------------------------------------------- */ int Tclexpat_Init (interp) Tcl_Interp *interp; /* Interpreter to initialise. */ { 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("expat", -1); classinfo->create = TclExpatCreate; classinfo->createCmd = NULL; classinfo->createEntity = TclExpatCreateEntityParser; classinfo->createEntityCmd = NULL; classinfo->parse = TclExpatParse; classinfo->parseCmd = NULL; classinfo->configure = TclExpatConfigure; classinfo->configureCmd = NULL; classinfo->get = TclExpatGet; classinfo->getCmd = NULL; classinfo->destroy = TclExpatDelete; classinfo->destroyCmd = NULL; classinfo->reset = NULL; classinfo->resetCmd = NULL; if (TclXML_RegisterXMLParser(interp, classinfo) != TCL_OK) { Tcl_SetResult(interp, "unable to register parser", NULL); return TCL_ERROR; } if (Tcl_PkgProvide(interp, "xml::expat", TCLXML_VERSION) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------------- * * TclExpatCreate -- * * Create an expat parser instance. * * Results: * Standard Tcl result. * * Side effects: * This creates an expat parser. * *---------------------------------------------------------------------------- */ static ClientData TclExpatCreate(interp, xmlinfo) Tcl_Interp *interp; TclXML_Info *xmlinfo; { TclExpatInfo *expat; /* * Create the data structures for this parser. */ if (!(expat = (TclExpatInfo *) ckalloc(sizeof(TclExpatInfo)))) { ckfree((char *)expat); Tcl_SetResult(interp, "unable to create parser", NULL); return NULL; } expat->interp = interp; expat->xmlinfo = xmlinfo; if (!(expat->parser = XML_ParserCreate(NULL))) { Tcl_SetResult(interp, "unable to create expat parser", NULL); ckfree((char *)expat); return NULL; } /* * Set all handlers for the parser. */ XML_SetElementHandler(expat->parser, TclExpatElementStartHandler, TclExpatElementEndHandler); XML_SetCharacterDataHandler(expat->parser, TclExpatCharacterDataHandler); XML_SetProcessingInstructionHandler(expat->parser, TclExpatProcessingInstructionHandler); XML_SetDefaultHandler(expat->parser, TclExpatDefaultHandler); XML_SetUnparsedEntityDeclHandler(expat->parser, TclExpatUnparsedDeclHandler); XML_SetNotationDeclHandler(expat->parser, TclExpatNotationDeclHandler); XML_SetExternalEntityRefHandler(expat->parser, TclExpatExternalEntityRefHandler); XML_SetUnknownEncodingHandler(expat->parser, TclExpatUnknownEncodingHandler, (void *) xmlinfo); /* Added by ericm@scriptics.com, 1999.6.25 */ /* Tell expat to use the TclExpat comment handler */ XML_SetCommentHandler(expat->parser, TclExpatCommentHandler); /* Tell expat to use the TclExpat "not standalone" handler */ XML_SetNotStandaloneHandler(expat->parser, TclExpatNotStandaloneHandler); #ifdef TCLXML_CDATASECTIONS /* Tell expat to use the TclExpat CdataSection handlers */ XML_SetCdataSectionHandler(expat->parser, TclExpatStartCdataSectionHandler, TclExpatEndCdataSectionHandler); #endif /* TCLXML_CDATASECTIONS */ /* Tell expat to use the TclExpat Element decl handler */ XML_SetElementDeclHandler(expat->parser, TclExpatElementDeclHandler); /* Tell expat to use the TclExpat Attlist decl handler */ XML_SetAttlistDeclHandler(expat->parser, TclExpatAttlistDeclHandler); /* Tell expat to use the TclExpat DOCTYPE decl handlers */ XML_SetDoctypeDeclHandler(expat->parser, TclExpatStartDoctypeDeclHandler, TclExpatEndDoctypeDeclHandler); XML_SetUserData(expat->parser, (void *) expat); return (ClientData) expat; } /* *---------------------------------------------------------------------- * * TclExpatCreateEntity -- * * Create an expat entity parser, based on the original * parser referred to by parent. * * Results: * New external entity parser created and initialized. * * Side effects: * The TclExpatInfo struct pointed to by expat is modified. * *---------------------------------------------------------------------- */ static ClientData TclExpatCreateEntityParser(interp, clientData) Tcl_Interp *interp; ClientData clientData; { TclExpatInfo *parent = (TclExpatInfo *) clientData; TclExpatInfo *expat; if (!(expat = (TclExpatInfo *) ckalloc(sizeof(TclExpatInfo)))) { ckfree((char *)expat); Tcl_SetResult(interp, "unable to create parser", NULL); return NULL; } expat->interp = parent->interp; expat->xmlinfo = parent->xmlinfo; if (!(expat->parser = XML_ExternalEntityParserCreate(parent->parser, (XML_Char *) Tcl_GetUnicode(parent->xmlinfo->context), NULL))) { Tcl_SetResult(interp, "unable to create expat external entity parser", NULL); return NULL; } XML_SetUserData(expat->parser, (void *) expat); return (ClientData) expat; } /* *---------------------------------------------------------------------------- * * TclExpatDelete -- * * Destroy the expat parser structure. * * Results: * None. * * Side effects: * Frees any memory allocated for the XML parser. * *---------------------------------------------------------------------------- */ static int TclExpatDelete(clientData) ClientData clientData; { TclExpatInfo *expat = (TclExpatInfo *) clientData; XML_ParserFree(expat->parser); ckfree((char *)expat); return TCL_OK; } /* *---------------------------------------------------------------------------- * * TclExpatParse -- * * Wrapper to invoke expat parser and check return result. * * Results: * TCL_OK if no errors, TCL_ERROR otherwise. * * Side effects: * Sets interpreter result as appropriate. * *---------------------------------------------------------------------------- */ static int TclExpatParse(clientData, data, len, final) ClientData clientData; char *data; int len; { TclExpatInfo *expat = (TclExpatInfo *) clientData; int result; char s[255]; result = XML_Parse(expat->parser, data, len, final); if (!result) { Tcl_ResetResult(expat->interp); sprintf(s, "%d", XML_GetCurrentLineNumber(expat->parser)); Tcl_AppendResult(expat->interp, "error \"", XML_ErrorString(XML_GetErrorCode(expat->parser)), "\" at line ", s, " character ", NULL); sprintf(s, "%d", XML_GetCurrentColumnNumber(expat->parser)); Tcl_AppendResult(expat->interp, s, NULL); return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------------- * * TclExpatConfigure -- * * Implements instance command for expat class objects. * * Results: * Depends on the method. * * Side effects: * Depends on the method. * *---------------------------------------------------------------------------- */ static int TclExpatConfigure (clientData, objc, objv) ClientData clientData; int objc; Tcl_Obj *CONST objv[]; { TclExpatInfo *expat = (TclExpatInfo *) clientData; static CONST84 char *switches[] = { "-baseurl", "-defaultexpandinternalentities", "-paramentityparsing", (char *) NULL }; enum switches { EXPAT_BASE, EXPAT_DEFAULTEXPANDINTERNALENTITIES, EXPAT_PARAMENTITYPARSING, }; static CONST84 char *paramEntityParsingValues[] = { "always", "never", "notstandalone" }; enum paramEntityParsingValues { EXPAT_PARAMENTITYPARSINGALWAYS, EXPAT_PARAMENTITYPARSINGNEVER, EXPAT_PARAMENTITYPARSINGNOTSTANDALONE }; int index, value, bool; Tcl_Obj *CONST *objPtr = objv; while (objc > 1) { if (Tcl_GetIndexFromObj(expat->interp, objPtr[0], switches, "switch", 0, &index) != TCL_OK) { Tcl_ResetResult(expat->interp); return TCL_OK; } switch ((enum switches) index) { case EXPAT_BASE: /* -base */ if (XML_SetBase(expat->parser, (const XML_Char*)Tcl_GetString(objPtr[1])) == 0) { Tcl_SetResult(expat->interp, "unable to set base URL", NULL); return TCL_ERROR; } break; case EXPAT_DEFAULTEXPANDINTERNALENTITIES: /* -defaultexpandinternalentities */ /* ericm@scriptics */ if (Tcl_GetBooleanFromObj(expat->interp, objPtr[1], &bool) != TCL_OK) { return TCL_ERROR; } XML_SetDefaultExpandInternalEntities(expat->parser, bool); break; case EXPAT_PARAMENTITYPARSING: /* -paramentityparsing */ /* ericm@scriptics */ if (Tcl_GetIndexFromObj(expat->interp, objPtr[1], paramEntityParsingValues, "value", 0, &value) != TCL_OK) { return TCL_ERROR; } switch ((enum paramEntityParsingValues) value) { case EXPAT_PARAMENTITYPARSINGALWAYS: XML_SetParamEntityParsing(expat->parser, XML_PARAM_ENTITY_PARSING_ALWAYS); break; case EXPAT_PARAMENTITYPARSINGNEVER: XML_SetParamEntityParsing(expat->parser, XML_PARAM_ENTITY_PARSING_NEVER); break; case EXPAT_PARAMENTITYPARSINGNOTSTANDALONE: XML_SetParamEntityParsing(expat->parser, XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE); break; } break; default: return TCL_OK; break; } objPtr += 2; objc -= 2; } return TCL_OK; } /* *---------------------------------------------------------------------------- * * TclExpatGet -- * * Returns runtime parser information, depending on option * ericm@scriptics.com, 1999.6.28 * * Results: * Option value. * * Side effects: * None. * *---------------------------------------------------------------------------- */ static int TclExpatGet (clientData, objc, objv) ClientData clientData; int objc; Tcl_Obj *CONST objv[]; { TclExpatInfo *expat = (TclExpatInfo *) clientData; static CONST84 char *switches[] = { "-specifiedattributecount", "-currentbytecount", "-currentlinenumber", "-currentcolumnnumber", "-currentbyteindex", (char *) NULL }; enum switches { EXPAT_SPECIFIEDATTRCOUNT, EXPAT_CURRENTBYTECOUNT, EXPAT_CURRENTLINENUMBER, EXPAT_CURRENTCOLUMNNUMBER, EXPAT_CURRENTBYTEINDEX }; int index, doParse = 0; Tcl_Obj *resultPtr; if (objc > 1) { Tcl_SetResult(expat->interp, "Only one value may be requested at a time", TCL_STATIC); return TCL_ERROR; } if (Tcl_GetIndexFromObj(expat->interp, objv[0], switches, "switch", 0, &index) != TCL_OK) { return TCL_ERROR; } resultPtr = Tcl_GetObjResult(expat->interp); switch ((enum switches) index) { case EXPAT_SPECIFIEDATTRCOUNT: Tcl_SetIntObj(resultPtr, XML_GetSpecifiedAttributeCount(expat->parser)); return TCL_OK; break; case EXPAT_CURRENTBYTECOUNT: Tcl_SetIntObj(resultPtr, XML_GetCurrentByteCount(expat->parser)); return TCL_OK; break; case EXPAT_CURRENTLINENUMBER: Tcl_SetIntObj(resultPtr, XML_GetCurrentLineNumber(expat->parser)); return TCL_OK; break; case EXPAT_CURRENTCOLUMNNUMBER: Tcl_SetIntObj(resultPtr, XML_GetCurrentColumnNumber(expat->parser)); return TCL_OK; break; case EXPAT_CURRENTBYTEINDEX: Tcl_SetLongObj(resultPtr, XML_GetCurrentByteIndex(expat->parser)); return TCL_OK; break; default: return TCL_ERROR; break; } return TCL_ERROR; } /* *---------------------------------------------------------------------------- * * TclExpat*Handler -- * * Called by expat for various document features. * * Results: * None. * * Side Effects: * Event passed to TclXML generic layer. * *---------------------------------------------------------------------------- */ static void TclExpatElementStartHandler(userdata, name, atts) void *userdata; const XML_Char *name; const XML_Char **atts; { TclExpatInfo *expat = (TclExpatInfo *) userdata; Tcl_Obj *attListObj; int count; attListObj = Tcl_NewListObj(0, NULL); for (count = 0; atts[count]; count += 2) { Tcl_ListObjAppendElement(NULL, attListObj, Tcl_NewStringObj((XML_Char*)atts[count], -1)); Tcl_ListObjAppendElement(NULL, attListObj, Tcl_NewStringObj((XML_Char*)atts[count + 1], -1)); } TclXML_ElementStartHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)name, -1), attListObj); } static void TclExpatElementEndHandler(userdata, name) void *userdata; const XML_Char *name; { TclExpatInfo *expat = (TclExpatInfo *) userdata; TclXML_ElementEndHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)name, -1)); } static void TclExpatCharacterDataHandler(userdata, s, len) void *userdata; const XML_Char *s; int len; { TclExpatInfo *expat = (TclExpatInfo *) userdata; TclXML_CharacterDataHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)s, len)); } static void TclExpatProcessingInstructionHandler(userdata, target, data) void *userdata; const XML_Char *target; const XML_Char *data; { TclExpatInfo *expat = (TclExpatInfo *) userdata; TclXML_ProcessingInstructionHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)target, -1), Tcl_NewStringObj((XML_Char*)data, -1)); } static void TclExpatDefaultHandler(userdata, s, len) void *userdata; const XML_Char *s; int len; { TclExpatInfo *expat = (TclExpatInfo *) userdata; TclXML_DefaultHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)s, len)); } static void TclExpatUnparsedDeclHandler(userdata, entityName, base, systemId, publicId, notationName) void *userdata; const XML_Char *entityName; const XML_Char *base; const XML_Char *systemId; const XML_Char *publicId; const XML_Char *notationName; { TclExpatInfo *expat = (TclExpatInfo *) userdata; TclXML_UnparsedDeclHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)entityName, -1), Tcl_NewStringObj((XML_Char*)base, -1), Tcl_NewStringObj((XML_Char*)systemId, -1), Tcl_NewStringObj((XML_Char*)publicId, -1), Tcl_NewStringObj((XML_Char*)notationName, -1) ); } static void TclExpatNotationDeclHandler(userdata, notationName, base, systemId, publicId) void *userdata; const XML_Char *notationName; const XML_Char *base; const XML_Char *systemId; const XML_Char *publicId; { TclExpatInfo *expat = (TclExpatInfo *) userdata; TclXML_NotationDeclHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)notationName, -1), Tcl_NewStringObj((XML_Char*)base, -1), Tcl_NewStringObj((XML_Char*)systemId, -1), Tcl_NewStringObj((XML_Char*)publicId, -1) ); } static void TclExpatCommentHandler(userdata, data) void *userdata; const XML_Char *data; { TclExpatInfo *expat = (TclExpatInfo *) userdata; TclXML_CommentHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)data, -1)); } static int TclExpatNotStandaloneHandler(userdata) void *userdata; { TclExpatInfo *expat = (TclExpatInfo *) userdata; return TclXML_NotStandaloneHandler(expat->xmlinfo); } static void TclExpatStartCdataSectionHandler(userdata) void *userdata; { TclExpatInfo *expat = (TclExpatInfo *) userdata; /* * TclXML makes no distinction between PCDATA and CDATA */ } static void TclExpatEndCdataSectionHandler(userdata) void *userdata; { TclExpatInfo *expat = (TclExpatInfo *) userdata; /* * TclXML makes no distinction between PCDATA and CDATA */ } /* *---------------------------------------------------------------------------- * * TclExpatExternalEntityRefHandler -- * * Called by expat for processing external entity references. * * Results: * None. * * Side Effects: * Callback script is invoked. * *---------------------------------------------------------------------------- */ static int TclExpatExternalEntityRefHandler(parser, context, base, systemId, publicId) XML_Parser parser; const XML_Char *context; const XML_Char *base; const XML_Char *systemId; const XML_Char *publicId; { TclExpatInfo *expat = (TclExpatInfo *) XML_GetUserData(parser); TclXML_ExternalEntityRefHandler( (ClientData) expat->xmlinfo, Tcl_NewStringObj((XML_Char*)context, -1), Tcl_NewStringObj((XML_Char*)base, -1), Tcl_NewStringObj((XML_Char*)systemId, -1), Tcl_NewStringObj((XML_Char*)publicId, -1)); return 1; } /* *---------------------------------------------------------------------------- * * TclExpatUnknownEncodingHandler -- * * Called by parser instance for processing a reference to a character in an * unknown encoding. * * Results: * None. * * Side Effects: * Callback script is invoked. * *---------------------------------------------------------------------------- */ static int TclExpatUnknownEncodingHandler(encodingHandlerData, name, info) void *encodingHandlerData; const XML_Char *name; XML_Encoding *info; { int result; result = TclXML_UnknownEncodingHandler( encodingHandlerData, Tcl_NewStringObj((XML_Char*)name, -1), (ClientData) info); return result; } static void TclExpatElementDeclHandler(userdata, name, contentspec) void *userdata; const XML_Char *name; XML_Char ***contentspec; { TclExpatInfo *expat = (TclExpatInfo *) userdata; Tcl_Obj *contentspecObj; int count; contentspecObj = Tcl_NewListObj(0, NULL); for (count = 0; contentspec[0][count] != NULL; count++) { Tcl_ListObjAppendElement(expat->interp, contentspecObj, Tcl_NewStringObj(contentspec[0][count], -1)); } TclXML_ElementDeclHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)name, -1), contentspecObj); } static void TclExpatAttlistDeclHandler(userdata, name, attributes) void *userdata; const XML_Char *name; XML_Char ***attributes; { TclExpatInfo *expat = (TclExpatInfo *) userdata; Tcl_Obj *attdefnObj; int count; attdefnObj = Tcl_NewListObj(0, NULL); for (count = 0; (*attributes)[count] != NULL; count++) { Tcl_ListObjAppendElement(expat->interp, attdefnObj, Tcl_NewStringObj(*attributes[count], -1)); } TclXML_AttlistDeclHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)name, -1), attdefnObj); } static void TclExpatStartDoctypeDeclHandler(userdata, name) void *userdata; const XML_Char *name; { TclExpatInfo *expat = (TclExpatInfo *) userdata; TclXML_StartDoctypeDeclHandler( expat->xmlinfo, Tcl_NewStringObj((XML_Char*)name, -1)); } static void TclExpatEndDoctypeDeclHandler(userdata) void *userdata; { TclExpatInfo *expat = (TclExpatInfo *) userdata; TclXML_EndDoctypeDeclHandler(expat->xmlinfo); }