1 /*
   2  * dynamic.c: Implementation of the EXSLT -- Dynamic module
   3  *
   4  * References:
   5  *   http://www.exslt.org/dyn/dyn.html
   6  *
   7  * See Copyright for the status of this software.
   8  *
   9  * Authors:
  10  *   Mark Vakoc <mark_vakoc@jdedwards.com>
  11  *   Thomas Broyer <tbroyer@ltgt.net>
  12  *
  13  * TODO:
  14  * elements:
  15  * functions:
  16  *    min
  17  *    max
  18  *    sum
  19  *    map
  20  *    closure
  21  */
  22 
  23 #define IN_LIBEXSLT
  24 #include "libexslt/libexslt.h"
  25 
  26 #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
  27 #include <win32config.h>
  28 #else
  29 #include "config.h"
  30 #endif
  31 
  32 #include <libxml/tree.h>
  33 #include <libxml/xpath.h>
  34 #include <libxml/xpathInternals.h>
  35 
  36 #include <libxslt/xsltconfig.h>
  37 #include <libxslt/xsltutils.h>
  38 #include <libxslt/xsltInternals.h>
  39 #include <libxslt/extensions.h>
  40 
  41 #include "exslt.h"
  42 
  43 /**
  44  * exsltDynEvaluateFunction:
  45  * @ctxt:  an XPath parser context
  46  * @nargs:  the number of arguments
  47  *
  48  * Evaluates the string as an XPath expression and returns the result
  49  * value, which may be a boolean, number, string, node set, result tree
  50  * fragment or external object.
  51  */
  52 
  53 static void
  54 exsltDynEvaluateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
  55     xmlChar *str = NULL;
  56     xmlXPathObjectPtr ret = NULL;
  57 
  58     if (ctxt == NULL)
  59         return;
  60     if (nargs != 1) {
  61         xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
  62         xsltGenericError(xsltGenericErrorContext,
  63             "dyn:evalute() : invalid number of args %d\n", nargs);
  64         ctxt->error = XPATH_INVALID_ARITY;
  65         return;
  66     }
  67     str = xmlXPathPopString(ctxt);
  68     /* return an empty node-set if an empty string is passed in */
  69     if (!str||!xmlStrlen(str)) {
  70         if (str) xmlFree(str);
  71         valuePush(ctxt,xmlXPathNewNodeSet(NULL));
  72         return;
  73     }
  74     ret = xmlXPathEval(str,ctxt->context);
  75     if (ret)
  76         valuePush(ctxt,ret);
  77     else {
  78         xsltGenericError(xsltGenericErrorContext,
  79             "dyn:evaluate() : unable to evaluate expression '%s'\n",str);
  80         valuePush(ctxt,xmlXPathNewNodeSet(NULL));
  81     }
  82     xmlFree(str);
  83     return;
  84 }
  85 
  86 /**
  87  * exsltDynMapFunction:
  88  * @ctxt:  an XPath parser context
  89  * @nargs:  the number of arguments
  90  *
  91  * Evaluates the string as an XPath expression and returns the result
  92  * value, which may be a boolean, number, string, node set, result tree
  93  * fragment or external object.
  94  */
  95 
  96 static void
  97 exsltDynMapFunction(xmlXPathParserContextPtr ctxt, int nargs)
  98 {
  99     xmlChar *str = NULL;
 100     xmlNodeSetPtr nodeset = NULL;
 101     xsltTransformContextPtr tctxt;
 102     xmlXPathCompExprPtr comp = NULL;
 103     xmlXPathObjectPtr ret = NULL;
 104     xmlDocPtr oldDoc, container = NULL;
 105     xmlNodePtr oldNode;
 106     int oldContextSize;
 107     int oldProximityPosition;
 108     int i, j;
 109 
 110 
 111     if (nargs != 2) {
 112         xmlXPathSetArityError(ctxt);
 113         return;
 114     }
 115     str = xmlXPathPopString(ctxt);
 116     if (xmlXPathCheckError(ctxt)) {
 117         xmlXPathSetTypeError(ctxt);
 118         return;
 119     }
 120 
 121     nodeset = xmlXPathPopNodeSet(ctxt);
 122     if (xmlXPathCheckError(ctxt)) {
 123         xmlXPathSetTypeError(ctxt);
 124         return;
 125     }
 126     if (str == NULL || !xmlStrlen(str) || !(comp = xmlXPathCompile(str))) {
 127         if (nodeset != NULL)
 128             xmlXPathFreeNodeSet(nodeset);
 129         if (str != NULL)
 130             xmlFree(str);
 131         valuePush(ctxt, xmlXPathNewNodeSet(NULL));
 132         return;
 133     }
 134 
 135     ret = xmlXPathNewNodeSet(NULL);
 136     if (ret == NULL) {
 137         xsltGenericError(xsltGenericErrorContext,
 138                          "exsltDynMapFunction: ret == NULL\n");
 139         goto cleanup;
 140     }
 141 
 142     oldDoc = ctxt->context->doc;
 143     oldNode = ctxt->context->node;
 144     oldContextSize = ctxt->context->contextSize;
 145     oldProximityPosition = ctxt->context->proximityPosition;
 146 
 147         /**
 148      * since we really don't know we're going to be adding node(s)
 149      * down the road we create the RVT regardless
 150      */
 151     tctxt = xsltXPathGetTransformContext(ctxt);
 152     if (tctxt == NULL) {
 153     xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
 154           "dyn:map : internal error tctxt == NULL\n");
 155     goto cleanup;
 156     }
 157     container = xsltCreateRVT(tctxt);
 158     if (container == NULL) {
 159     xsltTransformError(tctxt, NULL, NULL,
 160           "dyn:map : internal error container == NULL\n");
 161     goto cleanup;
 162     }
 163     xsltRegisterLocalRVT(tctxt, container);
 164     if (nodeset && nodeset->nodeNr > 0) {
 165         xmlXPathNodeSetSort(nodeset);
 166         ctxt->context->contextSize = nodeset->nodeNr;
 167         ctxt->context->proximityPosition = 0;
 168         for (i = 0; i < nodeset->nodeNr; i++) {
 169             xmlXPathObjectPtr subResult = NULL;
 170 
 171             ctxt->context->proximityPosition++;
 172             ctxt->context->node = nodeset->nodeTab[i];
 173             ctxt->context->doc = nodeset->nodeTab[i]->doc;
 174 
 175             subResult = xmlXPathCompiledEval(comp, ctxt->context);
 176             if (subResult != NULL) {
 177                 switch (subResult->type) {
 178                     case XPATH_NODESET:
 179                         if (subResult->nodesetval != NULL)
 180                             for (j = 0; j < subResult->nodesetval->nodeNr;
 181                                  j++)
 182                                 xmlXPathNodeSetAdd(ret->nodesetval,
 183                                                    subResult->nodesetval->
 184                                                    nodeTab[j]);
 185                         break;
 186                     case XPATH_BOOLEAN:
 187                         if (container != NULL) {
 188                             xmlNodePtr cur =
 189                                 xmlNewChild((xmlNodePtr) container, NULL,
 190                                             BAD_CAST "boolean",
 191                                             BAD_CAST (subResult->
 192                                             boolval ? "true" : ""));
 193                             if (cur != NULL) {
 194                                 cur->ns =
 195                                     xmlNewNs(cur,
 196                                              BAD_CAST
 197                                              "http://exslt.org/common",
 198                                              BAD_CAST "exsl");
 199                                 xmlXPathNodeSetAddUnique(ret->nodesetval,
 200                                                          cur);
 201                             }
 202                 xsltExtensionInstructionResultRegister(tctxt, ret);
 203                         }
 204                         break;
 205                     case XPATH_NUMBER:
 206                         if (container != NULL) {
 207                             xmlChar *val =
 208                                 xmlXPathCastNumberToString(subResult->
 209                                                            floatval);
 210                             xmlNodePtr cur =
 211                                 xmlNewChild((xmlNodePtr) container, NULL,
 212                                             BAD_CAST "number", val);
 213                             if (val != NULL)
 214                                 xmlFree(val);
 215 
 216                             if (cur != NULL) {
 217                                 cur->ns =
 218                                     xmlNewNs(cur,
 219                                              BAD_CAST
 220                                              "http://exslt.org/common",
 221                                              BAD_CAST "exsl");
 222                                 xmlXPathNodeSetAddUnique(ret->nodesetval,
 223                                                          cur);
 224                             }
 225                 xsltExtensionInstructionResultRegister(tctxt, ret);
 226                         }
 227                         break;
 228                     case XPATH_STRING:
 229                         if (container != NULL) {
 230                             xmlNodePtr cur =
 231                                 xmlNewChild((xmlNodePtr) container, NULL,
 232                                             BAD_CAST "string",
 233                                             subResult->stringval);
 234                             if (cur != NULL) {
 235                                 cur->ns =
 236                                     xmlNewNs(cur,
 237                                              BAD_CAST
 238                                              "http://exslt.org/common",
 239                                              BAD_CAST "exsl");
 240                                 xmlXPathNodeSetAddUnique(ret->nodesetval,
 241                                                          cur);
 242                             }
 243                 xsltExtensionInstructionResultRegister(tctxt, ret);
 244                         }
 245                         break;
 246             default:
 247                         break;
 248                 }
 249                 xmlXPathFreeObject(subResult);
 250             }
 251         }
 252     }
 253     ctxt->context->doc = oldDoc;
 254     ctxt->context->node = oldNode;
 255     ctxt->context->contextSize = oldContextSize;
 256     ctxt->context->proximityPosition = oldProximityPosition;
 257 
 258 
 259   cleanup:
 260     /* restore the xpath context */
 261     if (comp != NULL)
 262         xmlXPathFreeCompExpr(comp);
 263     if (nodeset != NULL)
 264         xmlXPathFreeNodeSet(nodeset);
 265     if (str != NULL)
 266         xmlFree(str);
 267     valuePush(ctxt, ret);
 268     return;
 269 }
 270 
 271 
 272 /**
 273  * exsltDynRegister:
 274  *
 275  * Registers the EXSLT - Dynamic module
 276  */
 277 
 278 void
 279 exsltDynRegister (void) {
 280     xsltRegisterExtModuleFunction ((const xmlChar *) "evaluate",
 281                    EXSLT_DYNAMIC_NAMESPACE,
 282                    exsltDynEvaluateFunction);
 283   xsltRegisterExtModuleFunction ((const xmlChar *) "map",
 284                    EXSLT_DYNAMIC_NAMESPACE,
 285                    exsltDynMapFunction);
 286 
 287 }