1#define IN_LIBEXSLT 2#include "libexslt/libexslt.h" 3 4#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) 5#include <win32config.h> 6#else 7#include "config.h" 8#endif 9 10#include <string.h> 11 12#include <libxml/tree.h> 13#include <libxml/xpath.h> 14#include <libxml/xpathInternals.h> 15#include <libxml/hash.h> 16#include <libxml/debugXML.h> 17 18#include <libxslt/xsltutils.h> 19#include <libxslt/variables.h> 20#include <libxslt/xsltInternals.h> 21#include <libxslt/extensions.h> 22#include <libxslt/transform.h> 23#include <libxslt/imports.h> 24 25#include "exslt.h" 26 27typedef struct _exsltFuncFunctionData exsltFuncFunctionData; 28struct _exsltFuncFunctionData { 29 int nargs; /* number of arguments to the function */ 30 xmlNodePtr content; /* the func:fuction template content */ 31}; 32 33typedef struct _exsltFuncData exsltFuncData; 34struct _exsltFuncData { 35 xmlHashTablePtr funcs; /* pointer to the stylesheet module data */ 36 xmlXPathObjectPtr result; /* returned by func:result */ 37 int error; /* did an error occur? */ 38}; 39 40typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp; 41struct _exsltFuncResultPreComp { 42 xsltElemPreComp comp; 43 xmlXPathCompExprPtr select; 44 xmlNsPtr *nsList; 45 int nsNr; 46}; 47 48/* Used for callback function in exsltInitFunc */ 49typedef struct _exsltFuncImportRegData exsltFuncImportRegData; 50struct _exsltFuncImportRegData { 51 xsltTransformContextPtr ctxt; 52 xmlHashTablePtr hash; 53}; 54 55static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, 56 int nargs); 57static exsltFuncFunctionData *exsltFuncNewFunctionData(void); 58 59/** 60 * exsltFuncRegisterFunc: 61 * @func: the #exsltFuncFunctionData for the function 62 * @ctxt: an XSLT transformation context 63 * @URI: the function namespace URI 64 * @name: the function name 65 * 66 * Registers a function declared by a func:function element 67 */ 68static void 69exsltFuncRegisterFunc (exsltFuncFunctionData *data, 70 xsltTransformContextPtr ctxt, 71 const xmlChar *URI, const xmlChar *name, 72 ATTRIBUTE_UNUSED const xmlChar *ignored) { 73 if ((data == NULL) || (ctxt == NULL) || (URI == NULL) || (name == NULL)) 74 return; 75 76 xsltGenericDebug(xsltGenericDebugContext, 77 "exsltFuncRegisterFunc: register {%s}%s\n", 78 URI, name); 79 xsltRegisterExtFunction(ctxt, name, URI, 80 exsltFuncFunctionFunction); 81} 82 83/* 84 * exsltFuncRegisterImportFunc 85 * @data: the exsltFuncFunctionData for the function 86 * @ch: structure containing context and hash table 87 * @URI: the function namespace URI 88 * @name: the function name 89 * 90 * Checks if imported function is already registered in top-level 91 * stylesheet. If not, copies function data and registers function 92 */ 93static void 94exsltFuncRegisterImportFunc (exsltFuncFunctionData *data, 95 exsltFuncImportRegData *ch, 96 const xmlChar *URI, const xmlChar *name, 97 ATTRIBUTE_UNUSED const xmlChar *ignored) { 98 exsltFuncFunctionData *func=NULL; 99 100 if ((data == NULL) || (ch == NULL) || (URI == NULL) || (name == NULL)) 101 return; 102 103 if (ch->ctxt == NULL || ch->hash == NULL) 104 return; 105 106 /* Check if already present */ 107 func = (exsltFuncFunctionData*)xmlHashLookup2(ch->hash, URI, name); 108 if (func == NULL) { /* Not yet present - copy it in */ 109 func = exsltFuncNewFunctionData(); 110 memcpy(func, data, sizeof(exsltFuncFunctionData)); 111 if (xmlHashAddEntry2(ch->hash, URI, name, func) < 0) { 112 xsltGenericError(xsltGenericErrorContext, 113 "Failed to register function {%s}%s\n", 114 URI, name); 115 } else { /* Do the registration */ 116 xsltGenericDebug(xsltGenericDebugContext, 117 "exsltFuncRegisterImportFunc: register {%s}%s\n", 118 URI, name); 119 xsltRegisterExtFunction(ch->ctxt, name, URI, 120 exsltFuncFunctionFunction); 121 } 122 } 123} 124 125/** 126 * exsltFuncInit: 127 * @ctxt: an XSLT transformation context 128 * @URI: the namespace URI for the extension 129 * 130 * Initializes the EXSLT - Functions module. 131 * Called at transformation-time; merges all 132 * functions declared in the import tree taking 133 * import precedence into account, i.e. overriding 134 * functions with lower import precedence. 135 * 136 * Returns the data for this transformation 137 */ 138static exsltFuncData * 139exsltFuncInit (xsltTransformContextPtr ctxt, const xmlChar *URI) { 140 exsltFuncData *ret; 141 xsltStylesheetPtr tmp; 142 exsltFuncImportRegData ch; 143 xmlHashTablePtr hash; 144 145 ret = (exsltFuncData *) xmlMalloc (sizeof(exsltFuncData)); 146 if (ret == NULL) { 147 xsltGenericError(xsltGenericErrorContext, 148 "exsltFuncInit: not enough memory\n"); 149 return(NULL); 150 } 151 memset(ret, 0, sizeof(exsltFuncData)); 152 153 ret->result = NULL; 154 ret->error = 0; 155 156 ch.hash = (xmlHashTablePtr) xsltStyleGetExtData(ctxt->style, URI); 157 ret->funcs = ch.hash; 158 xmlHashScanFull(ch.hash, (xmlHashScannerFull) exsltFuncRegisterFunc, ctxt); 159 tmp = ctxt->style; 160 ch.ctxt = ctxt; 161 while ((tmp=xsltNextImport(tmp))!=NULL) { 162 hash = xsltGetExtInfo(tmp, URI); 163 if (hash != NULL) { 164 xmlHashScanFull(hash, 165 (xmlHashScannerFull) exsltFuncRegisterImportFunc, &ch); 166 } 167 } 168 169 return(ret); 170} 171 172/** 173 * exsltFuncShutdown: 174 * @ctxt: an XSLT transformation context 175 * @URI: the namespace URI for the extension 176 * @data: the module data to free up 177 * 178 * Shutdown the EXSLT - Functions module 179 * Called at transformation-time. 180 */ 181static void 182exsltFuncShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, 183 const xmlChar *URI ATTRIBUTE_UNUSED, 184 exsltFuncData *data) { 185 if (data->result != NULL) 186 xmlXPathFreeObject(data->result); 187 xmlFree(data); 188} 189 190/** 191 * exsltFuncStyleInit: 192 * @style: an XSLT stylesheet 193 * @URI: the namespace URI for the extension 194 * 195 * Allocates the stylesheet data for EXSLT - Function 196 * Called at compile-time. 197 * 198 * Returns the allocated data 199 */ 200static xmlHashTablePtr 201exsltFuncStyleInit (xsltStylesheetPtr style ATTRIBUTE_UNUSED, 202 const xmlChar *URI ATTRIBUTE_UNUSED) { 203 return xmlHashCreate(1); 204} 205 206/** 207 * exsltFuncStyleShutdown: 208 * @style: an XSLT stylesheet 209 * @URI: the namespace URI for the extension 210 * @data: the stylesheet data to free up 211 * 212 * Shutdown the EXSLT - Function module 213 * Called at compile-time. 214 */ 215static void 216exsltFuncStyleShutdown (xsltStylesheetPtr style ATTRIBUTE_UNUSED, 217 const xmlChar *URI ATTRIBUTE_UNUSED, 218 xmlHashTablePtr data) { 219 xmlHashFree(data, (xmlHashDeallocator) xmlFree); 220} 221 222/** 223 * exsltFuncNewFunctionData: 224 * 225 * Allocates an #exslFuncFunctionData object 226 * 227 * Returns the new structure 228 */ 229static exsltFuncFunctionData * 230exsltFuncNewFunctionData (void) { 231 exsltFuncFunctionData *ret; 232 233 ret = (exsltFuncFunctionData *) xmlMalloc (sizeof(exsltFuncFunctionData)); 234 if (ret == NULL) { 235 xsltGenericError(xsltGenericErrorContext, 236 "exsltFuncNewFunctionData: not enough memory\n"); 237 return (NULL); 238 } 239 memset(ret, 0, sizeof(exsltFuncFunctionData)); 240 241 ret->nargs = 0; 242 ret->content = NULL; 243 244 return(ret); 245} 246 247/** 248 * exsltFreeFuncResultPreComp: 249 * @comp: the #exsltFuncResultPreComp to free up 250 * 251 * Deallocates an #exsltFuncResultPreComp 252 */ 253static void 254exsltFreeFuncResultPreComp (exsltFuncResultPreComp *comp) { 255 if (comp == NULL) 256 return; 257 258 if (comp->select != NULL) 259 xmlXPathFreeCompExpr (comp->select); 260 if (comp->nsList != NULL) 261 xmlFree(comp->nsList); 262 xmlFree(comp); 263} 264 265/** 266 * exsltFuncFunctionFunction: 267 * @ctxt: an XPath parser context 268 * @nargs: the number of arguments 269 * 270 * Evaluates the func:function element that defines the called function. 271 */ 272static void 273exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) { 274 xmlXPathObjectPtr obj, oldResult, ret; 275 exsltFuncData *data; 276 exsltFuncFunctionData *func; 277 xmlNodePtr paramNode, oldInsert, fake, content = NULL; 278 int oldBase; 279 xsltStackElemPtr params = NULL, param; 280 xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt); 281 int i; 282 283 /* 284 * retrieve func:function template 285 */ 286 data = (exsltFuncData *) xsltGetExtData (tctxt, 287 EXSLT_FUNCTIONS_NAMESPACE); 288 oldResult = data->result; 289 data->result = NULL; 290 291 func = (exsltFuncFunctionData*) xmlHashLookup2 (data->funcs, 292 ctxt->context->functionURI, 293 ctxt->context->function); 294 295 /* 296 * params handling 297 */ 298 if (nargs > func->nargs) { 299 xsltGenericError(xsltGenericErrorContext, 300 "{%s}%s: called with too many arguments\n", 301 ctxt->context->functionURI, ctxt->context->function); 302 ctxt->error = XPATH_INVALID_ARITY; 303 return; 304 } 305 if (func->content != NULL) { 306 paramNode = func->content->prev; 307 content = func->content; 308 } 309 else 310 paramNode = NULL; 311 if ((paramNode == NULL) && (func->nargs != 0)) { 312 xsltGenericError(xsltGenericErrorContext, 313 "exsltFuncFunctionFunction: nargs != 0 and " 314 "param == NULL\n"); 315 return; 316 } 317 318 /* set params */ 319 for (i = func->nargs; (i > nargs) && (paramNode != NULL); i--) { 320 paramNode = paramNode->prev; 321 if (content != NULL) 322 content = content->prev; 323 } 324 while ((i-- > 0) && (paramNode != NULL)) { 325 obj = valuePop(ctxt); 326 /* FIXME: this is a bit hackish */ 327 param = xsltParseStylesheetCallerParam (tctxt, paramNode); 328 param->computed = 1; 329 if (param->value != NULL) 330 xmlXPathFreeObject(param->value); 331 param->value = obj; 332 param->next = params; 333 params = param; 334 paramNode = paramNode->prev; 335 } 336 337 /* 338 * actual processing 339 */ 340 fake = xmlNewDocNode(tctxt->output, NULL, 341 (const xmlChar *)"fake", NULL); 342 oldInsert = tctxt->insert; 343 tctxt->insert = fake; 344 /* 345 * In order to give the function variables a new 'scope' we 346 * change varsBase in the context. 347 */ 348 oldBase = tctxt->varsBase; 349 tctxt->varsBase = tctxt->varsNr; 350 xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt), 351 content, NULL, params); 352 tctxt->insert = oldInsert; 353 tctxt->varsBase = oldBase; /* restore original scope */ 354 if (params != NULL) 355 xsltFreeStackElemList(params); 356 357 if (data->error != 0) 358 return; 359 360 if (data->result != NULL) 361 ret = data->result; 362 else 363 ret = xmlXPathNewCString(""); 364 365 data->result = oldResult; 366 367 /* 368 * It is an error if the instantiation of the template results in 369 * the generation of result nodes. 370 */ 371 if (fake->children != NULL) { 372#ifdef LIBXML_DEBUG_ENABLED 373 xmlDebugDumpNode (stderr, fake, 1); 374#endif 375 xsltGenericError(xsltGenericErrorContext, 376 "{%s}%s: cannot write to result tree while " 377 "executing a function\n", 378 ctxt->context->functionURI, ctxt->context->function); 379 xmlFreeNode(fake); 380 return; 381 } 382 xmlFreeNode(fake); 383 valuePush(ctxt, ret); 384} 385 386 387static void 388exsltFuncFunctionComp (xsltStylesheetPtr style, xmlNodePtr inst) { 389 xmlChar *name, *prefix; 390 xmlNsPtr ns; 391 xmlHashTablePtr data; 392 exsltFuncFunctionData *func; 393 394 if ((style == NULL) || (inst == NULL)) 395 return; 396 397 398 { 399 xmlChar *qname; 400 401 qname = xmlGetProp(inst, (const xmlChar *) "name"); 402 name = xmlSplitQName2 (qname, &prefix); 403 xmlFree(qname); 404 } 405 if ((name == NULL) || (prefix == NULL)) { 406 xsltGenericError(xsltGenericErrorContext, 407 "func:function: not a QName\n"); 408 if (name != NULL) 409 xmlFree(name); 410 return; 411 } 412 /* namespace lookup */ 413 ns = xmlSearchNs (inst->doc, inst, prefix); 414 if (ns == NULL) { 415 xsltGenericError(xsltGenericErrorContext, 416 "func:function: undeclared prefix %s\n", 417 prefix); 418 xmlFree(name); 419 xmlFree(prefix); 420 return; 421 } 422 xmlFree(prefix); 423 424 /* 425 * Create function data 426 */ 427 func = exsltFuncNewFunctionData(); 428 func->content = inst->children; 429 while (IS_XSLT_ELEM(func->content) && 430 IS_XSLT_NAME(func->content, "param")) { 431 func->content = func->content->next; 432 func->nargs++; 433 } 434 435 xsltParseTemplateContent(style, inst); 436 437 /* 438 * Register the function data such that it can be retrieved 439 * by exslFuncFunctionFunction 440 */ 441#ifdef XSLT_REFACTORED 442 /* 443 * Ensure that the hash table will be stored in the *current* 444 * stylesheet level in order to correctly evaluate the 445 * import precedence. 446 */ 447 data = (xmlHashTablePtr) 448 xsltStyleStylesheetLevelGetExtData(style, 449 EXSLT_FUNCTIONS_NAMESPACE); 450#else 451 data = (xmlHashTablePtr) 452 xsltStyleGetExtData (style, EXSLT_FUNCTIONS_NAMESPACE); 453#endif 454 if (data == NULL) { 455 xsltGenericError(xsltGenericErrorContext, 456 "exsltFuncFunctionComp: no stylesheet data\n"); 457 xmlFree(name); 458 return; 459 } 460 461 if (xmlHashAddEntry2 (data, ns->href, name, func) < 0) { 462 xsltTransformError(NULL, style, inst, 463 "Failed to register function {%s}%s\n", 464 ns->href, name); 465 style->errors++; 466 } else { 467 xsltGenericDebug(xsltGenericDebugContext, 468 "exsltFuncFunctionComp: register {%s}%s\n", 469 ns->href, name); 470 } 471 xmlFree(name); 472} 473 474static xsltElemPreCompPtr 475exsltFuncResultComp (xsltStylesheetPtr style, xmlNodePtr inst, 476 xsltTransformFunction function) { 477 xmlNodePtr test; 478 xmlChar *sel; 479 exsltFuncResultPreComp *ret; 480 481 /* 482 * "Validity" checking 483 */ 484 /* it is an error to have any following sibling elements aside 485 * from the xsl:fallback element. 486 */ 487 for (test = inst->next; test != NULL; test = test->next) { 488 if (test->type != XML_ELEMENT_NODE) 489 continue; 490 if (IS_XSLT_ELEM(test) && IS_XSLT_NAME(test, "fallback")) 491 continue; 492 xsltGenericError(xsltGenericErrorContext, 493 "exsltFuncResultElem: only xsl:fallback is " 494 "allowed to follow func:result\n"); 495 return (NULL); 496 } 497 /* it is an error for a func:result element to not be a descendant 498 * of func:function. 499 * it is an error if a func:result occurs within a func:result 500 * element. 501 * it is an error if instanciating the content of a variable 502 * binding element (i.e. xsl:variable, xsl:param) results in the 503 * instanciation of a func:result element. 504 */ 505 for (test = inst->parent; test != NULL; test = test->parent) { 506 if ((test->ns != NULL) && 507 (xmlStrEqual(test->ns->href, EXSLT_FUNCTIONS_NAMESPACE))) { 508 if (xmlStrEqual(test->name, (const xmlChar *) "function")) { 509 break; 510 } 511 if (xmlStrEqual(test->name, (const xmlChar *) "result")) { 512 xsltGenericError(xsltGenericErrorContext, 513 "func:result element not allowed within" 514 " another func:result element\n"); 515 return (NULL); 516 } 517 } 518 if (IS_XSLT_ELEM(test) && 519 (IS_XSLT_NAME(test, "variable") || 520 IS_XSLT_NAME(test, "param"))) { 521 xsltGenericError(xsltGenericErrorContext, 522 "func:result element not allowed within" 523 " a variable binding element\n"); 524 return (NULL); 525 } 526 } 527 528 /* 529 * Precomputation 530 */ 531 ret = (exsltFuncResultPreComp *) 532 xmlMalloc (sizeof(exsltFuncResultPreComp)); 533 if (ret == NULL) { 534 xsltPrintErrorContext(NULL, NULL, NULL); 535 xsltGenericError(xsltGenericErrorContext, 536 "exsltFuncResultComp : malloc failed\n"); 537 return (NULL); 538 } 539 memset(ret, 0, sizeof(exsltFuncResultPreComp)); 540 541 xsltInitElemPreComp ((xsltElemPreCompPtr) ret, style, inst, function, 542 (xsltElemPreCompDeallocator) exsltFreeFuncResultPreComp); 543 ret->select = NULL; 544 545 /* 546 * Precompute the select attribute 547 */ 548 sel = xmlGetNsProp(inst, (const xmlChar *) "select", NULL); 549 if (sel != NULL) { 550 ret->select = xmlXPathCompile (sel); 551 xmlFree(sel); 552 } 553 /* 554 * Precompute the namespace list 555 */ 556 ret->nsList = xmlGetNsList(inst->doc, inst); 557 if (ret->nsList != NULL) { 558 int i = 0; 559 while (ret->nsList[i] != NULL) 560 i++; 561 ret->nsNr = i; 562 } 563 return ((xsltElemPreCompPtr) ret); 564} 565 566static void 567exsltFuncResultElem (xsltTransformContextPtr ctxt, 568 xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, 569 exsltFuncResultPreComp *comp) { 570 exsltFuncData *data; 571 xmlXPathObjectPtr ret; 572 xmlNsPtr *oldNsList; 573 int oldNsNr; 574 575 /* It is an error if instantiating the content of the 576 * func:function element results in the instantiation of more than 577 * one func:result elements. 578 */ 579 data = (exsltFuncData *) xsltGetExtData (ctxt, EXSLT_FUNCTIONS_NAMESPACE); 580 if (data == NULL) { 581 xsltGenericError(xsltGenericErrorContext, 582 "exsltFuncReturnElem: data == NULL\n"); 583 return; 584 } 585 if (data->result != NULL) { 586 xsltGenericError(xsltGenericErrorContext, 587 "func:result already instanciated\n"); 588 data->error = 1; 589 return; 590 } 591 /* 592 * Processing 593 */ 594 if (comp->select != NULL) { 595 /* If the func:result element has a select attribute, then the 596 * value of the attribute must be an expression and the 597 * returned value is the object that results from evaluating 598 * the expression. In this case, the content must be empty. 599 */ 600 if (inst->children != NULL) { 601 xsltGenericError(xsltGenericErrorContext, 602 "func:result content must be empty if it" 603 " has a select attribute\n"); 604 data->error = 1; 605 return; 606 } 607 oldNsList = ctxt->xpathCtxt->namespaces; 608 oldNsNr = ctxt->xpathCtxt->nsNr; 609 ctxt->xpathCtxt->namespaces = comp->nsList; 610 ctxt->xpathCtxt->nsNr = comp->nsNr; 611 ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt); 612 ctxt->xpathCtxt->nsNr = oldNsNr; 613 ctxt->xpathCtxt->namespaces = oldNsList; 614 if (ret == NULL) { 615 xsltGenericError(xsltGenericErrorContext, 616 "exsltFuncResultElem: ret == NULL\n"); 617 return; 618 } 619 } else if (inst->children != NULL) { 620 /* If the func:result element does not have a select attribute 621 * and has non-empty content (i.e. the func:result element has 622 * one or more child nodes), then the content of the 623 * func:result element specifies the value. 624 */ 625 xmlNodePtr oldInsert; 626 xmlDocPtr container; 627 628 container = xsltCreateRVT(ctxt); 629 if (container == NULL) { 630 xsltGenericError(xsltGenericErrorContext, 631 "exsltFuncResultElem: out of memory\n"); 632 data->error = 1; 633 return; 634 } 635 xsltRegisterTmpRVT(ctxt, container); 636 oldInsert = ctxt->insert; 637 ctxt->insert = (xmlNodePtr) container; 638 xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node, 639 inst->children, NULL, NULL); 640 ctxt->insert = oldInsert; 641 642 ret = xmlXPathNewValueTree((xmlNodePtr) container); 643 if (ret == NULL) { 644 xsltGenericError(xsltGenericErrorContext, 645 "exsltFuncResultElem: ret == NULL\n"); 646 data->error = 1; 647 } else { 648 ret->boolval = 0; /* Freeing is not handled there anymore */ 649 } 650 } else { 651 /* If the func:result element has empty content and does not 652 * have a select attribute, then the returned value is an 653 * empty string. 654 */ 655 ret = xmlXPathNewCString(""); 656 } 657 data->result = ret; 658} 659 660/** 661 * exsltFuncRegister: 662 * 663 * Registers the EXSLT - Functions module 664 */ 665void 666exsltFuncRegister (void) { 667 xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE, 668 (xsltExtInitFunction) exsltFuncInit, 669 (xsltExtShutdownFunction) exsltFuncShutdown, 670 (xsltStyleExtInitFunction) exsltFuncStyleInit, 671 (xsltStyleExtShutdownFunction) exsltFuncStyleShutdown); 672 673 xsltRegisterExtModuleTopLevel ((const xmlChar *) "function", 674 EXSLT_FUNCTIONS_NAMESPACE, 675 exsltFuncFunctionComp); 676 xsltRegisterExtModuleElement ((const xmlChar *) "result", 677 EXSLT_FUNCTIONS_NAMESPACE, 678 (xsltPreComputeFunction)exsltFuncResultComp, 679 (xsltTransformFunction) exsltFuncResultElem); 680} 681