/*----------------------------------------------------------------------------
| Copyright (C) 1999 Jochen C. Loewer (loewerj@hotmail.com)
+-----------------------------------------------------------------------------
|
| Rcsid: @(#)$Id$
|
| The contents of this file are subject to the Mozilla Public License
| Version 2.0 (the "License"); you may not use this file except in
| compliance with the License. You may obtain a copy of the License at
| http://www.mozilla.org/MPL/
|
| Software distributed under the License is distributed on an "AS IS"
| basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
| License for the specific language governing rights and limitations
| under the License.
|
| The Original Code is tDOM.
|
| The Initial Developer of the Original Code is Jochen Loewer.
|
| Portions created by Jochen Loewer are Copyright (C) 1998, 1999
| Jochen Loewer. All Rights Reserved.
|
| Portions created by Zoran Vasiljevic are Copyright (C) 2000-2002
| Zoran Vasiljevic. All Rights Reserved.
|
| Portions created by Rolf Ade are Copyright (C) 1999-2002
| Rolf Ade. All Rights Reserved.
|
| Written by Zoran Vasiljevic
| July 12, 2000
|
\---------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
| Includes
|
\---------------------------------------------------------------------------*/
#include <dom.h>
#include <tcldom.h>
#include <tcl.h>
#include <nodecmd.h>
#define PARSER_NODE 9999 /* Hack so that we can invoke XML parser */
/* More hacked domNodeTypes - used to signal, that we want to check
name/data of the node to create. */
#define ELEMENT_NODE_ANAME_CHK 10000
#define ELEMENT_NODE_AVALUE_CHK 10001
#define ELEMENT_NODE_CHK 10002
#define TEXT_NODE_CHK 10003
#define COMMENT_NODE_CHK 10004
#define CDATA_SECTION_NODE_CHK 10005
#define PROCESSING_INSTRUCTION_NODE_NAME_CHK 10006
#define PROCESSING_INSTRUCTION_NODE_VALUE_CHK 10007
#define PROCESSING_INSTRUCTION_NODE_CHK 10008
/*----------------------------------------------------------------------------
| Types
|
| This structure represents one stack slot. The stack itself
| is implemented as double-linked-list of following structures.
|
\---------------------------------------------------------------------------*/
typedef struct StackSlot {
void *element; /* The stacked element */
struct StackSlot *nextPtr; /* Next link */
struct StackSlot *prevPtr; /* Previous link */
} StackSlot;
/*----------------------------------------------------------------------------
| Beginning of the stack and current element pointer are local
| to current thread and also local to this file.
| For non-threaded environments, it's a regular static.
|
\---------------------------------------------------------------------------*/
typedef struct CurrentStack {
StackSlot *elementStack;
StackSlot *currentSlot;
} CurrentStack;
/*----------------------------------------------------------------------------
| Structure used as clientData of the created commands.
| The structure stores, which type of node the command has
| to create and, in case of elementNodes and if given, the
| namespace of the node.
\---------------------------------------------------------------------------*/
typedef struct NodeInfo {
int type;
char *namespace;
int jsonType;
char *tagName;
} NodeInfo;
#ifndef TCL_THREADS
static CurrentStack dataKey;
# define TSDPTR(a) a
#else
static Tcl_ThreadDataKey dataKey;
# define TSDPTR(a) (CurrentStack*)Tcl_GetThreadData((a),sizeof(CurrentStack))
#endif
/*----------------------------------------------------------------------------
| Forward declarations
|
\---------------------------------------------------------------------------*/
static void * StackPush (void *);
static void * StackPop (void);
static void * StackTop (void);
static int NodeObjCmd (ClientData,Tcl_Interp*,int,Tcl_Obj *const o[]);
static void StackFinalize (ClientData);
extern int tcldom_appendXML (Tcl_Interp*, domNode*, Tcl_Obj*);
/*----------------------------------------------------------------------------
| StackPush
|
\---------------------------------------------------------------------------*/
static void *
StackPush (
void *element
) {
StackSlot *newElement;
CurrentStack *tsdPtr = TSDPTR(&dataKey);
/*-------------------------------------------------------------------
| Reuse already allocated stack slots, if any
|
\------------------------------------------------------------------*/
if (tsdPtr->currentSlot && tsdPtr->currentSlot->nextPtr) {
tsdPtr->currentSlot = tsdPtr->currentSlot->nextPtr;
tsdPtr->currentSlot->element = element;
return element;
}
/*-------------------------------------------------------------------
| Allocate new stack slot
|
\------------------------------------------------------------------*/
newElement = (StackSlot *)MALLOC(sizeof(StackSlot));
memset(newElement, 0, sizeof(StackSlot));
if (tsdPtr->elementStack == NULL) {
tsdPtr->elementStack = newElement;
#ifdef TCL_THREADS
Tcl_CreateThreadExitHandler(StackFinalize, tsdPtr->elementStack);
#else
Tcl_CreateExitHandler (StackFinalize, tsdPtr->elementStack);
#endif
} else {
tsdPtr->currentSlot->nextPtr = newElement;
newElement->prevPtr = tsdPtr->currentSlot;
}
tsdPtr->currentSlot = newElement;
tsdPtr->currentSlot->element = element;
return element;
}
/*----------------------------------------------------------------------------
| StackPop - pops the element from stack
|
\---------------------------------------------------------------------------*/
static void *
StackPop (void)
{
void *element;
CurrentStack *tsdPtr = TSDPTR(&dataKey);
element = tsdPtr->currentSlot->element;
if (tsdPtr->currentSlot->prevPtr) {
tsdPtr->currentSlot = tsdPtr->currentSlot->prevPtr;
} else {
tsdPtr->currentSlot->element = NULL;
}
return element;
}
/*----------------------------------------------------------------------------
| StackTop - returns top-level element from stack
|
\---------------------------------------------------------------------------*/
static void *
StackTop (void)
{
CurrentStack *tsdPtr = TSDPTR(&dataKey);
if (tsdPtr->currentSlot == NULL) {
return NULL;
}
return tsdPtr->currentSlot->element;
}
/*----------------------------------------------------------------------------
| StackFinalize - reclaims stack memory (slots only, not elements)
|
\---------------------------------------------------------------------------*/
static void
StackFinalize (
ClientData clientData
) {
StackSlot *tmp, *stack = (StackSlot *)clientData;
while (stack) {
tmp = stack->nextPtr;
FREE((char*)stack);
stack = tmp;
}
}
/*
*----------------------------------------------------------------------
*
* namespaceTail --
*
* Returns the trailing name at the end of a string with "::"
* namespace qualifiers. These qualifiers are namespace names
* separated by "::"s. For example, for "::foo::p" this function
* returns a pointer to the "p" in that obj string rep, and for
* "::" it returns a pointer to "".
*
* Results:
* Returns a pointer to the start of the tail name.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static char*
namespaceTail (
Tcl_Obj *nameObj
)
{
char *name,*p;
int len;
name = Tcl_GetStringFromObj(nameObj, &len);
p = name + len;
/* Isolate just the tail name, i.e. skip it's parent namespace */
while (--p > name) {
if ((*p == ':') && (*(p-1) == ':')) {
p++; /* just after the last "::" */
name = p;
break;
}
}
return name;
}
/*----------------------------------------------------------------------------
| NodeObjCmdDeleteProc
|
\---------------------------------------------------------------------------*/
static void
NodeObjCmdDeleteProc (
ClientData clientData
)
{
NodeInfo *nodeInfo = (NodeInfo *) clientData;
if (nodeInfo->namespace) {
FREE (nodeInfo->namespace);
}
if (nodeInfo->tagName) {
FREE (nodeInfo->tagName);
}
FREE (nodeInfo);
}
/*----------------------------------------------------------------------------
| NodeObjCmd
|
\---------------------------------------------------------------------------*/
static int
NodeObjCmd (
ClientData arg, /* Type of node to create. */
Tcl_Interp * interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[] /* Argument objects. */
) {
int type, createType, len, dlen, i, ret, disableOutputEscaping = 0,
index = 1;
char *tag, *p, *tval, *aval;
domNode *parent, *newNode = NULL;
domTextNode *textNode = NULL;
domDocument *doc;
Tcl_Obj *cmdObj, **opts;
NodeInfo *nodeInfo = (NodeInfo*) arg;
/*------------------------------------------------------------------------
| Need parent node to get the owner document and to append new
| child tag to it. The current parent node is stored on the stack.
|
\-----------------------------------------------------------------------*/
parent = (domNode *)StackTop();
if (parent == NULL) {
Tcl_AppendResult(interp, "called outside domNode context", NULL);
return TCL_ERROR;
}
doc = parent->ownerDocument;
/*------------------------------------------------------------------------
| Create new node according to type. Special case is the ELEMENT_NODE
| since here we may enter into recursion. The ELEMENT_NODE is the only
| node type which may have script body as last argument.
|
\-----------------------------------------------------------------------*/
ret = TCL_OK;
type = nodeInfo->type;
switch (abs(type)) {
case CDATA_SECTION_NODE:
case CDATA_SECTION_NODE_CHK:
case COMMENT_NODE:
case COMMENT_NODE_CHK:
case TEXT_NODE:
case TEXT_NODE_CHK:
if (objc != 2) {
if (abs(type) == TEXT_NODE || abs(type) == TEXT_NODE_CHK) {
if (objc != 3 ||
strcmp ("-disableOutputEscaping",
Tcl_GetStringFromObj (objv[1], &len))!=0) {
Tcl_WrongNumArgs(interp, 1, objv,
"?-disableOutputEscaping? text");
return TCL_ERROR;
} else {
disableOutputEscaping = 1;
index = 2;
}
} else {
Tcl_WrongNumArgs(interp, 1, objv, "text");
return TCL_ERROR;
}
}
tval = Tcl_GetStringFromObj(objv[index], &len);
switch (abs(type)) {
case TEXT_NODE_CHK:
if (!tcldom_textCheck (interp, tval, "text")) return TCL_ERROR;
createType = TEXT_NODE;
break;
case COMMENT_NODE_CHK:
if (!tcldom_commentCheck (interp, tval)) return TCL_ERROR;
createType = COMMENT_NODE;
break;
case CDATA_SECTION_NODE_CHK:
if (!tcldom_CDATACheck (interp, tval)) return TCL_ERROR;
createType = CDATA_SECTION_NODE;
break;
default:
createType = nodeInfo->type;
break;
}
textNode = domNewTextNode(doc, tval, len, createType);
textNode->info = nodeInfo->jsonType;
if (disableOutputEscaping) {
textNode->nodeFlags |= DISABLE_OUTPUT_ESCAPING;
}
domAppendChild(parent, (domNode*) textNode);
break;
case PROCESSING_INSTRUCTION_NODE_NAME_CHK:
case PROCESSING_INSTRUCTION_NODE_VALUE_CHK:
case PROCESSING_INSTRUCTION_NODE_CHK:
case PROCESSING_INSTRUCTION_NODE:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "target data");
return TCL_ERROR;
}
tval = Tcl_GetStringFromObj(objv[1], &len);
if (abs(type) == PROCESSING_INSTRUCTION_NODE_NAME_CHK
|| abs(type) == PROCESSING_INSTRUCTION_NODE_CHK) {
if (!tcldom_PINameCheck (interp, tval)) return TCL_ERROR;
}
aval = Tcl_GetStringFromObj(objv[2], &dlen);
if (abs(type) == PROCESSING_INSTRUCTION_NODE_VALUE_CHK
|| abs(type) == PROCESSING_INSTRUCTION_NODE_CHK) {
if (!tcldom_PIValueCheck (interp, aval)) return TCL_ERROR;
}
newNode = (domNode *)
domNewProcessingInstructionNode(doc, tval, len, aval, dlen);
domAppendChild(parent, newNode);
break;
case PARSER_NODE: /* non-standard node-type : a hack! */
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "markup");
return TCL_ERROR;
}
ret = tcldom_appendXML(interp, parent, objv[1]);
break;
case ELEMENT_NODE_ANAME_CHK:
case ELEMENT_NODE_AVALUE_CHK:
case ELEMENT_NODE_CHK:
case ELEMENT_NODE:
if (!nodeInfo->tagName) {
tag = Tcl_GetStringFromObj(objv[0], &len);
p = tag + len;
/* Isolate just the tag name, i.e. skip it's parent namespace */
while (--p > tag) {
if ((*p == ':') && (*(p-1) == ':')) {
p++; /* just after the last "::" */
tag = p;
break;
}
}
} else {
tag = nodeInfo->tagName;
}
newNode = domAppendNewElementNode (parent, tag, nodeInfo->namespace);
newNode->info = nodeInfo->jsonType;
/*
* Allow for following syntax:
* cmd ?-option value ...? ?script?
* cmd ?opton value ...? ?script?
* cmd key_value_list script
* where list contains "-key value ..." or "key value ..."
*/
if ((objc % 2) == 0) {
cmdObj = objv[objc-1];
len = objc - 2; /* skip both command and script */
opts = (Tcl_Obj**)objv + 1;
} else if((objc == 3)
&& Tcl_ListObjGetElements(interp,objv[1],&len,&opts)==TCL_OK
&& (len == 0 || len > 1)) {
if ((len % 2)) {
Tcl_AppendResult(interp, "list must have "
"an even number of elements", NULL);
return TCL_ERROR;
}
cmdObj = objv[2];
} else {
cmdObj = NULL;
len = objc - 1; /* skip command */
opts = (Tcl_Obj**)objv + 1;
}
for (i = 0; i < len; i += 2) {
tval = Tcl_GetString(opts[i]);
if (*tval == '-') {
tval++;
}
if (abs(type) == ELEMENT_NODE_ANAME_CHK
|| abs(type) == ELEMENT_NODE_CHK) {
if (!tcldom_nameCheck (interp, tval, "attribute", 0)) {
return TCL_ERROR;
}
}
aval = Tcl_GetString(opts[i+1]);
if (abs(type) == ELEMENT_NODE_AVALUE_CHK
|| abs(type) == ELEMENT_NODE_CHK) {
if (!tcldom_textCheck (interp, aval, "attribute")) {
return TCL_ERROR;
}
}
domSetAttribute(newNode, tval, aval);
}
if (cmdObj) {
ret = nodecmd_appendFromScript(interp, newNode, cmdObj);
}
break;
}
if (type < 0 && newNode != NULL) {
char buf[64];
tcldom_createNodeObj(interp, newNode, buf);
Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, strlen(buf)));
}
if (ret == TCL_OK) doc->nodeFlags |= NEEDS_RENUMBERING;
return ret;
}
/*----------------------------------------------------------------------------
| nodecmd_createNodeCmd - implements the "createNodeCmd" method of
| "dom" Tcl command
|
| This command is used to generate other Tcl commands which in turn
| generate tDOM nodes. These new commands can only be called within
| the context of the domNode command, however.
|
| Syntax: dom createNodeCmd ?-returnNodeCmd? <nodeType> cmdName
|
| where <nodeType> can be one of:
| elementNode, commentNode, textNode, cdataNode or piNode
|
| The optional "-returnNodeCmd" parameter, if given, instructs the
| command to return the object-based node command of the newly generated
| node. Without the parameter, the command returns current interp result.
|
| Example:
|
| % dom createNodeCmd elementNode html::body
| % dom createNodeCmd -returnNodeCmd elementNode html::title
| % dom createNodeCmd textNode html::t
|
| And usage:
|
| % set d [dom createDocument html]
| % set n [$d documentElement]
| % $n appendFromScript {
| html::body {
| set title [html::title {html::t "This is an example"}]
| $title setAttribute dummy 1
| }
| % puts [$n asHTML]
|
\---------------------------------------------------------------------------*/
int
nodecmd_createNodeCmd (
Tcl_Interp * interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[], /* Argument objects. */
int checkName, /* Flag: Name checks? */
int checkCharData /* Flag: Data checks? */
) {
int index, ret, type, nodecmd = 0, jsonType = 0, haveJsonType = 0;
int isElement = 0;
char *nsName, buf[64];
Tcl_Obj *tagName = NULL, *namespace = NULL;
Tcl_DString cmdName;
NodeInfo *nodeInfo;
/*
* Syntax:
*
* dom createNodeCmd ?-returnNodeCmd? nodeType commandName
*/
enum subCmd {
ELM_NODE, TXT_NODE, CDS_NODE, CMT_NODE, PIC_NODE, PRS_NODE
};
static const char *subcmds[] = {
"elementNode", "textNode", "cdataNode", "commentNode", "piNode",
"parserNode", NULL
};
static const char *options[] = {
"-returnNodeCmd", "-jsonType", "-tagName", "-namespace", NULL
};
enum option {
o_returnNodeCmd, o_jsonType, o_tagName, o_namespace
};
static const char *jsonTypes[] = {
"NONE",
"ARRAY",
"OBJECT",
"NULL",
"TRUE",
"FALSE",
"STRING",
"NUMBER"
};
if (objc < 3 ) {
goto usage;
}
while (objc > 3) {
if (Tcl_GetIndexFromObj (interp, objv[1], options, "option",
0, &index) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum option) index) {
case o_returnNodeCmd:
nodecmd = 1;
objc--;
objv++;
break;
case o_jsonType:
if (Tcl_GetIndexFromObj (interp, objv[2], jsonTypes, "jsonType",
1, &jsonType) != TCL_OK) {
return TCL_ERROR;
}
haveJsonType = 1;
objc -= 2;
objv += 2;
break;
case o_tagName:
tagName = objv[2];
objc -= 2;
objv += 2;
break;
case o_namespace:
namespace = objv[2];
objc -= 2;
objv += 2;
break;
}
}
if (objc != 3) {
goto usage;
}
ret = Tcl_GetIndexFromObj(interp, objv[1], subcmds, "nodeType", 0, &index);
if (ret != TCL_OK) {
return ret;
}
/*--------------------------------------------------------------------
| Construct fully qualified command name using current namespace
|
\-------------------------------------------------------------------*/
Tcl_DStringInit(&cmdName);
strcpy(buf, "namespace current");
ret = Tcl_Eval(interp, buf);
if (ret != TCL_OK) {
return ret;
}
nsName = (char *)Tcl_GetStringResult(interp);
Tcl_DStringAppend(&cmdName, nsName, -1);
if (strcmp(nsName, "::")) {
Tcl_DStringAppend(&cmdName, "::", 2);
}
Tcl_DStringAppend(&cmdName, Tcl_GetString(objv[2]), -1);
Tcl_ResetResult (interp);
switch ((enum subCmd)index) {
case ELM_NODE:
isElement = 1;
if (!haveJsonType) {
if (!tcldom_nameCheck(interp, namespaceTail(objv[2]),
"tag", 0)) {
return TCL_ERROR;
}
if (checkName && checkCharData) {
type = ELEMENT_NODE_CHK;
} else if (checkName) {
type = ELEMENT_NODE_ANAME_CHK;
} else if (checkCharData) {
type = ELEMENT_NODE_AVALUE_CHK;
} else {
type = ELEMENT_NODE;
}
} else {
if (jsonType > 2) {
Tcl_SetResult(interp, "For an element node the jsonType"
" argument must be one out of this list: ARRAY"
" OBJECT NONE.", NULL);
return TCL_ERROR;
}
type = ELEMENT_NODE;
}
break;
case PRS_NODE:
type = PARSER_NODE;
break;
case TXT_NODE:
if (!haveJsonType) {
if (checkCharData) {
type = TEXT_NODE_CHK;
} else {
type = TEXT_NODE;
}
} else {
if (jsonType < 3 && jsonType > 0) {
Tcl_SetResult(interp, "For a text node the jsonType "
"argument must be one out of this list: "
"TRUE FALSE NULL NUMBER STRING NONE",
NULL);
return TCL_ERROR;
}
type = TEXT_NODE;
}
break;
case CDS_NODE:
if (checkCharData) {
type = CDATA_SECTION_NODE_CHK;
} else {
type = CDATA_SECTION_NODE;
}
break;
case CMT_NODE:
if (checkCharData) {
type = COMMENT_NODE_CHK;
} else {
type = COMMENT_NODE;
}
break;
case PIC_NODE:
if (checkName && checkCharData) {
type = PROCESSING_INSTRUCTION_NODE_CHK;
} else if (checkName) {
type = PROCESSING_INSTRUCTION_NODE_NAME_CHK;
} else if (checkCharData) {
type = PROCESSING_INSTRUCTION_NODE_VALUE_CHK;
} else {
type = PROCESSING_INSTRUCTION_NODE;
}
break;
default:
Tcl_SetResult (interp, "Invalid/unexpected node type", NULL);
return TCL_ERROR;
}
if (tagName && !isElement) {
Tcl_SetResult(interp, "The -tagName option is allowed only for "
"element node commands.", NULL);
return TCL_ERROR;
}
if (namespace && !isElement) {
Tcl_SetResult(interp, "The -namespace option is allowed only for "
"element node commands.", NULL);
return TCL_ERROR;
}
if (haveJsonType && type != ELEMENT_NODE && type != TEXT_NODE) {
Tcl_SetResult(interp, "Only element and text nodes may have a "
"JSON type.", NULL);
return TCL_ERROR;
}
nodeInfo = (NodeInfo *) MALLOC (sizeof (NodeInfo));
nodeInfo->namespace = NULL;
nodeInfo->type = type;
if (nodecmd) {
nodeInfo->type *= -1; /* Signal this fact */
}
nodeInfo->jsonType = jsonType;
nodeInfo->tagName = NULL;
if (namespace) {
nodeInfo->namespace = tdomstrdup (Tcl_GetString(namespace));
}
if (tagName) {
nodeInfo->tagName = tdomstrdup (Tcl_GetString(tagName));
}
Tcl_CreateObjCommand(interp, Tcl_DStringValue(&cmdName), NodeObjCmd,
(ClientData)nodeInfo, NodeObjCmdDeleteProc);
Tcl_DStringResult(interp, &cmdName);
Tcl_DStringFree(&cmdName);
return TCL_OK;
usage:
Tcl_AppendResult(interp, "dom createNodeCmd\n"
"\t?-returnNodeCmd?\n"
"\t?-jsonType <jsonType>?\n"
"\t?-tagName <tagName>?\n"
" nodeType cmdName", NULL);
return TCL_ERROR;
}
/*
*----------------------------------------------------------------------
*
* nodecmd_appendFromScript --
*
* This procedure implements the dom method appendFromScript.
* See the user documentation for details on what it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* Appends new child nodes to node.
*
*----------------------------------------------------------------------
*/
int
nodecmd_appendFromScript (
Tcl_Interp *interp, /* Current interpreter. */
domNode *node, /* Parent dom node */
Tcl_Obj *cmdObj /* Argument objects. */
) {
int ret;
domNode *oldLastChild, *child, *nextChild;
if (node->nodeType != ELEMENT_NODE) {
Tcl_SetResult (interp, "NOT_AN_ELEMENT : can't append nodes", NULL);
return TCL_ERROR;
}
oldLastChild = node->lastChild;
StackPush((void *)node);
Tcl_AllowExceptions(interp);
ret = Tcl_EvalObjEx(interp, cmdObj, 0);
if (ret != TCL_ERROR) {
Tcl_ResetResult(interp);
}
StackPop();
if (ret == TCL_ERROR) {
if (oldLastChild) {
child = oldLastChild->nextSibling;
} else {
child = node->firstChild;
}
while (child) {
nextChild = child->nextSibling;
domFreeNode (child, NULL, NULL, 0);
child = nextChild;
}
if (oldLastChild) {
oldLastChild->nextSibling = NULL;
node->lastChild = oldLastChild;
} else {
node->firstChild = NULL;
node->lastChild = NULL;
}
}
return (ret == TCL_BREAK) ? TCL_OK : ret;
}
/*
*----------------------------------------------------------------------
*
* nodecmd_insertBeforeFromScript --
*
* This procedure implements the dom method
* insertBeforeFromScript. See the user documentation for details
* on what it does.
*
* This procedure is actually mostly a wrapper around
* nodecmd_appendFromScript.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* Insert new child nodes before referenceChild to node.
*
*----------------------------------------------------------------------
*/
int
nodecmd_insertBeforeFromScript (
Tcl_Interp *interp, /* Current interpreter. */
domNode *node, /* Parent dom node */
Tcl_Obj *cmdObj, /* Argument objects. */
domNode *refChild /* Insert new childs before this
* node; may be NULL */
) {
int ret;
domNode *storedLastChild, *n;
if (!refChild) {
return nodecmd_appendFromScript (interp, node, cmdObj);
}
if (node->nodeType != ELEMENT_NODE) {
Tcl_SetResult (interp, "NOT_AN_ELEMENT : can't append nodes", NULL);
return TCL_ERROR;
}
/* check, if node is in deed the parent of refChild */
if (refChild->parentNode != node) {
/* If node is the root node of a document and refChild
is in deed a child of this node, then
refChild->parentNode will be NULL. In this case, we
loop throu the childs of node, to see, if the refChild
is valid. */
Tcl_ResetResult (interp);
if (node->ownerDocument->rootNode == node) {
n = node->firstChild;
while (n) {
if (n == refChild) {
/* refChild is in deed a child of node */
break;
}
n = n->nextSibling;
}
if (!n) {
Tcl_SetStringObj(Tcl_GetObjResult(interp), "NOT_FOUND_ERR",
-1);
return TCL_ERROR;
}
} else {
Tcl_SetStringObj(Tcl_GetObjResult(interp), "NOT_FOUND_ERR", -1);
return TCL_ERROR;
}
}
storedLastChild = node->lastChild;
if (refChild->previousSibling) {
refChild->previousSibling->nextSibling = NULL;
node->lastChild = refChild->previousSibling;
} else {
node->firstChild = NULL;
node->lastChild = NULL;
}
ret = nodecmd_appendFromScript (interp, node, cmdObj);
if (node->lastChild) {
node->lastChild->nextSibling = refChild;
refChild->previousSibling = node->lastChild;
} else {
node->firstChild = refChild;
}
node->lastChild = storedLastChild;
return ret;
}
/*----------------------------------------------------------------------------
| nodecmd_curentNode
|
\---------------------------------------------------------------------------*/
void *
nodecmd_currentNode(void)
{
return StackTop();
}
/* EOF $RCSfile $ */
/* Emacs Setup Variables */
/* Local Variables: */
/* mode: C */
/* indent-tabs-mode: nil */
/* c-basic-offset: 4 */
/* End: */