attributes.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248
  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. }