1/*----------------------------------------------------------------------------
2|   Copyright (C) 1999  Jochen C. Loewer (loewerj@hotmail.com)
3+-----------------------------------------------------------------------------
4|
5|   Rcsid: @(#)$Id: nodecmd.c,v 1.20 2005/03/26 14:08:06 rolf Exp $
6|
7|   The contents of this file are subject to the Mozilla Public License
8|   Version 1.1 (the "License"); you may not use this file except in
9|   compliance with the License. You may obtain a copy of the License at
10|   http://www.mozilla.org/MPL/
11|
12|   Software distributed under the License is distributed on an "AS IS"
13|   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
14|   License for the specific language governing rights and limitations
15|   under the License.
16|
17|   The Original Code is tDOM.
18|
19|   The Initial Developer of the Original Code is Jochen Loewer.
20|
21|   Portions created by Jochen Loewer are Copyright (C) 1998, 1999
22|   Jochen Loewer. All Rights Reserved.
23|
24|   Portions created by Zoran Vasiljevic are Copyright (C) 2000-2002
25|   Zoran Vasiljevic. All Rights Reserved.
26|
27|   Portions created by Rolf Ade are Copyright (C) 1999-2002
28|   Rolf Ade. All Rights Reserved.
29|
30|   Written by Zoran Vasiljevic
31|   July 12, 2000
32|
33\---------------------------------------------------------------------------*/
34
35
36/*----------------------------------------------------------------------------
37|   Includes
38|
39\---------------------------------------------------------------------------*/
40#include <dom.h>
41#include <tcldom.h>
42#include <tcl.h>
43#include <nodecmd.h>
44
45#define PARSER_NODE 9999 /* Hack so that we can invoke XML parser */
46/* More hacked domNodeTypes - used to signal, that we want to check
47   name/data of the node to create. */
48#define ELEMENT_NODE_ANAME_CHK 10000
49#define ELEMENT_NODE_AVALUE_CHK 10001
50#define ELEMENT_NODE_CHK 10002
51#define TEXT_NODE_CHK 10003
52#define COMMENT_NODE_CHK 10004
53#define CDATA_SECTION_NODE_CHK 10005
54#define PROCESSING_INSTRUCTION_NODE_NAME_CHK 10006
55#define PROCESSING_INSTRUCTION_NODE_VALUE_CHK 10007
56#define PROCESSING_INSTRUCTION_NODE_CHK 10008
57
58/*----------------------------------------------------------------------------
59|   Types
60|
61|   This structure represents one stack slot. The stack itself
62|   is implemented as double-linked-list of following structures.
63|
64\---------------------------------------------------------------------------*/
65typedef struct StackSlot {
66    void             *element;   /* The stacked element */
67    struct StackSlot *nextPtr;   /* Next link */
68    struct StackSlot *prevPtr;   /* Previous link */
69} StackSlot;
70
71/*----------------------------------------------------------------------------
72|   Beginning of the stack and current element pointer are local
73|   to current thread and also local to this file.
74|   For non-threaded environments, it's a regular static.
75|
76\---------------------------------------------------------------------------*/
77typedef struct CurrentStack {
78    StackSlot *elementStack;
79    StackSlot *currentSlot;
80} CurrentStack;
81
82/*----------------------------------------------------------------------------
83|   Structure used as clientData of the created commands.
84|   The structure stores, which type of node the command has
85|   to create and, in case of elementNodes and if given, the
86|   namespace of the node.
87\---------------------------------------------------------------------------*/
88typedef struct NodeInfo {
89    int   type;
90    char *namespace;
91} NodeInfo;
92
93#ifndef TCL_THREADS
94  static CurrentStack dataKey;
95# define TSDPTR(a) a
96#else
97  static Tcl_ThreadDataKey dataKey;
98# define TSDPTR(a) (CurrentStack*)Tcl_GetThreadData((a),sizeof(CurrentStack))
99#endif
100
101/*----------------------------------------------------------------------------
102|   Forward declarations
103|
104\---------------------------------------------------------------------------*/
105static void * StackPush  _ANSI_ARGS_((void *));
106static void * StackPop   _ANSI_ARGS_((void));
107static void * StackTop   _ANSI_ARGS_((void));
108static int    NodeObjCmd _ANSI_ARGS_((ClientData,Tcl_Interp*,int,Tcl_Obj *CONST o[]));
109static void   StackFinalize _ANSI_ARGS_((ClientData));
110
111extern int tcldom_appendXML (Tcl_Interp*, domNode*, Tcl_Obj*);
112
113
114/*----------------------------------------------------------------------------
115|   StackPush
116|
117\---------------------------------------------------------------------------*/
118static void *
119StackPush (element)
120    void *element;
121{
122    StackSlot *newElement;
123    CurrentStack *tsdPtr = TSDPTR(&dataKey);
124
125    /*-------------------------------------------------------------------
126    |   Reuse already allocated stack slots, if any
127    |
128    \------------------------------------------------------------------*/
129    if (tsdPtr->currentSlot && tsdPtr->currentSlot->nextPtr) {
130        tsdPtr->currentSlot = tsdPtr->currentSlot->nextPtr;
131        tsdPtr->currentSlot->element = element;
132        return element;
133    }
134
135    /*-------------------------------------------------------------------
136    |   Allocate new stack slot
137    |
138    \------------------------------------------------------------------*/
139    newElement = (StackSlot *)MALLOC(sizeof(StackSlot));
140    memset(newElement, 0, sizeof(StackSlot));
141
142    if (tsdPtr->elementStack == NULL) {
143        tsdPtr->elementStack = newElement;
144#ifdef TCL_THREADS
145        Tcl_CreateThreadExitHandler(StackFinalize, tsdPtr->elementStack);
146#else
147        Tcl_CreateExitHandler (StackFinalize, tsdPtr->elementStack);
148#endif
149    } else {
150        tsdPtr->currentSlot->nextPtr = newElement;
151        newElement->prevPtr = tsdPtr->currentSlot;
152    }
153
154    tsdPtr->currentSlot = newElement;
155    tsdPtr->currentSlot->element = element;
156
157    return element;
158}
159
160/*----------------------------------------------------------------------------
161|   StackPop  -  pops the element from stack
162|
163\---------------------------------------------------------------------------*/
164static void *
165StackPop ()
166{
167    void *element;
168    CurrentStack *tsdPtr = TSDPTR(&dataKey);
169
170    element = tsdPtr->currentSlot->element;
171    if (tsdPtr->currentSlot->prevPtr) {
172        tsdPtr->currentSlot = tsdPtr->currentSlot->prevPtr;
173    } else {
174        tsdPtr->currentSlot->element = NULL;
175    }
176
177    return element;
178}
179
180/*----------------------------------------------------------------------------
181|   StackTop  -  returns top-level element from stack
182|
183\---------------------------------------------------------------------------*/
184static void *
185StackTop ()
186{
187    CurrentStack *tsdPtr = TSDPTR(&dataKey);
188
189    if (tsdPtr->currentSlot == NULL) {
190        return NULL;
191    }
192
193    return tsdPtr->currentSlot->element;
194}
195
196
197/*----------------------------------------------------------------------------
198|   StackFinalize - reclaims stack memory (slots only, not elements)
199|
200\---------------------------------------------------------------------------*/
201static void
202StackFinalize (clientData)
203    ClientData clientData;
204{
205    StackSlot *tmp, *stack = (StackSlot *)clientData;
206
207    while (stack) {
208        tmp = stack->nextPtr;
209        FREE((char*)stack);
210        stack = tmp;
211    }
212}
213
214/*
215 *----------------------------------------------------------------------
216 *
217 * namespaceTail --
218 *
219 *      Returns the trailing name at the end of a string with "::"
220 *      namespace qualifiers. These qualifiers are namespace names
221 *      separated by "::"s. For example, for "::foo::p" this function
222 *      returns a pointer to the "p" in that obj string rep, and for
223 *      "::" it returns a pointer to "".
224 *
225 * Results:
226 *	Returns a pointer to the start of the tail name.
227 *
228 * Side effects:
229 *	None.
230 *
231 *----------------------------------------------------------------------
232 */
233static char*
234namespaceTail (nameObj)
235    Tcl_Obj *nameObj;
236{
237    char *name,*p;
238    int   len;
239
240    name = Tcl_GetStringFromObj(nameObj, &len);
241    p = name + len;
242    /* Isolate just the tail name, i.e. skip it's parent namespace */
243    while (--p > name) {
244        if ((*p == ':') && (*(p-1) == ':')) {
245            p++; /* just after the last "::" */
246            name = p;
247            break;
248        }
249    }
250    return name;
251}
252
253/*----------------------------------------------------------------------------
254|   NodeObjCmdDeleteProc
255|
256\---------------------------------------------------------------------------*/
257static void
258NodeObjCmdDeleteProc (
259    ClientData clientData
260    )
261{
262    NodeInfo *nodeInfo = (NodeInfo *) clientData;
263
264    if (nodeInfo->namespace) {
265        FREE (nodeInfo->namespace);
266    }
267    FREE (nodeInfo);
268}
269
270/*----------------------------------------------------------------------------
271|   NodeObjCmd
272|
273\---------------------------------------------------------------------------*/
274static int
275NodeObjCmd (arg, interp, objc, objv)
276    ClientData      arg;                /* Type of node to create. */
277    Tcl_Interp    * interp;             /* Current interpreter. */
278    int             objc;               /* Number of arguments. */
279    Tcl_Obj *CONST  objv[];             /* Argument objects. */
280{
281    int type, createType, len, dlen, i, ret, disableOutputEscaping = 0,
282        index = 1;
283    char *tag, *p, *tval, *aval;
284    domNode *parent, *newNode = NULL;
285    domDocument *doc;
286    Tcl_Obj *cmdObj, **opts;
287    NodeInfo *nodeInfo = (NodeInfo*) arg;
288
289    /*------------------------------------------------------------------------
290    |   Need parent node to get the owner document and to append new
291    |   child tag to it. The current parent node is stored on the stack.
292    |
293    \-----------------------------------------------------------------------*/
294
295    parent = (domNode *)StackTop();
296    if (parent == NULL) {
297        Tcl_AppendResult(interp, "called outside domNode context", NULL);
298        return TCL_ERROR;
299    }
300    doc = parent->ownerDocument;
301
302    /*------------------------------------------------------------------------
303    |   Create new node according to type. Special case is the ELEMENT_NODE
304    |   since here we may enter into recursion. The ELEMENT_NODE is the only
305    |   node type which may have script body as last argument.
306    |
307    \-----------------------------------------------------------------------*/
308
309    ret  = TCL_OK;
310    type = nodeInfo->type;
311
312    switch (abs(type)) {
313    case CDATA_SECTION_NODE:
314    case CDATA_SECTION_NODE_CHK:
315    case COMMENT_NODE:
316    case COMMENT_NODE_CHK:
317    case TEXT_NODE:
318    case TEXT_NODE_CHK:
319        if (objc != 2) {
320            if (abs(type) == TEXT_NODE || abs(type) == TEXT_NODE_CHK) {
321                if (objc != 3 ||
322                    strcmp ("-disableOutputEscaping",
323                            Tcl_GetStringFromObj (objv[1], &len))!=0) {
324                    Tcl_WrongNumArgs(interp, 1, objv,
325                                     "?-disableOutputEscaping? text");
326                    return TCL_ERROR;
327                } else {
328                    disableOutputEscaping = 1;
329                    index = 2;
330                }
331            } else {
332                Tcl_WrongNumArgs(interp, 1, objv, "text");
333                return TCL_ERROR;
334            }
335        }
336        tval = Tcl_GetStringFromObj(objv[index], &len);
337        switch (abs(type)) {
338        case TEXT_NODE_CHK:
339            if (!tcldom_textCheck (interp, tval, "text")) return TCL_ERROR;
340            createType = TEXT_NODE;
341            break;
342        case COMMENT_NODE_CHK:
343            if (!tcldom_commentCheck (interp, tval)) return TCL_ERROR;
344            createType = COMMENT_NODE;
345            break;
346        case CDATA_SECTION_NODE_CHK:
347            if (!tcldom_CDATACheck (interp, tval)) return TCL_ERROR;
348            createType = CDATA_SECTION_NODE;
349            break;
350        default:
351            createType = nodeInfo->type;
352            break;
353        }
354        newNode = (domNode*)domNewTextNode(doc, tval, len, createType);
355        if (disableOutputEscaping) {
356            newNode->nodeFlags |= DISABLE_OUTPUT_ESCAPING;
357        }
358        domAppendChild(parent, newNode);
359        break;
360
361    case PROCESSING_INSTRUCTION_NODE_NAME_CHK:
362    case PROCESSING_INSTRUCTION_NODE_VALUE_CHK:
363    case PROCESSING_INSTRUCTION_NODE_CHK:
364    case PROCESSING_INSTRUCTION_NODE:
365        if (objc != 3) {
366            Tcl_WrongNumArgs(interp, 1, objv, "target data");
367            return TCL_ERROR;
368        }
369        tval = Tcl_GetStringFromObj(objv[1], &len);
370        if (abs(type) == PROCESSING_INSTRUCTION_NODE_NAME_CHK
371            || abs(type) == PROCESSING_INSTRUCTION_NODE_CHK) {
372            if (!tcldom_PINameCheck (interp, tval)) return TCL_ERROR;
373        }
374        aval = Tcl_GetStringFromObj(objv[2], &dlen);
375        if (abs(type) == PROCESSING_INSTRUCTION_NODE_VALUE_CHK
376            || abs(type) == PROCESSING_INSTRUCTION_NODE_CHK) {
377            if (!tcldom_PIValueCheck (interp, aval)) return TCL_ERROR;
378        }
379        newNode = (domNode *)
380            domNewProcessingInstructionNode(doc, tval, len, aval, dlen);
381        domAppendChild(parent, newNode);
382        break;
383
384    case PARSER_NODE: /* non-standard node-type : a hack! */
385        if (objc != 2) {
386            Tcl_WrongNumArgs(interp, 1, objv, "markup");
387            return TCL_ERROR;
388        }
389        ret = tcldom_appendXML(interp, parent, objv[1]);
390        break;
391
392    case ELEMENT_NODE_ANAME_CHK:
393    case ELEMENT_NODE_AVALUE_CHK:
394    case ELEMENT_NODE_CHK:
395    case ELEMENT_NODE:
396        tag = Tcl_GetStringFromObj(objv[0], &len);
397        p = tag + len;
398        /* Isolate just the tag name, i.e. skip it's parent namespace */
399        while (--p > tag) {
400            if ((*p == ':') && (*(p-1) == ':')) {
401                p++; /* just after the last "::" */
402                tag = p;
403                break;
404            }
405        }
406
407        newNode = domAppendNewElementNode (parent, tag, NULL);
408
409        /*
410         * Allow for following syntax:
411         *   cmd ?-option value ...? ?script?
412         *   cmd ?opton value ...? ?script?
413         *   cmd key_value_list script
414         *       where list contains "-key value ..." or "key value ..."
415         */
416
417        if ((objc % 2) == 0) {
418            cmdObj = objv[objc-1];
419            len  = objc - 2; /* skip both command and script */
420            opts = (Tcl_Obj**)objv + 1;
421        } else if((objc == 3)
422                  && Tcl_ListObjGetElements(interp,objv[1],&len,&opts)==TCL_OK
423                  && (len == 0 || len > 1)) {
424            if ((len % 2)) {
425                Tcl_AppendResult(interp, "list must have "
426                                 "an even number of elements", NULL);
427                return TCL_ERROR;
428            }
429            cmdObj = objv[2];
430        } else {
431            cmdObj = NULL;
432            len  = objc - 1; /* skip command */
433            opts = (Tcl_Obj**)objv + 1;
434        }
435        for (i = 0; i < len; i += 2) {
436            tval = Tcl_GetString(opts[i]);
437            if (*tval == '-') {
438                tval++;
439            }
440            if (abs(type) == ELEMENT_NODE_ANAME_CHK
441                || abs(type) == ELEMENT_NODE_CHK) {
442                if (!tcldom_nameCheck (interp, tval, "attribute", 0)) {
443                    return TCL_ERROR;
444                }
445            }
446            aval = Tcl_GetString(opts[i+1]);
447            if (abs(type) == ELEMENT_NODE_AVALUE_CHK
448                || abs(type) == ELEMENT_NODE_CHK) {
449                if (!tcldom_textCheck (interp, aval, "attribute")) {
450                    return TCL_ERROR;
451                }
452            }
453            domSetAttribute(newNode, tval, aval);
454        }
455        if (cmdObj) {
456            ret = nodecmd_appendFromScript(interp, newNode, cmdObj);
457        }
458        break;
459    }
460
461    if (type < 0 && newNode != NULL) {
462        char buf[64];
463        tcldom_createNodeObj(interp, newNode, buf);
464        Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, strlen(buf)));
465    }
466
467    if (ret == TCL_OK) doc->nodeFlags |= NEEDS_RENUMBERING;
468    return ret;
469}
470
471/*----------------------------------------------------------------------------
472|   nodecmd_createNodeCmd  -  implements the "createNodeCmd" method of
473|                             "dom" Tcl command
474|
475|   This command is used to generate other Tcl commands which in turn
476|   generate tDOM nodes. These new commands can only be called within
477|   the context of the domNode command, however.
478|
479|   Syntax: dom createNodeCmd ?-returnNodeCmd? <nodeType> cmdName
480|
481|           where <nodeType> can be one of:
482|              elementNode, commentNode, textNode, cdataNode or piNode
483|
484|   The optional "-returnNodeCmd" parameter, if given, instructs the
485|   command to return the object-based node command of the newly generated
486|   node. Without the parameter, the command returns current interp result.
487|
488|   Example:
489|
490|      % dom createNodeCmd                elementNode html::body
491|      % dom createNodeCmd -returnNodeCmd elementNode html::title
492|      % dom createNodeCmd                textNode    html::t
493|
494|   And usage:
495|
496|      % set d [dom createDocument html]
497|      % set n [$d documentElement]
498|      % $n appendFromScript {
499|           html::body {
500|           set title [html::title {html::t "This is an example"}]
501|           $title setAttribute dummy 1
502|      }
503|      % puts [$n asHTML]
504|
505\---------------------------------------------------------------------------*/
506int
507nodecmd_createNodeCmd (interp, objc, objv, checkName, checkCharData)
508    Tcl_Interp    * interp;             /* Current interpreter. */
509    int             objc;               /* Number of arguments. */
510    Tcl_Obj *CONST  objv[];             /* Argument objects. */
511    int             checkName;          /* Flag: Name checks? */
512    int             checkCharData;      /* Flag: Data checks? */
513{
514    int ix, index, ret, type, nodecmd = 0;
515    char *nsName, buf[64];
516    Tcl_DString cmdName;
517    NodeInfo *nodeInfo;
518
519    /*
520     * Syntax:
521     *
522     *     dom createNodeCmd ?-returnNodeCmd? nodeType commandName
523     */
524
525    enum subCmd {
526        ELM_NODE, TXT_NODE, CDS_NODE, CMT_NODE, PIC_NODE, PRS_NODE
527    };
528
529    static CONST84 char *subcmds[] = {
530        "elementNode", "textNode", "cdataNode", "commentNode", "piNode",
531        "parserNode", NULL
532    };
533
534    if (objc != 3 && objc != 4) {
535        goto usage;
536    }
537    if (objc == 4) {
538        if (strcmp(Tcl_GetString(objv[1]), "-returnNodeCmd")) {
539            goto usage;
540        }
541        nodecmd = 1;
542        ix = 2;
543    } else {
544        nodecmd = 0;
545        ix = 1;
546    }
547    ret = Tcl_GetIndexFromObj(interp, objv[ix], subcmds, "option", 0, &index);
548    if (ret != TCL_OK) {
549        return ret;
550    }
551
552    /*--------------------------------------------------------------------
553    |   Construct fully qualified command name using current namespace
554    |
555    \-------------------------------------------------------------------*/
556    Tcl_DStringInit(&cmdName);
557    strcpy(buf, "namespace current");
558    ret = Tcl_Eval(interp, buf);
559    if (ret != TCL_OK) {
560        return ret;
561    }
562    nsName = (char *)Tcl_GetStringResult(interp);
563    Tcl_DStringAppend(&cmdName, nsName, -1);
564    if (strcmp(nsName, "::")) {
565        Tcl_DStringAppend(&cmdName, "::", 2);
566    }
567    Tcl_DStringAppend(&cmdName, Tcl_GetString(objv[ix+1]), -1);
568
569    nodeInfo = (NodeInfo *) MALLOC (sizeof (NodeInfo));
570    nodeInfo->namespace = NULL;
571    Tcl_ResetResult (interp);
572    switch ((enum subCmd)index) {
573    case ELM_NODE:
574        if (!tcldom_nameCheck(interp, namespaceTail(objv[ix+1]), "tag", 0)) {
575            FREE (nodeInfo);
576            return TCL_ERROR;
577        }
578        if (checkName && checkCharData) {
579            type = ELEMENT_NODE_CHK;
580        } else if (checkName) {
581            type = ELEMENT_NODE_ANAME_CHK;
582        } else if (checkCharData) {
583            type = ELEMENT_NODE_AVALUE_CHK;
584        } else {
585            type = ELEMENT_NODE;
586        }
587        break;
588    case PRS_NODE:
589        type = PARSER_NODE;
590        break;
591    case TXT_NODE:
592        if (checkCharData) {
593            type = TEXT_NODE_CHK;
594        } else {
595            type = TEXT_NODE;
596        }
597        break;
598    case CDS_NODE:
599        if (checkCharData) {
600            type = CDATA_SECTION_NODE_CHK;
601        } else {
602            type = CDATA_SECTION_NODE;
603        }
604        break;
605    case CMT_NODE:
606        if (checkCharData) {
607            type = COMMENT_NODE_CHK;
608        } else {
609            type = COMMENT_NODE;
610        }
611        break;
612    case PIC_NODE:
613        if (checkName && checkCharData) {
614            type = PROCESSING_INSTRUCTION_NODE_CHK;
615        } else if (checkName) {
616            type = PROCESSING_INSTRUCTION_NODE_NAME_CHK;
617        } else if (checkCharData) {
618            type = PROCESSING_INSTRUCTION_NODE_VALUE_CHK;
619        } else {
620            type = PROCESSING_INSTRUCTION_NODE;
621        }
622        break;
623    }
624
625    nodeInfo->type = type;
626    if (nodecmd) {
627        nodeInfo->type *= -1; /* Signal this fact */
628    }
629    Tcl_CreateObjCommand(interp, Tcl_DStringValue(&cmdName), NodeObjCmd,
630                         (ClientData)nodeInfo, NodeObjCmdDeleteProc);
631    Tcl_DStringResult(interp, &cmdName);
632    Tcl_DStringFree(&cmdName);
633
634    return TCL_OK;
635
636 usage:
637    Tcl_AppendResult(interp, "dom createNodeCmd ?-returnNodeCmd?"
638                     " nodeType cmdName", NULL);
639    return TCL_ERROR;
640}
641
642
643/*
644 *----------------------------------------------------------------------
645 *
646 * nodecmd_appendFromScript --
647 *
648 *	This procedure implements the dom method appendFromScript.
649 *      See the user documentation for details on what it does.
650 *
651 * Results:
652 *	A standard Tcl result.
653 *
654 * Side effects:
655 *	Appends new child nodes to node.
656 *
657 *----------------------------------------------------------------------
658 */
659
660int
661nodecmd_appendFromScript (interp, node, cmdObj)
662    Tcl_Interp *interp;                 /* Current interpreter. */
663    domNode    *node;                   /* Parent dom node */
664    Tcl_Obj    *cmdObj;                 /* Argument objects. */
665{
666    int ret;
667    domNode *oldLastChild, *child, *nextChild;
668
669    if (node->nodeType != ELEMENT_NODE) {
670        Tcl_SetResult (interp, "NOT_AN_ELEMENT : can't append nodes", NULL);
671        return TCL_ERROR;
672    }
673
674    oldLastChild = node->lastChild;
675
676    StackPush((void *)node);
677    Tcl_AllowExceptions(interp);
678    ret = Tcl_EvalObj(interp, cmdObj);
679    if (ret != TCL_ERROR) {
680        Tcl_ResetResult(interp);
681    }
682    StackPop();
683
684    if (ret == TCL_ERROR) {
685        if (oldLastChild) {
686            child = oldLastChild->nextSibling;
687        } else {
688            child = node->firstChild;
689        }
690        while (child) {
691            nextChild = child->nextSibling;
692            domFreeNode (child, NULL, NULL, 0);
693            child = nextChild;
694        }
695        if (oldLastChild) {
696            oldLastChild->nextSibling = NULL;
697            node->lastChild = oldLastChild;
698        } else {
699            node->firstChild = NULL;
700            node->lastChild = NULL;
701        }
702    }
703
704    return (ret == TCL_BREAK) ? TCL_OK : ret;
705}
706
707
708/*
709 *----------------------------------------------------------------------
710 *
711 * nodecmd_insertBeforeFromScript --
712 *
713 *	This procedure implements the dom method
714 *	insertBeforeFromScript. See the user documentation for details
715 *	on what it does.
716 *
717 *      This procedure is actually mostly a wrapper around
718 *      nodecmd_appendFromScript.
719 *
720 * Results:
721 *	A standard Tcl result.
722 *
723 * Side effects:
724 *	Insert new child nodes before referenceChild to node.
725 *
726 *----------------------------------------------------------------------
727 */
728
729int
730nodecmd_insertBeforeFromScript (interp, node, cmdObj, refChild)
731    Tcl_Interp *interp;                 /* Current interpreter. */
732    domNode    *node;                   /* Parent dom node */
733    Tcl_Obj    *cmdObj;                 /* Argument objects. */
734    domNode    *refChild;               /* Insert new childs before this
735                                         * node; may be NULL */
736{
737    int      ret;
738    domNode *storedLastChild, *n;
739
740    if (!refChild) {
741        return nodecmd_appendFromScript (interp, node, cmdObj);
742    }
743
744    if (node->nodeType != ELEMENT_NODE) {
745        Tcl_SetResult (interp, "NOT_AN_ELEMENT : can't append nodes", NULL);
746        return TCL_ERROR;
747    }
748
749    /* check, if node is in deed the parent of refChild */
750    if (refChild->parentNode != node) {
751        /* If node is the root node of a document and refChild
752           is in deed a child of this node, then
753           refChild->parentNode will be NULL. In this case, we
754           loop throu the childs of node, to see, if the refChild
755           is valid. */
756        Tcl_ResetResult (interp);
757        if (node->ownerDocument->rootNode == node) {
758            n = node->firstChild;
759            while (n) {
760                if (n == refChild) {
761                    /* refChild is in deed a child of node */
762                    break;
763                }
764                n = n->nextSibling;
765            }
766            if (!n) {
767                Tcl_SetStringObj(Tcl_GetObjResult(interp), "NOT_FOUND_ERR",
768                                 -1);
769                return TCL_ERROR;
770            }
771        } else {
772            Tcl_SetStringObj(Tcl_GetObjResult(interp), "NOT_FOUND_ERR", -1);
773            return TCL_ERROR;
774        }
775    }
776
777    storedLastChild = node->lastChild;
778    if (refChild->previousSibling) {
779        refChild->previousSibling->nextSibling = NULL;
780        node->lastChild = refChild->previousSibling;
781    } else {
782        node->firstChild = NULL;
783        node->lastChild = NULL;
784    }
785    ret = nodecmd_appendFromScript (interp, node, cmdObj);
786    if (node->lastChild) {
787        node->lastChild->nextSibling = refChild;
788        refChild->previousSibling = node->lastChild;
789    } else {
790        node->firstChild = refChild;
791    }
792    node->lastChild = storedLastChild;
793
794    return ret;
795}
796
797
798/*----------------------------------------------------------------------------
799|   nodecmd_curentNode
800|
801\---------------------------------------------------------------------------*/
802
803void *
804nodecmd_currentNode(void)
805{
806    return StackTop();
807}
808
809/* EOF $RCSfile $ */
810
811/* Emacs Setup Variables */
812/* Local Variables:      */
813/* mode: C               */
814/* indent-tabs-mode: nil */
815/* c-basic-offset: 4     */
816/* End:                  */
817
818