| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * pattern.c: Implemetation of the template match compilation and lookup | |
| 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 /* | |
| 13 * TODO: handle pathological cases like *[*[@a="b"]] | |
| 14 * TODO: detect [number] at compilation, optimize accordingly | |
| 15 */ | |
| 16 | |
| 17 #define IN_LIBXSLT | |
| 18 #include "libxslt.h" | |
| 19 | |
| 20 #include <string.h> | |
| 21 | |
| 22 #include <libxml/xmlmemory.h> | |
| 23 #include <libxml/tree.h> | |
| 24 #include <libxml/valid.h> | |
| 25 #include <libxml/hash.h> | |
| 26 #include <libxml/xmlerror.h> | |
| 27 #include <libxml/parserInternals.h> | |
| 28 #include <libxml/xpath.h> | |
| 29 #include "xslt.h" | |
| 30 #include "xsltInternals.h" | |
| 31 #include "xsltutils.h" | |
| 32 #include "imports.h" | |
| 33 #include "templates.h" | |
| 34 #include "keys.h" | |
| 35 #include "pattern.h" | |
| 36 #include "documents.h" | |
| 37 | |
| 38 #ifdef WITH_XSLT_DEBUG | |
| 39 #define WITH_XSLT_DEBUG_PATTERN | |
| 40 #endif | |
| 41 | |
| 42 /* | |
| 43 * Types are private: | |
| 44 */ | |
| 45 | |
| 46 typedef enum { | |
| 47 XSLT_OP_END=0, | |
| 48 XSLT_OP_ROOT, | |
| 49 XSLT_OP_ELEM, | |
| 50 XSLT_OP_ATTR, | |
| 51 XSLT_OP_PARENT, | |
| 52 XSLT_OP_ANCESTOR, | |
| 53 XSLT_OP_ID, | |
| 54 XSLT_OP_KEY, | |
| 55 XSLT_OP_NS, | |
| 56 XSLT_OP_ALL, | |
| 57 XSLT_OP_PI, | |
| 58 XSLT_OP_COMMENT, | |
| 59 XSLT_OP_TEXT, | |
| 60 XSLT_OP_NODE, | |
| 61 XSLT_OP_PREDICATE | |
| 62 } xsltOp; | |
| 63 | |
| 64 typedef enum { | |
| 65 AXIS_CHILD=1, | |
| 66 AXIS_ATTRIBUTE | |
| 67 } xsltAxis; | |
| 68 | |
| 69 typedef struct _xsltStepState xsltStepState; | |
| 70 typedef xsltStepState *xsltStepStatePtr; | |
| 71 struct _xsltStepState { | |
| 72 int step; | |
| 73 xmlNodePtr node; | |
| 74 }; | |
| 75 | |
| 76 typedef struct _xsltStepStates xsltStepStates; | |
| 77 typedef xsltStepStates *xsltStepStatesPtr; | |
| 78 struct _xsltStepStates { | |
| 79 int nbstates; | |
| 80 int maxstates; | |
| 81 xsltStepStatePtr states; | |
| 82 }; | |
| 83 | |
| 84 typedef struct _xsltStepOp xsltStepOp; | |
| 85 typedef xsltStepOp *xsltStepOpPtr; | |
| 86 struct _xsltStepOp { | |
| 87 xsltOp op; | |
| 88 xmlChar *value; | |
| 89 xmlChar *value2; | |
| 90 xmlChar *value3; | |
| 91 xmlXPathCompExprPtr comp; | |
| 92 /* | |
| 93 * Optimisations for count | |
| 94 */ | |
| 95 int previousExtra; | |
| 96 int indexExtra; | |
| 97 int lenExtra; | |
| 98 }; | |
| 99 | |
| 100 struct _xsltCompMatch { | |
| 101 struct _xsltCompMatch *next; /* siblings in the name hash */ | |
| 102 float priority; /* the priority */ | |
| 103 const xmlChar *pattern; /* the pattern */ | |
| 104 const xmlChar *mode; /* the mode */ | |
| 105 const xmlChar *modeURI; /* the mode URI */ | |
| 106 xsltTemplatePtr template; /* the associated template */ | |
| 107 xmlNodePtr node; /* the containing element */ | |
| 108 | |
| 109 int direct; | |
| 110 /* TODO fix the statically allocated size steps[] */ | |
| 111 int nbStep; | |
| 112 int maxStep; | |
| 113 xmlNsPtr *nsList; /* the namespaces in scope */ | |
| 114 int nsNr; /* the number of namespaces in scope */ | |
| 115 xsltStepOpPtr steps; /* ops for computation */ | |
| 116 }; | |
| 117 | |
| 118 typedef struct _xsltParserContext xsltParserContext; | |
| 119 typedef xsltParserContext *xsltParserContextPtr; | |
| 120 struct _xsltParserContext { | |
| 121 xsltStylesheetPtr style; /* the stylesheet */ | |
| 122 xsltTransformContextPtr ctxt; /* the transformation or NULL */ | |
| 123 const xmlChar *cur; /* the current char being parsed */ | |
| 124 const xmlChar *base; /* the full expression */ | |
| 125 xmlDocPtr doc; /* the source document */ | |
| 126 xmlNodePtr elem; /* the source element */ | |
| 127 int error; /* error code */ | |
| 128 xsltCompMatchPtr comp; /* the result */ | |
| 129 }; | |
| 130 | |
| 131 /************************************************************************ | |
| 132 * * | |
| 133 * Type functions * | |
| 134 * * | |
| 135 ************************************************************************/ | |
| 136 | |
| 137 /** | |
| 138 * xsltNewCompMatch: | |
| 139 * | |
| 140 * Create a new XSLT CompMatch | |
| 141 * | |
| 142 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error | |
| 143 */ | |
| 144 static xsltCompMatchPtr | |
| 145 xsltNewCompMatch(void) { | |
| 146 xsltCompMatchPtr cur; | |
| 147 | |
| 148 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch)); | |
| 149 if (cur == NULL) { | |
| 150 xsltTransformError(NULL, NULL, NULL, | |
| 151 "xsltNewCompMatch : out of memory error\n"); | |
| 152 return(NULL); | |
| 153 } | |
| 154 memset(cur, 0, sizeof(xsltCompMatch)); | |
| 155 cur->maxStep = 10; | |
| 156 cur->nbStep = 0; | |
| 157 cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) * | |
| 158 cur->maxStep); | |
| 159 if (cur->steps == NULL) { | |
| 160 xsltTransformError(NULL, NULL, NULL, | |
| 161 "xsltNewCompMatch : out of memory error\n"); | |
| 162 xmlFree(cur); | |
| 163 return(NULL); | |
| 164 } | |
| 165 cur->nsNr = 0; | |
| 166 cur->nsList = NULL; | |
| 167 cur->direct = 0; | |
| 168 return(cur); | |
| 169 } | |
| 170 | |
| 171 /** | |
| 172 * xsltFreeCompMatch: | |
| 173 * @comp: an XSLT comp | |
| 174 * | |
| 175 * Free up the memory allocated by @comp | |
| 176 */ | |
| 177 static void | |
| 178 xsltFreeCompMatch(xsltCompMatchPtr comp) { | |
| 179 xsltStepOpPtr op; | |
| 180 int i; | |
| 181 | |
| 182 if (comp == NULL) | |
| 183 return; | |
| 184 if (comp->pattern != NULL) | |
| 185 xmlFree((xmlChar *)comp->pattern); | |
| 186 if (comp->nsList != NULL) | |
| 187 xmlFree(comp->nsList); | |
| 188 for (i = 0;i < comp->nbStep;i++) { | |
| 189 op = &comp->steps[i]; | |
| 190 if (op->value != NULL) | |
| 191 xmlFree(op->value); | |
| 192 if (op->value2 != NULL) | |
| 193 xmlFree(op->value2); | |
| 194 if (op->value3 != NULL) | |
| 195 xmlFree(op->value3); | |
| 196 if (op->comp != NULL) | |
| 197 xmlXPathFreeCompExpr(op->comp); | |
| 198 } | |
| 199 xmlFree(comp->steps); | |
| 200 memset(comp, -1, sizeof(xsltCompMatch)); | |
| 201 xmlFree(comp); | |
| 202 } | |
| 203 | |
| 204 /** | |
| 205 * xsltFreeCompMatchList: | |
| 206 * @comp: an XSLT comp list | |
| 207 * | |
| 208 * Free up the memory allocated by all the elements of @comp | |
| 209 */ | |
| 210 void | |
| 211 xsltFreeCompMatchList(xsltCompMatchPtr comp) { | |
| 212 xsltCompMatchPtr cur; | |
| 213 | |
| 214 while (comp != NULL) { | |
| 215 cur = comp; | |
| 216 comp = comp->next; | |
| 217 xsltFreeCompMatch(cur); | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 /** | |
| 222 * xsltNormalizeCompSteps: | |
| 223 * @payload: pointer to template hash table entry | |
| 224 * @data: pointer to the stylesheet | |
| 225 * @name: template match name | |
| 226 * | |
| 227 * This is a hashtable scanner function to normalize the compiled | |
| 228 * steps of an imported stylesheet. | |
| 229 */ | |
| 230 void xsltNormalizeCompSteps(void *payload, | |
| 231 void *data, const xmlChar *name ATTRIBUTE_UNUSED) { | |
| 232 xsltCompMatchPtr comp = payload; | |
| 233 xsltStylesheetPtr style = data; | |
| 234 int ix; | |
| 235 | |
| 236 for (ix = 0; ix < comp->nbStep; ix++) { | |
| 237 comp->steps[ix].previousExtra += style->extrasNr; | |
| 238 comp->steps[ix].indexExtra += style->extrasNr; | |
| 239 comp->steps[ix].lenExtra += style->extrasNr; | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 /** | |
| 244 * xsltNewParserContext: | |
| 245 * @style: the stylesheet | |
| 246 * @ctxt: the transformation context, if done at run-time | |
| 247 * | |
| 248 * Create a new XSLT ParserContext | |
| 249 * | |
| 250 * Returns the newly allocated xsltParserContextPtr or NULL in case of error | |
| 251 */ | |
| 252 static xsltParserContextPtr | |
| 253 xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) { | |
| 254 xsltParserContextPtr cur; | |
| 255 | |
| 256 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext)); | |
| 257 if (cur == NULL) { | |
| 258 xsltTransformError(NULL, NULL, NULL, | |
| 259 "xsltNewParserContext : malloc failed\n"); | |
| 260 return(NULL); | |
| 261 } | |
| 262 memset(cur, 0, sizeof(xsltParserContext)); | |
| 263 cur->style = style; | |
| 264 cur->ctxt = ctxt; | |
| 265 return(cur); | |
| 266 } | |
| 267 | |
| 268 /** | |
| 269 * xsltFreeParserContext: | |
| 270 * @ctxt: an XSLT parser context | |
| 271 * | |
| 272 * Free up the memory allocated by @ctxt | |
| 273 */ | |
| 274 static void | |
| 275 xsltFreeParserContext(xsltParserContextPtr ctxt) { | |
| 276 if (ctxt == NULL) | |
| 277 return; | |
| 278 memset(ctxt, -1, sizeof(xsltParserContext)); | |
| 279 xmlFree(ctxt); | |
| 280 } | |
| 281 | |
| 282 /** | |
| 283 * xsltCompMatchAdd: | |
| 284 * @comp: the compiled match expression | |
| 285 * @op: an op | |
| 286 * @value: the first value | |
| 287 * @value2: the second value | |
| 288 * @novar: flag to set XML_XPATH_NOVAR | |
| 289 * | |
| 290 * Add an step to an XSLT Compiled Match | |
| 291 * | |
| 292 * Returns -1 in case of failure, 0 otherwise. | |
| 293 */ | |
| 294 static int | |
| 295 xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp, | |
| 296 xsltOp op, xmlChar * value, xmlChar * value2, int novar) | |
| 297 { | |
| 298 if (comp->nbStep >= comp->maxStep) { | |
| 299 xsltStepOpPtr tmp; | |
| 300 | |
| 301 tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * | |
| 302 sizeof(xsltStepOp)); | |
| 303 if (tmp == NULL) { | |
| 304 xsltGenericError(xsltGenericErrorContext, | |
| 305 "xsltCompMatchAdd: memory re-allocation failure.\n"); | |
| 306 if (ctxt->style != NULL) | |
| 307 ctxt->style->errors++; | |
| 308 if (value) | |
| 309 xmlFree(value); | |
| 310 if (value2) | |
| 311 xmlFree(value2); | |
| 312 return (-1); | |
| 313 } | |
| 314 comp->maxStep *= 2; | |
| 315 comp->steps = tmp; | |
| 316 } | |
| 317 comp->steps[comp->nbStep].op = op; | |
| 318 comp->steps[comp->nbStep].value = value; | |
| 319 comp->steps[comp->nbStep].value2 = value2; | |
| 320 comp->steps[comp->nbStep].value3 = NULL; | |
| 321 comp->steps[comp->nbStep].comp = NULL; | |
| 322 if (ctxt->ctxt != NULL) { | |
| 323 comp->steps[comp->nbStep].previousExtra = | |
| 324 xsltAllocateExtraCtxt(ctxt->ctxt); | |
| 325 comp->steps[comp->nbStep].indexExtra = | |
| 326 xsltAllocateExtraCtxt(ctxt->ctxt); | |
| 327 comp->steps[comp->nbStep].lenExtra = | |
| 328 xsltAllocateExtraCtxt(ctxt->ctxt); | |
| 329 } else { | |
| 330 comp->steps[comp->nbStep].previousExtra = | |
| 331 xsltAllocateExtra(ctxt->style); | |
| 332 comp->steps[comp->nbStep].indexExtra = | |
| 333 xsltAllocateExtra(ctxt->style); | |
| 334 comp->steps[comp->nbStep].lenExtra = | |
| 335 xsltAllocateExtra(ctxt->style); | |
| 336 } | |
| 337 if (op == XSLT_OP_PREDICATE) { | |
| 338 xmlXPathContextPtr xctxt; | |
| 339 | |
| 340 if (ctxt->style != NULL) | |
| 341 xctxt = xmlXPathNewContext(ctxt->style->doc); | |
| 342 else | |
| 343 xctxt = xmlXPathNewContext(NULL); | |
| 344 #ifdef XML_XPATH_NOVAR | |
| 345 if (novar != 0) | |
| 346 xctxt->flags = XML_XPATH_NOVAR; | |
| 347 #endif | |
| 348 if (ctxt->style != NULL) | |
| 349 xctxt->dict = ctxt->style->dict; | |
| 350 comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value); | |
| 351 xmlXPathFreeContext(xctxt); | |
| 352 if (comp->steps[comp->nbStep].comp == NULL) { | |
| 353 xsltTransformError(NULL, ctxt->style, ctxt->elem, | |
| 354 "Failed to compile predicate\n"); | |
| 355 if (ctxt->style != NULL) | |
| 356 ctxt->style->errors++; | |
| 357 } | |
| 358 } | |
| 359 comp->nbStep++; | |
| 360 return (0); | |
| 361 } | |
| 362 | |
| 363 /** | |
| 364 * xsltSwapTopCompMatch: | |
| 365 * @comp: the compiled match expression | |
| 366 * | |
| 367 * reverse the two top steps. | |
| 368 */ | |
| 369 static void | |
| 370 xsltSwapTopCompMatch(xsltCompMatchPtr comp) { | |
| 371 int i; | |
| 372 int j = comp->nbStep - 1; | |
| 373 | |
| 374 if (j > 0) { | |
| 375 register xmlChar *tmp; | |
| 376 register xsltOp op; | |
| 377 register xmlXPathCompExprPtr expr; | |
| 378 register int t; | |
| 379 i = j - 1; | |
| 380 tmp = comp->steps[i].value; | |
| 381 comp->steps[i].value = comp->steps[j].value; | |
| 382 comp->steps[j].value = tmp; | |
| 383 tmp = comp->steps[i].value2; | |
| 384 comp->steps[i].value2 = comp->steps[j].value2; | |
| 385 comp->steps[j].value2 = tmp; | |
| 386 tmp = comp->steps[i].value3; | |
| 387 comp->steps[i].value3 = comp->steps[j].value3; | |
| 388 comp->steps[j].value3 = tmp; | |
| 389 op = comp->steps[i].op; | |
| 390 comp->steps[i].op = comp->steps[j].op; | |
| 391 comp->steps[j].op = op; | |
| 392 expr = comp->steps[i].comp; | |
| 393 comp->steps[i].comp = comp->steps[j].comp; | |
| 394 comp->steps[j].comp = expr; | |
| 395 t = comp->steps[i].previousExtra; | |
| 396 comp->steps[i].previousExtra = comp->steps[j].previousExtra; | |
| 397 comp->steps[j].previousExtra = t; | |
| 398 t = comp->steps[i].indexExtra; | |
| 399 comp->steps[i].indexExtra = comp->steps[j].indexExtra; | |
| 400 comp->steps[j].indexExtra = t; | |
| 401 t = comp->steps[i].lenExtra; | |
| 402 comp->steps[i].lenExtra = comp->steps[j].lenExtra; | |
| 403 comp->steps[j].lenExtra = t; | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 /** | |
| 408 * xsltReverseCompMatch: | |
| 409 * @ctxt: the parser context | |
| 410 * @comp: the compiled match expression | |
| 411 * | |
| 412 * reverse all the stack of expressions | |
| 413 */ | |
| 414 static void | |
| 415 xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) { | |
| 416 int i = 0; | |
| 417 int j = comp->nbStep - 1; | |
| 418 | |
| 419 while (j > i) { | |
| 420 register xmlChar *tmp; | |
| 421 register xsltOp op; | |
| 422 register xmlXPathCompExprPtr expr; | |
| 423 register int t; | |
| 424 | |
| 425 tmp = comp->steps[i].value; | |
| 426 comp->steps[i].value = comp->steps[j].value; | |
| 427 comp->steps[j].value = tmp; | |
| 428 tmp = comp->steps[i].value2; | |
| 429 comp->steps[i].value2 = comp->steps[j].value2; | |
| 430 comp->steps[j].value2 = tmp; | |
| 431 tmp = comp->steps[i].value3; | |
| 432 comp->steps[i].value3 = comp->steps[j].value3; | |
| 433 comp->steps[j].value3 = tmp; | |
| 434 op = comp->steps[i].op; | |
| 435 comp->steps[i].op = comp->steps[j].op; | |
| 436 comp->steps[j].op = op; | |
| 437 expr = comp->steps[i].comp; | |
| 438 comp->steps[i].comp = comp->steps[j].comp; | |
| 439 comp->steps[j].comp = expr; | |
| 440 t = comp->steps[i].previousExtra; | |
| 441 comp->steps[i].previousExtra = comp->steps[j].previousExtra; | |
| 442 comp->steps[j].previousExtra = t; | |
| 443 t = comp->steps[i].indexExtra; | |
| 444 comp->steps[i].indexExtra = comp->steps[j].indexExtra; | |
| 445 comp->steps[j].indexExtra = t; | |
| 446 t = comp->steps[i].lenExtra; | |
| 447 comp->steps[i].lenExtra = comp->steps[j].lenExtra; | |
| 448 comp->steps[j].lenExtra = t; | |
| 449 j--; | |
| 450 i++; | |
| 451 } | |
| 452 xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0); | |
| 453 | |
| 454 /* | |
| 455 * Detect consecutive XSLT_OP_PREDICATE and predicates on ops which | |
| 456 * haven't been optimized yet indicating a direct matching should be done. | |
| 457 */ | |
| 458 for (i = 0;i < comp->nbStep - 1;i++) { | |
| 459 xsltOp op = comp->steps[i].op; | |
| 460 | |
| 461 if ((op != XSLT_OP_ELEM) && | |
| 462 (op != XSLT_OP_ALL) && | |
| 463 (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) { | |
| 464 | |
| 465 comp->direct = 1; | |
| 466 if (comp->pattern[0] != '/') { | |
| 467 xmlChar *query; | |
| 468 | |
| 469 query = xmlStrdup((const xmlChar *)"//"); | |
| 470 query = xmlStrcat(query, comp->pattern); | |
| 471 | |
| 472 xmlFree((xmlChar *) comp->pattern); | |
| 473 comp->pattern = query; | |
| 474 } | |
| 475 break; | |
| 476 } | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 /************************************************************************ | |
| 481 * * | |
| 482 * The interpreter for the precompiled patterns * | |
| 483 * * | |
| 484 ************************************************************************/ | |
| 485 | |
| 486 static int | |
| 487 xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states, | |
| 488 int step, xmlNodePtr node) { | |
| 489 if ((states->states == NULL) || (states->maxstates <= 0)) { | |
| 490 states->maxstates = 4; | |
| 491 states->nbstates = 0; | |
| 492 states->states = xmlMalloc(4 * sizeof(xsltStepState)); | |
| 493 } | |
| 494 else if (states->maxstates <= states->nbstates) { | |
| 495 xsltStepState *tmp; | |
| 496 | |
| 497 tmp = (xsltStepStatePtr) xmlRealloc(states->states, | |
| 498 2 * states->maxstates * sizeof(xsltStepState)); | |
| 499 if (tmp == NULL) { | |
| 500 xsltGenericError(xsltGenericErrorContext, | |
| 501 "xsltPatPushState: memory re-allocation failure.\n"); | |
| 502 ctxt->state = XSLT_STATE_STOPPED; | |
| 503 return(-1); | |
| 504 } | |
| 505 states->states = tmp; | |
| 506 states->maxstates *= 2; | |
| 507 } | |
| 508 states->states[states->nbstates].step = step; | |
| 509 states->states[states->nbstates++].node = node; | |
| 510 #if 0 | |
| 511 fprintf(stderr, "Push: %d, %s\n", step, node->name); | |
| 512 #endif | |
| 513 return(0); | |
| 514 } | |
| 515 | |
| 516 /** | |
| 517 * xsltTestCompMatchDirect: | |
| 518 * @ctxt: a XSLT process context | |
| 519 * @comp: the precompiled pattern | |
| 520 * @node: a node | |
| 521 * @nsList: the namespaces in scope | |
| 522 * @nsNr: the number of namespaces in scope | |
| 523 * | |
| 524 * Test whether the node matches the pattern, do a direct evalutation | |
| 525 * and not a step by step evaluation. | |
| 526 * | |
| 527 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure | |
| 528 */ | |
| 529 static int | |
| 530 xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, | |
| 531 xmlNodePtr node, xmlNsPtr *nsList, int nsNr) { | |
| 532 xsltStepOpPtr sel = NULL; | |
| 533 xmlDocPtr prevdoc; | |
| 534 xmlDocPtr doc; | |
| 535 xmlXPathObjectPtr list; | |
| 536 int ix, j; | |
| 537 int nocache = 0; | |
| 538 int isRVT; | |
| 539 | |
| 540 doc = node->doc; | |
| 541 if (XSLT_IS_RES_TREE_FRAG(doc)) | |
| 542 isRVT = 1; | |
| 543 else | |
| 544 isRVT = 0; | |
| 545 sel = &comp->steps[0]; /* store extra in first step arbitrarily */ | |
| 546 | |
| 547 prevdoc = (xmlDocPtr) | |
| 548 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); | |
| 549 ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); | |
| 550 list = (xmlXPathObjectPtr) | |
| 551 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra); | |
| 552 | |
| 553 if ((list == NULL) || (prevdoc != doc)) { | |
| 554 xmlXPathObjectPtr newlist; | |
| 555 xmlNodePtr parent = node->parent; | |
| 556 xmlDocPtr olddoc; | |
| 557 xmlNodePtr oldnode; | |
| 558 int oldNsNr, oldContextSize, oldProximityPosition; | |
| 559 xmlNsPtr *oldNamespaces; | |
| 560 | |
| 561 oldnode = ctxt->xpathCtxt->node; | |
| 562 olddoc = ctxt->xpathCtxt->doc; | |
| 563 oldNsNr = ctxt->xpathCtxt->nsNr; | |
| 564 oldNamespaces = ctxt->xpathCtxt->namespaces; | |
| 565 oldContextSize = ctxt->xpathCtxt->contextSize; | |
| 566 oldProximityPosition = ctxt->xpathCtxt->proximityPosition; | |
| 567 ctxt->xpathCtxt->node = node; | |
| 568 ctxt->xpathCtxt->doc = doc; | |
| 569 ctxt->xpathCtxt->namespaces = nsList; | |
| 570 ctxt->xpathCtxt->nsNr = nsNr; | |
| 571 newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt); | |
| 572 ctxt->xpathCtxt->node = oldnode; | |
| 573 ctxt->xpathCtxt->doc = olddoc; | |
| 574 ctxt->xpathCtxt->namespaces = oldNamespaces; | |
| 575 ctxt->xpathCtxt->nsNr = oldNsNr; | |
| 576 ctxt->xpathCtxt->contextSize = oldContextSize; | |
| 577 ctxt->xpathCtxt->proximityPosition = oldProximityPosition; | |
| 578 if (newlist == NULL) | |
| 579 return(-1); | |
| 580 if (newlist->type != XPATH_NODESET) { | |
| 581 xmlXPathFreeObject(newlist); | |
| 582 return(-1); | |
| 583 } | |
| 584 ix = 0; | |
| 585 | |
| 586 if ((parent == NULL) || (node->doc == NULL) || isRVT) | |
| 587 nocache = 1; | |
| 588 | |
| 589 if (nocache == 0) { | |
| 590 if (list != NULL) | |
| 591 xmlXPathFreeObject(list); | |
| 592 list = newlist; | |
| 593 | |
| 594 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) = | |
| 595 (void *) list; | |
| 596 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = | |
| 597 (void *) doc; | |
| 598 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = | |
| 599 0; | |
| 600 XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) = | |
| 601 (xmlFreeFunc) xmlXPathFreeObject; | |
| 602 } else | |
| 603 list = newlist; | |
| 604 } | |
| 605 if ((list->nodesetval == NULL) || | |
| 606 (list->nodesetval->nodeNr <= 0)) { | |
| 607 if (nocache == 1) | |
| 608 xmlXPathFreeObject(list); | |
| 609 return(0); | |
| 610 } | |
| 611 /* TODO: store the index and use it for the scan */ | |
| 612 if (ix == 0) { | |
| 613 for (j = 0;j < list->nodesetval->nodeNr;j++) { | |
| 614 if (list->nodesetval->nodeTab[j] == node) { | |
| 615 if (nocache == 1) | |
| 616 xmlXPathFreeObject(list); | |
| 617 return(1); | |
| 618 } | |
| 619 } | |
| 620 } else { | |
| 621 } | |
| 622 if (nocache == 1) | |
| 623 xmlXPathFreeObject(list); | |
| 624 return(0); | |
| 625 } | |
| 626 | |
| 627 /** | |
| 628 * xsltTestPredicateMatch: | |
| 629 * @ctxt: a XSLT process context | |
| 630 * @comp: the precompiled pattern | |
| 631 * @node: a node | |
| 632 * @step: the predicate step | |
| 633 * @sel: the previous step | |
| 634 * | |
| 635 * Test whether the node matches the predicate | |
| 636 * | |
| 637 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure | |
| 638 */ | |
| 639 static int | |
| 640 xsltTestPredicateMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, | |
| 641 xmlNodePtr node, xsltStepOpPtr step, | |
| 642 xsltStepOpPtr sel) { | |
| 643 xmlNodePtr oldNode; | |
| 644 xmlDocPtr doc; | |
| 645 int oldCS, oldCP; | |
| 646 int pos = 0, len = 0; | |
| 647 int isRVT; | |
| 648 int match; | |
| 649 | |
| 650 if (step->value == NULL) | |
| 651 return(0); | |
| 652 if (step->comp == NULL) | |
| 653 return(0); | |
| 654 | |
| 655 doc = node->doc; | |
| 656 if (XSLT_IS_RES_TREE_FRAG(doc)) | |
| 657 isRVT = 1; | |
| 658 else | |
| 659 isRVT = 0; | |
| 660 | |
| 661 /* | |
| 662 * Recompute contextSize and proximityPosition. | |
| 663 * | |
| 664 * TODO: Make this work for additional ops. Currently, only XSLT_OP_ELEM | |
| 665 * and XSLT_OP_ALL are supported. | |
| 666 */ | |
| 667 oldCS = ctxt->xpathCtxt->contextSize; | |
| 668 oldCP = ctxt->xpathCtxt->proximityPosition; | |
| 669 if ((sel != NULL) && | |
| 670 (sel->op == XSLT_OP_ELEM) && | |
| 671 (sel->value != NULL) && | |
| 672 (node->type == XML_ELEMENT_NODE) && | |
| 673 (node->parent != NULL)) { | |
| 674 xmlNodePtr previous; | |
| 675 int nocache = 0; | |
| 676 | |
| 677 previous = (xmlNodePtr) | |
| 678 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); | |
| 679 if ((previous != NULL) && | |
| 680 (previous->parent == node->parent)) { | |
| 681 /* | |
| 682 * just walk back to adjust the index | |
| 683 */ | |
| 684 int indx = 0; | |
| 685 xmlNodePtr sibling = node; | |
| 686 | |
| 687 while (sibling != NULL) { | |
| 688 if (sibling == previous) | |
| 689 break; | |
| 690 if ((sibling->type == XML_ELEMENT_NODE) && | |
| 691 (previous->name != NULL) && | |
| 692 (sibling->name != NULL) && | |
| 693 (previous->name[0] == sibling->name[0]) && | |
| 694 (xmlStrEqual(previous->name, sibling->name))) | |
| 695 { | |
| 696 if ((sel->value2 == NULL) || | |
| 697 ((sibling->ns != NULL) && | |
| 698 (xmlStrEqual(sel->value2, sibling->ns->href)))) | |
| 699 indx++; | |
| 700 } | |
| 701 sibling = sibling->prev; | |
| 702 } | |
| 703 if (sibling == NULL) { | |
| 704 /* hum going backward in document order ... */ | |
| 705 indx = 0; | |
| 706 sibling = node; | |
| 707 while (sibling != NULL) { | |
| 708 if (sibling == previous) | |
| 709 break; | |
| 710 if ((sibling->type == XML_ELEMENT_NODE) && | |
| 711 (previous->name != NULL) && | |
| 712 (sibling->name != NULL) && | |
| 713 (previous->name[0] == sibling->name[0]) && | |
| 714 (xmlStrEqual(previous->name, sibling->name))) | |
| 715 { | |
| 716 if ((sel->value2 == NULL) || | |
| 717 ((sibling->ns != NULL) && | |
| 718 (xmlStrEqual(sel->value2, | |
| 719 sibling->ns->href)))) | |
| 720 { | |
| 721 indx--; | |
| 722 } | |
| 723 } | |
| 724 sibling = sibling->next; | |
| 725 } | |
| 726 } | |
| 727 if (sibling != NULL) { | |
| 728 pos = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) + indx; | |
| 729 /* | |
| 730 * If the node is in a Value Tree we need to | |
| 731 * save len, but cannot cache the node! | |
| 732 * (bugs 153137 and 158840) | |
| 733 */ | |
| 734 if (node->doc != NULL) { | |
| 735 len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival); | |
| 736 if (!isRVT) { | |
| 737 XSLT_RUNTIME_EXTRA(ctxt, | |
| 738 sel->previousExtra, ptr) = node; | |
| 739 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; | |
| 740 } | |
| 741 } | |
| 742 } else | |
| 743 pos = 0; | |
| 744 } else { | |
| 745 /* | |
| 746 * recompute the index | |
| 747 */ | |
| 748 xmlNodePtr parent = node->parent; | |
| 749 xmlNodePtr siblings = NULL; | |
| 750 | |
| 751 if (parent) siblings = parent->children; | |
| 752 | |
| 753 while (siblings != NULL) { | |
| 754 if (siblings->type == XML_ELEMENT_NODE) { | |
| 755 if (siblings == node) { | |
| 756 len++; | |
| 757 pos = len; | |
| 758 } else if ((node->name != NULL) && | |
| 759 (siblings->name != NULL) && | |
| 760 (node->name[0] == siblings->name[0]) && | |
| 761 (xmlStrEqual(node->name, siblings->name))) { | |
| 762 if ((sel->value2 == NULL) || | |
| 763 ((siblings->ns != NULL) && | |
| 764 (xmlStrEqual(sel->value2, siblings->ns->href)))) | |
| 765 len++; | |
| 766 } | |
| 767 } | |
| 768 siblings = siblings->next; | |
| 769 } | |
| 770 if ((parent == NULL) || (node->doc == NULL)) | |
| 771 nocache = 1; | |
| 772 else { | |
| 773 while (parent->parent != NULL) | |
| 774 parent = parent->parent; | |
| 775 if (((parent->type != XML_DOCUMENT_NODE) && | |
| 776 (parent->type != XML_HTML_DOCUMENT_NODE)) || | |
| 777 (parent != (xmlNodePtr) node->doc)) | |
| 778 nocache = 1; | |
| 779 } | |
| 780 } | |
| 781 if (pos != 0) { | |
| 782 ctxt->xpathCtxt->contextSize = len; | |
| 783 ctxt->xpathCtxt->proximityPosition = pos; | |
| 784 /* | |
| 785 * If the node is in a Value Tree we cannot | |
| 786 * cache it ! | |
| 787 */ | |
| 788 if ((!isRVT) && (node->doc != NULL) && | |
| 789 (nocache == 0)) { | |
| 790 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; | |
| 791 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; | |
| 792 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len; | |
| 793 } | |
| 794 } | |
| 795 } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) && | |
| 796 (node->type == XML_ELEMENT_NODE)) { | |
| 797 xmlNodePtr previous; | |
| 798 int nocache = 0; | |
| 799 | |
| 800 previous = (xmlNodePtr) | |
| 801 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); | |
| 802 if ((previous != NULL) && | |
| 803 (previous->parent == node->parent)) { | |
| 804 /* | |
| 805 * just walk back to adjust the index | |
| 806 */ | |
| 807 int indx = 0; | |
| 808 xmlNodePtr sibling = node; | |
| 809 | |
| 810 while (sibling != NULL) { | |
| 811 if (sibling == previous) | |
| 812 break; | |
| 813 if (sibling->type == XML_ELEMENT_NODE) | |
| 814 indx++; | |
| 815 sibling = sibling->prev; | |
| 816 } | |
| 817 if (sibling == NULL) { | |
| 818 /* hum going backward in document order ... */ | |
| 819 indx = 0; | |
| 820 sibling = node; | |
| 821 while (sibling != NULL) { | |
| 822 if (sibling == previous) | |
| 823 break; | |
| 824 if (sibling->type == XML_ELEMENT_NODE) | |
| 825 indx--; | |
| 826 sibling = sibling->next; | |
| 827 } | |
| 828 } | |
| 829 if (sibling != NULL) { | |
| 830 pos = XSLT_RUNTIME_EXTRA(ctxt, | |
| 831 sel->indexExtra, ival) + indx; | |
| 832 /* | |
| 833 * If the node is in a Value Tree we cannot | |
| 834 * cache it ! | |
| 835 */ | |
| 836 if ((node->doc != NULL) && !isRVT) { | |
| 837 len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival); | |
| 838 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; | |
| 839 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; | |
| 840 } | |
| 841 } else | |
| 842 pos = 0; | |
| 843 } else { | |
| 844 /* | |
| 845 * recompute the index | |
| 846 */ | |
| 847 xmlNodePtr parent = node->parent; | |
| 848 xmlNodePtr siblings = NULL; | |
| 849 | |
| 850 if (parent) siblings = parent->children; | |
| 851 | |
| 852 while (siblings != NULL) { | |
| 853 if (siblings->type == XML_ELEMENT_NODE) { | |
| 854 len++; | |
| 855 if (siblings == node) { | |
| 856 pos = len; | |
| 857 } | |
| 858 } | |
| 859 siblings = siblings->next; | |
| 860 } | |
| 861 if ((parent == NULL) || (node->doc == NULL)) | |
| 862 nocache = 1; | |
| 863 else { | |
| 864 while (parent->parent != NULL) | |
| 865 parent = parent->parent; | |
| 866 if (((parent->type != XML_DOCUMENT_NODE) && | |
| 867 (parent->type != XML_HTML_DOCUMENT_NODE)) || | |
| 868 (parent != (xmlNodePtr) node->doc)) | |
| 869 nocache = 1; | |
| 870 } | |
| 871 } | |
| 872 if (pos != 0) { | |
| 873 ctxt->xpathCtxt->contextSize = len; | |
| 874 ctxt->xpathCtxt->proximityPosition = pos; | |
| 875 /* | |
| 876 * If the node is in a Value Tree we cannot | |
| 877 * cache it ! | |
| 878 */ | |
| 879 if ((node->doc != NULL) && (nocache == 0) && !isRVT) { | |
| 880 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; | |
| 881 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; | |
| 882 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len; | |
| 883 } | |
| 884 } | |
| 885 } | |
| 886 | |
| 887 oldNode = ctxt->node; | |
| 888 ctxt->node = node; | |
| 889 | |
| 890 match = xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, comp->nsNr); | |
| 891 | |
| 892 if (pos != 0) { | |
| 893 ctxt->xpathCtxt->contextSize = oldCS; | |
| 894 ctxt->xpathCtxt->proximityPosition = oldCP; | |
| 895 } | |
| 896 ctxt->node = oldNode; | |
| 897 | |
| 898 return match; | |
| 899 } | |
| 900 | |
| 901 /** | |
| 902 * xsltTestCompMatch: | |
| 903 * @ctxt: a XSLT process context | |
| 904 * @comp: the precompiled pattern | |
| 905 * @node: a node | |
| 906 * @mode: the mode name or NULL | |
| 907 * @modeURI: the mode URI or NULL | |
| 908 * | |
| 909 * Test whether the node matches the pattern | |
| 910 * | |
| 911 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure | |
| 912 */ | |
| 913 static int | |
| 914 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, | |
| 915 xmlNodePtr matchNode, const xmlChar *mode, | |
| 916 const xmlChar *modeURI) { | |
| 917 int i; | |
| 918 int found = 0; | |
| 919 xmlNodePtr node = matchNode; | |
| 920 xmlNodePtr oldInst; | |
| 921 xsltStepOpPtr step, sel = NULL; | |
| 922 xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */ | |
| 923 | |
| 924 if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) { | |
| 925 xsltTransformError(ctxt, NULL, node, | |
| 926 "xsltTestCompMatch: null arg\n"); | |
| 927 return(-1); | |
| 928 } | |
| 929 if (mode != NULL) { | |
| 930 if (comp->mode == NULL) | |
| 931 return(0); | |
| 932 /* | |
| 933 * both mode strings must be interned on the stylesheet dictionary | |
| 934 */ | |
| 935 if (comp->mode != mode) | |
| 936 return(0); | |
| 937 } else { | |
| 938 if (comp->mode != NULL) | |
| 939 return(0); | |
| 940 } | |
| 941 if (modeURI != NULL) { | |
| 942 if (comp->modeURI == NULL) | |
| 943 return(0); | |
| 944 /* | |
| 945 * both modeURI strings must be interned on the stylesheet dictionary | |
| 946 */ | |
| 947 if (comp->modeURI != modeURI) | |
| 948 return(0); | |
| 949 } else { | |
| 950 if (comp->modeURI != NULL) | |
| 951 return(0); | |
| 952 } | |
| 953 | |
| 954 /* Some XPath functions rely on inst being set correctly. */ | |
| 955 oldInst = ctxt->inst; | |
| 956 ctxt->inst = comp->node; | |
| 957 | |
| 958 i = 0; | |
| 959 restart: | |
| 960 for (;i < comp->nbStep;i++) { | |
| 961 step = &comp->steps[i]; | |
| 962 if (step->op != XSLT_OP_PREDICATE) | |
| 963 sel = step; | |
| 964 switch (step->op) { | |
| 965 case XSLT_OP_END: | |
| 966 goto found; | |
| 967 case XSLT_OP_ROOT: | |
| 968 if ((node->type == XML_DOCUMENT_NODE) || | |
| 969 #ifdef LIBXML_DOCB_ENABLED | |
| 970 (node->type == XML_DOCB_DOCUMENT_NODE) || | |
| 971 #endif | |
| 972 (node->type == XML_HTML_DOCUMENT_NODE)) | |
| 973 continue; | |
| 974 if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' ')) | |
| 975 continue; | |
| 976 goto rollback; | |
| 977 case XSLT_OP_ELEM: | |
| 978 if (node->type != XML_ELEMENT_NODE) | |
| 979 goto rollback; | |
| 980 if (step->value == NULL) | |
| 981 continue; | |
| 982 if (step->value[0] != node->name[0]) | |
| 983 goto rollback; | |
| 984 if (!xmlStrEqual(step->value, node->name)) | |
| 985 goto rollback; | |
| 986 | |
| 987 /* Namespace test */ | |
| 988 if (node->ns == NULL) { | |
| 989 if (step->value2 != NULL) | |
| 990 goto rollback; | |
| 991 } else if (node->ns->href != NULL) { | |
| 992 if (step->value2 == NULL) | |
| 993 goto rollback; | |
| 994 if (!xmlStrEqual(step->value2, node->ns->href)) | |
| 995 goto rollback; | |
| 996 } | |
| 997 continue; | |
| 998 case XSLT_OP_ATTR: | |
| 999 if (node->type != XML_ATTRIBUTE_NODE) | |
| 1000 goto rollback; | |
| 1001 if (step->value != NULL) { | |
| 1002 if (step->value[0] != node->name[0]) | |
| 1003 goto rollback; | |
| 1004 if (!xmlStrEqual(step->value, node->name)) | |
| 1005 goto rollback; | |
| 1006 } | |
| 1007 /* Namespace test */ | |
| 1008 if (node->ns == NULL) { | |
| 1009 if (step->value2 != NULL) | |
| 1010 goto rollback; | |
| 1011 } else if (step->value2 != NULL) { | |
| 1012 if (!xmlStrEqual(step->value2, node->ns->href)) | |
| 1013 goto rollback; | |
| 1014 } | |
| 1015 continue; | |
| 1016 case XSLT_OP_PARENT: | |
| 1017 if ((node->type == XML_DOCUMENT_NODE) || | |
| 1018 (node->type == XML_HTML_DOCUMENT_NODE) || | |
| 1019 #ifdef LIBXML_DOCB_ENABLED | |
| 1020 (node->type == XML_DOCB_DOCUMENT_NODE) || | |
| 1021 #endif | |
| 1022 (node->type == XML_NAMESPACE_DECL)) | |
| 1023 goto rollback; | |
| 1024 node = node->parent; | |
| 1025 if (node == NULL) | |
| 1026 goto rollback; | |
| 1027 if (step->value == NULL) | |
| 1028 continue; | |
| 1029 if (step->value[0] != node->name[0]) | |
| 1030 goto rollback; | |
| 1031 if (!xmlStrEqual(step->value, node->name)) | |
| 1032 goto rollback; | |
| 1033 /* Namespace test */ | |
| 1034 if (node->ns == NULL) { | |
| 1035 if (step->value2 != NULL) | |
| 1036 goto rollback; | |
| 1037 } else if (node->ns->href != NULL) { | |
| 1038 if (step->value2 == NULL) | |
| 1039 goto rollback; | |
| 1040 if (!xmlStrEqual(step->value2, node->ns->href)) | |
| 1041 goto rollback; | |
| 1042 } | |
| 1043 continue; | |
| 1044 case XSLT_OP_ANCESTOR: | |
| 1045 /* TODO: implement coalescing of ANCESTOR/NODE ops */ | |
| 1046 if (step->value == NULL) { | |
| 1047 step = &comp->steps[i+1]; | |
| 1048 if (step->op == XSLT_OP_ROOT) | |
| 1049 goto found; | |
| 1050 /* added NS, ID and KEY as a result of bug 168208 */ | |
| 1051 if ((step->op != XSLT_OP_ELEM) && | |
| 1052 (step->op != XSLT_OP_ALL) && | |
| 1053 (step->op != XSLT_OP_NS) && | |
| 1054 (step->op != XSLT_OP_ID) && | |
| 1055 (step->op != XSLT_OP_KEY)) | |
| 1056 goto rollback; | |
| 1057 } | |
| 1058 if (node == NULL) | |
| 1059 goto rollback; | |
| 1060 if ((node->type == XML_DOCUMENT_NODE) || | |
| 1061 (node->type == XML_HTML_DOCUMENT_NODE) || | |
| 1062 #ifdef LIBXML_DOCB_ENABLED | |
| 1063 (node->type == XML_DOCB_DOCUMENT_NODE) || | |
| 1064 #endif | |
| 1065 (node->type == XML_NAMESPACE_DECL)) | |
| 1066 goto rollback; | |
| 1067 node = node->parent; | |
| 1068 if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) { | |
| 1069 xsltPatPushState(ctxt, &states, i, node); | |
| 1070 continue; | |
| 1071 } | |
| 1072 i++; | |
| 1073 if (step->value == NULL) { | |
| 1074 xsltPatPushState(ctxt, &states, i - 1, node); | |
| 1075 continue; | |
| 1076 } | |
| 1077 while (node != NULL) { | |
| 1078 if ((node->type == XML_ELEMENT_NODE) && | |
| 1079 (step->value[0] == node->name[0]) && | |
| 1080 (xmlStrEqual(step->value, node->name))) { | |
| 1081 /* Namespace test */ | |
| 1082 if (node->ns == NULL) { | |
| 1083 if (step->value2 == NULL) | |
| 1084 break; | |
| 1085 } else if (node->ns->href != NULL) { | |
| 1086 if ((step->value2 != NULL) && | |
| 1087 (xmlStrEqual(step->value2, node->ns->href))) | |
| 1088 break; | |
| 1089 } | |
| 1090 } | |
| 1091 node = node->parent; | |
| 1092 } | |
| 1093 if (node == NULL) | |
| 1094 goto rollback; | |
| 1095 xsltPatPushState(ctxt, &states, i - 1, node); | |
| 1096 continue; | |
| 1097 case XSLT_OP_ID: { | |
| 1098 /* TODO Handle IDs decently, must be done differently */ | |
| 1099 xmlAttrPtr id; | |
| 1100 | |
| 1101 if (node->type != XML_ELEMENT_NODE) | |
| 1102 goto rollback; | |
| 1103 | |
| 1104 id = xmlGetID(node->doc, step->value); | |
| 1105 if ((id == NULL) || (id->parent != node)) | |
| 1106 goto rollback; | |
| 1107 break; | |
| 1108 } | |
| 1109 case XSLT_OP_KEY: { | |
| 1110 xmlNodeSetPtr list; | |
| 1111 int indx; | |
| 1112 | |
| 1113 list = xsltGetKey(ctxt, step->value, | |
| 1114 step->value3, step->value2); | |
| 1115 if (list == NULL) | |
| 1116 goto rollback; | |
| 1117 for (indx = 0;indx < list->nodeNr;indx++) | |
| 1118 if (list->nodeTab[indx] == node) | |
| 1119 break; | |
| 1120 if (indx >= list->nodeNr) | |
| 1121 goto rollback; | |
| 1122 break; | |
| 1123 } | |
| 1124 case XSLT_OP_NS: | |
| 1125 if (node->type != XML_ELEMENT_NODE) | |
| 1126 goto rollback; | |
| 1127 if (node->ns == NULL) { | |
| 1128 if (step->value != NULL) | |
| 1129 goto rollback; | |
| 1130 } else if (node->ns->href != NULL) { | |
| 1131 if (step->value == NULL) | |
| 1132 goto rollback; | |
| 1133 if (!xmlStrEqual(step->value, node->ns->href)) | |
| 1134 goto rollback; | |
| 1135 } | |
| 1136 break; | |
| 1137 case XSLT_OP_ALL: | |
| 1138 if (node->type != XML_ELEMENT_NODE) | |
| 1139 goto rollback; | |
| 1140 break; | |
| 1141 case XSLT_OP_PREDICATE: { | |
| 1142 /* | |
| 1143 * When there is cascading XSLT_OP_PREDICATE or a predicate | |
| 1144 * after an op which hasn't been optimized yet, then use a | |
| 1145 * direct computation approach. It's not done directly | |
| 1146 * at the beginning of the routine to filter out as much | |
| 1147 * as possible this costly computation. | |
| 1148 */ | |
| 1149 if (comp->direct) { | |
| 1150 found = xsltTestCompMatchDirect(ctxt, comp, matchNode, | |
| 1151 comp->nsList, comp->nsNr); | |
| 1152 goto exit; | |
| 1153 } | |
| 1154 | |
| 1155 if (!xsltTestPredicateMatch(ctxt, comp, node, step, sel)) | |
| 1156 goto rollback; | |
| 1157 | |
| 1158 break; | |
| 1159 } | |
| 1160 case XSLT_OP_PI: | |
| 1161 if (node->type != XML_PI_NODE) | |
| 1162 goto rollback; | |
| 1163 if (step->value != NULL) { | |
| 1164 if (!xmlStrEqual(step->value, node->name)) | |
| 1165 goto rollback; | |
| 1166 } | |
| 1167 break; | |
| 1168 case XSLT_OP_COMMENT: | |
| 1169 if (node->type != XML_COMMENT_NODE) | |
| 1170 goto rollback; | |
| 1171 break; | |
| 1172 case XSLT_OP_TEXT: | |
| 1173 if ((node->type != XML_TEXT_NODE) && | |
| 1174 (node->type != XML_CDATA_SECTION_NODE)) | |
| 1175 goto rollback; | |
| 1176 break; | |
| 1177 case XSLT_OP_NODE: | |
| 1178 switch (node->type) { | |
| 1179 case XML_ELEMENT_NODE: | |
| 1180 case XML_CDATA_SECTION_NODE: | |
| 1181 case XML_PI_NODE: | |
| 1182 case XML_COMMENT_NODE: | |
| 1183 case XML_TEXT_NODE: | |
| 1184 break; | |
| 1185 default: | |
| 1186 goto rollback; | |
| 1187 } | |
| 1188 break; | |
| 1189 } | |
| 1190 } | |
| 1191 found: | |
| 1192 found = 1; | |
| 1193 exit: | |
| 1194 ctxt->inst = oldInst; | |
| 1195 if (states.states != NULL) { | |
| 1196 /* Free the rollback states */ | |
| 1197 xmlFree(states.states); | |
| 1198 } | |
| 1199 return found; | |
| 1200 rollback: | |
| 1201 /* got an error try to rollback */ | |
| 1202 if (states.states == NULL || states.nbstates <= 0) { | |
| 1203 found = 0; | |
| 1204 goto exit; | |
| 1205 } | |
| 1206 states.nbstates--; | |
| 1207 i = states.states[states.nbstates].step; | |
| 1208 node = states.states[states.nbstates].node; | |
| 1209 #if 0 | |
| 1210 fprintf(stderr, "Pop: %d, %s\n", i, node->name); | |
| 1211 #endif | |
| 1212 goto restart; | |
| 1213 } | |
| 1214 | |
| 1215 /** | |
| 1216 * xsltTestCompMatchList: | |
| 1217 * @ctxt: a XSLT process context | |
| 1218 * @node: a node | |
| 1219 * @comp: the precompiled pattern list | |
| 1220 * | |
| 1221 * Test whether the node matches one of the patterns in the list | |
| 1222 * | |
| 1223 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure | |
| 1224 */ | |
| 1225 int | |
| 1226 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 1227 xsltCompMatchPtr comp) { | |
| 1228 int ret; | |
| 1229 | |
| 1230 if ((ctxt == NULL) || (node == NULL)) | |
| 1231 return(-1); | |
| 1232 while (comp != NULL) { | |
| 1233 ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL); | |
| 1234 if (ret == 1) | |
| 1235 return(1); | |
| 1236 comp = comp->next; | |
| 1237 } | |
| 1238 return(0); | |
| 1239 } | |
| 1240 | |
| 1241 /************************************************************************ | |
| 1242 * * | |
| 1243 * Dedicated parser for templates * | |
| 1244 * * | |
| 1245 ************************************************************************/ | |
| 1246 | |
| 1247 #define CUR (*ctxt->cur) | |
| 1248 #define SKIP(val) ctxt->cur += (val) | |
| 1249 #define NXT(val) ctxt->cur[(val)] | |
| 1250 #define CUR_PTR ctxt->cur | |
| 1251 | |
| 1252 #define SKIP_BLANKS \ | |
| 1253 while (IS_BLANK_CH(CUR)) NEXT | |
| 1254 | |
| 1255 #define CURRENT (*ctxt->cur) | |
| 1256 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) | |
| 1257 | |
| 1258 | |
| 1259 #define PUSH(op, val, val2, novar)
\ | |
| 1260 if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto e
rror; | |
| 1261 | |
| 1262 #define SWAP() \ | |
| 1263 xsltSwapTopCompMatch(ctxt->comp); | |
| 1264 | |
| 1265 #define XSLT_ERROR(X) \ | |
| 1266 { xsltError(ctxt, __FILE__, __LINE__, X); \ | |
| 1267 ctxt->error = (X); return; } | |
| 1268 | |
| 1269 #define XSLT_ERROR0(X) \ | |
| 1270 { xsltError(ctxt, __FILE__, __LINE__, X); \ | |
| 1271 ctxt->error = (X); return(0); } | |
| 1272 | |
| 1273 /** | |
| 1274 * xsltScanLiteral: | |
| 1275 * @ctxt: the XPath Parser context | |
| 1276 * | |
| 1277 * Parse an XPath Litteral: | |
| 1278 * | |
| 1279 * [29] Literal ::= '"' [^"]* '"' | |
| 1280 * | "'" [^']* "'" | |
| 1281 * | |
| 1282 * Returns the Literal parsed or NULL | |
| 1283 */ | |
| 1284 | |
| 1285 static xmlChar * | |
| 1286 xsltScanLiteral(xsltParserContextPtr ctxt) { | |
| 1287 const xmlChar *q, *cur; | |
| 1288 xmlChar *ret = NULL; | |
| 1289 int val, len; | |
| 1290 | |
| 1291 SKIP_BLANKS; | |
| 1292 if (CUR == '"') { | |
| 1293 NEXT; | |
| 1294 cur = q = CUR_PTR; | |
| 1295 val = xmlStringCurrentChar(NULL, cur, &len); | |
| 1296 while ((IS_CHAR(val)) && (val != '"')) { | |
| 1297 cur += len; | |
| 1298 val = xmlStringCurrentChar(NULL, cur, &len); | |
| 1299 } | |
| 1300 if (!IS_CHAR(val)) { | |
| 1301 ctxt->error = 1; | |
| 1302 return(NULL); | |
| 1303 } else { | |
| 1304 ret = xmlStrndup(q, cur - q); | |
| 1305 } | |
| 1306 cur += len; | |
| 1307 CUR_PTR = cur; | |
| 1308 } else if (CUR == '\'') { | |
| 1309 NEXT; | |
| 1310 cur = q = CUR_PTR; | |
| 1311 val = xmlStringCurrentChar(NULL, cur, &len); | |
| 1312 while ((IS_CHAR(val)) && (val != '\'')) { | |
| 1313 cur += len; | |
| 1314 val = xmlStringCurrentChar(NULL, cur, &len); | |
| 1315 } | |
| 1316 if (!IS_CHAR(val)) { | |
| 1317 ctxt->error = 1; | |
| 1318 return(NULL); | |
| 1319 } else { | |
| 1320 ret = xmlStrndup(q, cur - q); | |
| 1321 } | |
| 1322 cur += len; | |
| 1323 CUR_PTR = cur; | |
| 1324 } else { | |
| 1325 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */ | |
| 1326 ctxt->error = 1; | |
| 1327 return(NULL); | |
| 1328 } | |
| 1329 return(ret); | |
| 1330 } | |
| 1331 | |
| 1332 /** | |
| 1333 * xsltScanNCName: | |
| 1334 * @ctxt: the XPath Parser context | |
| 1335 * | |
| 1336 * Parses a non qualified name | |
| 1337 * | |
| 1338 * Returns the Name parsed or NULL | |
| 1339 */ | |
| 1340 | |
| 1341 static xmlChar * | |
| 1342 xsltScanNCName(xsltParserContextPtr ctxt) { | |
| 1343 const xmlChar *q, *cur; | |
| 1344 xmlChar *ret = NULL; | |
| 1345 int val, len; | |
| 1346 | |
| 1347 SKIP_BLANKS; | |
| 1348 | |
| 1349 cur = q = CUR_PTR; | |
| 1350 val = xmlStringCurrentChar(NULL, cur, &len); | |
| 1351 if (!IS_LETTER(val) && (val != '_')) | |
| 1352 return(NULL); | |
| 1353 | |
| 1354 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || | |
| 1355 (val == '.') || (val == '-') || | |
| 1356 (val == '_') || | |
| 1357 (IS_COMBINING(val)) || | |
| 1358 (IS_EXTENDER(val))) { | |
| 1359 cur += len; | |
| 1360 val = xmlStringCurrentChar(NULL, cur, &len); | |
| 1361 } | |
| 1362 ret = xmlStrndup(q, cur - q); | |
| 1363 CUR_PTR = cur; | |
| 1364 return(ret); | |
| 1365 } | |
| 1366 | |
| 1367 /* | |
| 1368 * xsltCompileIdKeyPattern: | |
| 1369 * @ctxt: the compilation context | |
| 1370 * @name: a preparsed name | |
| 1371 * @aid: whether id/key are allowed there | |
| 1372 * @novar: flag to prohibit xslt var | |
| 1373 * | |
| 1374 * Compile the XSLT LocationIdKeyPattern | |
| 1375 * [3] IdKeyPattern ::= 'id' '(' Literal ')' | |
| 1376 * | 'key' '(' Literal ',' Literal ')' | |
| 1377 * | |
| 1378 * also handle NodeType and PI from: | |
| 1379 * | |
| 1380 * [7] NodeTest ::= NameTest | |
| 1381 * | NodeType '(' ')' | |
| 1382 * | 'processing-instruction' '(' Literal ')' | |
| 1383 */ | |
| 1384 static void | |
| 1385 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, | |
| 1386 int aid, int novar, xsltAxis axis) { | |
| 1387 xmlChar *lit = NULL; | |
| 1388 xmlChar *lit2 = NULL; | |
| 1389 | |
| 1390 if (CUR != '(') { | |
| 1391 xsltTransformError(NULL, NULL, NULL, | |
| 1392 "xsltCompileIdKeyPattern : ( expected\n"); | |
| 1393 ctxt->error = 1; | |
| 1394 return; | |
| 1395 } | |
| 1396 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) { | |
| 1397 if (axis != 0) { | |
| 1398 xsltTransformError(NULL, NULL, NULL, | |
| 1399 "xsltCompileIdKeyPattern : NodeTest expected\n"); | |
| 1400 ctxt->error = 1; | |
| 1401 return; | |
| 1402 } | |
| 1403 NEXT; | |
| 1404 SKIP_BLANKS; | |
| 1405 lit = xsltScanLiteral(ctxt); | |
| 1406 if (ctxt->error) { | |
| 1407 xsltTransformError(NULL, NULL, NULL, | |
| 1408 "xsltCompileIdKeyPattern : Literal expected\n"); | |
| 1409 return; | |
| 1410 } | |
| 1411 SKIP_BLANKS; | |
| 1412 if (CUR != ')') { | |
| 1413 xsltTransformError(NULL, NULL, NULL, | |
| 1414 "xsltCompileIdKeyPattern : ) expected\n"); | |
| 1415 xmlFree(lit); | |
| 1416 ctxt->error = 1; | |
| 1417 return; | |
| 1418 } | |
| 1419 NEXT; | |
| 1420 PUSH(XSLT_OP_ID, lit, NULL, novar); | |
| 1421 lit = NULL; | |
| 1422 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) { | |
| 1423 if (axis != 0) { | |
| 1424 xsltTransformError(NULL, NULL, NULL, | |
| 1425 "xsltCompileIdKeyPattern : NodeTest expected\n"); | |
| 1426 ctxt->error = 1; | |
| 1427 return; | |
| 1428 } | |
| 1429 NEXT; | |
| 1430 SKIP_BLANKS; | |
| 1431 lit = xsltScanLiteral(ctxt); | |
| 1432 if (ctxt->error) { | |
| 1433 xsltTransformError(NULL, NULL, NULL, | |
| 1434 "xsltCompileIdKeyPattern : Literal expected\n"); | |
| 1435 return; | |
| 1436 } | |
| 1437 SKIP_BLANKS; | |
| 1438 if (CUR != ',') { | |
| 1439 xsltTransformError(NULL, NULL, NULL, | |
| 1440 "xsltCompileIdKeyPattern : , expected\n"); | |
| 1441 xmlFree(lit); | |
| 1442 ctxt->error = 1; | |
| 1443 return; | |
| 1444 } | |
| 1445 NEXT; | |
| 1446 SKIP_BLANKS; | |
| 1447 lit2 = xsltScanLiteral(ctxt); | |
| 1448 if (ctxt->error) { | |
| 1449 xsltTransformError(NULL, NULL, NULL, | |
| 1450 "xsltCompileIdKeyPattern : Literal expected\n"); | |
| 1451 xmlFree(lit); | |
| 1452 return; | |
| 1453 } | |
| 1454 SKIP_BLANKS; | |
| 1455 if (CUR != ')') { | |
| 1456 xsltTransformError(NULL, NULL, NULL, | |
| 1457 "xsltCompileIdKeyPattern : ) expected\n"); | |
| 1458 xmlFree(lit); | |
| 1459 xmlFree(lit2); | |
| 1460 ctxt->error = 1; | |
| 1461 return; | |
| 1462 } | |
| 1463 NEXT; | |
| 1464 /* URGENT TODO: support namespace in keys */ | |
| 1465 PUSH(XSLT_OP_KEY, lit, lit2, novar); | |
| 1466 lit = NULL; | |
| 1467 lit2 = NULL; | |
| 1468 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) { | |
| 1469 NEXT; | |
| 1470 SKIP_BLANKS; | |
| 1471 if (CUR != ')') { | |
| 1472 lit = xsltScanLiteral(ctxt); | |
| 1473 if (ctxt->error) { | |
| 1474 xsltTransformError(NULL, NULL, NULL, | |
| 1475 "xsltCompileIdKeyPattern : Literal expected\n"); | |
| 1476 return; | |
| 1477 } | |
| 1478 SKIP_BLANKS; | |
| 1479 if (CUR != ')') { | |
| 1480 xsltTransformError(NULL, NULL, NULL, | |
| 1481 "xsltCompileIdKeyPattern : ) expected\n"); | |
| 1482 ctxt->error = 1; | |
| 1483 return; | |
| 1484 } | |
| 1485 } | |
| 1486 NEXT; | |
| 1487 PUSH(XSLT_OP_PI, lit, NULL, novar); | |
| 1488 lit = NULL; | |
| 1489 } else if (xmlStrEqual(name, (const xmlChar *)"text")) { | |
| 1490 NEXT; | |
| 1491 SKIP_BLANKS; | |
| 1492 if (CUR != ')') { | |
| 1493 xsltTransformError(NULL, NULL, NULL, | |
| 1494 "xsltCompileIdKeyPattern : ) expected\n"); | |
| 1495 ctxt->error = 1; | |
| 1496 return; | |
| 1497 } | |
| 1498 NEXT; | |
| 1499 PUSH(XSLT_OP_TEXT, NULL, NULL, novar); | |
| 1500 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) { | |
| 1501 NEXT; | |
| 1502 SKIP_BLANKS; | |
| 1503 if (CUR != ')') { | |
| 1504 xsltTransformError(NULL, NULL, NULL, | |
| 1505 "xsltCompileIdKeyPattern : ) expected\n"); | |
| 1506 ctxt->error = 1; | |
| 1507 return; | |
| 1508 } | |
| 1509 NEXT; | |
| 1510 PUSH(XSLT_OP_COMMENT, NULL, NULL, novar); | |
| 1511 } else if (xmlStrEqual(name, (const xmlChar *)"node")) { | |
| 1512 NEXT; | |
| 1513 SKIP_BLANKS; | |
| 1514 if (CUR != ')') { | |
| 1515 xsltTransformError(NULL, NULL, NULL, | |
| 1516 "xsltCompileIdKeyPattern : ) expected\n"); | |
| 1517 ctxt->error = 1; | |
| 1518 return; | |
| 1519 } | |
| 1520 NEXT; | |
| 1521 if (axis == AXIS_ATTRIBUTE) { | |
| 1522 PUSH(XSLT_OP_ATTR, NULL, NULL, novar); | |
| 1523 } | |
| 1524 else { | |
| 1525 PUSH(XSLT_OP_NODE, NULL, NULL, novar); | |
| 1526 } | |
| 1527 } else if (aid) { | |
| 1528 xsltTransformError(NULL, NULL, NULL, | |
| 1529 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n"); | |
| 1530 ctxt->error = 1; | |
| 1531 return; | |
| 1532 } else { | |
| 1533 xsltTransformError(NULL, NULL, NULL, | |
| 1534 "xsltCompileIdKeyPattern : node type\n"); | |
| 1535 ctxt->error = 1; | |
| 1536 return; | |
| 1537 } | |
| 1538 error: | |
| 1539 return; | |
| 1540 } | |
| 1541 | |
| 1542 /** | |
| 1543 * xsltCompileStepPattern: | |
| 1544 * @ctxt: the compilation context | |
| 1545 * @token: a posible precompiled name | |
| 1546 * @novar: flag to prohibit xslt variables from pattern | |
| 1547 * | |
| 1548 * Compile the XSLT StepPattern and generates a precompiled | |
| 1549 * form suitable for fast matching. | |
| 1550 * | |
| 1551 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* | |
| 1552 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier | |
| 1553 * | ('child' | 'attribute') '::' | |
| 1554 * from XPath | |
| 1555 * [7] NodeTest ::= NameTest | |
| 1556 * | NodeType '(' ')' | |
| 1557 * | 'processing-instruction' '(' Literal ')' | |
| 1558 * [8] Predicate ::= '[' PredicateExpr ']' | |
| 1559 * [9] PredicateExpr ::= Expr | |
| 1560 * [13] AbbreviatedAxisSpecifier ::= '@'? | |
| 1561 * [37] NameTest ::= '*' | NCName ':' '*' | QName | |
| 1562 */ | |
| 1563 | |
| 1564 static void | |
| 1565 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { | |
| 1566 xmlChar *name = NULL; | |
| 1567 const xmlChar *URI = NULL; | |
| 1568 xmlChar *URL = NULL; | |
| 1569 int level; | |
| 1570 xsltAxis axis = 0; | |
| 1571 | |
| 1572 SKIP_BLANKS; | |
| 1573 if ((token == NULL) && (CUR == '@')) { | |
| 1574 NEXT; | |
| 1575 axis = AXIS_ATTRIBUTE; | |
| 1576 } | |
| 1577 parse_node_test: | |
| 1578 if (token == NULL) | |
| 1579 token = xsltScanNCName(ctxt); | |
| 1580 if (token == NULL) { | |
| 1581 if (CUR == '*') { | |
| 1582 NEXT; | |
| 1583 if (axis == AXIS_ATTRIBUTE) { | |
| 1584 PUSH(XSLT_OP_ATTR, NULL, NULL, novar); | |
| 1585 } | |
| 1586 else { | |
| 1587 PUSH(XSLT_OP_ALL, NULL, NULL, novar); | |
| 1588 } | |
| 1589 goto parse_predicate; | |
| 1590 } else { | |
| 1591 xsltTransformError(NULL, NULL, NULL, | |
| 1592 "xsltCompileStepPattern : Name expected\n"); | |
| 1593 ctxt->error = 1; | |
| 1594 goto error; | |
| 1595 } | |
| 1596 } | |
| 1597 | |
| 1598 | |
| 1599 SKIP_BLANKS; | |
| 1600 if (CUR == '(') { | |
| 1601 xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis); | |
| 1602 xmlFree(token); | |
| 1603 token = NULL; | |
| 1604 if (ctxt->error) | |
| 1605 goto error; | |
| 1606 } else if (CUR == ':') { | |
| 1607 NEXT; | |
| 1608 if (CUR != ':') { | |
| 1609 xmlChar *prefix = token; | |
| 1610 xmlNsPtr ns; | |
| 1611 | |
| 1612 /* | |
| 1613 * This is a namespace match | |
| 1614 */ | |
| 1615 token = xsltScanNCName(ctxt); | |
| 1616 ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix); | |
| 1617 if (ns == NULL) { | |
| 1618 xsltTransformError(NULL, NULL, NULL, | |
| 1619 "xsltCompileStepPattern : no namespace bound to prefix %s\n", | |
| 1620 prefix); | |
| 1621 xmlFree(prefix); | |
| 1622 prefix=NULL; | |
| 1623 ctxt->error = 1; | |
| 1624 goto error; | |
| 1625 } else { | |
| 1626 URL = xmlStrdup(ns->href); | |
| 1627 } | |
| 1628 xmlFree(prefix); | |
| 1629 prefix=NULL; | |
| 1630 if (token == NULL) { | |
| 1631 if (CUR == '*') { | |
| 1632 NEXT; | |
| 1633 if (axis == AXIS_ATTRIBUTE) { | |
| 1634 PUSH(XSLT_OP_ATTR, NULL, URL, novar); | |
| 1635 URL = NULL; | |
| 1636 } | |
| 1637 else { | |
| 1638 PUSH(XSLT_OP_NS, URL, NULL, novar); | |
| 1639 URL = NULL; | |
| 1640 } | |
| 1641 } else { | |
| 1642 xsltTransformError(NULL, NULL, NULL, | |
| 1643 "xsltCompileStepPattern : Name expected\n"); | |
| 1644 ctxt->error = 1; | |
| 1645 goto error; | |
| 1646 } | |
| 1647 } else { | |
| 1648 if (axis == AXIS_ATTRIBUTE) { | |
| 1649 PUSH(XSLT_OP_ATTR, token, URL, novar); | |
| 1650 token = NULL; | |
| 1651 URL = NULL; | |
| 1652 } | |
| 1653 else { | |
| 1654 PUSH(XSLT_OP_ELEM, token, URL, novar); | |
| 1655 token = NULL; | |
| 1656 URL = NULL; | |
| 1657 } | |
| 1658 } | |
| 1659 } else { | |
| 1660 if (axis != 0) { | |
| 1661 xsltTransformError(NULL, NULL, NULL, | |
| 1662 "xsltCompileStepPattern : NodeTest expected\n"); | |
| 1663 ctxt->error = 1; | |
| 1664 goto error; | |
| 1665 } | |
| 1666 NEXT; | |
| 1667 if (xmlStrEqual(token, (const xmlChar *) "child")) { | |
| 1668 axis = AXIS_CHILD; | |
| 1669 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) { | |
| 1670 axis = AXIS_ATTRIBUTE; | |
| 1671 } else { | |
| 1672 xsltTransformError(NULL, NULL, NULL, | |
| 1673 "xsltCompileStepPattern : 'child' or 'attribute' expected\n"
); | |
| 1674 ctxt->error = 1; | |
| 1675 goto error; | |
| 1676 } | |
| 1677 xmlFree(token); | |
| 1678 token = NULL; | |
| 1679 SKIP_BLANKS; | |
| 1680 token = xsltScanNCName(ctxt); | |
| 1681 goto parse_node_test; | |
| 1682 } | |
| 1683 } else { | |
| 1684 URI = xsltGetQNameURI(ctxt->elem, &token); | |
| 1685 if (token == NULL) { | |
| 1686 ctxt->error = 1; | |
| 1687 goto error; | |
| 1688 } | |
| 1689 if (URI != NULL) | |
| 1690 URL = xmlStrdup(URI); | |
| 1691 if (axis == AXIS_ATTRIBUTE) { | |
| 1692 PUSH(XSLT_OP_ATTR, token, URL, novar); | |
| 1693 token = NULL; | |
| 1694 URL = NULL; | |
| 1695 } | |
| 1696 else { | |
| 1697 PUSH(XSLT_OP_ELEM, token, URL, novar); | |
| 1698 token = NULL; | |
| 1699 URL = NULL; | |
| 1700 } | |
| 1701 } | |
| 1702 parse_predicate: | |
| 1703 SKIP_BLANKS; | |
| 1704 level = 0; | |
| 1705 while (CUR == '[') { | |
| 1706 const xmlChar *q; | |
| 1707 xmlChar *ret = NULL; | |
| 1708 | |
| 1709 level++; | |
| 1710 NEXT; | |
| 1711 q = CUR_PTR; | |
| 1712 while (CUR != 0) { | |
| 1713 /* Skip over nested predicates */ | |
| 1714 if (CUR == '[') | |
| 1715 level++; | |
| 1716 else if (CUR == ']') { | |
| 1717 level--; | |
| 1718 if (level == 0) | |
| 1719 break; | |
| 1720 } else if (CUR == '"') { | |
| 1721 NEXT; | |
| 1722 while ((CUR != 0) && (CUR != '"')) | |
| 1723 NEXT; | |
| 1724 } else if (CUR == '\'') { | |
| 1725 NEXT; | |
| 1726 while ((CUR != 0) && (CUR != '\'')) | |
| 1727 NEXT; | |
| 1728 } | |
| 1729 NEXT; | |
| 1730 } | |
| 1731 if (CUR == 0) { | |
| 1732 xsltTransformError(NULL, NULL, NULL, | |
| 1733 "xsltCompileStepPattern : ']' expected\n"); | |
| 1734 ctxt->error = 1; | |
| 1735 return; | |
| 1736 } | |
| 1737 ret = xmlStrndup(q, CUR_PTR - q); | |
| 1738 PUSH(XSLT_OP_PREDICATE, ret, NULL, novar); | |
| 1739 ret = NULL; | |
| 1740 /* push the predicate lower than local test */ | |
| 1741 SWAP(); | |
| 1742 NEXT; | |
| 1743 SKIP_BLANKS; | |
| 1744 } | |
| 1745 return; | |
| 1746 error: | |
| 1747 if (token != NULL) | |
| 1748 xmlFree(token); | |
| 1749 if (name != NULL) | |
| 1750 xmlFree(name); | |
| 1751 } | |
| 1752 | |
| 1753 /** | |
| 1754 * xsltCompileRelativePathPattern: | |
| 1755 * @comp: the compilation context | |
| 1756 * @token: a posible precompiled name | |
| 1757 * @novar: flag to prohibit xslt variables | |
| 1758 * | |
| 1759 * Compile the XSLT RelativePathPattern and generates a precompiled | |
| 1760 * form suitable for fast matching. | |
| 1761 * | |
| 1762 * [4] RelativePathPattern ::= StepPattern | |
| 1763 * | RelativePathPattern '/' StepPattern | |
| 1764 * | RelativePathPattern '//' StepPattern | |
| 1765 */ | |
| 1766 static void | |
| 1767 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int no
var) { | |
| 1768 xsltCompileStepPattern(ctxt, token, novar); | |
| 1769 if (ctxt->error) | |
| 1770 goto error; | |
| 1771 SKIP_BLANKS; | |
| 1772 while ((CUR != 0) && (CUR != '|')) { | |
| 1773 if ((CUR == '/') && (NXT(1) == '/')) { | |
| 1774 PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); | |
| 1775 NEXT; | |
| 1776 NEXT; | |
| 1777 SKIP_BLANKS; | |
| 1778 xsltCompileStepPattern(ctxt, NULL, novar); | |
| 1779 } else if (CUR == '/') { | |
| 1780 PUSH(XSLT_OP_PARENT, NULL, NULL, novar); | |
| 1781 NEXT; | |
| 1782 SKIP_BLANKS; | |
| 1783 if ((CUR != 0) && (CUR != '|')) { | |
| 1784 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
| 1785 } | |
| 1786 } else { | |
| 1787 ctxt->error = 1; | |
| 1788 } | |
| 1789 if (ctxt->error) | |
| 1790 goto error; | |
| 1791 SKIP_BLANKS; | |
| 1792 } | |
| 1793 error: | |
| 1794 return; | |
| 1795 } | |
| 1796 | |
| 1797 /** | |
| 1798 * xsltCompileLocationPathPattern: | |
| 1799 * @ctxt: the compilation context | |
| 1800 * @novar: flag to prohibit xslt variables | |
| 1801 * | |
| 1802 * Compile the XSLT LocationPathPattern and generates a precompiled | |
| 1803 * form suitable for fast matching. | |
| 1804 * | |
| 1805 * [2] LocationPathPattern ::= '/' RelativePathPattern? | |
| 1806 * | IdKeyPattern (('/' | '//') RelativePathPattern)? | |
| 1807 * | '//'? RelativePathPattern | |
| 1808 */ | |
| 1809 static void | |
| 1810 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) { | |
| 1811 SKIP_BLANKS; | |
| 1812 if ((CUR == '/') && (NXT(1) == '/')) { | |
| 1813 /* | |
| 1814 * since we reverse the query | |
| 1815 * a leading // can be safely ignored | |
| 1816 */ | |
| 1817 NEXT; | |
| 1818 NEXT; | |
| 1819 ctxt->comp->priority = 0.5; /* '//' means not 0 priority */ | |
| 1820 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
| 1821 } else if (CUR == '/') { | |
| 1822 /* | |
| 1823 * We need to find root as the parent | |
| 1824 */ | |
| 1825 NEXT; | |
| 1826 SKIP_BLANKS; | |
| 1827 PUSH(XSLT_OP_ROOT, NULL, NULL, novar); | |
| 1828 if ((CUR != 0) && (CUR != '|')) { | |
| 1829 PUSH(XSLT_OP_PARENT, NULL, NULL, novar); | |
| 1830 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
| 1831 } | |
| 1832 } else if (CUR == '*') { | |
| 1833 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
| 1834 } else if (CUR == '@') { | |
| 1835 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
| 1836 } else { | |
| 1837 xmlChar *name; | |
| 1838 name = xsltScanNCName(ctxt); | |
| 1839 if (name == NULL) { | |
| 1840 xsltTransformError(NULL, NULL, NULL, | |
| 1841 "xsltCompileLocationPathPattern : Name expected\n"); | |
| 1842 ctxt->error = 1; | |
| 1843 return; | |
| 1844 } | |
| 1845 SKIP_BLANKS; | |
| 1846 if ((CUR == '(') && !xmlXPathIsNodeType(name)) { | |
| 1847 xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0); | |
| 1848 xmlFree(name); | |
| 1849 name = NULL; | |
| 1850 if ((CUR == '/') && (NXT(1) == '/')) { | |
| 1851 PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); | |
| 1852 NEXT; | |
| 1853 NEXT; | |
| 1854 SKIP_BLANKS; | |
| 1855 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
| 1856 } else if (CUR == '/') { | |
| 1857 PUSH(XSLT_OP_PARENT, NULL, NULL, novar); | |
| 1858 NEXT; | |
| 1859 SKIP_BLANKS; | |
| 1860 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
| 1861 } | |
| 1862 return; | |
| 1863 } | |
| 1864 xsltCompileRelativePathPattern(ctxt, name, novar); | |
| 1865 } | |
| 1866 error: | |
| 1867 return; | |
| 1868 } | |
| 1869 | |
| 1870 /** | |
| 1871 * xsltCompilePatternInternal: | |
| 1872 * @pattern: an XSLT pattern | |
| 1873 * @doc: the containing document | |
| 1874 * @node: the containing element | |
| 1875 * @style: the stylesheet | |
| 1876 * @runtime: the transformation context, if done at run-time | |
| 1877 * @novar: flag to prohibit xslt variables | |
| 1878 * | |
| 1879 * Compile the XSLT pattern and generates a list of precompiled form suitable | |
| 1880 * for fast matching. | |
| 1881 * | |
| 1882 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern | |
| 1883 * | |
| 1884 * Returns the generated pattern list or NULL in case of failure | |
| 1885 */ | |
| 1886 | |
| 1887 static xsltCompMatchPtr | |
| 1888 xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc, | |
| 1889 xmlNodePtr node, xsltStylesheetPtr style, | |
| 1890 xsltTransformContextPtr runtime, int novar) { | |
| 1891 xsltParserContextPtr ctxt = NULL; | |
| 1892 xsltCompMatchPtr element, first = NULL, previous = NULL; | |
| 1893 int current, start, end, level, j; | |
| 1894 | |
| 1895 if (pattern == NULL) { | |
| 1896 xsltTransformError(NULL, NULL, node, | |
| 1897 "xsltCompilePattern : NULL pattern\n"); | |
| 1898 return(NULL); | |
| 1899 } | |
| 1900 | |
| 1901 ctxt = xsltNewParserContext(style, runtime); | |
| 1902 if (ctxt == NULL) | |
| 1903 return(NULL); | |
| 1904 ctxt->doc = doc; | |
| 1905 ctxt->elem = node; | |
| 1906 current = end = 0; | |
| 1907 while (pattern[current] != 0) { | |
| 1908 start = current; | |
| 1909 while (IS_BLANK_CH(pattern[current])) | |
| 1910 current++; | |
| 1911 end = current; | |
| 1912 level = 0; | |
| 1913 while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) { | |
| 1914 if (pattern[end] == '[') | |
| 1915 level++; | |
| 1916 else if (pattern[end] == ']') | |
| 1917 level--; | |
| 1918 else if (pattern[end] == '\'') { | |
| 1919 end++; | |
| 1920 while ((pattern[end] != 0) && (pattern[end] != '\'')) | |
| 1921 end++; | |
| 1922 } else if (pattern[end] == '"') { | |
| 1923 end++; | |
| 1924 while ((pattern[end] != 0) && (pattern[end] != '"')) | |
| 1925 end++; | |
| 1926 } | |
| 1927 if (pattern[end] == 0) | |
| 1928 break; | |
| 1929 end++; | |
| 1930 } | |
| 1931 if (current == end) { | |
| 1932 xsltTransformError(NULL, NULL, node, | |
| 1933 "xsltCompilePattern : NULL pattern\n"); | |
| 1934 goto error; | |
| 1935 } | |
| 1936 element = xsltNewCompMatch(); | |
| 1937 if (element == NULL) { | |
| 1938 goto error; | |
| 1939 } | |
| 1940 if (first == NULL) | |
| 1941 first = element; | |
| 1942 else if (previous != NULL) | |
| 1943 previous->next = element; | |
| 1944 previous = element; | |
| 1945 | |
| 1946 ctxt->comp = element; | |
| 1947 ctxt->base = xmlStrndup(&pattern[start], end - start); | |
| 1948 if (ctxt->base == NULL) | |
| 1949 goto error; | |
| 1950 ctxt->cur = &(ctxt->base)[current - start]; | |
| 1951 element->pattern = ctxt->base; | |
| 1952 element->node = node; | |
| 1953 element->nsList = xmlGetNsList(doc, node); | |
| 1954 j = 0; | |
| 1955 if (element->nsList != NULL) { | |
| 1956 while (element->nsList[j] != NULL) | |
| 1957 j++; | |
| 1958 } | |
| 1959 element->nsNr = j; | |
| 1960 | |
| 1961 | |
| 1962 #ifdef WITH_XSLT_DEBUG_PATTERN | |
| 1963 xsltGenericDebug(xsltGenericDebugContext, | |
| 1964 "xsltCompilePattern : parsing '%s'\n", | |
| 1965 element->pattern); | |
| 1966 #endif | |
| 1967 /* | |
| 1968 Preset default priority to be zero. | |
| 1969 This may be changed by xsltCompileLocationPathPattern. | |
| 1970 */ | |
| 1971 element->priority = 0; | |
| 1972 xsltCompileLocationPathPattern(ctxt, novar); | |
| 1973 if (ctxt->error) { | |
| 1974 xsltTransformError(NULL, style, node, | |
| 1975 "xsltCompilePattern : failed to compile '%s'\n", | |
| 1976 element->pattern); | |
| 1977 if (style != NULL) style->errors++; | |
| 1978 goto error; | |
| 1979 } | |
| 1980 | |
| 1981 /* | |
| 1982 * Reverse for faster interpretation. | |
| 1983 */ | |
| 1984 xsltReverseCompMatch(ctxt, element); | |
| 1985 | |
| 1986 /* | |
| 1987 * Set-up the priority | |
| 1988 */ | |
| 1989 if (element->priority == 0) { /* if not yet determined */ | |
| 1990 if (((element->steps[0].op == XSLT_OP_ELEM) || | |
| 1991 (element->steps[0].op == XSLT_OP_ATTR) || | |
| 1992 (element->steps[0].op == XSLT_OP_PI)) && | |
| 1993 (element->steps[0].value != NULL) && | |
| 1994 (element->steps[1].op == XSLT_OP_END)) { | |
| 1995 ; /* previously preset */ | |
| 1996 } else if ((element->steps[0].op == XSLT_OP_ATTR) && | |
| 1997 (element->steps[0].value2 != NULL) && | |
| 1998 (element->steps[1].op == XSLT_OP_END)) { | |
| 1999 element->priority = -0.25; | |
| 2000 } else if ((element->steps[0].op == XSLT_OP_NS) && | |
| 2001 (element->steps[0].value != NULL) && | |
| 2002 (element->steps[1].op == XSLT_OP_END)) { | |
| 2003 element->priority = -0.25; | |
| 2004 } else if ((element->steps[0].op == XSLT_OP_ATTR) && | |
| 2005 (element->steps[0].value == NULL) && | |
| 2006 (element->steps[0].value2 == NULL) && | |
| 2007 (element->steps[1].op == XSLT_OP_END)) { | |
| 2008 element->priority = -0.5; | |
| 2009 } else if (((element->steps[0].op == XSLT_OP_PI) || | |
| 2010 (element->steps[0].op == XSLT_OP_TEXT) || | |
| 2011 (element->steps[0].op == XSLT_OP_ALL) || | |
| 2012 (element->steps[0].op == XSLT_OP_NODE) || | |
| 2013 (element->steps[0].op == XSLT_OP_COMMENT)) && | |
| 2014 (element->steps[1].op == XSLT_OP_END)) { | |
| 2015 element->priority = -0.5; | |
| 2016 } else { | |
| 2017 element->priority = 0.5; | |
| 2018 } | |
| 2019 } | |
| 2020 #ifdef WITH_XSLT_DEBUG_PATTERN | |
| 2021 xsltGenericDebug(xsltGenericDebugContext, | |
| 2022 "xsltCompilePattern : parsed %s, default priority %f\n", | |
| 2023 element->pattern, element->priority); | |
| 2024 #endif | |
| 2025 if (pattern[end] == '|') | |
| 2026 end++; | |
| 2027 current = end; | |
| 2028 } | |
| 2029 if (end == 0) { | |
| 2030 xsltTransformError(NULL, style, node, | |
| 2031 "xsltCompilePattern : NULL pattern\n"); | |
| 2032 if (style != NULL) style->errors++; | |
| 2033 goto error; | |
| 2034 } | |
| 2035 | |
| 2036 xsltFreeParserContext(ctxt); | |
| 2037 return(first); | |
| 2038 | |
| 2039 error: | |
| 2040 if (ctxt != NULL) | |
| 2041 xsltFreeParserContext(ctxt); | |
| 2042 if (first != NULL) | |
| 2043 xsltFreeCompMatchList(first); | |
| 2044 return(NULL); | |
| 2045 } | |
| 2046 | |
| 2047 /** | |
| 2048 * xsltCompilePattern: | |
| 2049 * @pattern: an XSLT pattern | |
| 2050 * @doc: the containing document | |
| 2051 * @node: the containing element | |
| 2052 * @style: the stylesheet | |
| 2053 * @runtime: the transformation context, if done at run-time | |
| 2054 * | |
| 2055 * Compile the XSLT pattern and generates a list of precompiled form suitable | |
| 2056 * for fast matching. | |
| 2057 * | |
| 2058 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern | |
| 2059 * | |
| 2060 * Returns the generated pattern list or NULL in case of failure | |
| 2061 */ | |
| 2062 | |
| 2063 xsltCompMatchPtr | |
| 2064 xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc, | |
| 2065 xmlNodePtr node, xsltStylesheetPtr style, | |
| 2066 xsltTransformContextPtr runtime) { | |
| 2067 return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0)); | |
| 2068 } | |
| 2069 | |
| 2070 /************************************************************************ | |
| 2071 * * | |
| 2072 * Module interfaces * | |
| 2073 * * | |
| 2074 ************************************************************************/ | |
| 2075 | |
| 2076 /** | |
| 2077 * xsltAddTemplate: | |
| 2078 * @style: an XSLT stylesheet | |
| 2079 * @cur: an XSLT template | |
| 2080 * @mode: the mode name or NULL | |
| 2081 * @modeURI: the mode URI or NULL | |
| 2082 * | |
| 2083 * Register the XSLT pattern associated to @cur | |
| 2084 * | |
| 2085 * Returns -1 in case of error, 0 otherwise | |
| 2086 */ | |
| 2087 int | |
| 2088 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur, | |
| 2089 const xmlChar *mode, const xmlChar *modeURI) { | |
| 2090 xsltCompMatchPtr pat, list, next; | |
| 2091 /* | |
| 2092 * 'top' will point to style->xxxMatch ptr - declaring as 'void' | |
| 2093 * avoids gcc 'type-punned pointer' warning. | |
| 2094 */ | |
| 2095 void **top = NULL; | |
| 2096 const xmlChar *name = NULL; | |
| 2097 float priority; /* the priority */ | |
| 2098 | |
| 2099 if ((style == NULL) || (cur == NULL)) | |
| 2100 return(-1); | |
| 2101 | |
| 2102 /* Register named template */ | |
| 2103 if (cur->name != NULL) { | |
| 2104 if (style->namedTemplates == NULL) { | |
| 2105 style->namedTemplates = xmlHashCreate(10); | |
| 2106 if (style->namedTemplates == NULL) | |
| 2107 return(-1); | |
| 2108 } | |
| 2109 else { | |
| 2110 void *dup = xmlHashLookup2(style->namedTemplates, cur->name, | |
| 2111 cur->nameURI); | |
| 2112 if (dup != NULL) { | |
| 2113 xsltTransformError(NULL, style, cur->elem, | |
| 2114 "xsl:template: error duplicate name '%s'\n", | |
| 2115 cur->name); | |
| 2116 style->errors++; | |
| 2117 return(-1); | |
| 2118 } | |
| 2119 } | |
| 2120 | |
| 2121 xmlHashAddEntry2(style->namedTemplates, cur->name, cur->nameURI, cur); | |
| 2122 } | |
| 2123 | |
| 2124 if (cur->match == NULL) | |
| 2125 return(0); | |
| 2126 | |
| 2127 priority = cur->priority; | |
| 2128 pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem, | |
| 2129 style, NULL, 1); | |
| 2130 if (pat == NULL) | |
| 2131 return(-1); | |
| 2132 while (pat) { | |
| 2133 next = pat->next; | |
| 2134 pat->next = NULL; | |
| 2135 name = NULL; | |
| 2136 | |
| 2137 pat->template = cur; | |
| 2138 if (mode != NULL) | |
| 2139 pat->mode = xmlDictLookup(style->dict, mode, -1); | |
| 2140 if (modeURI != NULL) | |
| 2141 pat->modeURI = xmlDictLookup(style->dict, modeURI, -1); | |
| 2142 if (priority != XSLT_PAT_NO_PRIORITY) | |
| 2143 pat->priority = priority; | |
| 2144 | |
| 2145 /* | |
| 2146 * insert it in the hash table list corresponding to its lookup name | |
| 2147 */ | |
| 2148 switch (pat->steps[0].op) { | |
| 2149 case XSLT_OP_ATTR: | |
| 2150 if (pat->steps[0].value != NULL) | |
| 2151 name = pat->steps[0].value; | |
| 2152 else | |
| 2153 top = &(style->attrMatch); | |
| 2154 break; | |
| 2155 case XSLT_OP_PARENT: | |
| 2156 case XSLT_OP_ANCESTOR: | |
| 2157 top = &(style->elemMatch); | |
| 2158 break; | |
| 2159 case XSLT_OP_ROOT: | |
| 2160 top = &(style->rootMatch); | |
| 2161 break; | |
| 2162 case XSLT_OP_KEY: | |
| 2163 top = &(style->keyMatch); | |
| 2164 break; | |
| 2165 case XSLT_OP_ID: | |
| 2166 /* TODO optimize ID !!! */ | |
| 2167 case XSLT_OP_NS: | |
| 2168 case XSLT_OP_ALL: | |
| 2169 top = &(style->elemMatch); | |
| 2170 break; | |
| 2171 case XSLT_OP_END: | |
| 2172 case XSLT_OP_PREDICATE: | |
| 2173 xsltTransformError(NULL, style, NULL, | |
| 2174 "xsltAddTemplate: invalid compiled pattern\n"); | |
| 2175 xsltFreeCompMatch(pat); | |
| 2176 return(-1); | |
| 2177 /* | |
| 2178 * TODO: some flags at the top level about type based patterns | |
| 2179 * would be faster than inclusion in the hash table. | |
| 2180 */ | |
| 2181 case XSLT_OP_PI: | |
| 2182 if (pat->steps[0].value != NULL) | |
| 2183 name = pat->steps[0].value; | |
| 2184 else | |
| 2185 top = &(style->piMatch); | |
| 2186 break; | |
| 2187 case XSLT_OP_COMMENT: | |
| 2188 top = &(style->commentMatch); | |
| 2189 break; | |
| 2190 case XSLT_OP_TEXT: | |
| 2191 top = &(style->textMatch); | |
| 2192 break; | |
| 2193 case XSLT_OP_ELEM: | |
| 2194 case XSLT_OP_NODE: | |
| 2195 if (pat->steps[0].value != NULL) | |
| 2196 name = pat->steps[0].value; | |
| 2197 else | |
| 2198 top = &(style->elemMatch); | |
| 2199 break; | |
| 2200 } | |
| 2201 if (name != NULL) { | |
| 2202 if (style->templatesHash == NULL) { | |
| 2203 style->templatesHash = xmlHashCreate(1024); | |
| 2204 if (style->templatesHash == NULL) { | |
| 2205 xsltFreeCompMatch(pat); | |
| 2206 return(-1); | |
| 2207 } | |
| 2208 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat)
; | |
| 2209 } else { | |
| 2210 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash, | |
| 2211 name, mode, modeURI); | |
| 2212 if (list == NULL) { | |
| 2213 xmlHashAddEntry3(style->templatesHash, name, | |
| 2214 mode, modeURI, pat); | |
| 2215 } else { | |
| 2216 /* | |
| 2217 * Note '<=' since one must choose among the matching | |
| 2218 * template rules that are left, the one that occurs | |
| 2219 * last in the stylesheet | |
| 2220 */ | |
| 2221 if (list->priority <= pat->priority) { | |
| 2222 pat->next = list; | |
| 2223 xmlHashUpdateEntry3(style->templatesHash, name, | |
| 2224 mode, modeURI, pat, NULL); | |
| 2225 } else { | |
| 2226 while (list->next != NULL) { | |
| 2227 if (list->next->priority <= pat->priority) | |
| 2228 break; | |
| 2229 list = list->next; | |
| 2230 } | |
| 2231 pat->next = list->next; | |
| 2232 list->next = pat; | |
| 2233 } | |
| 2234 } | |
| 2235 } | |
| 2236 } else if (top != NULL) { | |
| 2237 list = *top; | |
| 2238 if (list == NULL) { | |
| 2239 *top = pat; | |
| 2240 pat->next = NULL; | |
| 2241 } else if (list->priority <= pat->priority) { | |
| 2242 pat->next = list; | |
| 2243 *top = pat; | |
| 2244 } else { | |
| 2245 while (list->next != NULL) { | |
| 2246 if (list->next->priority <= pat->priority) | |
| 2247 break; | |
| 2248 list = list->next; | |
| 2249 } | |
| 2250 pat->next = list->next; | |
| 2251 list->next = pat; | |
| 2252 } | |
| 2253 } else { | |
| 2254 xsltTransformError(NULL, style, NULL, | |
| 2255 "xsltAddTemplate: invalid compiled pattern\n"); | |
| 2256 xsltFreeCompMatch(pat); | |
| 2257 return(-1); | |
| 2258 } | |
| 2259 #ifdef WITH_XSLT_DEBUG_PATTERN | |
| 2260 if (mode) | |
| 2261 xsltGenericDebug(xsltGenericDebugContext, | |
| 2262 "added pattern : '%s' mode '%s' priority %f\n", | |
| 2263 pat->pattern, pat->mode, pat->priority); | |
| 2264 else | |
| 2265 xsltGenericDebug(xsltGenericDebugContext, | |
| 2266 "added pattern : '%s' priority %f\n", | |
| 2267 pat->pattern, pat->priority); | |
| 2268 #endif | |
| 2269 | |
| 2270 pat = next; | |
| 2271 } | |
| 2272 return(0); | |
| 2273 } | |
| 2274 | |
| 2275 static int | |
| 2276 xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode) | |
| 2277 { | |
| 2278 if ((ctxt == NULL) || (contextNode == NULL)) { | |
| 2279 xsltTransformError(ctxt, NULL, ctxt->inst, | |
| 2280 "Internal error in xsltComputeAllKeys(): " | |
| 2281 "Bad arguments.\n"); | |
| 2282 return(-1); | |
| 2283 } | |
| 2284 | |
| 2285 if (ctxt->document == NULL) { | |
| 2286 /* | |
| 2287 * The document info will only be NULL if we have a RTF. | |
| 2288 */ | |
| 2289 if (contextNode->doc->_private != NULL) | |
| 2290 goto doc_info_mismatch; | |
| 2291 /* | |
| 2292 * On-demand creation of the document info (needed for keys). | |
| 2293 */ | |
| 2294 ctxt->document = xsltNewDocument(ctxt, contextNode->doc); | |
| 2295 if (ctxt->document == NULL) | |
| 2296 return(-1); | |
| 2297 } | |
| 2298 return xsltInitAllDocKeys(ctxt); | |
| 2299 | |
| 2300 doc_info_mismatch: | |
| 2301 xsltTransformError(ctxt, NULL, ctxt->inst, | |
| 2302 "Internal error in xsltComputeAllKeys(): " | |
| 2303 "The context's document info doesn't match the " | |
| 2304 "document info of the current result tree.\n"); | |
| 2305 ctxt->state = XSLT_STATE_STOPPED; | |
| 2306 return(-1); | |
| 2307 } | |
| 2308 | |
| 2309 /** | |
| 2310 * xsltGetTemplate: | |
| 2311 * @ctxt: a XSLT process context | |
| 2312 * @node: the node being processed | |
| 2313 * @style: the current style | |
| 2314 * | |
| 2315 * Finds the template applying to this node, if @style is non-NULL | |
| 2316 * it means one needs to look for the next imported template in scope. | |
| 2317 * | |
| 2318 * Returns the xsltTemplatePtr or NULL if not found | |
| 2319 */ | |
| 2320 xsltTemplatePtr | |
| 2321 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
| 2322 xsltStylesheetPtr style) | |
| 2323 { | |
| 2324 xsltStylesheetPtr curstyle; | |
| 2325 xsltTemplatePtr ret = NULL; | |
| 2326 const xmlChar *name = NULL; | |
| 2327 xsltCompMatchPtr list = NULL; | |
| 2328 float priority; | |
| 2329 int keyed = 0; | |
| 2330 | |
| 2331 if ((ctxt == NULL) || (node == NULL)) | |
| 2332 return(NULL); | |
| 2333 | |
| 2334 if (style == NULL) { | |
| 2335 curstyle = ctxt->style; | |
| 2336 } else { | |
| 2337 curstyle = xsltNextImport(style); | |
| 2338 } | |
| 2339 | |
| 2340 while ((curstyle != NULL) && (curstyle != style)) { | |
| 2341 priority = XSLT_PAT_NO_PRIORITY; | |
| 2342 /* TODO : handle IDs/keys here ! */ | |
| 2343 if (curstyle->templatesHash != NULL) { | |
| 2344 /* | |
| 2345 * Use the top name as selector | |
| 2346 */ | |
| 2347 switch (node->type) { | |
| 2348 case XML_ELEMENT_NODE: | |
| 2349 if (node->name[0] == ' ') | |
| 2350 break; | |
| 2351 case XML_ATTRIBUTE_NODE: | |
| 2352 case XML_PI_NODE: | |
| 2353 name = node->name; | |
| 2354 break; | |
| 2355 case XML_DOCUMENT_NODE: | |
| 2356 case XML_HTML_DOCUMENT_NODE: | |
| 2357 case XML_TEXT_NODE: | |
| 2358 case XML_CDATA_SECTION_NODE: | |
| 2359 case XML_COMMENT_NODE: | |
| 2360 case XML_ENTITY_REF_NODE: | |
| 2361 case XML_ENTITY_NODE: | |
| 2362 case XML_DOCUMENT_TYPE_NODE: | |
| 2363 case XML_DOCUMENT_FRAG_NODE: | |
| 2364 case XML_NOTATION_NODE: | |
| 2365 case XML_DTD_NODE: | |
| 2366 case XML_ELEMENT_DECL: | |
| 2367 case XML_ATTRIBUTE_DECL: | |
| 2368 case XML_ENTITY_DECL: | |
| 2369 case XML_NAMESPACE_DECL: | |
| 2370 case XML_XINCLUDE_START: | |
| 2371 case XML_XINCLUDE_END: | |
| 2372 break; | |
| 2373 default: | |
| 2374 return(NULL); | |
| 2375 | |
| 2376 } | |
| 2377 } | |
| 2378 if (name != NULL) { | |
| 2379 /* | |
| 2380 * find the list of applicable expressions based on the name | |
| 2381 */ | |
| 2382 list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash, | |
| 2383 name, ctxt->mode, ctxt->modeURI); | |
| 2384 } else | |
| 2385 list = NULL; | |
| 2386 while (list != NULL) { | |
| 2387 if (xsltTestCompMatch(ctxt, list, node, | |
| 2388 ctxt->mode, ctxt->modeURI)) { | |
| 2389 ret = list->template; | |
| 2390 priority = list->priority; | |
| 2391 break; | |
| 2392 } | |
| 2393 list = list->next; | |
| 2394 } | |
| 2395 list = NULL; | |
| 2396 | |
| 2397 /* | |
| 2398 * find alternate generic matches | |
| 2399 */ | |
| 2400 switch (node->type) { | |
| 2401 case XML_ELEMENT_NODE: | |
| 2402 if (node->name[0] == ' ') | |
| 2403 list = curstyle->rootMatch; | |
| 2404 else | |
| 2405 list = curstyle->elemMatch; | |
| 2406 if (node->psvi != NULL) keyed = 1; | |
| 2407 break; | |
| 2408 case XML_ATTRIBUTE_NODE: { | |
| 2409 xmlAttrPtr attr; | |
| 2410 | |
| 2411 list = curstyle->attrMatch; | |
| 2412 attr = (xmlAttrPtr) node; | |
| 2413 if (attr->psvi != NULL) keyed = 1; | |
| 2414 break; | |
| 2415 } | |
| 2416 case XML_PI_NODE: | |
| 2417 list = curstyle->piMatch; | |
| 2418 if (node->psvi != NULL) keyed = 1; | |
| 2419 break; | |
| 2420 case XML_DOCUMENT_NODE: | |
| 2421 case XML_HTML_DOCUMENT_NODE: { | |
| 2422 xmlDocPtr doc; | |
| 2423 | |
| 2424 list = curstyle->rootMatch; | |
| 2425 doc = (xmlDocPtr) node; | |
| 2426 if (doc->psvi != NULL) keyed = 1; | |
| 2427 break; | |
| 2428 } | |
| 2429 case XML_TEXT_NODE: | |
| 2430 case XML_CDATA_SECTION_NODE: | |
| 2431 list = curstyle->textMatch; | |
| 2432 if (node->psvi != NULL) keyed = 1; | |
| 2433 break; | |
| 2434 case XML_COMMENT_NODE: | |
| 2435 list = curstyle->commentMatch; | |
| 2436 if (node->psvi != NULL) keyed = 1; | |
| 2437 break; | |
| 2438 case XML_ENTITY_REF_NODE: | |
| 2439 case XML_ENTITY_NODE: | |
| 2440 case XML_DOCUMENT_TYPE_NODE: | |
| 2441 case XML_DOCUMENT_FRAG_NODE: | |
| 2442 case XML_NOTATION_NODE: | |
| 2443 case XML_DTD_NODE: | |
| 2444 case XML_ELEMENT_DECL: | |
| 2445 case XML_ATTRIBUTE_DECL: | |
| 2446 case XML_ENTITY_DECL: | |
| 2447 case XML_NAMESPACE_DECL: | |
| 2448 case XML_XINCLUDE_START: | |
| 2449 case XML_XINCLUDE_END: | |
| 2450 break; | |
| 2451 default: | |
| 2452 break; | |
| 2453 } | |
| 2454 while ((list != NULL) && | |
| 2455 ((ret == NULL) || (list->priority > priority))) { | |
| 2456 if (xsltTestCompMatch(ctxt, list, node, | |
| 2457 ctxt->mode, ctxt->modeURI)) { | |
| 2458 ret = list->template; | |
| 2459 priority = list->priority; | |
| 2460 break; | |
| 2461 } | |
| 2462 list = list->next; | |
| 2463 } | |
| 2464 /* | |
| 2465 * Some of the tests for elements can also apply to documents | |
| 2466 */ | |
| 2467 if ((node->type == XML_DOCUMENT_NODE) || | |
| 2468 (node->type == XML_HTML_DOCUMENT_NODE) || | |
| 2469 (node->type == XML_TEXT_NODE)) { | |
| 2470 list = curstyle->elemMatch; | |
| 2471 while ((list != NULL) && | |
| 2472 ((ret == NULL) || (list->priority > priority))) { | |
| 2473 if (xsltTestCompMatch(ctxt, list, node, | |
| 2474 ctxt->mode, ctxt->modeURI)) { | |
| 2475 ret = list->template; | |
| 2476 priority = list->priority; | |
| 2477 break; | |
| 2478 } | |
| 2479 list = list->next; | |
| 2480 } | |
| 2481 } else if ((node->type == XML_PI_NODE) || | |
| 2482 (node->type == XML_COMMENT_NODE)) { | |
| 2483 list = curstyle->elemMatch; | |
| 2484 while ((list != NULL) && | |
| 2485 ((ret == NULL) || (list->priority > priority))) { | |
| 2486 if (xsltTestCompMatch(ctxt, list, node, | |
| 2487 ctxt->mode, ctxt->modeURI)) { | |
| 2488 ret = list->template; | |
| 2489 priority = list->priority; | |
| 2490 break; | |
| 2491 } | |
| 2492 list = list->next; | |
| 2493 } | |
| 2494 } | |
| 2495 | |
| 2496 keyed_match: | |
| 2497 if (keyed) { | |
| 2498 list = curstyle->keyMatch; | |
| 2499 while ((list != NULL) && | |
| 2500 ((ret == NULL) || (list->priority > priority))) { | |
| 2501 if (xsltTestCompMatch(ctxt, list, node, | |
| 2502 ctxt->mode, ctxt->modeURI)) { | |
| 2503 ret = list->template; | |
| 2504 priority = list->priority; | |
| 2505 break; | |
| 2506 } | |
| 2507 list = list->next; | |
| 2508 } | |
| 2509 } | |
| 2510 else if (ctxt->hasTemplKeyPatterns && | |
| 2511 ((ctxt->document == NULL) || | |
| 2512 (ctxt->document->nbKeysComputed < ctxt->nbKeys))) | |
| 2513 { | |
| 2514 /* | |
| 2515 * Compute all remaining keys for this document. | |
| 2516 * | |
| 2517 * REVISIT TODO: I think this could be further optimized. | |
| 2518 */ | |
| 2519 if (xsltComputeAllKeys(ctxt, node) == -1) | |
| 2520 goto error; | |
| 2521 | |
| 2522 switch (node->type) { | |
| 2523 case XML_ELEMENT_NODE: | |
| 2524 if (node->psvi != NULL) keyed = 1; | |
| 2525 break; | |
| 2526 case XML_ATTRIBUTE_NODE: | |
| 2527 if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1; | |
| 2528 break; | |
| 2529 case XML_TEXT_NODE: | |
| 2530 case XML_CDATA_SECTION_NODE: | |
| 2531 case XML_COMMENT_NODE: | |
| 2532 case XML_PI_NODE: | |
| 2533 if (node->psvi != NULL) keyed = 1; | |
| 2534 break; | |
| 2535 case XML_DOCUMENT_NODE: | |
| 2536 case XML_HTML_DOCUMENT_NODE: | |
| 2537 if (((xmlDocPtr) node)->psvi != NULL) keyed = 1; | |
| 2538 break; | |
| 2539 default: | |
| 2540 break; | |
| 2541 } | |
| 2542 if (keyed) | |
| 2543 goto keyed_match; | |
| 2544 } | |
| 2545 if (ret != NULL) | |
| 2546 return(ret); | |
| 2547 | |
| 2548 /* | |
| 2549 * Cycle on next curstylesheet import. | |
| 2550 */ | |
| 2551 curstyle = xsltNextImport(curstyle); | |
| 2552 } | |
| 2553 | |
| 2554 error: | |
| 2555 return(NULL); | |
| 2556 } | |
| 2557 | |
| 2558 /** | |
| 2559 * xsltCleanupTemplates: | |
| 2560 * @style: an XSLT stylesheet | |
| 2561 * | |
| 2562 * Cleanup the state of the templates used by the stylesheet and | |
| 2563 * the ones it imports. | |
| 2564 */ | |
| 2565 void | |
| 2566 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) { | |
| 2567 } | |
| 2568 | |
| 2569 /** | |
| 2570 * xsltFreeTemplateHashes: | |
| 2571 * @style: an XSLT stylesheet | |
| 2572 * | |
| 2573 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism | |
| 2574 */ | |
| 2575 void | |
| 2576 xsltFreeTemplateHashes(xsltStylesheetPtr style) { | |
| 2577 if (style->templatesHash != NULL) | |
| 2578 xmlHashFree((xmlHashTablePtr) style->templatesHash, | |
| 2579 (xmlHashDeallocator) xsltFreeCompMatchList); | |
| 2580 if (style->rootMatch != NULL) | |
| 2581 xsltFreeCompMatchList(style->rootMatch); | |
| 2582 if (style->keyMatch != NULL) | |
| 2583 xsltFreeCompMatchList(style->keyMatch); | |
| 2584 if (style->elemMatch != NULL) | |
| 2585 xsltFreeCompMatchList(style->elemMatch); | |
| 2586 if (style->attrMatch != NULL) | |
| 2587 xsltFreeCompMatchList(style->attrMatch); | |
| 2588 if (style->parentMatch != NULL) | |
| 2589 xsltFreeCompMatchList(style->parentMatch); | |
| 2590 if (style->textMatch != NULL) | |
| 2591 xsltFreeCompMatchList(style->textMatch); | |
| 2592 if (style->piMatch != NULL) | |
| 2593 xsltFreeCompMatchList(style->piMatch); | |
| 2594 if (style->commentMatch != NULL) | |
| 2595 xsltFreeCompMatchList(style->commentMatch); | |
| 2596 if (style->namedTemplates != NULL) | |
| 2597 xmlHashFree(style->namedTemplates, NULL); | |
| 2598 } | |
| 2599 | |
| OLD | NEW |