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