tried to fix a problem in xsltProcessUserParamInternal reported by Babak
[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             prefix = NULL;
771         } else {
772             href = NULL;
773         }
774         xmlFree(ncname);
775         ncname = NULL;
776     } else {
777         href = NULL;
778         ncname = xmlStrdup(name);
779     }
780
781     /*
782      * Do the evaluation if @eval is non-zero.
783      */
784
785     result = NULL;
786     if (eval != 0) {
787         comp = xmlXPathCompile(value);
788         if (comp != NULL) {
789             oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
790             oldContextSize = ctxt->xpathCtxt->contextSize;
791             ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
792
793             /* 
794              * There is really no in scope namespace for parameters on the
795              * command line.
796              */
797
798             oldNsNr = ctxt->xpathCtxt->nsNr;
799             oldNamespaces = ctxt->xpathCtxt->namespaces;
800             ctxt->xpathCtxt->namespaces = NULL;
801             ctxt->xpathCtxt->nsNr = 0;
802             result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
803             ctxt->xpathCtxt->contextSize = oldContextSize;
804             ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
805             ctxt->xpathCtxt->nsNr = oldNsNr;
806             ctxt->xpathCtxt->namespaces = oldNamespaces;
807             xmlXPathFreeCompExpr(comp);
808         }
809         if (result == NULL) {
810             xsltPrintErrorContext(ctxt, style, NULL);
811             xsltGenericError(xsltGenericErrorContext,
812                 "Evaluating user parameter %s failed\n", name);
813             ctxt->state = XSLT_STATE_STOPPED;
814             xmlFree(ncname);
815             return(-1);
816         }
817     }
818
819     /* 
820      * If @eval is 0 then @value is to be taken literally and result is NULL
821      * 
822      * If @eval is not 0, then @value is an XPath expression and has been
823      * successfully evaluated and result contains the resulting value and
824      * is not NULL.
825      *
826      * Now create an xsltStackElemPtr for insertion into the context's
827      * global variable/parameter hash table.
828      */
829
830 #ifdef WITH_XSLT_DEBUG_VARIABLE
831 #ifdef LIBXML_DEBUG_ENABLED
832     if ((xsltGenericDebugContext == stdout) ||
833         (xsltGenericDebugContext == stderr))
834             xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
835                                     result, 0);
836 #endif
837 #endif
838
839     elem = xsltNewStackElem();
840     if (elem != NULL) {
841         elem->name = xmlStrdup(ncname);
842         if (value != NULL)
843             elem->select = xmlStrdup(value);
844         else
845             elem->select = NULL;
846         if (href)
847             elem->nameURI = xmlStrdup(href);
848         elem->tree = NULL;
849         elem->computed = 1;
850         if (eval == 0) {
851             elem->value = xmlXPathNewString(value);
852         } 
853         else {
854             elem->value = result;
855         }
856     }
857
858     /*
859      * Global parameters are stored in the XPath context variables pool.
860      */
861
862     res = xmlHashAddEntry2(ctxt->globalVars, ncname, href, elem);
863     if (res != 0) {
864         xsltFreeStackElem(elem);
865         xsltPrintErrorContext(ctxt, style, NULL);
866         xsltGenericError(xsltGenericErrorContext,
867             "Global parameter %s already defined\n", ncname);
868     }
869     xmlFree(ncname);
870     return(0);
871 }
872
873 /**
874  * xsltEvalUserParams:
875  *
876  * @ctxt:  the XSLT transformation context
877  * @params:  a NULL terminated array of parameters name/value tuples
878  *
879  * Evaluate the global variables of a stylesheet. This needs to be
880  * done on parsed stylesheets before starting to apply transformations.
881  * Each of the parameters is evaluated as an XPath expression and stored
882  * in the global variables/parameter hash table.  If you want your
883  * parameter used literally, use xsltQuoteUserParams.
884  *
885  * Returns 0 in case of success, -1 in case of error
886  */
887  
888 int
889 xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) {
890     int indx = 0;
891     const xmlChar *name;
892     const xmlChar *value;
893
894     if (params == NULL)
895         return(0);
896     while (params[indx] != NULL) {
897         name = (const xmlChar *) params[indx++];
898         value = (const xmlChar *) params[indx++];
899         if (xsltEvalOneUserParam(ctxt, name, value) != 0) 
900             return(-1);
901     }
902     return 0;
903 }
904
905 /**
906  * xsltQuoteUserParams:
907  *
908  * @ctxt:  the XSLT transformation context
909  * @params:  a NULL terminated arry of parameters names/values tuples
910  *
911  * Similar to xsltEvalUserParams, but the values are treated literally and
912  * are * *not* evaluated as XPath expressions. This should be done on parsed
913  * stylesheets before starting to apply transformations.
914  *
915  * Returns 0 in case of success, -1 in case of error.
916  */
917  
918 int
919 xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) {
920     int indx = 0;
921     const xmlChar *name;
922     const xmlChar *value;
923
924     if (params == NULL)
925         return(0);
926     while (params[indx] != NULL) {
927         name = (const xmlChar *) params[indx++];
928         value = (const xmlChar *) params[indx++];
929         if (xsltQuoteOneUserParam(ctxt, name, value) != 0) 
930             return(-1);
931     }
932     return 0;
933 }
934
935 /**
936  * xsltEvalOneUserParam:
937  *
938  * @ctxt:  the XSLT transformation context
939  * @name:  a null terminated string giving the name of the parameter
940  * @value  a null terminated string giving the XPath expression to be evaluated
941  *
942  * This is normally called from xsltEvalUserParams to process a single
943  * parameter from a list of parameters.  The @value is evaluated as an
944  * XPath expression and the result is stored in the context's global
945  * variable/parameter hash table.
946  *
947  * To have a parameter treated literally (not as an XPath expression)
948  * use xsltQuoteUserParams (or xsltQuoteOneUserParam).  For more
949  * details see description of xsltProcessOneUserParamInternal.
950  *
951  * Returns 0 in case of success, -1 in case of error.
952  */
953
954 int
955 xsltEvalOneUserParam(xsltTransformContextPtr ctxt,
956                      const xmlChar * name,
957                      const xmlChar * value) {
958     return xsltProcessUserParamInternal(ctxt, name, value,
959                                         1 /* xpath eval ? */);
960 }
961
962 /**
963  * xsltQuoteOneUserParam:
964  *
965  * @ctxt:  the XSLT transformation context
966  * @name:  a null terminated string giving the name of the parameter
967  * @value  a null terminated string giving the parameter value
968  *
969  * This is normally called from xsltQuoteUserParams to process a single
970  * parameter from a list of parameters.  The @value is stored in the
971  * context's global variable/parameter hash table.
972  *
973  * Returns 0 in case of success, -1 in case of error.
974  */
975
976 int
977 xsltQuoteOneUserParam(xsltTransformContextPtr ctxt,
978                          const xmlChar * name,
979                          const xmlChar * value) {
980     return xsltProcessUserParamInternal(ctxt, name, value,
981                                         0 /* xpath eval ? */);
982 }
983
984 /**
985  * xsltBuildVariable:
986  * @ctxt:  the XSLT transformation context
987  * @comp:  the precompiled form
988  * @tree:  the tree if select is NULL
989  *
990  * Computes a new variable value.
991  *
992  * Returns the xsltStackElemPtr or NULL in case of error
993  */
994 static xsltStackElemPtr
995 xsltBuildVariable(xsltTransformContextPtr ctxt, xsltStylePreCompPtr comp,
996                   xmlNodePtr tree) {
997     xsltStackElemPtr elem;
998
999 #ifdef WITH_XSLT_DEBUG_VARIABLE
1000     xsltGenericDebug(xsltGenericDebugContext,
1001                      "Building variable %s", comp->name);
1002     if (comp->select != NULL)
1003         xsltGenericDebug(xsltGenericDebugContext,
1004                          " select %s", comp->select);
1005     xsltGenericDebug(xsltGenericDebugContext, "\n");
1006 #endif
1007
1008     elem = xsltNewStackElem();
1009     if (elem == NULL)
1010         return(NULL);
1011     elem->comp = comp;
1012     elem->name = xmlStrdup(comp->name);
1013     if (comp->select != NULL)
1014         elem->select = xmlStrdup(comp->select);
1015     else
1016         elem->select = NULL;
1017     if (comp->ns)
1018         elem->nameURI = xmlStrdup(comp->ns);
1019     elem->tree = tree;
1020     if (elem->computed == 0) {
1021         elem->value = xsltEvalVariable(ctxt, elem, comp);
1022         if (elem->value != NULL)
1023             elem->computed = 1;
1024     }
1025     return(elem);
1026 }
1027
1028 /**
1029  * xsltRegisterVariable:
1030  * @ctxt:  the XSLT transformation context
1031  * @comp:  pointer to precompiled data
1032  * @tree:  the tree if select is NULL
1033  * @param:  this is a parameter actually
1034  *
1035  * Computes and register a new variable value. 
1036  *
1037  * Returns 0 in case of success, -1 in case of error
1038  */
1039 static int
1040 xsltRegisterVariable(xsltTransformContextPtr ctxt, xsltStylePreCompPtr comp,
1041                      xmlNodePtr tree, int param) {
1042     xsltStackElemPtr elem;
1043
1044     if (xsltCheckStackElem(ctxt, comp->name, comp->ns) != 0) {
1045         if (!param) {
1046             xsltPrintErrorContext(ctxt, NULL, comp->inst);
1047             xsltGenericError(xsltGenericErrorContext,
1048             "xsl:variable : redefining %s\n", comp->name);
1049         }
1050 #ifdef WITH_XSLT_DEBUG_VARIABLE
1051         else
1052             xsltGenericDebug(xsltGenericDebugContext,
1053                      "param %s defined by caller\n", comp->name);
1054 #endif
1055         return(0);
1056     }
1057     elem = xsltBuildVariable(ctxt, comp, tree);
1058     xsltAddStackElem(ctxt, elem);
1059     return(0);
1060 }
1061
1062 /**
1063  * xsltGlobalVariableLookup:
1064  * @ctxt:  the XSLT transformation context
1065  * @name:  the variable name
1066  * @ns_uri:  the variable namespace URI
1067  *
1068  * Search in the Variable array of the context for the given
1069  * variable value.
1070  *
1071  * Returns the value or NULL if not found
1072  */
1073 static xmlXPathObjectPtr
1074 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1075                          const xmlChar *ns_uri) {
1076     xsltStackElemPtr elem;
1077     xmlXPathObjectPtr ret = NULL;
1078
1079     /*
1080      * Lookup the global variables in XPath global variable hash table
1081      */
1082     if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL))
1083         return(NULL);
1084     elem = (xsltStackElemPtr)
1085             xmlHashLookup2(ctxt->globalVars, name, ns_uri);
1086     if (elem == NULL) {
1087 #ifdef WITH_XSLT_DEBUG_VARIABLE
1088         xsltGenericDebug(xsltGenericDebugContext,
1089                          "global variable not found %s\n", name);
1090 #endif
1091         return(NULL);
1092     }
1093     if (elem->computed == 0)
1094         ret = xsltEvalGlobalVariable(elem, ctxt);
1095     else
1096         ret = elem->value;
1097     return(xmlXPathObjectCopy(ret));
1098 }
1099
1100 /**
1101  * xsltVariableLookup:
1102  * @ctxt:  the XSLT transformation context
1103  * @name:  the variable name
1104  * @ns_uri:  the variable namespace URI
1105  *
1106  * Search in the Variable array of the context for the given
1107  * variable value.
1108  *
1109  * Returns the value or NULL if not found
1110  */
1111 xmlXPathObjectPtr
1112 xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1113                    const xmlChar *ns_uri) {
1114     xsltStackElemPtr elem;
1115
1116     if (ctxt == NULL)
1117         return(NULL);
1118
1119     elem = xsltStackLookup(ctxt, name, ns_uri);
1120     if (elem == NULL) {
1121         return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
1122     }
1123     if (elem->computed == 0) {
1124 #ifdef WITH_XSLT_DEBUG_VARIABLE
1125         xsltGenericDebug(xsltGenericDebugContext,
1126                          "uncomputed variable %s\n", name);
1127 #endif
1128         elem->value = xsltEvalVariable(ctxt, elem, NULL);
1129         elem->computed = 1;
1130     }
1131     if (elem->value != NULL)
1132         return(xmlXPathObjectCopy(elem->value));
1133 #ifdef WITH_XSLT_DEBUG_VARIABLE
1134     xsltGenericDebug(xsltGenericDebugContext,
1135                      "variable not found %s\n", name);
1136 #endif
1137     return(NULL);
1138 }
1139
1140 /**
1141  * xsltParseStylesheetCallerParam:
1142  * @ctxt:  the XSLT transformation context
1143  * @cur:  the "param" element
1144  *
1145  * parse an XSLT transformation param declaration, compute
1146  * its value but doesn't record it.
1147  *
1148  * It returns the new xsltStackElemPtr or NULL
1149  */
1150
1151 xsltStackElemPtr
1152 xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
1153     xmlNodePtr tree = NULL;
1154     xsltStackElemPtr elem = NULL;
1155     xsltStylePreCompPtr comp;
1156
1157     if ((cur == NULL) || (ctxt == NULL))
1158         return(NULL);
1159     comp = (xsltStylePreCompPtr) cur->_private;
1160     if (comp == NULL) {
1161         xsltPrintErrorContext(ctxt, NULL, cur);
1162         xsltGenericError(xsltGenericErrorContext,
1163             "xsl:param : compilation error\n");
1164         return(NULL);
1165     }
1166
1167     if (comp->name == NULL) {
1168         xsltPrintErrorContext(ctxt, NULL, cur);
1169         xsltGenericError(xsltGenericErrorContext,
1170             "xsl:param : missing name attribute\n");
1171         return(NULL);
1172     }
1173
1174 #ifdef WITH_XSLT_DEBUG_VARIABLE
1175     xsltGenericDebug(xsltGenericDebugContext,
1176             "Handling param %s\n", comp->name);
1177 #endif
1178
1179     if (comp->select == NULL) {
1180         tree = cur->children;
1181     } else {
1182 #ifdef WITH_XSLT_DEBUG_VARIABLE
1183         xsltGenericDebug(xsltGenericDebugContext,
1184             "        select %s\n", comp->select);
1185 #endif
1186         tree = cur;
1187     }
1188
1189     elem = xsltBuildVariable(ctxt, comp, tree);
1190
1191     return(elem);
1192 }
1193
1194 /**
1195  * xsltParseGlobalVariable:
1196  * @style:  the XSLT stylesheet
1197  * @cur:  the "variable" element
1198  *
1199  * parse an XSLT transformation variable declaration and record
1200  * its value.
1201  */
1202
1203 void
1204 xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) {
1205     xsltStylePreCompPtr comp;
1206
1207     if ((cur == NULL) || (style == NULL))
1208         return;
1209
1210     xsltStylePreCompute(style, cur);
1211     comp = (xsltStylePreCompPtr) cur->_private;
1212     if (comp == NULL) {
1213         xsltPrintErrorContext(NULL, style, cur);
1214         xsltGenericError(xsltGenericErrorContext,
1215              "xsl:variable : compilation failed\n");
1216         return;
1217     }
1218
1219     if (comp->name == NULL) {
1220         xsltPrintErrorContext(NULL, style, cur);
1221         xsltGenericError(xsltGenericErrorContext,
1222             "xsl:variable : missing name attribute\n");
1223         return;
1224     }
1225
1226 #ifdef WITH_XSLT_DEBUG_VARIABLE
1227     xsltGenericDebug(xsltGenericDebugContext,
1228         "Registering global variable %s\n", comp->name);
1229 #endif
1230
1231     xsltRegisterGlobalVariable(style, comp->name, comp->ns, comp->select,
1232                                cur->children, comp, NULL);
1233 }
1234
1235 /**
1236  * xsltParseGlobalParam:
1237  * @style:  the XSLT stylesheet
1238  * @cur:  the "param" element
1239  *
1240  * parse an XSLT transformation param declaration and record
1241  * its value.
1242  */
1243
1244 void
1245 xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
1246     xsltStylePreCompPtr comp;
1247
1248     if ((cur == NULL) || (style == NULL))
1249         return;
1250
1251     xsltStylePreCompute(style, cur);
1252     comp = (xsltStylePreCompPtr) cur->_private;
1253     if (comp == NULL) {
1254         xsltPrintErrorContext(NULL, style, cur);
1255         xsltGenericError(xsltGenericErrorContext,
1256              "xsl:param : compilation failed\n");
1257         return;
1258     }
1259
1260     if (comp->name == NULL) {
1261         xsltPrintErrorContext(NULL, style, cur);
1262         xsltGenericError(xsltGenericErrorContext,
1263             "xsl:param : missing name attribute\n");
1264         return;
1265     }
1266
1267 #ifdef WITH_XSLT_DEBUG_VARIABLE
1268     xsltGenericDebug(xsltGenericDebugContext,
1269         "Registering global param %s\n", comp->name);
1270 #endif
1271
1272     xsltRegisterGlobalVariable(style, comp->name, comp->ns, comp->select,
1273                                cur->children, comp, NULL);
1274 }
1275
1276 /**
1277  * xsltParseStylesheetVariable:
1278  * @ctxt:  the XSLT transformation context
1279  * @cur:  the "variable" element
1280  *
1281  * parse an XSLT transformation variable declaration and record
1282  * its value.
1283  */
1284
1285 void
1286 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
1287     xsltStylePreCompPtr comp;
1288
1289     if ((cur == NULL) || (ctxt == NULL))
1290         return;
1291
1292     comp = (xsltStylePreCompPtr) cur->_private;
1293     if (comp == NULL) {
1294         xsltPrintErrorContext(ctxt, NULL, cur);
1295         xsltGenericError(xsltGenericErrorContext,
1296              "xsl:variable : compilation failed\n");
1297         return;
1298     }
1299
1300     if (comp->name == NULL) {
1301         xsltPrintErrorContext(ctxt, NULL, cur);
1302         xsltGenericError(xsltGenericErrorContext,
1303             "xsl:variable : missing name attribute\n");
1304         return;
1305     }
1306
1307 #ifdef WITH_XSLT_DEBUG_VARIABLE
1308     xsltGenericDebug(xsltGenericDebugContext,
1309         "Registering variable %s\n", comp->name);
1310 #endif
1311
1312     xsltRegisterVariable(ctxt, comp, cur->children, 0);
1313 }
1314
1315 /**
1316  * xsltParseStylesheetParam:
1317  * @ctxt:  the XSLT transformation context
1318  * @cur:  the "param" element
1319  *
1320  * parse an XSLT transformation param declaration and record
1321  * its value.
1322  */
1323
1324 void
1325 xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
1326     xsltStylePreCompPtr comp;
1327
1328     if ((cur == NULL) || (ctxt == NULL))
1329         return;
1330
1331     comp = (xsltStylePreCompPtr) cur->_private;
1332     if (comp == NULL) {
1333         xsltPrintErrorContext(ctxt, NULL, cur);
1334         xsltGenericError(xsltGenericErrorContext,
1335              "xsl:param : compilation failed\n");
1336         return;
1337     }
1338
1339     if (comp->name == NULL) {
1340         xsltPrintErrorContext(ctxt, NULL, cur);
1341         xsltGenericError(xsltGenericErrorContext,
1342             "xsl:param : missing name attribute\n");
1343         return;
1344     }
1345
1346 #ifdef WITH_XSLT_DEBUG_VARIABLE
1347     xsltGenericDebug(xsltGenericDebugContext,
1348         "Registering param %s\n", comp->name);
1349 #endif
1350
1351     xsltRegisterVariable(ctxt, comp, cur->children, 1);
1352 }
1353
1354 /**
1355  * xsltFreeGlobalVariables:
1356  * @ctxt:  the XSLT transformation context
1357  *
1358  * Free up the data associated to the global variables
1359  * its value.
1360  */
1361
1362 void
1363 xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) {
1364     xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem);
1365 }
1366
1367 /**
1368  * xsltXPathVariableLookup:
1369  * @ctxt:  a void * but the the XSLT transformation context actually
1370  * @name:  the variable name
1371  * @ns_uri:  the variable namespace URI
1372  *
1373  * This is the entry point when a varibale is needed by the XPath
1374  * interpretor.
1375  *
1376  * Returns the value or NULL if not found
1377  */
1378 xmlXPathObjectPtr
1379 xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
1380                         const xmlChar *ns_uri) {
1381     xsltTransformContextPtr context;
1382     xmlXPathObjectPtr ret;
1383
1384     if ((ctxt == NULL) || (name == NULL))
1385         return(NULL);
1386
1387 #ifdef WITH_XSLT_DEBUG_VARIABLE
1388     xsltGenericDebug(xsltGenericDebugContext,
1389             "Lookup variable %s\n", name);
1390 #endif
1391     context = (xsltTransformContextPtr) ctxt;
1392     ret = xsltVariableLookup(context, name, ns_uri);
1393     if (ret == NULL) {
1394         xsltPrintErrorContext(ctxt, NULL, NULL);
1395         xsltGenericError(xsltGenericErrorContext,
1396             "unregistered variable %s\n", name);
1397     }
1398 #ifdef WITH_XSLT_DEBUG_VARIABLE
1399     if (ret != NULL)
1400         xsltGenericDebug(xsltGenericDebugContext,
1401             "found variable %s\n", name);
1402 #endif
1403     return(ret);
1404 }
1405
1406