1/*----------------------------------------------------------------------------
2|   Copyright (c) 2000 Jochen Loewer (loewerj@hotmail.com)
3|-----------------------------------------------------------------------------
4|
5|   $Id: domxslt.c,v 1.119 2008/03/04 21:03:09 rolf Exp $
6|
7|
8|   A XSLT implementation for tDOM, according to the W3C
9|   recommendation (16 Nov 1999).
10|   See http://www.w3.org/TR/1999/REC-xslt-19991116 for details.
11|
12|
13|   The contents of this file are subject to the Mozilla Public License
14|   Version 1.1 (the "License"); you may not use this file except in
15|   compliance with the License. You may obtain a copy of the License at
16|   http://www.mozilla.org/MPL/
17|
18|   Software distributed under the License is distributed on an "AS IS"
19|   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
20|   License for the specific language governing rights and limitations
21|   under the License.
22|
23|   The Original Code is tDOM.
24|
25|   The Initial Developer of the Original Code is Jochen Loewer
26|   Portions created by Jochen Loewer are Copyright (C) 1999, 2000
27|   Jochen Loewer. All Rights Reserved.
28|
29|   Contributor(s):
30|       Aug01    Rolf Ade   xsl:include, xsl:import, xsl:apply-imports,
31|                           document() plus several bug fixes
32|
33|       Fall/Winter 01 Rolf Ade rewrite of xsl:number, xsl:key/key(),
34|                               handling of toplevel var/parameter,
35|                               plenty of fixes and enhancements all
36|                               over the place.
37|
38|    2001-2007   Rolf Ade   All changes and enhancements.
39|
40|   written by Jochen Loewer
41|   June, 2000
42|
43\---------------------------------------------------------------------------*/
44
45
46
47/*----------------------------------------------------------------------------
48|   Includes
49|
50\---------------------------------------------------------------------------*/
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <math.h>
55#include <limits.h>
56#include <ctype.h>
57#include <locale.h>
58#include <dom.h>
59#include <domxpath.h>
60#include <domxslt.h>
61#include <tcl.h>         /* for hash tables */
62
63/*----------------------------------------------------------------------------
64|   Defines
65|
66\---------------------------------------------------------------------------*/
67#define XSLT_NAMESPACE  "http://www.w3.org/1999/XSL/Transform"
68#define INITIAL_SIZE_FOR_KEYSETS 10
69
70
71/*----------------------------------------------------------------------------
72|   Macros
73|
74\---------------------------------------------------------------------------*/
75#define DBG(x)
76#define TRACE(x)            DBG(fprintf(stderr,(x)))
77#define TRACE1(x,a)         DBG(fprintf(stderr,(x),(a)))
78#define TRACE2(x,a,b)       DBG(fprintf(stderr,(x),(a),(b)))
79#define TRACE3(x,a,b,c)     DBG(fprintf(stderr,(x),(a),(b),(c)))
80#define TRACE4(x,a,b,c,d)   DBG(fprintf(stderr,(x),(a),(b),(c),(d)))
81#define TRACE5(x,a,b,c,d,e) DBG(fprintf(stderr,(x),(a),(b),(c),(d),(e)))
82
83#define CHECK_RC            if (rc < 0) return rc
84#define CHECK_RC1(x)        if (rc < 0) {FREE((char*)(x)); return rc;}
85#define SET_TAG(t,n,s,v)    if (strcmp(n,s)==0) { t->info = v; return v; }
86#define SETSCOPESTART       xs->varFramesStack[xs->varFramesStackPtr].stop=1
87#define SETPARAMDEF         xs->varFramesStack[xs->varFramesStackPtr].stop=2
88
89#if defined(_MSC_VER)
90# define STRCASECMP(a,b)  stricmp (a,b)
91#else
92# define STRCASECMP(a,b)  strcasecmp (a,b)
93#endif
94
95extern void printAst (int depth, ast t);
96
97/*--------------------------------------------------------------------------
98|   xsltTag
99|
100\-------------------------------------------------------------------------*/
101typedef enum {
102
103    unknown = 1,
104    applyImports, applyTemplates, attribute, attributeSet, callTemplate,
105    choose, comment, copy, copyOf, decimalFormat, element, fallback, forEach,
106    xsltIf, import,
107    include, key, message, namespaceAlias, number, output, otherwise,
108    param, procinstr,
109    preserveSpace, sort, stylesheet, stripSpace, text, template,
110    transform, valueOf, variable, when, withParam
111
112} xsltTag;
113
114
115/*--------------------------------------------------------------------------
116|   xsltAttr
117|
118\-------------------------------------------------------------------------*/
119typedef enum {
120
121    a_caseorder = 1,
122    a_count, a_dataType, a_disableOutputEscaping,
123    a_doctypePublic, a_doctypeSystem, a_elements, a_encoding,
124    a_format, a_from, a_href, a_lang, a_level, a_match, a_mediaType, a_method,
125    a_mode, a_name, a_namespace, a_order, a_prio, a_select, a_space,
126    a_terminate, a_test, a_use, a_useAttributeSets, a_value,
127    a_groupingSeparator, a_groupingSize,
128    a_decimalSeparator, a_infinity, a_minusSign, a_nan, a_percent,
129    a_perMille, a_zeroDigit, a_digit, a_patternSeparator, a_version,
130    a_excludeResultPrefixes, a_extensionElementPrefixes,
131    a_stylesheetPrefix, a_resultPrefix, a_indent, a_omitXMLDeclaration,
132    a_standalone, a_cdataSectionElements
133
134} xsltAttr;
135
136
137/*--------------------------------------------------------------------------
138|   xsltExclExtNS
139|
140\-------------------------------------------------------------------------*/
141typedef struct xsltExclExtNS
142{
143    char                 * uri;
144
145    struct xsltExclExtNS * next;
146
147} xsltExclExtNS;
148
149/*--------------------------------------------------------------------------
150|   xsltSubDocs
151|
152\-------------------------------------------------------------------------*/
153typedef struct xsltSubDoc
154{
155    domDocument        * doc;
156    char               * baseURI;
157    Tcl_HashTable        keyData;
158    xsltExclExtNS      * excludeNS;
159    xsltExclExtNS      * extensionNS;
160    int                  fwCmpProcessing;
161    int                  isStylesheet;
162    int                  fixedXMLSource;
163    int                  mustFree;
164
165    struct xsltSubDoc  * next;
166
167} xsltSubDoc;
168
169/*--------------------------------------------------------------------------
170|   xsltTemplate
171|
172\-------------------------------------------------------------------------*/
173typedef struct xsltTemplate {
174
175    char       * match;
176    const char * name;
177    const char * nameURI;
178    ast          ast;
179    const char * mode;
180    const char * modeURI;
181    double       prio;
182    domNode    * content;
183    double       precedence;
184    ast          freeAst;
185    xsltSubDoc * sDoc;
186
187    struct xsltTemplate *next;
188
189} xsltTemplate;
190
191
192/*--------------------------------------------------------------------------
193|   xsltAttrSet
194|
195\-------------------------------------------------------------------------*/
196typedef struct xsltAttrSet {
197
198    const char * name;
199    const char * uri;
200    domNode    * content;
201
202    struct xsltAttrSet *next;
203
204} xsltAttrSet;
205
206/*--------------------------------------------------------------------------
207|   xsltNodeSet
208|
209\-------------------------------------------------------------------------*/
210typedef struct xsltNodeSet {
211
212    domNode       **nodes;
213    int             nr_nodes;
214    int             allocated;
215
216} xsltNodeSet;
217
218/*--------------------------------------------------------------------------
219|   xsltKeyInfo
220|
221\-------------------------------------------------------------------------*/
222typedef struct xsltKeyInfo {
223
224    domNode       * node;
225    char          * match;
226    ast             matchAst;
227    char          * use;
228    ast             useAst;
229
230    struct xsltKeyInfo * next;
231
232} xsltKeyInfo;
233
234/*--------------------------------------------------------------------------
235|   xsltVariable
236|
237\-------------------------------------------------------------------------*/
238typedef struct xsltVariable {
239
240    const char           * name;
241    const char           * uri;
242    domNode        * node;
243    xpathResultSet   rs;
244    int              active;
245
246} xsltVariable;
247
248/*--------------------------------------------------------------------------
249|   xsltVarFrame
250|
251\-------------------------------------------------------------------------*/
252typedef struct xsltVarFrame {
253
254    xsltVariable        * vars;
255    int                   polluted;
256    int                   nrOfVars;
257    int                   varStartIndex;
258    int                   stop;
259
260} xsltVarFrame;
261
262/*--------------------------------------------------------------------------
263|   xsltTopLevelVar
264|
265\-------------------------------------------------------------------------*/
266typedef struct xsltTopLevelVar
267{
268
269    domNode                 * node;
270    char                    * name;
271    int                       isParameter;
272    double                    precedence;
273
274} xsltTopLevelVar;
275
276/*--------------------------------------------------------------------------
277|   xsltVarInProcess
278|
279\-------------------------------------------------------------------------*/
280typedef struct xsltVarInProcess
281
282{
283    char                    *name;
284
285    struct xsltVarInProcess *next;
286
287} xsltVarInProcess;
288
289
290/*--------------------------------------------------------------------------
291|   xsltDecimalFormat
292|
293\-------------------------------------------------------------------------*/
294typedef struct xsltDecimalFormat
295{
296#if TclOnly8Bits
297    char   * name;
298    char   * uri;
299    char     decimalSeparator;
300    char     groupingSeparator;
301    char   * infinity;
302    char     minusSign;
303    char   * NaN;
304    char     percent;
305    char     zeroDigit;
306    char     digit;
307    char     patternSeparator;
308#else
309    char        * name;
310    char        * uri;
311    Tcl_UniChar   decimalSeparator;
312    Tcl_UniChar   groupingSeparator;
313    char        * infinity;
314    Tcl_UniChar   minusSign;
315    char        * NaN;
316    Tcl_UniChar   percent;
317    Tcl_UniChar   perMille;
318    Tcl_UniChar   zeroDigit;
319    Tcl_UniChar   digit;
320    Tcl_UniChar   patternSeparator;
321#endif /* TclOnly8Bits */
322
323    struct xsltDecimalFormat * next;
324
325} xsltDecimalFormat;
326
327
328/*--------------------------------------------------------------------------
329|   xsltWSInfo
330|
331\-------------------------------------------------------------------------*/
332typedef struct xsltWSInfo
333{
334
335    int            hasData;
336    int            stripAll;
337    double         wildcardPrec;
338    Tcl_HashTable  stripTokens;
339    Tcl_HashTable  preserveTokens;
340
341} xsltWSInfo;
342
343
344typedef struct xsltNSAlias
345{
346
347    char    *fromUri;
348    char    *toUri;
349    double   precedence;
350
351    struct xsltNSAlias *next;
352
353} xsltNSAlias;
354
355
356/*--------------------------------------------------------------------------
357|   xsltState
358|
359\-------------------------------------------------------------------------*/
360typedef struct {
361
362    xsltTemplate      * templates;
363    Tcl_HashTable       namedTemplates;
364    Tcl_HashTable       isElementTpls;
365    xsltWSInfo          wsInfo;
366    domNode           * xmlRootNode;
367    domDocInfo          doctype;
368    int                 indentOutput;
369    domDocument       * resultDoc;
370    domNode           * lastNode;
371    xsltVarFrame      * varFramesStack;
372    int                 varFramesStackPtr;
373    int                 varFramesStackLen;
374    xsltVariable      * varStack;
375    int                 varStackPtr;
376    int                 varStackLen;
377    xsltAttrSet       * attrSets;
378    Tcl_HashTable       xpaths;
379    Tcl_HashTable       pattern;
380    Tcl_HashTable       formats;
381    Tcl_HashTable       topLevelVars;
382    Tcl_HashTable       keyInfos;
383    xsltNSAlias       * nsAliases;
384    int                 nsUniqeNr;
385    xsltVarInProcess  * varsInProcess;
386    xpathCBs            cbs;
387    xpathFuncCallback   orig_funcCB;
388    void              * orig_funcClientData;
389    xsltMsgCB           xsltMsgCB;
390    void              * xsltMsgClientData;
391    xsltDecimalFormat * decimalFormats;
392    domNode           * current;
393    xsltSubDoc        * subDocs;
394    xsltSubDoc        * currentSubDoc;
395    xsltTemplate      * currentTplRule;
396    domNode           * currentXSLTNode;
397    domDocument       * xsltDoc;
398
399} xsltState;
400
401
402typedef enum {
403    latin_number, latin_upper, latin_lower,
404    roman_upper, roman_lower
405} xsltNumberingType;
406
407
408/*--------------------------------------------------------------------------
409|   xsltNumberFormatToken
410|
411\-------------------------------------------------------------------------*/
412typedef struct {
413
414    xsltNumberingType  type;
415    int                minlength;
416    char              *sepStart;
417    int                sepLen;
418
419} xsltNumberFormatToken;
420
421
422/*--------------------------------------------------------------------------
423|   xsltNumberFormat
424|
425\-------------------------------------------------------------------------*/
426typedef struct {
427
428    char                  *formatStr;
429    int                    prologLen;
430    xsltNumberFormatToken *tokens;
431    int                    maxtokens;
432    char                  *epilogStart;
433    int                    epilogLen;
434
435} xsltNumberFormat;
436
437
438/*--------------------------------------------------------------------------
439|   Prototypes
440|
441\-------------------------------------------------------------------------*/
442static int ApplyTemplates ( xsltState *xs, xpathResultSet *context,
443                            domNode *currentNode, int currentPos,
444                            domNode *actionNode, xpathResultSet *nodeList,
445                            const char *mode, const char *modeURI,
446                            char **errMsg);
447
448static int ApplyTemplate (  xsltState *xs, xpathResultSet *context,
449                            domNode *currentNode, domNode *exprContext,
450                            int currentPos, const char *mode,
451                            const char *modeURI, char **errMsg);
452
453static int ExecActions (xsltState *xs, xpathResultSet *context,
454                        domNode *currentNode, int currentPos,
455                        domNode *actionNode,  char **errMsg);
456
457static domDocument * getExternalDocument (Tcl_Interp *interp, xsltState *xs,
458                                          domDocument *xsltDoc,
459                                          const char *baseURI,
460                                          const char *href, int isStylesheet,
461                                          int fixedXMLSource, char **errMsg);
462
463
464/*----------------------------------------------------------------------------
465|   printXML
466|
467\---------------------------------------------------------------------------*/
468static void printXML (domNode *node, int level, int maxlevel) {
469
470    domTextNode *tnode;
471    domProcessingInstructionNode *pi;
472    char tmp[80];
473    int  i, l, n;
474
475    n = 0;
476    while (node) {
477
478        for (i=0;i<level;i++) fprintf(stderr, "  ");
479        if (node->nodeType == ELEMENT_NODE) {
480            if (node->firstChild && node->firstChild->nodeType == TEXT_NODE) {
481                tnode = (domTextNode*)node->firstChild;
482                l = tnode->valueLength;
483                if (l > 30) l = 30;
484                memmove(tmp, tnode->nodeValue, l);
485                tmp[l] = '\0';
486                fprintf(stderr, "<%s/domNode0x%p> '%s'\n", node->nodeName,
487                        node, tmp);
488            } else {
489                tmp[0] = '\0';
490                if ((level>=maxlevel) && (node->firstChild)) {
491                    strcpy( tmp, "...");
492                }
493                fprintf(stderr, "<%s/domNode0x%p> %s\n", node->nodeName,
494                        node, tmp);
495            }
496            if (level<maxlevel) {
497                if (node->firstChild) printXML(node->firstChild, level+1,
498                                               maxlevel);
499            }
500        }
501        if (node->nodeType == TEXT_NODE) {
502            tnode = (domTextNode*)node;
503            l = tnode->valueLength;
504            if (l > 70) l = 70;
505            memmove(tmp, tnode->nodeValue, l);
506            tmp[l] = '\0';
507            fprintf(stderr, "'%s'\n", tmp);
508        }
509        if (node->nodeType == COMMENT_NODE) {
510            tnode = (domTextNode*)node;
511            l = tnode->valueLength;
512            memmove (tmp, "<!--", 4);
513            if (l >70) l = 70;
514            memmove (&tmp[4], tnode->nodeValue, l);
515            memmove (&tmp[4+l], "-->", 3);
516            tmp[7+l] = '\0';
517            fprintf(stderr, "'%s'\n", tmp);
518        }
519        if (node->nodeType == PROCESSING_INSTRUCTION_NODE) {
520            pi = (domProcessingInstructionNode*)node;
521            l = pi->targetLength;
522            if (l > 70) l = 70;
523            memmove(tmp, pi->targetValue, l);
524            tmp[l] = '\0';
525            fprintf(stderr, "<?%s ", tmp);
526            l = pi->dataLength;
527            if (l > 70) l = 70;
528            memmove(tmp, pi->dataValue, l);
529            tmp[l] = '\0';
530            fprintf(stderr, "%s?>\n", tmp);
531        }
532        node = node->nextSibling;
533        n++;
534        if (n>8) { fprintf(stderr, "...\n"); return; }
535    }
536}
537
538/*----------------------------------------------------------------------------
539|   reportError
540|
541\---------------------------------------------------------------------------*/
542static void
543reportError (
544    domNode * node,
545    char    * str,
546    char   ** errMsg)
547{
548    Tcl_DString dStr;
549    char buffer[1024];
550    const char *baseURI;
551    int  line, column;
552
553    Tcl_DStringInit (&dStr);
554    baseURI = findBaseURI (node);
555    if (baseURI) {
556        Tcl_DStringAppend (&dStr, "In entity ", 10);
557        Tcl_DStringAppend (&dStr, baseURI, -1);
558    }
559    if (node->nodeFlags & HAS_LINE_COLUMN) {
560        domGetLineColumn (node, &line, &column);
561        sprintf (buffer, " at line %d, column %d:\n", line, column);
562        Tcl_DStringAppend (&dStr, buffer, -1);
563        Tcl_DStringAppend (&dStr, str, -1);
564    } else {
565        if (baseURI) Tcl_DStringAppend (&dStr, ": ", 2);
566        Tcl_DStringAppend (&dStr, str, -1);
567    }
568    if (*errMsg) FREE (*errMsg);
569    *errMsg = tdomstrdup (Tcl_DStringValue (&dStr));
570    Tcl_DStringFree (&dStr);
571}
572
573/*----------------------------------------------------------------------------
574|   getAttr
575|
576\---------------------------------------------------------------------------*/
577static char * getAttr (
578    domNode  *node,
579    char     *name,
580    xsltAttr  attrTypeNo
581)
582{
583    domAttrNode *attr;
584
585    attr = node->firstAttr;
586    while (attr) {
587
588        if (attr->info == (unsigned int)attrTypeNo) {
589            return attr->nodeValue;
590        } else if (attr->info == 0) {
591            if (strcmp ((char*)attr->nodeName, name)==0) {
592                attr->info = (unsigned int)attrTypeNo;
593                return attr->nodeValue;
594            }
595        }
596        attr = attr->nextSibling;
597    }
598    return NULL;
599}
600
601
602/*----------------------------------------------------------------------------
603|   getTag
604|
605\---------------------------------------------------------------------------*/
606static xsltTag getTag (
607    domNode  *node
608)
609{
610    const char *name;
611
612    if (node->nodeType != ELEMENT_NODE) {
613        node->info = (int)unknown;
614        return unknown;
615    }
616    if (node->info != 0) {
617        return (xsltTag)node->info;
618    }
619    name = domNamespaceURI(node);
620    if ((name == NULL) || (strcmp(name, XSLT_NAMESPACE)!=0)) {
621       node->info = (int)unknown;
622       return unknown;
623    }
624    name = domGetLocalName(node->nodeName);
625
626    switch (*name) {
627        case 'a': SET_TAG(node,name,"apply-imports",  applyImports);
628                  SET_TAG(node,name,"apply-templates",applyTemplates);
629                  SET_TAG(node,name,"attribute",      attribute);
630                  SET_TAG(node,name,"attribute-set",  attributeSet);
631                  break;
632        case 'c': SET_TAG(node,name,"call-template",  callTemplate);
633                  SET_TAG(node,name,"choose",         choose);
634                  SET_TAG(node,name,"comment",        comment);
635                  SET_TAG(node,name,"copy",           copy);
636                  SET_TAG(node,name,"copy-of",        copyOf);
637                  break;
638        case 'd': SET_TAG(node,name,"decimal-format", decimalFormat);
639                  break;
640        case 'e': SET_TAG(node,name,"element",        element);
641                  break;
642        case 'f': SET_TAG(node,name,"fallback",       fallback);
643                  SET_TAG(node,name,"for-each",       forEach);
644                  break;
645        case 'i': SET_TAG(node,name,"if",             xsltIf);
646                  SET_TAG(node,name,"import",         import);
647                  SET_TAG(node,name,"include",        include);
648                  break;
649        case 'k': SET_TAG(node,name,"key",            key);
650                  break;
651        case 'm': SET_TAG(node,name,"message",        message);
652                  break;
653        case 'n': SET_TAG(node,name,"namespace-alias",namespaceAlias);
654                  SET_TAG(node,name,"number",         number);
655                  break;
656        case 'o': SET_TAG(node,name,"output",         output);
657                  SET_TAG(node,name,"otherwise",      otherwise);
658                  break;
659        case 'p': SET_TAG(node,name,"param",          param);
660                  SET_TAG(node,name,"preserve-space", preserveSpace);
661                  SET_TAG(node,name,"processing-instruction", procinstr);
662                  break;
663        case 's': SET_TAG(node,name,"sort",           sort);
664                  SET_TAG(node,name,"stylesheet",     stylesheet);
665                  SET_TAG(node,name,"strip-space",    stripSpace);
666                  break;
667        case 't': SET_TAG(node,name,"template",       template);
668                  SET_TAG(node,name,"text",           text);
669                  SET_TAG(node,name,"transform",      transform);
670                  break;
671        case 'v': SET_TAG(node,name,"value-of",       valueOf);
672                  SET_TAG(node,name,"variable",       variable);
673                  break;
674        case 'w': SET_TAG(node,name,"when",           when);
675                  SET_TAG(node,name,"with-param",     withParam);
676                  break;
677    }
678    node->info = (int)unknown;
679    return unknown;
680}
681
682
683/*----------------------------------------------------------------------------
684|   xsltPopVarFrame
685|
686\---------------------------------------------------------------------------*/
687static void xsltPopVarFrame (
688    xsltState  * xs
689)
690{
691    int             i;
692    xsltVarFrame   *frame;
693
694    if (xs->varFramesStackPtr >= 0) {
695        frame = &xs->varFramesStack[xs->varFramesStackPtr];
696        if (frame->nrOfVars) {
697            for (i = frame->varStartIndex;
698                 i < frame->varStartIndex + frame->nrOfVars;
699                 i++) {
700                xpathRSFree (&(&xs->varStack[i])->rs);
701            }
702        }
703        xs->varStackPtr -= frame->nrOfVars;
704        xs->varFramesStackPtr--;
705    }
706}
707
708
709/*----------------------------------------------------------------------------
710|   xsltPushVarFrame
711|
712\---------------------------------------------------------------------------*/
713static void xsltPushVarFrame (
714    xsltState  * xs
715)
716{
717    xsltVarFrame  * frame;
718
719    xs->varFramesStackPtr++;
720    if (xs->varFramesStackPtr >= xs->varFramesStackLen) {
721        xs->varFramesStack = (xsltVarFrame *) REALLOC ((char*)xs->varFramesStack,
722                                                        sizeof (xsltVarFrame)
723                                                        * 2 * xs->varFramesStackLen);
724        xs->varFramesStackLen *= 2;
725    }
726    frame = &(xs->varFramesStack[xs->varFramesStackPtr]);
727    frame->polluted = 0;
728    frame->nrOfVars = 0;
729    frame->varStartIndex = -1;
730    frame->stop = 0;
731}
732
733
734/*----------------------------------------------------------------------------
735|   xsltAddExternalDocument
736|
737\---------------------------------------------------------------------------*/
738static int xsltAddExternalDocument (
739    xsltState       * xs,
740    const char      * baseURI,
741    const char      * str,
742    int               fixedXMLSource,
743    xpathResultSet  * result,
744    char           ** errMsg
745)
746{
747    xsltSubDoc  * sdoc;
748    domDocument * extDocument;
749    int           found;
750
751    DBG(
752        fprintf (stderr, "xsltAddExternalDocument: baseURI '%s'\n", baseURI);
753        fprintf (stderr, "xsltAddExternalDocument: systemID '%s'\n", str);
754    )
755
756    found = 0;
757    sdoc = xs->subDocs;
758    if (str) {
759        while (sdoc) {
760            if (!sdoc->isStylesheet
761                && sdoc->baseURI
762                && strcmp (sdoc->baseURI, str)==0) {
763                rsAddNode (result, sdoc->doc->rootNode);
764                found = 1;
765                break;
766            }
767            sdoc = sdoc->next;
768        }
769    }
770    if (!found) {
771        if (!xs->xsltDoc->extResolver) {
772            *errMsg = tdomstrdup("need resolver Script to include Stylesheet! "
773                                 "(use \"-externalentitycommand\")");
774            return -1;
775        }
776        extDocument = getExternalDocument (
777                         (Tcl_Interp*)xs->orig_funcClientData,
778                         xs, xs->xsltDoc, baseURI, str, 0, fixedXMLSource,
779                         errMsg);
780        if (extDocument) {
781            rsAddNode (result, extDocument->rootNode);
782        } else {
783            return -1;
784        }
785    }
786    return found;
787}
788
789
790/*----------------------------------------------------------------------------
791|   xsltNumberFormatTokenizer
792|
793\---------------------------------------------------------------------------*/
794static xsltNumberFormat* xsltNumberFormatTokenizer (
795    xsltState  *xs,
796    char       *formatStr,
797    char      **errMsg
798)
799{
800    char             *p;
801    int               hnew, clen, nrOfTokens = 0;
802    Tcl_HashEntry    *h;
803    xsltNumberFormat *format;
804
805    /* TODO: make it l18n aware. */
806
807    h = Tcl_CreateHashEntry (&xs->formats, formatStr, &hnew);
808    if (!hnew) {
809        return (xsltNumberFormat *) Tcl_GetHashValue (h);
810    } else {
811        format = (xsltNumberFormat *)MALLOC(sizeof (xsltNumberFormat));
812        memset (format, 0 , sizeof (xsltNumberFormat));
813        format->tokens = (xsltNumberFormatToken *)
814            MALLOC(sizeof (xsltNumberFormatToken) * 20);
815        memset (format->tokens, 0, sizeof (xsltNumberFormatToken) * 20);
816        format->maxtokens = 20;
817        Tcl_SetHashValue (h, format);
818        format->formatStr = p = Tcl_GetHashKey (&(xs->formats), h);
819    }
820    while (*p) {
821        clen = UTF8_CHAR_LEN(*p);
822        if (!clen) {
823            reportError (xs->currentXSLTNode, "xsl:number: UTF-8 form of"
824                         " character longer than 3 Byte", errMsg);
825            return NULL;
826        }
827        if (clen > 1) {
828            /* hack: skip over multibyte chars - this may be wrong */
829            format->prologLen += clen;
830            p += clen;
831            continue;
832        }
833        if (isalnum((unsigned char)*p)) break;
834        format->prologLen++;
835        p++;
836    }
837
838    format->tokens[0].minlength = 1;
839    if (!*p) {
840        format->tokens[0].type = latin_number;
841        return format;
842    }
843
844#define addSeperator  \
845    p++;                                         \
846    if (*p) {                                    \
847        format->tokens[nrOfTokens].sepStart = p; \
848    }                                            \
849    while (*p) {                                 \
850        clen = UTF8_CHAR_LEN(*p);                \
851        if (!clen) {                             \
852            reportError (xs->currentXSLTNode, "xsl:number: UTF-8 form of character longer than 3 Byte", errMsg); \
853            return NULL;                         \
854        }                                        \
855        if (clen > 1) {                          \
856            /* hack: skip over multibyte chars - this may be wrong */ \
857            format->tokens[nrOfTokens].sepLen += clen;  \
858            p += clen;                           \
859            continue;                            \
860        }                                        \
861        if (isalnum((unsigned char)*p)) break;                  \
862        format->tokens[nrOfTokens].sepLen++;     \
863        p++;                                     \
864    }                                            \
865    if (*p) {                                    \
866        if (format->tokens[nrOfTokens].sepLen == 0) goto wrongSyntax; \
867    }                                            \
868    nrOfTokens++;                                \
869    if (nrOfTokens == format->maxtokens) {       \
870        format->tokens = (xsltNumberFormatToken *) REALLOC ((char *)format->tokens, sizeof (xsltNumberFormatToken) * format->maxtokens * 2);  \
871        format->maxtokens *= 2;                  \
872    }                                            \
873    format->tokens[nrOfTokens].minlength = 1;    \
874    continue;
875
876    while (*p) {
877        if (*p == '0') {
878            format->tokens[nrOfTokens].minlength++;
879            p++;
880            continue;
881        }
882        if (*p == '1') {
883            format->tokens[nrOfTokens].type = latin_number;
884            addSeperator;
885        }
886        if (*p == 'A') {
887            if (isalnum((unsigned char)*(p+1))) goto wrongSyntax;
888            format->tokens[nrOfTokens].type = latin_upper;
889            addSeperator;
890        }
891        if (*p == 'a') {
892            if (isalnum((unsigned char)*(p+1))) goto wrongSyntax;
893            format->tokens[nrOfTokens].type = latin_lower;
894            addSeperator;
895        }
896        if (*p == 'I') {
897            if (isalnum((unsigned char)*(p+1))) goto wrongSyntax;
898            format->tokens[nrOfTokens].type = roman_upper;
899            addSeperator;
900        }
901        if (*p == 'i') {
902            if (isalnum((unsigned char)*(p+1))) goto wrongSyntax;
903            format->tokens[nrOfTokens].type = roman_lower;
904            addSeperator;
905        }
906        format->tokens[nrOfTokens].type = latin_number;
907        while (isalnum((unsigned char)*(p+1))) {
908            p++;
909        }
910        addSeperator;
911    }
912    format->epilogStart = format->tokens[nrOfTokens-1].sepStart;
913    format->tokens[nrOfTokens-1].sepStart = NULL;
914    format->epilogLen = format->tokens[nrOfTokens-1].sepLen;
915    format->tokens[nrOfTokens-1].sepLen = 0;
916    return format;
917
918 wrongSyntax:
919    reportError (xs->currentXSLTNode, "xsl:number: Wrong syntax in"
920                 " format attribute", errMsg);
921    return NULL;
922}
923
924/*----------------------------------------------------------------------------
925|   formatValue
926|
927\---------------------------------------------------------------------------*/
928static void formatValue (
929    xsltNumberFormat *f,
930    int              *useFormatToken,
931    int               value,
932    Tcl_DString      *str,
933    char             *groupingSeparator,
934    long              groupingSize,
935    int               addSeperater
936)
937{
938    int         len, fulllen, gslen, upper = 0, e, m, b, i, z, v;
939    char        tmp[80], *pt;
940    Tcl_DString tmp1;
941    static struct { char *digit; char *ldigit; int value; } RomanDigit[] = {
942          { "M" , "m" , 1000, },
943          { "CM", "cm",  900, },
944          { "D" , "d" ,  500, },
945          { "CD", "cd",  400, },
946          { "C" , "c" ,  100, },
947          { "XC", "xc",   90, },
948          { "L" , "l" ,   50, },
949          { "XL", "xl",   40, },
950          { "X" , "x" ,   10, },
951          { "IX", "ix",    9, },
952          { "V" , "v" ,    5, },
953          { "IV", "iv",    4, },
954          { "I" , "i" ,    1  }
955    };
956
957    switch (f->tokens[*useFormatToken].type) {
958    case latin_number:
959        sprintf (tmp, "%d", value);
960        fulllen = len = strlen (tmp);
961        if (f->tokens[*useFormatToken].minlength > fulllen) {
962            fulllen = f->tokens[*useFormatToken].minlength;
963        }
964        if (groupingSeparator) {
965            gslen = strlen (groupingSeparator);
966            Tcl_DStringInit (&tmp1);
967            if (len < f->tokens[*useFormatToken].minlength) {
968                for (i = 0; i <  f->tokens[*useFormatToken].minlength - len; i++) {
969                    Tcl_DStringAppend (&tmp1, "0", 1);
970                }
971            }
972            Tcl_DStringAppend (&tmp1, tmp, len);
973            pt = Tcl_DStringValue (&tmp1);
974            len = Tcl_DStringLength (&tmp1);
975            m = len % groupingSize;
976            if (m) {
977                Tcl_DStringAppend (str, pt, m);
978                pt += m;
979            }
980            i = len - m;
981            while (i) {
982                if (i != len) {
983                    Tcl_DStringAppend (str, groupingSeparator, gslen);
984                }
985                Tcl_DStringAppend (str, pt, groupingSize);
986                pt += groupingSize;
987                i -= groupingSize;
988            }
989            Tcl_DStringFree (&tmp1);
990        } else {
991            for (i = 0; i < fulllen - len; i++) {
992                Tcl_DStringAppend (str, "0", 1);
993            }
994            Tcl_DStringAppend (str, tmp, len);
995        }
996        goto appendSeperator;
997        break;
998
999    case latin_upper:
1000        upper = 1;
1001        /* fall thru */
1002    case latin_lower:
1003        /* Home grown algorithm. (And I'm really not happy with it.)
1004           Please let rolf@pointsman.de know how to do this better /
1005           faster / more clever. */
1006
1007        if (value <= 0) {
1008            /* Hm, zero can't be expressed with letter sequences...
1009               What to do? One of the several cases, not mentioned
1010               by the spec. */
1011            /* fall back to latin numbers */
1012            sprintf (tmp, "%d", value);
1013            break;
1014        }
1015        e = 1;
1016        m = b = 26;
1017        while (value > m) {
1018            b *= 26;
1019            m += b;
1020            e++;
1021        }
1022        m -= b;
1023        value -= m;
1024        for (i = 0; i < e; i++) {
1025            b /= 26;
1026            z = value / b;
1027            value = value - z*b;
1028            if (i < e -1) {
1029                if (value == 0) {
1030                    value += b;
1031                } else {
1032                    z++;
1033                }
1034            }
1035            if (upper) {
1036                tmp[i] = 64+z;
1037            } else {
1038                tmp[i] = 96+z;
1039            }
1040        }
1041        tmp[i] = '\0';
1042        break;
1043
1044    case roman_upper:
1045        upper = 1;
1046        /* fall thru */
1047    case roman_lower:
1048        /* Algorithm follows the idear of the converter
1049           at http://mini.net/cgi-bin/wikit/1749.html */
1050
1051        /* Side note: There exists a rarely used roman notation
1052           to express figures up to a few millions. Does somebody
1053           really need this? */
1054
1055        if (value > 3999 || value <= 0) {
1056            /* fall back to latin numbers */
1057            sprintf (tmp, "%d", value);
1058            break;
1059        }
1060        if (value == 0) {
1061            /* what to do with zero??? */
1062            sprintf (tmp, "%d", 0);
1063            break;
1064        }
1065        v = 0;  tmp[0] = '\0';
1066        while (value > 0) {
1067            while (value >= RomanDigit[v].value) {
1068                if (upper) { strcat(tmp,  RomanDigit[v].digit);  }
1069                      else { strcat(tmp,  RomanDigit[v].ldigit); }
1070                value -= RomanDigit[v].value;
1071            }
1072            v++;
1073        }
1074        break;
1075
1076    default:
1077        sprintf (tmp, "%d", value);
1078        break;
1079    }
1080    len = strlen (tmp);
1081    Tcl_DStringAppend (str, tmp, len);
1082 appendSeperator:
1083    if (addSeperater) {
1084        if (f->tokens[*useFormatToken].sepStart) {
1085            Tcl_DStringAppend (str, f->tokens[*useFormatToken].sepStart,
1086                               f->tokens[*useFormatToken].sepLen);
1087            *useFormatToken += 1;
1088        } else {
1089            if (*useFormatToken > 0) {
1090                Tcl_DStringAppend (str, f->tokens[*useFormatToken-1].sepStart,
1091                                   f->tokens[*useFormatToken-1].sepLen);
1092            } else {
1093                /* insert default seperator '.' */
1094                Tcl_DStringAppend (str, ".", 1);
1095            }
1096        }
1097    }
1098
1099    return;
1100}
1101
1102/*----------------------------------------------------------------------------
1103|   xsltFormatNumber
1104|
1105\---------------------------------------------------------------------------*/
1106#if TclOnly8Bits
1107static int xsltFormatNumber (
1108    double              number,
1109    char              * formatStr,
1110    xsltDecimalFormat * df,
1111    char             ** resultStr,
1112    int               * resultLen,
1113    char             ** errMsg
1114)
1115{
1116    char *p, prefix[800], suffix[800], s[2400], n[800], f[800];
1117    char *negformat = NULL, save = '\0', save1;
1118    int i, l, zl, g, nHash, nZero, fHash, fZero, gLen, isNeg;
1119/*      struct lconv *lc = NULL;  */
1120    char wrongFormat[] = "Unable to interpret format pattern.";
1121
1122    DBG(fprintf(stderr, "\nformatStr='%s' \n", formatStr);)
1123    if (number < 0.0) {
1124        isNeg = 1;
1125        number *= -1.0;
1126    } else {
1127        isNeg = 0;
1128    }
1129    p = formatStr;
1130    while (*p) {
1131        if (*p == df->patternSeparator) {
1132            *p = '\0';
1133            negformat = ++p;
1134            break;
1135        }
1136        p++;
1137    }
1138    /* Check for more than one patternSeparator in the formatStr */
1139    while (*p) {
1140        if (*p == df->patternSeparator) {
1141            *errMsg = tdomstrdup(wrongFormat);
1142            return -1;
1143        }
1144        p++;
1145    }
1146    p = formatStr;
1147
1148    i = 0;
1149    while (*p
1150           && (*p!=df->zeroDigit)
1151           && (*p!=df->digit)
1152           && (*p!=df->groupingSeparator)
1153           && (*p!=df->decimalSeparator)) {
1154        if (i<79) { prefix[i++] = *p; }
1155        p++;
1156    }
1157    prefix[i] = '\0';
1158    nHash = nZero = fHash = fZero = 0;
1159    gLen = -2222;
1160    while (*p) {
1161             if (*p==df->digit) {
1162                 if (nZero) {*errMsg = tdomstrdup(wrongFormat); return -1;}
1163                 nHash++;}
1164        else if (*p==df->zeroDigit) { nZero++; }
1165        else if (*p==df->groupingSeparator) { gLen=-1; }
1166        else break;
1167        p++; gLen++;
1168    }
1169    if (*p && (*p==df->decimalSeparator)) {
1170        p++;
1171        while (*p && (*p==df->zeroDigit)) { p++; fZero++; }
1172        while (*p && (*p==df->digit)) { p++; fHash++; }
1173    }
1174    i = 0;
1175    while (*p) {
1176        if (i<79) { suffix[i++] = *p; }
1177        p++;
1178    }
1179    suffix[i] = '\0';
1180    if (save) *p = save;
1181
1182    if (isNeg && negformat) {
1183        /* Only prefix and suffix are taken from the second format string */
1184        p++;
1185        i = 0;
1186        while (*p
1187               && *p!=df->zeroDigit
1188               && *p!=df->digit
1189               && *p!=df->groupingSeparator
1190               && *p!=df->decimalSeparator) {
1191            if (i<79) { prefix[i++] = *p; }
1192            p++;
1193        }
1194        prefix[i] = '\0';
1195        while (*p
1196               && ((*p==df->zeroDigit)
1197                   || (*p==df->digit)
1198                   || (*p==df->groupingSeparator)
1199                   || (*p==df->decimalSeparator))) p++;
1200        i = 0;
1201        while (*p) {
1202            if (i<79) { suffix[i++] = *p; }
1203            p++;
1204        }
1205        suffix[i] = '\0';
1206    }
1207
1208    if (isNeg) {
1209        if (negformat) {
1210            if (prefix[0]=='\0' && suffix[0]=='\0') {
1211                prefix[0] = df->minusSign;
1212                prefix[1] = '\0';
1213            }
1214        } else {
1215            i = 0;
1216            save = prefix[0];
1217            prefix[0] = df->minusSign;
1218            while (i < 79) {
1219                i++;
1220                if (save == '\0') {
1221                    prefix[i] = save;
1222                    break;
1223                }
1224                save1 = prefix[i];
1225                prefix[i] = save;
1226                save = save1;
1227            }
1228            if (i == 79) prefix[79] = '\0';
1229        }
1230    }
1231    if (prefix[0]=='\xc2' && prefix[1]=='\xa4') {
1232/*          lc = localeconv(); */
1233/*          if (strlen (lc->currency_symbol) > 79 */
1234/*              || lc->currency_symbol[0] == '\0') { */
1235            prefix[0] = '$';
1236            prefix[1] = '\0';
1237/*          } else { */
1238/*              strcpy (prefix, lc->currency_symbol); */
1239/*          } */
1240    }
1241
1242    if (suffix[0] == df->percent) {
1243        number *= 100.0;
1244    } else
1245    if (suffix[0]=='\xe2' && suffix[1]=='\x80' && suffix[2]=='\xb0') {
1246        number *= 1000.0;
1247    }
1248
1249    if (fHash + fZero == 0) {
1250        i = (int) (number+0.5);
1251    } else {
1252        i = (int) number;
1253    }
1254    DBG(fprintf(stderr,"normal part nZero=%d i=%d glen=%d\n", nZero, i, gLen);)
1255    /* fill in grouping char */
1256    if (gLen > 0) {
1257        if (i < 0.0) {isNeg = 1; i *= -1;}
1258        else isNeg = 0;
1259        sprintf(s,"%0*d", nZero, i);
1260        l = strlen(s);
1261        /* if (l > (nHash+nZero)) { l = nHash+nZero; } */
1262        DBG(fprintf(stderr,"s='%s isNeg=%d'\n", s, isNeg);)
1263        zl = l + ((l-1) / gLen);
1264        DBG(fprintf(stderr, "l=%d zl=%d \n", l, zl);)
1265        n[zl--] = '\0';
1266        p = s + strlen(s) -1;
1267        g = 0;
1268        while (zl>=0) {
1269            g++;
1270            n[zl--] = *p--;
1271            if ((g == gLen) && (zl>=1)) {
1272                n[zl--] = df->groupingSeparator;
1273                g = 0;
1274            }
1275        }
1276        DBG(fprintf(stderr,"s='%s' --> n='%s'\n", s, n);)
1277
1278    } else {
1279        sprintf(n,"%0*d", nZero, i);
1280        DBG(fprintf(stderr,"n='%s'\n", n);)
1281    }
1282
1283    DBG(fprintf(stderr, "number=%f Hash=%d fZero=%d \n", number, fHash, fZero);)
1284    if ((fHash+fZero) > 0) {
1285        i = (int) number;
1286        /* format fraction part */
1287        if (number >= 0.0) {
1288            sprintf(f,"%0.*f", fZero+fHash, number -i);
1289        } else {
1290            sprintf(f,"%0.*f", fZero+fHash, -1.0 * (number -i) );
1291        }
1292        l = strlen(f);
1293        while (l>0 && fHash>0) {   /* strip not need 0's */
1294            if (f[l-1] == '0') {
1295                f[l-1]='\0'; l--; fHash--;
1296            } else {
1297                break;
1298            }
1299        }
1300        DBG(fprintf(stderr, "f='%s'\n", f);)
1301        sprintf(s,"%s%s%c%s%s", prefix, n, df->decimalSeparator, &(f[2]), suffix);
1302    } else {
1303        sprintf(s,"%s%s%s", prefix, n, suffix);
1304    }
1305    DBG(fprintf(stderr, "returning s='%s' \n\n", s);)
1306    *resultStr = tdomstrdup(s);
1307    *resultLen = strlen(s);
1308    return 0;
1309}
1310
1311#else
1312
1313static int addCurrencySymbol (
1314    Tcl_UniChar  *p,
1315    Tcl_UniChar  *result,
1316    int          *i
1317)
1318{
1319    Tcl_DString dStr;
1320    Tcl_UniChar *p1, *currencySymbol;
1321    int move = 0;
1322    struct lconv *lc;
1323
1324    setlocale (LC_MONETARY, "");
1325    lc = localeconv();
1326    Tcl_DStringInit (&dStr);
1327    if (*(p+1) == 0xa4) {
1328        if (lc->int_curr_symbol[0] == '\0') {
1329            currencySymbol = Tcl_UtfToUniCharDString ("$", -1, &dStr);
1330        } else {
1331            currencySymbol =
1332                Tcl_UtfToUniCharDString (lc->int_curr_symbol, -1, &dStr);
1333        }
1334        move = 1;
1335    } else {
1336        if (lc->currency_symbol[0] == '\0') {
1337            currencySymbol = Tcl_UtfToUniCharDString ("$", -1, &dStr);
1338        } else {
1339            currencySymbol =
1340                Tcl_UtfToUniCharDString (lc->currency_symbol, -1, &dStr);
1341        }
1342    }
1343    p1 = currencySymbol;
1344    while (*p1 && (*i < 79)) {
1345        result[(*i)++] = *p1;
1346        p1++;
1347    }
1348    Tcl_DStringFree (&dStr);
1349    return move;
1350}
1351
1352static int xsltFormatNumber (
1353    double              number,
1354    char              * formatStr,
1355    xsltDecimalFormat * df,
1356    char             ** resultStr,
1357    int               * resultLen,
1358    char             ** errMsg
1359)
1360{
1361    Tcl_UniChar prefix1[800], prefix2[800], suffix1[800], suffix2[800];
1362    Tcl_UniChar save = '\0', save1, t, *prefix, *suffix, n[800], f[800];
1363    Tcl_UniChar uniCharNull = '\0';
1364    char stmp[240], ftmp[80];
1365    char wrongFormat[] = "Unable to interpret format pattern.";
1366    int i, j, k, l, zl, g, nHash, nZero, fHash, fZero, gLen, isNeg;
1367    int prefixMinux, percentMul = 0, perMilleMul = 0;
1368    Tcl_DString  dStr, s;
1369    Tcl_UniChar *format, *negformat = NULL, *p, *p1;
1370    DBG(Tcl_DString dbStr;)
1371
1372    DBG(fprintf(stderr, "number: '%f'\nformatStr='%s' \n", number, formatStr);)
1373    prefix1[0] = '\0';
1374    prefix2[0] = '\0';
1375    suffix1[0] = '\0';
1376    suffix2[0] = '\0';
1377    n[0] = '\0';
1378    f[0] = '\n';
1379    prefix = NULL;
1380    suffix = NULL;
1381    Tcl_DStringInit (&s);
1382    Tcl_DStringInit (&dStr);
1383    if (number < 0.0) {
1384        isNeg = 1;
1385        number *= -1.0;
1386    } else if (number == 0.0) {
1387        sprintf (stmp, "%f", number);
1388        if (stmp[0] == '-') isNeg = 1;
1389        else isNeg = 0;
1390    } else {
1391        isNeg = 0;
1392    }
1393    format = Tcl_UtfToUniCharDString (formatStr, -1, &dStr);
1394    p = format;
1395    while (*p) {
1396        if (*p == df->patternSeparator) {
1397            save = *p;
1398            *p = '\0';
1399            negformat = ++p;
1400            break;
1401        }
1402        p++;
1403    }
1404    /* Check for more than one patternSeparator in the formatStr */
1405    while (*p) {
1406        if (*p == df->patternSeparator) {
1407            *errMsg =
1408                tdomstrdup("More than one patternSeparator in the pattern");
1409            goto xsltFormatNumberError;
1410        }
1411        p++;
1412    }
1413    p = format;
1414
1415    i = 0;
1416    while (*p
1417           && (*p!=df->zeroDigit)
1418           && (*p!=df->digit)
1419           && (*p!=df->groupingSeparator)
1420           && (*p!=df->decimalSeparator)) {
1421        if (*p == df->percent) (percentMul = 1);
1422        else if (*p == df->perMille) (perMilleMul = 1);
1423        if (i<79) {
1424            if (*p == 0xa4) {
1425                p += addCurrencySymbol (p, prefix1, &i);
1426           } else {
1427                prefix1[i++] = *p;
1428            }
1429        }
1430        p++;
1431    }
1432    prefix1[i] = '\0';
1433    nHash = nZero = fHash = fZero = 0;
1434    gLen = -2222;
1435    while (*p) {
1436        if (*p==df->digit) {
1437            if (nZero) {
1438                *errMsg = tdomstrdup(wrongFormat);
1439                goto xsltFormatNumberError;
1440            }
1441            nHash++;
1442        }
1443        else if (*p==df->zeroDigit) { nZero++; }
1444        else if (*p==df->groupingSeparator) { gLen=-1; }
1445        else break;
1446        p++; gLen++;
1447    }
1448    if (*p && (*p==df->decimalSeparator)) {
1449        p++;
1450        while (*p && (*p==df->zeroDigit)) { p++; fZero++; }
1451        while (*p && (*p==df->digit)) { p++; fHash++; }
1452    }
1453    i = 0;
1454    while (*p) {
1455        /* Check for more than one decimalSeparator */
1456        if (*p == df->decimalSeparator) {
1457            *errMsg =
1458                tdomstrdup("More than one decimalSeparator in subpattern");
1459            goto xsltFormatNumberError;
1460        }
1461        /* Check for groupingSeparator after decimalSeparator */
1462        if (*p == df->groupingSeparator) {
1463            *errMsg = tdomstrdup("GroupingSeparator after decimalSeparator");
1464            goto xsltFormatNumberError;
1465        }
1466        if (*p == df->percent) (percentMul = 1);
1467        else if (*p == df->perMille) (perMilleMul = 1);
1468        if (i<79) {
1469            if (*p == 0xa4) {
1470                p += addCurrencySymbol (p, suffix1, &i);
1471            } else {
1472                suffix1[i++] = *p;
1473            }
1474        }
1475        p++;
1476    }
1477    suffix1[i] = '\0';
1478    if (save) *p = save;
1479
1480    if (isNeg && negformat) {
1481        /* Only prefix and suffix are taken from the second format string */
1482        percentMul = 0; perMilleMul = 0;
1483        p++;
1484        i = 0;
1485        while (*p
1486               && *p!=df->zeroDigit
1487               && *p!=df->digit
1488               && *p!=df->groupingSeparator
1489               && *p!=df->decimalSeparator) {
1490            if (*p == df->percent) (percentMul = 1);
1491            else if (*p == df->perMille) (perMilleMul = 1);
1492            if (i<79) {
1493                if (*p == 0xa4) {
1494                    p += addCurrencySymbol (p, prefix2, &i);
1495                } else {
1496                    prefix2[i++] = *p;
1497                }
1498            }
1499            p++;
1500        }
1501        prefix2[i] = '\0';
1502        while (*p
1503               && ((*p==df->zeroDigit)
1504                   || (*p==df->digit)
1505                   || (*p==df->groupingSeparator)
1506                   || (*p==df->decimalSeparator))) p++;
1507        i = 0;
1508        while (*p) {
1509            if (*p == df->percent) (percentMul = 1);
1510            else if (*p == df->perMille) (perMilleMul = 1);
1511            if (i<79) {
1512                if (*p == 0xa4) {
1513                    p += addCurrencySymbol (p, suffix2, &i);
1514                } else {
1515                    suffix2[i++] = *p;
1516                }
1517            }
1518            p++;
1519        }
1520        suffix2[i] = '\0';
1521    }
1522
1523    if (isNeg) {
1524        if (negformat) {
1525            prefixMinux = 1;
1526            p = prefix1;
1527            p1 = prefix2;
1528            while (prefixMinux) {
1529                if (*p != *p1) {
1530                    prefixMinux = 0;
1531                    break;
1532                }
1533                if (*p == 0) break;
1534                p++; p1++;
1535            }
1536            if (prefixMinux) {
1537                p = suffix1;
1538                p1 = suffix2;
1539                while (prefixMinux) {
1540                    if (*p != *p1) {
1541                        prefixMinux = 0;
1542                        break;
1543                    }
1544                    if (*p == 0) break;
1545                    p++; p1++;
1546                }
1547            }
1548            prefix = prefix2;
1549            suffix = suffix2;
1550        } else {
1551            prefixMinux = 1;
1552            prefix = prefix1;
1553            suffix = suffix1;
1554        }
1555        if (prefixMinux) {
1556            i = 0;
1557            save = prefix[0];
1558            prefix[0] = df->minusSign;
1559            while (i < 79) {
1560                i++;
1561                save1 = prefix[i];
1562                prefix[i] = save;
1563                if (save == 0) break;
1564                save = save1;
1565            }
1566            if (i == 79) prefix[79] = '\0';
1567        }
1568    } else {
1569        prefix = prefix1;
1570        suffix = suffix1;
1571    }
1572
1573    DBG(
1574        Tcl_DStringInit (&dbStr);
1575        fprintf (stderr, "prefix: '%s' ", Tcl_UniCharToUtfDString(prefix, Tcl_UniCharLen (prefix), &dbStr));
1576        Tcl_DStringFree (&dbStr);
1577        Tcl_DStringInit (&dbStr);
1578        fprintf (stderr, "suffix: '%s'\n", Tcl_UniCharToUtfDString(suffix, Tcl_UniCharLen (suffix), &dbStr));
1579        Tcl_DStringFree (&dbStr);
1580    )
1581
1582    if (percentMul) {
1583        number *= 100.0;
1584    } else if (perMilleMul) {
1585        number *= 1000.0;
1586    }
1587
1588    if (fHash + fZero == 0) {
1589        i = (int) (number+0.5);
1590    } else {
1591        i = (int) number;
1592        /* format fraction part */
1593        DBG(fprintf(stderr, "formating fraction part: '%f', fZero+fHash: '%d'\n",
1594                    number - i, fZero+fHash);)
1595        sprintf(ftmp,"%.*f", fZero+fHash, number -i);
1596        DBG(fprintf(stderr, "raw formated fraction part: '%s'\n", ftmp);)
1597        if (ftmp[0] == '1') {
1598            i++;
1599        }
1600    }
1601
1602    DBG(fprintf(stderr,"normal part nZero=%d i=%d glen=%d\n", nZero, i, gLen);)
1603    /* fill in grouping char */
1604    if (gLen > 0) {
1605        sprintf(stmp,"%0*d", nZero, i);
1606        l = strlen (stmp);
1607        for (j = 0; j < l; j++) {
1608            t = df->zeroDigit + stmp[j] - 48;
1609            Tcl_DStringAppend (&s, (char*)&t, sizeof (Tcl_UniChar));
1610        }
1611        DBG(
1612            Tcl_DStringInit(&dbStr);
1613            fprintf (stderr, "'%s' ---> ..\n", stmp);
1614            fprintf(stderr,"s='%s' isNeg=%d'\n",
1615                    Tcl_UniCharToUtfDString (
1616                        (Tcl_UniChar*)Tcl_DStringValue (&s),
1617                        Tcl_UniCharLen((Tcl_UniChar*)Tcl_DStringValue(&s)), &dStr
1618                        ),
1619                    isNeg);
1620            Tcl_DStringFree (&dbStr);
1621        )
1622        zl = l + ((l-1) / gLen);
1623        DBG(fprintf(stderr, "l=%d zl=%d \n", l, zl);)
1624        n[zl--] = '\0';
1625        p = (Tcl_UniChar*)Tcl_DStringValue (&s) + l - 1;
1626        g = 0;
1627        while (zl>=0) {
1628            g++;
1629            n[zl--] = *p--;
1630            if ((g == gLen) && (zl>=1)) {
1631                n[zl--] = df->groupingSeparator;
1632                g = 0;
1633            }
1634        }
1635        Tcl_DStringSetLength (&s, 0);
1636        DBG(
1637            Tcl_DStringInit (&dbStr);
1638            fprintf(stderr,"s='%s' --> ",
1639                    Tcl_UniCharToUtfDString (
1640                        (Tcl_UniChar*)Tcl_DStringValue (&s),
1641                        Tcl_UniCharLen((Tcl_UniChar*)Tcl_DStringValue(&s)),
1642                        &dStr));
1643            Tcl_DStringFree (&dbStr);
1644            Tcl_DStringInit (&dbStr);
1645            fprintf(stderr,"n='%s'\n",
1646                    Tcl_UniCharToUtfDString (n, Tcl_UniCharLen (n), &dbStr));
1647            Tcl_DStringFree (&dbStr);
1648        )
1649    } else {
1650        sprintf(stmp,"%0*d", nZero, i);
1651        l = strlen (stmp);
1652        for (j = 0; j < l; j++) {
1653            n[j] = df->zeroDigit + (int) stmp[j] - 48;
1654        }
1655        n[l] = '\0';
1656        DBG(
1657            Tcl_DStringInit (&dbStr);
1658            fprintf(stderr,"n='%s'\n",
1659                    Tcl_UniCharToUtfDString(n, Tcl_UniCharLen (n), &dbStr));
1660            Tcl_DStringFree (&dbStr);
1661        )
1662    }
1663    DBG(fprintf(stderr, "number=%f fHash=%d fZero=%d \n", number, fHash,
1664                fZero);)
1665    if ((fHash+fZero) > 0) {
1666        l = strlen(ftmp);
1667        while (l>0 && fHash>0) {   /* strip not need 0's */
1668            if (ftmp[l-1] == '0') {
1669                ftmp[l-1]='\0'; l--; fHash--;
1670            } else {
1671                break;
1672            }
1673        }
1674        k = 0;
1675        if ((number - i != 0.0) || (fZero > 0)) {
1676            while (ftmp[k] != '.') k++;
1677            k++;
1678            for (j = k ; j < l; j++) {
1679                f[j] = df->zeroDigit + (int) ftmp[j] - 48;
1680            }
1681            f[l] = '\0';
1682        }
1683        DBG(fprintf(stderr, "f='%s'\n", f);)
1684
1685        if (prefix) {
1686            Tcl_DStringAppend (&s, (char*) prefix,
1687                               Tcl_UniCharLen (prefix) * sizeof(Tcl_UniChar));
1688        }
1689        Tcl_DStringAppend (&s, (char*) n,
1690                           Tcl_UniCharLen (n) * sizeof(Tcl_UniChar));
1691        if (k) {
1692            Tcl_DStringAppend (&s, (char*)&df->decimalSeparator,
1693                               sizeof (Tcl_UniChar));
1694            Tcl_DStringAppend (&s, (char*)&(f[k]),
1695                               Tcl_UniCharLen (&(f[k])) * sizeof(Tcl_UniChar));
1696        }
1697        if (suffix) {
1698            Tcl_DStringAppend (&s, (char *) suffix,
1699                               Tcl_UniCharLen (suffix) * sizeof(Tcl_UniChar));
1700        }
1701        Tcl_DStringAppend (&s, (char *)&uniCharNull, sizeof (Tcl_UniChar));
1702    } else {
1703        if (prefix) {
1704            Tcl_DStringAppend (&s, (char*) prefix,
1705                               Tcl_UniCharLen (prefix) * sizeof(Tcl_UniChar));
1706        }
1707        Tcl_DStringAppend (&s, (char*) n,
1708                           Tcl_UniCharLen (n) * sizeof(Tcl_UniChar));
1709        if (suffix) {
1710            Tcl_DStringAppend (&s, (char *) suffix,
1711                               Tcl_UniCharLen (suffix) * sizeof(Tcl_UniChar));
1712        }
1713        Tcl_DStringAppend (&s, (char *)&uniCharNull, sizeof (Tcl_UniChar));
1714    }
1715    DBG(
1716        Tcl_DStringInit (&dbStr);
1717        fprintf(stderr, "returning s='%s' \n\n",
1718                Tcl_UniCharToUtfDString(
1719                    (Tcl_UniChar*)Tcl_DStringValue (&s),
1720                    Tcl_UniCharLen((Tcl_UniChar*)Tcl_DStringValue(&s)), &dStr
1721                    ));
1722        Tcl_DStringFree (&dbStr);
1723    )
1724    Tcl_DStringSetLength (&dStr, 0);
1725    *resultStr = tdomstrdup(
1726        Tcl_UniCharToUtfDString(
1727            (Tcl_UniChar*)Tcl_DStringValue (&s),
1728            Tcl_UniCharLen((Tcl_UniChar*)Tcl_DStringValue(&s)), &dStr
1729            )
1730        );
1731    Tcl_DStringFree (&dStr);
1732    Tcl_DStringFree (&s);
1733    *resultLen = strlen(*resultStr);
1734    return 0;
1735
1736 xsltFormatNumberError:
1737    Tcl_DStringFree (&dStr);
1738    Tcl_DStringFree (&s);
1739    return -1;
1740}
1741
1742#endif /* TclOnly8Bits */
1743
1744
1745static xsltNodeSet *
1746createXsltNodeSet ()
1747{
1748    xsltNodeSet * ns;
1749
1750    ns = (xsltNodeSet *) MALLOC (sizeof(xsltNodeSet));
1751    ns->nodes = (domNode**)MALLOC(INITIAL_SIZE_FOR_KEYSETS * sizeof(domNode*));
1752    ns->allocated = INITIAL_SIZE_FOR_KEYSETS;
1753    ns->nr_nodes = 0;
1754    return ns;
1755}
1756
1757
1758
1759/* Helper proc for buildKeyInfoForDoc. Adds node to the node set ns at
1760   the right position (in document order), if not already
1761   present. This is the same as the core of rsAddNode does. The used
1762   method to add may look a bit simpleminded, but experience shows,
1763   that in the vast majority of the cases node simply has to be
1764   appended to the array. */
1765
1766static void nsAddNode (
1767    xsltNodeSet *ns,
1768    domNode *node
1769    )
1770{
1771    int insertIndex, i;
1772
1773    insertIndex = ns->nr_nodes;
1774    for (i = ns->nr_nodes - 1; i >= 0; i--) {
1775        if (node == ns->nodes[i]) return;
1776        if (!domPrecedes (node, ns->nodes[i])) {
1777            break;
1778        }
1779        insertIndex--;
1780    }
1781    if (ns->nr_nodes + 1 >= ns->allocated) {
1782        ns->nodes = (domNode**)REALLOC((void*)ns->nodes,
1783                               2 * ns->allocated * sizeof(domNode*));
1784        ns->allocated *= 2;
1785    }
1786    if (insertIndex == ns->nr_nodes) {
1787        ns->nodes[ns->nr_nodes++] = node;
1788    } else {
1789        for (i = ns->nr_nodes - 1; i >= insertIndex; i--) {
1790            ns->nodes[i+1] = ns->nodes[i];
1791        }
1792        ns->nodes[insertIndex] = node;
1793        ns->nr_nodes++;
1794    }
1795}
1796
1797static int buildKeyInfoForDoc (
1798    xsltSubDoc     *sd,
1799    char           *keyId,
1800    Tcl_HashTable  *keyInfos,
1801    xsltState      *xs,
1802    char          **errMsg
1803)
1804{
1805    int             hnew, rc, docOrder, i;
1806    char           *useValue;
1807    domNode        *node, *savedCurrent;
1808    xpathResultSet  rs, context;
1809    Tcl_HashTable  *valueTable;
1810    Tcl_HashEntry  *h;
1811    xsltKeyInfo    *kinfo, *kinfoStart;
1812    xsltNodeSet    *keyValues;
1813
1814    h = Tcl_FindHashEntry (keyInfos, keyId);
1815    /* key must exist, this is already checked */
1816    kinfoStart = (xsltKeyInfo *) Tcl_GetHashValue (h);
1817
1818    /* this must be a new entry, no check for hnew==1 needed */
1819    h = Tcl_CreateHashEntry (&(sd->keyData), keyId, &hnew);
1820    valueTable = (Tcl_HashTable *)MALLOC(sizeof (Tcl_HashTable));
1821    Tcl_InitHashTable (valueTable, TCL_STRING_KEYS);
1822    Tcl_SetHashValue (h, valueTable);
1823
1824    savedCurrent = xs->current;
1825    node = sd->doc->rootNode;
1826    while (node) {
1827        kinfo = kinfoStart;
1828        while (kinfo) {
1829            rc = xpathMatches (kinfo->matchAst, kinfo->node, node, &(xs->cbs),
1830                               errMsg);
1831            if (rc < 0) {
1832                TRACE1("xpathMatches had errors '%s' \n", *errMsg);
1833                return rc;
1834            }
1835            if (rc > 0) {
1836                TRACE("found match for key !\n");
1837                xpathRSInit (&rs);
1838                xpathRSInit (&context);
1839                rsAddNode (&context, node);
1840                DBG(printXML(node, 0, 2);)
1841                docOrder = 1;
1842                xs->current = node;
1843                rc = xpathEvalSteps (kinfo->useAst, &context, node,
1844                                     kinfo->node, 0, &docOrder, &(xs->cbs),
1845                                     &rs, errMsg);
1846                if (rc != XPATH_OK) {
1847                    xpathRSFree (&rs);
1848                    xpathRSFree (&context);
1849                    return rc;
1850                }
1851                DBG(rsPrint(&rs));
1852                if (rs.type == xNodeSetResult) {
1853                    for (i = 0; i < rs.nr_nodes; i++) {
1854                        useValue = xpathFuncStringForNode (rs.nodes[i]);
1855                        TRACE1("use value = '%s'\n", useValue);
1856                        h = Tcl_CreateHashEntry (valueTable, useValue, &hnew);
1857                        if (hnew) {
1858                            keyValues = createXsltNodeSet();
1859                        } else {
1860                            keyValues = (xsltNodeSet *) Tcl_GetHashValue (h);
1861                        }
1862                        nsAddNode (keyValues, node);
1863                        if (hnew) Tcl_SetHashValue (h, keyValues);
1864                        FREE(useValue);
1865                    }
1866                }
1867                else if (rs.type != EmptyResult) {
1868                    useValue = xpathFuncString (&rs);
1869                    TRACE1("use value = '%s'\n", useValue);
1870                    h = Tcl_CreateHashEntry (valueTable, useValue, &hnew);
1871                    if (hnew) {
1872                        keyValues = createXsltNodeSet();
1873                    } else {
1874                        keyValues = (xsltNodeSet *) Tcl_GetHashValue (h);
1875                    }
1876                    nsAddNode (keyValues, node);
1877                    if (hnew) Tcl_SetHashValue (h, keyValues);
1878                    FREE(useValue);
1879                }
1880                xpathRSFree( &context );
1881                xpathRSFree( &rs );
1882            }
1883            kinfo = kinfo->next;
1884        }
1885        if ((node->nodeType == ELEMENT_NODE) && (node->firstAttr)) {
1886            node = (domNode*) node->firstAttr;
1887            continue;
1888        }
1889        if ((node->nodeType == ATTRIBUTE_NODE)) {
1890            if (((domAttrNode*)node)->nextSibling) {
1891                node = (domNode*) ((domAttrNode*)node)->nextSibling;
1892                continue;
1893            }
1894            node = ((domAttrNode*)node)->parentNode;
1895        }
1896        if ((node->nodeType == ELEMENT_NODE) && (node->firstChild)) {
1897            node = node->firstChild;
1898            continue;
1899        }
1900        if (node->nextSibling) {
1901            node = node->nextSibling;
1902            continue;
1903        }
1904        while ( node->parentNode &&
1905                (node->parentNode->nextSibling == NULL) ) {
1906            node = node->parentNode;
1907        }
1908        if (node->parentNode) {
1909            node = node->parentNode->nextSibling;
1910        } else {
1911            break;
1912        }
1913    }
1914    xs->current = savedCurrent;
1915    return 0;
1916}
1917
1918
1919/*----------------------------------------------------------------------------
1920|   sortNodeSetByNodeNumber
1921|
1922\---------------------------------------------------------------------------*/
1923static void sortNodeSetByNodeNumber(
1924    domNode *nodes[],
1925    int      n
1926)
1927{
1928    int i, j, ln, rn;
1929    domNode *tmp;
1930
1931    while (n > 1) {
1932        tmp = nodes[0]; nodes[0] = nodes[n/2]; nodes[n/2] = tmp;
1933        for (i = 0, j = n; ; ) {
1934            do {
1935                --j;
1936            } while (domPrecedes (nodes[0], nodes[j]));
1937            do {
1938                ++i;
1939            } while (i < j && domPrecedes (nodes[i], nodes[0]));
1940            if (i >= j)  break;
1941            tmp = nodes[i]; nodes[i] = nodes[j]; nodes[j] = tmp;
1942        }
1943        tmp = nodes[j]; nodes[j] = nodes[0]; nodes[0] = tmp;
1944        ln = j;
1945        rn = n - ++j;
1946        if (ln < rn) {
1947            sortNodeSetByNodeNumber(nodes, ln);
1948            nodes += j;
1949            n = rn;
1950        } else {
1951            sortNodeSetByNodeNumber(&(nodes[j]), rn);
1952            n = ln;
1953        }
1954    }
1955}
1956
1957/*----------------------------------------------------------------------------
1958|   sortByDocOrder
1959|
1960\---------------------------------------------------------------------------*/
1961void sortByDocOrder (
1962    xpathResultSet  * rs
1963)
1964{
1965    if (rs->type != xNodeSetResult) return;
1966    sortNodeSetByNodeNumber(rs->nodes, rs->nr_nodes);
1967}
1968
1969/*----------------------------------------------------------------------------
1970|   StripXMLSpace
1971|
1972\---------------------------------------------------------------------------*/
1973static void StripXMLSpace (
1974    xsltState  * xs,
1975    domNode    * node
1976)
1977{
1978    domNode       *child, *newChild, *parent;
1979    int            i, len, onlySpace, found, strip;
1980    char          *p, prefix[MAX_PREFIX_LEN];
1981    const char    *localName;
1982    double        *f;
1983    domNS         *ns;
1984    Tcl_HashEntry *h;
1985    Tcl_DString    dStr;
1986
1987
1988    if (node->nodeType == TEXT_NODE) {
1989        p = ((domTextNode*)node)->nodeValue;
1990        len = ((domTextNode*)node)->valueLength;
1991        onlySpace = 1;
1992        for (i=0; i<len; i++) {
1993            if (!IS_XML_WHITESPACE(*p)) {
1994                onlySpace = 0;
1995                break;
1996            }
1997            p++;
1998        }
1999        if (onlySpace) {
2000            parent = node->parentNode;
2001            while (parent) {
2002                p = getAttr(parent,"xml:space", a_space);
2003                if (p!=NULL) {
2004                    if (strcmp(p,"preserve")==0) return;
2005                    if (strcmp(p,"default")==0)  break;
2006                }
2007                parent = parent->parentNode;
2008            }
2009            DBG(fprintf(stderr, "removing domNode0x%x(len %d) under '%s' \n",
2010                        node, len, node->parentNode->nodeName);)
2011                domDeleteNode (node, NULL, NULL);
2012        }
2013    } else
2014    if (node->nodeType == ELEMENT_NODE) {
2015        if (node->firstChild == NULL) return;
2016        strip = xs->wsInfo.stripAll;
2017        found = 0;
2018        if (node->namespace) {
2019            domSplitQName (node->nodeName, prefix, &localName);
2020        } else {
2021            prefix[0] = '\0';
2022            localName = node->nodeName;
2023        }
2024        ns = NULL;
2025        Tcl_DStringInit (&dStr);
2026        if (prefix[0] != '\0') {
2027            ns =  domLookupPrefix (node, prefix);
2028            if (ns) {
2029                Tcl_DStringAppend (&dStr, ns->uri, -1);
2030                Tcl_DStringAppend (&dStr, ":*", 2);
2031                if (xs->wsInfo.stripAll) {
2032                    h = Tcl_FindHashEntry (&xs->wsInfo.preserveTokens,
2033                                           Tcl_DStringValue (&dStr));
2034                } else {
2035                    h = Tcl_FindHashEntry (&xs->wsInfo.stripTokens,
2036                                           Tcl_DStringValue (&dStr));
2037                }
2038                if (h) {
2039                    f = Tcl_GetHashValue (h);
2040                    if (*f >= xs->wsInfo.wildcardPrec) {
2041                        strip = !xs->wsInfo.stripAll;
2042                        found = 1;
2043                    }
2044                }
2045                if (!found) {
2046                    Tcl_DStringFree (&dStr);
2047                    Tcl_DStringInit (&dStr);
2048                    Tcl_DStringAppend (&dStr, ns->uri, -1);
2049                    Tcl_DStringAppend (&dStr, ":", 1);
2050                }
2051            }
2052        }
2053        if (!found) {
2054            Tcl_DStringAppend (&dStr, localName, -1);
2055            if (xs->wsInfo.stripAll) {
2056                h = Tcl_FindHashEntry (&xs->wsInfo.preserveTokens,
2057                                       Tcl_DStringValue (&dStr));
2058            } else {
2059                h = Tcl_FindHashEntry (&xs->wsInfo.stripTokens,
2060                                       Tcl_DStringValue (&dStr));
2061            }
2062            if (h) {
2063                f = Tcl_GetHashValue (h);
2064                if (*f >= xs->wsInfo.wildcardPrec) {
2065                    strip = !xs->wsInfo.stripAll;
2066                }
2067            }
2068        }
2069        Tcl_DStringFree (&dStr);
2070        if (strip) {
2071            child = node->firstChild;
2072            while (child) {
2073                newChild = child->nextSibling;
2074                StripXMLSpace (xs, child);
2075                child = newChild;
2076            }
2077        } else {
2078            child = node->firstChild;
2079            while (child) {
2080                if (child->nodeType == ELEMENT_NODE) {
2081                    StripXMLSpace (xs, child);
2082                }
2083                child = child->nextSibling;
2084            }
2085        }
2086    }
2087}
2088
2089/*----------------------------------------------------------------------------
2090|   xsltXPathFuncs
2091|
2092\---------------------------------------------------------------------------*/
2093static int xsltXPathFuncs (
2094    void            * clientData,
2095    char            * funcName,
2096    domNode         * ctxNode,
2097    int               ctxPos,
2098    xpathResultSet  * ctx,
2099    domNode         * exprContext,
2100    int               argc,
2101    xpathResultSets * argv,
2102    xpathResultSet  * result,
2103    char           ** errMsg
2104)
2105{
2106    xsltState         * xs = clientData;
2107    char              * keyId, *filterValue, *str = NULL;
2108    char                prefix[MAX_PREFIX_LEN];
2109    const char        * localName, *baseURI, *nsStr;
2110    int                 rc, i, len, NaN, freeStr, x;
2111    double              n;
2112    xsltNodeSet       * keyValues;
2113    Tcl_HashEntry     * h;
2114    Tcl_HashTable     * docKeyData;
2115    xsltSubDoc        * sdoc;
2116    domDocument       * ownerDoc;
2117    Tcl_DString         dStr;
2118    domNS             * ns;
2119    xsltDecimalFormat * df;
2120
2121    DBG ( fprintf(stderr,"xsltXPathFuncs funcName='%s'\n",funcName); )
2122
2123    if (strcmp(funcName, "key")==0) {
2124        /*--------------------------------------------------------------------
2125        |   'key' function
2126        \-------------------------------------------------------------------*/
2127        DBG(fprintf(stderr,"xslt key function called!\n");)
2128        if (argc != 2) {
2129            reportError (exprContext, "key() needs two arguments!", errMsg);
2130            return -1;
2131        }
2132        /* check, if there is a key definition with the given name */
2133        keyId = xpathFuncString(argv[0]);
2134        TRACE1("keyId='%s' \n", keyId);
2135        domSplitQName (keyId, prefix, &localName);
2136        Tcl_DStringInit (&dStr);
2137        if (prefix[0] != '\0') {
2138            ns = domLookupPrefix (exprContext, prefix);
2139            if (!ns) {
2140                reportError (exprContext, "There isn't a namespace bound to"
2141                             " the prefix.", errMsg);
2142                FREE(keyId);
2143                return -1;
2144            }
2145            Tcl_DStringAppend (&dStr, ns->uri, -1);
2146        }
2147        Tcl_DStringAppend (&dStr, localName, -1);
2148        FREE(keyId);
2149        h = Tcl_FindHashEntry (&xs->keyInfos, Tcl_DStringValue (&dStr));
2150        if (!h) {
2151            reportError (exprContext, "Unknown key in key() function call!",
2152                         errMsg);
2153            Tcl_DStringFree (&dStr);
2154            return -1;
2155        }
2156
2157        /* Short cut for empty result sets. */
2158        if (argv[1]->type == EmptyResult) {
2159            Tcl_DStringFree (&dStr);
2160            return 0;
2161        }
2162
2163        /* find the doc, the context node belongs to */
2164        sdoc = xs->subDocs;
2165        if (ctxNode->nodeType == ATTRIBUTE_NODE) {
2166            ownerDoc = ((domAttrNode *)ctxNode)->parentNode->ownerDocument;
2167        } else {
2168            ownerDoc = ctxNode->ownerDocument;
2169        }
2170        while (sdoc) {
2171            if (sdoc->doc == ownerDoc) break;
2172            sdoc = sdoc->next;
2173        }
2174        DBG(if (!sdoc) fprintf (stderr, "key() function: ctxNode doesn't belong to a doc out of subDocs!!! This could not happen!. ERROR\n");
2175            else (fprintf (stderr, "key() function: ctxNode belongs to doc %s\n", sdoc->baseURI));)
2176
2177        h = Tcl_FindHashEntry (&(sdoc->keyData), Tcl_DStringValue (&dStr));
2178        if (!h) {
2179            if (buildKeyInfoForDoc(sdoc, Tcl_DStringValue (&dStr),
2180                                   &(xs->keyInfos),xs,errMsg)<0) {
2181                Tcl_DStringFree (&dStr);
2182                return -1;
2183            }
2184            h = Tcl_FindHashEntry (&(sdoc->keyData), Tcl_DStringValue (&dStr));
2185        }
2186        Tcl_DStringFree (&dStr);
2187
2188        docKeyData = (Tcl_HashTable *) Tcl_GetHashValue (h);
2189
2190        if (argv[1]->type == xNodeSetResult) {
2191            for (i = 0; i < argv[1]->nr_nodes; i++) {
2192                filterValue = xpathFuncStringForNode (argv[1]->nodes[i]);
2193                TRACE1("filterValue='%s' \n", filterValue);
2194                h = Tcl_FindHashEntry (docKeyData, filterValue);
2195                if (h) {
2196                    keyValues = (xsltNodeSet *) Tcl_GetHashValue (h);
2197                    if (result->type == EmptyResult) {
2198                        result->type = xNodeSetResult;
2199                        result->nodes = keyValues->nodes;
2200                        result->intvalue = 1;
2201                        result->nr_nodes = keyValues->nr_nodes;
2202                        result->allocated = keyValues->allocated;
2203                    } else {
2204                        for (x = 0; x < keyValues->nr_nodes; x++) {
2205                            rsAddNode(result, keyValues->nodes[x]);
2206                        }
2207                    }
2208                }
2209                FREE(filterValue);
2210            }
2211        } else {
2212           filterValue = xpathFuncString(argv[1]);
2213           TRACE1("filterValue='%s' \n", filterValue);
2214           h = Tcl_FindHashEntry (docKeyData, filterValue);
2215           if (h) {
2216               keyValues = (xsltNodeSet *) Tcl_GetHashValue (h);
2217               if (result->type == EmptyResult) {
2218                   result->type = xNodeSetResult;
2219                   result->nodes = keyValues->nodes;
2220                   result->intvalue = 1;
2221                   result->nr_nodes = keyValues->nr_nodes;
2222                   result->allocated = keyValues->allocated;
2223               } else {
2224                   for (x = 0; x < keyValues->nr_nodes; x++) {
2225                       rsAddNode(result, keyValues->nodes[x]);
2226                   }
2227               }
2228           }
2229           FREE(filterValue);
2230        }
2231        return 0;
2232    } else
2233    if (strcmp(funcName, "current")==0) {
2234        /*--------------------------------------------------------------------
2235        |   'current' function
2236        \-------------------------------------------------------------------*/
2237        DBG(fprintf(stderr, "xsltXPathFuncs 'current' = 'domNode0x%x' \n",
2238                    xs->current);)
2239        if (argc != 0) {
2240            reportError (exprContext, "current() must not have any arguments",
2241                         errMsg);
2242            return -1;
2243        }
2244        rsAddNode(result, xs->current);
2245        return 0;
2246    } else
2247    if (strcmp (funcName, "format-number")==0) {
2248        /*--------------------------------------------------------------------
2249        |   'format-number' function
2250        \-------------------------------------------------------------------*/
2251        DBG(fprintf(stderr, "before format-number argc=%d \n", argc);)
2252        if (argc == 3) {
2253            str = xpathFuncString (argv[2]);
2254            domSplitQName (str, prefix, &localName);
2255            ns = NULL;
2256            if (prefix[0] != '\0') {
2257                ns = domLookupPrefix (exprContext, prefix);
2258                if (!ns) {
2259                    reportError (exprContext, "There isn't a namespace bound"
2260                                 " to the prefix.", errMsg);
2261                    FREE(str);
2262                    return -1;
2263                }
2264            }
2265            df = xs->decimalFormats->next;
2266            while (df) {
2267                if (strcmp(df->name, str)==0
2268                    && ((df->uri == NULL && ns == NULL)
2269                        || (df->uri != NULL
2270                            && ns != NULL
2271                            && (strcmp (df->uri, ns->uri)==0)))) {
2272                    break;
2273                }
2274                df = df->next;
2275            }
2276            FREE(str);
2277            if (df == NULL) {
2278                reportError (exprContext, "There isn't a decimal format with"
2279                             " this name.", errMsg);
2280                return -1;
2281            }
2282        } else
2283        if (argc == 2) {
2284            df = xs->decimalFormats;
2285        } else {
2286            reportError (exprContext, "format-number: wrong # parameters:"
2287                         " format-number(number, string, ?string?)!", errMsg);
2288            return -1;
2289        }
2290        NaN = 0;
2291        n   = xpathFuncNumber (argv[0], &NaN);
2292        if (NaN) {
2293            if      (NaN == 2) rsSetString (result, df->NaN);
2294            else if (NaN == 1) rsSetString (result, df->infinity);
2295            else {
2296                Tcl_DStringInit (&dStr);
2297                Tcl_DStringAppend (&dStr, "-", 1);
2298                Tcl_DStringAppend (&dStr, df->infinity, -1);
2299                rsSetString (result, Tcl_DStringValue (&dStr));
2300            }
2301            return 0;
2302        }
2303        str = xpathFuncString (argv[1]);
2304        DBG(fprintf(stderr, "1 str='%s' \n", str);)
2305        result->type = StringResult;
2306        rc = xsltFormatNumber(n, str, df, &(result->string),
2307                              &(result->string_len), errMsg);
2308        FREE(str);
2309        if (rc < 0) {
2310            result->type = EmptyResult;
2311            return rc;
2312        }
2313        DBG(fprintf(stderr, "after format-number \n");)
2314        return 0;
2315    } else
2316    if (strcmp (funcName, "document")==0) {
2317        /*--------------------------------------------------------------------
2318        |   'document' function
2319        \-------------------------------------------------------------------*/
2320        DBG(fprintf(stderr, "xsltXPathFuncs 'document' \n");)
2321        if (argc == 1) {
2322            if (argv[0]->type == xNodeSetResult) {
2323                for (i = 0; i < argv[0]->nr_nodes; i++) {
2324                    freeStr = 0;
2325                    if (argv[0]->nodes[i]->nodeType == ATTRIBUTE_NODE) {
2326                        nsStr = ((domAttrNode*)argv[0]->nodes[i])->nodeValue;
2327                        baseURI = findBaseURI (((domAttrNode*)argv[0]->nodes[i])->parentNode);
2328                    } else {
2329                        str = xpathGetStringValue (argv[0]->nodes[i], &len);
2330                        nsStr = str;
2331                        freeStr = 1;
2332                        baseURI = findBaseURI (argv[0]->nodes[i]);
2333                    }
2334                    /* the case document('') */
2335                    if (*nsStr == '\0') {
2336                        if (freeStr) {
2337                            FREE(str);
2338                            freeStr = 0;
2339                        }
2340                        nsStr = baseURI;
2341                    }
2342                    if (xsltAddExternalDocument(xs, baseURI, nsStr, 0,
2343                                                result, errMsg) < 0) {
2344                        if (freeStr) FREE(str);
2345                        return -1;
2346                    }
2347                    if (xs->wsInfo.hasData) {
2348                        StripXMLSpace (xs, xs->subDocs->doc->documentElement);
2349                    }
2350                    if (freeStr) FREE(str);
2351                }
2352            } else {
2353                str = xpathFuncString (argv[0]);
2354                nsStr = str;
2355                if (xs->currentXSLTNode) {
2356                    baseURI = findBaseURI (xs->currentXSLTNode);
2357                } else
2358                if (xs->currentTplRule) {
2359                    baseURI = findBaseURI (xs->currentTplRule->content);
2360                } else {
2361                    baseURI = findBaseURI (xs->xsltDoc->rootNode);
2362                }
2363                if (*nsStr == '\0') {
2364                    nsStr = baseURI;
2365                }
2366                DBG (fprintf (stderr, "document() call, with 1 string arg = '%s'\n", str);)
2367                if (xsltAddExternalDocument(xs, baseURI, nsStr, 1,
2368                                            result, errMsg) < 0) {
2369                    FREE(str);
2370                    return -1;
2371                }
2372                if (xs->wsInfo.hasData) {
2373                    StripXMLSpace (xs, xs->subDocs->doc->documentElement);
2374                }
2375                FREE(str);
2376            }
2377        } else
2378        if (argc == 2) {
2379            if (argv[1]->type != xNodeSetResult) {
2380                reportError (exprContext, "second arg of document() has to be"
2381                             " a nodeset!", errMsg);
2382                return -1;
2383            }
2384            if (argv[1]->nodes[0]->nodeType == ATTRIBUTE_NODE) {
2385                baseURI = findBaseURI (((domAttrNode*)argv[1]->nodes[0])->parentNode);
2386            } else {
2387                baseURI = findBaseURI (argv[1]->nodes[0]);
2388            }
2389            if (argv[0]->type == xNodeSetResult) {
2390                for (i = 0; i < argv[0]->nr_nodes; i++) {
2391                    freeStr = 0;
2392                    if (argv[0]->nodes[i]->nodeType == ATTRIBUTE_NODE) {
2393                        nsStr = ((domAttrNode*)argv[0]->nodes[i])->nodeValue;
2394                    } else {
2395                        str = xpathGetStringValue (argv[0]->nodes[i], &len);
2396                        freeStr = 1;
2397                        nsStr = str;
2398                    }
2399                    if (*nsStr == '\0') {
2400                        FREE(str);
2401                        freeStr = 0;
2402                        nsStr = baseURI;
2403                    }
2404                    if (xsltAddExternalDocument(xs, baseURI, nsStr, 0,
2405                                                result, errMsg) < 0) {
2406                        if (freeStr) FREE(str);
2407                        return -1;
2408                    }
2409                    if (xs->wsInfo.hasData) {
2410                        StripXMLSpace (xs, xs->subDocs->doc->documentElement);
2411                    }
2412                    if (freeStr) FREE(str);
2413                }
2414            } else {
2415                str = xpathFuncString (argv[0]);
2416                nsStr = str;
2417                if (*str == '\0') {
2418                    nsStr = baseURI;
2419                }
2420                if (xsltAddExternalDocument(xs, baseURI, nsStr, 0,
2421                                            result, errMsg) < 0) {
2422                    FREE(str);
2423                    return -1;
2424                }
2425                if (xs->wsInfo.hasData) {
2426                    StripXMLSpace (xs, xs->subDocs->doc->documentElement);
2427                }
2428                FREE(str);
2429            }
2430        } else {
2431            reportError (exprContext, "wrong # of args in document() call!",
2432                         errMsg);
2433            return -1;
2434        }
2435        return 0;
2436     } else {
2437        /* chain back to original callback */
2438        if (xs->orig_funcCB) {
2439            return (xs->orig_funcCB)(xs->orig_funcClientData, funcName,
2440                                     ctxNode, ctxPos, ctx, exprContext,
2441                                     argc, argv, result, errMsg);
2442        }
2443    }
2444    return 0;
2445}
2446
2447
2448
2449/*----------------------------------------------------------------------------
2450|   evalXPath
2451|
2452\---------------------------------------------------------------------------*/
2453static int evalXPath (
2454    xsltState       * xs,
2455    xpathResultSet  * context,
2456    domNode         * currentNode,
2457    int               currentPos,
2458    char            * xpath,
2459    xpathResultSet  * rs,
2460    char           ** errMsg
2461)
2462{
2463    int rc, hnew, docOrder = 1;
2464    ast t;
2465    domNode *savedCurrent;
2466    Tcl_HashEntry *h;
2467
2468    h = Tcl_CreateHashEntry (&(xs->xpaths), xpath, &hnew);
2469    if (!hnew) {
2470        t = (ast)Tcl_GetHashValue(h);
2471    } else {
2472        rc = xpathParse (xpath, xs->currentXSLTNode, XPATH_EXPR, NULL, NULL,
2473                         &t, errMsg);
2474        if (rc < 0) {
2475            reportError (xs->currentXSLTNode, *errMsg, errMsg);
2476            return rc;
2477        }
2478        Tcl_SetHashValue(h, t);
2479    }
2480    xpathRSInit( rs );
2481
2482    DBG(fprintf (stderr, "evalXPath evaluating xpath:\n");)
2483    DBG(printAst(3,t);)
2484    savedCurrent = xs->current;
2485    xs->current = currentNode;
2486    rc = xpathEvalSteps( t, context, currentNode, xs->currentXSLTNode,
2487                         currentPos, &docOrder, &(xs->cbs), rs, errMsg);
2488    xs->current = savedCurrent;
2489    if (rc != XPATH_OK) {
2490        reportError (xs->currentXSLTNode, *errMsg, errMsg);
2491        xpathRSFree( rs );
2492    }
2493
2494    return rc;
2495}
2496
2497
2498/*----------------------------------------------------------------------------
2499|   nodeGreater
2500|
2501\---------------------------------------------------------------------------*/
2502static int nodeGreater (
2503    int         typeText,
2504    int         asc,
2505    int         upperFirst,
2506    char      * strA,
2507    char      * strB,
2508    double      realA,
2509    double      realB,
2510    int       * greater
2511)
2512{
2513    int             rc;
2514#if TclOnly8Bits == 0
2515    char           *strAptr, *strBptr;
2516    int             lenA, lenB, len;
2517    Tcl_UniChar     unicharA, unicharB;
2518#endif
2519
2520    *greater = 0;
2521
2522    if (typeText) {
2523
2524#if TclOnly8Bits
2525        /* TODO: this only works for 7 bit ASCII */
2526        rc = STRCASECMP(strA, strB);
2527        if (rc == 0) {
2528            rc = strcmp (strA, strB);
2529            if (!upperFirst) {
2530                rc *= -1;
2531            }
2532        }
2533DBG(   fprintf(stderr, "nodeGreater %d <-- strA='%s' strB='%s'\n", rc, strA, strB);)
2534#else
2535        lenA = Tcl_NumUtfChars (strA, -1);
2536        lenB = Tcl_NumUtfChars (strB, -1);
2537        len = (lenA < lenB ? lenA : lenB);
2538        rc = Tcl_UtfNcasecmp (strA, strB, len);
2539        if (rc == 0) {
2540            if (lenA > lenB) {
2541                rc = 1;
2542            } else if (lenA < lenB) {
2543                rc = -1;
2544            }
2545        }
2546        if (rc == 0) {
2547            strAptr = strA;
2548            strBptr = strB;
2549            while (len-- > 0) {
2550                strAptr += Tcl_UtfToUniChar(strAptr, &unicharA);
2551                strBptr += Tcl_UtfToUniChar(strBptr, &unicharB);
2552                if (unicharA != unicharB) {
2553                    rc = unicharA - unicharB;
2554                    break;
2555                }
2556            }
2557            if (!upperFirst) {
2558                rc *= -1;
2559            }
2560        }
2561#endif
2562        if (asc) *greater = (rc > 0);
2563            else *greater = (rc < 0);
2564
2565    } else {
2566DBG(   fprintf(stderr, "nodeGreater  realA='%f' realB='%f'\n",realA, realB);)
2567        if (IS_NAN (realA) || IS_NAN (realB)) {
2568            if (asc) {
2569                if (IS_NAN (realA) && !IS_NAN (realB)) {
2570                    *greater = 0;
2571                } else {
2572                    if (IS_NAN (realB) && !IS_NAN (realA)) *greater = 1;
2573                }
2574            } else {
2575                if (IS_NAN (realA) && !IS_NAN(realB)) {
2576                    *greater = 1;
2577                } else {
2578                    if (IS_NAN (realB) && !IS_NAN(realA)) *greater = 0;
2579                }
2580            }
2581        } else {
2582            if (asc) *greater = (realA > realB);
2583            else *greater = (realA < realB);
2584        }
2585    }
2586    return 0;
2587}
2588
2589static int fastMergeSort (
2590    int         txt,
2591    int         asc,
2592    int         upperFirst,
2593    domNode   * a[],
2594    int       * posa,
2595    domNode   * b[],
2596    int       * posb,
2597    char     ** vs,
2598    double    * vd,
2599    char     ** vstmp,
2600    double    * vdtmp,
2601    int         size,
2602    char     ** errMsg
2603) {
2604    domNode *tmp;
2605    int tmpPos, lptr, rptr, middle, i, j, gt, rc;
2606    char    *tmpVs;
2607    double   tmpVd;
2608
2609    if (size < 10) {
2610          /* use simple and fast insertion for small sizes ! */
2611        for (i = 1; i < size; i++) {
2612            tmp    = a    [i];
2613            tmpPos = posa [i];
2614            tmpVs  = vs   [i];
2615            tmpVd  = vd   [i];
2616            j = i;
2617            if (j>0) {
2618                rc = nodeGreater(txt, asc, upperFirst, vs[j-1], tmpVs,
2619                                   vd[j-1], tmpVd, &gt);
2620                CHECK_RC;
2621            }
2622            while ( j > 0 && gt) {
2623                a   [j] = a   [j-1];
2624                posa[j] = posa[j-1];
2625                vs  [j] = vs  [j-1];
2626                vd  [j] = vd  [j-1];
2627                j--;
2628                if (j>0) {
2629                    rc = nodeGreater(txt, asc, upperFirst, vs[j-1], tmpVs,
2630                                       vd[j-1], tmpVd, &gt);
2631                    CHECK_RC;
2632                }
2633            }
2634            a   [j] = tmp;
2635            posa[j] = tmpPos;
2636            vs  [j] = tmpVs;
2637            vd  [j] = tmpVd;
2638        }
2639        return 0;
2640    }
2641    middle = size/2;
2642
2643    rc = fastMergeSort(txt, asc, upperFirst, a, posa, b, posb, vs, vd,
2644                         vstmp, vdtmp, middle, errMsg);
2645    CHECK_RC;
2646    rc = fastMergeSort(txt, asc, upperFirst, a+middle, posa+middle, b+middle,
2647                         posb+middle, vs+middle, vd+middle, vstmp+middle,
2648                         vdtmp+middle, size-middle, errMsg);
2649    CHECK_RC;
2650
2651    lptr = 0;
2652    rptr = middle;
2653
2654    for (i = 0; i < size; i++) {
2655        if (lptr == middle) {
2656            b    [i] = a   [rptr  ];
2657            posb [i] = posa[rptr  ];
2658            vstmp[i] = vs  [rptr  ];
2659            vdtmp[i] = vd  [rptr++];
2660        } else if (rptr < size) {
2661            rc = nodeGreater(txt, asc, upperFirst, vs[lptr], vs[rptr],
2662                             vd[lptr], vd[rptr], &gt);
2663            if (gt) {
2664                b    [i] = a   [rptr  ];
2665                posb [i] = posa[rptr  ];
2666                vstmp[i] = vs  [rptr  ];
2667                vdtmp[i] = vd  [rptr++];
2668            } else {
2669                b    [i] = a   [lptr  ];
2670                posb [i] = posa[lptr  ];
2671                vstmp[i] = vs  [lptr  ];
2672                vdtmp[i] = vd  [lptr++];
2673            }
2674        } else {
2675            b    [i] = a   [lptr  ];
2676            posb [i] = posa[lptr  ];
2677            vstmp[i] = vs  [lptr  ];
2678            vdtmp[i] = vd  [lptr++];
2679        }
2680    }
2681    memcpy(a,    b,     size*sizeof(domNode*));
2682    memcpy(posa, posb,  size*sizeof(int*));
2683    memcpy(vs,   vstmp, size*sizeof(char*));
2684    memcpy(vd,   vdtmp, size*sizeof(double));
2685    return 0;
2686}
2687
2688static int sortNodeSetFastMerge(
2689    int         txt,
2690    int         asc,
2691    int         upperFirst,
2692    domNode   * nodes[],
2693    int         n,
2694    char     ** vs,
2695    double    * vd,
2696    int       * pos,
2697    char     ** errMsg
2698)
2699{
2700    domNode **b;
2701    int      *posb;
2702    char    **vstmp;
2703    double   *vdtmp;
2704    int       rc;
2705
2706    b = (domNode **)MALLOC(n * sizeof(domNode *));
2707    posb = (int *)MALLOC(n * sizeof(int));
2708    vstmp = (char **)MALLOC(sizeof (char *) * n);
2709    vdtmp = (double *)MALLOC(sizeof (double) * n);
2710
2711    rc = fastMergeSort(txt, asc, upperFirst, nodes, pos, b, posb, vs, vd,
2712                         vstmp, vdtmp, n, errMsg);
2713    FREE((char*)posb);
2714    FREE((char*)b);
2715    FREE((char*)vstmp);
2716    FREE((char*)vdtmp);
2717    CHECK_RC;
2718    return 0;
2719}
2720
2721/*----------------------------------------------------------------------------
2722|   xsltSetVar
2723|
2724\---------------------------------------------------------------------------*/
2725static int xsltSetVar (
2726    xsltState       * xs,
2727    char            * variableName,
2728    xpathResultSet  * context,
2729    domNode         * currentNode,
2730    int               currentPos,
2731    char            * select,
2732    domNode         * actionNode,
2733    int               active,
2734    char           ** errMsg
2735)
2736{
2737    xsltVariable   * var;
2738    int              rc;
2739    xpathResultSet   rs;
2740    xsltVarFrame    *tmpFrame = NULL;
2741    domNode         *fragmentNode, *savedLastNode;
2742    char             prefix[MAX_PREFIX_LEN];
2743    const char      *localName;
2744    domNS           *ns;
2745
2746    TRACE1("xsltSetVar variableName='%s' \n", variableName);
2747    if (select!=NULL) {
2748        TRACE2("xsltSetVar variableName='%s' select='%s'\n", variableName, select);
2749        rc = evalXPath (xs, context, currentNode, currentPos, select, &rs,
2750                        errMsg);
2751        CHECK_RC;
2752    } else {
2753        if (!actionNode->firstChild) {
2754            xpathRSInit (&rs);
2755            rsSetString (&rs, "");
2756        } else {
2757            fragmentNode = domNewElementNode(xs->resultDoc, "",
2758                                             ELEMENT_NODE);
2759            savedLastNode = xs->lastNode;
2760            xs->lastNode = fragmentNode;
2761            /* process the children as well */
2762            xsltPushVarFrame (xs);
2763            rc = ExecActions(xs, context, currentNode, currentPos,
2764                             actionNode->firstChild, errMsg);
2765            xsltPopVarFrame (xs);
2766            CHECK_RC;
2767            xpathRSInit(&rs);
2768            rsAddNodeFast(&rs, fragmentNode);
2769            xs->lastNode = savedLastNode;
2770        }
2771    }
2772    tmpFrame = &xs->varFramesStack[xs->varFramesStackPtr];
2773
2774    xs->varStackPtr++;
2775    if (xs->varStackPtr >= xs->varStackLen) {
2776        xs->varStack = (xsltVariable *) REALLOC ((char*)xs->varStack,
2777                                                 sizeof (xsltVariable)
2778                                                 * 2 * xs->varStackLen);
2779        xs->varStackLen *= 2;
2780    }
2781    var = &(xs->varStack[xs->varStackPtr]);
2782    if (tmpFrame->varStartIndex == -1) {
2783        tmpFrame->varStartIndex = xs->varStackPtr;
2784    }
2785    tmpFrame->nrOfVars++;
2786    domSplitQName (variableName, prefix, &localName);
2787    if (prefix[0] != '\0') {
2788        ns = domLookupPrefix (actionNode, prefix);
2789        if (!ns) {
2790            reportError (actionNode, "There isn't a namespace bound to"
2791                         " the prefix.", errMsg);
2792            return -1;
2793        }
2794        var->uri  = ns->uri;
2795        var->name = localName;
2796    } else {
2797        var->uri  = NULL;
2798        var->name = variableName;
2799    }
2800    tmpFrame->polluted = 1;
2801    var->node   = actionNode;
2802    var->rs     = rs;
2803    var->active = active;
2804    DBG(rsPrint(&(var->rs)));
2805    return 0;
2806}
2807
2808/*----------------------------------------------------------------------------
2809|   xsltVarExists
2810|
2811\---------------------------------------------------------------------------*/
2812static int xsltVarExists (
2813    xsltState  * xs,
2814    char       * variableName,
2815    domNode    * exprContext
2816)
2817{
2818    int           i, frameIndex, found = 0;
2819    char          prefix[MAX_PREFIX_LEN];
2820    const char   *localName, *uri, *varName;
2821    domNS        *ns;
2822    xsltVarFrame *frame;
2823
2824    TRACE1("xsltVarExists variableName='%s' \n", variableName);
2825    domSplitQName (variableName, prefix, &localName);
2826    if (prefix[0]) {
2827        ns = domLookupPrefix (exprContext, prefix);
2828        if (!ns) {
2829            /* TODO: this is an error, not only 'not found' */
2830            return 0;
2831        }
2832        uri = ns->uri;
2833        varName = localName;
2834    } else {
2835        uri = NULL;
2836        varName = variableName;
2837    }
2838    frameIndex = xs->varFramesStackPtr;
2839    while (frameIndex >= 0) {
2840        frame = &xs->varFramesStack[frameIndex];
2841        for (i = frame->varStartIndex;
2842             i < frame->varStartIndex + frame->nrOfVars;
2843             i++) {
2844            if ( (uri && !((&xs->varStack[i])->uri))
2845                 || (!uri && (&xs->varStack[i])->uri)
2846                 || (uri && (&xs->varStack[i])->uri
2847                     && (strcmp (uri, (&xs->varStack[i])->uri)!=0))
2848                ) continue;
2849            if (strcmp((&xs->varStack[i])->name, varName)==0) {
2850                found = 1;
2851                (&xs->varStack[i])->active = 1;
2852                break; /* found the variable */
2853            }
2854        }
2855        if (found) return 1;
2856        if (frame->stop) break;
2857        frameIndex--;
2858    }
2859
2860    return 0;
2861}
2862
2863
2864/*----------------------------------------------------------------------------
2865|   xsltGetVar
2866|
2867\---------------------------------------------------------------------------*/
2868static int xsltGetVar (
2869    void           * clientData,
2870    char           * variableName,
2871    char           * varURI,
2872    xpathResultSet * result,
2873    char           **errMsg
2874)
2875{
2876    xsltState        *xs = clientData;
2877    xsltVarFrame     *frame;
2878    xsltVariable     *var;
2879    int               rc, i, frameIndex, parFrameSkiped = 0;
2880    char             *select;
2881    Tcl_HashEntry    *h;
2882    xsltTopLevelVar  *topLevelVar;
2883    xsltVarInProcess *varInProcess, thisVarInProcess;
2884    xpathResultSet    nodeList;
2885    domNode          *savedCurrentXSLTNode;
2886    Tcl_DString       dErrMsg;
2887
2888    TRACE1("xsltGetVar variableName='%s' \n", variableName);
2889    frameIndex = xs->varFramesStackPtr;
2890    while (frameIndex >= 0) {
2891        frame = &xs->varFramesStack[frameIndex];
2892        if (frame->stop == 2 && !parFrameSkiped) {
2893            parFrameSkiped = 1;
2894            frameIndex--;
2895            continue;
2896        }
2897        for (i = frame->varStartIndex;
2898             i < frame->varStartIndex + frame->nrOfVars;
2899             i++) {
2900            var = &xs->varStack[i];
2901            if (!var->active) continue;
2902            if ( (varURI && !var->uri)
2903                 || (!varURI && var->uri)
2904                 || (varURI && var->uri && (strcmp (varURI, var->uri)!=0))
2905                ) continue;
2906            if (strcmp(var->name, variableName)==0) {
2907                TRACE1("xsltGetVar '%s':\n", variableName);
2908                DBG(rsPrint(&(var->rs)));
2909                rsCopy(result, &(var->rs) );
2910                return XPATH_OK;
2911            }
2912        }
2913        if ((frame->stop == 1) && frameIndex > 1) frameIndex = 1;
2914        frameIndex--;
2915    }
2916
2917    if (xs->varsInProcess) {
2918        h = Tcl_FindHashEntry (&xs->topLevelVars, variableName);
2919        if (h) {
2920            topLevelVar = (xsltTopLevelVar *) Tcl_GetHashValue (h);
2921            /* check for circular definitions */
2922            varInProcess = xs->varsInProcess;
2923            while (varInProcess) {
2924                if (strcmp(varInProcess->name, variableName)==0) {
2925                    reportError (topLevelVar->node, "circular top level"
2926                                 " variabale definition detected", errMsg);
2927                    return XPATH_EVAL_ERR;
2928                }
2929                varInProcess = varInProcess->next;
2930            }
2931            thisVarInProcess.name = variableName;
2932            thisVarInProcess.next = xs->varsInProcess;
2933            xs->varsInProcess = &thisVarInProcess;
2934
2935            xpathRSInit( &nodeList );
2936            rsAddNodeFast( &nodeList, xs->xmlRootNode);
2937            savedCurrentXSLTNode = xs->currentXSLTNode;
2938            xs->currentXSLTNode = topLevelVar->node;
2939            select = getAttr (topLevelVar->node, "select", a_select);
2940            rc = xsltSetVar (xs, variableName, &nodeList, xs->xmlRootNode,
2941                             0, select, topLevelVar->node, 1, errMsg);
2942            xpathRSFree ( &nodeList );
2943            CHECK_RC;
2944            rc = xsltGetVar (xs, variableName, varURI, result, errMsg);
2945            CHECK_RC;
2946            /* remove var out of the varsInProcess list. Should be first
2947               in the list, shouldn't it? */
2948            varInProcess = xs->varsInProcess;
2949            if (varInProcess != &thisVarInProcess) {
2950                domPanic ("error in top level vars processing");
2951            }
2952            xs->varsInProcess = varInProcess->next;
2953            xs->currentXSLTNode = savedCurrentXSLTNode;
2954            return XPATH_OK;
2955        }
2956    }
2957    Tcl_DStringInit (&dErrMsg);
2958    Tcl_DStringAppend (&dErrMsg, "Variable \"", -1);
2959    Tcl_DStringAppend (&dErrMsg, variableName, -1);
2960    Tcl_DStringAppend (&dErrMsg, "\" has not been declared.", -1);
2961    reportError (xs->currentXSLTNode, Tcl_DStringValue (&dErrMsg), errMsg);
2962    Tcl_DStringFree (&dErrMsg);
2963    return XPATH_EVAL_ERR;
2964}
2965
2966/*----------------------------------------------------------------------------
2967|   addMatch
2968|
2969\---------------------------------------------------------------------------*/
2970static int addMatch (
2971    xsltState     *xs,
2972    domNode       *node,
2973    xsltTemplate  *tpl,
2974    char          *prioStr,
2975    ast            a,
2976    char         **errMsg
2977    )
2978{
2979    xsltTemplate  *t, *prevTpl;
2980    int            rc, hnew;
2981    Tcl_DString    dStr;
2982    Tcl_HashEntry *h;
2983
2984    if (a->type == CombinePath) {
2985        t = (xsltTemplate *)MALLOC(sizeof(xsltTemplate));
2986        t->freeAst    = NULL;
2987        t->name       = NULL;
2988        t->nameURI    = NULL;
2989        t->mode       = tpl->mode;
2990        t->modeURI    = tpl->modeURI;
2991        t->content    = tpl->content;
2992        t->precedence = tpl->precedence;
2993        t->sDoc       = tpl->sDoc;
2994        t->next       = NULL;
2995        if (prioStr) {
2996            t->prio   = tpl->prio;
2997        }
2998        rc = addMatch (xs, node, t, prioStr, a->child->child, errMsg);
2999        CHECK_RC1(t);
3000        tpl->ast = a->child->next->child;
3001    } else {
3002        tpl->ast = a;
3003    }
3004
3005    if (!prioStr) {
3006        tpl->prio = xpathGetPrio(tpl->ast);
3007        TRACE1("prio = %f for \n", tpl->prio);
3008        DBG(printAst( 0, tpl->ast);)
3009        TRACE("\n");
3010    }
3011
3012    if ((tpl->ast->type == IsElement && tpl->ast->strvalue[0] != '*')
3013        || tpl->ast->type == IsFQElement) {
3014        Tcl_DStringInit (&dStr);
3015        if (tpl->ast->type == IsFQElement) {
3016            Tcl_DStringAppend (&dStr, tpl->ast->strvalue, -1);
3017            Tcl_DStringAppend (&dStr, ":", 1);
3018        }
3019        if (tpl->mode) {
3020            if (tpl->modeURI) {
3021                Tcl_DStringAppend (&dStr, tpl->modeURI, -1);
3022                Tcl_DStringAppend (&dStr, ":", 1);
3023            }
3024            Tcl_DStringAppend (&dStr, tpl->mode, -1);
3025            Tcl_DStringAppend (&dStr, ":", 1);
3026        }
3027        if (tpl->ast->type == IsFQElement) {
3028            Tcl_DStringAppend (&dStr, tpl->ast->child->strvalue, -1);
3029        } else {
3030            Tcl_DStringAppend (&dStr, tpl->ast->strvalue, -1);
3031        }
3032        h = Tcl_CreateHashEntry (&(xs->isElementTpls),
3033                                 Tcl_DStringValue (&dStr), &hnew);
3034        Tcl_DStringFree (&dStr);
3035
3036        if (hnew) {
3037            tpl->next = NULL;
3038            Tcl_SetHashValue (h, tpl);
3039        } else {
3040            t = (xsltTemplate *) Tcl_GetHashValue (h);
3041            prevTpl = NULL;
3042            while (   t
3043                      && t->precedence >= tpl->precedence
3044                      && t->prio > tpl->prio) {
3045                prevTpl = t;
3046                t = t->next;
3047            }
3048            if (prevTpl) {
3049                tpl->next = t;
3050                prevTpl->next = tpl;
3051            } else {
3052                tpl->next = Tcl_GetHashValue (h);
3053                Tcl_SetHashValue (h, tpl);
3054            }
3055        }
3056    } else {
3057        if (xs->templates == NULL) {
3058            xs->templates = tpl;
3059        } else {
3060            t = xs->templates;
3061            prevTpl = NULL;
3062            while (   t
3063                      && t->precedence >= tpl->precedence
3064                      && t->prio > tpl->prio) {
3065                prevTpl = t;
3066                t = t->next;
3067            }
3068            if (prevTpl) {
3069                tpl->next = t;
3070                prevTpl->next = tpl;
3071            } else {
3072                tpl->next = xs->templates;
3073                xs->templates = tpl;
3074            }
3075        }
3076    }
3077    TRACE5("AddTemplate '%s' '%s' '%s' '%s' '%2.2f' \n\n",
3078            tpl->match, tpl->name, tpl->mode, tpl->modeURI, tpl->prio);
3079    return 0;
3080}
3081
3082/*----------------------------------------------------------------------------
3083|   xsltAddTemplate
3084|
3085\---------------------------------------------------------------------------*/
3086static int xsltAddTemplate (
3087    xsltState *xs,
3088    domNode   *node,
3089    double     precedence,
3090    char     **errMsg
3091)
3092{
3093    xsltTemplate  *tpl, *t;
3094    char          *prioStr, *str, prefix[MAX_PREFIX_LEN];
3095    const char    *localName;
3096    int            rc, hnew;
3097    domNS         *ns;
3098    Tcl_HashEntry *h;
3099    Tcl_DString    dStr;
3100    xsltSubDoc    *sDoc;
3101
3102    tpl = (xsltTemplate *)MALLOC(sizeof(xsltTemplate));
3103
3104    tpl->match      = getAttr(node,"match", a_match);
3105    str = getAttr(node, "name", a_name);
3106    if (!tpl->match && !str) {
3107        reportError (node, " xsl:template must have a name or"
3108                     " match attribute (or both)", errMsg);
3109        FREE ((char*)tpl);
3110        return -1;
3111    }
3112    tpl->name       = NULL;
3113    tpl->nameURI    = NULL;
3114    if (str) {
3115        if (!domIsQNAME (str)) {
3116            reportError (node, "The value of the \"name\" attribute must"
3117                         " be a qname", errMsg);
3118            FREE ((char*)tpl);
3119            return -1;
3120        }
3121        domSplitQName (str, prefix, &localName);
3122        if (prefix[0] != '\0') {
3123            ns = domLookupPrefix (node, prefix);
3124            if (!ns) {
3125                reportError (node, "The prefix of the \"name\" attribute"
3126                             " value isn't bound to a namespace.", errMsg);
3127                FREE ((char*)tpl);
3128                return -1;
3129            }
3130            tpl->nameURI = ns->uri;
3131            Tcl_DStringInit (&dStr);
3132            Tcl_DStringAppend (&dStr, ns->uri, -1);
3133            Tcl_DStringAppend (&dStr, ":", 1);
3134            Tcl_DStringAppend (&dStr, localName, -1);
3135            h = Tcl_CreateHashEntry (&(xs->namedTemplates),
3136                                     Tcl_DStringValue (&dStr), &hnew);
3137            Tcl_DStringFree (&dStr);
3138        } else {
3139            h = Tcl_CreateHashEntry (&(xs->namedTemplates), localName, &hnew);
3140        }
3141        tpl->name   = localName;
3142        if (!hnew) {
3143            t = (xsltTemplate *) Tcl_GetHashValue (h);
3144            if (t->precedence == precedence) {
3145                reportError (node, "There is already a template with the"
3146                             " same name and precedence.", errMsg);
3147                FREE ((char*)tpl);
3148                return -1;
3149            }
3150            if (!t->match) {
3151                FREE ((char*)t);
3152            }
3153        }
3154        Tcl_SetHashValue (h, tpl);
3155        TRACE3("Added named Template '%s' '%2.2f' '%2.2f' \n\n",
3156            tpl->name, tpl->precedence, tpl->prio);
3157    }
3158    tpl->ast        = NULL;
3159    tpl->mode       = NULL;
3160    tpl->modeURI    = NULL;
3161    str = getAttr (node, "mode", a_mode);
3162    if (str) {
3163        rc = 0;
3164        if (!domIsQNAME (str)) {
3165            reportError (node, "The value of the \"mode\" attribute must"
3166                         " be a qname.", errMsg);
3167            rc = -1;
3168        }
3169        if (!tpl->match) {
3170            reportError (node, "A template without a \"match\" attribute must"
3171                         " not have a \"mode\" attribute.", errMsg);
3172            rc = -1;
3173        }
3174        domSplitQName (str, prefix, &localName);
3175        if (prefix[0] != '\0') {
3176            ns = domLookupPrefix (node, prefix);
3177            if (!ns) {
3178                reportError (node, "The prefix of the \"mode\" attribute value"
3179                             " isn't bound to a namespace.", errMsg);
3180                rc = -1;
3181            }
3182            tpl->modeURI = ns->uri;
3183        }
3184        tpl->mode = localName;
3185        if (rc < 0) {
3186            /* If the template has a name attribute, it is already stored in
3187               in the namedTemplates hash table and will be freed. */
3188            if (!tpl->name) {
3189                FREE ((char*)tpl);
3190            }
3191            return -1;
3192        }
3193    }
3194    tpl->prio       = 0.5;
3195    tpl->content    = node;
3196    tpl->precedence = precedence;
3197    tpl->next       = NULL;
3198
3199    prioStr = getAttr(node,"priority", a_prio);
3200    if (prioStr) {
3201        tpl->prio = (double)atof(prioStr);
3202    }
3203
3204    sDoc = xs->subDocs;
3205    while (sDoc) {
3206        if (sDoc->doc == node->ownerDocument) break;
3207        sDoc = sDoc->next;
3208    }
3209    tpl->sDoc = sDoc;
3210
3211    TRACE1("compiling XPATH '%s' ...\n", tpl->match);
3212    if (tpl->match) {
3213        rc = xpathParse(tpl->match, node, XPATH_TEMPMATCH_PATTERN, NULL, NULL,
3214                        &(tpl->freeAst), errMsg);
3215        if (rc < 0) {
3216            reportError (node, *errMsg, errMsg);
3217        } else {
3218            rc = addMatch (xs, node, tpl, prioStr, tpl->freeAst, errMsg);
3219        }
3220        if (rc < 0) {
3221            if (tpl->name) {
3222                /* The template is already stored in the namedTemplates
3223                   hash table. Therefor we don't free tpl here, but
3224                   set tpl->match to NULL, which ensures, that the
3225                   tpl will be freed while the namedTemplates hash table
3226                   is cleand up. */
3227                tpl->match = NULL;
3228            } else {
3229                free ((char*)tpl);
3230            }
3231            return rc;
3232        }
3233    }
3234
3235    return 0;
3236}
3237
3238/*----------------------------------------------------------------------------
3239|   ExecUseAttributeSets
3240|
3241\---------------------------------------------------------------------------*/
3242static int ExecUseAttributeSets (
3243    xsltState         * xs,
3244    xpathResultSet    * context,
3245    domNode           * currentNode,
3246    int                 currentPos,
3247    domNode           * actionNode,
3248    char              * styles,
3249    char             ** errMsg
3250)
3251{
3252    xsltAttrSet *attrSet;
3253    char        *pc, *aSet, save, *str, prefix[MAX_PREFIX_LEN];
3254    const char  *localName;
3255    int          rc;
3256    domNS       *ns;
3257
3258    pc = styles;
3259    while (*pc) {
3260        while (*pc && IS_XML_WHITESPACE(*pc)) pc++;
3261        if (*pc == '\0') break;
3262        aSet = pc;
3263        while (*pc && !IS_XML_WHITESPACE(*pc)) pc++;
3264        save = *pc;
3265        *pc = '\0';
3266        TRACE1("use-attribute-set '%s' \n", aSet);
3267        attrSet = xs->attrSets;
3268        while (attrSet) {
3269            TRACE2("use-Attr: '%s' == '%s' ? \n", attrSet->name, aSet);
3270            rc = 0;
3271            if (!attrSet->uri) {
3272                if (strcmp(attrSet->name, aSet)==0) rc = 1;
3273            }
3274            else {
3275                domSplitQName (aSet, prefix, &localName);
3276                if (prefix[0] != '\0') {
3277                    ns = domLookupPrefix (actionNode, prefix);
3278                    if (ns) {
3279                        if (strcmp (ns->uri, attrSet->uri)==0) {
3280                            if (strcmp (attrSet->name, localName)==0) rc = 1;
3281                        }
3282                    }
3283                }
3284            }
3285            if (rc) {
3286                str = getAttr (attrSet->content, "use-attribute-sets",
3287                               a_useAttributeSets);
3288                if (str) {
3289                    rc = ExecUseAttributeSets (xs, context, currentNode,
3290                                               currentPos, attrSet->content,
3291                                               str, errMsg);
3292                    CHECK_RC;
3293                }
3294                rc = ExecActions(xs, context, currentNode, currentPos,
3295                                 attrSet->content->firstChild, errMsg);
3296                CHECK_RC;
3297            }
3298            attrSet = attrSet->next;
3299        }
3300        *pc = save;
3301    }
3302    return 0;
3303}
3304
3305/*----------------------------------------------------------------------------
3306|   evalAttrTemplates
3307|
3308\---------------------------------------------------------------------------*/
3309static int evalAttrTemplates (
3310    xsltState       * xs,
3311    xpathResultSet  * context,
3312    domNode         * currentNode,
3313    int               currentPos,
3314    char            * str,
3315    char           ** out,
3316    char           ** errMsg
3317)
3318{
3319    xpathResultSet  rs;
3320    char           *tplStart = NULL, *tplResult, *pc, literalChar;
3321    int             rc, aLen, inTpl = 0, p = 0, inLiteral = 0;
3322
3323    aLen = 500;
3324    *out = MALLOC(aLen);
3325    while (*str) {
3326        if (inTpl) {
3327            if (!inLiteral) {
3328                if (*str == '\'') {
3329                    inLiteral = 1;
3330                    literalChar = '\'';
3331                } else
3332                if (*str == '"') {
3333                    inLiteral = 1;
3334                    literalChar = '"';
3335                }
3336            } else {
3337                if (*str == literalChar) {
3338                    inLiteral = 0;
3339                }
3340            }
3341            if (*str == '}' && !inLiteral) {
3342
3343                *str = '\0';
3344                TRACE1("attrTpl: '%s' \n", tplStart);
3345                rc = evalXPath (xs, context, currentNode, currentPos,
3346                                tplStart, &rs, errMsg);
3347                *str = '}';
3348                CHECK_RC1(*out);
3349                tplResult = xpathFuncString( &rs );
3350                DBG(fprintf(stderr, "attrTpl tplResult='%s' \n", tplResult);)
3351                xpathRSFree( &rs );
3352                pc = tplResult;
3353                while (*pc) {
3354                   (*out)[p++] = *pc++;
3355                    if (p>=aLen) { /* enlarge output buffer */
3356                         *out = REALLOC(*out, 2*aLen);
3357                         aLen += aLen;
3358                    }
3359                }
3360                inTpl = 0;
3361                FREE(tplResult);
3362            }
3363        } else {
3364            if (*str == '{') {
3365                if (*(str+1) == '{') {
3366                    /*-----------------------------------------------------
3367                    |    read over escaped '{':
3368                    |        '{{text text}}' -> '{text text}'
3369                    \----------------------------------------------------*/
3370                    str++;
3371                    (*out)[p++] = *str++;
3372                    if (p>=aLen) { /* enlarge output buffer */
3373                        *out = REALLOC(*out, 2*aLen);
3374                        aLen += aLen;
3375                    }
3376                    while (*str && (*str != '}') && (*(str-1) != '}')) {
3377                        (*out)[p++] = *str++;
3378                        if (p>=aLen) { /* enlarge output buffer */
3379                            *out = REALLOC(*out, 2*aLen);
3380                            aLen += aLen;
3381                        }
3382                    }
3383                    if (!*str) break;
3384                } else {
3385                    tplStart = str+1;
3386                    inTpl = 1;
3387                    inLiteral = 0;
3388                }
3389            } else {
3390                if (*str == '}' && *(str+1) == '}') {
3391                    str++;
3392                }
3393                (*out)[p++] = *str;
3394                if (p>=aLen) { /* enlarge output buffer */
3395                    *out = REALLOC(*out, 2*aLen);
3396                    aLen += aLen;
3397                }
3398            }
3399        }
3400        str++;
3401    }
3402    (*out)[p] = '\0';
3403    DBG(fprintf(stderr, "evalAttrTemplates out='%s' \n", (*out) );)
3404    return 0;
3405}
3406
3407
3408/*----------------------------------------------------------------------------
3409|   setParamVars
3410|
3411\---------------------------------------------------------------------------*/
3412static int setParamVars (
3413    xsltState       * xs,
3414    xpathResultSet  * context,
3415    domNode         * currentNode,
3416    int               currentPos,
3417    domNode         * actionNode,
3418    char           ** errMsg
3419)
3420{
3421    domNode *child;
3422    char    *str, *select;
3423    int      rc;
3424
3425    child = actionNode->firstChild;
3426    while (child) {
3427        if (child->nodeType == ELEMENT_NODE) {
3428            TRACE1("setParamVars child '%s' \n", child->nodeName);
3429            if (child->info == withParam) {
3430                str = getAttr(child, "name", a_name);
3431                if (str) {
3432                    TRACE1("setting with-param '%s' \n", str);
3433                    xs->currentXSLTNode = child;
3434                    select = getAttr(child, "select", a_select);
3435                    if (select && child->firstChild) {
3436                        reportError (child, "An xsl:parameter element with a"
3437                                     " select attribute must be empty",
3438                                     errMsg);
3439                        return -1;
3440                    }
3441                    TRACE1("with-param select='%s'\n", select);
3442                    rc = xsltSetVar(xs, str, context, currentNode,
3443                                    currentPos, select, child, 0, errMsg);
3444                    CHECK_RC;
3445                } else {
3446                    reportError (child, "xsl:with-param: missing mandatory"
3447                                 " attribute \"name\".", errMsg);
3448                    return -1;
3449                }
3450            }
3451        }
3452        child = child->nextSibling;
3453    }
3454    return 0;
3455}
3456
3457
3458/*----------------------------------------------------------------------------
3459|   doSortActions
3460|
3461\---------------------------------------------------------------------------*/
3462static int doSortActions (
3463    xsltState       * xs,
3464    xpathResultSet  * nodelist,
3465    domNode         * actionNode,
3466    xpathResultSet  * context,
3467    domNode         * currentNode,
3468    int               currentPos,
3469    char           ** errMsg
3470)
3471{
3472    domNode       *child;
3473    char          *str, *evStr, *select, *lang;
3474    char         **vs = NULL;
3475    char           prefix[MAX_PREFIX_LEN];
3476    const char    *localName;
3477    double        *vd = NULL;
3478    int            rc = 0, typeText, ascending, upperFirst, *pos = NULL, i, NaN;
3479    xpathResultSet rs;
3480
3481    child = actionNode->lastChild; /* do it backwards, so that multiple sort
3482                                      levels are correctly processed */
3483    while (child) {
3484        if (child->nodeType == ELEMENT_NODE) {
3485            TRACE1("doSortActions child '%s' \n", child->nodeName);
3486            if (child->info == sort) {
3487                if (child->firstChild) {
3488                    reportError (child, "xsl:sort has to be empty.", errMsg);
3489                    rc = -1;
3490                    break;
3491                }
3492                typeText  = 1;
3493                ascending = 1;
3494                upperFirst = 1;
3495                select = getAttr(child, "select", a_select);
3496                if (!select) select = ".";
3497                xs->currentXSLTNode = child;
3498                str = getAttr(child, "data-type", a_dataType);
3499                if (str) {
3500                    rc = evalAttrTemplates (xs, context, currentNode,
3501                                            currentPos, str, &evStr, errMsg);
3502                    CHECK_RC;
3503                    if (strcmp(evStr,"text")==0) typeText = 1;
3504                    else if (strcmp(evStr,"number")==0) typeText = 0;
3505                    else {
3506                        domSplitQName (evStr, prefix, &localName);
3507                        if (prefix[0] == '\0') {
3508                            reportError (child, "data-type must be text, "
3509                                         "number or a prefixed name", errMsg);
3510                            FREE(evStr);
3511                            rc = -1;
3512                            break;
3513                        }
3514                        /* OK, so it is a legal value. But we currently
3515                           don't support non-standard data-types. We use
3516                           the default, that is typeText = 1. */
3517                    }
3518                    FREE(evStr);
3519                }
3520                str = getAttr(child, "order", a_order);
3521                if (str) {
3522                    rc = evalAttrTemplates (xs, context, currentNode,
3523                                            currentPos, str, &evStr, errMsg);
3524                    CHECK_RC;
3525                    if (strcmp(evStr,"descending")==0) ascending = 0;
3526                    else if (strcmp(evStr, "ascending")==0) ascending = 1;
3527                    else {
3528                        reportError (child, "order must be ascending or"
3529                                     " descending", errMsg);
3530                        FREE(evStr);
3531                        rc = -1;
3532                        break;
3533                    }
3534                    FREE(evStr);
3535                }
3536                str = getAttr(child, "case-order", a_caseorder);
3537                if (str) {
3538                    rc = evalAttrTemplates (xs, context, currentNode,
3539                                            currentPos, str, &evStr, errMsg);
3540                    CHECK_RC;
3541                    if (strcmp(evStr,"lower-first")==0) upperFirst = 0;
3542                    else if (strcmp(evStr, "upper-first")==0) upperFirst = 1;
3543                    else {
3544                        reportError (child, "case-order must be lower-first"
3545                                     " or upper-first", errMsg);
3546                        FREE(evStr);
3547                        rc = -1;
3548                        break;
3549                    }
3550                    FREE(evStr);
3551                }
3552                /* jcl: TODO */
3553                lang = getAttr(child, "lang", a_lang);
3554
3555                TRACE4("sorting with '%s' typeText %d ascending %d nodeSetLen=%d\n",
3556                       select, typeText, ascending, nodelist->nr_nodes);
3557                CHECK_RC;
3558                if (!pos)
3559                    pos = (int*)MALLOC(sizeof(int) * nodelist->nr_nodes);
3560                for (i=0; i<nodelist->nr_nodes;i++) pos[i] = i;
3561
3562                xs->currentXSLTNode = child;
3563
3564                if (!vs) {
3565                    vs = (char **)MALLOC(sizeof (char *) * nodelist->nr_nodes);
3566                    for (i=0; i<nodelist->nr_nodes;i++) vs[i] = NULL;
3567                    vd = (double *)MALLOC(sizeof (double) * nodelist->nr_nodes);
3568                    for (i=0; i<nodelist->nr_nodes;i++) vd[i] = 0.0;
3569                }
3570                for (i = 0; i < nodelist->nr_nodes; i++) {
3571                    xpathRSInit (&rs);
3572                    rc = evalXPath (xs, nodelist, nodelist->nodes[i], i,
3573                                    select, &rs, errMsg);
3574                    if (rc < 0)
3575                        goto doSortActionCleanUp;
3576
3577                    if (typeText) {
3578                        vs[i] = xpathFuncString (&rs);
3579                    } else {
3580                        vd[i] = xpathFuncNumber (&rs, &NaN);
3581                    }
3582                    xpathRSFree (&rs);
3583                }
3584                rc = sortNodeSetFastMerge (typeText, ascending, upperFirst,
3585                                           nodelist->nodes, nodelist->nr_nodes,
3586                                           vs, vd, pos, errMsg);
3587                if (typeText) {
3588                    for (i = 0; i < nodelist->nr_nodes; i++) {
3589                        FREE(vs[i]);
3590                    }
3591                }
3592                if (rc < 0)
3593                    goto doSortActionCleanUp;
3594            }
3595        }
3596        child = child->previousSibling;
3597    }
3598 doSortActionCleanUp:
3599    if (pos) FREE((char*)pos);
3600    if (vs) FREE((char*)vs);
3601    if (vd) FREE((char*)vd);
3602    return rc;
3603}
3604
3605
3606/*----------------------------------------------------------------------------
3607|   xsltNumber
3608|
3609\---------------------------------------------------------------------------*/
3610static int xsltNumber (
3611    xsltState       * xs,
3612    xpathResultSet  * context,
3613    domNode         * currentNode,
3614    int               currentPos,
3615    domNode         * actionNode,
3616    char           ** errMsg
3617)
3618{
3619    xpathResultSet    rs;
3620    int               rc, vs[20], NaN, hnew, i, useFormatToken, vVals = 0;
3621    int              *v, *vd = NULL;
3622    long              groupingSize = 0;
3623    char             *value, *level, *count, *from, *str, *str1, *format;
3624    char             *groupingSeparator = NULL, *groupingSizeStr = NULL;
3625    char             *tail;
3626    ast               t_count, t_from;
3627    domNode          *node, *start;
3628    Tcl_HashEntry    *h;
3629    xsltNumberFormat *f;
3630    Tcl_DString       dStr;
3631    domProcessingInstructionNode *pi;
3632
3633    v = vs;
3634    value = getAttr(actionNode, "value",  a_value);
3635    str   = getAttr(actionNode, "format", a_format); if (!str) str = "1";
3636    xs->currentXSLTNode = actionNode;
3637    rc = evalAttrTemplates( xs, context, currentNode, currentPos,
3638                            str, &format, errMsg);
3639    CHECK_RC;
3640    f = xsltNumberFormatTokenizer (xs, format, errMsg);
3641    if (!f) {
3642        FREE(format);
3643        return -1;
3644    }
3645    str = getAttr(actionNode, "grouping-separator", a_groupingSeparator);
3646    if (str) {
3647        str1 = getAttr (actionNode, "grouping-size", a_groupingSize);
3648        if (str1) {
3649            rc = evalAttrTemplates (xs, context, currentNode, currentPos, str,
3650                                    &groupingSeparator, errMsg);
3651            if (rc < 0) goto xsltNumberError;
3652            rc = evalAttrTemplates (xs, context, currentNode, currentPos, str1,
3653                                    &groupingSizeStr, errMsg);
3654            if (rc < 0) goto xsltNumberError;
3655            groupingSize = strtol (groupingSizeStr, &tail, 10);
3656            if (groupingSize <= 0) {
3657                /* This covers both cases: non integer value after evaluation
3658                   and wrong (<= 0) integer value. */
3659                reportError (actionNode, "The value of \"grouping-size\" must"
3660                             " evaluate to a positiv integer.", errMsg);
3661                goto xsltNumberError;
3662            }
3663        }
3664    }
3665
3666    if (value) {
3667        TRACE2("xsltNumber value='%s' format='%s' \n", value, format);
3668        rc = evalXPath(xs, context, currentNode, currentPos,
3669                       value, &rs, errMsg);
3670        if (rc < 0) goto xsltNumberError;
3671        vVals = 1;
3672        v[0] = xpathRound(xpathFuncNumber( &rs, &NaN ));
3673        /* MARK recoverable error */
3674        /* This is one of the not so satisfying corners of the xslt
3675         * rec. The rec doesn't say, what to do, if the value isn't a
3676         * (finit) number. E24 from the erratas doesn't makes things
3677         * much better - a little bit dubious wording and a not very
3678         * convincing decision. Well, at least saxon seems to follow
3679         * the words of E24. I'll postpone this topic. */
3680        if (NaN) v[0] = 0;
3681        xpathRSFree( &rs );
3682    } else {
3683        level = getAttr(actionNode, "level",  a_level);
3684        if (!level) level = "single";
3685        count = getAttr(actionNode, "count",  a_count);
3686        from  = getAttr(actionNode, "from",   a_from);
3687        TRACE3("xsltNumber  format='%s' count='%s' from='%s' \n", format, count, from);
3688        if (count) {
3689            h = Tcl_CreateHashEntry (&(xs->pattern), count, &hnew);
3690            if (!hnew) {
3691                t_count = (ast) Tcl_GetHashValue (h);
3692            } else {
3693                rc = xpathParse (count, actionNode, XPATH_FORMAT_PATTERN, NULL,
3694                                 NULL, &t_count, errMsg);
3695                if (rc < 0) goto xsltNumberError;
3696                Tcl_SetHashValue (h, t_count);
3697            }
3698        } else {
3699            Tcl_DStringInit (&dStr);
3700            if (currentNode->nodeType == ELEMENT_NODE) {
3701                /* TODO: This is wrong. Instead this should use the
3702                   "expanded-name" of the current node. */
3703                Tcl_DStringAppend (&dStr, currentNode->nodeName, -1);
3704            } else
3705            if (currentNode->nodeType == ATTRIBUTE_NODE) {
3706                Tcl_DStringAppend (&dStr, "@", 1);
3707                Tcl_DStringAppend (&dStr, currentNode->nodeName, -1);
3708            } else
3709            if (currentNode->nodeType == COMMENT_NODE) {
3710                Tcl_DStringAppend (&dStr, "comment()", -1);
3711            } else
3712            if (currentNode->nodeType == TEXT_NODE) {
3713                Tcl_DStringAppend (&dStr, "text()", -1);
3714            } else
3715            if (currentNode->nodeType == PROCESSING_INSTRUCTION_NODE) {
3716                Tcl_DStringAppend (&dStr, "processing-instruction('", -1);
3717                pi = (domProcessingInstructionNode *)currentNode;
3718                Tcl_DStringAppend (&dStr, pi->targetValue, pi->targetLength);
3719                Tcl_DStringAppend (&dStr, "')", 2);
3720            } else {
3721                reportError (actionNode, "unknown node type!!!", errMsg);
3722                return -1;
3723            }
3724            h = Tcl_CreateHashEntry (&(xs->pattern), Tcl_DStringValue(&dStr),
3725                                     &hnew);
3726            if (!hnew) {
3727                t_count = (ast) Tcl_GetHashValue (h);
3728            } else {
3729                rc = xpathParse (Tcl_DStringValue (&dStr), actionNode,
3730                                 XPATH_FORMAT_PATTERN, NULL, NULL, &t_count,
3731                                 errMsg);
3732                if (rc < 0) {
3733                    Tcl_DStringFree (&dStr);
3734                    goto xsltNumberError;
3735                }
3736                Tcl_SetHashValue (h, t_count);
3737            }
3738            Tcl_DStringFree (&dStr);
3739        }
3740        if (from) {
3741            h = Tcl_CreateHashEntry (&(xs->pattern), from, &hnew);
3742            if (!hnew) {
3743                t_from = (ast) Tcl_GetHashValue (h);
3744            } else {
3745                rc = xpathParse (from, actionNode, XPATH_FORMAT_PATTERN, NULL,
3746                                 NULL, &t_from, errMsg);
3747                if (rc < 0) goto xsltNumberError;
3748                Tcl_SetHashValue (h, t_from);
3749            }
3750        }
3751
3752        if (strcmp (level, "single")==0) {
3753            node = currentNode;
3754            start = NULL;
3755            if (from) {
3756                while (node) {
3757                    rc = xpathMatches (t_from, actionNode, node, &(xs->cbs),
3758                                       errMsg);
3759                    if (rc < 0) goto xsltNumberError;
3760                    if (rc) break;
3761                    if (node->nodeType == ATTRIBUTE_NODE)
3762                        node = ((domAttrNode *)node)->parentNode;
3763                    else node = node->parentNode;
3764                }
3765            }
3766            node = currentNode;
3767            while (node != start) {
3768                rc = xpathMatches (t_count, actionNode, node, &(xs->cbs),
3769                                   errMsg);
3770                if (rc < 0) goto xsltNumberError;
3771                if (rc) break;
3772                if (node->nodeType == ATTRIBUTE_NODE)
3773                    node = ((domAttrNode *)node)->parentNode;
3774                else node = node->parentNode;
3775            }
3776            if (node == start) {
3777                domAppendNewTextNode (xs->lastNode, "", 0, TEXT_NODE, 0);
3778                FREE(format);
3779                return 0;
3780            } else {
3781                vVals = 1;
3782                v[0] = 1;
3783                node = domPreviousSibling (node);
3784                while (node) {
3785                    rc = xpathMatches (t_count, actionNode, node, &(xs->cbs),
3786                                       errMsg);
3787                    if (rc < 0) goto xsltNumberError;
3788                    if (rc) v[0]++;
3789                    node = domPreviousSibling (node);
3790                }
3791            }
3792        } else
3793        if (strcmp (level, "multiple")==0) {
3794            xpathRSInit (&rs);
3795            node = currentNode;
3796            while (node) {
3797                if (from) {
3798                    rc = xpathMatches (t_from, actionNode, node, &(xs->cbs),
3799                                       errMsg);
3800                    if (rc < 0) goto xsltNumberError;
3801                    if (rc) break;
3802                }
3803                rc = xpathMatches (t_count, actionNode, node, &(xs->cbs),
3804                                   errMsg);
3805                if (rc < 0) goto xsltNumberError;
3806                if (rc) rsAddNode (&rs, node);
3807                if (node->nodeType == ATTRIBUTE_NODE)
3808                    node = ((domAttrNode *)node)->parentNode;
3809                else node = node->parentNode;
3810            }
3811            if (rs.nr_nodes > 20) {
3812                vd = (int *)MALLOC(sizeof (int) * rs.nr_nodes);
3813                v = vd;
3814            }
3815            vVals = rs.nr_nodes;
3816            v[0] = 0;
3817            for (i = 0;  i < rs.nr_nodes; i++) {
3818                node = domPreviousSibling (rs.nodes[i]);
3819                v[i] = 1;
3820                while (node) {
3821                    rc = xpathMatches (t_count, actionNode, node, &(xs->cbs),
3822                                       errMsg);
3823                    if (rc < 0) goto xsltNumberError;
3824                    if (rc) v[i]++;
3825                    node = domPreviousSibling (node);
3826                }
3827            }
3828            xpathRSFree (&rs);
3829        } else
3830        if (strcmp (level, "any")==0) {
3831            v[0] = 0;
3832            vVals = 1;
3833            node = currentNode;
3834            while (node) {
3835                if (from) {
3836                    rc = xpathMatches (t_from, actionNode, node, &(xs->cbs),
3837                                       errMsg);
3838                    if (rc < 0) goto xsltNumberError;
3839                    if (rc) break;
3840                }
3841                rc = xpathMatches (t_count, actionNode, node, &(xs->cbs),
3842                                   errMsg);
3843                if (rc < 0) goto xsltNumberError;
3844                if (rc) v[0]++;
3845                if (domPreviousSibling (node)) {
3846                    node = domPreviousSibling (node);
3847                    while ((node->nodeType == ELEMENT_NODE)
3848                           && node->lastChild) {
3849                        node = node->lastChild;
3850                    }
3851                    continue;
3852                }
3853                if (node->nodeType == ATTRIBUTE_NODE) {
3854                    node = ((domAttrNode *)node)->parentNode;
3855                } else {
3856                    node = node->parentNode;
3857                }
3858            }
3859        } else {
3860            reportError (actionNode, "xsl:number: Wrong \"level\" attribute"
3861                         " value!", errMsg);
3862            goto xsltNumberError;
3863        }
3864    }
3865
3866    Tcl_DStringInit (&dStr);
3867    useFormatToken = 0;
3868    if (f->prologLen) {
3869        Tcl_DStringAppend (&dStr, f->formatStr, f->prologLen);
3870    }
3871    for (i = 0; i < vVals -1; i++) {
3872        formatValue (f, &useFormatToken, v[i], &dStr,
3873                     groupingSeparator, groupingSize, 1);
3874    }
3875    if (vVals > 0) {
3876        formatValue (f, &useFormatToken, v[vVals-1], &dStr,
3877                     groupingSeparator, groupingSize, 0);
3878        if (f->epilogLen) {
3879            Tcl_DStringAppend (&dStr, f->epilogStart, f->epilogLen);
3880        }
3881        domAppendNewTextNode(xs->lastNode, Tcl_DStringValue (&dStr),
3882                             Tcl_DStringLength (&dStr), TEXT_NODE, 0);
3883    }
3884    FREE(format);
3885    if (groupingSeparator) FREE (groupingSeparator);
3886    if (groupingSizeStr) FREE (groupingSizeStr);
3887    if (vd) {
3888        FREE((char *)vd);
3889    }
3890    Tcl_DStringFree (&dStr);
3891    return 0;
3892
3893 xsltNumberError:
3894    if (format) FREE (format);
3895    if (groupingSeparator) FREE (groupingSeparator);
3896    if (groupingSizeStr) FREE (groupingSizeStr);
3897    return -1;
3898}
3899
3900/*----------------------------------------------------------------------------
3901|   ExecAction
3902|
3903\---------------------------------------------------------------------------*/
3904static int ExecAction (
3905    xsltState       * xs,
3906    xpathResultSet  * context,
3907    domNode         * currentNode,
3908    int               currentPos,
3909    domNode         * actionNode,
3910    char           ** errMsg
3911)
3912{
3913    domNode        *child, *n, *n1, *savedLastNode, *fragmentNode;
3914    xsltTemplate   *tpl, *currentTplRule, *tplChoosen;
3915    domAttrNode    *attr, *attr1;
3916    domTextNode    *tnode;
3917    domNS          *ns, *ns1;
3918    xsltSubDoc     *sDoc, *currentSubDoc;
3919    xsltExclExtNS  *eNS;
3920    xsltNSAlias    *nsAlias;
3921    Tcl_DString     dStr;
3922    domProcessingInstructionNode *pi;
3923    xpathResultSet  rs, nodeList;
3924    char           *str, *str2, *select, *pc, *nsAT, *out;
3925    const char     *mode, *modeURI, *localName, *uri, *nsStr;
3926    char            prefix[MAX_PREFIX_LEN];
3927    int             rc, b, i, len, terminate, chooseState, disableEsc = 0;
3928    double          currentPrio, currentPrec;
3929    Tcl_HashEntry  *h;
3930
3931    if (actionNode->nodeType == TEXT_NODE) {
3932        domAppendNewTextNode(xs->lastNode,
3933                             ((domTextNode*)actionNode)->nodeValue,
3934                             ((domTextNode*)actionNode)->valueLength,
3935                             TEXT_NODE, 0);
3936        return 0;
3937    }
3938    if (actionNode->nodeType != ELEMENT_NODE) return 0;
3939
3940    TRACE1("\nExecAction '%s' \n", actionNode->nodeName);
3941    DBG (printXML (currentNode, 3, 5);)
3942    xs->currentXSLTNode = actionNode;
3943    switch ( actionNode->info ) {
3944
3945        case applyImports:
3946            if (actionNode->firstChild) {
3947                reportError(actionNode, "xsl:apply-imports has to be empty!",
3948                            errMsg);
3949                return -1;
3950            }
3951            if (!xs->currentTplRule) {
3952                reportError(actionNode, "xsl:apply-imports not allowed here!",
3953                            errMsg);
3954                return -1;
3955            }
3956            tplChoosen = NULL;
3957            currentPrio = -100000.0;
3958            currentPrec = 0.0;
3959            mode = xs->currentTplRule->mode;
3960            modeURI = xs->currentTplRule->modeURI;
3961
3962            if (currentNode->nodeType == ELEMENT_NODE) {
3963                Tcl_DStringInit (&dStr);
3964                if (currentNode->namespace) {
3965                    domSplitQName (currentNode->nodeName, prefix, &localName);
3966                    Tcl_DStringAppend (&dStr, domNamespaceURI (currentNode),
3967                                       -1);
3968                    Tcl_DStringAppend (&dStr, ":", 1);
3969                }
3970                if (mode) {
3971                    if (modeURI) {
3972                        Tcl_DStringAppend (&dStr, modeURI, -1);
3973                        Tcl_DStringAppend (&dStr, ":", 1);
3974                    }
3975                    Tcl_DStringAppend (&dStr, mode, -1);
3976                    Tcl_DStringAppend (&dStr, ":", 1);
3977                }
3978                if (currentNode->namespace) {
3979                    Tcl_DStringAppend (&dStr, localName, -1);
3980                } else {
3981                    Tcl_DStringAppend (&dStr, currentNode->nodeName, -1);
3982                }
3983                h = Tcl_FindHashEntry (&xs->isElementTpls,
3984                                       Tcl_DStringValue (&dStr));
3985                Tcl_DStringFree (&dStr);
3986
3987                if (h) {
3988                    for (tpl = (xsltTemplate *) Tcl_GetHashValue (h);
3989                         tpl != NULL;
3990                         tpl = tpl->next) {
3991                        if (tpl->precedence > xs->currentTplRule->precedence
3992                            || tpl == xs->currentTplRule) continue;
3993                        TRACE3("find element tpl match='%s' mode='%s' name='%s'\n",
3994                               tpl->match, tpl->mode, tpl->name);
3995                        TRACE4("tpl has prio='%f' precedence='%f'\n", tpl->prio, tpl->precedence, currentPrio, currentPrec);
3996                        rc = xpathMatches ( tpl->ast, actionNode, currentNode,
3997                                            &(xs->cbs), errMsg);
3998                        if (rc < 0) {
3999                            TRACE1("xpathMatches had errors '%s' \n", *errMsg);
4000                            return rc;
4001                        }
4002                        if (rc == 0) continue;
4003                        TRACE3("matching '%s': %f > %f ? \n", tpl->match, tpl->prio , currentPrio);
4004                        tplChoosen = tpl;
4005                        currentPrio = tpl->prio;
4006                        currentPrec = tpl->precedence;
4007                        break;
4008                    }
4009                }
4010            }
4011
4012            TRACE2("apply-imports: current template precedence='%f' mode='%s'\n", xs->currentTplRule->precedence, xs->currentTplRule->mode);
4013            for (tpl = xs->templates; tpl != NULL; tpl = tpl->next) {
4014                TRACE4("find tpl match='%s' mode='%s' modeURI='%s' name='%s'\n",
4015                       tpl->match, tpl->mode, tpl->modeURI, tpl->name);
4016                /* exclude those, which don't match the current mode
4017                   and the currentTplRule */
4018                if (   ( mode && !tpl->mode)
4019                       || (!mode &&  tpl->mode)
4020                       || ( mode &&  tpl->mode && (strcmp(mode,tpl->mode)!=0))
4021                       || (!modeURI && tpl->modeURI)
4022                       || ( modeURI && !tpl->modeURI)
4023                       || ( modeURI && tpl->modeURI && (strcmp(modeURI, tpl->modeURI)!=0))
4024                       || (tpl == xs->currentTplRule)
4025                    ) {
4026                    TRACE("doesn't match mode\n");
4027                    continue;
4028                }
4029                TRACE4("tpl has prio='%f' precedence='%f', currentPrio='%f', currentPrec='%f'\n", tpl->prio, tpl->precedence, currentPrio, currentPrec);
4030                if (tpl->match && tpl->precedence < xs->currentTplRule->precedence
4031                    && tpl->precedence >= currentPrec) {
4032                    if (tpl->precedence > currentPrec
4033                        || tpl->prio >= currentPrio) {
4034                        rc = xpathMatches (tpl->ast, actionNode, currentNode,
4035                                           &(xs->cbs), errMsg);
4036                        CHECK_RC;
4037                        if (rc == 0) continue;
4038                        TRACE3("matching '%s': %f > %f ? \n", tpl->match, tpl->prio , currentPrio);
4039                        tplChoosen = tpl;
4040                        currentPrec = tpl->precedence;
4041                        currentPrio = tpl->prio;
4042                        TRACE1("TAKING '%s' \n", tpl->match);
4043                    }
4044                }
4045            }
4046
4047            if (tplChoosen == NULL) {
4048
4049                TRACE("nothing matches -> execute built-in template \n");
4050
4051                /*--------------------------------------------------------------------
4052                |   execute built-in template
4053                \-------------------------------------------------------------------*/
4054                if (currentNode->nodeType == TEXT_NODE) {
4055                    domAppendNewTextNode(xs->lastNode,
4056                                         ((domTextNode*)currentNode)->nodeValue,
4057                                         ((domTextNode*)currentNode)->valueLength,
4058                                         TEXT_NODE, 0);
4059                    break;
4060                } else
4061                if (currentNode->nodeType == ELEMENT_NODE) {
4062                    child = currentNode->firstChild;
4063                } else
4064                if (currentNode->nodeType == ATTRIBUTE_NODE) {
4065                    domAppendNewTextNode (xs->lastNode,
4066                                          ((domAttrNode*)currentNode)->nodeValue,
4067                                          ((domAttrNode*)currentNode)->valueLength,
4068                                          TEXT_NODE, 0);
4069                    break;
4070                } else {
4071                    /* for all other node we don't have to recurse deeper */
4072                    break;
4073                }
4074                xpathRSInit( &rs );
4075                while (child) {
4076                    rsAddNodeFast ( &rs, child);
4077                    child = child->nextSibling;
4078                }
4079                rc = ApplyTemplates (xs, context, currentNode, currentPos,
4080                                actionNode, &rs, mode, modeURI, errMsg);
4081                xpathRSFree( &rs );
4082                CHECK_RC;
4083
4084                break;
4085            }
4086
4087            xsltPushVarFrame (xs);
4088            SETSCOPESTART;
4089            currentTplRule = xs->currentTplRule;
4090            currentSubDoc = xs->currentSubDoc;
4091            xs->currentTplRule = tplChoosen;
4092            xs->currentSubDoc = tplChoosen->sDoc;
4093            rc = ExecActions(xs, context, currentNode, currentPos,
4094                             tplChoosen->content->firstChild, errMsg);
4095            xsltPopVarFrame (xs);
4096            CHECK_RC;
4097            xs->currentTplRule = currentTplRule;
4098            xs->currentSubDoc = currentSubDoc;
4099            break;
4100
4101        case applyTemplates:
4102            mode    = NULL;
4103            modeURI = NULL;
4104            str     = getAttr (actionNode, "mode", a_mode);
4105            if (str) {
4106                domSplitQName (str, prefix, &localName);
4107                if (prefix[0] != '\0') {
4108                    ns = domLookupPrefix (actionNode, prefix);
4109                    if (!ns) {
4110                        reportError (actionNode, "The prefix of the \"name\""
4111                                     " attribute value isn't bound to a"
4112                                     " namespace.", errMsg);
4113                        return -1;
4114                    }
4115                    modeURI = ns->uri;
4116                }
4117                mode = localName;
4118            }
4119            select  = getAttr(actionNode, "select", a_select);
4120            if (!select) {
4121                xpathRSInit (&rs);
4122                if (currentNode->nodeType == ELEMENT_NODE) {
4123                    child = currentNode->firstChild;
4124                    while (child) {
4125                        rsAddNodeFast (&rs, child);
4126                        child = child->nextSibling;
4127                    }
4128                }
4129            } else {
4130                xpathRSInit( &nodeList );
4131                rsAddNodeFast( &nodeList, currentNode );
4132                DBG(rsPrint( &nodeList ));
4133                TRACE2("applyTemplates: select = '%s' mode='%s'\n", select, mode);
4134                rc = evalXPath(xs, &nodeList, currentNode, 1, select, &rs, errMsg);
4135                xpathRSFree( &nodeList );
4136                CHECK_RC;
4137                TRACE1("applyTemplates: evalXPath for select = '%s' gave back:\n", select);
4138                DBG(rsPrint(&rs));
4139            }
4140
4141            if (rs.type == EmptyResult) break;
4142            else if (rs.type != xNodeSetResult) {
4143                reportError (actionNode, "The \"select\" expression of"
4144                             " xsl:apply-templates elements must evaluate to"
4145                             " a node set.", errMsg);
4146                xpathRSFree (&rs);
4147                return -1;
4148            }
4149
4150            rc = doSortActions (xs, &rs, actionNode, context, currentNode,
4151                                currentPos, errMsg);
4152            if (rc < 0) {
4153                xpathRSFree (&rs);
4154                return rc;
4155            }
4156            /* should not be necessary, because every node set is
4157               returned already in doc Order */
4158            /*  if (!sorted) sortByDocOrder(&rs); */
4159
4160            TRACE1("                evalXPath for select = '%s': (SORTED)\n", select);
4161            DBG(rsPrint(&rs));
4162
4163            rc = ApplyTemplates(xs, context, currentNode, currentPos,
4164                                actionNode, &rs, mode, modeURI, errMsg);
4165            xpathRSFree( &rs );
4166            CHECK_RC;
4167            break;
4168
4169        case attribute:
4170            if (xs->lastNode->firstChild) {
4171                /* Adding an Attribute to an element after
4172                   children have been added to it is an error.
4173                   Ignore the attribute. */
4174                break;
4175            }
4176            nsAT = getAttr(actionNode, "namespace", a_namespace);
4177            str = getAttr(actionNode, "name", a_name);
4178            if (!str) {
4179                reportError (actionNode, "xsl:attribute: missing mandatory"
4180                             " attribute \"name\".", errMsg);
4181                return -1;
4182            }
4183
4184            rc = evalAttrTemplates( xs, context, currentNode, currentPos,
4185                                    str, &str2, errMsg);
4186            CHECK_RC;
4187            domSplitQName (str2, prefix, &localName);
4188            if ((prefix[0] != '\0' &&  !domIsNCNAME (prefix))
4189                 || !domIsNCNAME (localName)) {
4190                reportError (actionNode, "xsl:attribute: Attribute name is not"
4191                             " a valid QName.", errMsg);
4192                FREE(str2);
4193                return -1;
4194            }
4195            out = NULL;
4196            if (nsAT) {
4197                rc = evalAttrTemplates( xs, context, currentNode, currentPos,
4198                                        nsAT, &out, errMsg);
4199                CHECK_RC1(str2);
4200            }
4201
4202            Tcl_DStringInit (&dStr);
4203            if (prefix[0] == '\0' && (strcmp(str2, "xmlns")==0)) {
4204                goto ignoreAttribute;
4205            }
4206            /* It isn't allowed to create namespace attributes with
4207               xsl:attribute, a "xmlns" prefix must be rewritten; see
4208               XSLT rec 7.1.3 */
4209            if (prefix[0] && (strcmp(prefix, "xmlns")==0)) {
4210                sprintf (prefix, "ns%d", xs->nsUniqeNr++);
4211                Tcl_DStringAppend (&dStr, prefix, -1);
4212                Tcl_DStringAppend (&dStr, ":", 1);
4213                Tcl_DStringAppend (&dStr, localName, -1);
4214            } else {
4215                if (out) {
4216                    if (out[0] == '\0') {
4217                        if (prefix[0] != '\0') {
4218                            Tcl_DStringAppend (&dStr, localName, -1);
4219                        } else {
4220                            Tcl_DStringAppend (&dStr, str2, -1);
4221                        }
4222                        FREE(out);
4223                        out = NULL;
4224                    } else {
4225                        if (prefix[0] == '\0') {
4226                            ns = domLookupURI (xs->lastNode, out);
4227                            if (ns && (ns->prefix[0] != '\0')) {
4228                                Tcl_DStringAppend (&dStr, ns->prefix, -1);
4229                                Tcl_DStringAppend (&dStr, ":", 1);
4230                            } else {
4231                                sprintf (prefix, "ns%d", xs->nsUniqeNr++);
4232                                Tcl_DStringAppend (&dStr, prefix, -1);
4233                                Tcl_DStringAppend (&dStr, ":", 1);
4234                            }
4235                        }
4236                        Tcl_DStringAppend (&dStr, str2, -1);
4237                    }
4238                } else {
4239                    if (prefix[0] != '\0') {
4240                        ns = domLookupPrefix (actionNode, prefix);
4241                        if (ns) out = tdomstrdup (ns->uri);
4242                        else goto ignoreAttribute;
4243                    }
4244                    Tcl_DStringAppend (&dStr, str2, -1);
4245                }
4246            }
4247
4248            savedLastNode = xs->lastNode;
4249            xs->lastNode  = domNewElementNode (xs->resultDoc,
4250                                               "container", ELEMENT_NODE);
4251            xsltPushVarFrame (xs);
4252            rc = ExecActions(xs, context, currentNode, currentPos,
4253                             actionNode->firstChild, errMsg);
4254            xsltPopVarFrame (xs);
4255            if (rc < 0) {
4256                if (out) FREE(out);
4257                FREE(str2);
4258                return rc;
4259            }
4260            pc = xpathGetStringValue (xs->lastNode, &len);
4261            DBG(fprintf (stderr, "xsl:attribute: create attribute \"%s\" with value \"%s\" in namespace \"%s\"\n", Tcl_DStringValue (&dStr), pc, out);)
4262            domSetAttributeNS (savedLastNode, Tcl_DStringValue (&dStr), pc,
4263                               out, 1);
4264            FREE(pc);
4265            Tcl_DStringFree (&dStr);
4266            domDeleteNode (xs->lastNode, NULL, NULL);
4267            xs->lastNode = savedLastNode;
4268    ignoreAttribute:
4269            if (out) FREE(out);
4270            FREE(str2);
4271            break;
4272
4273        case attributeSet:
4274            reportError (actionNode, "xsl:attribute-set is only allowed at"
4275                         " top-level.", errMsg);
4276            return -1;
4277
4278        case callTemplate:
4279            tplChoosen = NULL;
4280            currentPrec = INT_MIN;
4281            str = getAttr(actionNode, "name", a_name);
4282            if (!str) {
4283                reportError (actionNode, "xsl:call-template must have a"
4284                             " \"name\" attribute", errMsg);
4285                return -1;
4286            }
4287            domSplitQName (str, prefix, &localName);
4288            uri = NULL;
4289            if (prefix[0] != '\0') {
4290                ns = domLookupPrefix (actionNode, prefix);
4291                if (!ns) {
4292                    reportError (actionNode, "The prefix of the \"name\""
4293                                 " attribute value isn't bound to a"
4294                                 " namespace.", errMsg);
4295                    return -1;
4296                }
4297                uri = ns->uri;
4298            }
4299            if (uri) {
4300                Tcl_DStringInit (&dStr);
4301                Tcl_DStringAppend (&dStr, uri, -1);
4302                Tcl_DStringAppend (&dStr, ":", 1);
4303                Tcl_DStringAppend (&dStr, localName, -1);
4304                h = Tcl_FindHashEntry (&(xs->namedTemplates),
4305                                       Tcl_DStringValue (&dStr));
4306                Tcl_DStringFree (&dStr);
4307            } else {
4308                h = Tcl_FindHashEntry (&(xs->namedTemplates), localName);
4309            }
4310            if (!h) {
4311                reportError (actionNode, "xsl:call-template calls a non"
4312                             " existend template!", errMsg);
4313                return -1;
4314            }
4315            tplChoosen = (xsltTemplate *) Tcl_GetHashValue (h);
4316            xsltPushVarFrame (xs);
4317            SETPARAMDEF;
4318            TRACE3("call template %s match='%s' name='%s' \n", localName,
4319                   tplChoosen->match, tplChoosen->name);
4320            DBG(printXML(xs->lastNode, 0, 2);)
4321            rc = setParamVars (xs, context, currentNode, currentPos,
4322                               actionNode, errMsg);
4323            if (rc < 0) {
4324                xsltPopVarFrame (xs);
4325                return rc;
4326            }
4327            currentSubDoc = xs->currentSubDoc;
4328            xs->currentSubDoc = tplChoosen->sDoc;
4329            SETSCOPESTART;
4330            rc = ExecActions(xs, context, currentNode, currentPos,
4331                             tplChoosen->content->firstChild, errMsg);
4332            TRACE2("called template '%s': ApplyTemplate/ExecActions rc = %d \n", localName, rc);
4333            xsltPopVarFrame (xs);
4334            CHECK_RC;
4335            xs->currentSubDoc = currentSubDoc;
4336            DBG(printXML(xs->lastNode, 0, 2);)
4337            break;
4338
4339       case choose:
4340            chooseState = 0;
4341            for( child = actionNode->firstChild;  child != NULL;
4342                 child = child->nextSibling)
4343            {
4344                if (child->nodeType != ELEMENT_NODE) continue;
4345                switch (child->info) {
4346                    case when:
4347                        if (chooseState > 1) {
4348                            reportError (actionNode, "\"otherwise\" clause"
4349                                         " must be after all \"when\""
4350                                         " clauses", errMsg);
4351                            return -1;
4352                        } else {
4353                            chooseState = 1;
4354                        }
4355                        str = getAttr(child, "test", a_test);
4356                        if (str) {
4357                            TRACE1("checking when test '%s' \n", str);
4358                            rc = evalXPath(xs, context, currentNode,
4359                                           currentPos, str, &rs, errMsg);
4360                            CHECK_RC;
4361                            b = xpathFuncBoolean( &rs );
4362                            xpathRSFree( &rs );
4363                            if (b) {
4364                                TRACE("test is true!\n");
4365                                /* process the children as well */
4366                                xsltPushVarFrame (xs);
4367                                rc = ExecActions(xs, context,
4368                                                 currentNode, currentPos,
4369                                                 child->firstChild, errMsg);
4370                                xsltPopVarFrame (xs);
4371                                CHECK_RC;
4372                                return 0;
4373                            }
4374                        } else {
4375                            reportError (child, "xsl:when: missing mandatory"
4376                                         " attribute \"test\".", errMsg);
4377                            return -1;
4378                        }
4379                        break;
4380
4381                    case otherwise:
4382                        if (chooseState != 1) {
4383                            if (chooseState == 0) {
4384                                reportError (actionNode, "\"choose\" must"
4385                                             " have at least one \"when\""
4386                                             " clause", errMsg);
4387                            } else {
4388                                reportError (child, "only one \"otherwise\""
4389                                             " clause allowed inside a"
4390                                             " \"choose\"", errMsg);
4391                            }
4392                            return -1;
4393                        } else {
4394                            chooseState = 2;
4395                        }
4396                        /* process the children as well */
4397                        xsltPushVarFrame (xs);
4398                        rc = ExecActions(xs, context, currentNode, currentPos,
4399                                         child->firstChild, errMsg);
4400                        xsltPopVarFrame (xs);
4401                        CHECK_RC;
4402                        break;
4403
4404                    default:
4405                        reportError (actionNode, "only otherwise or when"
4406                                     " allowed in choose!",
4407                                     errMsg);
4408                        return -1;
4409                }
4410            }
4411            if (chooseState == 0) {
4412                reportError (actionNode, "\"choose\" must have at least"
4413                             " one \"when\" clause", errMsg);
4414                return -1;
4415            }
4416            break;
4417
4418        case comment:
4419            fragmentNode = domNewElementNode(xs->resultDoc, "", ELEMENT_NODE);
4420            savedLastNode = xs->lastNode;
4421            xs->lastNode = fragmentNode;
4422            xsltPushVarFrame (xs);
4423            rc = ExecActions(xs, context, currentNode, currentPos,
4424                             actionNode->firstChild, errMsg);
4425            xsltPopVarFrame (xs);
4426            CHECK_RC;
4427            child = fragmentNode->firstChild;
4428            while (child) {
4429                if (child->nodeType != TEXT_NODE) {
4430                    domDeleteNode (fragmentNode, NULL, NULL);
4431                    reportError (actionNode, "xsl:comment must not create"
4432                                 " nodes other than text nodes.", errMsg);
4433                    return -1;
4434                }
4435                child = child->nextSibling;
4436            }
4437            str = xpathGetStringValue (fragmentNode, &len);
4438            if (!domIsComment (str)) {
4439                reportError (actionNode, "Invalide comment value", errMsg);
4440                domDeleteNode (fragmentNode, NULL, NULL);
4441                FREE(str);
4442                return -1;
4443            }
4444            xs->lastNode = savedLastNode;
4445            domAppendNewTextNode(xs->lastNode, str, len, COMMENT_NODE, 0);
4446            domDeleteNode (fragmentNode, NULL, NULL);
4447            FREE(str);
4448            break;
4449
4450        case copy:
4451            DBG(if (currentNode->nodeType == ATTRIBUTE_NODE) {
4452                    fprintf(stderr, "copy '%s' \n", ((domAttrNode*)currentNode)->nodeName);
4453                } else {
4454                    fprintf(stderr, "copy '%s' \n", currentNode->nodeName);
4455                })
4456            if (currentNode->nodeType == TEXT_NODE) {
4457                DBG(fprintf(stderr, "node is TEXT_NODE \n");)
4458                tnode = (domTextNode*)currentNode;
4459                n = (domNode*)
4460                    domAppendNewTextNode(xs->lastNode,
4461                                         tnode->nodeValue,
4462                                         tnode->valueLength,
4463                                         TEXT_NODE, 0);
4464            } else
4465            if (currentNode->nodeType == ELEMENT_NODE) {
4466                DBG(fprintf(stderr, "node is ELEMENT_NODE \n");)
4467                savedLastNode = xs->lastNode;
4468                if (currentNode != currentNode->ownerDocument->rootNode) {
4469                    n = domAppendNewElementNode(xs->lastNode,
4470                                                currentNode->nodeName,
4471                                                domNamespaceURI(currentNode) );
4472                    xs->lastNode = n;
4473                    str = getAttr(actionNode, "use-attribute-sets",
4474                              a_useAttributeSets);
4475                    domCopyNS (currentNode, xs->lastNode);
4476                    if (str) {
4477                        rc = ExecUseAttributeSets (xs, context, currentNode,
4478                                                   currentPos, actionNode,
4479                                                   str, errMsg);
4480                        CHECK_RC;
4481                    }
4482                }
4483                /* process the children only for root and element nodes */
4484                xsltPushVarFrame (xs);
4485                rc = ExecActions(xs, context, currentNode, currentPos,
4486                                 actionNode->firstChild, errMsg);
4487                xsltPopVarFrame (xs);
4488                CHECK_RC;
4489                xs->lastNode = savedLastNode;
4490            } else
4491            if (currentNode->nodeType == PROCESSING_INSTRUCTION_NODE) {
4492                pi = (domProcessingInstructionNode*)currentNode;
4493                n = (domNode*)
4494                    domNewProcessingInstructionNode (xs->lastNode->ownerDocument,
4495                                                     pi->targetValue,
4496                                                     pi->targetLength,
4497                                                     pi->dataValue,
4498                                                     pi->dataLength);
4499                domAppendChild (xs->lastNode, n);
4500
4501            } else
4502            if (currentNode->nodeType == COMMENT_NODE) {
4503                DBG(fprintf(stderr, "node is COMMENT_NODE \n");)
4504                tnode = (domTextNode *)currentNode;
4505                n = (domNode *) domAppendNewTextNode (xs->lastNode,
4506                                                      tnode->nodeValue,
4507                                                      tnode->valueLength,
4508                                                      COMMENT_NODE, 0);
4509            } else
4510            if (currentNode->nodeType == ATTRIBUTE_NODE) {
4511                DBG(fprintf(stderr, "node is ATTRIBUTE_NODE \n");)
4512                if (xs->lastNode->firstChild) {
4513                    /* Adding an Attribute to an element after
4514                       children have been added to it is an error.
4515                       Ignore the attribute. */
4516                    break;
4517                }
4518                if (xs->lastNode == xs->resultDoc->rootNode) {
4519                    reportError (actionNode, "Cannot write an attribute"
4520                                 " when there is no open start tag", errMsg);
4521                    return -1;
4522                }
4523                attr = (domAttrNode *)currentNode;
4524                domSetAttributeNS (xs->lastNode, attr->nodeName,
4525                                   attr->nodeValue,
4526                                   domNamespaceURI (currentNode), 1);
4527            }
4528            break;
4529
4530        case copyOf:
4531            if (actionNode->firstChild) {
4532                reportError (actionNode, "xsl:copy-of has to be empty.",
4533                             errMsg);
4534                return -1;
4535            }
4536            select = getAttr(actionNode, "select", a_select);
4537            if (!select) {
4538                reportError (actionNode, "xsl:copy-of: missing mandatory"
4539                             " attribute \"select\".", errMsg);
4540                return -1;
4541            }
4542
4543            rc = evalXPath(xs, context, currentNode, currentPos, select,
4544                           &rs, errMsg);
4545            CHECK_RC;
4546            TRACE1(" copyOf select='%s':\n", select);
4547            DBG(rsPrint(&rs));
4548            if (rs.type == xNodeSetResult) {
4549                for (i=0; i<rs.nr_nodes; i++) {
4550                    if (rs.nodes[i]->nodeType == ATTRIBUTE_NODE) {
4551                        attr = (domAttrNode*)rs.nodes[i];
4552                        if (attr ->nodeFlags & IS_NS_NODE) {
4553                            /* If someone selects explicitely namespace nodes
4554                               to copy-of with e.g namespace::* (remember: @*
4555                               doesn't select namespace nodes), we must this
4556                               handle seperately.*/
4557                            /* The xmlns:xml namespace node will always
4558                               be in scope, but never needed to be copied,
4559                               because the result tree will also always
4560                               already have it. To suppress, that the result
4561                               tree gets glutted with xmlns:xml declarations
4562                               (they would not harm, but ev. irritate some and
4563                               are unnecessary, we check this here as a
4564                               special case */
4565                            if (attr->namespace == 1) {
4566                                continue;
4567                            }
4568                            ns = NULL;
4569                        } else {
4570                            ns = domGetNamespaceByIndex (
4571                                attr->parentNode->ownerDocument,
4572                                attr->namespace
4573                                );
4574                        }
4575                        if (ns) uri = ns->uri;
4576                        else uri = NULL;
4577                        if (xs->lastNode == xs->resultDoc->rootNode) {
4578                            reportError (actionNode, "Cannot write an"
4579                                         " attribute when there is no open"
4580                                         " start tag", errMsg);
4581                            xpathRSFree (&rs);
4582                            return -1;
4583                        }
4584                        domSetAttributeNS(xs->lastNode, attr->nodeName,
4585                                          attr->nodeValue, uri, 1);
4586                    } else {
4587                        if (*(rs.nodes[i]->nodeName) == '\0') {
4588                            /* The rootNode of the Document or the rootNode
4589                               of a result tree fragment */
4590                            child = rs.nodes[i]->firstChild;
4591                            while (child) {
4592                                domCopyTo(child, xs->lastNode, 1);
4593                                child = child->nextSibling;
4594                            }
4595                        } else {
4596                            domCopyTo (rs.nodes[i], xs->lastNode, 1);
4597                        }
4598                    }
4599                }
4600            } else {
4601                str = xpathFuncString( &rs );
4602                TRACE1("copyOf: xpathString='%s' \n", str);
4603                domAppendNewTextNode(xs->lastNode, str, strlen(str),
4604                                     TEXT_NODE, 0);
4605                FREE(str);
4606            }
4607            xpathRSFree( &rs );
4608            break;
4609
4610        case decimalFormat:
4611            reportError (actionNode, "xsl:decimal-format is only allowed"
4612                         " at toplevel.", errMsg);
4613            return -1;
4614
4615        case element:
4616            nsAT = getAttr(actionNode, "namespace", a_namespace);
4617            str  = getAttr(actionNode, "name", a_name);
4618            if (!str) {
4619                reportError (actionNode, "xsl:element: missing mandatory"
4620                             " attribute \"name\".", errMsg);
4621                return -1;
4622            }
4623
4624            rc = evalAttrTemplates( xs, context, currentNode, currentPos,
4625                                    str, &str2, errMsg);
4626            CHECK_RC;
4627            if (!domIsNAME (str2)) {
4628                reportError (actionNode, "xsl:element: Element name is not a"
4629                             " valid QName.", errMsg);
4630                FREE(str2);
4631                return -1;
4632            }
4633            out = NULL;
4634            nsStr = NULL;
4635            if (nsAT) {
4636                rc = evalAttrTemplates( xs, context, currentNode, currentPos,
4637                                        nsAT, &out, errMsg);
4638                CHECK_RC1(str2);
4639                nsStr = out;
4640            } else {
4641                domSplitQName (str2, prefix, &localName);
4642                if ((prefix[0] != '\0' &&  !domIsNCNAME (prefix))
4643                    || !domIsNCNAME (localName)) {
4644                    reportError (actionNode, "xsl:element: Element name is"
4645                                 " not a valid QName.", errMsg);
4646                    FREE(str2);
4647                    return -1;
4648                }
4649                ns = domLookupPrefix (actionNode, prefix);
4650                if (ns) nsStr = ns->uri;
4651                else {
4652                    if (prefix[0] != '\0') {
4653                        reportError (actionNode, "xsl:element: there isn't"
4654                                     " a URI associated with the prefix of"
4655                                     " the element name.", errMsg);
4656                        FREE(str2);
4657                        return -1;
4658                    }
4659                }
4660            }
4661            savedLastNode = xs->lastNode;
4662            xs->lastNode = domAppendNewElementNode (xs->lastNode, str2, nsStr);
4663            FREE(str2);
4664            if (nsAT) FREE(out);
4665            str = getAttr(actionNode, "use-attribute-sets", a_useAttributeSets);
4666            if (str) {
4667                TRACE1("use-attribute-sets = '%s' \n", str);
4668                rc = ExecUseAttributeSets (xs, context, currentNode,
4669                                           currentPos, actionNode,
4670                                           str, errMsg);
4671                CHECK_RC;
4672            }
4673            /* process the children as well */
4674            if (actionNode->firstChild) {
4675                xsltPushVarFrame (xs);
4676                rc = ExecActions(xs, context, currentNode, currentPos,
4677                                 actionNode->firstChild, errMsg);
4678                xsltPopVarFrame (xs);
4679            }
4680            xs->lastNode = savedLastNode;
4681            CHECK_RC;
4682            break;
4683
4684        case fallback: return 0;
4685
4686        case forEach:
4687            select = getAttr(actionNode, "select", a_select);
4688            if (!select) {
4689                reportError (actionNode, "xsl:for-each: The select attribute"
4690                             " is required.", errMsg);
4691                return -1;
4692            }
4693            DBG (
4694              if (currentNode->nodeType == ELEMENT_NODE) {
4695                  fprintf (stderr,
4696                        "forEach select from Element Node '%s' domNode0x%x:\n",
4697                           currentNode->nodeName, currentNode);
4698                  if (currentNode->firstChild) {
4699                      fprintf(stderr,
4700                              "forEach select from child '%s' domNode0x%x:\n",
4701                              currentNode->firstChild->nodeName,
4702                              currentNode->firstChild);
4703                  }
4704              } else if (currentNode->nodeType == ATTRIBUTE_NODE) {
4705                  fprintf (stderr, "forEach select from Attribute Node '%s' Value '%s'\n", ((domAttrNode *)currentNode)->nodeName, ((domAttrNode *)currentNode)->nodeValue);
4706              } else {
4707                  fprintf (stderr, "forEach select from nodetype %d\n", currentNode->nodeType);
4708              }
4709            )
4710            xpathRSInit( &nodeList );
4711            rsAddNodeFast( &nodeList, currentNode );
4712            DBG(rsPrint( &nodeList ));
4713            rc = evalXPath(xs, &nodeList, currentNode, 1, select, &rs, errMsg);
4714            xpathRSFree (&nodeList);
4715            if (rc < 0) {
4716                return rc;
4717            }
4718            CHECK_RC;
4719            TRACE1("forEach: evalXPath for select = '%s' gave back:\n", select);
4720            DBG(rsPrint(&rs));
4721
4722            if (rs.type == xNodeSetResult) {
4723                rc = doSortActions (xs, &rs, actionNode, context, currentNode,
4724                                    currentPos, errMsg);
4725                if (rc < 0) {
4726                    xpathRSFree (&rs);
4727                    return rc;
4728                }
4729                /* should not be necessary, because every node set is
4730                   returned already in doc Order */
4731                /*  if (!sorted) sortByDocOrder(&rs); */
4732
4733                TRACE1(" forEach for select = '%s': (SORTED)\n", select);
4734                DBG(rsPrint(&rs));
4735                currentTplRule = xs->currentTplRule;
4736                xs->currentTplRule = NULL;
4737                xsltPushVarFrame (xs);
4738                for (i=0; i<rs.nr_nodes; i++) {
4739                    /* process the children as well */
4740                    rc = ExecActions(xs, &rs, rs.nodes[i], i,
4741                                     actionNode->firstChild, errMsg);
4742                    if (rc < 0) {
4743                        xsltPopVarFrame (xs);
4744                        xpathRSFree( &rs );
4745                        return rc;
4746                    }
4747                    if ((&xs->varFramesStack[xs->varFramesStackPtr])->polluted) {
4748                        xsltPopVarFrame (xs);
4749                        xsltPushVarFrame (xs);
4750                    }
4751                }
4752                xsltPopVarFrame (xs);
4753                xs->currentTplRule = currentTplRule;
4754            } else {
4755                if (rs.type != EmptyResult) {
4756                    reportError (actionNode, "The \"select\" expression of"
4757                                 " xsl:for-each elements must evaluate to a"
4758                                 " node set.", errMsg);
4759                    xpathRSFree (&rs);
4760                    return -1;
4761                }
4762            }
4763            xpathRSFree( &rs );
4764            break;
4765
4766        case xsltIf:
4767            str = getAttr(actionNode, "test", a_test);
4768            if (str) {
4769                rc = evalXPath(xs, context, currentNode, currentPos, str,
4770                               &rs, errMsg);
4771                CHECK_RC;
4772                b = xpathFuncBoolean( &rs );
4773                xpathRSFree( &rs );
4774                if (b) {
4775                    /* process the children as well */
4776                    xsltPushVarFrame (xs);
4777                    rc = ExecActions(xs, context, currentNode, currentPos,
4778                                     actionNode->firstChild, errMsg);
4779                    xsltPopVarFrame (xs);
4780                    CHECK_RC;
4781                }
4782            } else {
4783                reportError (actionNode, "xsl:if: missing mandatory attribute"
4784                             " \"test\".", errMsg);
4785                return -1;
4786            }
4787            break;
4788
4789        case import:
4790            reportError (actionNode, "xsl:import is only allowed at toplevel.",
4791                         errMsg);
4792            return -1;
4793        case include:
4794            reportError (actionNode, "xsl:include is only allowed at"
4795                         " toplevel.", errMsg);
4796            return -1;
4797        case key:
4798            reportError (actionNode, "xsl:key is only allowed at toplevel.",
4799                         errMsg);
4800            return -1;
4801
4802        case message:
4803            str  = getAttr(actionNode,"terminate", a_terminate);
4804            if (!str) terminate = 0;
4805            else if (strcmp (str, "yes") == 0) terminate = 1;
4806            else if (strcmp (str, "no")  == 0) terminate = 0;
4807            else {
4808                reportError (actionNode, "Value for terminate should equal"
4809                             " 'yes' or 'no'", errMsg);
4810                return -1;
4811            }
4812            fragmentNode = domNewElementNode(xs->resultDoc, "",
4813                                             ELEMENT_NODE);
4814            savedLastNode = xs->lastNode;
4815            xs->lastNode = fragmentNode;
4816            xsltPushVarFrame (xs);
4817            rc = ExecActions(xs, context, currentNode, currentPos,
4818                             actionNode->firstChild, errMsg);
4819            xsltPopVarFrame (xs);
4820            CHECK_RC;
4821
4822            str2 = xpathGetStringValue(fragmentNode, &len);
4823            xs->xsltMsgCB (xs->xsltMsgClientData, str2, len, terminate);
4824            FREE(str2);
4825            xs->lastNode = savedLastNode;
4826            domDeleteNode (fragmentNode, NULL, NULL);
4827            if (terminate) {
4828                reportError (actionNode, "xsl:message with attribute"
4829                             " \"terminate\"=\"yes\"", errMsg);
4830                return -1;
4831            }
4832            return 0;
4833
4834        case namespaceAlias:
4835            reportError (actionNode, "xsl:namespaceAlias is only allowed"
4836                         " at toplevel.", errMsg);
4837            return -1;
4838
4839        case number:
4840            if (actionNode->firstChild) {
4841                reportError (actionNode, "xsl:number has to be empty.",
4842                             errMsg);
4843                return -1;
4844            }
4845            rc = xsltNumber(xs, context, currentNode, currentPos,
4846                            actionNode, errMsg);
4847            CHECK_RC;
4848            break;
4849
4850        case output:  return 0;
4851
4852        case otherwise:
4853            reportError (actionNode, "xsl:otherwise must be immediately"
4854                         " within xsl:choose", errMsg);
4855            return -1;
4856
4857        case param:
4858            str = getAttr(actionNode, "name", a_name);
4859            if (str) {
4860                TRACE1("setting param '%s' ??\n", str);
4861                if (!xsltVarExists(xs, str, actionNode)) {
4862                    TRACE1("setting param '%s': yes \n", str);
4863                    select = getAttr(actionNode, "select", a_select);
4864                    if (select && actionNode->firstChild) {
4865                        reportError (actionNode, "An xsl:parameter element "
4866                                     " with a select attribute must be empty",
4867                                     errMsg);
4868                        return -1;
4869                    }
4870                    TRACE1("param select='%s'\n", select);
4871                    rc = xsltSetVar(xs, str, context, currentNode,
4872                                    currentPos, select, actionNode, 1, errMsg);
4873                    CHECK_RC;
4874                }
4875            } else {
4876                reportError (actionNode, "xsl:param: missing mandatory "
4877                             " attribute \"name\".", errMsg);
4878                return -1;
4879            }
4880            break;
4881
4882        case preserveSpace: return 0;
4883
4884        case procinstr:
4885            str = getAttr(actionNode, "name", a_name);
4886            if (str) {
4887                rc = evalAttrTemplates( xs, context, currentNode, currentPos,
4888                                        str, &str2, errMsg);
4889                CHECK_RC;
4890                if (!domIsPINAME (str2) || !domIsNCNAME(str2)) {
4891                    reportError (actionNode, "xsl:processing-instruction: "
4892                                 "Processing instruction name is invalid.",
4893                                 errMsg);
4894                    FREE(str2);
4895                    return -1;
4896                }
4897            } else {
4898                reportError (actionNode, "xsl:processing-instruction:"
4899                             " missing mandatory attribute \"name\".", errMsg);
4900                return -1;
4901            }
4902            fragmentNode = domNewElementNode(xs->resultDoc, "", ELEMENT_NODE);
4903            savedLastNode = xs->lastNode;
4904            xs->lastNode = fragmentNode;
4905            xsltPushVarFrame (xs);
4906            rc = ExecActions(xs, context, currentNode, currentPos,
4907                             actionNode->firstChild, errMsg);
4908            xsltPopVarFrame (xs);
4909            CHECK_RC;
4910            child = fragmentNode->firstChild;
4911            while (child) {
4912                if (child->nodeType != TEXT_NODE) {
4913                    domDeleteNode (fragmentNode, NULL, NULL);
4914                    reportError (actionNode, "xsl:processing-instruction must "
4915                                 "not create nodes other than text nodes.",
4916                                 errMsg);
4917                    FREE(str2);
4918                    return -1;
4919                }
4920                child = child->nextSibling;
4921            }
4922            str = xpathGetStringValue (fragmentNode, &len);
4923            if (!domIsPIValue (str)) {
4924                reportError (actionNode, "Invalide processing instruction "
4925                             "value", errMsg);
4926                domDeleteNode (fragmentNode, NULL, NULL);
4927                FREE(str);
4928                FREE(str2);
4929                return -1;
4930            }
4931            xs->lastNode = savedLastNode;
4932
4933            n = (domNode*)domNewProcessingInstructionNode(
4934                xs->resultDoc, str2, strlen(str2), str, len);
4935            domAppendChild(xs->lastNode, n);
4936            domDeleteNode (fragmentNode, NULL, NULL);
4937            FREE(str2);
4938            FREE(str);
4939            break;
4940
4941        case sort:
4942        case stylesheet:
4943        case stripSpace:
4944        case template:
4945            return 0;
4946
4947        case text:
4948            str = getAttr(actionNode, "disable-output-escaping",
4949                          a_disableOutputEscaping);
4950            if (str) {
4951                if (strcmp (str, "yes")==0) disableEsc = 1;
4952            }
4953            pc = xpathGetStringValue (actionNode, &len);
4954            DBG(fprintf(stderr, "text: pc='%s'%d \n", pc, len);)
4955            domAppendNewTextNode(xs->lastNode, pc, len, TEXT_NODE, disableEsc);
4956            FREE(pc);
4957            break;
4958
4959        case transform: return 0;
4960
4961        case valueOf:
4962            if (actionNode->firstChild) {
4963                reportError (actionNode, "xsl:value-of has to be empty.",
4964                             errMsg);
4965                return -1;
4966            }
4967            str = getAttr(actionNode, "disable-output-escaping",
4968                          a_disableOutputEscaping);
4969            if (str) {
4970                if (strcmp (str, "yes")==0) disableEsc = 1;
4971            }
4972            str = getAttr(actionNode, "select", a_select);
4973            if (str) {
4974                TRACE1("valueOf: str='%s' \n", str);
4975                rc = evalXPath(xs, context, currentNode, currentPos, str,
4976                               &rs, errMsg);
4977                CHECK_RC;
4978                DBG(rsPrint(&rs));
4979                str = xpathFuncString( &rs );
4980                TRACE1("valueOf: xpathString='%s' \n", str);
4981                domAppendNewTextNode(xs->lastNode, str, strlen(str),
4982                                     TEXT_NODE, disableEsc);
4983                xpathRSFree( &rs );
4984                FREE(str);
4985            } else {
4986                reportError (actionNode, "xsl:value-of must have a"
4987                             " \"select\" attribute!", errMsg);
4988                return -1;
4989            }
4990            break;
4991
4992        case variable:
4993            str = getAttr(actionNode, "name", a_name);
4994            if (str) {
4995                if (xsltVarExists (xs, str, actionNode)) {
4996                    Tcl_DStringInit (&dStr);
4997                    Tcl_DStringAppend (&dStr, "Variable '", -1);
4998                    Tcl_DStringAppend (&dStr, str, -1);
4999                    Tcl_DStringAppend (
5000                        &dStr, "' is already declared in this template", -1);
5001                    reportError (actionNode, Tcl_DStringValue (&dStr), errMsg);
5002                    return -1;
5003                }
5004                select = getAttr(actionNode, "select", a_select);
5005                if (select && actionNode->firstChild) {
5006                    reportError (actionNode, "An xsl:variable element with a"
5007                                 " select attribute must be empty", errMsg);
5008                    return -1;
5009                }
5010                TRACE1("variable select='%s'\n", select);
5011                rc = xsltSetVar(xs, str, context, currentNode, currentPos,
5012                                select, actionNode, 1, errMsg);
5013                CHECK_RC;
5014            } else {
5015                reportError (actionNode, "xsl:variable must have a \"name\""
5016                             " attribute!", errMsg);
5017                return -1;
5018            }
5019            break;
5020
5021        case when:
5022            reportError (actionNode, "xsl:when must be immediately within"
5023                         " xsl:choose", errMsg);
5024            return -1;
5025
5026        case withParam:
5027            return 0;
5028
5029        default:
5030            sDoc = xs->currentSubDoc;
5031            if (actionNode->namespace) {
5032                ns = actionNode->ownerDocument->namespaces[actionNode->namespace - 1];
5033                eNS = sDoc->extensionNS;
5034                while (eNS) {
5035                    if (eNS->uri) {
5036                        if (strcmp (eNS->uri, ns->uri)==0) break;
5037                    } else {
5038                        if (ns->prefix[0] == '\0') break;
5039                    }
5040                    eNS = eNS->next;
5041                }
5042                if (eNS) {
5043                    /* An extension element; process fallback */
5044                    child = actionNode->firstChild;
5045                    while (child) {
5046                        if (child->info == fallback) {
5047                            xsltPushVarFrame (xs);
5048                            rc = ExecActions (xs, context, currentNode,
5049                                              currentPos, child->firstChild,
5050                                              errMsg);
5051                            xsltPopVarFrame (xs);
5052                            CHECK_RC;
5053                        }
5054                        child = child->nextSibling;
5055                    }
5056                    return 0;
5057                }
5058            }
5059            savedLastNode = xs->lastNode;
5060            DBG(fprintf(stderr, "append new tag '%s' uri='%s' \n",
5061                        actionNode->nodeName, domNamespaceURI(actionNode) ););
5062            xs->lastNode = domAppendLiteralNode (xs->lastNode, actionNode);
5063            n = actionNode;
5064
5065            while (n) {
5066                attr = n->firstAttr;
5067                while (attr && (attr->nodeFlags & IS_NS_NODE)) {
5068                    /* xslt namespace isn't copied */
5069                    /* Well, xslt implementors doesn't seem to agree
5070                       at which point this rule out of the second paragraph
5071                       of 7.1.1 must be applied: before or after applying
5072                       the namespace aliases (or, in other words: is this
5073                       rule (of not copying the XSLT namespace for lre)
5074                       considered, at the time, the lre is found in the
5075                       stylesheet or at the time, the lre is written to the
5076                       result doc). In deed the rec doesn't clarify this
5077                       explicitly. */
5078                    if (strcmp (attr->nodeValue, XSLT_NAMESPACE)==0){
5079                        attr = attr->nextSibling;
5080                        continue;
5081                    }
5082                    ns = n->ownerDocument->namespaces[attr->namespace-1];
5083                    rc = 0;
5084                    n1 = actionNode;
5085                    while (n1 != n) {
5086                        attr1 = n1->firstAttr;
5087                        while (attr1 && (attr1->nodeFlags & IS_NS_NODE)) {
5088                            ns1 = n1->ownerDocument->namespaces[attr1->namespace-1];
5089                            if (strcmp (ns1->prefix, ns->prefix)==0) {
5090                                rc = 1;
5091                                break;
5092                            }
5093                            attr1 = attr1->nextSibling;
5094                        }
5095                        if (rc) break;
5096                        n1 = n1->parentNode;
5097                    }
5098                    if (rc) {
5099                        attr = attr->nextSibling;
5100                        continue;
5101                    }
5102
5103                    str = ns->uri;
5104                    nsAlias = xs->nsAliases;
5105                    while (nsAlias) {
5106                        if (strcmp (nsAlias->fromUri, ns->uri)==0) {
5107                            ns->uri = nsAlias->toUri;
5108                            break;
5109                        }
5110                        nsAlias = nsAlias->next;
5111                    }
5112                    eNS = sDoc->excludeNS;
5113                    while (eNS) {
5114                        if (eNS->uri) {
5115                            if (strcmp (eNS->uri, ns->uri)==0) break;
5116                        } else {
5117                            if (ns->prefix[0] == '\0') break;
5118                        }
5119                        eNS = eNS->next;
5120                    }
5121                    if (!eNS) {
5122                        eNS = sDoc->extensionNS;
5123                        while (eNS) {
5124                            if (eNS->uri) {
5125                                if (strcmp (eNS->uri, ns->uri)==0) break;
5126                            } else {
5127                                if (ns->prefix[0] == '\0') break;
5128                            }
5129                            eNS = eNS->next;
5130                        }
5131                        if (!eNS) {
5132                            domAddNSToNode (xs->lastNode, ns);
5133                        }
5134                    }
5135                    ns->uri = str;
5136                    attr = attr->nextSibling;
5137                }
5138                n = n->parentNode;
5139            }
5140            /* It's not clear, what to do, if the literal result
5141               element is in a namespace, that should be excluded. We
5142               follow saxon and xalan, which both add the namespace of
5143               the literal result element always to the result tree,
5144               to ensure, that the result tree is conform to the XML
5145               namespace recommendation. */
5146            if (actionNode->namespace) {
5147                ns = actionNode->ownerDocument->namespaces[actionNode->namespace-1];
5148                str = ns->uri;
5149                nsAlias = xs->nsAliases;
5150                while (nsAlias) {
5151                    if (strcmp (nsAlias->fromUri, ns->uri)==0) {
5152                        ns->uri = nsAlias->toUri;
5153                        break;
5154                    }
5155                    nsAlias = nsAlias->next;
5156                }
5157                ns1 = domAddNSToNode (xs->lastNode, ns);
5158                if (ns1) {
5159                    xs->lastNode->namespace = ns1->index;
5160                }
5161                ns->uri = str;
5162            } else {
5163                ns = domLookupPrefix (xs->lastNode, "");
5164                if (ns) {
5165                    if (strcmp (ns->uri, "")!=0) {
5166                        attr = domSetAttributeNS (xs->lastNode, "xmlns", "",
5167                                                  NULL, 1);
5168                        if (attr) {
5169                            xs->lastNode->namespace = attr->namespace;
5170                        }
5171                    } else {
5172                        xs->lastNode->namespace = ns->index;
5173                    }
5174                }
5175            }
5176
5177            n = xs->lastNode;
5178            /* process the attributes */
5179            attr = actionNode->firstAttr;
5180            while (attr) {
5181                if (attr->nodeFlags & IS_NS_NODE) {
5182                    attr = attr->nextSibling;
5183                    continue;
5184                }
5185                /* TODO: xsl:exclude-result-prefixes attribute on literal
5186                         elements on the ancestor-or-self axis */
5187                uri = domNamespaceURI((domNode*)attr);
5188                if (uri && strcmp(uri, XSLT_NAMESPACE)==0) {
5189                    domSplitQName((char*)attr->nodeName, prefix, &localName);
5190                    if (strcmp(localName,"use-attribute-sets")==0) {
5191                        str = attr->nodeValue;
5192                        rc = ExecUseAttributeSets (xs, context, currentNode,
5193                                                   currentPos, actionNode,
5194                                                   str, errMsg);
5195                        CHECK_RC;
5196                    }
5197                } else {
5198                    rc = evalAttrTemplates( xs, context, currentNode,
5199                                            currentPos, attr->nodeValue, &str,
5200                                            errMsg);
5201                    CHECK_RC;
5202                    if (uri) {
5203                        nsAlias = xs->nsAliases;
5204                        while (nsAlias) {
5205                            if (strcmp (nsAlias->fromUri, uri)==0) {
5206                                uri = nsAlias->toUri;
5207                                break;
5208                            }
5209                            nsAlias = nsAlias->next;
5210                        }
5211                    }
5212                    domSetAttributeNS (n, attr->nodeName, str, uri, 1);
5213                    FREE(str);
5214                }
5215                attr = attr->nextSibling;
5216            }
5217            /* process the children as well */
5218            xsltPushVarFrame (xs);
5219            rc = ExecActions(xs, context, currentNode, currentPos,
5220                             actionNode->firstChild, errMsg);
5221            xsltPopVarFrame (xs);
5222            CHECK_RC;
5223            xs->lastNode = savedLastNode;
5224            return 0;
5225    }
5226    return 0;
5227}
5228
5229/*----------------------------------------------------------------------------
5230|   ExecActions
5231|
5232\---------------------------------------------------------------------------*/
5233static int ExecActions (
5234    xsltState       * xs,
5235    xpathResultSet  * context,
5236    domNode         * currentNode,
5237    int               currentPos,
5238    domNode         * actionNode,
5239    char           ** errMsg
5240)
5241{
5242    domNode *savedLastNode, *savedCurrentNode;
5243    int rc;
5244
5245    savedLastNode    = xs->lastNode;
5246    savedCurrentNode = xs->current;
5247
5248    while (actionNode) {
5249        xs->current = currentNode;
5250        rc = ExecAction (xs, context, currentNode, currentPos, actionNode,
5251                         errMsg);
5252        if (rc < 0) {
5253            xs->lastNode = savedLastNode;
5254            xs->current  = savedCurrentNode;
5255            return rc;
5256        }
5257        actionNode = actionNode->nextSibling;
5258    }
5259    xs->lastNode = savedLastNode;
5260    xs->current  = savedCurrentNode;
5261    return 0;
5262}
5263
5264/*----------------------------------------------------------------------------
5265|   ApplyTemplate
5266|
5267\---------------------------------------------------------------------------*/
5268static int ApplyTemplate (
5269    xsltState      * xs,
5270    xpathResultSet * context,
5271    domNode        * currentNode,
5272    domNode        * exprContext,
5273    int              currentPos,
5274    const char     * mode,
5275    const char     * modeURI,
5276    char          ** errMsg
5277)
5278{
5279    xsltTemplate   *tpl;
5280    xsltTemplate   *tplChoosen, *currentTplRule;
5281    domNode        *child;
5282    xpathResultSet  rs;
5283    int             rc;
5284    double          currentPrio, currentPrec;
5285    char            prefix[MAX_PREFIX_LEN];
5286    const char     *localName;
5287    Tcl_HashEntry  *h;
5288    Tcl_DString     dStr;
5289    xsltSubDoc     *currentSubDoc;
5290
5291    TRACE2("\n\nApplyTemplate mode='%s' currentPos=%d \n", mode, currentPos);
5292    DBG(printXML (currentNode, 0, 1);)
5293
5294    /*--------------------------------------------------------------
5295    |   find template
5296    \-------------------------------------------------------------*/
5297    tplChoosen  = NULL;
5298    currentPrio = -100000.0;
5299    currentPrec = 0.0;
5300
5301
5302    if (currentNode->nodeType == ELEMENT_NODE) {
5303        Tcl_DStringInit (&dStr);
5304        if (currentNode->namespace) {
5305            domSplitQName (currentNode->nodeName, prefix, &localName);
5306            Tcl_DStringAppend (&dStr, domNamespaceURI (currentNode), -1);
5307            Tcl_DStringAppend (&dStr, ":", 1);
5308        }
5309        if (mode) {
5310            if (modeURI) {
5311                Tcl_DStringAppend (&dStr, modeURI, -1);
5312                Tcl_DStringAppend (&dStr, ":", 1);
5313            }
5314            Tcl_DStringAppend (&dStr, mode, -1);
5315            Tcl_DStringAppend (&dStr, ":", 1);
5316        }
5317        if (currentNode->namespace) {
5318            Tcl_DStringAppend (&dStr, localName, -1);
5319        } else {
5320            Tcl_DStringAppend (&dStr, currentNode->nodeName, -1);
5321        }
5322        h = Tcl_FindHashEntry (&xs->isElementTpls, Tcl_DStringValue (&dStr));
5323        Tcl_DStringFree (&dStr);
5324
5325        if (h) {
5326            for (tpl = (xsltTemplate *) Tcl_GetHashValue (h);
5327                 tpl != NULL;
5328                 tpl = tpl->next) {
5329                TRACE3("find element tpl match='%s' mode='%s' name='%s'\n",
5330                       tpl->match, tpl->mode, tpl->name);
5331                TRACE4("tpl has prio='%f' precedence='%f'\n", tpl->prio, tpl->precedence, currentPrio, currentPrec);
5332                rc = xpathMatches ( tpl->ast, tpl->content, currentNode,
5333                                    &(xs->cbs), errMsg);
5334                if (rc < 0) {
5335                    TRACE1("xpathMatches had errors '%s' \n", *errMsg);
5336                    return rc;
5337                }
5338                if (rc == 0) continue;
5339                TRACE3("matching '%s': %f > %f ? \n", tpl->match, tpl->prio , currentPrio);
5340                tplChoosen = tpl;
5341                currentPrio = tpl->prio;
5342                currentPrec = tpl->precedence;
5343                break;
5344            }
5345        }
5346    }
5347
5348    for( tpl = xs->templates; tpl != NULL; tpl = tpl->next) {
5349
5350        TRACE3("find tpl match='%s' mode='%s' name='%s'\n",
5351               tpl->match, tpl->mode, tpl->name);
5352
5353        /* exclude those, which don't match the current mode */
5354        if (   ( mode && !tpl->mode)
5355            || (!mode &&  tpl->mode)
5356            || ( mode &&  tpl->mode && (strcmp(mode,tpl->mode)!=0))
5357            || (!modeURI && tpl->modeURI)
5358            || ( modeURI && !tpl->modeURI)
5359            || ( modeURI && tpl->modeURI && (strcmp(modeURI, tpl->modeURI)!=0))
5360        ) {
5361            TRACE("doesn't match mode\n");
5362            continue; /* doesn't match mode */
5363        }
5364        TRACE4("tpl has prio='%f' precedence='%f', currentPrio='%f', currentPrec='%f'\n", tpl->prio, tpl->precedence, currentPrio, currentPrec);
5365        /* According to xslt rec 5.5: First test precedence */
5366        if (tpl->precedence < currentPrec) break;
5367        if (tpl->precedence == currentPrec) {
5368            if (tpl->prio < currentPrio) break;
5369            if (tpl->prio == currentPrio
5370                && domPrecedes (tpl->content, tplChoosen->content))
5371                break;
5372        }
5373        rc = xpathMatches ( tpl->ast, tpl->content, currentNode, &(xs->cbs),
5374                            errMsg);
5375        TRACE1("xpathMatches = %d \n", rc);
5376        if (rc < 0) {
5377            TRACE1("xpathMatches had errors '%s' \n", *errMsg);
5378            return rc;
5379        }
5380        if (rc == 0) continue;
5381        TRACE3("matching '%s': %f > %f ? \n", tpl->match, tpl->prio , currentPrio);
5382        tplChoosen = tpl;
5383        TRACE1("TAKING '%s' \n", tpl->match);
5384        break;
5385    }
5386
5387    if (tplChoosen == NULL) {
5388        TRACE("nothing matches -> execute built-in template \n");
5389
5390        /*--------------------------------------------------------------------
5391        |   execute built-in template
5392        \-------------------------------------------------------------------*/
5393        if (currentNode->nodeType == TEXT_NODE) {
5394            domAppendNewTextNode(xs->lastNode,
5395                                 ((domTextNode*)currentNode)->nodeValue,
5396                                 ((domTextNode*)currentNode)->valueLength,
5397                                 TEXT_NODE, 0);
5398            return 0;
5399        } else
5400        if (currentNode->nodeType == DOCUMENT_NODE) {
5401            child = ((domDocument*)currentNode)->documentElement;
5402        } else
5403        if (currentNode->nodeType == ELEMENT_NODE) {
5404            child = currentNode->firstChild;
5405        } else
5406        if (currentNode->nodeType == ATTRIBUTE_NODE) {
5407            domAppendNewTextNode (xs->lastNode,
5408                                  ((domAttrNode*)currentNode)->nodeValue,
5409                                  ((domAttrNode*)currentNode)->valueLength,
5410                                  TEXT_NODE, 0);
5411            return 0;
5412        } else {
5413            return 0; /* for all other nodes we don't have to recurse deeper */
5414        }
5415        xpathRSInit( &rs );
5416        while (child) {
5417            rsAddNodeFast ( &rs, child);
5418            child = child->nextSibling;
5419        }
5420        rc = ApplyTemplates (xs, context, currentNode, currentPos, exprContext,
5421                             &rs, mode, modeURI, errMsg);
5422        xpathRSFree( &rs );
5423        CHECK_RC;
5424
5425    } else {
5426        TRACE1("tplChoosen '%s' \n", tplChoosen->match);
5427        currentTplRule = xs->currentTplRule;
5428        currentSubDoc = xs->currentSubDoc;
5429        xs->currentTplRule = tplChoosen;
5430        xs->currentSubDoc = tplChoosen->sDoc;
5431        DBG(printXML (tplChoosen->content->firstChild, 0, 1);)
5432        rc = ExecActions(xs, context, currentNode, currentPos,
5433                         tplChoosen->content->firstChild, errMsg);
5434        TRACE1("ApplyTemplate/ExecActions rc = %d \n", rc);
5435        xs->currentTplRule = currentTplRule;
5436        xs->currentSubDoc = currentSubDoc;
5437        CHECK_RC;
5438    }
5439    return 0;
5440}
5441
5442
5443/*----------------------------------------------------------------------------
5444|   ApplyTemplates
5445|
5446\---------------------------------------------------------------------------*/
5447static int ApplyTemplates (
5448    xsltState      * xs,
5449    xpathResultSet * context,
5450    domNode        * currentNode,
5451    int              currentPos,
5452    domNode        * actionNode,
5453    xpathResultSet * nodeList,
5454    const char     * mode,
5455    const char     * modeURI,
5456    char          ** errMsg
5457)
5458{
5459    domNode  * savedLastNode;
5460    int        i, rc, needNewVarFrame = 1;
5461
5462    if (nodeList->type == xNodeSetResult) {
5463        savedLastNode = xs->lastNode;
5464        for (i=0; i < nodeList->nr_nodes; i++) {
5465            if (needNewVarFrame) {
5466                xsltPushVarFrame (xs);
5467                SETPARAMDEF;
5468                rc = setParamVars (xs, context, currentNode, currentPos,
5469                                   actionNode, errMsg);
5470                if (rc < 0) {
5471                    xsltPopVarFrame (xs);
5472                    xs->lastNode = savedLastNode;
5473                    return rc;
5474                }
5475                SETSCOPESTART;
5476                (&xs->varFramesStack[xs->varFramesStackPtr])->polluted = 0;
5477            }
5478            rc = ApplyTemplate (xs, nodeList, nodeList->nodes[i], actionNode,
5479                                i, mode, modeURI, errMsg);
5480            if (rc < 0) {
5481                xsltPopVarFrame (xs);
5482                xs->lastNode = savedLastNode;
5483                return rc;
5484            }
5485            if ((&xs->varFramesStack[xs->varFramesStackPtr])->polluted) {
5486                xsltPopVarFrame (xs);
5487                needNewVarFrame = 1;
5488            } else needNewVarFrame = 0;
5489        }
5490        if (!needNewVarFrame) {
5491            xsltPopVarFrame (xs);
5492        }
5493        xs->lastNode = savedLastNode;
5494    } else {
5495        TRACE("ApplyTemplates: nodeList not a NodeSetResult !!!\n");
5496        DBG(rsPrint(nodeList);)
5497    }
5498    return 0;
5499}
5500
5501
5502/*----------------------------------------------------------------------------
5503|   fillElementList
5504|
5505\---------------------------------------------------------------------------*/
5506static int fillElementList (
5507    xsltWSInfo   * wsInfo,
5508    int            strip,
5509    double         precedence,
5510    domNode      * node,
5511    char         * str,
5512    char        ** errMsg
5513)
5514{
5515    char *pc, *start, save;
5516    char prefix[MAX_PREFIX_LEN];
5517    const char *localName;
5518    double *f;
5519    int   hnew;
5520    Tcl_HashEntry *h;
5521    Tcl_DString dStr;
5522    domNS  *ns;
5523
5524    pc = str;
5525    while (*pc) {
5526        while (*pc && IS_XML_WHITESPACE(*pc)) pc++;
5527        if (*pc == '\0') break;
5528        start = pc;
5529        while (*pc && !IS_XML_WHITESPACE(*pc)) pc++;
5530        save = *pc;
5531        *pc = '\0';
5532        wsInfo->hasData = 1;
5533        if (strcmp (start, "*")==0) {
5534            if (strip) wsInfo->stripAll = 1;
5535            else       wsInfo->stripAll = 0;
5536            wsInfo->wildcardPrec = precedence;
5537        } else {
5538            Tcl_DStringInit (&dStr);
5539            ns = NULL;
5540            domSplitQName (start, prefix, &localName);
5541            if (prefix[0] != '\0') {
5542                if (!domIsNCNAME (prefix)) {
5543                    reportError (node, "Invalid token", errMsg);
5544                    *pc = save;
5545                    Tcl_DStringFree (&dStr);
5546                    return -1;
5547                }
5548                ns = domLookupPrefix (node, prefix);
5549                if (!ns) {
5550                    reportError (node, "prefix isn't bound to a namespace",
5551                                 errMsg);
5552                    *pc = save;
5553                    Tcl_DStringFree (&dStr);
5554                    return -1;
5555                }
5556                Tcl_DStringAppend (&dStr, ns->uri, -1);
5557                Tcl_DStringAppend (&dStr, ":", 1);
5558            }
5559            if (strcmp ("*", localName) != 0) {
5560                if (!domIsNCNAME (localName)) {
5561                    reportError (node, "Invalid token", errMsg);
5562                    *pc = save;
5563                    Tcl_DStringFree (&dStr);
5564                    return -1;
5565                }
5566            }
5567            Tcl_DStringAppend (&dStr, localName, -1);
5568            if (strip) {
5569                h = Tcl_FindHashEntry (&wsInfo->preserveTokens,
5570                                       Tcl_DStringValue (&dStr));
5571            } else {
5572                h = Tcl_FindHashEntry (&wsInfo->stripTokens,
5573                                       Tcl_DStringValue (&dStr));
5574            }
5575            if (h) {
5576                FREE (Tcl_GetHashValue (h));
5577                Tcl_DeleteHashEntry (h);
5578            }
5579            if (strip) {
5580                h = Tcl_CreateHashEntry (&wsInfo->stripTokens,
5581                                         Tcl_DStringValue (&dStr), &hnew);
5582            } else {
5583                h = Tcl_CreateHashEntry (&wsInfo->preserveTokens,
5584                                         Tcl_DStringValue (&dStr), &hnew);
5585            }
5586            if (hnew) {
5587                f = (double *)MALLOC(sizeof (double));
5588                *f = precedence;
5589                Tcl_SetHashValue (h, f);
5590            } else {
5591                f = (double *)Tcl_GetHashValue (h);
5592                *f = precedence;
5593            }
5594            Tcl_DStringFree (&dStr);
5595        }
5596        *pc = save;
5597    }
5598    return 1;
5599}
5600
5601/*----------------------------------------------------------------------------
5602|   getCdataSectionElements
5603|
5604\---------------------------------------------------------------------------*/
5605static int
5606getCdataSectionElements (
5607    domNode        * node,
5608    char           * qnameList,
5609    Tcl_HashTable  * HashTable,
5610    char          ** errMsg
5611    )
5612{
5613    char *pc, *start, save, prefix[MAX_PREFIX_LEN];
5614    const char *localName;
5615    int hnew;
5616    Tcl_HashEntry *h;
5617
5618    Tcl_DString dStr;
5619    domNS  *ns;
5620
5621    Tcl_DStringInit (&dStr);
5622    pc = qnameList;
5623    while (*pc) {
5624        while (*pc && IS_XML_WHITESPACE(*pc)) pc++;
5625        if (*pc == '\0') break;
5626        start = pc;
5627        while (*pc && !IS_XML_WHITESPACE(*pc)) pc++;
5628        save = *pc;
5629        *pc = '\0';
5630        domSplitQName (start, prefix, &localName);
5631        if (prefix[0] != '\0') {
5632            if (!domIsNCNAME (prefix)) {
5633                Tcl_DStringSetLength (&dStr, 0);
5634                Tcl_DStringAppend (&dStr, "Invalid prefix '", -1);
5635                Tcl_DStringAppend (&dStr, prefix, -1);
5636                Tcl_DStringAppend (&dStr, "'.", 2);
5637                reportError (node, Tcl_DStringValue (&dStr), errMsg);
5638                Tcl_DStringFree (&dStr);
5639                return 0;
5640            }
5641            ns = domLookupPrefix (node, prefix);
5642            if (!ns) {
5643                Tcl_DStringSetLength (&dStr, 0);
5644                Tcl_DStringAppend (&dStr, "There isn't a namespace bound to"
5645                                   " the prefix '", -1);
5646                Tcl_DStringAppend (&dStr, prefix, -1);
5647                Tcl_DStringAppend (&dStr, "'.", 2);
5648                reportError (node, Tcl_DStringValue (&dStr), errMsg);
5649                Tcl_DStringFree (&dStr);
5650                return 0;
5651            }
5652            Tcl_DStringAppend (&dStr, ns->uri, -1);
5653            Tcl_DStringAppend (&dStr, ":", 1);
5654        }
5655        if (!domIsNCNAME (localName)) {
5656            Tcl_DStringSetLength (&dStr, 0);
5657            Tcl_DStringAppend (&dStr, "Invalid name '", -1);
5658            Tcl_DStringAppend (&dStr, prefix, -1);
5659            Tcl_DStringAppend (&dStr, "'.", 2);
5660            reportError (node, Tcl_DStringValue (&dStr), errMsg);
5661            Tcl_DStringFree (&dStr);
5662            return 0;
5663        }
5664        Tcl_DStringAppend (&dStr, localName, -1);
5665        h = Tcl_CreateHashEntry (HashTable, Tcl_DStringValue (&dStr), &hnew);
5666        Tcl_DStringSetLength (&dStr, 0);
5667        *pc = save;
5668    }
5669    return 1;
5670}
5671
5672/*----------------------------------------------------------------------------
5673|   StripXSLTSpace
5674|
5675\---------------------------------------------------------------------------*/
5676static void StripXSLTSpace (
5677    domNode    * node
5678)
5679{
5680    domNode *child, *newChild, *parent;
5681    int     i, len, onlySpace;
5682    char   *p;
5683
5684    if (node->nodeType == TEXT_NODE) {
5685        node->info = (int)unknown;
5686        p = ((domTextNode*)node)->nodeValue;
5687        len = ((domTextNode*)node)->valueLength;
5688        onlySpace = 1;
5689        for (i=0; i<len; i++) {
5690            if (!IS_XML_WHITESPACE(*p)) {
5691                onlySpace = 0;
5692                break;
5693            }
5694            p++;
5695        }
5696        if (onlySpace) {
5697            if (node->parentNode && (node->parentNode->info == text)) {
5698                /* keep white texts below xsl:text elements */
5699                return;
5700            }
5701            parent = node->parentNode;
5702            while (parent) {
5703                p = getAttr(parent,"xml:space", a_space);
5704                if (p!=NULL) {
5705                    if (strcmp(p,"preserve")==0) return;
5706                    if (strcmp(p,"default")==0)  break;
5707                }
5708                parent = parent->parentNode;
5709            }
5710            DBG(fprintf(stderr, "removing domNode0x%x(len %d) under '%s' \n",
5711                        node, len, node->parentNode->nodeName);)
5712            domDeleteNode (node, NULL, NULL);
5713        }
5714    } else
5715    if (node->nodeType == ELEMENT_NODE) {
5716        getTag(node);
5717        child = node->firstChild;
5718        while (child) {
5719            newChild = child->nextSibling;
5720            StripXSLTSpace (child);
5721            child = newChild;
5722        }
5723    } else {
5724        node->info = (int)unknown;
5725    }
5726}
5727
5728
5729/*----------------------------------------------------------------------------
5730|   addExclExtNS
5731|
5732\---------------------------------------------------------------------------*/
5733static int
5734parseList (
5735    xsltSubDoc  *docData,
5736    domNode     *xsltRoot,
5737    char        *str,
5738    int          extensionNS,
5739    char       **errMsg
5740    )
5741{
5742    xsltExclExtNS *eNS;
5743    char          *pc, *start, save;
5744    domNS         *ns;
5745
5746    if (str) {
5747        pc = str;
5748        while (*pc) {
5749            while (*pc && IS_XML_WHITESPACE(*pc))
5750                pc++;
5751            if (*pc == '\0') break;
5752            start = pc;
5753            while (*pc && !IS_XML_WHITESPACE(*pc))
5754                pc++;
5755            save = *pc;
5756            *pc = '\0';
5757            eNS = (xsltExclExtNS *)MALLOC(sizeof (xsltExclExtNS));
5758            eNS->uri = NULL;
5759            if (extensionNS) {
5760                eNS->next = docData->extensionNS;
5761                docData->extensionNS = eNS;
5762            } else {
5763                eNS->next = docData->excludeNS;
5764                docData->excludeNS = eNS;
5765            }
5766            if (strcmp (start, "#default")==0) {
5767                ns = domLookupPrefix (xsltRoot, "");
5768                if (!ns) {
5769                    reportError (xsltRoot, "All prefixes listed in"
5770                                 " exclude-result-prefixes and"
5771                                 " extension-element-prefixes must be"
5772                                 " bound to a namespace.",
5773                                 errMsg);
5774                    return -1;
5775                }
5776            } else {
5777                ns = domLookupPrefix (xsltRoot, start);
5778                if (!ns) {
5779                    reportError (xsltRoot, "All prefixes listed in"
5780                                 " exclude-result-prefixes and"
5781                                 " extension-element-prefixes must be"
5782                                 " bound to a namespace.",
5783                                 errMsg);
5784                    return -1;
5785                }
5786                eNS->uri = tdomstrdup (ns->uri);
5787            }
5788            *pc = save;
5789        }
5790    }
5791    return 1;
5792}
5793
5794static int
5795addExclExtNS (
5796    xsltSubDoc  *docData,
5797    domNode     *xsltRoot,
5798    char       **errMsg
5799    )
5800{
5801    char  *str, *tailptr;
5802    int    rc;
5803    double d;
5804
5805    str = getAttr (xsltRoot, "version", a_version);
5806    if (!str) {
5807        reportError (xsltRoot, "missing mandatory attribute \"version\".",
5808                     errMsg);
5809        return -1;
5810    }
5811    d = strtod (str, &tailptr);
5812    if (d == 0.0 && tailptr == str) {
5813        reportError (xsltRoot, "The value of the attribute \"version\" must"
5814                     " be a number.", errMsg);
5815        return -1;
5816    }
5817    if (d > 1.0) {
5818        docData->fwCmpProcessing = 1;
5819    } else {
5820        if (d != 1.0) {
5821            reportError (xsltRoot, "Strange \"version\" value.", errMsg);
5822            return -1;
5823            docData->fwCmpProcessing = 0;
5824        }
5825    }
5826
5827    str = getAttr (xsltRoot, "exclude-result-prefixes",
5828                   a_excludeResultPrefixes);
5829    rc = parseList (docData, xsltRoot, str, 0, errMsg);
5830    CHECK_RC;
5831
5832    str = getAttr (xsltRoot, "extension-element-prefixes",
5833                   a_extensionElementPrefixes);
5834    rc = parseList (docData, xsltRoot, str, 1, errMsg);
5835    CHECK_RC;
5836    return 1;
5837}
5838
5839/*----------------------------------------------------------------------------
5840|   getExternalDocument
5841|
5842\---------------------------------------------------------------------------*/
5843static domDocument *
5844getExternalDocument (
5845    Tcl_Interp  *interp,
5846    xsltState   *xs,
5847    domDocument *xsltDoc,
5848    const char  *baseURI,
5849    const char  *href,
5850    int          isStylesheet,
5851    int          fixedXMLSource,
5852    char       **errMsg
5853    )
5854{
5855    Tcl_Obj      *cmdPtr, *resultObj, *extbaseObj, *xmlstringObj;
5856    Tcl_Obj      *channelIdObj, *resultTypeObj;
5857    int           len, mode, result, storeLineColumn;
5858    char         *resultType, *extbase, *xmlstring, *channelId, s[20];
5859    char         *extResolver = NULL;
5860    CONST84 char *str;
5861    domDocument  *doc;
5862    xsltSubDoc   *sdoc;
5863    XML_Parser    parser;
5864    Tcl_Channel   chan;
5865    Tcl_DString   dStr;
5866
5867    if (isStylesheet && (href[0] == '\0')) {
5868        *errMsg = tdomstrdup("Recursive import/include: stylesheet tries "
5869                             "to access itself.");
5870        return NULL;
5871    }
5872    cmdPtr = Tcl_NewStringObj (xsltDoc->extResolver, -1);
5873    Tcl_IncrRefCount (cmdPtr);
5874    if (baseURI) {
5875        Tcl_ListObjAppendElement(interp, cmdPtr,
5876                                 Tcl_NewStringObj (baseURI,
5877                                                   strlen(baseURI)));
5878    } else {
5879        Tcl_ListObjAppendElement(interp, cmdPtr,
5880                                 Tcl_NewStringObj ("", 0));
5881    }
5882    Tcl_ListObjAppendElement (interp, cmdPtr, (href ?
5883                              Tcl_NewStringObj (href, strlen (href))
5884                              : Tcl_NewStringObj ("", 0)));
5885    Tcl_ListObjAppendElement (interp, cmdPtr,
5886                              Tcl_NewStringObj ("", 0));
5887
5888#if TclOnly8Bits
5889    result = Tcl_GlobalEvalObj(interp, cmdPtr);
5890#else
5891    result = Tcl_EvalObjEx (interp, cmdPtr, TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL);
5892#endif
5893
5894    Tcl_DecrRefCount (cmdPtr);
5895    resultObj = Tcl_GetObjResult (interp);
5896    Tcl_IncrRefCount (resultObj);
5897
5898    if (result != TCL_OK) {
5899        goto wrongScriptResult;
5900    }
5901
5902    result = Tcl_ListObjLength (interp, resultObj, &len);
5903    if ((result != TCL_OK) || (len != 3)) {
5904        goto wrongScriptResult;
5905    }
5906    result = Tcl_ListObjIndex (interp, resultObj, 0, &resultTypeObj);
5907    if (result != TCL_OK) {
5908        goto wrongScriptResult;
5909    }
5910    resultType = Tcl_GetString(resultTypeObj);
5911    if (strcmp (resultType, "string") == 0) {
5912        result = Tcl_ListObjIndex (interp, resultObj, 2, &xmlstringObj);
5913        xmlstring = Tcl_GetStringFromObj (xmlstringObj, &len);
5914        chan = NULL;
5915    } else if (strcmp (resultType, "channel") == 0) {
5916        xmlstring = NULL;
5917        len = 0;
5918        result = Tcl_ListObjIndex (interp, resultObj, 2, &channelIdObj);
5919        channelId = Tcl_GetString(channelIdObj);
5920        chan = Tcl_GetChannel (interp, channelId, &mode);
5921        if (chan == (Tcl_Channel) NULL) {
5922            goto wrongScriptResult;
5923        }
5924        if ((mode & TCL_READABLE) == 0) {
5925            *errMsg = tdomstrdup("-externalentitycommand returned a channel that wasn't opened for reading");
5926            return NULL;
5927        }
5928    } else if (strcmp (resultType, "filename") == 0) {
5929          *errMsg = tdomstrdup("-externalentitycommand result type \"filename\" not yet implemented");
5930          return NULL;
5931    } else {
5932        goto wrongScriptResult;
5933    }
5934    result = Tcl_ListObjIndex (interp, resultObj, 1, &extbaseObj);
5935    extbase = Tcl_GetString(extbaseObj);
5936
5937    /* Since stylesheets and source docouments have different white space
5938       stripping rules, an already parsed tree could only reused, if the
5939       'usage type' of the already present tree is the same as for the
5940       currently requested document */
5941    sdoc = xs->subDocs;
5942    while (sdoc) {
5943        if (isStylesheet == sdoc->isStylesheet
5944            && sdoc->baseURI
5945            && strcmp(sdoc->baseURI, extbase) == 0) {
5946            Tcl_DecrRefCount (resultObj);
5947            return sdoc->doc;
5948        }
5949        sdoc = sdoc->next;
5950    }
5951
5952    if (xsltDoc->documentElement->nodeFlags & HAS_LINE_COLUMN) {
5953        storeLineColumn = 1;
5954    } else {
5955        storeLineColumn = 0;
5956    }
5957
5958    parser = XML_ParserCreate_MM (NULL, MEM_SUITE, NULL);
5959
5960    Tcl_ResetResult (interp);
5961    if (xsltDoc->extResolver) {
5962        extResolver = tdomstrdup (xsltDoc->extResolver);
5963    }
5964    /* keep white space, no fiddling with the encoding (is this
5965       a good idea?) */
5966    doc = domReadDocument (parser, xmlstring, len, 0, 0, storeLineColumn, 0,
5967                           chan, extbase, extResolver, 0,
5968                           (int) XML_PARAM_ENTITY_PARSING_ALWAYS, interp);
5969
5970    if (doc == NULL) {
5971        DBG(fprintf (stderr, "parse error, str len %d, xmlstring: -->%s<--\n",
5972                     strlen (xmlstring), xmlstring);)
5973        Tcl_DStringInit (&dStr);
5974        Tcl_DStringAppend (&dStr, "Error while processing external entity \"",
5975                           -1);
5976        Tcl_DStringAppend (&dStr, href, -1);
5977        Tcl_DStringAppend (&dStr, "\":\n", -1);
5978        str = Tcl_GetStringResult (interp);
5979        if (str[0] == '\0') {
5980            Tcl_DStringAppend (&dStr, "At line ", -1);
5981            sprintf (s, "%ld", XML_GetCurrentLineNumber (parser));
5982            Tcl_DStringAppend (&dStr, s, -1);
5983            Tcl_DStringAppend (&dStr, " character ", -1);
5984            sprintf (s, "%ld", XML_GetCurrentColumnNumber (parser));
5985            Tcl_DStringAppend (&dStr, s, -1);
5986            Tcl_DStringAppend (&dStr, ": ", 2);
5987            Tcl_DStringAppend (&dStr,
5988                               XML_ErrorString (XML_GetErrorCode(parser)), -1);
5989        } else {
5990            Tcl_DStringAppend (&dStr, str, -1);
5991        }
5992        *errMsg = tdomstrdup (Tcl_DStringValue (&dStr));
5993        Tcl_DStringFree (&dStr);
5994        XML_ParserFree (parser);
5995        Tcl_DecrRefCount (resultObj);
5996        return NULL;
5997    }
5998    XML_ParserFree (parser);
5999
6000    /* TODO: If the stylesheet use the
6001       literal-result-element-as-stylesheet form, rewrite it to a
6002       "ordinary" stylesheet with root element xsl:stylesheet, with
6003       one template child with match pattern "/". */
6004
6005    sdoc = (xsltSubDoc*)MALLOC(sizeof (xsltSubDoc));
6006    sdoc->doc = doc;
6007    sdoc->baseURI = tdomstrdup (extbase);
6008    Tcl_InitHashTable (&(sdoc->keyData), TCL_STRING_KEYS);
6009    sdoc->excludeNS = NULL;
6010    sdoc->extensionNS = NULL;
6011    sdoc->fwCmpProcessing = 0;
6012    sdoc->mustFree = 1;
6013    sdoc->isStylesheet = isStylesheet;
6014    sdoc->fixedXMLSource = fixedXMLSource;
6015    if (isStylesheet) {
6016        if (addExclExtNS (sdoc, doc->documentElement, errMsg) < 0) {
6017            Tcl_DeleteHashTable (&(sdoc->keyData));
6018            domFreeDocument (sdoc->doc, NULL, NULL);
6019            FREE (sdoc->baseURI);
6020            FREE (sdoc);
6021            Tcl_DecrRefCount (resultObj);
6022            return NULL;
6023        }
6024        StripXSLTSpace (doc->rootNode);
6025    }
6026    sdoc->next = xs->subDocs;
6027    xs->subDocs = sdoc;
6028    Tcl_DecrRefCount (resultObj);
6029
6030    return doc;
6031
6032 wrongScriptResult:
6033    *errMsg = tdomstrdup(Tcl_GetStringResult(interp));
6034    Tcl_DecrRefCount (resultObj);
6035    return NULL;
6036}
6037
6038/*----------------------------------------------------------------------------
6039|   processTopLevelVars
6040|
6041\---------------------------------------------------------------------------*/
6042static int processTopLevelVars (
6043    domNode       * xmlNode,
6044    xsltState     * xs,
6045    char         ** parameters,
6046    int             ignoreUndeclaredParameters,
6047    char         ** errMsg
6048    )
6049{
6050    int                rc, i;
6051    char              *select, prefix[MAX_PREFIX_LEN];
6052    const char        *localName;
6053    xpathResultSet     nodeList, rs;
6054    Tcl_HashEntry     *entryPtr;
6055    Tcl_HashSearch     search;
6056    xsltTopLevelVar   *topLevelVar;
6057    xsltVarInProcess   varInProcess;
6058    xsltVariable      *var;
6059    domNS             *ns;
6060    Tcl_DString        dStr;
6061
6062    xpathRSInit (&nodeList);
6063    rsAddNodeFast (&nodeList, xmlNode);
6064
6065    if (parameters) {
6066        i = 0;
6067        while (parameters[i]) {
6068            domSplitQName (parameters[i], prefix, &localName);
6069            ns = NULL;
6070            if (prefix[0] != '\0') {
6071                ns = domLookupPrefix (xs->xsltDoc->documentElement, prefix);
6072                if (!ns) {
6073                    Tcl_DStringInit (&dStr);
6074                    Tcl_DStringAppend (&dStr, "No namespace bound to prefix"
6075                                       " (passed parameter \"", -1);
6076                    Tcl_DStringAppend (&dStr, parameters[i], -1);
6077                    Tcl_DStringAppend (&dStr, "\")", -1);
6078                    *errMsg = tdomstrdup (Tcl_DStringValue (&dStr));
6079                    Tcl_DStringFree (&dStr);
6080                    xpathRSFree (&nodeList);
6081                    return -1;
6082                }
6083            }
6084            Tcl_DStringInit (&dStr);
6085            if (ns) Tcl_DStringAppend (&dStr, ns->uri, -1);
6086            Tcl_DStringAppend (&dStr, localName, -1);
6087            entryPtr = Tcl_FindHashEntry (&xs->topLevelVars,
6088                                          Tcl_DStringValue (&dStr));
6089            Tcl_DStringFree (&dStr);
6090            if (!entryPtr) {
6091                if (ignoreUndeclaredParameters) {
6092                    i += 2;
6093                    continue;
6094                }
6095                Tcl_DStringInit (&dStr);
6096                Tcl_DStringAppend (&dStr, "There isn't a parameter named \"", -1);
6097                Tcl_DStringAppend (&dStr, parameters[i], -1);
6098                Tcl_DStringAppend (&dStr, "\" defined at top level in the stylesheet.", -1);
6099                *errMsg = tdomstrdup (Tcl_DStringValue (&dStr));
6100                Tcl_DStringFree (&dStr);
6101                xpathRSFree (&nodeList);
6102                return -1;
6103            }
6104            topLevelVar = (xsltTopLevelVar *) Tcl_GetHashValue (entryPtr);
6105            if (!topLevelVar->isParameter) {
6106                Tcl_DStringInit (&dStr);
6107                Tcl_DStringAppend (&dStr, "\"", 1);
6108                Tcl_DStringAppend (&dStr, parameters[i], -1);
6109                Tcl_DStringAppend (&dStr, "\" is defined as variable, not as parameter.", -1);
6110                *errMsg = tdomstrdup (Tcl_DStringValue (&dStr));
6111                Tcl_DStringFree (&dStr);
6112                xpathRSFree (&nodeList);
6113                return -1;
6114            }
6115            if (xsltVarExists (xs, parameters[i], NULL)) {
6116                i += 2;
6117                continue;
6118            }
6119
6120            xpathRSInit (&rs);
6121            rsSetString (&rs, parameters[i+1]);
6122
6123            xs->varStackPtr++;
6124            if (xs->varStackPtr >= xs->varStackLen) {
6125                xs->varStack = (xsltVariable *) REALLOC ((char*)xs->varStack,
6126                                                         sizeof (xsltVariable)
6127                                                         * 2*xs->varStackLen);
6128                xs->varStackLen *= 2;
6129            }
6130            var = &(xs->varStack[xs->varStackPtr]);
6131            if (!xs->varFramesStack->nrOfVars) {
6132                xs->varFramesStack->varStartIndex = xs->varStackPtr;
6133            }
6134            xs->varFramesStack->nrOfVars++;
6135            var->name   = localName;
6136            if (ns) var->uri = ns->uri;
6137            else    var->uri = NULL;
6138            var->node   = topLevelVar->node;
6139            var->rs     = rs;
6140            var->active = 1;
6141
6142            i += 2;
6143        }
6144    }
6145    for (entryPtr = Tcl_FirstHashEntry(&xs->topLevelVars, &search);
6146            entryPtr != (Tcl_HashEntry*) NULL;
6147            entryPtr = Tcl_NextHashEntry(&search)) {
6148        topLevelVar = (xsltTopLevelVar *)Tcl_GetHashValue (entryPtr);
6149        if (xsltVarExists (xs, topLevelVar->name, topLevelVar->node)) {
6150            continue;
6151        }
6152        varInProcess.name = topLevelVar->name;
6153        varInProcess.next = NULL;
6154        xs->varsInProcess = &varInProcess;
6155
6156        xs->currentXSLTNode = topLevelVar->node;
6157        select = getAttr (topLevelVar->node, "select", a_select);
6158        if (select && topLevelVar->node->firstChild) {
6159            xpathRSFree (&nodeList);
6160            reportError (topLevelVar->node, "xsl:variable and xsl:param"
6161                         " elements with a select attribute must be empty",
6162                         errMsg);
6163            return -1;
6164        }
6165        rc = xsltSetVar(xs, topLevelVar->name, &nodeList, xmlNode, 0, select,
6166                        topLevelVar->node, 1, errMsg);
6167        if (rc < 0) {
6168            xpathRSFree (&nodeList);
6169            return rc;
6170        }
6171    }
6172    xpathRSFree (&nodeList);
6173    xs->currentXSLTNode = NULL;
6174    xs->varsInProcess = NULL;
6175    return 0;
6176}
6177
6178/*----------------------------------------------------------------------------
6179|   processTopLevel
6180|
6181\---------------------------------------------------------------------------*/
6182static int processTopLevel (
6183    Tcl_Interp    * interp,
6184    domNode       * xsltDocumentElement,
6185    xsltState     * xs,
6186    double          precedence,
6187    double        * precedenceLowBound,
6188    char         ** errMsg
6189)
6190{
6191    domNode           *node;
6192    domDocument       *extStyleSheet;
6193    int                rc, hnew, clen, newdf = 0, nonImportElemSeen = 0;
6194    int                ignore;
6195    double             childPrecedence, childLowBound;
6196    char              *str, *name, *match, *use, *href;
6197    char               prefix[MAX_PREFIX_LEN];
6198    const char        *localName, *baseURI;
6199    xsltTag            tag;
6200    xsltAttrSet       *attrSet;
6201    xsltKeyInfo       *keyInfo;
6202    xsltDecimalFormat *df;
6203    xsltTopLevelVar   *topLevelVar;
6204    xsltNSAlias       *nsAlias;
6205    domNS             *ns, *nsFrom, *nsTo;
6206    Tcl_HashEntry     *h;
6207    Tcl_DString        dStr;
6208
6209
6210    DBG(fprintf (stderr, "start processTopLevel. precedence: %f precedenceLowBound %f\n", precedence, *precedenceLowBound););
6211    node = xsltDocumentElement->firstChild;
6212    while (node) {
6213        tag = getTag (node);
6214        if (!nonImportElemSeen && tag != unknown && tag != import) {
6215            nonImportElemSeen = 1;
6216        }
6217        switch ( tag ) {
6218
6219            case attributeSet:
6220                str = getAttr(node, "name", a_name);
6221                if (str) {
6222                    domSplitQName (str, prefix, &localName);
6223                    ns = NULL;
6224                    if (prefix[0] != '\0') {
6225                        ns = domLookupPrefix (node, prefix);
6226                        if (!ns) {
6227                            reportError (node, "There isn't a namespace"
6228                                         " bound to the prefix.", errMsg);
6229                            return -1;
6230                        }
6231                    }
6232                    if (xs->attrSets) {
6233                        attrSet = xs->attrSets;
6234                        while (attrSet->next) attrSet = attrSet->next;
6235                        attrSet->next =
6236                            (xsltAttrSet*)MALLOC(sizeof(xsltAttrSet));
6237                        attrSet = attrSet->next;
6238                    } else {
6239                        attrSet = (xsltAttrSet*)MALLOC(sizeof(xsltAttrSet));
6240                        xs->attrSets = attrSet;
6241                    }
6242                    attrSet->next    = NULL;
6243                    attrSet->content = node;
6244                    attrSet->name    = localName;
6245                    if (ns) {
6246                        attrSet->uri = ns->uri;
6247                    } else {
6248                        attrSet->uri = NULL;
6249                    }
6250                } else {
6251                    reportError (node, "xsl:attribute-set: missing mandatory"
6252                                 " attribute \"name\".", errMsg);
6253                    return -1;
6254                }
6255                break;
6256
6257            case decimalFormat:
6258                if (node->firstChild) {
6259                    reportError (node, "xsl:decimal-format has to be empty.",
6260                                 errMsg);
6261                    return -1;
6262                }
6263                str = getAttr(node, "name", a_name);
6264                if (str) {
6265                    domSplitQName (str, prefix, &localName);
6266                    ns = NULL;
6267                    if (prefix[0] != '\0') {
6268                        ns = domLookupPrefix (node, prefix);
6269                        if (!ns) {
6270                            reportError (node, "There isn't a namespace bound"
6271                                         " to the prefix.", errMsg);
6272                            return -1;
6273                        }
6274                    }
6275                    /* a named decimal format */
6276                    df = xs->decimalFormats->next;
6277                    while (df) {
6278                        if (strcmp(df->name, str)==0
6279                            && ((df->uri == NULL && ns == NULL)
6280                                || (df->uri != NULL
6281                                    && ns != NULL
6282                                    && (strcmp (df->uri, ns->uri)==0)))) {
6283                            /* already existing, override it */
6284                            break;
6285                        }
6286                        df = df->next;
6287                    }
6288                    if (df == NULL) {
6289                        df = (xsltDecimalFormat*)MALLOC(sizeof(xsltDecimalFormat));
6290                        memset (df, 0, sizeof (xsltDecimalFormat));
6291                        newdf = 1;
6292                        /* initialize to defaults */
6293#if TclOnly8Bits
6294                        df->decimalSeparator  = '.';
6295                        df->groupingSeparator = ',';
6296                        df->infinity          = "Infinity";
6297                        df->minusSign         = '-';
6298                        df->NaN               = "NaN";
6299                        df->percent           = '%';
6300                        df->zeroDigit         = '0';
6301                        df->digit             = '#';
6302                        df->patternSeparator  = ';';
6303#else
6304                        df->decimalSeparator  = 46;
6305                        df->groupingSeparator = 44;
6306                        df->infinity          = "Infinity";
6307                        df->minusSign         = 45;
6308                        df->NaN               = "NaN";
6309                        df->percent           = 37;
6310                        df->perMille          = 0x2030;
6311                        df->zeroDigit         = 48;
6312                        df->digit             = 35;
6313                        df->patternSeparator  = 59;
6314#endif /* TclOnly8Bits */
6315                        df->name = tdomstrdup(str);
6316                        if (ns) df->uri = tdomstrdup(ns->uri);
6317                        else df->uri = NULL;
6318                        /* prepend into list of decimal format
6319                           after the default one */
6320                        df->next = xs->decimalFormats->next;
6321                        xs->decimalFormats->next = df;
6322                    }
6323                } else {
6324                    /* definitions for default decimal format */
6325                    df = xs->decimalFormats;
6326                }
6327                str = getAttr(node, "decimal-separator",  a_decimalSeparator);
6328                if (str) {
6329#if TclOnly8Bits
6330                    if (str[1] != '\0') {
6331                        reportError (node, "decimal-separator has to be a"
6332                                     " single char", errMsg);
6333                        if (newdf) FREE((char*)df);
6334                        return -1;
6335                    }
6336                    df->decimalSeparator = str[0];
6337#else
6338                    clen = UTF8_CHAR_LEN (str[0]);
6339                    if (str[clen] != '\0') {
6340                        reportError (node, "decimal-separator has to be a"
6341                                     " single char", errMsg);
6342                        if (newdf) FREE((char*)df);
6343                        return -1;
6344                    }
6345                    Tcl_UtfToUniChar (str, &df->decimalSeparator);
6346#endif /* TclOnly8Bits */
6347                }
6348                str = getAttr(node, "grouping-separator", a_groupingSeparator);
6349                if (str) {
6350#if TclOnly8Bits
6351                    if (str[1] != '\0') {
6352                        reportError (node, "grouping-separator has to be a"
6353                                     " single char", errMsg);
6354                        if (newdf) FREE((char*)df);
6355                        return -1;
6356                    }
6357                    df->groupingSeparator = str[0];
6358#else
6359                    clen = UTF8_CHAR_LEN (str[0]);
6360                    if (str[clen] != '\0') {
6361                        reportError (node, "groupingSeparator has to be a"
6362                                     " single char", errMsg);
6363                        if (newdf) FREE((char*)df);
6364                        return -1;
6365                    }
6366                    Tcl_UtfToUniChar (str, &df->groupingSeparator);
6367#endif /* TclOnly8Bits */
6368                }
6369                str = getAttr(node, "infinity",           a_infinity);
6370                if (str) df->infinity = str;
6371                str = getAttr(node, "minus-sign",         a_minusSign);
6372                if (str) {
6373#if TclOnly8Bits
6374                    if (str[1] != '\0') {
6375                        reportError (node, "minus-sign has to be a single"
6376                                     " char", errMsg);
6377                        return -1;
6378                    }
6379                    df->minusSign = str[0];
6380#else
6381                    clen = UTF8_CHAR_LEN (str[0]);
6382                    if (str[clen] != '\0') {
6383                        reportError (node, "minus-sign has to be a single"
6384                                     " char", errMsg);
6385                        if (newdf) FREE((char*)df);
6386                        return -1;
6387                    }
6388                    Tcl_UtfToUniChar (str, &df->minusSign);
6389#endif /* TclOnly8Bits */
6390                }
6391                str = getAttr(node, "NaN",                a_nan);
6392                if (str) df->NaN = str;
6393                str = getAttr(node, "percent",            a_percent);
6394                if (str) {
6395#if TclOnly8Bits
6396                    if (str[1] != '\0') {
6397                        reportError (node, "percent has to be a single"
6398                                     " char", errMsg);
6399                        return -1;
6400                    }
6401                    df->percent = str[0];
6402#else
6403                    clen = UTF8_CHAR_LEN (str[0]);
6404                    if (str[clen] != '\0') {
6405                        reportError (node, "percent has to be a single"
6406                                     " char", errMsg);
6407                        if (newdf) FREE((char*)df);
6408                        return -1;
6409                    }
6410                    Tcl_UtfToUniChar (str, &df->percent);
6411#endif /* TclOnly8Bits */
6412                }
6413                str = getAttr(node, "per-mille",          a_perMille);
6414                if (str) {
6415#if TclOnly8Bits
6416                    reportError (node, "User defined per-mille sign not"
6417                                 " supported, sorry.", errMsg);
6418                    return -1;
6419#else
6420                    clen = UTF8_CHAR_LEN (str[0]);
6421                    if (str[clen] != '\0') {
6422                        reportError (node, "per-mille has to be a single"
6423                                     " char", errMsg);
6424                        if (newdf) FREE((char*)df);
6425                        return -1;
6426                    }
6427                    Tcl_UtfToUniChar (str, &df->perMille);
6428#endif /* TclOnly8Bits */
6429                }
6430                str = getAttr(node, "zero-digit",         a_zeroDigit);
6431                if (str) {
6432#if TclOnly8Bits
6433                    if (str[1] != '\0') {
6434                        reportError (node, "zero-digit has to be a single"
6435                                     " char", errMsg);
6436                        return -1;
6437                    }
6438                    df->zeroDigit = str[0];
6439#else
6440                    clen = UTF8_CHAR_LEN (str[0]);
6441                    if (str[clen] != '\0') {
6442                        reportError (node, "zero-digit has to be a single"
6443                                     " char", errMsg);
6444                        if (newdf) FREE((char*)df);
6445                        return -1;
6446                    }
6447                    Tcl_UtfToUniChar (str, &df->zeroDigit);
6448#endif /* TclOnly8Bits */
6449                }
6450                str = getAttr(node, "digit",              a_digit);
6451                if (str) {
6452#if TclOnly8Bits
6453                    if (str[1] != '\0') {
6454                        reportError (node, "digit has to be a single char",
6455                                     errMsg);
6456                        return -1;
6457                    }
6458                    df->digit = str[0];
6459#else
6460                    clen = UTF8_CHAR_LEN (str[0]);
6461                    if (str[clen] != '\0') {
6462                        reportError (node, "digit has to be a single char",
6463                                     errMsg);
6464                        if (newdf) FREE((char*)df);
6465                        return -1;
6466                    }
6467                    Tcl_UtfToUniChar (str, &df->digit);
6468#endif /* TclOnly8Bits */
6469                }
6470                str = getAttr(node, "pattern-separator",  a_patternSeparator);
6471                if (str) {
6472#if TclOnly8Bits
6473                    if (str[1] != '\0') {
6474                        reportError (node, "pattern-separator has to be a"
6475                                     " single char", errMsg);
6476                        return -1;
6477                    }
6478                    df->patternSeparator = str[0];
6479#else
6480                    clen = UTF8_CHAR_LEN (str[0]);
6481                    if (str[clen] != '\0') {
6482                        reportError (node, "pattern-separator has to be a"
6483                                     " single char", errMsg);
6484                        return -1;
6485                    }
6486                    Tcl_UtfToUniChar (str, &df->patternSeparator);
6487#endif /* TclOnly8Bits */
6488                }
6489                break;
6490
6491            case import:
6492                if (nonImportElemSeen) {
6493                    reportError (node, "xsl:import elements must come first",
6494                                 errMsg);
6495                    return -1;
6496                }
6497                if (node->firstChild) {
6498                    reportError (node, "xsl:import has to be empty!", errMsg);
6499                    return -1;
6500                }
6501                if (!node->ownerDocument->extResolver) {
6502                    reportError (node, "need resolver Script to include"
6503                                 " Stylesheet! (use"
6504                                 " \"-externalentitycommand\")", errMsg);
6505                    return -1;
6506                }
6507                baseURI = findBaseURI (node);
6508                href = getAttr (node, "href", a_href);
6509                if (!href) {
6510                    reportError (node, "xsl:import: missing mandatory"
6511                                 " attribute \"href\".", errMsg);
6512                    return -1;
6513                }
6514                extStyleSheet = getExternalDocument (interp, xs,
6515                                                     node->ownerDocument,
6516                                                     baseURI, href, 1, 0,
6517                                                     errMsg);
6518                if (!extStyleSheet) {
6519                    return -1;
6520                }
6521                childPrecedence = (precedence + *precedenceLowBound) / 2;
6522                childLowBound = *precedenceLowBound;
6523                rc = processTopLevel (interp, extStyleSheet->documentElement,
6524                                      xs, childPrecedence, &childLowBound,
6525                                      errMsg);
6526                *precedenceLowBound = childPrecedence;
6527                if (rc != 0) {
6528                    return rc;
6529                }
6530                break;
6531
6532            case include:
6533                if (node->firstChild) {
6534                    reportError (node, "xsl:include has to be empty.", errMsg);
6535                    return -1;
6536                }
6537                if (!node->ownerDocument->extResolver) {
6538                    reportError (node, "need resolver Script to include"
6539                                 "Stylesheet. (use"
6540                                 " \"-externalentitycommand\")", errMsg);
6541                    return -1;
6542                }
6543                baseURI = findBaseURI (node);
6544                href = getAttr (node, "href", a_href);
6545                if (!href) {
6546                    reportError (node, "xsl:include: missing mandatory"
6547                                 " attribute \"href\".", errMsg);
6548                    return -1;
6549                }
6550                extStyleSheet = getExternalDocument (interp, xs,
6551                                                     node->ownerDocument,
6552                                                     baseURI, href, 1, 0,
6553                                                     errMsg);
6554                if (!extStyleSheet) {
6555                    return -1;
6556                }
6557                xs->currentXSLTNode = extStyleSheet->documentElement;
6558                rc = processTopLevel (interp, extStyleSheet->documentElement,
6559                                      xs, precedence, precedenceLowBound,
6560                                      errMsg);
6561                if (rc != 0) {
6562                    return rc;
6563                }
6564                break;
6565
6566            case key:
6567                if (node->firstChild) {
6568                    reportError (node, "xsl:key has to be empty.", errMsg);
6569                    return -1;
6570                }
6571                name = getAttr(node, "name", a_name);
6572                if (!name) {
6573                    reportError (node, "xsl:key: missing mandatory"
6574                                 " attribute \"name\".", errMsg);
6575                    return -1;
6576                }
6577                match = getAttr(node, "match", a_match);
6578                if (!match) {
6579                    reportError (node, "xsl:key: missing mandatory"
6580                                 " attribute \"match\".", errMsg);
6581                    return -1;
6582                }
6583                use = getAttr(node, "use", a_use);
6584                if (!use) {
6585                    reportError (node, "xsl:key: missing mandatory"
6586                                 " attribute \"use\".", errMsg);
6587                    return -1;
6588                }
6589
6590                keyInfo = (xsltKeyInfo *)MALLOC(sizeof(xsltKeyInfo));
6591                keyInfo->node = node;
6592                rc = xpathParse (match, node, XPATH_KEY_MATCH_PATTERN, NULL,
6593                                 NULL, &(keyInfo->matchAst), errMsg);
6594                if (rc < 0) {
6595                    reportError (node, *errMsg, errMsg);
6596                    free ((char*)keyInfo);
6597                    return rc;
6598                }
6599                keyInfo->use       = use;
6600                rc = xpathParse (use, node, XPATH_KEY_USE_EXPR, NULL,
6601                                 NULL, &(keyInfo->useAst), errMsg);
6602                if (rc < 0) {
6603                    reportError (node, *errMsg, errMsg);
6604                    xpathFreeAst (keyInfo->matchAst);
6605                    free ((char*)keyInfo);
6606                    return rc;
6607                }
6608                domSplitQName (name, prefix, &localName);
6609                Tcl_DStringInit (&dStr);
6610                if (prefix[0] != '\0') {
6611                    ns = domLookupPrefix (node, prefix);
6612                    if (!ns) {
6613                        reportError (node, "There isn't a namespace bound"
6614                                     " to the prefix.", errMsg);
6615                        xpathFreeAst (keyInfo->matchAst);
6616                        xpathFreeAst (keyInfo->useAst);
6617                        FREE((char*)keyInfo);
6618                        return -1;
6619                    }
6620                    Tcl_DStringAppend (&dStr, ns->uri, -1);
6621                }
6622                Tcl_DStringAppend (&dStr, localName, -1);
6623                h = Tcl_CreateHashEntry (&(xs->keyInfos),
6624                                         Tcl_DStringValue (&dStr), &hnew);
6625                Tcl_DStringFree (&dStr);
6626                if (hnew) {
6627                    keyInfo->next  = NULL;
6628                } else {
6629                    keyInfo->next  = (xsltKeyInfo *)Tcl_GetHashValue (h);
6630                }
6631                Tcl_SetHashValue (h, keyInfo);
6632                break;
6633
6634            case namespaceAlias:
6635                if (node->firstChild) {
6636                    reportError (node, "xsl:namespace-alias has to be empty.",
6637                                 errMsg);
6638                    return -1;
6639                }
6640
6641                str = getAttr (node, "stylesheet-prefix", a_stylesheetPrefix);
6642                if (!str) {
6643                    reportError (node, "xsl:namespace-alias: missing"
6644                                 " mandatory attribute"
6645                                 " \"stylesheet-prefix\".", errMsg);
6646                    return -1 ;
6647                }
6648                if (strcmp (str, "#default")==0) {
6649                    str = NULL;
6650                    nsFrom = domLookupPrefix (node, "");
6651                } else {
6652                    nsFrom = domLookupPrefix (node, str);
6653                }
6654                if (!nsFrom) {
6655                    reportError (node, "xsl:namespace-alias: no namespace"
6656                                 " bound to the \"stylesheet-prefix\".",
6657                                 errMsg);
6658                    return -1;
6659                }
6660
6661                str = getAttr (node, "result-prefix", a_resultPrefix);
6662                if (!str) {
6663                    reportError (node, "xsl:namespace-alias: missing mandatory"
6664                                 " attribute \"result-prefix\".", errMsg);
6665                    return -1;
6666                }
6667                if (strcmp (str, "#default")==0) {
6668                    nsTo = domLookupPrefix (node, "");
6669                } else {
6670                    nsTo = domLookupPrefix (node, str);
6671                }
6672                if (!nsTo) {
6673                    reportError (node, "xsl:namespace-alias: no namespace"
6674                                 " bound to the \"result-prefix\".", errMsg);
6675                    return -1;
6676                }
6677
6678                nsAlias = xs->nsAliases;
6679                ignore = 0;
6680                while (nsAlias) {
6681                    if (strcmp (nsAlias->fromUri, nsFrom->uri)==0) {
6682                        if (nsAlias->precedence > precedence) {
6683                            ignore = 1;
6684                        }
6685                        break;
6686                    }
6687                    nsAlias = nsAlias->next;
6688                }
6689                if (ignore) break;
6690                if (nsAlias) {
6691                    FREE(nsAlias->toUri);
6692                } else {
6693                    nsAlias = (xsltNSAlias *)MALLOC(sizeof (xsltNSAlias));
6694                    nsAlias->fromUri = tdomstrdup (nsFrom->uri);
6695                    nsAlias->next = xs->nsAliases;
6696                    xs->nsAliases = nsAlias;
6697                }
6698                nsAlias->toUri = tdomstrdup (nsTo->uri);
6699                nsAlias->precedence = precedence;
6700                break;
6701
6702            case output:
6703                if (node->firstChild) {
6704                    reportError (node, "xsl:output has to be empty.", errMsg);
6705                    return -1;
6706                }
6707                str = getAttr(node, "method", a_method);
6708                if (str) {
6709                    if (xs->doctype.method) FREE(xs->doctype.method);
6710                    xs->doctype.method    = tdomstrdup(str);
6711                }
6712                str = getAttr(node, "encoding", a_encoding);
6713                if (str) {
6714                    if (xs->doctype.encoding) FREE(xs->doctype.encoding);
6715                    xs->doctype.encoding  = tdomstrdup(str);
6716                }
6717                str = getAttr(node, "omit-xml-declaration",
6718                              a_omitXMLDeclaration);
6719                if (str) {
6720                    if (strcmp (str, "yes") == 0) {
6721                        xs->doctype.omitXMLDeclaration = 1;
6722                    } else if (strcmp (str, "no") == 0) {
6723                        xs->doctype.omitXMLDeclaration = 0;
6724                    } else {
6725                        reportError (node, "Unexpected value for"
6726                                     " 'omit-xml-declaration' attribute",
6727                                     errMsg);
6728                        return -1;
6729                    }
6730                }
6731                str = getAttr(node, "standalone", a_standalone);
6732                if (str) {
6733                    if (strcmp (str, "yes") == 0) {
6734                        xs->doctype.standalone = 1;
6735                    } else if (strcmp (str, "no") == 0) {
6736                        xs->doctype.standalone = 0;
6737                    } else {
6738                        reportError (node, "Unexpected value for 'standalone'"
6739                                     " attribute", errMsg);
6740                        return -1;
6741                    }
6742                }
6743                str = getAttr(node, "doctype-public", a_doctypePublic);
6744                if (str) {
6745                    if (xs->doctype.publicId) {
6746                        FREE ((char*) xs->doctype.publicId);
6747                    }
6748                    xs->doctype.publicId = tdomstrdup(str);
6749                }
6750                str = getAttr(node, "doctype-system", a_doctypeSystem);
6751                if (str) {
6752                    if (xs->doctype.systemId) {
6753                        FREE ((char*) xs->doctype.systemId);
6754                    }
6755                    xs->doctype.systemId = tdomstrdup(str);
6756                }
6757                str = getAttr(node, "cdata-section-elements",
6758                              a_cdataSectionElements);
6759                if (str) {
6760                    if (!xs->doctype.cdataSectionElements) {
6761                        xs->doctype.cdataSectionElements =
6762                            (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable));
6763                        Tcl_InitHashTable (xs->doctype.cdataSectionElements,
6764                                           TCL_STRING_KEYS);
6765                    }
6766                    if (!getCdataSectionElements (node, str,
6767                            xs->doctype.cdataSectionElements, errMsg)) {
6768                        return -1;
6769                    }
6770                }
6771                str = getAttr(node, "indent", a_indent);
6772                if (str) {
6773                    if (strcmp (str, "yes") == 0) {
6774                        xs->indentOutput = 1;
6775                    } else if (strcmp (str, "no") == 0) {
6776                        xs->indentOutput = 0;
6777                    } else {
6778                        reportError (node, "Unexpected value for 'indent'"
6779                                     " attribute.", errMsg);
6780                        return -1;
6781                    }
6782                }
6783                str = getAttr(node, "media-type", a_mediaType);
6784                if (str) {
6785                    if (xs->doctype.mediaType) FREE(xs->doctype.mediaType);
6786                    xs->doctype.mediaType = tdomstrdup(str);
6787                }
6788                break;
6789
6790            case preserveSpace:
6791                if (node->firstChild) {
6792                    reportError (node, "xsl:preserve-space has to be empty.",
6793                                 errMsg);
6794                    return -1;
6795                }
6796                str = getAttr(node, "elements", a_elements);
6797                if (str) {
6798                    rc = fillElementList(&xs->wsInfo, 0, precedence,
6799                                         node, str, errMsg);
6800                    CHECK_RC;
6801                } else {
6802                    reportError (node, "xsl:preserve-space: missing required"
6803                                 " attribute \"elements\".", errMsg);
6804                    return -1;
6805                }
6806                break;
6807
6808            case stripSpace:
6809                if (node->firstChild) {
6810                    reportError (node, "xsl:strip-space has to be empty.",
6811                                 errMsg);
6812                    return -1;
6813                }
6814                str = getAttr(node, "elements", a_elements);
6815                if (str) {
6816                    rc = fillElementList(&xs->wsInfo, 1, precedence, node,
6817                                         str, errMsg);
6818                    CHECK_RC;
6819                } else {
6820                    reportError (node, "xsl:strip-space: missing required"
6821                                 " attribute \"elements\".", errMsg);
6822                    return -1;
6823                }
6824                break;
6825
6826            case template:
6827                rc = xsltAddTemplate (xs, node, precedence, errMsg);
6828                CHECK_RC;
6829                break;
6830
6831            case param:
6832            case variable:
6833                str = getAttr(node, "name", a_name);
6834                if (!str) {
6835                    reportError (node, "xsl:variable and xsl:param elements"
6836                                 " must have a \"name\" attribute.", errMsg);
6837                    return -1;
6838                }
6839                domSplitQName (str, prefix, &localName);
6840                ns = NULL;
6841                if (prefix[0] != '\0') {
6842                    ns = domLookupPrefix (node, prefix);
6843                    if (!ns) {
6844                        reportError (node, "There isn't a namespace bound"
6845                                     " to the prefix.", errMsg);
6846                        return -1;
6847                    }
6848                }
6849                Tcl_DStringInit (&dStr);
6850                if (ns) {
6851                    Tcl_DStringAppend (&dStr, ns->uri, -1);
6852                }
6853                Tcl_DStringAppend (&dStr, localName, -1);
6854                h = Tcl_CreateHashEntry (&(xs->topLevelVars),
6855                                         Tcl_DStringValue (&dStr), &hnew);
6856                Tcl_DStringFree (&dStr);
6857                if (!hnew) {
6858                    topLevelVar = (xsltTopLevelVar *)Tcl_GetHashValue (h);
6859                    /* Since imported stylesheets are processed at the
6860                       point at which they encounters the definitions are
6861                       already in increasing order of import precedence.
6862                       Therefor we have only to check, if there is a
6863                       top level var or parm with the same precedence */
6864                    if (topLevelVar->precedence == precedence) {
6865                        reportError (node, "There is already a variable"
6866                                     " or parameter with this name with the"
6867                                     " same import precedence.", errMsg);
6868                        return -1;
6869                    }
6870                } else {
6871                    topLevelVar = (xsltTopLevelVar *)
6872                        MALLOC (sizeof (xsltTopLevelVar));
6873                    Tcl_SetHashValue (h, topLevelVar);
6874                }
6875                topLevelVar->node = node;
6876                topLevelVar->name = str;
6877                if (tag == param) {
6878                    topLevelVar->isParameter = 1;
6879                } else {
6880                    topLevelVar->isParameter = 0;
6881                }
6882                topLevelVar->precedence = precedence;
6883
6884                break;
6885
6886            default:
6887                if (node->nodeType == ELEMENT_NODE) {
6888                    if (!node->namespace) {
6889                        reportError (node, "Top level elements must have a"
6890                                     " non-null namespace URI.", errMsg);
6891                        return -1;
6892                    }
6893                    if (strcmp (XSLT_NAMESPACE, domNamespaceURI (node))==0) {
6894                        if (!xs->currentSubDoc->fwCmpProcessing) {
6895                            reportError (node, "XSLT element not allowed"
6896                                         " on top level or unknown XSLT"
6897                                         " element.", errMsg);
6898
6899                            return -1;
6900                        }
6901                    }
6902                } else if (node->nodeType == TEXT_NODE) {
6903                    char *pc;
6904                    int   i, only_whites;
6905
6906                    only_whites = 1;
6907                    for (i=0, pc = ((domTextNode*)node)->nodeValue;
6908                         i < ((domTextNode*)node)->valueLength;
6909                         i++, pc++) {
6910                        if (!IS_XML_WHITESPACE(*pc)) {
6911                            only_whites = 0;
6912                            break;
6913                        }
6914                    }
6915                    if (!only_whites) {
6916                        reportError (node, "Non-whitespace text is not"
6917                                     " allowed between top level elements.",
6918                                     errMsg);
6919                        return -1;
6920                    }
6921                }
6922
6923                break;
6924        }
6925        node = node->nextSibling;
6926    }
6927    return 0;
6928}
6929
6930/*----------------------------------------------------------------------------
6931|   xsltFreeState
6932|
6933\---------------------------------------------------------------------------*/
6934static void
6935xsltFreeState (
6936    xsltState      * xs
6937) {
6938    xsltDecimalFormat *df,  *dfsave;
6939    xsltKeyInfo       *ki,  *kisave;
6940    xsltNodeSet       *kvalues;
6941    xsltSubDoc        *sd,  *sdsave;
6942    xsltAttrSet       *as,  *assave;
6943    xsltTemplate      *tpl, *tplsave;
6944    xsltNumberFormat  *nf;
6945    ast                t;
6946    xsltTopLevelVar   *tlv;
6947    xsltNSAlias       *nsAlias, *nsAliasSave;
6948    xsltExclExtNS     *eNS, *eNSsave;
6949    Tcl_HashEntry     *entryPtr, *entryPtr1;
6950    Tcl_HashSearch     search, search1;
6951    Tcl_HashTable     *htable;
6952    double            *f;
6953
6954
6955    if (xs->doctype.systemId) FREE(xs->doctype.systemId);
6956    if (xs->doctype.publicId) FREE(xs->doctype.publicId);
6957    if (xs->doctype.internalSubset) FREE(xs->doctype.internalSubset);
6958    if (xs->doctype.cdataSectionElements) {
6959        Tcl_DeleteHashTable (xs->doctype.cdataSectionElements);
6960        FREE (xs->doctype.cdataSectionElements);
6961    }
6962    for (entryPtr = Tcl_FirstHashEntry (&xs->namedTemplates, &search);
6963         entryPtr != (Tcl_HashEntry*) NULL;
6964         entryPtr = Tcl_NextHashEntry (&search)) {
6965        tpl = (xsltTemplate *) Tcl_GetHashValue (entryPtr);
6966        if (!tpl->match) {
6967            FREE(tpl);
6968        }
6969    }
6970    Tcl_DeleteHashTable (&xs->namedTemplates);
6971
6972    for (entryPtr = Tcl_FirstHashEntry (&xs->isElementTpls, &search);
6973         entryPtr != (Tcl_HashEntry*) NULL;
6974         entryPtr = Tcl_NextHashEntry (&search)) {
6975        tpl = (xsltTemplate *) Tcl_GetHashValue (entryPtr);
6976        while (tpl) {
6977            if (tpl->freeAst) xpathFreeAst (tpl->freeAst);
6978            tplsave = tpl;
6979            tpl = tpl->next;
6980            FREE(tplsave);
6981        }
6982    }
6983    Tcl_DeleteHashTable (&xs->isElementTpls);
6984
6985    for (entryPtr = Tcl_FirstHashEntry(&xs->xpaths, &search);
6986            entryPtr != (Tcl_HashEntry*) NULL;
6987            entryPtr = Tcl_NextHashEntry(&search)) {
6988        t = (ast) Tcl_GetHashValue (entryPtr);
6989        xpathFreeAst (t);
6990    }
6991    Tcl_DeleteHashTable(&xs->xpaths);
6992
6993    for (entryPtr = Tcl_FirstHashEntry(&xs->pattern, &search);
6994            entryPtr != (Tcl_HashEntry*) NULL;
6995            entryPtr = Tcl_NextHashEntry(&search)) {
6996        t = (ast) Tcl_GetHashValue (entryPtr);
6997        xpathFreeAst (t);
6998    }
6999    Tcl_DeleteHashTable(&xs->pattern);
7000
7001    for (entryPtr = Tcl_FirstHashEntry(&xs->formats, &search);
7002            entryPtr != (Tcl_HashEntry*) NULL;
7003            entryPtr = Tcl_NextHashEntry(&search)) {
7004        nf = (xsltNumberFormat *) Tcl_GetHashValue (entryPtr);
7005        FREE(nf->tokens);
7006        FREE(nf);
7007    }
7008    Tcl_DeleteHashTable(&xs->formats);
7009
7010    if (&xs->topLevelVars) {
7011        for (entryPtr = Tcl_FirstHashEntry(&xs->topLevelVars, &search);
7012             entryPtr != (Tcl_HashEntry*) NULL;
7013             entryPtr = Tcl_NextHashEntry(&search)) {
7014            tlv = (xsltTopLevelVar *) Tcl_GetHashValue (entryPtr);
7015            FREE(tlv);
7016        }
7017        Tcl_DeleteHashTable (&xs->topLevelVars);
7018    }
7019
7020    /*--- free key definition information ---*/
7021    for (entryPtr = Tcl_FirstHashEntry (&xs->keyInfos, &search);
7022         entryPtr != (Tcl_HashEntry*) NULL;
7023         entryPtr = Tcl_NextHashEntry (&search)) {
7024        ki = (xsltKeyInfo *) Tcl_GetHashValue (entryPtr);
7025        while (ki) {
7026            kisave = ki;
7027            ki = ki->next;
7028            xpathFreeAst (kisave->matchAst);
7029            xpathFreeAst (kisave->useAst);
7030            FREE(kisave);
7031        }
7032    }
7033    Tcl_DeleteHashTable (&xs->keyInfos);
7034
7035    /*--- free sub documents ---*/
7036    sd = xs->subDocs;
7037    while (sd)  {
7038        sdsave = sd;
7039        sd = sd->next;
7040        for (entryPtr = Tcl_FirstHashEntry (&sdsave->keyData, &search);
7041             entryPtr != (Tcl_HashEntry*) NULL;
7042             entryPtr = Tcl_NextHashEntry (&search)) {
7043            htable = (Tcl_HashTable *) Tcl_GetHashValue (entryPtr);
7044            for (entryPtr1 = Tcl_FirstHashEntry (htable, &search1);
7045                 entryPtr1 != (Tcl_HashEntry*) NULL;
7046                 entryPtr1 = Tcl_NextHashEntry (&search1)) {
7047                kvalues = (xsltNodeSet *) Tcl_GetHashValue (entryPtr1);
7048                FREE(kvalues->nodes);
7049                FREE(kvalues);
7050            }
7051            Tcl_DeleteHashTable (htable);
7052            FREE(htable);
7053        }
7054        Tcl_DeleteHashTable (&sdsave->keyData);
7055        eNS = sdsave->excludeNS;
7056        while (eNS) {
7057            if (eNS->uri) FREE(eNS->uri);
7058            eNSsave = eNS;
7059            eNS = eNS->next;
7060            FREE(eNSsave);
7061        }
7062        eNS = sdsave->extensionNS;
7063        while (eNS) {
7064            if (eNS->uri) FREE(eNS->uri);
7065            eNSsave = eNS;
7066            eNS = eNS->next;
7067            FREE(eNSsave);
7068        }
7069        if (sdsave->baseURI) FREE(sdsave->baseURI);
7070        if (sdsave->mustFree) {
7071            domFreeDocument (sdsave->doc, NULL, NULL);
7072        }
7073        FREE(sdsave);
7074    }
7075
7076    nsAlias = xs->nsAliases;
7077    while (nsAlias) {
7078        nsAliasSave = nsAlias;
7079        nsAlias = nsAlias->next;
7080        if (nsAliasSave->fromUri) FREE(nsAliasSave->fromUri);
7081        if (nsAliasSave->toUri) FREE(nsAliasSave->toUri);
7082        FREE(nsAliasSave);
7083    }
7084
7085    /*--- free decimal formats ---*/
7086    df = xs->decimalFormats;
7087    while (df) {
7088        dfsave = df;
7089        df = df->next;
7090        if (dfsave->name) FREE(dfsave->name);
7091        if (dfsave->uri) FREE(dfsave->uri);
7092        FREE(dfsave);
7093    }
7094
7095    /*--- free attribute sets ---*/
7096    as = xs->attrSets;
7097    while (as) {
7098       assave = as;
7099       as = as->next;
7100       FREE(assave);
7101    }
7102
7103    /*--- free templates ---*/
7104    tpl = xs->templates;
7105    while (tpl) {
7106       tplsave = tpl;
7107       if (tpl->freeAst) xpathFreeAst (tpl->freeAst);
7108       tpl = tpl->next;
7109       FREE(tplsave);
7110    }
7111
7112    for (entryPtr = Tcl_FirstHashEntry (&(xs->wsInfo.stripTokens), &search);
7113         entryPtr != (Tcl_HashEntry*) NULL;
7114         entryPtr = Tcl_NextHashEntry (&search)) {
7115        f = (double *) Tcl_GetHashValue (entryPtr);
7116        FREE(f);
7117    }
7118    Tcl_DeleteHashTable (&(xs->wsInfo.stripTokens));
7119
7120    for (entryPtr = Tcl_FirstHashEntry (&(xs->wsInfo.preserveTokens), &search);
7121         entryPtr != (Tcl_HashEntry*) NULL;
7122         entryPtr = Tcl_NextHashEntry (&search)) {
7123        f = (double *) Tcl_GetHashValue (entryPtr);
7124        FREE(f);
7125    }
7126    Tcl_DeleteHashTable (&(xs->wsInfo.preserveTokens));
7127
7128    FREE(xs->varFramesStack);
7129    FREE(xs->varStack);
7130    if (xs->doctype.method) FREE(xs->doctype.method);
7131    if (xs->doctype.encoding) FREE(xs->doctype.encoding);
7132    if (xs->doctype.mediaType) FREE(xs->doctype.mediaType);
7133    FREE (xs);
7134}
7135
7136void
7137xsltFreeStateWrapper (
7138    void      *clientData
7139    )
7140{
7141    xsltFreeState ((xsltState *)clientData);
7142}
7143
7144/*----------------------------------------------------------------------------
7145|   xsltResetState
7146|
7147\---------------------------------------------------------------------------*/
7148static void
7149xsltResetState (
7150    xsltState      * xs
7151    )
7152{
7153    xsltSubDoc        *sd,  *sdsave, *lastSubDoc = NULL;
7154    xsltNodeSet       *kvalues;
7155    Tcl_HashEntry     *entryPtr, *entryPtr1;
7156    Tcl_HashSearch     search, search1;
7157    Tcl_HashTable     *htable;
7158
7159
7160
7161
7162    /* Free the sub documents, which are resolved relative to nodes in
7163     * the xml source. */
7164    /* XML documents don't have excludeNS and extensionNS information
7165       and the already parsed XSLT documents information is
7166       preserved, therefor we don't touch excludeNS and extensionNS
7167       information */
7168    /* This loop works only as coded, because, the first subdoc will
7169     * always be the primary xslt doc, so xs->subDocs will not
7170     * change. Crusty stuff, this code. */
7171    sd = xs->subDocs;
7172    while (sd)  {
7173        sdsave = sd;
7174        sd = sd->next;
7175        if (sdsave->isStylesheet || sdsave->fixedXMLSource) {
7176            if (lastSubDoc) {
7177                lastSubDoc->next = sdsave;
7178            } else {
7179                xs->subDocs = sdsave;
7180            }
7181            lastSubDoc = sdsave;
7182            sdsave->next = NULL;
7183        } else {
7184            for (entryPtr = Tcl_FirstHashEntry (&sdsave->keyData, &search);
7185                 entryPtr != (Tcl_HashEntry*) NULL;
7186                 entryPtr = Tcl_NextHashEntry (&search)) {
7187                htable = (Tcl_HashTable *) Tcl_GetHashValue (entryPtr);
7188                for (entryPtr1 = Tcl_FirstHashEntry (htable, &search1);
7189                     entryPtr1 != (Tcl_HashEntry*) NULL;
7190                     entryPtr1 = Tcl_NextHashEntry (&search1)) {
7191                    kvalues = (xsltNodeSet *) Tcl_GetHashValue (entryPtr1);
7192                    FREE(kvalues->nodes);
7193                    FREE(kvalues);
7194                }
7195                Tcl_DeleteHashTable (htable);
7196                FREE(htable);
7197            }
7198            Tcl_DeleteHashTable (&sdsave->keyData);
7199
7200            if (sdsave->mustFree) {
7201                domFreeDocument (sdsave->doc, NULL, NULL);
7202            }
7203            if (sdsave->baseURI) FREE(sdsave->baseURI);
7204
7205            FREE(sdsave);
7206        }
7207    }
7208    xs->nsUniqeNr = 0;
7209    /* In theory, the varFramesStack and varStack pointers should
7210       be always back to there inital state. But to be sure, we
7211       re-initialize, just in case of a bizarre error or something. */
7212    xs->varFramesStackPtr = -1;
7213    xs->varStackPtr       = -1;
7214}
7215
7216/*----------------------------------------------------------------------------
7217|   xsltCompileStylesheet
7218|
7219\---------------------------------------------------------------------------*/
7220void *
7221xsltCompileStylesheet (
7222    domDocument       * xsltDoc,
7223    xpathFuncCallback   funcCB,
7224    void              * xpathFuncClientData,
7225    int                 guardXSLTTree,
7226    char             ** errMsg
7227    )
7228{
7229    domNode        *node;
7230    int             rc;
7231    char           *tailptr;
7232    const char     *baseURI;
7233    double          d, precedence, precedenceLowBound;
7234    xsltState      *xs;
7235    xsltSubDoc     *sdoc;
7236    domAttrNode    *attr;
7237    xsltTemplate   *tpl;
7238
7239    *errMsg = NULL;
7240    xs = (xsltState *) MALLOC (sizeof (xsltState));
7241
7242    Tcl_InitHashTable ( &(xs->namedTemplates), TCL_STRING_KEYS);
7243    Tcl_InitHashTable ( &(xs->isElementTpls), TCL_STRING_KEYS);
7244    xs->cbs.varCB           = xsltGetVar;
7245    xs->cbs.varClientData   = (void*)xs;
7246    xs->cbs.funcCB          = xsltXPathFuncs;
7247    xs->cbs.funcClientData  = xs;
7248    xs->orig_funcCB         = funcCB;
7249    xs->orig_funcClientData = xpathFuncClientData;
7250    xs->xsltMsgCB           = NULL;
7251    xs->xsltMsgClientData   = NULL;
7252    xs->varFramesStack      = (xsltVarFrame *)MALLOC(sizeof (xsltVarFrame)*4);
7253    xs->varFramesStackPtr   = -1;
7254    xs->varFramesStackLen   = 4;
7255    xs->varStack            = (xsltVariable *)MALLOC(sizeof (xsltVariable)*8);
7256    xs->varStackPtr         = -1;
7257    xs->varStackLen         = 8;
7258    xs->templates           = NULL;
7259    xs->lastNode            = NULL;
7260    xs->attrSets            = NULL;
7261    xs->decimalFormats      = (xsltDecimalFormat*)MALLOC(sizeof (xsltDecimalFormat));
7262    xs->subDocs             = NULL;
7263    xs->currentTplRule      = NULL;
7264    xs->currentXSLTNode     = NULL;
7265    xs->xsltDoc             = xsltDoc;
7266    xs->varsInProcess       = NULL;
7267    xs->nsAliases           = NULL;
7268    xs->nsUniqeNr           = 0;
7269    Tcl_InitHashTable ( &(xs->wsInfo.stripTokens), TCL_STRING_KEYS);
7270    Tcl_InitHashTable ( &(xs->wsInfo.preserveTokens), TCL_STRING_KEYS);
7271    xs->wsInfo.hasData      = 0;
7272    xs->wsInfo.stripAll     = 0;
7273    xs->wsInfo.wildcardPrec = 0.0;
7274    Tcl_InitHashTable ( &(xs->xpaths), TCL_STRING_KEYS);
7275    Tcl_InitHashTable ( &(xs->pattern), TCL_STRING_KEYS);
7276    Tcl_InitHashTable ( &(xs->formats), TCL_STRING_KEYS);
7277    Tcl_InitHashTable ( &(xs->topLevelVars), TCL_STRING_KEYS);
7278    Tcl_InitHashTable ( &(xs->keyInfos), TCL_STRING_KEYS);
7279    xs->decimalFormats->name              = NULL;
7280    xs->decimalFormats->uri               = NULL;
7281#if TclOnly8Bits
7282    xs->decimalFormats->decimalSeparator  = '.';
7283    xs->decimalFormats->groupingSeparator = ',';
7284    xs->decimalFormats->minusSign         = '-';
7285    xs->decimalFormats->percent           = '%';
7286    xs->decimalFormats->zeroDigit         = '0';
7287    xs->decimalFormats->digit             = '#';
7288    xs->decimalFormats->patternSeparator  = ';';
7289#else
7290    xs->decimalFormats->decimalSeparator  = 46;
7291    xs->decimalFormats->groupingSeparator = 44;
7292    xs->decimalFormats->minusSign         = 45;
7293    xs->decimalFormats->percent           = 37;
7294    xs->decimalFormats->perMille          = 0x2030;
7295    xs->decimalFormats->zeroDigit         = 48;
7296    xs->decimalFormats->digit             = 35;
7297    xs->decimalFormats->patternSeparator  = 59;
7298#endif /* TclOnly8Bits */
7299    xs->decimalFormats->infinity          = "Infinity";
7300    xs->decimalFormats->NaN               = "NaN";
7301    xs->decimalFormats->next              = NULL;
7302    xs->indentOutput = 0;
7303    memset (&xs->doctype, 0, sizeof (domDocInfo));
7304
7305    node = xsltDoc->documentElement;
7306
7307    /* add the xslt doc to the doc list */
7308    sdoc = (xsltSubDoc*)MALLOC(sizeof (xsltSubDoc));
7309    sdoc->doc = xsltDoc;
7310    baseURI = findBaseURI (xsltDoc->documentElement);
7311    if (baseURI) {
7312        sdoc->baseURI = tdomstrdup (baseURI);
7313    } else {
7314        sdoc->baseURI = NULL;
7315    }
7316    Tcl_InitHashTable (&(sdoc->keyData), TCL_STRING_KEYS);
7317    sdoc->excludeNS = NULL;
7318    sdoc->extensionNS = NULL;
7319    sdoc->fwCmpProcessing = 0;
7320    sdoc->isStylesheet = 1;
7321    sdoc->next = xs->subDocs;
7322    sdoc->mustFree = !guardXSLTTree;
7323    sdoc->fixedXMLSource = 0;
7324    xs->subDocs = sdoc;
7325
7326    xs->currentSubDoc = sdoc;
7327
7328    if ((getTag(node) != stylesheet) && (getTag(node) != transform)) {
7329        /* Check for "Literal Result Element as Stylesheet" (XSLT rec 2.3) */
7330        attr = domGetAttributeNodeNS (node, XSLT_NAMESPACE, "version");
7331        if (!attr) {
7332            reportError (node, "The supplied DOM tree does not appear to be"
7333                         " a stylesheet.", errMsg);
7334            goto error;
7335        }
7336        d = strtod (attr->nodeValue, &tailptr);
7337        if (d == 0.0 && tailptr == attr->nodeValue) {
7338            reportError (node, "The value of the attribute \"version\" must"
7339                         " be a number.", errMsg);
7340            goto error;
7341        }
7342        if (d > 1.0) {
7343            sdoc->fwCmpProcessing = 1;
7344        } else if (d < 1.0) {
7345            reportError (node, "Strange \"xsl:version\" value, don't know,"
7346                         " how to handle.", errMsg);
7347            goto error;
7348        }
7349        StripXSLTSpace (xsltDoc->rootNode);
7350        /* According to XSLT rec 2.3 we add the literal result element as
7351           template, which matches "/" */
7352        tpl = (xsltTemplate *) MALLOC (sizeof (xsltTemplate));
7353        tpl->match      = "/";
7354        tpl->name       = NULL;
7355        tpl->nameURI    = NULL;
7356        tpl->mode       = NULL;
7357        tpl->modeURI    = NULL;
7358        tpl->prio       = 0.5;
7359        tpl->content    = node->ownerDocument->rootNode;
7360        tpl->precedence = 1.0;
7361        tpl->next       = NULL;
7362        tpl->sDoc       = sdoc;
7363        rc = xpathParse (tpl->match, node, XPATH_TEMPMATCH_PATTERN, NULL, NULL,
7364                         &(tpl->freeAst), errMsg);
7365        tpl->ast        = tpl->freeAst;
7366        xs->templates = tpl;
7367        if (rc < 0) goto error;
7368    } else {
7369        rc = addExclExtNS (sdoc, node, errMsg);
7370        if (rc < 0) goto error;
7371
7372        StripXSLTSpace (xsltDoc->rootNode);
7373        precedence = 1.0;
7374        precedenceLowBound = 0.0;
7375        rc = 0;
7376        rc = processTopLevel (xpathFuncClientData, node, xs, precedence,
7377                              &precedenceLowBound, errMsg);
7378        if (rc != 0) goto error;
7379    }
7380
7381    return xs;
7382
7383 error:
7384    xsltFreeState (xs);
7385    return NULL;
7386}
7387
7388/*----------------------------------------------------------------------------
7389|   xsltProcess
7390|
7391\---------------------------------------------------------------------------*/
7392int xsltProcess (
7393    domDocument       * xsltDoc,
7394    domNode           * xmlNode,
7395    void              * xsltCmdData,
7396    char             ** parameters,
7397    int                 ignoreUndeclaredParameters,
7398    xpathFuncCallback   funcCB,
7399    void              * xpathFuncClientData,
7400    xsltMsgCB           xsltMsgCB,
7401    void              * xsltMsgClientData,
7402    char             ** errMsg,
7403    domDocument      ** resultDoc
7404    )
7405{
7406    xpathResultSet  nodeList;
7407    domNode        *node;
7408    int             rc, hnew;
7409    const char     *baseURI;
7410    xsltState      *xs;
7411    xsltSubDoc     *sdoc;
7412    Tcl_HashEntry  *entryPtr;
7413    Tcl_HashSearch  search;
7414
7415    *errMsg = NULL;
7416    if (xsltCmdData) {
7417        xs = (xsltState *) xsltCmdData;
7418    } else {
7419        xs = (xsltState *) xsltCompileStylesheet (xsltDoc, funcCB,
7420                                                  xpathFuncClientData,
7421                                                  1, errMsg);
7422        if (!xs) return -1;
7423    }
7424
7425    if (xmlNode->nodeType == DOCUMENT_NODE) {
7426        xmlNode = ((domDocument *)xmlNode)->rootNode;
7427    } else {
7428        xmlNode = xmlNode->ownerDocument->rootNode;
7429    }
7430    DBG(printXML(xmlNode, 0, 1);)
7431
7432    if (xmlNode->ownerDocument->nodeFlags & NEEDS_RENUMBERING) {
7433        domRenumberTree (xmlNode->ownerDocument->rootNode);
7434        xmlNode->ownerDocument->nodeFlags &= ~NEEDS_RENUMBERING;
7435    }
7436
7437    xs->resultDoc           = domCreateDoc(NULL, 0);
7438    if (xs->doctype.systemId) {
7439        xs->resultDoc->doctype = (domDocInfo *)MALLOC (sizeof (domDocInfo));
7440        memset (xs->resultDoc->doctype, 0, (sizeof (domDocInfo)));
7441        xs->resultDoc->doctype->systemId = tdomstrdup (xs->doctype.systemId);
7442    }
7443    if (xs->doctype.publicId) {
7444        if (!xs->resultDoc->doctype) {
7445            xs->resultDoc->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo));
7446            memset (xs->resultDoc->doctype, 0, (sizeof (domDocInfo)));
7447        }
7448        xs->resultDoc->doctype->publicId = tdomstrdup (xs->doctype.publicId);
7449    }
7450    if (xs->doctype.encoding) {
7451        if (!xs->resultDoc->doctype) {
7452            xs->resultDoc->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo));
7453            memset (xs->resultDoc->doctype, 0, (sizeof (domDocInfo)));
7454        }
7455        xs->resultDoc->doctype->encoding = tdomstrdup (xs->doctype.encoding);
7456    }
7457    if (xs->doctype.mediaType) {
7458        if (!xs->resultDoc->doctype) {
7459            xs->resultDoc->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo));
7460            memset (xs->resultDoc->doctype, 0, (sizeof (domDocInfo)));
7461        }
7462        xs->resultDoc->doctype->mediaType = tdomstrdup (xs->doctype.mediaType);
7463    }
7464    if (xs->doctype.standalone) {
7465        if (!xs->resultDoc->doctype) {
7466            xs->resultDoc->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo));
7467            memset (xs->resultDoc->doctype, 0, (sizeof (domDocInfo)));
7468        }
7469        xs->resultDoc->doctype->standalone = 1;
7470    }
7471    if (xs->indentOutput) {
7472        xs->resultDoc->nodeFlags |= OUTPUT_DEFAULT_INDENT;
7473    }
7474    if (xs->doctype.cdataSectionElements) {
7475        if (!xs->resultDoc->doctype) {
7476            xs->resultDoc->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo));
7477            memset (xs->resultDoc->doctype, 0, (sizeof (domDocInfo)));
7478        }
7479        xs->resultDoc->doctype->cdataSectionElements =
7480            (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable));
7481        Tcl_InitHashTable (xs->resultDoc->doctype->cdataSectionElements,
7482                           TCL_STRING_KEYS);
7483        for (entryPtr = Tcl_FirstHashEntry (xs->doctype.cdataSectionElements,
7484                                            &search);
7485             entryPtr != (Tcl_HashEntry*) NULL;
7486             entryPtr = Tcl_NextHashEntry (&search)) {
7487            Tcl_CreateHashEntry (xs->resultDoc->doctype->cdataSectionElements,
7488                                 Tcl_GetHashKey (
7489                                     xs->doctype.cdataSectionElements, entryPtr
7490                                     ), &hnew);
7491        }
7492    }
7493
7494    xs->xmlRootNode         = xmlNode;
7495    xs->lastNode            = xs->resultDoc->rootNode;
7496    xs->xsltMsgCB           = xsltMsgCB;
7497    xs->xsltMsgClientData   = xsltMsgClientData;
7498
7499
7500    xsltPushVarFrame(xs);
7501    xpathRSInit( &nodeList );
7502    rsAddNodeFast( &nodeList, xmlNode);
7503
7504    /*  strip space from the XML document, if necessary */
7505    if (xs->wsInfo.hasData) {
7506        StripXMLSpace (xs, xmlNode);
7507    }
7508
7509    /* add the xml doc to the doc list */
7510    sdoc = (xsltSubDoc*)MALLOC(sizeof (xsltSubDoc));
7511    sdoc->doc = xmlNode->ownerDocument;
7512    baseURI = findBaseURI (xmlNode);
7513    if (baseURI) {
7514        sdoc->baseURI = tdomstrdup (baseURI);
7515    } else {
7516        sdoc->baseURI = NULL;
7517    }
7518    Tcl_InitHashTable (&(sdoc->keyData), TCL_STRING_KEYS);
7519    sdoc->excludeNS = NULL;
7520    sdoc->extensionNS = NULL;
7521    sdoc->fwCmpProcessing = 0;
7522    sdoc->isStylesheet = 0;
7523    sdoc->mustFree = 0;
7524    sdoc->fixedXMLSource = 0;
7525    sdoc->next = xs->subDocs;
7526    xs->subDocs = sdoc;
7527
7528    xs->currentSubDoc = sdoc;
7529
7530    rc = processTopLevelVars (xmlNode, xs, parameters,
7531                              ignoreUndeclaredParameters, errMsg);
7532    if (rc != 0) goto error;
7533
7534    node = xs->xsltDoc->documentElement;
7535    rc = ApplyTemplates (xs, &nodeList, xmlNode, 0, node, &nodeList, NULL,
7536                         NULL, errMsg);
7537    if (rc != 0) goto error;
7538
7539    /* Rudimentary xsl:output support */
7540    if (xs->doctype.method) {
7541        if (!xs->resultDoc->doctype) {
7542            xs->resultDoc->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo));
7543            memset (xs->resultDoc->doctype, 0, (sizeof (domDocInfo)));
7544        }
7545        xs->resultDoc->doctype->method = tdomstrdup (xs->doctype.method);
7546    } else {
7547        /* default output method */
7548        node = xs->resultDoc->rootNode->firstChild;
7549        while (node) {
7550            if (node->nodeType == TEXT_NODE) {
7551                char *pc;
7552                int   i, only_whites;
7553
7554                only_whites = 1;
7555                for (i=0, pc = ((domTextNode*)node)->nodeValue;
7556                     i < ((domTextNode*)node)->valueLength;
7557                     i++, pc++) {
7558                    if (!IS_XML_WHITESPACE(*pc)) {
7559                        only_whites = 0;
7560                        break;
7561                    }
7562                }
7563                if (!only_whites) break;
7564            }
7565            if (node->nodeType == ELEMENT_NODE) {
7566                if (STRCASECMP(node->nodeName, "html")==0) {
7567                    if (!xs->resultDoc->doctype) {
7568                        xs->resultDoc->doctype =
7569                            (domDocInfo*)MALLOC (sizeof (domDocInfo));
7570                        memset (xs->resultDoc->doctype, 0,
7571                                (sizeof (domDocInfo)));
7572                    }
7573                    xs->resultDoc->doctype->method = tdomstrdup ("html");
7574                }
7575                break;
7576            }
7577            node = node->nextSibling;
7578        }
7579    }
7580    /* Make the first ELEMENT_NODE the documentElement. There could
7581       be text, comment or PI nodes before the first element node.
7582       If the root node doesn't have an element node under it's childs,
7583       fall back to the firstChild as documentElement. */
7584    domSetDocumentElement (xs->resultDoc);
7585
7586    *resultDoc = xs->resultDoc;
7587
7588    xsltPopVarFrame (xs);
7589    xpathRSFree( &nodeList );
7590    if (xsltCmdData) {
7591        xsltResetState (xs);
7592    } else {
7593        xsltFreeState (xs);
7594    }
7595    return 0;
7596
7597 error:
7598    xsltPopVarFrame (xs);
7599    xpathRSFree( &nodeList );
7600    domFreeDocument (xs->resultDoc, NULL, NULL);
7601    if (xsltCmdData) {
7602        xsltResetState (xs);
7603    } else {
7604        xsltFreeState (xs);
7605    }
7606    return -1;
7607
7608} /* xsltProcess */
7609
7610