/* * call-seq: * push(node) * * Append +node+ to the NodeSet. */ static VALUE push(VALUE self, VALUE rb_node) { xmlNodeSetPtr node_set; xmlNodePtr node; if(!rb_obj_is_kind_of(rb_node, cNokogiriXmlNode)) rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node"); Data_Get_Struct(self, xmlNodeSet, node_set); Data_Get_Struct(rb_node, xmlNode, node); xmlXPathNodeSetAdd(node_set, node); return self; }
/* * call-seq: * push(node) * * Append +node+ to the NodeSet. */ static VALUE push(VALUE self, VALUE rb_node) { nokogiriNodeSetTuple *tuple; xmlNodePtr node; if(!(rb_obj_is_kind_of(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of(rb_node, cNokogiriXmlNamespace))) rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace"); Data_Get_Struct(self, nokogiriNodeSetTuple, tuple); Data_Get_Struct(rb_node, xmlNode, node); xmlXPathNodeSetAdd(tuple->node_set, node); return self; }
/** * xmlSecNodeSetGetChildren: * @doc: the pointer to an XML document. * @parent: the pointer to parent XML node or NULL if we want to include all document nodes. * @withComments: the flag include comments or not. * @invert: the "invert" flag. * * Creates a new nodes set that contains: * - if @withComments is not 0 and @invert is 0: * all nodes in the @parent subtree; * - if @withComments is 0 and @invert is 0: * all nodes in the @parent subtree except comment nodes; * - if @withComments is not 0 and @invert not is 0: * all nodes in the @doc except nodes in the @parent subtree; * - if @withComments is 0 and @invert is 0: * all nodes in the @doc except nodes in the @parent subtree * and comment nodes. * * Returns pointer to the newly created #xmlSecNodeSet structure * or NULL if an error occurs. */ EXPORT_C xmlSecNodeSetPtr xmlSecNodeSetGetChildren(xmlDocPtr doc, const xmlNodePtr parent, int withComments, int invert) { xmlNodeSetPtr nodes; xmlSecNodeSetType type; xmlSecNodeSetPtr result = NULL; xmlSecAssert2(doc != NULL, NULL); nodes = xmlXPathNodeSetCreate(parent); if(nodes == NULL) { xmlSecError(XMLSEC_ERRORS_HERE, NULL, "xmlXPathNodeSetCreate", XMLSEC_ERRORS_R_XML_FAILED, XMLSEC_ERRORS_NO_MESSAGE); return(NULL); } /* if parent is NULL then we add all the doc children */ if(parent == NULL) { xmlNodePtr cur; for(cur = doc->children; cur != NULL; cur = cur->next) { if(withComments || (cur->type != XML_COMMENT_NODE)) { xmlXPathNodeSetAdd(nodes, cur); } } } if(withComments && invert) { type = xmlSecNodeSetTreeInvert; } else if(withComments && !invert) { type = xmlSecNodeSetTree; } else if(!withComments && invert) { type = xmlSecNodeSetTreeWithoutCommentsInvert; } else { /* if(!withComments && !invert) */ type = xmlSecNodeSetTreeWithoutComments; } result = xmlSecNodeSetCreate(doc, nodes, type); if ( !result ) { xmlXPathFreeNodeSet( nodes ); } return result; }
/** * 'insert' operation */ static void edInsert(xmlDocPtr doc, xmlNodeSetPtr nodes, const char *val, const char *name, XmlNodeType type, int mode) { int i; xmlXPathEmptyNodeSet(previous_insertion); for (i = 0; i < nodes->nodeNr; i++) { xmlNodePtr node; if (nodes->nodeTab[i] == (void*) doc && mode != 0) { fprintf(stderr, "The document node cannot have siblings.\n"); exit(EXIT_INTERNAL_ERROR); } /* update node */ if (type == XML_ATTR) { node = (xmlNodePtr) xmlNewProp(nodes->nodeTab[i], BAD_CAST name, BAD_CAST val); } else if (type == XML_ELEM) { node = xmlNewDocNode(doc, NULL /* TODO: NS */, BAD_CAST name, BAD_CAST val); if (mode > 0) xmlAddNextSibling(nodes->nodeTab[i], node); else if (mode < 0) xmlAddPrevSibling(nodes->nodeTab[i], node); else xmlAddChild(nodes->nodeTab[i], node); } else if (type == XML_TEXT) { node = xmlNewDocText(doc, BAD_CAST val); if (mode > 0) xmlAddNextSibling(nodes->nodeTab[i], node); else if (mode < 0) xmlAddPrevSibling(nodes->nodeTab[i], node); else xmlAddChild(nodes->nodeTab[i], node); } xmlXPathNodeSetAdd(previous_insertion, node); } }
xmlXPathObjectPtr rxml_xpath_from_value(VALUE value) { xmlXPathObjectPtr result = NULL; switch (TYPE(value)) { case T_TRUE: case T_FALSE: result = xmlXPathNewBoolean(RTEST(value)); break; case T_FIXNUM: case T_FLOAT: result = xmlXPathNewFloat(NUM2DBL(value)); break; case T_STRING: result = xmlXPathWrapString(xmlStrdup((const xmlChar *)StringValuePtr(value))); break; case T_NIL: result = xmlXPathNewNodeSet(NULL); break; case T_ARRAY: { long i, j; result = xmlXPathNewNodeSet(NULL); for (i = RARRAY_LEN(value); i > 0; i--) { xmlXPathObjectPtr obj = rxml_xpath_from_value(rb_ary_shift(value)); if ((obj->nodesetval != NULL) && (obj->nodesetval->nodeNr != 0)) { for (j = 0; j < obj->nodesetval->nodeNr; j++) { xmlXPathNodeSetAdd(result->nodesetval, obj->nodesetval->nodeTab[j]); } } } break; } default: rb_raise(rb_eTypeError, "can't convert object of type %s to XPath object", rb_obj_classname(value) ); } return result; }
/** * xsltInitCtxtKey: * @ctxt: an XSLT transformation context * @idoc: the document information (holds key values) * @keyDef: the key definition * * Computes the key tables this key and for the current input document. * * Returns: 0 on success, -1 on error */ int xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc, xsltKeyDefPtr keyDef) { int i, len, k; xmlNodeSetPtr matchList = NULL, keylist; xmlXPathObjectPtr matchRes = NULL, useRes = NULL; xmlChar *str = NULL; xsltKeyTablePtr table; xmlNodePtr oldInst, cur; xmlNodePtr oldContextNode; xsltDocumentPtr oldDocInfo; int oldXPPos, oldXPSize; xmlDocPtr oldXPDoc; int oldXPNsNr; xmlNsPtr *oldXPNamespaces; xmlXPathContextPtr xpctxt; #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel); #endif if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL)) return(-1); /* * Detect recursive keys */ if (ctxt->keyInitLevel > ctxt->nbKeys) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS, xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: key definition of %s is recursive\n", keyDef->name)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "Key definition for %s is recursive\n", keyDef->name); ctxt->state = XSLT_STATE_STOPPED; return(-1); } ctxt->keyInitLevel++; xpctxt = ctxt->xpathCtxt; idoc->nbKeysComputed++; /* * Save context state. */ oldInst = ctxt->inst; oldDocInfo = ctxt->document; oldContextNode = ctxt->node; oldXPDoc = xpctxt->doc; oldXPPos = xpctxt->proximityPosition; oldXPSize = xpctxt->contextSize; oldXPNsNr = xpctxt->nsNr; oldXPNamespaces = xpctxt->namespaces; /* * Set up contexts. */ ctxt->document = idoc; ctxt->node = (xmlNodePtr) idoc->doc; ctxt->inst = keyDef->inst; xpctxt->doc = idoc->doc; xpctxt->node = (xmlNodePtr) idoc->doc; /* TODO : clarify the use of namespaces in keys evaluation */ xpctxt->namespaces = keyDef->nsList; xpctxt->nsNr = keyDef->nsNr; /* * Evaluate the 'match' expression of the xsl:key. * TODO: The 'match' is a *pattern*. */ matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt); if (matchRes == NULL) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "Failed to evaluate the 'match' expression.\n"); ctxt->state = XSLT_STATE_STOPPED; goto error; } else { if (matchRes->type == XPATH_NODESET) { matchList = matchRes->nodesetval; #ifdef WITH_XSLT_DEBUG_KEYS if (matchList != NULL) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluates to %d nodes\n", keyDef->match, matchList->nodeNr)); #endif } else { /* * Is not a node set, but must be. */ #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s is not a node set\n", keyDef->match)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "The 'match' expression did not evaluate to a node set.\n"); ctxt->state = XSLT_STATE_STOPPED; goto error; } } if ((matchList == NULL) || (matchList->nodeNr <= 0)) goto exit; /** * Multiple key definitions for the same name are allowed, so * we must check if the key is already present for this doc */ table = (xsltKeyTablePtr) idoc->keys; while (table != NULL) { if (xmlStrEqual(table->name, keyDef->name) && (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) || ((keyDef->nameURI != NULL) && (table->nameURI != NULL) && (xmlStrEqual(table->nameURI, keyDef->nameURI))))) break; table = table->next; } /** * If the key was not previously defined, create it now and * chain it to the list of keys for the doc */ if (table == NULL) { table = xsltNewKeyTable(keyDef->name, keyDef->nameURI); if (table == NULL) goto error; table->next = idoc->keys; idoc->keys = table; } /* * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!) * "...the use attribute of the xsl:key element is evaluated with x as " the current node and with a node list containing just x as the * current node list" */ xpctxt->contextSize = 1; xpctxt->proximityPosition = 1; for (i = 0; i < matchList->nodeNr; i++) { cur = matchList->nodeTab[i]; if (! IS_XSLT_REAL_NODE(cur)) continue; xpctxt->node = cur; /* * Process the 'use' of the xsl:key. * SPEC XSLT 1.0: * "The use attribute is an expression specifying the values of * the key; the expression is evaluated once for each node that * matches the pattern." */ if (useRes != NULL) xmlXPathFreeObject(useRes); useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt); if (useRes == NULL) { xsltTransformError(ctxt, NULL, keyDef->inst, "Failed to evaluate the 'use' expression.\n"); ctxt->state = XSLT_STATE_STOPPED; break; } if (useRes->type == XPATH_NODESET) { if ((useRes->nodesetval != NULL) && (useRes->nodesetval->nodeNr != 0)) { len = useRes->nodesetval->nodeNr; str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]); } else { continue; } } else { len = 1; if (useRes->type == XPATH_STRING) { /* * Consume the string value. */ str = useRes->stringval; useRes->stringval = NULL; } else { str = xmlXPathCastToString(useRes); } } /* * Process all strings. */ k = 0; while (1) { if (str == NULL) goto next_string; #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str)); #endif keylist = xmlHashLookup(table->keys, str); if (keylist == NULL) { keylist = xmlXPathNodeSetCreate(cur); if (keylist == NULL) goto error; xmlHashAddEntry(table->keys, str, keylist); } else { /* * TODO: How do we know if this function failed? */ xmlXPathNodeSetAdd(keylist, cur); } switch (cur->type) { case XML_ELEMENT_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: cur->psvi = keyDef; break; case XML_ATTRIBUTE_NODE: ((xmlAttrPtr) cur)->psvi = keyDef; break; case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: ((xmlDocPtr) cur)->psvi = keyDef; break; default: break; } xmlFree(str); str = NULL; next_string: k++; if (k >= len) break; str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]); } } exit: error: ctxt->keyInitLevel--; /* * Restore context state. */ xpctxt->doc = oldXPDoc; xpctxt->nsNr = oldXPNsNr; xpctxt->namespaces = oldXPNamespaces; xpctxt->proximityPosition = oldXPPos; xpctxt->contextSize = oldXPSize; ctxt->node = oldContextNode; ctxt->document = oldDocInfo; ctxt->inst = oldInst; if (str) xmlFree(str); if (useRes != NULL) xmlXPathFreeObject(useRes); if (matchRes != NULL) xmlXPathFreeObject(matchRes); return(0); }
static lx_nodeset_t * js_rpc_get_reply (xmlXPathParserContext *ctxt, js_session_t *jsp) { lx_document_t *docp = js_rpc_get_document(jsp); if (docp == NULL) goto fail; lx_node_t *nop = lx_document_root(docp); if (nop == NULL) { jsio_trace("jsio: could not find document root"); goto fail; } if (trace_flag_is_set(trace_file, CS_TRC_RPC)) lx_trace_node(nop, "results of rpc"); if (!streq(xmlNodeName(nop), XMLRPC_APINAME)) { jsio_trace("jsio: could not find api tag"); goto fail; } /* * Create a Result Value Tree container, and register it with RVT garbage * collector. */ xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt); xmlDocPtr container = xsltCreateRVT(tctxt); xsltRegisterLocalRVT(tctxt, container); for (nop = lx_node_children(nop); nop; nop = lx_node_next(nop)) { if (streq(xmlNodeName(nop), XMLRPC_REPLY)) { lx_node_t *cop, *newp; lx_nodeset_t *setp = xmlXPathNodeSetCreate(NULL); if (setp == NULL) { jsio_trace("jsio: could not allocate set"); goto fail; } ext_jcs_fix_namespaces(nop); for (cop = lx_node_children(nop); cop; cop = lx_node_next(cop)) { if (cop->type == XML_TEXT_NODE && streq((const char *) cop->content, "\n")) continue; newp = xmlCopyNode(cop, 1); xmlXPathNodeSetAdd(setp, newp); /* * Attach the return nodes with RVT container, so that the * nodes will be freed when the container is freed. */ xmlAddChild((lx_node_t *) container, newp); } lx_document_free(docp); return setp; } } fail: jsio_trace("invalid reply to rpc"); if (docp) lx_document_free(docp); return NULL; }
static void exsltDynMapFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *str = NULL; xmlNodeSetPtr nodeset = NULL; xsltTransformContextPtr tctxt; xmlXPathCompExprPtr comp = NULL; xmlXPathObjectPtr ret = NULL; xmlDocPtr oldDoc, container = NULL; xmlNodePtr oldNode; int oldContextSize; int oldProximityPosition; int i, j; if (nargs != 2) { xmlXPathSetArityError(ctxt); return; } str = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } nodeset = xmlXPathPopNodeSet(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } if (str == NULL || !xmlStrlen(str) || !(comp = xmlXPathCompile(str))) { if (nodeset != NULL) xmlXPathFreeNodeSet(nodeset); if (str != NULL) xmlFree(str); valuePush(ctxt, xmlXPathNewNodeSet(NULL)); return; } ret = xmlXPathNewNodeSet(NULL); if (ret == NULL) { xsltGenericError(xsltGenericErrorContext, "exsltDynMapFunction: ret == NULL\n"); goto cleanup; } oldDoc = ctxt->context->doc; oldNode = ctxt->context->node; oldContextSize = ctxt->context->contextSize; oldProximityPosition = ctxt->context->proximityPosition; tctxt = xsltXPathGetTransformContext(ctxt); if (tctxt == NULL) { xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, "dyn:map : internal error tctxt == NULL\n"); goto cleanup; } container = xsltCreateRVT(tctxt); if (container == NULL) { xsltTransformError(tctxt, NULL, NULL, "dyn:map : internal error container == NULL\n"); goto cleanup; } xsltRegisterLocalRVT(tctxt, container); if (nodeset && nodeset->nodeNr > 0) { xmlXPathNodeSetSort(nodeset); ctxt->context->contextSize = nodeset->nodeNr; ctxt->context->proximityPosition = 0; for (i = 0; i < nodeset->nodeNr; i++) { xmlXPathObjectPtr subResult = NULL; ctxt->context->proximityPosition++; ctxt->context->node = nodeset->nodeTab[i]; ctxt->context->doc = nodeset->nodeTab[i]->doc; subResult = xmlXPathCompiledEval(comp, ctxt->context); if (subResult != NULL) { switch (subResult->type) { case XPATH_NODESET: if (subResult->nodesetval != NULL) for (j = 0; j < subResult->nodesetval->nodeNr; j++) xmlXPathNodeSetAdd(ret->nodesetval, subResult->nodesetval-> nodeTab[j]); break; case XPATH_BOOLEAN: if (container != NULL) { xmlNodePtr cur = xmlNewChild((xmlNodePtr) container, NULL, BAD_CAST "boolean", BAD_CAST (subResult-> boolval ? "true" : "")); if (cur != NULL) { cur->ns = xmlNewNs(cur, BAD_CAST "http://exslt.org/common", BAD_CAST "exsl"); xmlXPathNodeSetAddUnique(ret->nodesetval, cur); } xsltExtensionInstructionResultRegister(tctxt, ret); } break; case XPATH_NUMBER: if (container != NULL) { xmlChar *val = xmlXPathCastNumberToString(subResult-> floatval); xmlNodePtr cur = xmlNewChild((xmlNodePtr) container, NULL, BAD_CAST "number", val); if (val != NULL) xmlFree(val); if (cur != NULL) { cur->ns = xmlNewNs(cur, BAD_CAST "http://exslt.org/common", BAD_CAST "exsl"); xmlXPathNodeSetAddUnique(ret->nodesetval, cur); } xsltExtensionInstructionResultRegister(tctxt, ret); } break; case XPATH_STRING: if (container != NULL) { xmlNodePtr cur = xmlNewChild((xmlNodePtr) container, NULL, BAD_CAST "string", subResult->stringval); if (cur != NULL) { cur->ns = xmlNewNs(cur, BAD_CAST "http://exslt.org/common", BAD_CAST "exsl"); xmlXPathNodeSetAddUnique(ret->nodesetval, cur); } xsltExtensionInstructionResultRegister(tctxt, ret); } break; default: break; } xmlXPathFreeObject(subResult); } } } ctxt->context->doc = oldDoc; ctxt->context->node = oldNode; ctxt->context->contextSize = oldContextSize; ctxt->context->proximityPosition = oldProximityPosition; cleanup: if (comp != NULL) xmlXPathFreeCompExpr(comp); if (nodeset != NULL) xmlXPathFreeNodeSet(nodeset); if (str != NULL) xmlFree(str); valuePush(ctxt, ret); return; }
bool Package::Unpack() { PackagePtr sharedMe = shared_from_this(); // very basic sanity check xmlNodePtr root = xmlDocGetRootElement(_opf); string rootName(reinterpret_cast<const char*>(root->name)); rootName.tolower(); if ( rootName != "package" ) { HandleError(EPUBError::OPFInvalidPackageDocument); return false; // not an OPF file, innit? } if ( _getProp(root, "version").empty() ) { HandleError(EPUBError::OPFPackageHasNoVersion); } InstallPrefixesFromAttributeValue(_getProp(root, "prefix", ePub3NamespaceURI)); // go through children to determine the CFI index of the <spine> tag static const xmlChar* kSpineName = BAD_CAST "spine"; static const xmlChar* kManifestName = BAD_CAST "manifest"; static const xmlChar* kMetadataName = BAD_CAST "metadata"; _spineCFIIndex = 0; uint32_t idx = 0; xmlNodePtr child = xmlFirstElementChild(root); while ( child != nullptr ) { idx += 2; if ( xmlStrEqual(child->name, kSpineName) ) { _spineCFIIndex = idx; if ( _spineCFIIndex != 6 ) HandleError(EPUBError::OPFSpineOutOfOrder); } else if ( xmlStrEqual(child->name, kManifestName) && idx != 4 ) { HandleError(EPUBError::OPFManifestOutOfOrder); } else if ( xmlStrEqual(child->name, kMetadataName) && idx != 2 ) { HandleError(EPUBError::OPFMetadataOutOfOrder); } child = xmlNextElementSibling(child); } if ( _spineCFIIndex == 0 ) { HandleError(EPUBError::OPFNoSpine); return false; // spineless! } #if EPUB_COMPILER_SUPPORTS(CXX_INITIALIZER_LISTS) XPathWrangler xpath(_opf, {{"opf", OPFNamespace}, {"dc", DCNamespace}}); #else XPathWrangler::NamespaceList __m; __m["opf"] = OPFNamespace; __m["dc"] = DCNamespace; XPathWrangler xpath(_opf, __m); #endif // simple things: manifest and spine items xmlNodeSetPtr manifestNodes = nullptr; xmlNodeSetPtr spineNodes = nullptr; try { manifestNodes = xpath.Nodes("/opf:package/opf:manifest/opf:item"); spineNodes = xpath.Nodes("/opf:package/opf:spine/opf:itemref"); if ( manifestNodes == nullptr ) { HandleError(EPUBError::OPFNoManifestItems); } if ( spineNodes == nullptr ) { HandleError(EPUBError::OPFNoSpineItems); } for ( int i = 0; i < manifestNodes->nodeNr; i++ ) { auto p = std::make_shared<ManifestItem>(sharedMe); if ( p->ParseXML(p, manifestNodes->nodeTab[i]) ) { #if EPUB_HAVE(CXX_MAP_EMPLACE) _manifest.emplace(p->Identifier(), p); #else _manifest[p->Identifier()] = p; #endif StoreXMLIdentifiable(p); } else { // TODO: Need an error here } } // check fallback chains typedef std::map<string, bool> IdentSet; IdentSet idents; for ( auto &pair : _manifest ) { ManifestItemPtr item = pair.second; if ( item->FallbackID().empty() ) continue; idents[item->XMLIdentifier()] = true; while ( !item->FallbackID().empty() ) { if ( idents[item->FallbackID()] ) { HandleError(EPUBError::OPFFallbackChainCircularReference); break; } item = item->Fallback(); } idents.clear(); } SpineItemPtr cur; for ( int i = 0; i < spineNodes->nodeNr; i++ ) { auto next = std::make_shared<SpineItem>(sharedMe); if ( next->ParseXML(next, spineNodes->nodeTab[i]) == false ) { // TODO: need an error code here continue; } // validation of idref auto manifestFound = _manifest.find(next->Idref()); if ( manifestFound == _manifest.end() ) { HandleError(EPUBError::OPFInvalidSpineIdref, _Str(next->Idref(), " does not correspond to a manifest item")); continue; } // validation of spine resource type w/fallbacks ManifestItemPtr manifestItem = next->ManifestItem(); bool isContentDoc = false; do { if ( manifestItem->MediaType() == "application/xhtml+xml" || manifestItem->MediaType() == "image/svg" ) { isContentDoc = true; break; } } while ( (manifestItem = manifestItem->Fallback()) ); if ( !isContentDoc ) HandleError(EPUBError::OPFFallbackChainHasNoContentDocument); StoreXMLIdentifiable(next); if ( cur != nullptr ) { cur->SetNextItem(next); } else { _spine = next; } cur = next; } } catch (const std::system_error& exc) { if ( manifestNodes != nullptr ) xmlXPathFreeNodeSet(manifestNodes); if ( spineNodes != nullptr ) xmlXPathFreeNodeSet(spineNodes); if ( exc.code().category() == epub_spec_category() ) throw; return false; } catch (...) { if ( manifestNodes != nullptr ) xmlXPathFreeNodeSet(manifestNodes); if ( spineNodes != nullptr ) xmlXPathFreeNodeSet(spineNodes); return false; } xmlXPathFreeNodeSet(manifestNodes); xmlXPathFreeNodeSet(spineNodes); // now the metadata, which is slightly more involved due to extensions xmlNodeSetPtr metadataNodes = nullptr; xmlNodeSetPtr refineNodes = xmlXPathNodeSetCreate(nullptr); try { shared_ptr<PropertyHolder> holderPtr = std::dynamic_pointer_cast<PropertyHolder>(sharedMe); metadataNodes = xpath.Nodes("/opf:package/opf:metadata/*"); if ( metadataNodes == nullptr ) HandleError(EPUBError::OPFNoMetadata); bool foundIdentifier = false, foundTitle = false, foundLanguage = false, foundModDate = false; string uniqueIDRef = _getProp(root, "unique-identifier"); if ( uniqueIDRef.empty() ) HandleError(EPUBError::OPFPackageUniqueIDInvalid); for ( int i = 0; i < metadataNodes->nodeNr; i++ ) { xmlNodePtr node = metadataNodes->nodeTab[i]; PropertyPtr p; if ( node->ns != nullptr && xmlStrcmp(node->ns->href, BAD_CAST DCNamespace) == 0 ) { // definitely a main node p = std::make_shared<Property>(holderPtr); } else if ( _getProp(node, "name").size() > 0 ) { // it's an ePub2 item-- ignore it continue; } else if ( _getProp(node, "refines").empty() ) { // not refining anything, so it's a main node p = std::make_shared<Property>(holderPtr); } else { // by elimination it's refining something-- we'll process it later when we know we've got all the main nodes in there xmlXPathNodeSetAdd(refineNodes, node); } if ( p && p->ParseMetaElement(node) ) { switch ( p->Type() ) { case DCType::Identifier: { foundIdentifier = true; if ( !uniqueIDRef.empty() && uniqueIDRef != p->XMLIdentifier() ) HandleError(EPUBError::OPFPackageUniqueIDInvalid); break; } case DCType::Title: { foundTitle = true; break; } case DCType::Language: { foundLanguage = true; break; } case DCType::Custom: { if ( p->PropertyIdentifier() == MakePropertyIRI("modified", "dcterms") ) foundModDate = true; break; } default: break; } AddProperty(p); StoreXMLIdentifiable(p); } } if ( !foundIdentifier ) HandleError(EPUBError::OPFMissingIdentifierMetadata); if ( !foundTitle ) HandleError(EPUBError::OPFMissingTitleMetadata); if ( !foundLanguage ) HandleError(EPUBError::OPFMissingLanguageMetadata); if ( !foundModDate ) HandleError(EPUBError::OPFMissingModificationDateMetadata); for ( int i = 0; i < refineNodes->nodeNr; i++ ) { xmlNodePtr node = refineNodes->nodeTab[i]; string ident = _getProp(node, "refines"); if ( ident.empty() ) { HandleError(EPUBError::OPFInvalidRefinementAttribute, "Empty IRI for 'refines' attribute"); continue; } if ( ident[0] == '#' ) { ident = ident.substr(1); } else { // validation only right now IRI iri(ident); if ( iri.IsEmpty() ) { HandleError(EPUBError::OPFInvalidRefinementAttribute, _Str("#", ident, " is not a valid IRI")); } else if ( iri.IsRelative() == false ) { HandleError(EPUBError::OPFInvalidRefinementAttribute, _Str(iri.IRIString(), " is not a relative IRI")); } continue; } auto found = _xmlIDLookup.find(ident); if ( found == _xmlIDLookup.end() ) { HandleError(EPUBError::OPFInvalidRefinementTarget, _Str("#", ident, " does not reference an item in this document")); continue; } PropertyPtr prop = std::dynamic_pointer_cast<Property>(found->second); if ( prop ) { // it's a property, so this is an extension PropertyExtensionPtr extPtr = std::make_shared<PropertyExtension>(prop); if ( extPtr->ParseMetaElement(node) ) prop->AddExtension(extPtr); } else { // not a property, so treat this as a plain property shared_ptr<PropertyHolder> ptr = std::dynamic_pointer_cast<PropertyHolder>(found->second); if ( ptr ) { prop = std::make_shared<Property>(ptr); if ( prop->ParseMetaElement(node) ) ptr->AddProperty(prop); } } } // now look at the <spine> element for properties xmlNodePtr spineNode = xmlFirstElementChild(root); for ( uint32_t i = 2; i < _spineCFIIndex; i += 2 ) spineNode = xmlNextElementSibling(spineNode); string value = _getProp(spineNode, "page-progression-direction"); if ( !value.empty() ) { PropertyPtr prop = std::make_shared<Property>(holderPtr); prop->SetPropertyIdentifier(MakePropertyIRI("page-progression-direction")); prop->SetValue(value); AddProperty(prop); } } catch (std::system_error& exc) { if ( metadataNodes != nullptr ) xmlXPathFreeNodeSet(metadataNodes); if ( refineNodes != nullptr ) xmlXPathFreeNodeSet(refineNodes); if ( exc.code().category() == epub_spec_category() ) throw; return false; } catch (...) { if ( metadataNodes != nullptr ) xmlXPathFreeNodeSet(metadataNodes); if ( refineNodes != nullptr ) xmlXPathFreeNodeSet(refineNodes); return false; } xmlXPathFreeNodeSet(metadataNodes); xmlXPathFreeNodeSet(refineNodes); // now any content type bindings xmlNodeSetPtr bindingNodes = nullptr; try { bindingNodes = xpath.Nodes("/opf:package/opf:bindings/*"); if ( bindingNodes != nullptr ) { for ( int i = 0; i < bindingNodes->nodeNr; i++ ) { xmlNodePtr node = bindingNodes->nodeTab[i]; if ( xmlStrcasecmp(node->name, MediaTypeElementName) != 0 ) continue; //////////////////////////////////////////////////////////// // ePub Publications 3.0 §3.4.16: The `mediaType` Element // The media-type attribute is required. string mediaType = _getProp(node, "media-type"); if ( mediaType.empty() ) { HandleError(EPUBError::OPFBindingHandlerNoMediaType); throw false; } // Each child mediaType of a bindings element must define a unique // content type in its media-type attribute, and the media type // specified must not be a Core Media Type. if ( _contentHandlers[mediaType].empty() == false ) { // user shouldn't have added manual things yet, but for safety we'll look anyway for ( auto ptr : _contentHandlers[mediaType] ) { if ( typeid(*ptr) == typeid(MediaHandler) ) { HandleError(EPUBError::OPFMultipleBindingsForMediaType); } } } if ( CoreMediaTypes.find(mediaType) != CoreMediaTypes.end() ) { HandleError(EPUBError::OPFCoreMediaTypeBindingEncountered); } // The handler attribute is required string handlerID = _getProp(node, "handler"); if ( handlerID.empty() ) { HandleError(EPUBError::OPFBindingHandlerNotFound); } // The required handler attribute must reference the ID [XML] of an // item in the manifest of the default implementation for this media // type. The referenced item must be an XHTML Content Document. ManifestItemPtr handlerItem = ManifestItemWithID(handlerID); if ( !handlerItem ) { HandleError(EPUBError::OPFBindingHandlerNotFound); } if ( handlerItem->MediaType() != "application/xhtml+xml" ) { HandleError(EPUBError::OPFBindingHandlerInvalidType, _Str("Media handlers must be XHTML content documents, but referenced item has type '", handlerItem->MediaType(), "'.")); } // All XHTML Content Documents designated as handlers must have the // `scripted` property set in their manifest item's `properties` // attribute. if ( handlerItem->HasProperty(ItemProperties::HasScriptedContent) == false ) { HandleError(EPUBError::OPFBindingHandlerNotScripted); } // all good-- install it now _contentHandlers[mediaType].push_back(std::make_shared<MediaHandler>(sharedMe, mediaType, handlerItem->AbsolutePath())); } } } catch (std::exception& exc) { std::cerr << "Exception processing OPF file: " << exc.what() << std::endl; if ( bindingNodes != nullptr ) xmlXPathFreeNodeSet(bindingNodes); throw; } catch (...) { if ( bindingNodes != nullptr ) xmlXPathFreeNodeSet(bindingNodes); return false; } xmlXPathFreeNodeSet(bindingNodes); // now the navigation tables for ( auto item : _manifest ) { if ( !item.second->HasProperty(ItemProperties::Navigation) ) continue; NavigationList tables = NavTablesFromManifestItem(sharedMe, item.second); for ( auto table : tables ) { // have to dynamic_cast these guys to get the right pointer type shared_ptr<class NavigationTable> navTable = std::dynamic_pointer_cast<class NavigationTable>(table); #if EPUB_HAVE(CXX_MAP_EMPLACE) _navigation.emplace(navTable->Type(), navTable); #else _navigation[navTable->Type()] = navTable; #endif } } // lastly, let's set the media support information InitMediaSupport(); return true; }
/** * 'update' operation */ static void edUpdate(xmlDocPtr doc, xmlNodeSetPtr nodes, const char *val, XmlNodeType type, xmlXPathContextPtr ctxt) { int i; xmlXPathCompExprPtr xpath = NULL; if (type == XML_EXPR) { xpath = xmlXPathCompile((const xmlChar*) val); if (!xpath) return; } for (i = 0; i < nodes->nodeNr; i++) { /* update node */ if (type == XML_EXPR) { xmlXPathObjectPtr res; ctxt->node = nodes->nodeTab[i]; res = xmlXPathCompiledEval(xpath, ctxt); if (res->type == XPATH_NODESET || res->type == XPATH_XSLT_TREE) { int j; xmlNodePtr oldChild; xmlNodeSetPtr oldChildren = xmlXPathNodeSetCreate(NULL); /* NOTE: newChildren can be NULL for empty result set */ xmlNodeSetPtr newChildren = res->nodesetval; /* NOTE: nodes can be both oldChildren and newChildren */ /* unlink the old children */ for (oldChild = nodes->nodeTab[i]->children; oldChild; oldChild = oldChild->next) { xmlUnlinkNode(oldChild); /* we can't free it now because an oldChild can also be newChild! just put it in the list */ xmlXPathNodeSetAdd(oldChildren, oldChild); } /* add the new children */ for (j = 0; newChildren && j < newChildren->nodeNr; j++) { xmlNodePtr node = newChildren->nodeTab[j]; xmlAddChild(nodes->nodeTab[i], /* if node is linked to this doc we need to copy */ (node->doc == doc)? xmlDocCopyNode(node, doc, 1) : node); newChildren->nodeTab[j] = NULL; } newChildren->nodeNr = 0; /* NOTE: if any oldChildren were newChildren, they've been copied so we can free them all now */ for (j = 0; j < oldChildren->nodeNr; j++) { xmlFreeNode(oldChildren->nodeTab[j]); oldChildren->nodeTab[j] = NULL; } oldChildren->nodeNr = 0; xmlXPathFreeNodeSet(oldChildren); } else { res = xmlXPathConvertString(res); update_string(doc, nodes->nodeTab[i], res->stringval); } xmlXPathFreeObject(res); } else { update_string(doc, nodes->nodeTab[i], (const xmlChar*) val); } } xmlXPathFreeCompExpr(xpath); }
bool Package::Unpack() { // very basic sanity check xmlNodePtr root = xmlDocGetRootElement(_opf); string rootName(reinterpret_cast<const char*>(root->name)); rootName.tolower(); if ( rootName != "package" ) return false; // not an OPF file, innit? InstallPrefixesFromAttributeValue(_getProp(root, "prefix", ePub3NamespaceURI)); // go through children to determine the CFI index of the <spine> tag static const xmlChar* kSpineName = BAD_CAST "spine"; _spineCFIIndex = 0; xmlNodePtr child = root->children; while ( child != nullptr ) { if ( child->type == XML_ELEMENT_NODE ) { _spineCFIIndex += 2; if ( xmlStrEqual(child->name, kSpineName) ) break; } child = child->next; } if ( _spineCFIIndex == 0 ) return false; // spineless! XPathWrangler xpath(_opf, {{"opf", OPFNamespace}, {"dc", DCNamespace}}); // simple things: manifest and spine items xmlNodeSetPtr manifestNodes = nullptr; xmlNodeSetPtr spineNodes = nullptr; try { manifestNodes = xpath.Nodes("/opf:package/opf:manifest/opf:item"); spineNodes = xpath.Nodes("/opf:package/opf:spine/opf:itemref"); if ( manifestNodes == nullptr || spineNodes == nullptr ) throw false; // looks invalid, or at least unusable, to me for ( int i = 0; i < manifestNodes->nodeNr; i++ ) { ManifestItem *p = new ManifestItem(manifestNodes->nodeTab[i], this); _manifest.emplace(p->Identifier(), p); } SpineItem* cur = nullptr; for ( int i = 0; i < spineNodes->nodeNr; i++ ) { SpineItem* next = new SpineItem(spineNodes->nodeTab[i], this); if ( cur != nullptr ) { cur->SetNextItem(next); } else { _spine.reset(next); } cur = next; } } catch (...) { if ( manifestNodes != nullptr ) xmlXPathFreeNodeSet(manifestNodes); if ( spineNodes != nullptr ) xmlXPathFreeNodeSet(spineNodes); return false; } xmlXPathFreeNodeSet(manifestNodes); xmlXPathFreeNodeSet(spineNodes); // now the metadata, which is slightly more involved due to extensions xmlNodeSetPtr metadataNodes = nullptr; xmlNodeSetPtr refineNodes = xmlXPathNodeSetCreate(nullptr); try { metadataNodes = xpath.Nodes("/opf:package/opf:metadata/*"); if ( metadataNodes == nullptr ) throw false; std::map<string, class Metadata*> metadataByID; for ( int i = 0; i < metadataNodes->nodeNr; i++ ) { xmlNodePtr node = metadataNodes->nodeTab[i]; class Metadata* p = nullptr; if ( node->ns != nullptr && xmlStrcmp(node->ns->href, BAD_CAST DCNamespace) == 0 ) { // definitely a main node p = new class Metadata(node, this); } else if ( _getProp(node, "name").size() > 0 ) { // it's an ePub2 item-- ignore it continue; } else if ( _getProp(node, "refines").empty() ) { // not refining anything, so it's a main node p = new class Metadata(node, this); } else { // by elimination it's refining something-- we'll process it later when we know we've got all the main nodes in there xmlXPathNodeSetAdd(refineNodes, node); } if ( p != nullptr ) { _metadata.push_back(p); if ( !p->Identifier().empty() ) metadataByID[p->Identifier()] = p; } } for ( int i = 0; i < refineNodes->nodeNr; i++ ) { xmlNodePtr node = refineNodes->nodeTab[i]; string ident = _getProp(node, "refines"); if ( ident.empty() ) continue; if ( ident[0] == '#' ) ident = ident.substr(1); auto found = metadataByID.find(ident); if ( found == metadataByID.end() ) continue; found->second->AddExtension(node, this); } } catch (...) { if ( metadataNodes != nullptr ) xmlXPathFreeNodeSet(metadataNodes); if ( refineNodes != nullptr ) xmlXPathFreeNodeSet(refineNodes); return false; } xmlXPathFreeNodeSet(metadataNodes); xmlXPathFreeNodeSet(refineNodes); // now any content type bindings xmlNodeSetPtr bindingNodes = nullptr; try { bindingNodes = xpath.Nodes("/opf:package/opf:bindings/*"); if ( bindingNodes != nullptr ) { for ( int i = 0; i < bindingNodes->nodeNr; i++ ) { xmlNodePtr node = bindingNodes->nodeTab[i]; if ( xmlStrcasecmp(node->name, MediaTypeElementName) != 0 ) continue; //////////////////////////////////////////////////////////// // ePub Publications 3.0 §3.4.16: The `mediaType` Element // The media-type attribute is required. string mediaType = _getProp(node, "media-type"); if ( mediaType.empty() ) { throw std::invalid_argument("mediaType element has missing or empty media-type attribute."); } // Each child mediaType of a bindings element must define a unique // content type in its media-type attribute, and the media type // specified must not be a Core Media Type. if ( _contentHandlers[mediaType].empty() == false ) { // user shouldn't have added manual things yet, but for safety we'll look anyway for ( auto ptr : _contentHandlers[mediaType] ) { if ( typeid(*ptr) == typeid(MediaHandler) ) { throw std::invalid_argument(_Str("Duplicate media handler found for type '", mediaType, "'.")); } } } if ( CoreMediaTypes.find(mediaType) != CoreMediaTypes.end() ) { throw std::invalid_argument("mediaType element specifies an EPUB Core Media Type."); } // The handler attribute is required string handlerID = _getProp(node, "handler"); if ( handlerID.empty() ) { throw std::invalid_argument("mediaType element has missing or empty handler attribute."); } // The required handler attribute must reference the ID [XML] of an // item in the manifest of the default implementation for this media // type. The referenced item must be an XHTML Content Document. const ManifestItem* handlerItem = ManifestItemWithID(handlerID); if ( handlerItem == nullptr ) { throw std::invalid_argument(_Str("mediaType element references non-existent handler with ID '", handlerID, "'.")); } if ( handlerItem->MediaType() != "application/xhtml+xml" ) { throw std::invalid_argument(_Str("Media handlers must be XHTML content documents, but referenced item has type '", handlerItem->MediaType(), "'.")); } // All XHTML Content Documents designated as handlers must have the // `scripted` property set in their manifest item's `properties` // attribute. if ( handlerItem->HasProperty(ItemProperties::HasScriptedContent) == false ) { throw std::invalid_argument("Media handlers must have the `scripted` property."); } // all good-- install it now _contentHandlers[mediaType].push_back(new MediaHandler(this, mediaType, handlerItem->AbsolutePath())); } } } catch (std::exception& exc) { std::cerr << "Exception processing OPF file: " << exc.what() << std::endl; if ( bindingNodes != nullptr ) xmlXPathFreeNodeSet(bindingNodes); return false; } catch (...) { if ( bindingNodes != nullptr ) xmlXPathFreeNodeSet(bindingNodes); return false; } xmlXPathFreeNodeSet(bindingNodes); // now the navigation tables for ( auto item : _manifest ) { if ( !item.second->HasProperty(ItemProperties::Navigation) ) continue; NavigationList tables = NavTablesFromManifestItem(item.second); for ( auto table : tables ) { // have to dynamic_cast these guys to get the right pointer type class NavigationTable* navTable = dynamic_cast<class NavigationTable*>(table); _navigation.emplace(navTable->Type(), navTable); } } return true; }
/** * xsltInitCtxtKey: * @ctxt: an XSLT transformation context * @doc: an XSLT document * @keyd: the key definition * * Computes the key tables this key and for the current input document. */ int xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc, xsltKeyDefPtr keyd) { int i; xmlNodeSetPtr nodelist = NULL, keylist; xmlXPathObjectPtr res = NULL; xmlChar *str, **list; xsltKeyTablePtr table; int oldPos, oldSize; xmlNodePtr oldInst; xmlNodePtr oldNode; xsltDocumentPtr oldDoc; xmlDocPtr oldXDoc; int oldNsNr; xmlNsPtr *oldNamespaces; doc->nbKeysComputed++; /* * Evaluate the nodelist */ oldXDoc= ctxt->xpathCtxt->doc; oldPos = ctxt->xpathCtxt->proximityPosition; oldSize = ctxt->xpathCtxt->contextSize; oldNsNr = ctxt->xpathCtxt->nsNr; oldNamespaces = ctxt->xpathCtxt->namespaces; oldInst = ctxt->inst; oldDoc = ctxt->document; oldNode = ctxt->node; if (keyd->comp == NULL) goto error; if (keyd->usecomp == NULL) goto error; ctxt->document = doc; ctxt->xpathCtxt->doc = doc->doc; ctxt->xpathCtxt->node = (xmlNodePtr) doc->doc; ctxt->node = (xmlNodePtr) doc->doc; /* TODO : clarify the use of namespaces in keys evaluation */ ctxt->xpathCtxt->namespaces = keyd->nsList; ctxt->xpathCtxt->nsNr = keyd->nsNr; ctxt->inst = keyd->inst; res = xmlXPathCompiledEval(keyd->comp, ctxt->xpathCtxt); ctxt->xpathCtxt->contextSize = oldSize; ctxt->xpathCtxt->proximityPosition = oldPos; ctxt->inst = oldInst; if (res != NULL) { if (res->type == XPATH_NODESET) { nodelist = res->nodesetval; #ifdef WITH_XSLT_DEBUG_KEYS if (nodelist != NULL) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluates to %d nodes\n", keyd->match, nodelist->nodeNr)); #endif } else { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s is not a node set\n", keyd->match)); #endif goto error; } } else { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluation failed\n", keyd->match)); #endif ctxt->state = XSLT_STATE_STOPPED; goto error; } /* * for each node in the list evaluate the key and insert the node */ if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) goto error; /** * Multiple key definitions for the same name are allowed, so * we must check if the key is already present for this doc */ table = (xsltKeyTablePtr) doc->keys; while (table != NULL) { if (xmlStrEqual(table->name, keyd->name) && (((keyd->nameURI == NULL) && (table->nameURI == NULL)) || ((keyd->nameURI != NULL) && (table->nameURI != NULL) && (xmlStrEqual(table->nameURI, keyd->nameURI))))) break; table = table->next; } /** * If the key was not previously defined, create it now and * chain it to the list of keys for the doc */ if (table == NULL) { table = xsltNewKeyTable(keyd->name, keyd->nameURI); if (table == NULL) goto error; table->next = doc->keys; doc->keys = table; } for (i = 0;i < nodelist->nodeNr;i++) { if (IS_XSLT_REAL_NODE(nodelist->nodeTab[i])) { ctxt->node = nodelist->nodeTab[i]; list = xsltEvalXPathKeys(ctxt, keyd->usecomp, keyd); if (list != NULL) { int ix = 0; str = list[ix++]; while (str != NULL) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : node associated to(%s,%s)\n", keyd->name, str)); #endif keylist = xmlHashLookup(table->keys, str); if (keylist == NULL) { keylist = xmlXPathNodeSetCreate(nodelist->nodeTab[i]); xmlHashAddEntry(table->keys, str, keylist); } else { xmlXPathNodeSetAdd(keylist, nodelist->nodeTab[i]); } switch (nodelist->nodeTab[i]->type) { case XML_ELEMENT_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: nodelist->nodeTab[i]->psvi = keyd; break; case XML_ATTRIBUTE_NODE: { xmlAttrPtr attr = (xmlAttrPtr) nodelist->nodeTab[i]; attr->psvi = keyd; break; } case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: { xmlDocPtr kdoc = (xmlDocPtr) nodelist->nodeTab[i]; kdoc->psvi = keyd; break; } default: break; } xmlFree(str); str = list[ix++]; } xmlFree(list); #ifdef WITH_XSLT_DEBUG_KEYS } else { XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : use %s failed to return strings\n", keyd->use)); #endif } } } error: ctxt->document = oldDoc; ctxt->xpathCtxt->doc = oldXDoc; ctxt->xpathCtxt->nsNr = oldNsNr; ctxt->xpathCtxt->namespaces = oldNamespaces; ctxt->node = oldNode; if (res != NULL) xmlXPathFreeObject(res); return(0); }