#include #include #include #include #include static void node_set (xmlXPathParserContextPtr ctxt, int nargs); static void tokenise (xmlXPathParserContextPtr ctxt, int nargs); /* usage: ./xpfunctest xml-doc */ int main (int argc, char **argv) { xmlXPathContextPtr ctxt; xmlXPathObjectPtr obj; xmlNsPtr ns; xmlDocPtr doc; xmlNodePtr node; xmlChar *string; int i; argc--; argv++; if (argc < 2) exit (1); doc = xmlParseFile (*argv++); node = xmlDocGetRootElement (doc); ctxt = xmlXPathNewContext (doc); ctxt->node = node; ns = xmlSearchNsByHref (doc, ctxt->node, (const xmlChar *) "http://xpath.test/"); if (ns != NULL) xmlXPathRegisterNs (ctxt, ns->prefix, ns->href); xmlXPathRegisterFuncNS (ctxt, (const xmlChar *) "node-set", (const xmlChar *) "http://xpath.test/", node_set); xmlXPathRegisterFuncNS (ctxt, (const xmlChar *) "tokenize", (const xmlChar *) "http://xpath.test/", tokenise); obj = xmlXPathEval ((const xmlChar *) *argv, ctxt); if (obj != NULL) { if (obj->type == XPATH_NODESET || obj->type == XPATH_XSLT_TREE) { if (obj->nodesetval != NULL) for (i = 0; i < obj->nodesetval->nodeNr; i++) { xmlElemDump (stdout, doc, obj->nodesetval->nodeTab[i]); printf ("\n"); } } else if ((string = xmlXPathCastToString (obj)) != NULL) { printf ("string = '%s'\n", string); xmlFree (string); } xmlXPathFreeObject (obj); } exit (0); } /* taken from libxslt extra.c */ static void node_set (xmlXPathParserContextPtr ctxt, int nargs) { if (nargs != 1) { ctxt->error = XPATH_INVALID_ARITY; return; } if ((ctxt->value == NULL) || ((ctxt->value->type != XPATH_XSLT_TREE) && (ctxt->value->type != XPATH_NODESET))) { ctxt->error = XPATH_INVALID_TYPE; return; } if (ctxt->value->type == XPATH_XSLT_TREE) { ctxt->value->type = XPATH_NODESET; } } /* taken from libexslt string.c and modified to avoid reference to xslt context. */ static void tokenise (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *str, *delimiters, *cur; const xmlChar *token, *delimiter; xmlNodePtr node; xmlDocPtr doc; xmlXPathObjectPtr ret; if ((nargs < 1) || (nargs > 2)) { xmlXPathSetArityError (ctxt); return; } if (nargs == 2) { delimiters = xmlXPathPopString (ctxt); if (xmlXPathCheckError (ctxt)) return; } else { delimiters = xmlStrdup ((const xmlChar *) "\t\r\n "); } if (delimiters == NULL) return; str = xmlXPathPopString (ctxt); if (xmlXPathCheckError (ctxt) || (str == NULL)) { xmlFree (delimiters); return; } doc = ctxt->context->doc; ret = xmlXPathNewNodeSet (NULL); if (ret != NULL) { ret->boolval = 1; /* * This is a hack: token elements are added as children of a * fake element node. This is necessary to free them up * correctly when freeing the node-set. */ ret->user = (void *) xmlNewDocNode (doc, NULL, (const xmlChar *) "fake", NULL); if (ret->user == NULL) goto error; } for (cur = str, token = str; *cur != 0; cur++) { for (delimiter = delimiters; *delimiter != 0; delimiter++) { if (*cur == *delimiter) { if (cur == token) { /* discard empty tokens */ break; } *cur = 0; node = xmlNewDocNode (doc, NULL, (const xmlChar *) "token", token); *cur = *delimiter; token = cur + 1; xmlAddChild ((xmlNodePtr) ret->user, node); xmlXPathNodeSetAdd (ret->nodesetval, node); break; } } } node = xmlNewDocNode (doc, NULL, (const xmlChar *) "token", token); xmlAddChild ((xmlNodePtr) ret->user, node); xmlXPathNodeSetAdd (ret->nodesetval, node); valuePush (ctxt, ret); ret = NULL; /* hack to prevent freeing ret later */ error: if (ret != NULL) xmlXPathFreeObject (ret); if (str != NULL) xmlFree (str); if (delimiters != NULL) xmlFree (delimiters); }