functions.c 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. /*
  2. * functions.c: Implementation of the XSLT extra functions
  3. *
  4. * Reference:
  5. * http://www.w3.org/TR/1999/REC-xslt-19991116
  6. *
  7. * See Copyright for the status of this software.
  8. *
  9. * daniel@veillard.com
  10. * Bjorn Reese <breese@users.sourceforge.net> for number formatting
  11. */
  12. #define IN_LIBXSLT
  13. #include "libxslt.h"
  14. #include <string.h>
  15. #ifdef HAVE_SYS_TYPES_H
  16. #include <sys/types.h>
  17. #endif
  18. #ifdef HAVE_CTYPE_H
  19. #include <ctype.h>
  20. #endif
  21. #include <libxml/xmlmemory.h>
  22. #include <libxml/parser.h>
  23. #include <libxml/tree.h>
  24. #include <libxml/valid.h>
  25. #include <libxml/hash.h>
  26. #include <libxml/xmlerror.h>
  27. #include <libxml/xpath.h>
  28. #include <libxml/xpathInternals.h>
  29. #include <libxml/parserInternals.h>
  30. #include <libxml/uri.h>
  31. #include <libxml/xpointer.h>
  32. #include "xslt.h"
  33. #include "xsltInternals.h"
  34. #include "xsltutils.h"
  35. #include "functions.h"
  36. #include "extensions.h"
  37. #include "numbersInternals.h"
  38. #include "keys.h"
  39. #include "documents.h"
  40. #ifdef WITH_XSLT_DEBUG
  41. #define WITH_XSLT_DEBUG_FUNCTION
  42. #endif
  43. /*
  44. * Some versions of DocBook XSL use the vendor string to detect
  45. * supporting chunking, this is a workaround to be considered
  46. * in the list of decent XSLT processors <grin/>
  47. */
  48. #define DOCBOOK_XSL_HACK
  49. /**
  50. * xsltXPathFunctionLookup:
  51. * @vctxt: a void * but the XSLT transformation context actually
  52. * @name: the function name
  53. * @ns_uri: the function namespace URI
  54. *
  55. * This is the entry point when a function is needed by the XPath
  56. * interpretor.
  57. *
  58. * Returns the callback function or NULL if not found
  59. */
  60. xmlXPathFunction
  61. xsltXPathFunctionLookup (void *vctxt,
  62. const xmlChar *name, const xmlChar *ns_uri) {
  63. xmlXPathContextPtr ctxt = (xmlXPathContextPtr) vctxt;
  64. xmlXPathFunction ret;
  65. if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL))
  66. return (NULL);
  67. #ifdef WITH_XSLT_DEBUG_FUNCTION
  68. xsltGenericDebug(xsltGenericDebugContext,
  69. "Lookup function {%s}%s\n", ns_uri, name);
  70. #endif
  71. /* give priority to context-level functions */
  72. /*
  73. ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri);
  74. */
  75. XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri);
  76. if (ret == NULL)
  77. ret = xsltExtModuleFunctionLookup(name, ns_uri);
  78. #ifdef WITH_XSLT_DEBUG_FUNCTION
  79. if (ret != NULL)
  80. xsltGenericDebug(xsltGenericDebugContext,
  81. "found function %s\n", name);
  82. #endif
  83. return(ret);
  84. }
  85. /************************************************************************
  86. * *
  87. * Module interfaces *
  88. * *
  89. ************************************************************************/
  90. static void
  91. xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
  92. {
  93. xsltTransformContextPtr tctxt;
  94. xmlURIPtr uri;
  95. xmlChar *fragment;
  96. xsltDocumentPtr idoc; /* document info */
  97. xmlDocPtr doc;
  98. xmlXPathContextPtr xptrctxt = NULL;
  99. xmlXPathObjectPtr resObj = NULL;
  100. tctxt = xsltXPathGetTransformContext(ctxt);
  101. if (tctxt == NULL) {
  102. xsltTransformError(NULL, NULL, NULL,
  103. "document() : internal error tctxt == NULL\n");
  104. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  105. return;
  106. }
  107. uri = xmlParseURI((const char *) URI);
  108. if (uri == NULL) {
  109. xsltTransformError(tctxt, NULL, NULL,
  110. "document() : failed to parse URI\n");
  111. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  112. return;
  113. }
  114. /*
  115. * check for and remove fragment identifier
  116. */
  117. fragment = (xmlChar *)uri->fragment;
  118. if (fragment != NULL) {
  119. xmlChar *newURI;
  120. uri->fragment = NULL;
  121. newURI = xmlSaveUri(uri);
  122. idoc = xsltLoadDocument(tctxt, newURI);
  123. xmlFree(newURI);
  124. } else
  125. idoc = xsltLoadDocument(tctxt, URI);
  126. xmlFreeURI(uri);
  127. if (idoc == NULL) {
  128. if ((URI == NULL) ||
  129. (URI[0] == '#') ||
  130. ((tctxt->style->doc != NULL) &&
  131. (xmlStrEqual(tctxt->style->doc->URL, URI))))
  132. {
  133. /*
  134. * This selects the stylesheet's doc itself.
  135. */
  136. doc = tctxt->style->doc;
  137. } else {
  138. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  139. if (fragment != NULL)
  140. xmlFree(fragment);
  141. return;
  142. }
  143. } else
  144. doc = idoc->doc;
  145. if (fragment == NULL) {
  146. valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc));
  147. return;
  148. }
  149. /* use XPointer of HTML location for fragment ID */
  150. #ifdef LIBXML_XPTR_ENABLED
  151. xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
  152. if (xptrctxt == NULL) {
  153. xsltTransformError(tctxt, NULL, NULL,
  154. "document() : internal error xptrctxt == NULL\n");
  155. goto out_fragment;
  156. }
  157. #if LIBXML_VERSION >= 20911 || \
  158. defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
  159. xptrctxt->opLimit = ctxt->context->opLimit;
  160. xptrctxt->opCount = ctxt->context->opCount;
  161. xptrctxt->depth = ctxt->context->depth;
  162. resObj = xmlXPtrEval(fragment, xptrctxt);
  163. ctxt->context->opCount = xptrctxt->opCount;
  164. #else
  165. resObj = xmlXPtrEval(fragment, xptrctxt);
  166. #endif
  167. xmlXPathFreeContext(xptrctxt);
  168. #endif /* LIBXML_XPTR_ENABLED */
  169. if (resObj == NULL)
  170. goto out_fragment;
  171. switch (resObj->type) {
  172. case XPATH_NODESET:
  173. break;
  174. case XPATH_UNDEFINED:
  175. case XPATH_BOOLEAN:
  176. case XPATH_NUMBER:
  177. case XPATH_STRING:
  178. case XPATH_POINT:
  179. case XPATH_USERS:
  180. case XPATH_XSLT_TREE:
  181. case XPATH_RANGE:
  182. case XPATH_LOCATIONSET:
  183. xsltTransformError(tctxt, NULL, NULL,
  184. "document() : XPointer does not select a node set: #%s\n",
  185. fragment);
  186. goto out_object;
  187. }
  188. valuePush(ctxt, resObj);
  189. xmlFree(fragment);
  190. return;
  191. out_object:
  192. xmlXPathFreeObject(resObj);
  193. out_fragment:
  194. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  195. xmlFree(fragment);
  196. }
  197. /**
  198. * xsltDocumentFunction:
  199. * @ctxt: the XPath Parser context
  200. * @nargs: the number of arguments
  201. *
  202. * Implement the document() XSLT function
  203. * node-set document(object, node-set?)
  204. */
  205. void
  206. xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs)
  207. {
  208. xmlXPathObjectPtr obj, obj2 = NULL;
  209. xmlChar *base = NULL, *URI;
  210. if ((nargs < 1) || (nargs > 2)) {
  211. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  212. "document() : invalid number of args %d\n",
  213. nargs);
  214. ctxt->error = XPATH_INVALID_ARITY;
  215. return;
  216. }
  217. if (ctxt->value == NULL) {
  218. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  219. "document() : invalid arg value\n");
  220. ctxt->error = XPATH_INVALID_TYPE;
  221. return;
  222. }
  223. if (nargs == 2) {
  224. if (ctxt->value->type != XPATH_NODESET) {
  225. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  226. "document() : invalid arg expecting a nodeset\n");
  227. ctxt->error = XPATH_INVALID_TYPE;
  228. return;
  229. }
  230. obj2 = valuePop(ctxt);
  231. }
  232. if (ctxt->value->type == XPATH_NODESET) {
  233. int i;
  234. xmlXPathObjectPtr newobj, ret;
  235. obj = valuePop(ctxt);
  236. ret = xmlXPathNewNodeSet(NULL);
  237. if ((obj != NULL) && obj->nodesetval) {
  238. for (i = 0; i < obj->nodesetval->nodeNr; i++) {
  239. valuePush(ctxt,
  240. xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
  241. xmlXPathStringFunction(ctxt, 1);
  242. if (nargs == 2) {
  243. valuePush(ctxt, xmlXPathObjectCopy(obj2));
  244. } else {
  245. valuePush(ctxt,
  246. xmlXPathNewNodeSet(obj->nodesetval->
  247. nodeTab[i]));
  248. }
  249. xsltDocumentFunction(ctxt, 2);
  250. newobj = valuePop(ctxt);
  251. ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
  252. newobj->nodesetval);
  253. xmlXPathFreeObject(newobj);
  254. }
  255. }
  256. if (obj != NULL)
  257. xmlXPathFreeObject(obj);
  258. if (obj2 != NULL)
  259. xmlXPathFreeObject(obj2);
  260. valuePush(ctxt, ret);
  261. return;
  262. }
  263. /*
  264. * Make sure it's converted to a string
  265. */
  266. xmlXPathStringFunction(ctxt, 1);
  267. if (ctxt->value->type != XPATH_STRING) {
  268. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  269. "document() : invalid arg expecting a string\n");
  270. ctxt->error = XPATH_INVALID_TYPE;
  271. if (obj2 != NULL)
  272. xmlXPathFreeObject(obj2);
  273. return;
  274. }
  275. obj = valuePop(ctxt);
  276. if (obj->stringval == NULL) {
  277. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  278. } else {
  279. xsltTransformContextPtr tctxt;
  280. tctxt = xsltXPathGetTransformContext(ctxt);
  281. if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
  282. (obj2->nodesetval->nodeNr > 0) &&
  283. IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) {
  284. xmlNodePtr target;
  285. target = obj2->nodesetval->nodeTab[0];
  286. if ((target->type == XML_ATTRIBUTE_NODE) ||
  287. (target->type == XML_PI_NODE)) {
  288. target = ((xmlAttrPtr) target)->parent;
  289. }
  290. base = xmlNodeGetBase(target->doc, target);
  291. } else {
  292. if ((tctxt != NULL) && (tctxt->inst != NULL)) {
  293. base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
  294. } else if ((tctxt != NULL) && (tctxt->style != NULL) &&
  295. (tctxt->style->doc != NULL)) {
  296. base = xmlNodeGetBase(tctxt->style->doc,
  297. (xmlNodePtr) tctxt->style->doc);
  298. }
  299. }
  300. URI = xmlBuildURI(obj->stringval, base);
  301. if (base != NULL)
  302. xmlFree(base);
  303. if (URI == NULL) {
  304. if ((tctxt != NULL) && (tctxt->style != NULL) &&
  305. (tctxt->style->doc != NULL) &&
  306. (xmlStrEqual(URI, tctxt->style->doc->URL))) {
  307. /* This selects the stylesheet's doc itself. */
  308. valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc));
  309. } else {
  310. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  311. }
  312. } else {
  313. xsltDocumentFunctionLoadDocument( ctxt, URI );
  314. xmlFree(URI);
  315. }
  316. }
  317. xmlXPathFreeObject(obj);
  318. if (obj2 != NULL)
  319. xmlXPathFreeObject(obj2);
  320. }
  321. /**
  322. * xsltKeyFunction:
  323. * @ctxt: the XPath Parser context
  324. * @nargs: the number of arguments
  325. *
  326. * Implement the key() XSLT function
  327. * node-set key(string, object)
  328. */
  329. void
  330. xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
  331. xmlXPathObjectPtr obj1, obj2;
  332. if (nargs != 2) {
  333. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  334. "key() : expects two arguments\n");
  335. ctxt->error = XPATH_INVALID_ARITY;
  336. return;
  337. }
  338. /*
  339. * Get the key's value.
  340. */
  341. obj2 = valuePop(ctxt);
  342. xmlXPathStringFunction(ctxt, 1);
  343. if ((obj2 == NULL) ||
  344. (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  345. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  346. "key() : invalid arg expecting a string\n");
  347. ctxt->error = XPATH_INVALID_TYPE;
  348. xmlXPathFreeObject(obj2);
  349. return;
  350. }
  351. /*
  352. * Get the key's name.
  353. */
  354. obj1 = valuePop(ctxt);
  355. if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) {
  356. int i;
  357. xmlXPathObjectPtr newobj, ret;
  358. ret = xmlXPathNewNodeSet(NULL);
  359. if (obj2->nodesetval != NULL) {
  360. for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
  361. valuePush(ctxt, xmlXPathObjectCopy(obj1));
  362. valuePush(ctxt,
  363. xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
  364. xmlXPathStringFunction(ctxt, 1);
  365. xsltKeyFunction(ctxt, 2);
  366. newobj = valuePop(ctxt);
  367. ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
  368. newobj->nodesetval);
  369. xmlXPathFreeObject(newobj);
  370. }
  371. }
  372. valuePush(ctxt, ret);
  373. } else {
  374. xmlNodeSetPtr nodelist = NULL;
  375. xmlChar *key = NULL, *value;
  376. const xmlChar *keyURI;
  377. xsltTransformContextPtr tctxt;
  378. xmlChar *qname, *prefix;
  379. xmlXPathContextPtr xpctxt = ctxt->context;
  380. xmlNodePtr tmpNode = NULL;
  381. xsltDocumentPtr oldDocInfo;
  382. tctxt = xsltXPathGetTransformContext(ctxt);
  383. oldDocInfo = tctxt->document;
  384. if (xpctxt->node == NULL) {
  385. xsltTransformError(tctxt, NULL, tctxt->inst,
  386. "Internal error in xsltKeyFunction(): "
  387. "The context node is not set on the XPath context.\n");
  388. tctxt->state = XSLT_STATE_STOPPED;
  389. goto error;
  390. }
  391. /*
  392. * Get the associated namespace URI if qualified name
  393. */
  394. qname = obj1->stringval;
  395. key = xmlSplitQName2(qname, &prefix);
  396. if (key == NULL) {
  397. key = xmlStrdup(obj1->stringval);
  398. keyURI = NULL;
  399. if (prefix != NULL)
  400. xmlFree(prefix);
  401. } else {
  402. if (prefix != NULL) {
  403. keyURI = xmlXPathNsLookup(xpctxt, prefix);
  404. if (keyURI == NULL) {
  405. xsltTransformError(tctxt, NULL, tctxt->inst,
  406. "key() : prefix %s is not bound\n", prefix);
  407. /*
  408. * TODO: Shouldn't we stop here?
  409. */
  410. }
  411. xmlFree(prefix);
  412. } else {
  413. keyURI = NULL;
  414. }
  415. }
  416. /*
  417. * Force conversion of first arg to string
  418. */
  419. valuePush(ctxt, obj2);
  420. xmlXPathStringFunction(ctxt, 1);
  421. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  422. xsltTransformError(tctxt, NULL, tctxt->inst,
  423. "key() : invalid arg expecting a string\n");
  424. ctxt->error = XPATH_INVALID_TYPE;
  425. goto error;
  426. }
  427. obj2 = valuePop(ctxt);
  428. value = obj2->stringval;
  429. /*
  430. * We need to ensure that ctxt->document is available for
  431. * xsltGetKey().
  432. * First find the relevant doc, which is the context node's
  433. * owner doc; using context->doc is not safe, since
  434. * the doc could have been acquired via the document() function,
  435. * or the doc might be a Result Tree Fragment.
  436. * FUTURE INFO: In XSLT 2.0 the key() function takes an additional
  437. * argument indicating the doc to use.
  438. */
  439. if (xpctxt->node->type == XML_NAMESPACE_DECL) {
  440. /*
  441. * REVISIT: This is a libxml hack! Check xpath.c for details.
  442. * The XPath module sets the owner element of a ns-node on
  443. * the ns->next field.
  444. */
  445. if ((((xmlNsPtr) xpctxt->node)->next != NULL) &&
  446. (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE))
  447. {
  448. tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next;
  449. }
  450. } else
  451. tmpNode = xpctxt->node;
  452. if ((tmpNode == NULL) || (tmpNode->doc == NULL)) {
  453. xsltTransformError(tctxt, NULL, tctxt->inst,
  454. "Internal error in xsltKeyFunction(): "
  455. "Couldn't get the doc of the XPath context node.\n");
  456. goto error;
  457. }
  458. if ((tctxt->document == NULL) ||
  459. (tctxt->document->doc != tmpNode->doc))
  460. {
  461. if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) {
  462. /*
  463. * This is a Result Tree Fragment.
  464. */
  465. if (tmpNode->doc->_private == NULL) {
  466. tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc);
  467. if (tmpNode->doc->_private == NULL)
  468. goto error;
  469. }
  470. tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private;
  471. } else {
  472. /*
  473. * May be the initial source doc or a doc acquired via the
  474. * document() function.
  475. */
  476. tctxt->document = xsltFindDocument(tctxt, tmpNode->doc);
  477. }
  478. if (tctxt->document == NULL) {
  479. xsltTransformError(tctxt, NULL, tctxt->inst,
  480. "Internal error in xsltKeyFunction(): "
  481. "Could not get the document info of a context doc.\n");
  482. tctxt->state = XSLT_STATE_STOPPED;
  483. goto error;
  484. }
  485. }
  486. /*
  487. * Get/compute the key value.
  488. */
  489. nodelist = xsltGetKey(tctxt, key, keyURI, value);
  490. error:
  491. tctxt->document = oldDocInfo;
  492. valuePush(ctxt, xmlXPathWrapNodeSet(
  493. xmlXPathNodeSetMerge(NULL, nodelist)));
  494. if (key != NULL)
  495. xmlFree(key);
  496. }
  497. if (obj1 != NULL)
  498. xmlXPathFreeObject(obj1);
  499. if (obj2 != NULL)
  500. xmlXPathFreeObject(obj2);
  501. }
  502. /**
  503. * xsltUnparsedEntityURIFunction:
  504. * @ctxt: the XPath Parser context
  505. * @nargs: the number of arguments
  506. *
  507. * Implement the unparsed-entity-uri() XSLT function
  508. * string unparsed-entity-uri(string)
  509. */
  510. void
  511. xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
  512. xmlXPathObjectPtr obj;
  513. xmlChar *str;
  514. if ((nargs != 1) || (ctxt->value == NULL)) {
  515. xsltGenericError(xsltGenericErrorContext,
  516. "unparsed-entity-uri() : expects one string arg\n");
  517. ctxt->error = XPATH_INVALID_ARITY;
  518. return;
  519. }
  520. obj = valuePop(ctxt);
  521. if (obj->type != XPATH_STRING) {
  522. obj = xmlXPathConvertString(obj);
  523. }
  524. str = obj->stringval;
  525. if (str == NULL) {
  526. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  527. } else {
  528. xmlEntityPtr entity;
  529. entity = xmlGetDocEntity(ctxt->context->doc, str);
  530. if (entity == NULL) {
  531. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  532. } else {
  533. if (entity->URI != NULL)
  534. valuePush(ctxt, xmlXPathNewString(entity->URI));
  535. else
  536. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  537. }
  538. }
  539. xmlXPathFreeObject(obj);
  540. }
  541. /**
  542. * xsltFormatNumberFunction:
  543. * @ctxt: the XPath Parser context
  544. * @nargs: the number of arguments
  545. *
  546. * Implement the format-number() XSLT function
  547. * string format-number(number, string, string?)
  548. */
  549. void
  550. xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
  551. {
  552. xmlXPathObjectPtr numberObj = NULL;
  553. xmlXPathObjectPtr formatObj = NULL;
  554. xmlXPathObjectPtr decimalObj = NULL;
  555. xsltStylesheetPtr sheet;
  556. xsltDecimalFormatPtr formatValues = NULL;
  557. xmlChar *result;
  558. const xmlChar *ncname;
  559. const xmlChar *prefix = NULL;
  560. const xmlChar *nsUri = NULL;
  561. xsltTransformContextPtr tctxt;
  562. tctxt = xsltXPathGetTransformContext(ctxt);
  563. if ((tctxt == NULL) || (tctxt->inst == NULL))
  564. return;
  565. sheet = tctxt->style;
  566. if (sheet == NULL)
  567. return;
  568. formatValues = sheet->decimalFormat;
  569. switch (nargs) {
  570. case 3:
  571. CAST_TO_STRING;
  572. decimalObj = valuePop(ctxt);
  573. ncname = xsltSplitQName(sheet->dict, decimalObj->stringval, &prefix);
  574. if (prefix != NULL) {
  575. xmlNsPtr ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, prefix);
  576. if (ns == NULL) {
  577. xsltTransformError(tctxt, NULL, NULL,
  578. "format-number : No namespace found for QName '%s:%s'\n",
  579. prefix, ncname);
  580. sheet->errors++;
  581. ncname = NULL;
  582. }
  583. else {
  584. nsUri = ns->href;
  585. }
  586. }
  587. if (ncname != NULL) {
  588. formatValues = xsltDecimalFormatGetByQName(sheet, nsUri, ncname);
  589. }
  590. if (formatValues == NULL) {
  591. xsltTransformError(tctxt, NULL, NULL,
  592. "format-number() : undeclared decimal format '%s'\n",
  593. decimalObj->stringval);
  594. }
  595. /* Intentional fall-through */
  596. case 2:
  597. CAST_TO_STRING;
  598. formatObj = valuePop(ctxt);
  599. CAST_TO_NUMBER;
  600. numberObj = valuePop(ctxt);
  601. break;
  602. default:
  603. XP_ERROR(XPATH_INVALID_ARITY);
  604. }
  605. if (formatValues != NULL) {
  606. if (xsltFormatNumberConversion(formatValues,
  607. formatObj->stringval,
  608. numberObj->floatval,
  609. &result) == XPATH_EXPRESSION_OK) {
  610. valuePush(ctxt, xmlXPathNewString(result));
  611. xmlFree(result);
  612. }
  613. }
  614. xmlXPathFreeObject(numberObj);
  615. xmlXPathFreeObject(formatObj);
  616. xmlXPathFreeObject(decimalObj);
  617. }
  618. /**
  619. * xsltGenerateIdFunction:
  620. * @ctxt: the XPath Parser context
  621. * @nargs: the number of arguments
  622. *
  623. * Implement the generate-id() XSLT function
  624. * string generate-id(node-set?)
  625. */
  626. void
  627. xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
  628. static char base_address;
  629. xmlNodePtr cur = NULL;
  630. xmlXPathObjectPtr obj = NULL;
  631. long val;
  632. xmlChar str[30];
  633. if (nargs == 0) {
  634. cur = ctxt->context->node;
  635. } else if (nargs == 1) {
  636. xmlNodeSetPtr nodelist;
  637. int i, ret;
  638. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
  639. ctxt->error = XPATH_INVALID_TYPE;
  640. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  641. "generate-id() : invalid arg expecting a node-set\n");
  642. return;
  643. }
  644. obj = valuePop(ctxt);
  645. nodelist = obj->nodesetval;
  646. if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
  647. xmlXPathFreeObject(obj);
  648. valuePush(ctxt, xmlXPathNewCString(""));
  649. return;
  650. }
  651. cur = nodelist->nodeTab[0];
  652. for (i = 1;i < nodelist->nodeNr;i++) {
  653. ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
  654. if (ret == -1)
  655. cur = nodelist->nodeTab[i];
  656. }
  657. } else {
  658. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  659. "generate-id() : invalid number of args %d\n", nargs);
  660. ctxt->error = XPATH_INVALID_ARITY;
  661. return;
  662. }
  663. if (obj)
  664. xmlXPathFreeObject(obj);
  665. val = (long)((char *)cur - (char *)&base_address);
  666. if (val >= 0) {
  667. snprintf((char *)str, sizeof(str), "idp%ld", val);
  668. } else {
  669. snprintf((char *)str, sizeof(str), "idm%ld", -val);
  670. }
  671. valuePush(ctxt, xmlXPathNewString(str));
  672. }
  673. /**
  674. * xsltSystemPropertyFunction:
  675. * @ctxt: the XPath Parser context
  676. * @nargs: the number of arguments
  677. *
  678. * Implement the system-property() XSLT function
  679. * object system-property(string)
  680. */
  681. void
  682. xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
  683. xmlXPathObjectPtr obj;
  684. xmlChar *prefix, *name;
  685. const xmlChar *nsURI = NULL;
  686. if (nargs != 1) {
  687. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  688. "system-property() : expects one string arg\n");
  689. ctxt->error = XPATH_INVALID_ARITY;
  690. return;
  691. }
  692. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  693. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  694. "system-property() : invalid arg expecting a string\n");
  695. ctxt->error = XPATH_INVALID_TYPE;
  696. return;
  697. }
  698. obj = valuePop(ctxt);
  699. if (obj->stringval == NULL) {
  700. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  701. } else {
  702. name = xmlSplitQName2(obj->stringval, &prefix);
  703. if (name == NULL) {
  704. name = xmlStrdup(obj->stringval);
  705. } else {
  706. nsURI = xmlXPathNsLookup(ctxt->context, prefix);
  707. if (nsURI == NULL) {
  708. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  709. "system-property() : prefix %s is not bound\n", prefix);
  710. }
  711. }
  712. if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) {
  713. #ifdef DOCBOOK_XSL_HACK
  714. if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
  715. xsltStylesheetPtr sheet;
  716. xsltTransformContextPtr tctxt;
  717. tctxt = xsltXPathGetTransformContext(ctxt);
  718. if ((tctxt != NULL) && (tctxt->inst != NULL) &&
  719. (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) &&
  720. (tctxt->inst->parent != NULL) &&
  721. (xmlStrEqual(tctxt->inst->parent->name,
  722. BAD_CAST "template")))
  723. sheet = tctxt->style;
  724. else
  725. sheet = NULL;
  726. if ((sheet != NULL) && (sheet->doc != NULL) &&
  727. (sheet->doc->URL != NULL) &&
  728. (xmlStrstr(sheet->doc->URL,
  729. (const xmlChar *)"chunk") != NULL)) {
  730. valuePush(ctxt, xmlXPathNewString(
  731. (const xmlChar *)"libxslt (SAXON 6.2 compatible)"));
  732. } else {
  733. valuePush(ctxt, xmlXPathNewString(
  734. (const xmlChar *)XSLT_DEFAULT_VENDOR));
  735. }
  736. } else
  737. #else
  738. if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
  739. valuePush(ctxt, xmlXPathNewString(
  740. (const xmlChar *)XSLT_DEFAULT_VENDOR));
  741. } else
  742. #endif
  743. if (xmlStrEqual(name, (const xmlChar *)"version")) {
  744. valuePush(ctxt, xmlXPathNewString(
  745. (const xmlChar *)XSLT_DEFAULT_VERSION));
  746. } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) {
  747. valuePush(ctxt, xmlXPathNewString(
  748. (const xmlChar *)XSLT_DEFAULT_URL));
  749. } else {
  750. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  751. }
  752. } else {
  753. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  754. }
  755. if (name != NULL)
  756. xmlFree(name);
  757. if (prefix != NULL)
  758. xmlFree(prefix);
  759. }
  760. xmlXPathFreeObject(obj);
  761. }
  762. /**
  763. * xsltElementAvailableFunction:
  764. * @ctxt: the XPath Parser context
  765. * @nargs: the number of arguments
  766. *
  767. * Implement the element-available() XSLT function
  768. * boolean element-available(string)
  769. */
  770. void
  771. xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
  772. xmlXPathObjectPtr obj;
  773. xmlChar *prefix, *name;
  774. const xmlChar *nsURI = NULL;
  775. xsltTransformContextPtr tctxt;
  776. if (nargs != 1) {
  777. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  778. "element-available() : expects one string arg\n");
  779. ctxt->error = XPATH_INVALID_ARITY;
  780. return;
  781. }
  782. xmlXPathStringFunction(ctxt, 1);
  783. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  784. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  785. "element-available() : invalid arg expecting a string\n");
  786. ctxt->error = XPATH_INVALID_TYPE;
  787. return;
  788. }
  789. obj = valuePop(ctxt);
  790. tctxt = xsltXPathGetTransformContext(ctxt);
  791. if ((tctxt == NULL) || (tctxt->inst == NULL)) {
  792. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  793. "element-available() : internal error tctxt == NULL\n");
  794. xmlXPathFreeObject(obj);
  795. valuePush(ctxt, xmlXPathNewBoolean(0));
  796. return;
  797. }
  798. name = xmlSplitQName2(obj->stringval, &prefix);
  799. if (name == NULL) {
  800. xmlNsPtr ns;
  801. name = xmlStrdup(obj->stringval);
  802. ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
  803. if (ns != NULL) nsURI = ns->href;
  804. } else {
  805. nsURI = xmlXPathNsLookup(ctxt->context, prefix);
  806. if (nsURI == NULL) {
  807. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  808. "element-available() : prefix %s is not bound\n", prefix);
  809. }
  810. }
  811. if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) {
  812. valuePush(ctxt, xmlXPathNewBoolean(1));
  813. } else {
  814. valuePush(ctxt, xmlXPathNewBoolean(0));
  815. }
  816. xmlXPathFreeObject(obj);
  817. if (name != NULL)
  818. xmlFree(name);
  819. if (prefix != NULL)
  820. xmlFree(prefix);
  821. }
  822. /**
  823. * xsltFunctionAvailableFunction:
  824. * @ctxt: the XPath Parser context
  825. * @nargs: the number of arguments
  826. *
  827. * Implement the function-available() XSLT function
  828. * boolean function-available(string)
  829. */
  830. void
  831. xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
  832. xmlXPathObjectPtr obj;
  833. xmlChar *prefix, *name;
  834. const xmlChar *nsURI = NULL;
  835. if (nargs != 1) {
  836. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  837. "function-available() : expects one string arg\n");
  838. ctxt->error = XPATH_INVALID_ARITY;
  839. return;
  840. }
  841. xmlXPathStringFunction(ctxt, 1);
  842. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  843. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  844. "function-available() : invalid arg expecting a string\n");
  845. ctxt->error = XPATH_INVALID_TYPE;
  846. return;
  847. }
  848. obj = valuePop(ctxt);
  849. name = xmlSplitQName2(obj->stringval, &prefix);
  850. if (name == NULL) {
  851. name = xmlStrdup(obj->stringval);
  852. } else {
  853. nsURI = xmlXPathNsLookup(ctxt->context, prefix);
  854. if (nsURI == NULL) {
  855. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  856. "function-available() : prefix %s is not bound\n", prefix);
  857. }
  858. }
  859. if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
  860. valuePush(ctxt, xmlXPathNewBoolean(1));
  861. } else {
  862. valuePush(ctxt, xmlXPathNewBoolean(0));
  863. }
  864. xmlXPathFreeObject(obj);
  865. if (name != NULL)
  866. xmlFree(name);
  867. if (prefix != NULL)
  868. xmlFree(prefix);
  869. }
  870. /**
  871. * xsltCurrentFunction:
  872. * @ctxt: the XPath Parser context
  873. * @nargs: the number of arguments
  874. *
  875. * Implement the current() XSLT function
  876. * node-set current()
  877. */
  878. static void
  879. xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
  880. xsltTransformContextPtr tctxt;
  881. if (nargs != 0) {
  882. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  883. "current() : function uses no argument\n");
  884. ctxt->error = XPATH_INVALID_ARITY;
  885. return;
  886. }
  887. tctxt = xsltXPathGetTransformContext(ctxt);
  888. if (tctxt == NULL) {
  889. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  890. "current() : internal error tctxt == NULL\n");
  891. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  892. } else {
  893. valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
  894. }
  895. }
  896. /************************************************************************
  897. * *
  898. * Registration of XSLT and libxslt functions *
  899. * *
  900. ************************************************************************/
  901. /**
  902. * xsltRegisterAllFunctions:
  903. * @ctxt: the XPath context
  904. *
  905. * Registers all default XSLT functions in this context
  906. */
  907. void
  908. xsltRegisterAllFunctions(xmlXPathContextPtr ctxt)
  909. {
  910. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
  911. xsltCurrentFunction);
  912. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
  913. xsltDocumentFunction);
  914. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
  915. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
  916. xsltUnparsedEntityURIFunction);
  917. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
  918. xsltFormatNumberFunction);
  919. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
  920. xsltGenerateIdFunction);
  921. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
  922. xsltSystemPropertyFunction);
  923. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
  924. xsltElementAvailableFunction);
  925. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
  926. xsltFunctionAvailableFunction);
  927. }