attributes.c 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249
  1. /*
  2. * attributes.c: Implementation of the XSLT attributes handling
  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. */
  11. #define IN_LIBXSLT
  12. #include "libxslt.h"
  13. #include <string.h>
  14. #ifdef HAVE_SYS_TYPES_H
  15. #include <sys/types.h>
  16. #endif
  17. #ifdef HAVE_MATH_H
  18. #include <math.h>
  19. #endif
  20. #ifdef HAVE_FLOAT_H
  21. #include <float.h>
  22. #endif
  23. #ifdef HAVE_IEEEFP_H
  24. #include <ieeefp.h>
  25. #endif
  26. #ifdef HAVE_NAN_H
  27. #include <nan.h>
  28. #endif
  29. #ifdef HAVE_CTYPE_H
  30. #include <ctype.h>
  31. #endif
  32. #include <libxml/xmlmemory.h>
  33. #include <libxml/tree.h>
  34. #include <libxml/hash.h>
  35. #include <libxml/xmlerror.h>
  36. #include <libxml/uri.h>
  37. #include <libxml/parserInternals.h>
  38. #include "xslt.h"
  39. #include "xsltInternals.h"
  40. #include "xsltutils.h"
  41. #include "attributes.h"
  42. #include "namespaces.h"
  43. #include "templates.h"
  44. #include "imports.h"
  45. #include "transform.h"
  46. #include "preproc.h"
  47. #define WITH_XSLT_DEBUG_ATTRIBUTES
  48. #ifdef WITH_XSLT_DEBUG
  49. #define WITH_XSLT_DEBUG_ATTRIBUTES
  50. #endif
  51. /*
  52. * Useful macros
  53. */
  54. #ifdef IS_BLANK
  55. #undef IS_BLANK
  56. #endif
  57. #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
  58. ((c) == 0x0D))
  59. #define IS_BLANK_NODE(n) \
  60. (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
  61. #define ATTRSET_UNRESOLVED 0
  62. #define ATTRSET_RESOLVING 1
  63. #define ATTRSET_RESOLVED 2
  64. /*
  65. * The in-memory structure corresponding to an XSLT Attribute in
  66. * an attribute set
  67. */
  68. typedef struct _xsltAttrElem xsltAttrElem;
  69. typedef xsltAttrElem *xsltAttrElemPtr;
  70. struct _xsltAttrElem {
  71. struct _xsltAttrElem *next;/* chained list */
  72. xmlNodePtr attr; /* the xsl:attribute definition */
  73. };
  74. typedef struct _xsltUseAttrSet xsltUseAttrSet;
  75. typedef xsltUseAttrSet *xsltUseAttrSetPtr;
  76. struct _xsltUseAttrSet {
  77. struct _xsltUseAttrSet *next; /* chained list */
  78. const xmlChar *ncname;
  79. const xmlChar *ns;
  80. };
  81. typedef struct _xsltAttrSet xsltAttrSet;
  82. typedef xsltAttrSet *xsltAttrSetPtr;
  83. struct _xsltAttrSet {
  84. int state;
  85. xsltAttrElemPtr attrs; /* list head */
  86. xsltUseAttrSetPtr useAttrSets; /* list head */
  87. };
  88. typedef struct _xsltAttrSetContext xsltAttrSetContext;
  89. typedef xsltAttrSetContext *xsltAttrSetContextPtr;
  90. struct _xsltAttrSetContext {
  91. xsltStylesheetPtr topStyle;
  92. xsltStylesheetPtr style;
  93. };
  94. static void
  95. xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
  96. xsltStylesheetPtr style, const xmlChar *name,
  97. const xmlChar *ns, int depth);
  98. /************************************************************************
  99. * *
  100. * XSLT Attribute handling *
  101. * *
  102. ************************************************************************/
  103. /**
  104. * xsltNewAttrElem:
  105. * @attr: the new xsl:attribute node
  106. *
  107. * Create a new XSLT AttrElem
  108. *
  109. * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
  110. */
  111. static xsltAttrElemPtr
  112. xsltNewAttrElem(xmlNodePtr attr) {
  113. xsltAttrElemPtr cur;
  114. cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
  115. if (cur == NULL) {
  116. xsltGenericError(xsltGenericErrorContext,
  117. "xsltNewAttrElem : malloc failed\n");
  118. return(NULL);
  119. }
  120. memset(cur, 0, sizeof(xsltAttrElem));
  121. cur->attr = attr;
  122. return(cur);
  123. }
  124. /**
  125. * xsltFreeAttrElem:
  126. * @attr: an XSLT AttrElem
  127. *
  128. * Free up the memory allocated by @attr
  129. */
  130. static void
  131. xsltFreeAttrElem(xsltAttrElemPtr attr) {
  132. xmlFree(attr);
  133. }
  134. /**
  135. * xsltFreeAttrElemList:
  136. * @list: an XSLT AttrElem list
  137. *
  138. * Free up the memory allocated by @list
  139. */
  140. static void
  141. xsltFreeAttrElemList(xsltAttrElemPtr list) {
  142. xsltAttrElemPtr next;
  143. while (list != NULL) {
  144. next = list->next;
  145. xsltFreeAttrElem(list);
  146. list = next;
  147. }
  148. }
  149. /**
  150. * xsltAddAttrElemList:
  151. * @list: an XSLT AttrElem list
  152. * @attr: the new xsl:attribute node
  153. *
  154. * Add the new attribute to the list.
  155. *
  156. * Returns the new list pointer
  157. */
  158. static xsltAttrElemPtr
  159. xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
  160. xsltAttrElemPtr next, cur;
  161. if (attr == NULL)
  162. return(list);
  163. if (list == NULL)
  164. return(xsltNewAttrElem(attr));
  165. cur = list;
  166. while (cur != NULL) {
  167. next = cur->next;
  168. if (next == NULL) {
  169. cur->next = xsltNewAttrElem(attr);
  170. return(list);
  171. }
  172. cur = next;
  173. }
  174. return(list);
  175. }
  176. /**
  177. * xsltNewUseAttrSet:
  178. * @ncname: local name
  179. * @ns: namespace URI
  180. *
  181. * Create a new XSLT UseAttrSet
  182. *
  183. * Returns the newly allocated xsltUseAttrSetPtr or NULL in case of error.
  184. */
  185. static xsltUseAttrSetPtr
  186. xsltNewUseAttrSet(const xmlChar *ncname, const xmlChar *ns) {
  187. xsltUseAttrSetPtr cur;
  188. cur = (xsltUseAttrSetPtr) xmlMalloc(sizeof(xsltUseAttrSet));
  189. if (cur == NULL) {
  190. xsltGenericError(xsltGenericErrorContext,
  191. "xsltNewUseAttrSet : malloc failed\n");
  192. return(NULL);
  193. }
  194. memset(cur, 0, sizeof(xsltUseAttrSet));
  195. cur->ncname = ncname;
  196. cur->ns = ns;
  197. return(cur);
  198. }
  199. /**
  200. * xsltFreeUseAttrSet:
  201. * @use: an XSLT UseAttrSet
  202. *
  203. * Free up the memory allocated by @use
  204. */
  205. static void
  206. xsltFreeUseAttrSet(xsltUseAttrSetPtr use) {
  207. xmlFree(use);
  208. }
  209. /**
  210. * xsltFreeUseAttrSetList:
  211. * @list: an XSLT UseAttrSet list
  212. *
  213. * Free up the memory allocated by @list
  214. */
  215. static void
  216. xsltFreeUseAttrSetList(xsltUseAttrSetPtr list) {
  217. xsltUseAttrSetPtr next;
  218. while (list != NULL) {
  219. next = list->next;
  220. xsltFreeUseAttrSet(list);
  221. list = next;
  222. }
  223. }
  224. /**
  225. * xsltAddUseAttrSetList:
  226. * @list: a xsltUseAttrSet list
  227. * @ncname: local name
  228. * @ns: namespace URI
  229. *
  230. * Add the use-attribute-set name to the list.
  231. *
  232. * Returns the new list pointer.
  233. */
  234. static xsltUseAttrSetPtr
  235. xsltAddUseAttrSetList(xsltUseAttrSetPtr list, const xmlChar *ncname,
  236. const xmlChar *ns) {
  237. xsltUseAttrSetPtr next, cur;
  238. if (ncname == NULL)
  239. return(list);
  240. if (list == NULL)
  241. return(xsltNewUseAttrSet(ncname, ns));
  242. cur = list;
  243. while (cur != NULL) {
  244. if ((cur->ncname == ncname) && (cur->ns == ns))
  245. return(list);
  246. next = cur->next;
  247. if (next == NULL) {
  248. cur->next = xsltNewUseAttrSet(ncname, ns);
  249. return(list);
  250. }
  251. cur = next;
  252. }
  253. return(list);
  254. }
  255. /**
  256. * xsltNewAttrSet:
  257. *
  258. * Create a new attribute set.
  259. *
  260. * Returns the newly allocated xsltAttrSetPtr or NULL in case of error.
  261. */
  262. static xsltAttrSetPtr
  263. xsltNewAttrSet() {
  264. xsltAttrSetPtr cur;
  265. cur = (xsltAttrSetPtr) xmlMalloc(sizeof(xsltAttrSet));
  266. if (cur == NULL) {
  267. xsltGenericError(xsltGenericErrorContext,
  268. "xsltNewAttrSet : malloc failed\n");
  269. return(NULL);
  270. }
  271. memset(cur, 0, sizeof(xsltAttrSet));
  272. return(cur);
  273. }
  274. /**
  275. * xsltFreeAttrSet:
  276. * @set: an attribute set
  277. *
  278. * Free memory allocated by @set
  279. */
  280. static void
  281. xsltFreeAttrSet(xsltAttrSetPtr set) {
  282. if (set == NULL)
  283. return;
  284. xsltFreeAttrElemList(set->attrs);
  285. xsltFreeUseAttrSetList(set->useAttrSets);
  286. xmlFree(set);
  287. }
  288. /**
  289. * xsltMergeAttrSets:
  290. * @set: an attribute set
  291. * @other: another attribute set
  292. *
  293. * Add all the attributes from @other to @set,
  294. * but drop redefinition of existing values.
  295. */
  296. static void
  297. xsltMergeAttrSets(xsltAttrSetPtr set, xsltAttrSetPtr other) {
  298. xsltAttrElemPtr cur;
  299. xsltAttrElemPtr old = other->attrs;
  300. int add;
  301. while (old != NULL) {
  302. /*
  303. * Check that the attribute is not yet in the list
  304. */
  305. cur = set->attrs;
  306. add = 1;
  307. while (cur != NULL) {
  308. xsltStylePreCompPtr curComp = cur->attr->psvi;
  309. xsltStylePreCompPtr oldComp = old->attr->psvi;
  310. if ((curComp->name == oldComp->name) &&
  311. (curComp->ns == oldComp->ns)) {
  312. add = 0;
  313. break;
  314. }
  315. if (cur->next == NULL)
  316. break;
  317. cur = cur->next;
  318. }
  319. if (add == 1) {
  320. if (cur == NULL) {
  321. set->attrs = xsltNewAttrElem(old->attr);
  322. } else if (add) {
  323. cur->next = xsltNewAttrElem(old->attr);
  324. }
  325. }
  326. old = old->next;
  327. }
  328. }
  329. /************************************************************************
  330. * *
  331. * Module interfaces *
  332. * *
  333. ************************************************************************/
  334. /**
  335. * xsltParseStylesheetAttributeSet:
  336. * @style: the XSLT stylesheet
  337. * @cur: the "attribute-set" element
  338. *
  339. * parse an XSLT stylesheet attribute-set element
  340. */
  341. void
  342. xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
  343. const xmlChar *ncname;
  344. const xmlChar *prefix;
  345. const xmlChar *nsUri = NULL;
  346. xmlChar *value;
  347. xmlNodePtr child;
  348. xsltAttrSetPtr set;
  349. if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
  350. return;
  351. value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL);
  352. if ((value == NULL) || (*value == 0)) {
  353. xsltGenericError(xsltGenericErrorContext,
  354. "xsl:attribute-set : name is missing\n");
  355. if (value)
  356. xmlFree(value);
  357. return;
  358. }
  359. if (xmlValidateQName(value, 0)) {
  360. xsltTransformError(NULL, style, cur,
  361. "xsl:attribute-set : The name '%s' is not a valid QName.\n",
  362. value);
  363. style->errors++;
  364. xmlFree(value);
  365. return;
  366. }
  367. ncname = xsltSplitQName(style->dict, value, &prefix);
  368. xmlFree(value);
  369. value = NULL;
  370. if (prefix != NULL) {
  371. xmlNsPtr ns = xmlSearchNs(style->doc, cur, prefix);
  372. if (ns == NULL) {
  373. xsltTransformError(NULL, style, cur,
  374. "xsl:attribute-set : No namespace found for QName '%s:%s'\n",
  375. prefix, ncname);
  376. style->errors++;
  377. return;
  378. }
  379. nsUri = ns->href;
  380. }
  381. if (style->attributeSets == NULL) {
  382. #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
  383. xsltGenericDebug(xsltGenericDebugContext,
  384. "creating attribute set table\n");
  385. #endif
  386. style->attributeSets = xmlHashCreate(10);
  387. }
  388. if (style->attributeSets == NULL)
  389. return;
  390. set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
  391. if (set == NULL) {
  392. set = xsltNewAttrSet();
  393. if (set == NULL)
  394. return;
  395. xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set);
  396. }
  397. /*
  398. * Parse the content. Only xsl:attribute elements are allowed.
  399. */
  400. child = cur->children;
  401. while (child != NULL) {
  402. /*
  403. * Report invalid nodes.
  404. */
  405. if ((child->type != XML_ELEMENT_NODE) ||
  406. (child->ns == NULL) ||
  407. (! IS_XSLT_ELEM(child)))
  408. {
  409. if (child->type == XML_ELEMENT_NODE)
  410. xsltTransformError(NULL, style, child,
  411. "xsl:attribute-set : unexpected child %s\n",
  412. child->name);
  413. else
  414. xsltTransformError(NULL, style, child,
  415. "xsl:attribute-set : child of unexpected type\n");
  416. } else if (!IS_XSLT_NAME(child, "attribute")) {
  417. xsltTransformError(NULL, style, child,
  418. "xsl:attribute-set : unexpected child xsl:%s\n",
  419. child->name);
  420. } else {
  421. #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
  422. xsltGenericDebug(xsltGenericDebugContext,
  423. "add attribute to list %s\n", ncname);
  424. #endif
  425. xsltStylePreCompute(style, child);
  426. if (child->children != NULL) {
  427. #ifdef XSLT_REFACTORED
  428. xsltParseSequenceConstructor(XSLT_CCTXT(style),
  429. child->children);
  430. #else
  431. xsltParseTemplateContent(style, child);
  432. #endif
  433. }
  434. if (child->psvi == NULL) {
  435. xsltTransformError(NULL, style, child,
  436. "xsl:attribute-set : internal error, attribute %s not "
  437. "compiled\n", child->name);
  438. }
  439. else {
  440. set->attrs = xsltAddAttrElemList(set->attrs, child);
  441. }
  442. }
  443. child = child->next;
  444. }
  445. /*
  446. * Process attribute "use-attribute-sets".
  447. */
  448. value = xmlGetNsProp(cur, BAD_CAST "use-attribute-sets", NULL);
  449. if (value != NULL) {
  450. const xmlChar *curval, *endval;
  451. curval = value;
  452. while (*curval != 0) {
  453. while (IS_BLANK(*curval)) curval++;
  454. if (*curval == 0)
  455. break;
  456. endval = curval;
  457. while ((*endval != 0) && (!IS_BLANK(*endval))) endval++;
  458. curval = xmlDictLookup(style->dict, curval, endval - curval);
  459. if (curval) {
  460. const xmlChar *ncname2 = NULL;
  461. const xmlChar *prefix2 = NULL;
  462. const xmlChar *nsUri2 = NULL;
  463. #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
  464. xsltGenericDebug(xsltGenericDebugContext,
  465. "xsl:attribute-set : %s adds use %s\n", ncname, curval);
  466. #endif
  467. if (xmlValidateQName(curval, 0)) {
  468. xsltTransformError(NULL, style, cur,
  469. "xsl:attribute-set : The name '%s' in "
  470. "use-attribute-sets is not a valid QName.\n", curval);
  471. style->errors++;
  472. xmlFree(value);
  473. return;
  474. }
  475. ncname2 = xsltSplitQName(style->dict, curval, &prefix2);
  476. if (prefix2 != NULL) {
  477. xmlNsPtr ns2 = xmlSearchNs(style->doc, cur, prefix2);
  478. if (ns2 == NULL) {
  479. xsltTransformError(NULL, style, cur,
  480. "xsl:attribute-set : No namespace found for QName "
  481. "'%s:%s' in use-attribute-sets\n",
  482. prefix2, ncname2);
  483. style->errors++;
  484. xmlFree(value);
  485. return;
  486. }
  487. nsUri2 = ns2->href;
  488. }
  489. set->useAttrSets = xsltAddUseAttrSetList(set->useAttrSets,
  490. ncname2, nsUri2);
  491. }
  492. curval = endval;
  493. }
  494. xmlFree(value);
  495. value = NULL;
  496. }
  497. #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
  498. xsltGenericDebug(xsltGenericDebugContext,
  499. "updated attribute list %s\n", ncname);
  500. #endif
  501. }
  502. /**
  503. * xsltResolveUseAttrSets:
  504. * @set: the attribute set
  505. * @asctx: the context for attribute set resolution
  506. * @depth: recursion depth
  507. *
  508. * Process "use-attribute-sets".
  509. */
  510. static void
  511. xsltResolveUseAttrSets(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
  512. int depth) {
  513. xsltStylesheetPtr cur;
  514. xsltAttrSetPtr other;
  515. xsltUseAttrSetPtr use = set->useAttrSets;
  516. xsltUseAttrSetPtr next;
  517. while (use != NULL) {
  518. /*
  519. * Iterate top stylesheet and all imports.
  520. */
  521. cur = topStyle;
  522. while (cur != NULL) {
  523. if (cur->attributeSets) {
  524. other = xmlHashLookup2(cur->attributeSets, use->ncname,
  525. use->ns);
  526. if (other != NULL) {
  527. xsltResolveAttrSet(other, topStyle, cur, use->ncname,
  528. use->ns, depth + 1);
  529. xsltMergeAttrSets(set, other);
  530. break;
  531. }
  532. }
  533. cur = xsltNextImport(cur);
  534. }
  535. next = use->next;
  536. /* Free useAttrSets early. */
  537. xsltFreeUseAttrSet(use);
  538. use = next;
  539. }
  540. set->useAttrSets = NULL;
  541. }
  542. /**
  543. * xsltResolveAttrSet:
  544. * @set: the attribute set
  545. * @asctx: the context for attribute set resolution
  546. * @name: the local name of the attirbute set
  547. * @ns: the namespace of the attribute set
  548. * @depth: recursion depth
  549. *
  550. * resolve the references in an attribute set.
  551. */
  552. static void
  553. xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
  554. xsltStylesheetPtr style, const xmlChar *name,
  555. const xmlChar *ns, int depth) {
  556. xsltStylesheetPtr cur;
  557. xsltAttrSetPtr other;
  558. if (set->state == ATTRSET_RESOLVED)
  559. return;
  560. if (set->state == ATTRSET_RESOLVING) {
  561. xsltTransformError(NULL, topStyle, NULL,
  562. "xsl:attribute-set : use-attribute-sets recursion detected"
  563. " on %s\n", name);
  564. topStyle->errors++;
  565. set->state = ATTRSET_RESOLVED;
  566. return;
  567. }
  568. if (depth > 100) {
  569. xsltTransformError(NULL, topStyle, NULL,
  570. "xsl:attribute-set : use-attribute-sets maximum recursion "
  571. "depth exceeded on %s\n", name);
  572. topStyle->errors++;
  573. return;
  574. }
  575. set->state = ATTRSET_RESOLVING;
  576. xsltResolveUseAttrSets(set, topStyle, depth);
  577. /* Merge imported sets. */
  578. cur = xsltNextImport(style);
  579. while (cur != NULL) {
  580. if (cur->attributeSets != NULL) {
  581. other = xmlHashLookup2(cur->attributeSets, name, ns);
  582. if (other != NULL) {
  583. #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
  584. xsltGenericDebug(xsltGenericDebugContext,
  585. "xsl:attribute-set : merging import for %s\n", name);
  586. #endif
  587. xsltResolveUseAttrSets(other, topStyle, depth);
  588. xsltMergeAttrSets(set, other);
  589. xmlHashRemoveEntry2(cur->attributeSets, name, ns, NULL);
  590. xsltFreeAttrSet(other);
  591. }
  592. }
  593. cur = xsltNextImport(cur);
  594. }
  595. set->state = ATTRSET_RESOLVED;
  596. }
  597. /**
  598. * xsltResolveSASCallback:
  599. * @set: the attribute set
  600. * @asctx: the context for attribute set resolution
  601. * @name: the local name of the attirbute set
  602. * @ns: the namespace of the attribute set
  603. *
  604. * resolve the references in an attribute set.
  605. */
  606. static void
  607. xsltResolveSASCallback(void *payload, void *data,
  608. const xmlChar *name, const xmlChar *ns,
  609. ATTRIBUTE_UNUSED const xmlChar *ignored) {
  610. xsltAttrSetPtr set = (xsltAttrSetPtr) payload;
  611. xsltAttrSetContextPtr asctx = (xsltAttrSetContextPtr) data;
  612. xsltStylesheetPtr topStyle = asctx->topStyle;
  613. xsltStylesheetPtr style = asctx->style;
  614. xsltResolveAttrSet(set, topStyle, style, name, ns, 1);
  615. /* Move attribute sets to top stylesheet. */
  616. if (style != topStyle) {
  617. /*
  618. * This imported stylesheet won't be visited anymore. Don't bother
  619. * removing the hash entry.
  620. */
  621. if (xmlHashAddEntry2(topStyle->attributeSets, name, ns, set) < 0) {
  622. xsltGenericError(xsltGenericErrorContext,
  623. "xsl:attribute-set : internal error, can't move imported "
  624. " attribute set %s\n", name);
  625. }
  626. }
  627. }
  628. /**
  629. * xsltResolveStylesheetAttributeSet:
  630. * @style: the XSLT stylesheet
  631. *
  632. * resolve the references between attribute sets.
  633. */
  634. void
  635. xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
  636. xsltStylesheetPtr cur;
  637. xsltAttrSetContext asctx;
  638. #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
  639. xsltGenericDebug(xsltGenericDebugContext,
  640. "Resolving attribute sets references\n");
  641. #endif
  642. asctx.topStyle = style;
  643. cur = style;
  644. while (cur != NULL) {
  645. if (cur->attributeSets != NULL) {
  646. if (style->attributeSets == NULL) {
  647. #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
  648. xsltGenericDebug(xsltGenericDebugContext,
  649. "creating attribute set table\n");
  650. #endif
  651. style->attributeSets = xmlHashCreate(10);
  652. }
  653. asctx.style = cur;
  654. xmlHashScanFull(cur->attributeSets, xsltResolveSASCallback,
  655. &asctx);
  656. if (cur != style) {
  657. /*
  658. * the attribute lists have either been migrated to style
  659. * or freed directly in xsltResolveSASCallback()
  660. */
  661. xmlHashFree(cur->attributeSets, NULL);
  662. cur->attributeSets = NULL;
  663. }
  664. }
  665. cur = xsltNextImport(cur);
  666. }
  667. }
  668. /**
  669. * xsltAttribute:
  670. * @ctxt: a XSLT process context
  671. * @contextNode: the current node in the source tree
  672. * @inst: the xsl:attribute element
  673. * @castedComp: precomputed information
  674. *
  675. * Process the xslt attribute node on the source node
  676. */
  677. void
  678. xsltAttribute(xsltTransformContextPtr ctxt,
  679. xmlNodePtr contextNode,
  680. xmlNodePtr inst,
  681. xsltElemPreCompPtr castedComp)
  682. {
  683. #ifdef XSLT_REFACTORED
  684. xsltStyleItemAttributePtr comp =
  685. (xsltStyleItemAttributePtr) castedComp;
  686. #else
  687. xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
  688. #endif
  689. xmlNodePtr targetElem;
  690. xmlChar *prop = NULL;
  691. const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL;
  692. xmlChar *value = NULL;
  693. xmlNsPtr ns = NULL;
  694. xmlAttrPtr attr;
  695. if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) ||
  696. (inst->type != XML_ELEMENT_NODE) )
  697. return;
  698. /*
  699. * A comp->has_name == 0 indicates that we need to skip this instruction,
  700. * since it was evaluated to be invalid already during compilation.
  701. */
  702. if (!comp->has_name)
  703. return;
  704. /*
  705. * BIG NOTE: This previously used xsltGetSpecialNamespace() and
  706. * xsltGetNamespace(), but since both are not appropriate, we
  707. * will process namespace lookup here to avoid adding yet another
  708. * ns-lookup function to namespaces.c.
  709. */
  710. /*
  711. * SPEC XSLT 1.0: Error cases:
  712. * - Creating nodes other than text nodes during the instantiation of
  713. * the content of the xsl:attribute element; implementations may
  714. * either signal the error or ignore the offending nodes."
  715. */
  716. if (comp == NULL) {
  717. xsltTransformError(ctxt, NULL, inst,
  718. "Internal error in xsltAttribute(): "
  719. "The XSLT 'attribute' instruction was not compiled.\n");
  720. return;
  721. }
  722. /*
  723. * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
  724. * So report an internal error?
  725. */
  726. if (ctxt->insert == NULL)
  727. return;
  728. /*
  729. * SPEC XSLT 1.0:
  730. * "Adding an attribute to a node that is not an element;
  731. * implementations may either signal the error or ignore the attribute."
  732. *
  733. * TODO: I think we should signal such errors in the future, and maybe
  734. * provide an option to ignore such errors.
  735. */
  736. targetElem = ctxt->insert;
  737. if (targetElem->type != XML_ELEMENT_NODE)
  738. return;
  739. /*
  740. * SPEC XSLT 1.0:
  741. * "Adding an attribute to an element after children have been added
  742. * to it; implementations may either signal the error or ignore the
  743. * attribute."
  744. *
  745. * TODO: We should decide whether not to report such errors or
  746. * to ignore them; note that we *ignore* if the parent is not an
  747. * element, but here we report an error.
  748. */
  749. if (targetElem->children != NULL) {
  750. /*
  751. * NOTE: Ah! This seems to be intended to support streamed
  752. * result generation!.
  753. */
  754. xsltTransformError(ctxt, NULL, inst,
  755. "xsl:attribute: Cannot add attributes to an "
  756. "element if children have been already added "
  757. "to the element.\n");
  758. return;
  759. }
  760. /*
  761. * Process the name
  762. * ----------------
  763. */
  764. #ifdef WITH_DEBUGGER
  765. if (ctxt->debugStatus != XSLT_DEBUG_NONE)
  766. xslHandleDebugger(inst, contextNode, NULL, ctxt);
  767. #endif
  768. if (comp->name == NULL) {
  769. /* TODO: fix attr acquisition wrt to the XSLT namespace */
  770. prop = xsltEvalAttrValueTemplate(ctxt, inst,
  771. (const xmlChar *) "name", XSLT_NAMESPACE);
  772. if (prop == NULL) {
  773. xsltTransformError(ctxt, NULL, inst,
  774. "xsl:attribute: The attribute 'name' is missing.\n");
  775. goto error;
  776. }
  777. if (xmlValidateQName(prop, 0)) {
  778. xsltTransformError(ctxt, NULL, inst,
  779. "xsl:attribute: The effective name '%s' is not a "
  780. "valid QName.\n", prop);
  781. /* we fall through to catch any further errors, if possible */
  782. }
  783. /*
  784. * Reject a name of "xmlns".
  785. */
  786. if (xmlStrEqual(prop, BAD_CAST "xmlns")) {
  787. xsltTransformError(ctxt, NULL, inst,
  788. "xsl:attribute: The effective name 'xmlns' is not allowed.\n");
  789. xmlFree(prop);
  790. goto error;
  791. }
  792. name = xsltSplitQName(ctxt->dict, prop, &prefix);
  793. xmlFree(prop);
  794. } else {
  795. /*
  796. * The "name" value was static.
  797. */
  798. #ifdef XSLT_REFACTORED
  799. prefix = comp->nsPrefix;
  800. name = comp->name;
  801. #else
  802. name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
  803. #endif
  804. }
  805. /*
  806. * Process namespace semantics
  807. * ---------------------------
  808. *
  809. * Evaluate the namespace name.
  810. */
  811. if (comp->has_ns) {
  812. /*
  813. * The "namespace" attribute was existent.
  814. */
  815. if (comp->ns != NULL) {
  816. /*
  817. * No AVT; just plain text for the namespace name.
  818. */
  819. if (comp->ns[0] != 0)
  820. nsName = comp->ns;
  821. } else {
  822. xmlChar *tmpNsName;
  823. /*
  824. * Eval the AVT.
  825. */
  826. /* TODO: check attr acquisition wrt to the XSLT namespace */
  827. tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
  828. (const xmlChar *) "namespace", XSLT_NAMESPACE);
  829. /*
  830. * This fixes bug #302020: The AVT might also evaluate to the
  831. * empty string; this means that the empty string also indicates
  832. * "no namespace".
  833. * SPEC XSLT 1.0:
  834. * "If the string is empty, then the expanded-name of the
  835. * attribute has a null namespace URI."
  836. */
  837. if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
  838. nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
  839. xmlFree(tmpNsName);
  840. }
  841. if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) {
  842. xsltTransformError(ctxt, NULL, inst,
  843. "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ "
  844. "forbidden.\n");
  845. goto error;
  846. }
  847. if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) {
  848. prefix = BAD_CAST "xml";
  849. } else if (xmlStrEqual(prefix, BAD_CAST "xml")) {
  850. prefix = NULL;
  851. }
  852. } else if (prefix != NULL) {
  853. /*
  854. * SPEC XSLT 1.0:
  855. * "If the namespace attribute is not present, then the QName is
  856. * expanded into an expanded-name using the namespace declarations
  857. * in effect for the xsl:attribute element, *not* including any
  858. * default namespace declaration."
  859. */
  860. ns = xmlSearchNs(inst->doc, inst, prefix);
  861. if (ns == NULL) {
  862. /*
  863. * Note that this is treated as an error now (checked with
  864. * Saxon, Xalan-J and MSXML).
  865. */
  866. xsltTransformError(ctxt, NULL, inst,
  867. "xsl:attribute: The QName '%s:%s' has no "
  868. "namespace binding in scope in the stylesheet; "
  869. "this is an error, since the namespace was not "
  870. "specified by the instruction itself.\n", prefix, name);
  871. } else
  872. nsName = ns->href;
  873. }
  874. /*
  875. * Find/create a matching ns-decl in the result tree.
  876. */
  877. ns = NULL;
  878. #if 0
  879. if (0) {
  880. /*
  881. * OPTIMIZE TODO: How do we know if we are adding to a
  882. * fragment or to the result tree?
  883. *
  884. * If we are adding to a result tree fragment (i.e., not to the
  885. * actual result tree), we'll don't bother searching for the
  886. * ns-decl, but just store it in the dummy-doc of the result
  887. * tree fragment.
  888. */
  889. if (nsName != NULL) {
  890. /*
  891. * TODO: Get the doc of @targetElem.
  892. */
  893. ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix);
  894. }
  895. }
  896. #endif
  897. if (nsName != NULL) {
  898. /*
  899. * Something about ns-prefixes:
  900. * SPEC XSLT 1.0:
  901. * "XSLT processors may make use of the prefix of the QName specified
  902. * in the name attribute when selecting the prefix used for outputting
  903. * the created attribute as XML; however, they are not required to do
  904. * so and, if the prefix is xmlns, they must not do so"
  905. */
  906. /*
  907. * xsl:attribute can produce a scenario where the prefix is NULL,
  908. * so generate a prefix.
  909. */
  910. if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) {
  911. xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
  912. ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, targetElem);
  913. xmlFree(pref);
  914. } else {
  915. ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix,
  916. targetElem);
  917. }
  918. if (ns == NULL) {
  919. xsltTransformError(ctxt, NULL, inst,
  920. "Namespace fixup error: Failed to acquire an in-scope "
  921. "namespace binding for the generated attribute '{%s}%s'.\n",
  922. nsName, name);
  923. goto error;
  924. }
  925. }
  926. /*
  927. * Construction of the value
  928. * -------------------------
  929. */
  930. if (inst->children == NULL) {
  931. /*
  932. * No content.
  933. * TODO: Do we need to put the empty string in ?
  934. */
  935. attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) "");
  936. } else if ((inst->children->next == NULL) &&
  937. ((inst->children->type == XML_TEXT_NODE) ||
  938. (inst->children->type == XML_CDATA_SECTION_NODE)))
  939. {
  940. xmlNodePtr copyTxt;
  941. /*
  942. * xmlSetNsProp() will take care of duplicates.
  943. */
  944. attr = xmlSetNsProp(ctxt->insert, ns, name, NULL);
  945. if (attr == NULL) /* TODO: report error ? */
  946. goto error;
  947. /*
  948. * This was taken over from xsltCopyText() (transform.c).
  949. */
  950. if (ctxt->internalized &&
  951. (ctxt->insert->doc != NULL) &&
  952. (ctxt->insert->doc->dict == ctxt->dict))
  953. {
  954. copyTxt = xmlNewText(NULL);
  955. if (copyTxt == NULL) /* TODO: report error */
  956. goto error;
  957. /*
  958. * This is a safe scenario where we don't need to lookup
  959. * the dict.
  960. */
  961. copyTxt->content = inst->children->content;
  962. /*
  963. * Copy "disable-output-escaping" information.
  964. * TODO: Does this have any effect for attribute values
  965. * anyway?
  966. */
  967. if (inst->children->name == xmlStringTextNoenc)
  968. copyTxt->name = xmlStringTextNoenc;
  969. } else {
  970. /*
  971. * Copy the value.
  972. */
  973. copyTxt = xmlNewText(inst->children->content);
  974. if (copyTxt == NULL) /* TODO: report error */
  975. goto error;
  976. }
  977. attr->children = attr->last = copyTxt;
  978. copyTxt->parent = (xmlNodePtr) attr;
  979. copyTxt->doc = attr->doc;
  980. /*
  981. * Copy "disable-output-escaping" information.
  982. * TODO: Does this have any effect for attribute values
  983. * anyway?
  984. */
  985. if (inst->children->name == xmlStringTextNoenc)
  986. copyTxt->name = xmlStringTextNoenc;
  987. /*
  988. * since we create the attribute without content IDness must be
  989. * asserted as a second step
  990. */
  991. if ((copyTxt->content != NULL) &&
  992. (xmlIsID(attr->doc, attr->parent, attr)))
  993. xmlAddID(NULL, attr->doc, copyTxt->content, attr);
  994. } else {
  995. /*
  996. * The sequence constructor might be complex, so instantiate it.
  997. */
  998. value = xsltEvalTemplateString(ctxt, contextNode, inst);
  999. if (value != NULL) {
  1000. attr = xmlSetNsProp(ctxt->insert, ns, name, value);
  1001. xmlFree(value);
  1002. } else {
  1003. /*
  1004. * TODO: Do we have to add the empty string to the attr?
  1005. * TODO: Does a value of NULL indicate an
  1006. * error in xsltEvalTemplateString() ?
  1007. */
  1008. attr = xmlSetNsProp(ctxt->insert, ns, name,
  1009. (const xmlChar *) "");
  1010. }
  1011. }
  1012. error:
  1013. return;
  1014. }
  1015. /**
  1016. * xsltApplyAttributeSet:
  1017. * @ctxt: the XSLT stylesheet
  1018. * @node: the node in the source tree.
  1019. * @inst: the attribute node "xsl:use-attribute-sets"
  1020. * @attrSets: the list of QNames of the attribute-sets to be applied
  1021. *
  1022. * Apply the xsl:use-attribute-sets.
  1023. * If @attrSets is NULL, then @inst will be used to exctract this
  1024. * value.
  1025. * If both, @attrSets and @inst, are NULL, then this will do nothing.
  1026. */
  1027. void
  1028. xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
  1029. xmlNodePtr inst,
  1030. const xmlChar *attrSets)
  1031. {
  1032. const xmlChar *ncname = NULL;
  1033. const xmlChar *prefix = NULL;
  1034. const xmlChar *curstr, *endstr;
  1035. xsltAttrSetPtr set;
  1036. xsltStylesheetPtr style;
  1037. if (attrSets == NULL) {
  1038. if (inst == NULL)
  1039. return;
  1040. else {
  1041. /*
  1042. * Extract the value from @inst.
  1043. */
  1044. if (inst->type == XML_ATTRIBUTE_NODE) {
  1045. if ( ((xmlAttrPtr) inst)->children != NULL)
  1046. attrSets = ((xmlAttrPtr) inst)->children->content;
  1047. }
  1048. if (attrSets == NULL) {
  1049. /*
  1050. * TODO: Return an error?
  1051. */
  1052. return;
  1053. }
  1054. }
  1055. }
  1056. /*
  1057. * Parse/apply the list of QNames.
  1058. */
  1059. curstr = attrSets;
  1060. while (*curstr != 0) {
  1061. while (IS_BLANK(*curstr))
  1062. curstr++;
  1063. if (*curstr == 0)
  1064. break;
  1065. endstr = curstr;
  1066. while ((*endstr != 0) && (!IS_BLANK(*endstr)))
  1067. endstr++;
  1068. curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr);
  1069. if (curstr) {
  1070. xmlNsPtr ns;
  1071. const xmlChar *nsUri = NULL;
  1072. #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
  1073. xsltGenericDebug(xsltGenericDebugContext,
  1074. "apply attribute set %s\n", curstr);
  1075. #endif
  1076. if (xmlValidateQName(curstr, 0)) {
  1077. xsltTransformError(ctxt, NULL, inst,
  1078. "The name '%s' in use-attribute-sets is not a valid "
  1079. "QName.\n", curstr);
  1080. return;
  1081. }
  1082. ncname = xsltSplitQName(ctxt->dict, curstr, &prefix);
  1083. if (prefix != NULL) {
  1084. ns = xmlSearchNs(inst->doc, inst, prefix);
  1085. if (ns == NULL) {
  1086. xsltTransformError(ctxt, NULL, inst,
  1087. "use-attribute-set : No namespace found for QName "
  1088. "'%s:%s'\n", prefix, ncname);
  1089. return;
  1090. }
  1091. nsUri = ns->href;
  1092. }
  1093. style = ctxt->style;
  1094. #ifdef WITH_DEBUGGER
  1095. if ((style != NULL) &&
  1096. (style->attributeSets != NULL) &&
  1097. (ctxt->debugStatus != XSLT_DEBUG_NONE))
  1098. {
  1099. set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
  1100. if ((set != NULL) && (set->attrs != NULL) &&
  1101. (set->attrs->attr != NULL))
  1102. xslHandleDebugger(set->attrs->attr->parent, node, NULL,
  1103. ctxt);
  1104. }
  1105. #endif
  1106. /*
  1107. * Lookup the referenced attribute-set. All attribute sets were
  1108. * moved to the top stylesheet so there's no need to iterate
  1109. * imported stylesheets
  1110. */
  1111. set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
  1112. if (set != NULL) {
  1113. xsltAttrElemPtr cur = set->attrs;
  1114. while (cur != NULL) {
  1115. if (cur->attr != NULL) {
  1116. xsltAttribute(ctxt, node, cur->attr,
  1117. cur->attr->psvi);
  1118. }
  1119. cur = cur->next;
  1120. }
  1121. }
  1122. }
  1123. curstr = endstr;
  1124. }
  1125. }
  1126. static void
  1127. xsltFreeAttributeSetsEntry(void *payload,
  1128. const xmlChar *name ATTRIBUTE_UNUSED) {
  1129. xsltFreeAttrSet((xsltAttrSetPtr) payload);
  1130. }
  1131. /**
  1132. * xsltFreeAttributeSetsHashes:
  1133. * @style: an XSLT stylesheet
  1134. *
  1135. * Free up the memory used by attribute sets
  1136. */
  1137. void
  1138. xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
  1139. if (style->attributeSets != NULL)
  1140. xmlHashFree((xmlHashTablePtr) style->attributeSets,
  1141. xsltFreeAttributeSetsEntry);
  1142. style->attributeSets = NULL;
  1143. }