functions.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  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. resObj = xmlXPtrEval(fragment, xptrctxt);
  158. xmlXPathFreeContext(xptrctxt);
  159. #endif
  160. if (resObj == NULL)
  161. goto out_fragment;
  162. switch (resObj->type) {
  163. case XPATH_NODESET:
  164. break;
  165. case XPATH_UNDEFINED:
  166. case XPATH_BOOLEAN:
  167. case XPATH_NUMBER:
  168. case XPATH_STRING:
  169. case XPATH_POINT:
  170. case XPATH_USERS:
  171. case XPATH_XSLT_TREE:
  172. case XPATH_RANGE:
  173. case XPATH_LOCATIONSET:
  174. xsltTransformError(tctxt, NULL, NULL,
  175. "document() : XPointer does not select a node set: #%s\n",
  176. fragment);
  177. goto out_object;
  178. }
  179. valuePush(ctxt, resObj);
  180. xmlFree(fragment);
  181. return;
  182. out_object:
  183. xmlXPathFreeObject(resObj);
  184. out_fragment:
  185. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  186. xmlFree(fragment);
  187. }
  188. /**
  189. * xsltDocumentFunction:
  190. * @ctxt: the XPath Parser context
  191. * @nargs: the number of arguments
  192. *
  193. * Implement the document() XSLT function
  194. * node-set document(object, node-set?)
  195. */
  196. void
  197. xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs)
  198. {
  199. xmlXPathObjectPtr obj, obj2 = NULL;
  200. xmlChar *base = NULL, *URI;
  201. if ((nargs < 1) || (nargs > 2)) {
  202. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  203. "document() : invalid number of args %d\n",
  204. nargs);
  205. ctxt->error = XPATH_INVALID_ARITY;
  206. return;
  207. }
  208. if (ctxt->value == NULL) {
  209. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  210. "document() : invalid arg value\n");
  211. ctxt->error = XPATH_INVALID_TYPE;
  212. return;
  213. }
  214. if (nargs == 2) {
  215. if (ctxt->value->type != XPATH_NODESET) {
  216. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  217. "document() : invalid arg expecting a nodeset\n");
  218. ctxt->error = XPATH_INVALID_TYPE;
  219. return;
  220. }
  221. obj2 = valuePop(ctxt);
  222. }
  223. if (ctxt->value->type == XPATH_NODESET) {
  224. int i;
  225. xmlXPathObjectPtr newobj, ret;
  226. obj = valuePop(ctxt);
  227. ret = xmlXPathNewNodeSet(NULL);
  228. if ((obj != NULL) && obj->nodesetval) {
  229. for (i = 0; i < obj->nodesetval->nodeNr; i++) {
  230. valuePush(ctxt,
  231. xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
  232. xmlXPathStringFunction(ctxt, 1);
  233. if (nargs == 2) {
  234. valuePush(ctxt, xmlXPathObjectCopy(obj2));
  235. } else {
  236. valuePush(ctxt,
  237. xmlXPathNewNodeSet(obj->nodesetval->
  238. nodeTab[i]));
  239. }
  240. xsltDocumentFunction(ctxt, 2);
  241. newobj = valuePop(ctxt);
  242. ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
  243. newobj->nodesetval);
  244. xmlXPathFreeObject(newobj);
  245. }
  246. }
  247. if (obj != NULL)
  248. xmlXPathFreeObject(obj);
  249. if (obj2 != NULL)
  250. xmlXPathFreeObject(obj2);
  251. valuePush(ctxt, ret);
  252. return;
  253. }
  254. /*
  255. * Make sure it's converted to a string
  256. */
  257. xmlXPathStringFunction(ctxt, 1);
  258. if (ctxt->value->type != XPATH_STRING) {
  259. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  260. "document() : invalid arg expecting a string\n");
  261. ctxt->error = XPATH_INVALID_TYPE;
  262. if (obj2 != NULL)
  263. xmlXPathFreeObject(obj2);
  264. return;
  265. }
  266. obj = valuePop(ctxt);
  267. if (obj->stringval == NULL) {
  268. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  269. } else {
  270. xsltTransformContextPtr tctxt;
  271. tctxt = xsltXPathGetTransformContext(ctxt);
  272. if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
  273. (obj2->nodesetval->nodeNr > 0) &&
  274. IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) {
  275. xmlNodePtr target;
  276. target = obj2->nodesetval->nodeTab[0];
  277. if ((target->type == XML_ATTRIBUTE_NODE) ||
  278. (target->type == XML_PI_NODE)) {
  279. target = ((xmlAttrPtr) target)->parent;
  280. }
  281. base = xmlNodeGetBase(target->doc, target);
  282. } else {
  283. if ((tctxt != NULL) && (tctxt->inst != NULL)) {
  284. base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
  285. } else if ((tctxt != NULL) && (tctxt->style != NULL) &&
  286. (tctxt->style->doc != NULL)) {
  287. base = xmlNodeGetBase(tctxt->style->doc,
  288. (xmlNodePtr) tctxt->style->doc);
  289. }
  290. }
  291. URI = xmlBuildURI(obj->stringval, base);
  292. if (base != NULL)
  293. xmlFree(base);
  294. if (URI == NULL) {
  295. if ((tctxt != NULL) && (tctxt->style != NULL) &&
  296. (tctxt->style->doc != NULL) &&
  297. (xmlStrEqual(URI, tctxt->style->doc->URL))) {
  298. /* This selects the stylesheet's doc itself. */
  299. valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc));
  300. } else {
  301. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  302. }
  303. } else {
  304. xsltDocumentFunctionLoadDocument( ctxt, URI );
  305. xmlFree(URI);
  306. }
  307. }
  308. xmlXPathFreeObject(obj);
  309. if (obj2 != NULL)
  310. xmlXPathFreeObject(obj2);
  311. }
  312. /**
  313. * xsltKeyFunction:
  314. * @ctxt: the XPath Parser context
  315. * @nargs: the number of arguments
  316. *
  317. * Implement the key() XSLT function
  318. * node-set key(string, object)
  319. */
  320. void
  321. xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
  322. xmlXPathObjectPtr obj1, obj2;
  323. if (nargs != 2) {
  324. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  325. "key() : expects two arguments\n");
  326. ctxt->error = XPATH_INVALID_ARITY;
  327. return;
  328. }
  329. /*
  330. * Get the key's value.
  331. */
  332. obj2 = valuePop(ctxt);
  333. xmlXPathStringFunction(ctxt, 1);
  334. if ((obj2 == NULL) ||
  335. (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  336. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  337. "key() : invalid arg expecting a string\n");
  338. ctxt->error = XPATH_INVALID_TYPE;
  339. xmlXPathFreeObject(obj2);
  340. return;
  341. }
  342. /*
  343. * Get the key's name.
  344. */
  345. obj1 = valuePop(ctxt);
  346. if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) {
  347. int i;
  348. xmlXPathObjectPtr newobj, ret;
  349. ret = xmlXPathNewNodeSet(NULL);
  350. if (obj2->nodesetval != NULL) {
  351. for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
  352. valuePush(ctxt, xmlXPathObjectCopy(obj1));
  353. valuePush(ctxt,
  354. xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
  355. xmlXPathStringFunction(ctxt, 1);
  356. xsltKeyFunction(ctxt, 2);
  357. newobj = valuePop(ctxt);
  358. ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
  359. newobj->nodesetval);
  360. xmlXPathFreeObject(newobj);
  361. }
  362. }
  363. valuePush(ctxt, ret);
  364. } else {
  365. xmlNodeSetPtr nodelist = NULL;
  366. xmlChar *key = NULL, *value;
  367. const xmlChar *keyURI;
  368. xsltTransformContextPtr tctxt;
  369. xmlChar *qname, *prefix;
  370. xmlXPathContextPtr xpctxt = ctxt->context;
  371. xmlNodePtr tmpNode = NULL;
  372. xsltDocumentPtr oldDocInfo;
  373. tctxt = xsltXPathGetTransformContext(ctxt);
  374. oldDocInfo = tctxt->document;
  375. if (xpctxt->node == NULL) {
  376. xsltTransformError(tctxt, NULL, tctxt->inst,
  377. "Internal error in xsltKeyFunction(): "
  378. "The context node is not set on the XPath context.\n");
  379. tctxt->state = XSLT_STATE_STOPPED;
  380. goto error;
  381. }
  382. /*
  383. * Get the associated namespace URI if qualified name
  384. */
  385. qname = obj1->stringval;
  386. key = xmlSplitQName2(qname, &prefix);
  387. if (key == NULL) {
  388. key = xmlStrdup(obj1->stringval);
  389. keyURI = NULL;
  390. if (prefix != NULL)
  391. xmlFree(prefix);
  392. } else {
  393. if (prefix != NULL) {
  394. keyURI = xmlXPathNsLookup(xpctxt, prefix);
  395. if (keyURI == NULL) {
  396. xsltTransformError(tctxt, NULL, tctxt->inst,
  397. "key() : prefix %s is not bound\n", prefix);
  398. /*
  399. * TODO: Shouldn't we stop here?
  400. */
  401. }
  402. xmlFree(prefix);
  403. } else {
  404. keyURI = NULL;
  405. }
  406. }
  407. /*
  408. * Force conversion of first arg to string
  409. */
  410. valuePush(ctxt, obj2);
  411. xmlXPathStringFunction(ctxt, 1);
  412. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  413. xsltTransformError(tctxt, NULL, tctxt->inst,
  414. "key() : invalid arg expecting a string\n");
  415. ctxt->error = XPATH_INVALID_TYPE;
  416. goto error;
  417. }
  418. obj2 = valuePop(ctxt);
  419. value = obj2->stringval;
  420. /*
  421. * We need to ensure that ctxt->document is available for
  422. * xsltGetKey().
  423. * First find the relevant doc, which is the context node's
  424. * owner doc; using context->doc is not safe, since
  425. * the doc could have been acquired via the document() function,
  426. * or the doc might be a Result Tree Fragment.
  427. * FUTURE INFO: In XSLT 2.0 the key() function takes an additional
  428. * argument indicating the doc to use.
  429. */
  430. if (xpctxt->node->type == XML_NAMESPACE_DECL) {
  431. /*
  432. * REVISIT: This is a libxml hack! Check xpath.c for details.
  433. * The XPath module sets the owner element of a ns-node on
  434. * the ns->next field.
  435. */
  436. if ((((xmlNsPtr) xpctxt->node)->next != NULL) &&
  437. (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE))
  438. {
  439. tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next;
  440. }
  441. } else
  442. tmpNode = xpctxt->node;
  443. if ((tmpNode == NULL) || (tmpNode->doc == NULL)) {
  444. xsltTransformError(tctxt, NULL, tctxt->inst,
  445. "Internal error in xsltKeyFunction(): "
  446. "Couldn't get the doc of the XPath context node.\n");
  447. goto error;
  448. }
  449. if ((tctxt->document == NULL) ||
  450. (tctxt->document->doc != tmpNode->doc))
  451. {
  452. if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) {
  453. /*
  454. * This is a Result Tree Fragment.
  455. */
  456. if (tmpNode->doc->_private == NULL) {
  457. tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc);
  458. if (tmpNode->doc->_private == NULL)
  459. goto error;
  460. }
  461. tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private;
  462. } else {
  463. /*
  464. * May be the initial source doc or a doc acquired via the
  465. * document() function.
  466. */
  467. tctxt->document = xsltFindDocument(tctxt, tmpNode->doc);
  468. }
  469. if (tctxt->document == NULL) {
  470. xsltTransformError(tctxt, NULL, tctxt->inst,
  471. "Internal error in xsltKeyFunction(): "
  472. "Could not get the document info of a context doc.\n");
  473. tctxt->state = XSLT_STATE_STOPPED;
  474. goto error;
  475. }
  476. }
  477. /*
  478. * Get/compute the key value.
  479. */
  480. nodelist = xsltGetKey(tctxt, key, keyURI, value);
  481. error:
  482. tctxt->document = oldDocInfo;
  483. valuePush(ctxt, xmlXPathWrapNodeSet(
  484. xmlXPathNodeSetMerge(NULL, nodelist)));
  485. if (key != NULL)
  486. xmlFree(key);
  487. }
  488. if (obj1 != NULL)
  489. xmlXPathFreeObject(obj1);
  490. if (obj2 != NULL)
  491. xmlXPathFreeObject(obj2);
  492. }
  493. /**
  494. * xsltUnparsedEntityURIFunction:
  495. * @ctxt: the XPath Parser context
  496. * @nargs: the number of arguments
  497. *
  498. * Implement the unparsed-entity-uri() XSLT function
  499. * string unparsed-entity-uri(string)
  500. */
  501. void
  502. xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
  503. xmlXPathObjectPtr obj;
  504. xmlChar *str;
  505. if ((nargs != 1) || (ctxt->value == NULL)) {
  506. xsltGenericError(xsltGenericErrorContext,
  507. "unparsed-entity-uri() : expects one string arg\n");
  508. ctxt->error = XPATH_INVALID_ARITY;
  509. return;
  510. }
  511. obj = valuePop(ctxt);
  512. if (obj->type != XPATH_STRING) {
  513. obj = xmlXPathConvertString(obj);
  514. }
  515. str = obj->stringval;
  516. if (str == NULL) {
  517. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  518. } else {
  519. xmlEntityPtr entity;
  520. entity = xmlGetDocEntity(ctxt->context->doc, str);
  521. if (entity == NULL) {
  522. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  523. } else {
  524. if (entity->URI != NULL)
  525. valuePush(ctxt, xmlXPathNewString(entity->URI));
  526. else
  527. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  528. }
  529. }
  530. xmlXPathFreeObject(obj);
  531. }
  532. /**
  533. * xsltFormatNumberFunction:
  534. * @ctxt: the XPath Parser context
  535. * @nargs: the number of arguments
  536. *
  537. * Implement the format-number() XSLT function
  538. * string format-number(number, string, string?)
  539. */
  540. void
  541. xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
  542. {
  543. xmlXPathObjectPtr numberObj = NULL;
  544. xmlXPathObjectPtr formatObj = NULL;
  545. xmlXPathObjectPtr decimalObj = NULL;
  546. xsltStylesheetPtr sheet;
  547. xsltDecimalFormatPtr formatValues = NULL;
  548. xmlChar *result;
  549. const xmlChar *ncname;
  550. const xmlChar *prefix = NULL;
  551. const xmlChar *nsUri = NULL;
  552. xsltTransformContextPtr tctxt;
  553. tctxt = xsltXPathGetTransformContext(ctxt);
  554. if ((tctxt == NULL) || (tctxt->inst == NULL))
  555. return;
  556. sheet = tctxt->style;
  557. if (sheet == NULL)
  558. return;
  559. formatValues = sheet->decimalFormat;
  560. switch (nargs) {
  561. case 3:
  562. CAST_TO_STRING;
  563. decimalObj = valuePop(ctxt);
  564. ncname = xsltSplitQName(sheet->dict, decimalObj->stringval, &prefix);
  565. if (prefix != NULL) {
  566. xmlNsPtr ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, prefix);
  567. if (ns == NULL) {
  568. xsltTransformError(tctxt, NULL, NULL,
  569. "format-number : No namespace found for QName '%s:%s'\n",
  570. prefix, ncname);
  571. sheet->errors++;
  572. ncname = NULL;
  573. }
  574. else {
  575. nsUri = ns->href;
  576. }
  577. }
  578. if (ncname != NULL) {
  579. formatValues = xsltDecimalFormatGetByQName(sheet, nsUri, ncname);
  580. }
  581. if (formatValues == NULL) {
  582. xsltTransformError(tctxt, NULL, NULL,
  583. "format-number() : undeclared decimal format '%s'\n",
  584. decimalObj->stringval);
  585. }
  586. /* Intentional fall-through */
  587. case 2:
  588. CAST_TO_STRING;
  589. formatObj = valuePop(ctxt);
  590. CAST_TO_NUMBER;
  591. numberObj = valuePop(ctxt);
  592. break;
  593. default:
  594. XP_ERROR(XPATH_INVALID_ARITY);
  595. }
  596. if (formatValues != NULL) {
  597. if (xsltFormatNumberConversion(formatValues,
  598. formatObj->stringval,
  599. numberObj->floatval,
  600. &result) == XPATH_EXPRESSION_OK) {
  601. valuePush(ctxt, xmlXPathNewString(result));
  602. xmlFree(result);
  603. }
  604. }
  605. xmlXPathFreeObject(numberObj);
  606. xmlXPathFreeObject(formatObj);
  607. xmlXPathFreeObject(decimalObj);
  608. }
  609. /**
  610. * xsltGenerateIdFunction:
  611. * @ctxt: the XPath Parser context
  612. * @nargs: the number of arguments
  613. *
  614. * Implement the generate-id() XSLT function
  615. * string generate-id(node-set?)
  616. */
  617. void
  618. xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
  619. static char base_address;
  620. xmlNodePtr cur = NULL;
  621. xmlXPathObjectPtr obj = NULL;
  622. long val;
  623. xmlChar str[30];
  624. if (nargs == 0) {
  625. cur = ctxt->context->node;
  626. } else if (nargs == 1) {
  627. xmlNodeSetPtr nodelist;
  628. int i, ret;
  629. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
  630. ctxt->error = XPATH_INVALID_TYPE;
  631. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  632. "generate-id() : invalid arg expecting a node-set\n");
  633. return;
  634. }
  635. obj = valuePop(ctxt);
  636. nodelist = obj->nodesetval;
  637. if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
  638. xmlXPathFreeObject(obj);
  639. valuePush(ctxt, xmlXPathNewCString(""));
  640. return;
  641. }
  642. cur = nodelist->nodeTab[0];
  643. for (i = 1;i < nodelist->nodeNr;i++) {
  644. ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
  645. if (ret == -1)
  646. cur = nodelist->nodeTab[i];
  647. }
  648. } else {
  649. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  650. "generate-id() : invalid number of args %d\n", nargs);
  651. ctxt->error = XPATH_INVALID_ARITY;
  652. return;
  653. }
  654. if (obj)
  655. xmlXPathFreeObject(obj);
  656. val = (long)((char *)cur - (char *)&base_address);
  657. if (val >= 0) {
  658. snprintf((char *)str, sizeof(str), "idp%ld", val);
  659. } else {
  660. snprintf((char *)str, sizeof(str), "idm%ld", -val);
  661. }
  662. valuePush(ctxt, xmlXPathNewString(str));
  663. }
  664. /**
  665. * xsltSystemPropertyFunction:
  666. * @ctxt: the XPath Parser context
  667. * @nargs: the number of arguments
  668. *
  669. * Implement the system-property() XSLT function
  670. * object system-property(string)
  671. */
  672. void
  673. xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
  674. xmlXPathObjectPtr obj;
  675. xmlChar *prefix, *name;
  676. const xmlChar *nsURI = NULL;
  677. if (nargs != 1) {
  678. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  679. "system-property() : expects one string arg\n");
  680. ctxt->error = XPATH_INVALID_ARITY;
  681. return;
  682. }
  683. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  684. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  685. "system-property() : invalid arg expecting a string\n");
  686. ctxt->error = XPATH_INVALID_TYPE;
  687. return;
  688. }
  689. obj = valuePop(ctxt);
  690. if (obj->stringval == NULL) {
  691. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  692. } else {
  693. name = xmlSplitQName2(obj->stringval, &prefix);
  694. if (name == NULL) {
  695. name = xmlStrdup(obj->stringval);
  696. } else {
  697. nsURI = xmlXPathNsLookup(ctxt->context, prefix);
  698. if (nsURI == NULL) {
  699. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  700. "system-property() : prefix %s is not bound\n", prefix);
  701. }
  702. }
  703. if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) {
  704. #ifdef DOCBOOK_XSL_HACK
  705. if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
  706. xsltStylesheetPtr sheet;
  707. xsltTransformContextPtr tctxt;
  708. tctxt = xsltXPathGetTransformContext(ctxt);
  709. if ((tctxt != NULL) && (tctxt->inst != NULL) &&
  710. (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) &&
  711. (tctxt->inst->parent != NULL) &&
  712. (xmlStrEqual(tctxt->inst->parent->name,
  713. BAD_CAST "template")))
  714. sheet = tctxt->style;
  715. else
  716. sheet = NULL;
  717. if ((sheet != NULL) && (sheet->doc != NULL) &&
  718. (sheet->doc->URL != NULL) &&
  719. (xmlStrstr(sheet->doc->URL,
  720. (const xmlChar *)"chunk") != NULL)) {
  721. valuePush(ctxt, xmlXPathNewString(
  722. (const xmlChar *)"libxslt (SAXON 6.2 compatible)"));
  723. } else {
  724. valuePush(ctxt, xmlXPathNewString(
  725. (const xmlChar *)XSLT_DEFAULT_VENDOR));
  726. }
  727. } else
  728. #else
  729. if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
  730. valuePush(ctxt, xmlXPathNewString(
  731. (const xmlChar *)XSLT_DEFAULT_VENDOR));
  732. } else
  733. #endif
  734. if (xmlStrEqual(name, (const xmlChar *)"version")) {
  735. valuePush(ctxt, xmlXPathNewString(
  736. (const xmlChar *)XSLT_DEFAULT_VERSION));
  737. } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) {
  738. valuePush(ctxt, xmlXPathNewString(
  739. (const xmlChar *)XSLT_DEFAULT_URL));
  740. } else {
  741. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  742. }
  743. } else {
  744. valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
  745. }
  746. if (name != NULL)
  747. xmlFree(name);
  748. if (prefix != NULL)
  749. xmlFree(prefix);
  750. }
  751. xmlXPathFreeObject(obj);
  752. }
  753. /**
  754. * xsltElementAvailableFunction:
  755. * @ctxt: the XPath Parser context
  756. * @nargs: the number of arguments
  757. *
  758. * Implement the element-available() XSLT function
  759. * boolean element-available(string)
  760. */
  761. void
  762. xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
  763. xmlXPathObjectPtr obj;
  764. xmlChar *prefix, *name;
  765. const xmlChar *nsURI = NULL;
  766. xsltTransformContextPtr tctxt;
  767. if (nargs != 1) {
  768. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  769. "element-available() : expects one string arg\n");
  770. ctxt->error = XPATH_INVALID_ARITY;
  771. return;
  772. }
  773. xmlXPathStringFunction(ctxt, 1);
  774. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  775. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  776. "element-available() : invalid arg expecting a string\n");
  777. ctxt->error = XPATH_INVALID_TYPE;
  778. return;
  779. }
  780. obj = valuePop(ctxt);
  781. tctxt = xsltXPathGetTransformContext(ctxt);
  782. if ((tctxt == NULL) || (tctxt->inst == NULL)) {
  783. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  784. "element-available() : internal error tctxt == NULL\n");
  785. xmlXPathFreeObject(obj);
  786. valuePush(ctxt, xmlXPathNewBoolean(0));
  787. return;
  788. }
  789. name = xmlSplitQName2(obj->stringval, &prefix);
  790. if (name == NULL) {
  791. xmlNsPtr ns;
  792. name = xmlStrdup(obj->stringval);
  793. ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
  794. if (ns != NULL) nsURI = ns->href;
  795. } else {
  796. nsURI = xmlXPathNsLookup(ctxt->context, prefix);
  797. if (nsURI == NULL) {
  798. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  799. "element-available() : prefix %s is not bound\n", prefix);
  800. }
  801. }
  802. if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) {
  803. valuePush(ctxt, xmlXPathNewBoolean(1));
  804. } else {
  805. valuePush(ctxt, xmlXPathNewBoolean(0));
  806. }
  807. xmlXPathFreeObject(obj);
  808. if (name != NULL)
  809. xmlFree(name);
  810. if (prefix != NULL)
  811. xmlFree(prefix);
  812. }
  813. /**
  814. * xsltFunctionAvailableFunction:
  815. * @ctxt: the XPath Parser context
  816. * @nargs: the number of arguments
  817. *
  818. * Implement the function-available() XSLT function
  819. * boolean function-available(string)
  820. */
  821. void
  822. xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
  823. xmlXPathObjectPtr obj;
  824. xmlChar *prefix, *name;
  825. const xmlChar *nsURI = NULL;
  826. if (nargs != 1) {
  827. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  828. "function-available() : expects one string arg\n");
  829. ctxt->error = XPATH_INVALID_ARITY;
  830. return;
  831. }
  832. xmlXPathStringFunction(ctxt, 1);
  833. if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
  834. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  835. "function-available() : invalid arg expecting a string\n");
  836. ctxt->error = XPATH_INVALID_TYPE;
  837. return;
  838. }
  839. obj = valuePop(ctxt);
  840. name = xmlSplitQName2(obj->stringval, &prefix);
  841. if (name == NULL) {
  842. name = xmlStrdup(obj->stringval);
  843. } else {
  844. nsURI = xmlXPathNsLookup(ctxt->context, prefix);
  845. if (nsURI == NULL) {
  846. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  847. "function-available() : prefix %s is not bound\n", prefix);
  848. }
  849. }
  850. if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
  851. valuePush(ctxt, xmlXPathNewBoolean(1));
  852. } else {
  853. valuePush(ctxt, xmlXPathNewBoolean(0));
  854. }
  855. xmlXPathFreeObject(obj);
  856. if (name != NULL)
  857. xmlFree(name);
  858. if (prefix != NULL)
  859. xmlFree(prefix);
  860. }
  861. /**
  862. * xsltCurrentFunction:
  863. * @ctxt: the XPath Parser context
  864. * @nargs: the number of arguments
  865. *
  866. * Implement the current() XSLT function
  867. * node-set current()
  868. */
  869. static void
  870. xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
  871. xsltTransformContextPtr tctxt;
  872. if (nargs != 0) {
  873. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  874. "current() : function uses no argument\n");
  875. ctxt->error = XPATH_INVALID_ARITY;
  876. return;
  877. }
  878. tctxt = xsltXPathGetTransformContext(ctxt);
  879. if (tctxt == NULL) {
  880. xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
  881. "current() : internal error tctxt == NULL\n");
  882. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  883. } else {
  884. valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
  885. }
  886. }
  887. /************************************************************************
  888. * *
  889. * Registration of XSLT and libxslt functions *
  890. * *
  891. ************************************************************************/
  892. /**
  893. * xsltRegisterAllFunctions:
  894. * @ctxt: the XPath context
  895. *
  896. * Registers all default XSLT functions in this context
  897. */
  898. void
  899. xsltRegisterAllFunctions(xmlXPathContextPtr ctxt)
  900. {
  901. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
  902. xsltCurrentFunction);
  903. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
  904. xsltDocumentFunction);
  905. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
  906. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
  907. xsltUnparsedEntityURIFunction);
  908. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
  909. xsltFormatNumberFunction);
  910. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
  911. xsltGenerateIdFunction);
  912. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
  913. xsltSystemPropertyFunction);
  914. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
  915. xsltElementAvailableFunction);
  916. xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
  917. xsltFunctionAvailableFunction);
  918. }