preparing 1.0.14 updated rebuilt implemented the IN_LIBXSLT and
[platform/upstream/libxslt.git] / libxslt / variables.c
1 /*
2  * variables.c: Implementation of the variable storage 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 #define IN_LIBXSLT
13 #include "libxslt.h"
14
15 #include <string.h>
16
17 #include <libxml/xmlmemory.h>
18 #include <libxml/tree.h>
19 #include <libxml/valid.h>
20 #include <libxml/hash.h>
21 #include <libxml/xmlerror.h>
22 #include <libxml/xpath.h>
23 #include <libxml/xpathInternals.h>
24 #include <libxml/parserInternals.h>
25 #include "xslt.h"
26 #include "xsltInternals.h"
27 #include "xsltutils.h"
28 #include "variables.h"
29 #include "transform.h"
30 #include "imports.h"
31 #include "preproc.h"
32
33 #ifdef WITH_XSLT_DEBUG
34 #define WITH_XSLT_DEBUG_VARIABLE
35 #endif
36
37 /************************************************************************
38  *                                                                      *
39  *                      Module interfaces                               *
40  *                                                                      *
41  ************************************************************************/
42
43 /**
44  * xsltNewStackElem:
45  *
46  * Create a new XSLT ParserContext
47  *
48  * Returns the newly allocated xsltParserStackElem or NULL in case of error
49  */
50 static xsltStackElemPtr
51 xsltNewStackElem(void) {
52     xsltStackElemPtr cur;
53
54     cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
55     if (cur == NULL) {
56         xsltPrintErrorContext(NULL, NULL, NULL);
57         xsltGenericError(xsltGenericErrorContext,
58                 "xsltNewStackElem : malloc failed\n");
59         return(NULL);
60     }
61     cur->computed = 0;
62     cur->name = NULL;
63     cur->nameURI = NULL;
64     cur->select = NULL;
65     cur->tree = NULL;
66     cur->value = NULL;
67     cur->comp = NULL;
68     return(cur);
69 }
70
71 /**
72  * xsltCopyStackElem:
73  * @elem:  an XSLT stack element
74  *
75  * Makes a copy of the stack element
76  *
77  * Returns the copy of NULL
78  */
79 static xsltStackElemPtr
80 xsltCopyStackElem(xsltStackElemPtr elem) {
81     xsltStackElemPtr cur;
82
83     cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
84     if (cur == NULL) {
85         xsltPrintErrorContext(NULL, NULL, NULL);
86         xsltGenericError(xsltGenericErrorContext,
87                 "xsltCopyStackElem : malloc failed\n");
88         return(NULL);
89     }
90     cur->name = xmlStrdup(elem->name);
91     cur->nameURI = xmlStrdup(elem->nameURI);
92     cur->select = xmlStrdup(elem->select);
93     cur->tree = elem->tree;
94     cur->comp = elem->comp;
95     cur->computed = 0;
96     cur->value = NULL;
97     return(cur);
98 }
99
100 /**
101  * xsltFreeStackElem:
102  * @elem:  an XSLT stack element
103  *
104  * Free up the memory allocated by @elem
105  */
106 static void
107 xsltFreeStackElem(xsltStackElemPtr elem) {
108     if (elem == NULL)
109         return;
110     if (elem->name != NULL)
111         xmlFree(elem->name);
112     if (elem->nameURI != NULL)
113         xmlFree(elem->nameURI);
114     if (elem->select != NULL)
115         xmlFree(elem->select);
116     if (elem->value != NULL)
117         xmlXPathFreeObject(elem->value);
118
119     xmlFree(elem);
120 }
121
122 /**
123  * xsltFreeStackElemList:
124  * @elem:  an XSLT stack element
125  *
126  * Free up the memory allocated by @elem
127  */
128 void
129 xsltFreeStackElemList(xsltStackElemPtr elem) {
130     xsltStackElemPtr next;
131
132     while(elem != NULL) {
133         next = elem->next;
134         xsltFreeStackElem(elem);
135         elem = next;
136     }
137 }
138
139 /**
140  * xsltCheckStackElem:
141  * @ctxt:  xn XSLT transformation context
142  * @name:  the variable name
143  * @nameURI:  the variable namespace URI
144  *
145  * check wether the variable or param is already defined
146  *
147  * Returns 1 if present, 0 if not, -1 in case of failure.
148  */
149 static int
150 xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name,
151                    const xmlChar *nameURI) {
152     xsltStackElemPtr cur;
153
154     if ((ctxt == NULL) || (name == NULL))
155         return(-1);
156
157     cur = ctxt->vars;
158     while (cur != NULL) {
159         if (xmlStrEqual(name, cur->name)) {
160             if (((nameURI == NULL) && (cur->nameURI == NULL)) ||
161                 ((nameURI != NULL) && (cur->nameURI != NULL) &&
162                  (xmlStrEqual(nameURI, cur->nameURI)))) {
163                 return(1);
164             }
165         }
166         cur = cur->next;
167     }
168     return(0);
169 }
170
171 /**
172  * xsltAddStackElem:
173  * @ctxt:  xn XSLT transformation context
174  * @elem:  a stack element
175  *
176  * add a new element at this level of the stack.
177  *
178  * Returns 0 in case of success, -1 in case of failure.
179  */
180 static int
181 xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) {
182     if ((ctxt == NULL) || (elem == NULL))
183         return(-1);
184
185     elem->next = ctxt->varsTab[ctxt->varsNr - 1];
186     ctxt->varsTab[ctxt->varsNr - 1] = elem;
187     ctxt->vars = elem;
188     return(0);
189 }
190
191 /**
192  * xsltAddStackElemList:
193  * @ctxt:  xn XSLT transformation context
194  * @elems:  a stack element list
195  *
196  * add the new element list at this level of the stack.
197  *
198  * Returns 0 in case of success, -1 in case of failure.
199  */
200 int
201 xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems) {
202     xsltStackElemPtr cur;
203
204     if ((ctxt == NULL) || (elems == NULL))
205         return(-1);
206
207     /* TODO: check doublons */
208     if (ctxt->varsTab[ctxt->varsNr - 1] != NULL) {
209         cur = ctxt->varsTab[ctxt->varsNr - 1];
210         while (cur->next != NULL)
211             cur = cur->next;
212         cur->next = elems;
213     } else {
214         elems->next = ctxt->varsTab[ctxt->varsNr - 1];
215         ctxt->varsTab[ctxt->varsNr - 1] = elems;
216         ctxt->vars = elems;
217     }
218     return(0);
219 }
220
221 /**
222  * xsltStackLookup:
223  * @ctxt:  an XSLT transformation context
224  * @name:  the local part of the name
225  * @nameURI:  the URI part of the name
226  *
227  * Locate an element in the stack based on its name.
228  */
229 static xsltStackElemPtr
230 xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
231                 const xmlChar *nameURI) {
232     xsltStackElemPtr ret = NULL;
233     int i;
234     xsltStackElemPtr cur;
235
236     if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0))
237         return(NULL);
238
239     /*
240      * Do the lookup from the top of the stack, but
241      * don't use params being computed in a call-param
242      */
243     ;
244
245     for (i = ctxt->varsNr; i > ctxt->varsBase; i--) {
246         cur = ctxt->varsTab[i-1];
247         while (cur != NULL) {
248             if (xmlStrEqual(cur->name, name)) {
249                 if (nameURI == NULL) {
250                     if (cur->nameURI == NULL) {
251                         return(cur);
252                     }
253                 } else {
254                     if ((cur->nameURI != NULL) &&
255                         (xmlStrEqual(cur->nameURI, nameURI))) {
256                         return(cur);
257                     }
258                 }
259
260             }
261             cur = cur->next;
262         }
263     }
264     return(ret);
265 }
266
267 /************************************************************************
268  *                                                                      *
269  *                      Module interfaces                               *
270  *                                                                      *
271  ************************************************************************/
272
273 /**
274  * xsltEvalVariable:
275  * @ctxt:  the XSLT transformation context
276  * @elem:  the variable or parameter.
277  * @precomp: pointer to precompiled data
278  *
279  * Evaluate a variable value.
280  *
281  * Returns the XPath Object value or NULL in case of error
282  */
283 static xmlXPathObjectPtr
284 xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr elem,
285                  xsltStylePreCompPtr precomp) {
286     xmlXPathObjectPtr result = NULL;
287     int oldProximityPosition, oldContextSize;
288     xmlNodePtr oldInst, oldNode;
289     xsltDocumentPtr oldDoc;
290     int oldNsNr;
291     xmlNsPtr *oldNamespaces;
292
293     if ((ctxt == NULL) || (elem == NULL))
294         return(NULL);
295
296 #ifdef WITH_XSLT_DEBUG_VARIABLE
297     xsltGenericDebug(xsltGenericDebugContext,
298         "Evaluating variable %s\n", elem->name);
299 #endif
300     if (elem->select != NULL) {
301         xmlXPathCompExprPtr comp = NULL;
302
303         if ((precomp != NULL) && (precomp->comp != NULL)) {
304             comp = precomp->comp;
305         } else {
306             comp = xmlXPathCompile(elem->select);
307         }
308         if (comp == NULL)
309             return(NULL);
310         oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
311         oldContextSize = ctxt->xpathCtxt->contextSize;
312         ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
313         oldDoc = ctxt->document;
314         oldNode = ctxt->node;
315         oldInst = ctxt->inst;
316         oldNsNr = ctxt->xpathCtxt->nsNr;
317         oldNamespaces = ctxt->xpathCtxt->namespaces;
318         if (precomp != NULL) {
319             ctxt->inst = precomp->inst;
320             ctxt->xpathCtxt->namespaces = precomp->nsList;
321             ctxt->xpathCtxt->nsNr = precomp->nsNr;
322         } else {
323             ctxt->inst = NULL;
324             ctxt->xpathCtxt->namespaces = NULL;
325             ctxt->xpathCtxt->nsNr = 0;
326         }
327         result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
328         ctxt->xpathCtxt->contextSize = oldContextSize;
329         ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
330         ctxt->xpathCtxt->nsNr = oldNsNr;
331         ctxt->xpathCtxt->namespaces = oldNamespaces;
332         ctxt->inst = oldInst;
333         ctxt->node = oldNode;
334         ctxt->document = oldDoc;
335         if ((precomp == NULL) || (precomp->comp == NULL))
336             xmlXPathFreeCompExpr(comp);
337         if (result == NULL) {
338             xsltPrintErrorContext(ctxt, NULL, precomp->inst);
339             xsltGenericError(xsltGenericErrorContext,
340                 "Evaluating variable %s failed\n", elem->name);
341             ctxt->state = XSLT_STATE_STOPPED;
342 #ifdef WITH_XSLT_DEBUG_VARIABLE
343 #ifdef LIBXML_DEBUG_ENABLED
344         } else {
345             if ((xsltGenericDebugContext == stdout) ||
346                 (xsltGenericDebugContext == stderr))
347                 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
348                                         result, 0);
349 #endif
350 #endif
351         }
352     } else {
353         if (elem->tree == NULL) {
354             result = xmlXPathNewCString("");
355         } else {
356             /*
357              * This is a result tree fragment.
358              */
359             xmlNodePtr container;
360             xmlNodePtr oldInsert;
361             xmlDocPtr  oldoutput;
362
363             container = xmlNewDocNode(ctxt->document->doc, NULL,
364                               (const xmlChar *) "fake node libxslt", NULL);
365             if (container == NULL)
366                 return(NULL);
367             container->parent = NULL;
368
369             oldoutput = ctxt->output;
370             ctxt->output = NULL;
371             oldInsert = ctxt->insert;
372             ctxt->insert = container;
373             xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL);
374             ctxt->insert = oldInsert;
375             ctxt->output = oldoutput;
376
377             result = xmlXPathNewValueTree(container);
378             if (result == NULL) {
379                 result = xmlXPathNewCString("");
380             } else {
381                 /*
382                  * Tag the subtree for removal once consumed
383                  */
384                 result->boolval = 1;
385             }
386 #ifdef WITH_XSLT_DEBUG_VARIABLE
387 #ifdef LIBXML_DEBUG_ENABLED
388             if ((xsltGenericDebugContext == stdout) ||
389                 (xsltGenericDebugContext == stderr))
390                 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
391                                         result, 0);
392 #endif
393 #endif
394         }
395     }
396     return(result);
397 }
398
399 /**
400  * xsltEvalGlobalVariable:
401  * @elem:  the variable or parameter.
402  * @ctxt:  the XSLT transformation context
403  *
404  * Evaluate a global variable value.
405  *
406  * Returns the XPath Object value or NULL in case of error
407  */
408 static xmlXPathObjectPtr
409 xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) {
410     xmlXPathObjectPtr result = NULL;
411     xsltStylePreCompPtr precomp;
412     int oldProximityPosition, oldContextSize;
413     xmlNodePtr oldInst;
414     int oldNsNr;
415     xmlNsPtr *oldNamespaces;
416
417     if ((ctxt == NULL) || (elem == NULL))
418         return(NULL);
419     if (elem->computed)
420         return(elem->value);
421
422
423 #ifdef WITH_XSLT_DEBUG_VARIABLE
424     xsltGenericDebug(xsltGenericDebugContext,
425         "Evaluating global variable %s\n", elem->name);
426 #endif
427
428 #ifdef WITH_DEBUGGER
429     if ((xslDebugStatus != XSLT_DEBUG_NONE) &&
430         elem->comp && elem->comp->inst)
431         xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt);
432 #endif
433
434     precomp = elem->comp;
435     if (elem->select != NULL) {
436         xmlXPathCompExprPtr comp = NULL;
437
438         if ((precomp != NULL) && (precomp->comp != NULL)) {
439             comp = precomp->comp;
440         } else {
441             comp = xmlXPathCompile(elem->select);
442         }
443         if (comp == NULL)
444             return(NULL);
445         oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
446         oldContextSize = ctxt->xpathCtxt->contextSize;
447         oldInst = ctxt->inst;
448         oldNsNr = ctxt->xpathCtxt->nsNr;
449         oldNamespaces = ctxt->xpathCtxt->namespaces;
450         if (precomp != NULL) {
451             ctxt->inst = precomp->inst;
452             ctxt->xpathCtxt->namespaces = precomp->nsList;
453             ctxt->xpathCtxt->nsNr = precomp->nsNr;
454         } else {
455             ctxt->inst = NULL;
456             ctxt->xpathCtxt->namespaces = NULL;
457             ctxt->xpathCtxt->nsNr = 0;
458         }
459         ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
460         result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
461         ctxt->xpathCtxt->contextSize = oldContextSize;
462         ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
463         ctxt->inst = oldInst;
464         ctxt->xpathCtxt->nsNr = oldNsNr;
465         ctxt->xpathCtxt->namespaces = oldNamespaces;
466         if ((precomp == NULL) || (precomp->comp == NULL))
467             xmlXPathFreeCompExpr(comp);
468         if (result == NULL) {
469             xsltPrintErrorContext(ctxt, NULL, precomp->inst);
470             xsltGenericError(xsltGenericErrorContext,
471                 "Evaluating global variable %s failed\n", elem->name);
472             ctxt->state = XSLT_STATE_STOPPED;
473 #ifdef WITH_XSLT_DEBUG_VARIABLE
474 #ifdef LIBXML_DEBUG_ENABLED
475         } else {
476             if ((xsltGenericDebugContext == stdout) ||
477                 (xsltGenericDebugContext == stderr))
478                 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
479                                         result, 0);
480 #endif
481 #endif
482         }
483     } else {
484         if (elem->tree == NULL) {
485             result = xmlXPathNewCString("");
486         } else {
487             /*
488              * This is a result tree fragment.
489              */
490             xmlNodePtr container;
491             xmlNodePtr oldInsert;
492             xmlDocPtr  oldoutput;
493
494             container = xmlNewDocNode(ctxt->document->doc, NULL,
495                               (const xmlChar *) "fake node libxslt", NULL);
496             if (container == NULL)
497                 return(NULL);
498             container->parent = NULL;
499
500             oldoutput = ctxt->output;
501             ctxt->output = NULL;
502             oldInsert = ctxt->insert;
503             ctxt->insert = container;
504             xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL);
505             ctxt->insert = oldInsert;
506             ctxt->output = oldoutput;
507
508             result = xmlXPathNewValueTree(container);
509             if (result == NULL) {
510                 result = xmlXPathNewCString("");
511             } else {
512                 /*
513                  * Tag the subtree for removal once consumed
514                  */
515                 result->boolval = 1;
516             }
517 #ifdef WITH_XSLT_DEBUG_VARIABLE
518 #ifdef LIBXML_DEBUG_ENABLED
519             if ((xsltGenericDebugContext == stdout) ||
520                 (xsltGenericDebugContext == stderr))
521                 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
522                                         result, 0);
523 #endif
524 #endif
525         }
526     }
527     if (result != NULL) {
528         elem->value = result;
529         elem->computed = 1;
530     }
531     return(result);
532 }
533
534 /**
535  * xsltEvalGlobalVariables:
536  * @ctxt:  the XSLT transformation context
537  *
538  * Evaluate the global variables of a stylesheet. This need to be
539  * done on parsed stylesheets before starting to apply transformations
540  *
541  * Returns 0 in case of success, -1 in case of error
542  */
543 int
544 xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
545     xsltStackElemPtr elem;
546     xsltStylesheetPtr style;
547
548     if (ctxt == NULL)
549         return(-1);
550  
551 #ifdef WITH_XSLT_DEBUG_VARIABLE
552     xsltGenericDebug(xsltGenericDebugContext,
553         "Registering global variables\n");
554 #endif
555     ctxt->node = (xmlNodePtr) ctxt->document->doc;
556     ctxt->xpathCtxt->contextSize = 1;
557     ctxt->xpathCtxt->proximityPosition = 1;
558
559     /*
560      * Walk the list from the stylesheets and populate the hash table
561      */
562     style = ctxt->style;
563     while (style != NULL) {
564         elem = style->variables;
565         
566 #ifdef WITH_XSLT_DEBUG_VARIABLE
567         if ((style->doc != NULL) && (style->doc->URL != NULL)) {
568             xsltGenericDebug(xsltGenericDebugContext,
569                              "Registering global variables from %s\n",
570                              style->doc->URL);
571         }
572 #endif
573
574         while (elem != NULL) {
575             xsltStackElemPtr def;
576
577             /*
578              * Global variables are stored in the variables pool.
579              */
580             def = (xsltStackElemPtr) 
581                     xmlHashLookup2(ctxt->globalVars,
582                                  elem->name, elem->nameURI);
583             if (def == NULL) {
584                 int res;
585
586                 def = xsltCopyStackElem(elem);
587                 res = xmlHashAddEntry2(ctxt->globalVars,
588                                  elem->name, elem->nameURI, def);
589             } else if ((elem->comp != NULL) &&
590                        (elem->comp->type == XSLT_FUNC_VARIABLE)) {
591                 /*
592                  * Redefinition of variables from a different stylesheet
593                  * should not generate a message.
594                  */
595                 if ((elem->comp->inst != NULL) &&
596                     (def->comp != NULL) && (def->comp->inst != NULL) &&
597                     (elem->comp->inst->doc == def->comp->inst->doc)) {
598                     xsltPrintErrorContext(ctxt, style, elem->comp->inst);
599                     xsltGenericError(xsltGenericErrorContext,
600                         "Global variable %s already defined\n", elem->name);
601                 }
602             }
603             elem = elem->next;
604         }
605
606         style = xsltNextImport(style);
607     }
608
609     /*
610      * This part does the actual evaluation
611      */
612     ctxt->node = (xmlNodePtr) ctxt->document->doc;
613     ctxt->xpathCtxt->contextSize = 1;
614     ctxt->xpathCtxt->proximityPosition = 1;
615     xmlHashScan(ctxt->globalVars,
616                 (xmlHashScanner) xsltEvalGlobalVariable, ctxt);
617
618     return(0);
619 }
620
621 /**
622  * xsltRegisterGlobalVariable:
623  * @style:  the XSLT transformation context
624  * @name:  the variable name
625  * @ns_uri:  the variable namespace URI
626  * @select:  the expression which need to be evaluated to generate a value
627  * @tree:  the subtree if select is NULL
628  * @comp:  the precompiled value
629  * @value:  the string value if available
630  *
631  * Register a new variable value. If @value is NULL it unregisters
632  * the variable
633  *
634  * Returns 0 in case of success, -1 in case of error
635  */
636 static int
637 xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
638                      const xmlChar *ns_uri, const xmlChar *select,
639                      xmlNodePtr tree, xsltStylePreCompPtr comp,
640                      const xmlChar *value) {
641     xsltStackElemPtr elem, tmp;
642     if (style == NULL)
643         return(-1);
644     if (name == NULL)
645         return(-1);
646     if (comp == NULL)
647         return(-1);
648
649 #ifdef WITH_XSLT_DEBUG_VARIABLE
650     if (comp->type == XSLT_FUNC_PARAM)
651         xsltGenericDebug(xsltGenericDebugContext,
652                          "Defining global param %s\n", name);
653     else
654         xsltGenericDebug(xsltGenericDebugContext,
655                          "Defining global variable %s\n", name);
656 #endif
657
658     elem = xsltNewStackElem();
659     if (elem == NULL)
660         return(-1);
661     elem->comp = comp;
662     elem->name = xmlStrdup(name);
663     elem->select = xmlStrdup(select);
664     if (ns_uri)
665         elem->nameURI = xmlStrdup(ns_uri);
666     elem->tree = tree;
667     tmp = style->variables;
668     if (tmp == NULL) {
669         elem->next = NULL;
670         style->variables = elem;
671     } else {
672         while (tmp->next != NULL)
673             tmp = tmp->next;
674         elem->next = NULL;
675         tmp->next = elem;
676     }
677     if (value != NULL) {
678         elem->computed = 1;
679         elem->value = xmlXPathNewString(value);
680     }
681     return(0);
682 }
683
684 /**
685  * xsltProcessUserParamInternal
686  *
687  * @ctxt:  the XSLT transformation context
688  * @name:  a null terminated parameter name
689  * @value: a null terminated value (may be an XPath expression)
690  * @eval:  0 to treat the value literally, else evaluate as XPath expression
691  *
692  * If @eval is 0 then @value is treated literally and is stored in the global
693  * parameter/variable table without any change.
694  *
695  * Uf @eval is 1 then @value is treated as an XPath expression and is
696  * evaluated.  In this case, if you want to pass a string which will be
697  * interpreted literally then it must be enclosed in single or double quotes.
698  * If the string contains single quotes (double quotes) then it cannot be
699  * enclosed single quotes (double quotes).  If the string which you want to
700  * be treated literally contains both single and double quotes (e.g. Meet
701  * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable
702  * quoting character.  You cannot use &apos; or &quot; inside the string
703  * because the replacement of character entities with their equivalents is
704  * done at a different stage of processing.  The solution is to call
705  * xsltQuoteUserParams or xsltQuoteOneUserParam.
706  *
707  * This needs to be done on parsed stylesheets before starting to apply
708  * transformations.  Normally this will be called (directly or indirectly)
709  * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams,
710  * or xsltQuoteOneUserParam.
711  *
712  * Returns 0 in case of success, -1 in case of error
713  */
714
715 static
716 int
717 xsltProcessUserParamInternal(xsltTransformContextPtr ctxt,
718                              const xmlChar * name,
719                              const xmlChar * value,
720                              int eval) {
721
722     xsltStylesheetPtr style;
723     xmlChar *ncname;
724     xmlChar *prefix;
725     const xmlChar *href;
726     xmlXPathCompExprPtr comp;
727     xmlXPathObjectPtr result;
728     int oldProximityPosition;
729     int oldContextSize;
730     int oldNsNr;
731     xmlNsPtr *oldNamespaces;
732     xsltStackElemPtr elem;
733     int res;
734
735     if (ctxt == NULL)
736         return(-1);
737     if (name == NULL)
738         return(0);
739     if (value == NULL)
740         return(0);
741
742     style = ctxt->style;
743
744 #ifdef WITH_XSLT_DEBUG_VARIABLE
745     xsltGenericDebug(xsltGenericDebugContext,
746             "Evaluating user parameter %s=%s\n", name, value);
747 #endif
748
749     /*
750      * Name lookup
751      */
752
753     ncname = xmlSplitQName2(name, &prefix);
754     href = NULL;
755     if (ncname != NULL) {
756         if (prefix != NULL) {
757             xmlNsPtr ns;
758
759             ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc),
760                              prefix);
761             if (ns == NULL) {
762                 xsltPrintErrorContext(ctxt, style, NULL);
763                 xsltGenericError(xsltGenericErrorContext,
764                 "user param : no namespace bound to prefix %s\n", prefix);
765                 href = NULL;
766             } else {
767                 href = ns->href;
768             }
769             xmlFree(prefix);
770         } else {
771             href = NULL;
772         }
773         xmlFree(ncname);
774     } else {
775         href = NULL;
776         ncname = xmlStrdup(name);
777     }
778
779     /*
780      * Do the evaluation if @eval is non-zero.
781      */
782
783     result = NULL;
784     if (eval != 0) {
785         comp = xmlXPathCompile(value);
786         if (comp != NULL) {
787             oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
788             oldContextSize = ctxt->xpathCtxt->contextSize;
789             ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
790
791             /* 
792              * There is really no in scope namespace for parameters on the
793              * command line.
794              */
795
796             oldNsNr = ctxt->xpathCtxt->nsNr;
797             oldNamespaces = ctxt->xpathCtxt->namespaces;
798             ctxt->xpathCtxt->namespaces = NULL;
799             ctxt->xpathCtxt->nsNr = 0;
800             result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
801             ctxt->xpathCtxt->contextSize = oldContextSize;
802             ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
803             ctxt->xpathCtxt->nsNr = oldNsNr;
804             ctxt->xpathCtxt->namespaces = oldNamespaces;
805             xmlXPathFreeCompExpr(comp);
806         }
807         if (result == NULL) {
808             xsltPrintErrorContext(ctxt, style, NULL);
809             xsltGenericError(xsltGenericErrorContext,
810                 "Evaluating user parameter %s failed\n", name);
811             ctxt->state = XSLT_STATE_STOPPED;
812             xmlFree(ncname);
813             return(-1);
814         }
815     }
816
817     /* 
818      * If @eval is 0 then @value is to be taken literally and result is NULL
819      * 
820      * If @eval is not 0, then @value is an XPath expression and has been
821      * successfully evaluated and result contains the resulting value and
822      * is not NULL.
823      *
824      * Now create an xsltStackElemPtr for insertion into the context's
825      * global variable/parameter hash table.
826      */
827
828 #ifdef WITH_XSLT_DEBUG_VARIABLE
829 #ifdef LIBXML_DEBUG_ENABLED
830     if ((xsltGenericDebugContext == stdout) ||
831         (xsltGenericDebugContext == stderr))
832             xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
833                                     result, 0);
834 #endif
835 #endif
836
837     elem = xsltNewStackElem();
838     if (elem != NULL) {
839         elem->name = xmlStrdup(ncname);
840         if (value != NULL)
841             elem->select = xmlStrdup(value);
842         else
843             elem->select = NULL;
844         if (href)
845             elem->nameURI = xmlStrdup(href);
846         elem->tree = NULL;
847         elem->computed = 1;
848         if (eval == 0) {
849             elem->value = xmlXPathNewString(value);
850         } 
851         else {
852             elem->value = result;
853         }
854     }
855
856     /*
857      * Global parameters are stored in the XPath context variables pool.
858      */
859
860     res = xmlHashAddEntry2(ctxt->globalVars, ncname, href, elem);
861     if (res != 0) {
862         xsltFreeStackElem(elem);
863         xsltPrintErrorContext(ctxt, style, NULL);
864         xsltGenericError(xsltGenericErrorContext,
865             "Global parameter %s already defined\n", ncname);
866     }
867     xmlFree(ncname);
868     return(0);
869 }
870
871 /**
872  * xsltEvalUserParams:
873  *
874  * @ctxt:  the XSLT transformation context
875  * @params:  a NULL terminated array of parameters name/value tuples
876  *
877  * Evaluate the global variables of a stylesheet. This needs to be
878  * done on parsed stylesheets before starting to apply transformations.
879  * Each of the parameters is evaluated as an XPath expression and stored
880  * in the global variables/parameter hash table.  If you want your
881  * parameter used literally, use xsltQuoteUserParams.
882  *
883  * Returns 0 in case of success, -1 in case of error
884  */
885  
886 int
887 xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) {
888     int indx = 0;
889     const xmlChar *name;
890     const xmlChar *value;
891
892     if (params == NULL)
893         return(0);
894     while (params[indx] != NULL) {
895         name = (const xmlChar *) params[indx++];
896         value = (const xmlChar *) params[indx++];
897         if (xsltEvalOneUserParam(ctxt, name, value) != 0) 
898             return(-1);
899     }
900     return 0;
901 }
902
903 /**
904  * xsltQuoteUserParams:
905  *
906  * @ctxt:  the XSLT transformation context
907  * @params:  a NULL terminated arry of parameters names/values tuples
908  *
909  * Similar to xsltEvalUserParams, but the values are treated literally and
910  * are * *not* evaluated as XPath expressions. This should be done on parsed
911  * stylesheets before starting to apply transformations.
912  *
913  * Returns 0 in case of success, -1 in case of error.
914  */
915  
916 int
917 xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) {
918     int indx = 0;
919     const xmlChar *name;
920     const xmlChar *value;
921
922     if (params == NULL)
923         return(0);
924     while (params[indx] != NULL) {
925         name = (const xmlChar *) params[indx++];
926         value = (const xmlChar *) params[indx++];
927         if (xsltQuoteOneUserParam(ctxt, name, value) != 0) 
928             return(-1);
929     }
930     return 0;
931 }
932
933 /**
934  * xsltEvalOneUserParam:
935  *
936  * @ctxt:  the XSLT transformation context
937  * @name:  a null terminated string giving the name of the parameter
938  * @value  a null terminated string giving the XPath expression to be evaluated
939  *
940  * This is normally called from xsltEvalUserParams to process a single
941  * parameter from a list of parameters.  The @value is evaluated as an
942  * XPath expression and the result is stored in the context's global
943  * variable/parameter hash table.
944  *
945  * To have a parameter treated literally (not as an XPath expression)
946  * use xsltQuoteUserParams (or xsltQuoteOneUserParam).  For more
947  * details see description of xsltProcessOneUserParamInternal.
948  *
949  * Returns 0 in case of success, -1 in case of error.
950  */
951
952 int
953 xsltEvalOneUserParam(xsltTransformContextPtr ctxt,
954                      const xmlChar * name,
955                      const xmlChar * value) {
956     return xsltProcessUserParamInternal(ctxt, name, value,
957                                         1 /* xpath eval ? */);
958 }
959
960 /**
961  * xsltQuoteOneUserParam:
962  *
963  * @ctxt:  the XSLT transformation context
964  * @name:  a null terminated string giving the name of the parameter
965  * @value  a null terminated string giving the parameter value
966  *
967  * This is normally called from xsltQuoteUserParams to process a single
968  * parameter from a list of parameters.  The @value is stored in the
969  * context's global variable/parameter hash table.
970  *
971  * Returns 0 in case of success, -1 in case of error.
972  */
973
974 int
975 xsltQuoteOneUserParam(xsltTransformContextPtr ctxt,
976                          const xmlChar * name,
977                          const xmlChar * value) {
978     return xsltProcessUserParamInternal(ctxt, name, value,
979                                         0 /* xpath eval ? */);
980 }
981
982 /**
983  * xsltBuildVariable:
984  * @ctxt:  the XSLT transformation context
985  * @comp:  the precompiled form
986  * @tree:  the tree if select is NULL
987  *
988  * Computes a new variable value.
989  *
990  * Returns the xsltStackElemPtr or NULL in case of error
991  */
992 static xsltStackElemPtr
993 xsltBuildVariable(xsltTransformContextPtr ctxt, xsltStylePreCompPtr comp,
994                   xmlNodePtr tree) {
995     xsltStackElemPtr elem;
996
997 #ifdef WITH_XSLT_DEBUG_VARIABLE
998     xsltGenericDebug(xsltGenericDebugContext,
999                      "Building variable %s", comp->name);
1000     if (comp->select != NULL)
1001         xsltGenericDebug(xsltGenericDebugContext,
1002                          " select %s", comp->select);
1003     xsltGenericDebug(xsltGenericDebugContext, "\n");
1004 #endif
1005
1006     elem = xsltNewStackElem();
1007     if (elem == NULL)
1008         return(NULL);
1009     elem->comp = comp;
1010     elem->name = xmlStrdup(comp->name);
1011     if (comp->select != NULL)
1012         elem->select = xmlStrdup(comp->select);
1013     else
1014         elem->select = NULL;
1015     if (comp->ns)
1016         elem->nameURI = xmlStrdup(comp->ns);
1017     elem->tree = tree;
1018     if (elem->computed == 0) {
1019         elem->value = xsltEvalVariable(ctxt, elem, comp);
1020         if (elem->value != NULL)
1021             elem->computed = 1;
1022     }
1023     return(elem);
1024 }
1025
1026 /**
1027  * xsltRegisterVariable:
1028  * @ctxt:  the XSLT transformation context
1029  * @comp:  pointer to precompiled data
1030  * @tree:  the tree if select is NULL
1031  * @param:  this is a parameter actually
1032  *
1033  * Computes and register a new variable value. 
1034  *
1035  * Returns 0 in case of success, -1 in case of error
1036  */
1037 static int
1038 xsltRegisterVariable(xsltTransformContextPtr ctxt, xsltStylePreCompPtr comp,
1039                      xmlNodePtr tree, int param) {
1040     xsltStackElemPtr elem;
1041
1042     if (xsltCheckStackElem(ctxt, comp->name, comp->ns) != 0) {
1043         if (!param) {
1044             xsltPrintErrorContext(ctxt, NULL, comp->inst);
1045             xsltGenericError(xsltGenericErrorContext,
1046             "xsl:variable : redefining %s\n", comp->name);
1047         }
1048 #ifdef WITH_XSLT_DEBUG_VARIABLE
1049         else
1050             xsltGenericDebug(xsltGenericDebugContext,
1051                      "param %s defined by caller\n", comp->name);
1052 #endif
1053         return(0);
1054     }
1055     elem = xsltBuildVariable(ctxt, comp, tree);
1056     xsltAddStackElem(ctxt, elem);
1057     return(0);
1058 }
1059
1060 /**
1061  * xsltGlobalVariableLookup:
1062  * @ctxt:  the XSLT transformation context
1063  * @name:  the variable name
1064  * @ns_uri:  the variable namespace URI
1065  *
1066  * Search in the Variable array of the context for the given
1067  * variable value.
1068  *
1069  * Returns the value or NULL if not found
1070  */
1071 static xmlXPathObjectPtr
1072 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1073                          const xmlChar *ns_uri) {
1074     xsltStackElemPtr elem;
1075     xmlXPathObjectPtr ret = NULL;
1076
1077     /*
1078      * Lookup the global variables in XPath global variable hash table
1079      */
1080     if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL))
1081         return(NULL);
1082     elem = (xsltStackElemPtr)
1083             xmlHashLookup2(ctxt->globalVars, name, ns_uri);
1084     if (elem == NULL) {
1085 #ifdef WITH_XSLT_DEBUG_VARIABLE
1086         xsltGenericDebug(xsltGenericDebugContext,
1087                          "global variable not found %s\n", name);
1088 #endif
1089         return(NULL);
1090     }
1091     if (elem->computed == 0)
1092         ret = xsltEvalGlobalVariable(elem, ctxt);
1093     else
1094         ret = elem->value;
1095     return(xmlXPathObjectCopy(ret));
1096 }
1097
1098 /**
1099  * xsltVariableLookup:
1100  * @ctxt:  the XSLT transformation context
1101  * @name:  the variable name
1102  * @ns_uri:  the variable namespace URI
1103  *
1104  * Search in the Variable array of the context for the given
1105  * variable value.
1106  *
1107  * Returns the value or NULL if not found
1108  */
1109 xmlXPathObjectPtr
1110 xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1111                    const xmlChar *ns_uri) {
1112     xsltStackElemPtr elem;
1113
1114     if (ctxt == NULL)
1115         return(NULL);
1116
1117     elem = xsltStackLookup(ctxt, name, ns_uri);
1118     if (elem == NULL) {
1119         return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
1120     }
1121     if (elem->computed == 0) {
1122 #ifdef WITH_XSLT_DEBUG_VARIABLE
1123         xsltGenericDebug(xsltGenericDebugContext,
1124                          "uncomputed variable %s\n", name);
1125 #endif
1126         elem->value = xsltEvalVariable(ctxt, elem, NULL);
1127         elem->computed = 1;
1128     }
1129     if (elem->value != NULL)
1130         return(xmlXPathObjectCopy(elem->value));
1131 #ifdef WITH_XSLT_DEBUG_VARIABLE
1132     xsltGenericDebug(xsltGenericDebugContext,
1133                      "variable not found %s\n", name);
1134 #endif
1135     return(NULL);
1136 }
1137
1138 /**
1139  * xsltParseStylesheetCallerParam:
1140  * @ctxt:  the XSLT transformation context
1141  * @cur:  the "param" element
1142  *
1143  * parse an XSLT transformation param declaration, compute
1144  * its value but doesn't record it.
1145  *
1146  * It returns the new xsltStackElemPtr or NULL
1147  */
1148
1149 xsltStackElemPtr
1150 xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
1151     xmlNodePtr tree = NULL;
1152     xsltStackElemPtr elem = NULL;
1153     xsltStylePreCompPtr comp;
1154
1155     if ((cur == NULL) || (ctxt == NULL))
1156         return(NULL);
1157     comp = (xsltStylePreCompPtr) cur->_private;
1158     if (comp == NULL) {
1159         xsltPrintErrorContext(ctxt, NULL, cur);
1160         xsltGenericError(xsltGenericErrorContext,
1161             "xsl:param : compilation error\n");
1162         return(NULL);
1163     }
1164
1165     if (comp->name == NULL) {
1166         xsltPrintErrorContext(ctxt, NULL, cur);
1167         xsltGenericError(xsltGenericErrorContext,
1168             "xsl:param : missing name attribute\n");
1169         return(NULL);
1170     }
1171
1172 #ifdef WITH_XSLT_DEBUG_VARIABLE
1173     xsltGenericDebug(xsltGenericDebugContext,
1174             "Handling param %s\n", comp->name);
1175 #endif
1176
1177     if (comp->select == NULL) {
1178         tree = cur->children;
1179     } else {
1180 #ifdef WITH_XSLT_DEBUG_VARIABLE
1181         xsltGenericDebug(xsltGenericDebugContext,
1182             "        select %s\n", comp->select);
1183 #endif
1184         tree = cur;
1185     }
1186
1187     elem = xsltBuildVariable(ctxt, comp, tree);
1188
1189     return(elem);
1190 }
1191
1192 /**
1193  * xsltParseGlobalVariable:
1194  * @style:  the XSLT stylesheet
1195  * @cur:  the "variable" element
1196  *
1197  * parse an XSLT transformation variable declaration and record
1198  * its value.
1199  */
1200
1201 void
1202 xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) {
1203     xsltStylePreCompPtr comp;
1204
1205     if ((cur == NULL) || (style == NULL))
1206         return;
1207
1208     xsltStylePreCompute(style, cur);
1209     comp = (xsltStylePreCompPtr) cur->_private;
1210     if (comp == NULL) {
1211         xsltPrintErrorContext(NULL, style, cur);
1212         xsltGenericError(xsltGenericErrorContext,
1213              "xsl:variable : compilation failed\n");
1214         return;
1215     }
1216
1217     if (comp->name == NULL) {
1218         xsltPrintErrorContext(NULL, style, cur);
1219         xsltGenericError(xsltGenericErrorContext,
1220             "xsl:variable : missing name attribute\n");
1221         return;
1222     }
1223
1224 #ifdef WITH_XSLT_DEBUG_VARIABLE
1225     xsltGenericDebug(xsltGenericDebugContext,
1226         "Registering global variable %s\n", comp->name);
1227 #endif
1228
1229     xsltRegisterGlobalVariable(style, comp->name, comp->ns, comp->select,
1230                                cur->children, comp, NULL);
1231 }
1232
1233 /**
1234  * xsltParseGlobalParam:
1235  * @style:  the XSLT stylesheet
1236  * @cur:  the "param" element
1237  *
1238  * parse an XSLT transformation param declaration and record
1239  * its value.
1240  */
1241
1242 void
1243 xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
1244     xsltStylePreCompPtr comp;
1245
1246     if ((cur == NULL) || (style == NULL))
1247         return;
1248
1249     xsltStylePreCompute(style, cur);
1250     comp = (xsltStylePreCompPtr) cur->_private;
1251     if (comp == NULL) {
1252         xsltPrintErrorContext(NULL, style, cur);
1253         xsltGenericError(xsltGenericErrorContext,
1254              "xsl:param : compilation failed\n");
1255         return;
1256     }
1257
1258     if (comp->name == NULL) {
1259         xsltPrintErrorContext(NULL, style, cur);
1260         xsltGenericError(xsltGenericErrorContext,
1261             "xsl:param : missing name attribute\n");
1262         return;
1263     }
1264
1265 #ifdef WITH_XSLT_DEBUG_VARIABLE
1266     xsltGenericDebug(xsltGenericDebugContext,
1267         "Registering global param %s\n", comp->name);
1268 #endif
1269
1270     xsltRegisterGlobalVariable(style, comp->name, comp->ns, comp->select,
1271                                cur->children, comp, NULL);
1272 }
1273
1274 /**
1275  * xsltParseStylesheetVariable:
1276  * @ctxt:  the XSLT transformation context
1277  * @cur:  the "variable" element
1278  *
1279  * parse an XSLT transformation variable declaration and record
1280  * its value.
1281  */
1282
1283 void
1284 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
1285     xsltStylePreCompPtr comp;
1286
1287     if ((cur == NULL) || (ctxt == NULL))
1288         return;
1289
1290     comp = (xsltStylePreCompPtr) cur->_private;
1291     if (comp == NULL) {
1292         xsltPrintErrorContext(ctxt, NULL, cur);
1293         xsltGenericError(xsltGenericErrorContext,
1294              "xsl:variable : compilation failed\n");
1295         return;
1296     }
1297
1298     if (comp->name == NULL) {
1299         xsltPrintErrorContext(ctxt, NULL, cur);
1300         xsltGenericError(xsltGenericErrorContext,
1301             "xsl:variable : missing name attribute\n");
1302         return;
1303     }
1304
1305 #ifdef WITH_XSLT_DEBUG_VARIABLE
1306     xsltGenericDebug(xsltGenericDebugContext,
1307         "Registering variable %s\n", comp->name);
1308 #endif
1309
1310     xsltRegisterVariable(ctxt, comp, cur->children, 0);
1311 }
1312
1313 /**
1314  * xsltParseStylesheetParam:
1315  * @ctxt:  the XSLT transformation context
1316  * @cur:  the "param" element
1317  *
1318  * parse an XSLT transformation param declaration and record
1319  * its value.
1320  */
1321
1322 void
1323 xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
1324     xsltStylePreCompPtr comp;
1325
1326     if ((cur == NULL) || (ctxt == NULL))
1327         return;
1328
1329     comp = (xsltStylePreCompPtr) cur->_private;
1330     if (comp == NULL) {
1331         xsltPrintErrorContext(ctxt, NULL, cur);
1332         xsltGenericError(xsltGenericErrorContext,
1333              "xsl:param : compilation failed\n");
1334         return;
1335     }
1336
1337     if (comp->name == NULL) {
1338         xsltPrintErrorContext(ctxt, NULL, cur);
1339         xsltGenericError(xsltGenericErrorContext,
1340             "xsl:param : missing name attribute\n");
1341         return;
1342     }
1343
1344 #ifdef WITH_XSLT_DEBUG_VARIABLE
1345     xsltGenericDebug(xsltGenericDebugContext,
1346         "Registering param %s\n", comp->name);
1347 #endif
1348
1349     xsltRegisterVariable(ctxt, comp, cur->children, 1);
1350 }
1351
1352 /**
1353  * xsltFreeGlobalVariables:
1354  * @ctxt:  the XSLT transformation context
1355  *
1356  * Free up the data associated to the global variables
1357  * its value.
1358  */
1359
1360 void
1361 xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) {
1362     xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem);
1363 }
1364
1365 /**
1366  * xsltXPathVariableLookup:
1367  * @ctxt:  a void * but the the XSLT transformation context actually
1368  * @name:  the variable name
1369  * @ns_uri:  the variable namespace URI
1370  *
1371  * This is the entry point when a varibale is needed by the XPath
1372  * interpretor.
1373  *
1374  * Returns the value or NULL if not found
1375  */
1376 xmlXPathObjectPtr
1377 xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
1378                         const xmlChar *ns_uri) {
1379     xsltTransformContextPtr context;
1380     xmlXPathObjectPtr ret;
1381
1382     if ((ctxt == NULL) || (name == NULL))
1383         return(NULL);
1384
1385 #ifdef WITH_XSLT_DEBUG_VARIABLE
1386     xsltGenericDebug(xsltGenericDebugContext,
1387             "Lookup variable %s\n", name);
1388 #endif
1389     context = (xsltTransformContextPtr) ctxt;
1390     ret = xsltVariableLookup(context, name, ns_uri);
1391     if (ret == NULL) {
1392         xsltPrintErrorContext(ctxt, NULL, NULL);
1393         xsltGenericError(xsltGenericErrorContext,
1394             "unregistered variable %s\n", name);
1395     }
1396 #ifdef WITH_XSLT_DEBUG_VARIABLE
1397     if (ret != NULL)
1398         xsltGenericDebug(xsltGenericDebugContext,
1399             "found variable %s\n", name);
1400 #endif
1401     return(ret);
1402 }
1403
1404