attrvt.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*
  2. * attrvt.c: Implementation of the XSL Transformation 1.0 engine
  3. * attribute value template handling part.
  4. *
  5. * References:
  6. * http://www.w3.org/TR/1999/REC-xslt-19991116
  7. *
  8. * Michael Kay "XSLT Programmer's Reference" pp 637-643
  9. * Writing Multiple Output Files
  10. *
  11. * See Copyright for the status of this software.
  12. *
  13. * daniel@veillard.com
  14. */
  15. #define IN_LIBXSLT
  16. #include "libxslt.h"
  17. #include <string.h>
  18. #include <libxml/xmlmemory.h>
  19. #include <libxml/tree.h>
  20. #include <libxml/xpath.h>
  21. #include <libxml/xpathInternals.h>
  22. #include "xslt.h"
  23. #include "xsltutils.h"
  24. #include "xsltInternals.h"
  25. #include "templates.h"
  26. #ifdef WITH_XSLT_DEBUG
  27. #define WITH_XSLT_DEBUG_AVT
  28. #endif
  29. #define MAX_AVT_SEG 10
  30. typedef struct _xsltAttrVT xsltAttrVT;
  31. typedef xsltAttrVT *xsltAttrVTPtr;
  32. struct _xsltAttrVT {
  33. struct _xsltAttrVT *next; /* next xsltAttrVT */
  34. int nb_seg; /* Number of segments */
  35. int max_seg; /* max capacity before re-alloc needed */
  36. int strstart; /* is the start a string */
  37. /*
  38. * the namespaces in scope
  39. */
  40. xmlNsPtr *nsList;
  41. int nsNr;
  42. /*
  43. * the content is an alternate of string and xmlXPathCompExprPtr
  44. */
  45. #if __STDC_VERSION__ >= 199901L
  46. /* Using a C99 flexible array member avoids false positives under UBSan */
  47. void *segments[];
  48. #else
  49. void *segments[1];
  50. #endif
  51. };
  52. /**
  53. * xsltNewAttrVT:
  54. * @style: a XSLT process context
  55. *
  56. * Build a new xsltAttrVT structure
  57. *
  58. * Returns the structure or NULL in case of error
  59. */
  60. static xsltAttrVTPtr
  61. xsltNewAttrVT(xsltStylesheetPtr style) {
  62. xsltAttrVTPtr cur;
  63. size_t size = sizeof(xsltAttrVT) + MAX_AVT_SEG * sizeof(void*);
  64. cur = (xsltAttrVTPtr) xmlMalloc(size);
  65. if (cur == NULL) {
  66. xsltTransformError(NULL, style, NULL,
  67. "xsltNewAttrVTPtr : malloc failed\n");
  68. if (style != NULL) style->errors++;
  69. return(NULL);
  70. }
  71. memset(cur, 0, size);
  72. cur->nb_seg = 0;
  73. cur->max_seg = MAX_AVT_SEG;
  74. cur->strstart = 0;
  75. cur->next = style->attVTs;
  76. /*
  77. * Note: this pointer may be changed by a re-alloc within xsltCompileAttr,
  78. * so that code may change the stylesheet pointer also!
  79. */
  80. style->attVTs = (xsltAttrVTPtr) cur;
  81. return(cur);
  82. }
  83. /**
  84. * xsltFreeAttrVT:
  85. * @avt: pointer to an xsltAttrVT structure
  86. *
  87. * Free up the memory associated to the attribute value template
  88. */
  89. static void
  90. xsltFreeAttrVT(xsltAttrVTPtr avt) {
  91. int i;
  92. if (avt == NULL) return;
  93. if (avt->strstart == 1) {
  94. for (i = 0;i < avt->nb_seg; i += 2)
  95. if (avt->segments[i] != NULL)
  96. xmlFree((xmlChar *) avt->segments[i]);
  97. for (i = 1;i < avt->nb_seg; i += 2)
  98. xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
  99. } else {
  100. for (i = 0;i < avt->nb_seg; i += 2)
  101. xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
  102. for (i = 1;i < avt->nb_seg; i += 2)
  103. if (avt->segments[i] != NULL)
  104. xmlFree((xmlChar *) avt->segments[i]);
  105. }
  106. if (avt->nsList != NULL)
  107. xmlFree(avt->nsList);
  108. xmlFree(avt);
  109. }
  110. /**
  111. * xsltFreeAVTList:
  112. * @avt: pointer to an list of AVT structures
  113. *
  114. * Free up the memory associated to the attribute value templates
  115. */
  116. void
  117. xsltFreeAVTList(void *avt) {
  118. xsltAttrVTPtr cur = (xsltAttrVTPtr) avt, next;
  119. while (cur != NULL) {
  120. next = cur->next;
  121. xsltFreeAttrVT(cur);
  122. cur = next;
  123. }
  124. }
  125. /**
  126. * xsltSetAttrVTsegment:
  127. * @ avt: pointer to an xsltAttrVT structure
  128. * @ val: the value to be set to the next available segment
  129. *
  130. * Within xsltCompileAttr there are several places where a value
  131. * needs to be added to the 'segments' array within the xsltAttrVT
  132. * structure, and at each place the allocated size may have to be
  133. * re-allocated. This routine takes care of that situation.
  134. *
  135. * Returns the avt pointer, which may have been changed by a re-alloc
  136. */
  137. static xsltAttrVTPtr
  138. xsltSetAttrVTsegment(xsltAttrVTPtr avt, void *val) {
  139. if (avt->nb_seg >= avt->max_seg) {
  140. size_t size = sizeof(xsltAttrVT) +
  141. (avt->max_seg + MAX_AVT_SEG) * sizeof(void *);
  142. xsltAttrVTPtr tmp = (xsltAttrVTPtr) xmlRealloc(avt, size);
  143. if (tmp == NULL) {
  144. xsltFreeAttrVT(avt);
  145. return NULL;
  146. }
  147. avt = tmp;
  148. memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *));
  149. avt->max_seg += MAX_AVT_SEG;
  150. }
  151. avt->segments[avt->nb_seg++] = val;
  152. return avt;
  153. }
  154. /**
  155. * xsltCompileAttr:
  156. * @style: a XSLT process context
  157. * @attr: the attribute coming from the stylesheet.
  158. *
  159. * Precompile an attribute in a stylesheet, basically it checks if it is
  160. * an attribute value template, and if yes, establish some structures needed
  161. * to process it at transformation time.
  162. */
  163. void
  164. xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) {
  165. const xmlChar *str;
  166. const xmlChar *cur;
  167. xmlChar *ret = NULL;
  168. xmlChar *expr = NULL;
  169. xsltAttrVTPtr avt;
  170. int i = 0, lastavt = 0;
  171. if ((style == NULL) || (attr == NULL) || (attr->children == NULL))
  172. return;
  173. if ((attr->children->type != XML_TEXT_NODE) ||
  174. (attr->children->next != NULL)) {
  175. xsltTransformError(NULL, style, attr->parent,
  176. "Attribute '%s': The content is expected to be a single text "
  177. "node when compiling an AVT.\n", attr->name);
  178. style->errors++;
  179. return;
  180. }
  181. str = attr->children->content;
  182. if ((xmlStrchr(str, '{') == NULL) &&
  183. (xmlStrchr(str, '}') == NULL)) return;
  184. #ifdef WITH_XSLT_DEBUG_AVT
  185. xsltGenericDebug(xsltGenericDebugContext,
  186. "Found AVT %s: %s\n", attr->name, str);
  187. #endif
  188. if (attr->psvi != NULL) {
  189. #ifdef WITH_XSLT_DEBUG_AVT
  190. xsltGenericDebug(xsltGenericDebugContext,
  191. "AVT %s: already compiled\n", attr->name);
  192. #endif
  193. return;
  194. }
  195. /*
  196. * Create a new AVT object.
  197. */
  198. avt = xsltNewAttrVT(style);
  199. if (avt == NULL)
  200. return;
  201. attr->psvi = avt;
  202. avt->nsList = xmlGetNsList(attr->doc, attr->parent);
  203. if (avt->nsList != NULL) {
  204. while (avt->nsList[i] != NULL)
  205. i++;
  206. }
  207. avt->nsNr = i;
  208. cur = str;
  209. while (*cur != 0) {
  210. if (*cur == '{') {
  211. if (*(cur+1) == '{') { /* escaped '{' */
  212. cur++;
  213. ret = xmlStrncat(ret, str, cur - str);
  214. cur++;
  215. str = cur;
  216. continue;
  217. }
  218. if (*(cur+1) == '}') { /* skip empty AVT */
  219. ret = xmlStrncat(ret, str, cur - str);
  220. cur += 2;
  221. str = cur;
  222. continue;
  223. }
  224. if ((ret != NULL) || (cur - str > 0)) {
  225. ret = xmlStrncat(ret, str, cur - str);
  226. str = cur;
  227. if (avt->nb_seg == 0)
  228. avt->strstart = 1;
  229. if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
  230. goto error;
  231. ret = NULL;
  232. lastavt = 0;
  233. }
  234. cur++;
  235. while ((*cur != 0) && (*cur != '}')) {
  236. /* Need to check for literal (bug539741) */
  237. if ((*cur == '\'') || (*cur == '"')) {
  238. char delim = *(cur++);
  239. while ((*cur != 0) && (*cur != delim))
  240. cur++;
  241. if (*cur != 0)
  242. cur++; /* skip the ending delimiter */
  243. } else
  244. cur++;
  245. }
  246. if (*cur == 0) {
  247. xsltTransformError(NULL, style, attr->parent,
  248. "Attribute '%s': The AVT has an unmatched '{'.\n",
  249. attr->name);
  250. style->errors++;
  251. goto error;
  252. }
  253. str++;
  254. expr = xmlStrndup(str, cur - str);
  255. if (expr == NULL) {
  256. /*
  257. * TODO: What needs to be done here?
  258. */
  259. XSLT_TODO
  260. goto error;
  261. } else {
  262. xmlXPathCompExprPtr comp;
  263. comp = xsltXPathCompile(style, expr);
  264. if (comp == NULL) {
  265. xsltTransformError(NULL, style, attr->parent,
  266. "Attribute '%s': Failed to compile the expression "
  267. "'%s' in the AVT.\n", attr->name, expr);
  268. style->errors++;
  269. goto error;
  270. }
  271. if (avt->nb_seg == 0)
  272. avt->strstart = 0;
  273. if (lastavt == 1) {
  274. if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL)
  275. goto error;
  276. }
  277. if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL)
  278. goto error;
  279. lastavt = 1;
  280. xmlFree(expr);
  281. expr = NULL;
  282. }
  283. cur++;
  284. str = cur;
  285. } else if (*cur == '}') {
  286. cur++;
  287. if (*cur == '}') { /* escaped '}' */
  288. ret = xmlStrncat(ret, str, cur - str);
  289. cur++;
  290. str = cur;
  291. continue;
  292. } else {
  293. xsltTransformError(NULL, style, attr->parent,
  294. "Attribute '%s': The AVT has an unmatched '}'.\n",
  295. attr->name);
  296. goto error;
  297. }
  298. } else
  299. cur++;
  300. }
  301. if ((ret != NULL) || (cur - str > 0)) {
  302. ret = xmlStrncat(ret, str, cur - str);
  303. str = cur;
  304. if (avt->nb_seg == 0)
  305. avt->strstart = 1;
  306. if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
  307. goto error;
  308. ret = NULL;
  309. }
  310. error:
  311. if (avt == NULL) {
  312. xsltTransformError(NULL, style, attr->parent,
  313. "xsltCompileAttr: malloc problem\n");
  314. } else {
  315. if (attr->psvi != avt) { /* may have changed from realloc */
  316. attr->psvi = avt;
  317. /*
  318. * This is a "hack", but I can't see any clean method of
  319. * doing it. If a re-alloc has taken place, then the pointer
  320. * for this AVT may have changed. style->attVTs was set by
  321. * xsltNewAttrVT, so it needs to be re-set to the new value!
  322. */
  323. style->attVTs = avt;
  324. }
  325. }
  326. if (ret != NULL)
  327. xmlFree(ret);
  328. if (expr != NULL)
  329. xmlFree(expr);
  330. }
  331. /**
  332. * xsltEvalAVT:
  333. * @ctxt: the XSLT transformation context
  334. * @avt: the prevompiled attribute value template info
  335. * @node: the node hosting the attribute
  336. *
  337. * Process the given AVT, and return the new string value.
  338. *
  339. * Returns the computed string value or NULL, must be deallocated by the
  340. * caller.
  341. */
  342. xmlChar *
  343. xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) {
  344. xmlChar *ret = NULL, *tmp;
  345. xmlXPathCompExprPtr comp;
  346. xsltAttrVTPtr cur = (xsltAttrVTPtr) avt;
  347. int i;
  348. int str;
  349. if ((ctxt == NULL) || (avt == NULL) || (node == NULL))
  350. return(NULL);
  351. str = cur->strstart;
  352. for (i = 0;i < cur->nb_seg;i++) {
  353. if (str) {
  354. ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]);
  355. } else {
  356. comp = (xmlXPathCompExprPtr) cur->segments[i];
  357. tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList);
  358. if (tmp != NULL) {
  359. if (ret != NULL) {
  360. ret = xmlStrcat(ret, tmp);
  361. xmlFree(tmp);
  362. } else {
  363. ret = tmp;
  364. }
  365. }
  366. }
  367. str = !str;
  368. }
  369. return(ret);
  370. }