--- /dev/null
+/* Parser interface for DOM-based parser (libxml) rather than
+ stream-based SAX-type parser */
+
+#include "postgres.h"
+#include "fmgr.h"
+
+/* libxml includes */
+
+#include <libxml/xpath.h>
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+
+/* declarations */
+
+static void *pgxml_palloc(size_t size);
+static void *pgxml_repalloc(void *ptr, size_t size);
+static void pgxml_pfree(void *ptr);
+static char *pgxml_pstrdup(const char* string);
+
+static void pgxml_parser_init();
+
+static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset, xmlDocPtr doc,
+ xmlChar *toptagname, xmlChar *septagname,
+ int format);
+
+static xmlChar *pgxml_texttoxmlchar(text *textstring);
+
+
+Datum pgxml_parse(PG_FUNCTION_ARGS);
+Datum pgxml_xpath(PG_FUNCTION_ARGS);
+
+/* memory handling passthrough functions (e.g. palloc, pstrdup are
+ currently macros, and the others might become so...) */
+
+static void *pgxml_palloc(size_t size)
+{
+ return palloc(size);
+}
+
+static void *pgxml_repalloc(void *ptr, size_t size)
+{
+ return repalloc(ptr,size);
+}
+
+static void pgxml_pfree(void *ptr)
+{
+ return pfree(ptr);
+}
+
+static char *pgxml_pstrdup(const char *string)
+{
+ return pstrdup(string);
+}
+
+static void pgxml_parser_init()
+{
+ /* This code should also set parser settings from user-supplied
+ info. Quite how these settings are made is another matter :) */
+
+ xmlMemSetup(pgxml_pfree, pgxml_palloc, pgxml_repalloc, pgxml_pstrdup);
+ xmlInitParser();
+
+}
+
+
+/* Returns true if document is well-formed */
+
+PG_FUNCTION_INFO_V1(pgxml_parse);
+
+Datum
+pgxml_parse(PG_FUNCTION_ARGS)
+{
+ /* called as pgxml_parse(document) */
+ xmlDocPtr doctree;
+ text *t = PG_GETARG_TEXT_P(0); /*document buffer */
+ int32 docsize = VARSIZE(t) - VARHDRSZ;
+
+ pgxml_parser_init();
+
+ doctree = xmlParseMemory((char *) VARDATA(t), docsize);
+ if (doctree == NULL) {
+ /* xmlCleanupParser(); */
+ PG_RETURN_BOOL(false); /* i.e. not well-formed */
+ }
+ /* xmlCleanupParser(); */
+ xmlFreeDoc(doctree);
+ PG_RETURN_BOOL(true);
+}
+
+static xmlChar
+*pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
+ xmlDocPtr doc,
+ xmlChar *toptagname,
+ xmlChar *septagname,
+ int format)
+{
+ /* Function translates a nodeset into a text representation */
+ /* iterates over each node in the set and calls xmlNodeDump to write
+ it to an xmlBuffer -from which an xmlChar * string is returned. */
+ /* each representation is surrounded by <tagname> ... </tagname> */
+ /* if format==0, add a newline between nodes?? */
+
+ xmlBufferPtr buf;
+ xmlChar *result;
+ int i;
+
+ buf =xmlBufferCreate();
+
+ if ((toptagname != NULL) && (xmlStrlen(toptagname)>0)) {
+ xmlBufferWriteChar(buf,"<");
+ xmlBufferWriteCHAR(buf,toptagname);
+ xmlBufferWriteChar(buf,">");
+ }
+ if (nodeset != NULL) {
+ for (i=0; i < nodeset->nodeNr; i++) {
+ if ((septagname != NULL) && (xmlStrlen(septagname)>0)) {
+ xmlBufferWriteChar(buf,"<");
+ xmlBufferWriteCHAR(buf,septagname);
+ xmlBufferWriteChar(buf,">");
+ }
+ xmlNodeDump(buf, doc, nodeset->nodeTab[i],1,(format==2));
+
+ if ((septagname != NULL) && (xmlStrlen(septagname)>0)) {
+ xmlBufferWriteChar(buf,"</");
+ xmlBufferWriteCHAR(buf,septagname);
+ xmlBufferWriteChar(buf,">");
+ }
+ if (format) {
+ xmlBufferWriteChar(buf,"\n");
+ }
+ }
+ }
+
+ if ((toptagname != NULL) && (xmlStrlen(toptagname)>0)) {
+ xmlBufferWriteChar(buf,"</");
+ xmlBufferWriteCHAR(buf,toptagname);
+ xmlBufferWriteChar(buf,">");
+ }
+ result = xmlStrdup(buf->content);
+ xmlBufferFree(buf);
+ return result;
+}
+
+static xmlChar *pgxml_texttoxmlchar(text *textstring) {
+ xmlChar *res;
+ int32 txsize;
+
+ txsize=VARSIZE(textstring)-VARHDRSZ;
+ res = (xmlChar *) palloc(txsize+1);
+ memcpy((char *)res, VARDATA(textstring), txsize);
+ res[txsize]='\0';
+ return res;
+}
+
+
+PG_FUNCTION_INFO_V1(pgxml_xpath);
+
+Datum
+pgxml_xpath(PG_FUNCTION_ARGS)
+{
+ xmlDocPtr doctree;
+ xmlXPathContextPtr ctxt;
+ xmlXPathObjectPtr res;
+ xmlChar *xpath, *xpresstr, *toptag, *septag;
+ xmlXPathCompExprPtr comppath;
+
+ int32 docsize,ressize;
+ text *t, *xpres;
+
+ t = PG_GETARG_TEXT_P(0); /*document buffer */
+ xpath = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(1)); /* XPath expression */
+ toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
+ septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
+
+ docsize = VARSIZE(t) - VARHDRSZ;
+
+ pgxml_parser_init();
+
+ doctree = xmlParseMemory((char *) VARDATA(t), docsize);
+ if (doctree == NULL) { /* not well-formed */
+ PG_RETURN_NULL();
+ }
+
+ ctxt = xmlXPathNewContext(doctree);
+ ctxt->node = xmlDocGetRootElement(doctree);
+
+ /* compile the path */
+ comppath = xmlXPathCompile(xpath);
+ if (comppath == NULL) {
+ elog(NOTICE, "XPath syntax error");
+ xmlFreeDoc(doctree);
+ pfree((void *) xpath);
+ PG_RETURN_NULL();
+ }
+
+ /* Now evaluate the path expression. */
+ res = xmlXPathCompiledEval(comppath,ctxt);
+ xmlXPathFreeCompExpr(comppath);
+
+ if (res==NULL) {
+ xmlFreeDoc(doctree);
+ pfree((void *)xpath);
+ PG_RETURN_NULL(); /* seems appropriate */
+ }
+ /* now we dump this node, ?surrounding by tags? */
+ /* To do this, we look first at the type */
+ switch(res->type) {
+ case XPATH_NODESET:
+ xpresstr = pgxmlNodeSetToText(res->nodesetval,
+ doctree,
+ toptag, septag, 0);
+ break;
+ case XPATH_STRING:
+ xpresstr = xmlStrdup(res->stringval);
+ break;
+ default:
+ elog(NOTICE,"Unsupported XQuery result: %d",res->type);
+ xpresstr = xmlStrdup("<unsupported/>");
+ }
+
+
+ /* Now convert this result back to text */
+ ressize = strlen(xpresstr);
+ xpres = (text *) palloc(ressize+VARHDRSZ);
+ memcpy(VARDATA(xpres),xpresstr,ressize);
+ VARATT_SIZEP(xpres)=ressize + VARHDRSZ;
+
+ /* Free various storage */
+ xmlFreeDoc(doctree);
+ pfree((void *) xpath);
+ xmlFree(xpresstr);
+
+ PG_RETURN_TEXT_P(xpres);
+}
+
+
+
+