[18214] | 1 | /* |
---|
| 2 | * namespaces.c: Implementation of the XSLT namespaces 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 | |
---|
| 12 | #define IN_LIBXSLT |
---|
| 13 | #include "libxslt.h" |
---|
| 14 | |
---|
| 15 | #include <string.h> |
---|
| 16 | |
---|
| 17 | #ifdef HAVE_SYS_TYPES_H |
---|
| 18 | #include <sys/types.h> |
---|
| 19 | #endif |
---|
| 20 | #ifdef HAVE_MATH_H |
---|
| 21 | #include <math.h> |
---|
| 22 | #endif |
---|
| 23 | #ifdef HAVE_FLOAT_H |
---|
| 24 | #include <float.h> |
---|
| 25 | #endif |
---|
| 26 | #ifdef HAVE_IEEEFP_H |
---|
| 27 | #include <ieeefp.h> |
---|
| 28 | #endif |
---|
| 29 | #ifdef HAVE_NAN_H |
---|
| 30 | #include <nan.h> |
---|
| 31 | #endif |
---|
| 32 | #ifdef HAVE_CTYPE_H |
---|
| 33 | #include <ctype.h> |
---|
| 34 | #endif |
---|
| 35 | |
---|
| 36 | #include <libxml/xmlmemory.h> |
---|
| 37 | #include <libxml/tree.h> |
---|
| 38 | #include <libxml/hash.h> |
---|
| 39 | #include <libxml/xmlerror.h> |
---|
| 40 | #include <libxml/uri.h> |
---|
| 41 | #include "xslt.h" |
---|
| 42 | #include "xsltInternals.h" |
---|
| 43 | #include "xsltutils.h" |
---|
| 44 | #include "namespaces.h" |
---|
| 45 | #include "imports.h" |
---|
| 46 | |
---|
| 47 | |
---|
| 48 | |
---|
| 49 | /************************************************************************ |
---|
| 50 | * * |
---|
| 51 | * Module interfaces * |
---|
| 52 | * * |
---|
| 53 | ************************************************************************/ |
---|
| 54 | |
---|
| 55 | /** |
---|
| 56 | * xsltNamespaceAlias: |
---|
| 57 | * @style: the XSLT stylesheet |
---|
| 58 | * @node: the xsl:namespace-alias node |
---|
| 59 | * |
---|
| 60 | * Read the stylesheet-prefix and result-prefix attributes, register |
---|
| 61 | * them as well as the corresponding namespace. |
---|
| 62 | */ |
---|
| 63 | void |
---|
| 64 | xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) { |
---|
| 65 | xmlChar *sprefix; |
---|
| 66 | xmlNsPtr sNs; |
---|
| 67 | xmlChar *rprefix; |
---|
| 68 | xmlNsPtr rNs; |
---|
| 69 | |
---|
| 70 | sprefix = xsltGetNsProp(node, (const xmlChar *)"stylesheet-prefix", |
---|
| 71 | XSLT_NAMESPACE); |
---|
| 72 | if (sprefix == NULL) { |
---|
| 73 | xsltTransformError(NULL, style, node, |
---|
| 74 | "namespace-alias: stylesheet-prefix attribute missing\n"); |
---|
| 75 | return; |
---|
| 76 | } |
---|
| 77 | rprefix = xsltGetNsProp(node, (const xmlChar *)"result-prefix", |
---|
| 78 | XSLT_NAMESPACE); |
---|
| 79 | if (rprefix == NULL) { |
---|
| 80 | xsltTransformError(NULL, style, node, |
---|
| 81 | "namespace-alias: result-prefix attribute missing\n"); |
---|
| 82 | goto error; |
---|
| 83 | } |
---|
| 84 | if (xmlStrEqual(sprefix, (const xmlChar *)"#default")) { |
---|
| 85 | sNs = xmlSearchNs(node->doc, node, NULL); |
---|
| 86 | } else { |
---|
| 87 | sNs = xmlSearchNs(node->doc, node, sprefix); |
---|
| 88 | } |
---|
| 89 | if ((sNs == NULL) || (sNs->href == NULL)) { |
---|
| 90 | xsltTransformError(NULL, style, node, |
---|
| 91 | "namespace-alias: prefix %s not bound to any namespace\n", |
---|
| 92 | sprefix); |
---|
| 93 | goto error; |
---|
| 94 | } |
---|
| 95 | if (xmlStrEqual(rprefix, (const xmlChar *)"#default")) { |
---|
| 96 | rNs = xmlSearchNs(node->doc, node, NULL); |
---|
| 97 | } else { |
---|
| 98 | rNs = xmlSearchNs(node->doc, node, rprefix); |
---|
| 99 | } |
---|
| 100 | if ((rNs == NULL) || (rNs->href == NULL)) { |
---|
| 101 | xsltTransformError(NULL, style, node, |
---|
| 102 | "namespace-alias: prefix %s not bound to any namespace\n", |
---|
| 103 | rprefix); |
---|
| 104 | goto error; |
---|
| 105 | } |
---|
| 106 | if (style->nsAliases == NULL) |
---|
| 107 | style->nsAliases = xmlHashCreate(10); |
---|
| 108 | if (style->nsAliases == NULL) { |
---|
| 109 | xsltTransformError(NULL, style, node, |
---|
| 110 | "namespace-alias: cannot create hash table\n"); |
---|
| 111 | goto error; |
---|
| 112 | } |
---|
| 113 | xmlHashAddEntry((xmlHashTablePtr) style->nsAliases, |
---|
| 114 | sNs->href, (void *) rNs->href); |
---|
| 115 | |
---|
| 116 | error: |
---|
| 117 | if (sprefix != NULL) |
---|
| 118 | xmlFree(sprefix); |
---|
| 119 | if (rprefix != NULL) |
---|
| 120 | xmlFree(rprefix); |
---|
| 121 | } |
---|
| 122 | |
---|
| 123 | /** |
---|
| 124 | * xsltGetSpecialNamespace: |
---|
| 125 | * @ctxt: a transformation context |
---|
| 126 | * @cur: the input node |
---|
| 127 | * @URI: the namespace URI |
---|
| 128 | * @prefix: the suggested prefix |
---|
| 129 | * @out: the output node (or its parent) |
---|
| 130 | * |
---|
| 131 | * Find the right namespace value for this URI, if needed create |
---|
| 132 | * and add a new namespace decalaration on the node |
---|
| 133 | * |
---|
| 134 | * Returns the namespace node to use or NULL |
---|
| 135 | */ |
---|
| 136 | xmlNsPtr |
---|
| 137 | xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, |
---|
| 138 | const xmlChar *URI, const xmlChar *prefix, xmlNodePtr out) { |
---|
| 139 | xmlNsPtr ret; |
---|
| 140 | static int prefixno = 1; |
---|
| 141 | char nprefix[10]; |
---|
| 142 | |
---|
| 143 | if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (URI == NULL)) |
---|
| 144 | return(NULL); |
---|
| 145 | |
---|
[19101] | 146 | if ((prefix == NULL) && (URI[0] == 0)) { |
---|
| 147 | ret = xmlSearchNs(out->doc, out, NULL); |
---|
| 148 | if (ret != NULL) { |
---|
| 149 | ret = xmlNewNs(out, URI, prefix); |
---|
| 150 | return(ret); |
---|
| 151 | } |
---|
| 152 | return(NULL); |
---|
| 153 | } |
---|
| 154 | |
---|
[18214] | 155 | if ((out->parent != NULL) && |
---|
| 156 | (out->parent->type == XML_ELEMENT_NODE) && |
---|
| 157 | (out->parent->ns != NULL) && |
---|
| 158 | (xmlStrEqual(out->parent->ns->href, URI))) |
---|
| 159 | ret = out->parent->ns; |
---|
| 160 | else |
---|
| 161 | ret = xmlSearchNsByHref(out->doc, out, URI); |
---|
| 162 | |
---|
[18542] | 163 | if ((ret == NULL) || (ret->prefix == NULL)) { |
---|
[18214] | 164 | if (prefix == NULL) { |
---|
| 165 | do { |
---|
| 166 | sprintf(nprefix, "ns%d", prefixno++); |
---|
| 167 | ret = xmlSearchNs(out->doc, out, (xmlChar *)nprefix); |
---|
| 168 | } while (ret != NULL); |
---|
| 169 | prefix = (const xmlChar *) &nprefix[0]; |
---|
| 170 | } |
---|
| 171 | if (out->type == XML_ELEMENT_NODE) |
---|
| 172 | ret = xmlNewNs(out, URI, prefix); |
---|
| 173 | } |
---|
| 174 | return(ret); |
---|
| 175 | } |
---|
| 176 | |
---|
| 177 | /** |
---|
| 178 | * xsltGetNamespace: |
---|
| 179 | * @ctxt: a transformation context |
---|
| 180 | * @cur: the input node |
---|
| 181 | * @ns: the namespace |
---|
| 182 | * @out: the output node (or its parent) |
---|
| 183 | * |
---|
| 184 | * Find the right namespace value for this prefix, if needed create |
---|
| 185 | * and add a new namespace decalaration on the node |
---|
| 186 | * Handle namespace aliases |
---|
| 187 | * |
---|
| 188 | * Returns the namespace node to use or NULL |
---|
| 189 | */ |
---|
| 190 | xmlNsPtr |
---|
| 191 | xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, |
---|
| 192 | xmlNodePtr out) { |
---|
| 193 | xsltStylesheetPtr style; |
---|
| 194 | xmlNsPtr ret; |
---|
| 195 | const xmlChar *URI = NULL; /* the replacement URI */ |
---|
| 196 | |
---|
| 197 | if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (ns == NULL)) |
---|
| 198 | return(NULL); |
---|
| 199 | |
---|
| 200 | style = ctxt->style; |
---|
| 201 | while (style != NULL) { |
---|
| 202 | if (style->nsAliases != NULL) |
---|
| 203 | URI = (const xmlChar *) |
---|
| 204 | xmlHashLookup(style->nsAliases, ns->href); |
---|
| 205 | if (URI != NULL) |
---|
| 206 | break; |
---|
| 207 | |
---|
| 208 | style = xsltNextImport(style); |
---|
| 209 | } |
---|
| 210 | |
---|
| 211 | if (URI == NULL) |
---|
| 212 | URI = ns->href; |
---|
| 213 | |
---|
| 214 | if ((out->parent != NULL) && |
---|
| 215 | (out->parent->type == XML_ELEMENT_NODE) && |
---|
| 216 | (out->parent->ns != NULL) && |
---|
| 217 | (xmlStrEqual(out->parent->ns->href, URI))) |
---|
| 218 | ret = out->parent->ns; |
---|
[19101] | 219 | else { |
---|
| 220 | if (ns->prefix != NULL) { |
---|
| 221 | ret = xmlSearchNs(out->doc, out, ns->prefix); |
---|
| 222 | if ((ret == NULL) || (!xmlStrEqual(ns->href, URI))) { |
---|
| 223 | ret = xmlSearchNsByHref(out->doc, out, URI); |
---|
| 224 | } |
---|
| 225 | } else { |
---|
| 226 | ret = xmlSearchNsByHref(out->doc, out, URI); |
---|
| 227 | } |
---|
| 228 | } |
---|
[18214] | 229 | |
---|
| 230 | if (ret == NULL) { |
---|
| 231 | if (out->type == XML_ELEMENT_NODE) |
---|
| 232 | ret = xmlNewNs(out, URI, ns->prefix); |
---|
| 233 | } |
---|
| 234 | return(ret); |
---|
| 235 | } |
---|
| 236 | |
---|
| 237 | /** |
---|
| 238 | * xsltCopyNamespaceList: |
---|
| 239 | * @ctxt: a transformation context |
---|
| 240 | * @node: the target node |
---|
| 241 | * @cur: the first namespace |
---|
| 242 | * |
---|
| 243 | * Do a copy of an namespace list. If @node is non-NULL the |
---|
| 244 | * new namespaces are added automatically. This handles namespaces |
---|
| 245 | * aliases |
---|
| 246 | * |
---|
| 247 | * Returns: a new xmlNsPtr, or NULL in case of error. |
---|
| 248 | */ |
---|
| 249 | xmlNsPtr |
---|
| 250 | xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, |
---|
| 251 | xmlNsPtr cur) { |
---|
| 252 | xmlNsPtr ret = NULL, tmp; |
---|
| 253 | xmlNsPtr p = NULL,q; |
---|
| 254 | const xmlChar *URI; |
---|
| 255 | |
---|
| 256 | if (cur == NULL) |
---|
| 257 | return(NULL); |
---|
| 258 | if (cur->type != XML_NAMESPACE_DECL) |
---|
| 259 | return(NULL); |
---|
| 260 | |
---|
| 261 | /* |
---|
| 262 | * One can add namespaces only on element nodes |
---|
| 263 | */ |
---|
| 264 | if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) |
---|
| 265 | node = NULL; |
---|
| 266 | |
---|
| 267 | while (cur != NULL) { |
---|
| 268 | if (cur->type != XML_NAMESPACE_DECL) |
---|
| 269 | break; |
---|
| 270 | |
---|
| 271 | /* |
---|
| 272 | * Avoid duplicating namespace declrations on the tree |
---|
| 273 | */ |
---|
| 274 | if ((node != NULL) && (node->ns != NULL) && |
---|
| 275 | (xmlStrEqual(node->ns->href, cur->href)) && |
---|
| 276 | (xmlStrEqual(node->ns->prefix, cur->prefix))) { |
---|
| 277 | cur = cur->next; |
---|
| 278 | continue; |
---|
| 279 | } |
---|
| 280 | tmp = xmlSearchNs(node->doc, node, cur->prefix); |
---|
| 281 | if ((tmp != NULL) && (xmlStrEqual(tmp->href, cur->href))) { |
---|
| 282 | cur = cur->next; |
---|
| 283 | continue; |
---|
| 284 | } |
---|
| 285 | |
---|
| 286 | if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { |
---|
| 287 | /* TODO apply cascading */ |
---|
| 288 | URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases, |
---|
| 289 | cur->href); |
---|
| 290 | if (URI != NULL) { |
---|
| 291 | q = xmlNewNs(node, URI, cur->prefix); |
---|
| 292 | } else { |
---|
| 293 | q = xmlNewNs(node, cur->href, cur->prefix); |
---|
| 294 | } |
---|
| 295 | if (p == NULL) { |
---|
| 296 | ret = p = q; |
---|
| 297 | } else { |
---|
| 298 | p->next = q; |
---|
| 299 | p = q; |
---|
| 300 | } |
---|
| 301 | } |
---|
| 302 | cur = cur->next; |
---|
| 303 | } |
---|
| 304 | return(ret); |
---|
| 305 | } |
---|
| 306 | |
---|
| 307 | /** |
---|
| 308 | * xsltCopyNamespace: |
---|
| 309 | * @ctxt: a transformation context |
---|
| 310 | * @node: the target node |
---|
| 311 | * @cur: the namespace node |
---|
| 312 | * |
---|
| 313 | * Do a copy of an namespace node. If @node is non-NULL the |
---|
| 314 | * new namespaces are added automatically. This handles namespaces |
---|
| 315 | * aliases |
---|
| 316 | * |
---|
| 317 | * Returns: a new xmlNsPtr, or NULL in case of error. |
---|
| 318 | */ |
---|
| 319 | xmlNsPtr |
---|
| 320 | xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr node, |
---|
| 321 | xmlNsPtr cur) { |
---|
| 322 | xmlNsPtr ret = NULL; |
---|
| 323 | const xmlChar *URI; |
---|
| 324 | |
---|
| 325 | if (cur == NULL) |
---|
| 326 | return(NULL); |
---|
| 327 | if (cur->type != XML_NAMESPACE_DECL) |
---|
| 328 | return(NULL); |
---|
| 329 | |
---|
| 330 | /* |
---|
| 331 | * One can add namespaces only on element nodes |
---|
| 332 | */ |
---|
| 333 | if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) |
---|
| 334 | node = NULL; |
---|
| 335 | |
---|
| 336 | if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { |
---|
| 337 | URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases, |
---|
| 338 | cur->href); |
---|
| 339 | if (URI != NULL) { |
---|
| 340 | ret = xmlNewNs(node, URI, cur->prefix); |
---|
| 341 | } else { |
---|
| 342 | ret = xmlNewNs(node, cur->href, cur->prefix); |
---|
| 343 | } |
---|
| 344 | } |
---|
| 345 | return(ret); |
---|
| 346 | } |
---|
| 347 | |
---|
| 348 | |
---|
| 349 | /** |
---|
| 350 | * xsltFreeNamespaceAliasHashes: |
---|
| 351 | * @style: an XSLT stylesheet |
---|
| 352 | * |
---|
| 353 | * Free up the memory used by namespaces aliases |
---|
| 354 | */ |
---|
| 355 | void |
---|
| 356 | xsltFreeNamespaceAliasHashes(xsltStylesheetPtr style) { |
---|
| 357 | if (style->nsAliases != NULL) |
---|
| 358 | xmlHashFree((xmlHashTablePtr) style->nsAliases, NULL); |
---|
| 359 | style->nsAliases = NULL; |
---|
| 360 | } |
---|