Index: generic/dom.h ================================================================== --- generic/dom.h +++ generic/dom.h @@ -102,10 +102,14 @@ # define Tcl_Panic panic # define Tcl_GetString(a) Tcl_GetStringFromObj((a), NULL) #endif #define domPanic(msg) Tcl_Panic((msg)); + +#ifndef STATIC_STACK_SIZE +# define STATIC_STACK_SIZE 32 +#endif /* * If compiled against threaded Tcl core, we must take * some extra care about process-wide globals and the * way we name Tcl object accessor commands. Index: generic/tcldom.c ================================================================== --- generic/tcldom.c +++ generic/tcldom.c @@ -394,10 +394,15 @@ NULL, NULL, UpdateStringOfTdomNode, SetTdomNodeFromAny }; + +typedef struct { + domNode *node; + int cdata; +} asXMLStack; /*---------------------------------------------------------------------------- | Prototypes for procedures defined later in this file: | \---------------------------------------------------------------------------*/ @@ -2756,34 +2761,34 @@ writeChars(htmlString, chan, tag, -1); writeChars(htmlString, chan, ">", 1); } } - /*---------------------------------------------------------------------------- | tcldom_treeAsXML | \---------------------------------------------------------------------------*/ static void tcldom_treeAsXML ( Tcl_Obj *xmlString, domNode *node, int indent, - int level, - int doIndent, Tcl_Channel chan, Tcl_Obj *encString, - int cdataChild, int outputFlags, int indentAttrs ) { domAttrNode *attrs; - domNode *child; + asXMLStack staticStack[STATIC_STACK_SIZE]; + asXMLStack *dynStack = NULL; + int dynStackSize = 0; domDocument *doc; - int first, hasElements, i; - char prefix[MAX_PREFIX_LEN], *start, *p; + int isDoc = 0; + int level = 0, cdataChild = 0; + int i, indBufCount, attindBufCount; + char prefix[MAX_PREFIX_LEN], *start, *p, *indBuf, *attindBuf; const char *localName; Tcl_HashEntry *h; Tcl_DString dStr; if (outputFlags & SERIALIZE_XML_DECLARATION) { @@ -2803,10 +2808,11 @@ writeChars(xmlString, chan, "\"", 1); } writeChars(xmlString, chan, "?>\n", 3); } if (node->nodeType == DOCUMENT_NODE) { + isDoc = 1; doc = (domDocument*) node; if (outputFlags & SERIALIZE_DOCTYPE_DECLARATION && doc->documentElement) { writeChars(xmlString, chan, "documentElement->nodeName, -1); @@ -2832,227 +2838,279 @@ writeChars(xmlString, chan, "]", 1); } } writeChars(xmlString, chan, ">\n", 2); } - child = doc->rootNode->firstChild; - while (child) { - tcldom_treeAsXML(xmlString, child, indent, level, doIndent, chan, - NULL, 0, outputFlags, indentAttrs); - child = child->nextSibling; - } - return; - } - - if (node->nodeType == TEXT_NODE) { - if (cdataChild) { - writeChars(xmlString, chan, "nodeValue; - while (i < ((domTextNode*)node)->valueLength) { - if (*p == ']') { - p++; i++;; - if (i >= ((domTextNode*)node)->valueLength) break; - if (*p == ']') { - p++; i++;; - if (i >= ((domTextNode*)node)->valueLength) break; - if (*p == '>') { - writeChars(xmlString, chan, start, p-start); - writeChars(xmlString, chan, "]]>", 13); - start = p+1; - } - } - } - p++; i++;; - } - writeChars(xmlString, chan, start, p-start); - writeChars(xmlString, chan, "]]>", 3); - } else { - if (node->nodeFlags & DISABLE_OUTPUT_ESCAPING) { - writeChars(xmlString, chan, ((domTextNode*)node)->nodeValue, - ((domTextNode*)node)->valueLength); - } else { - tcldom_AppendEscaped(xmlString, chan, - ((domTextNode*)node)->nodeValue, - ((domTextNode*)node)->valueLength, - outputFlags); - } - } - return; - } - - if (node->nodeType == CDATA_SECTION_NODE) { - writeChars(xmlString, chan, "nodeValue, - ((domTextNode*)node)->valueLength); - writeChars(xmlString, chan, "]]>", 3); - return; - } - - if ((indent != -1) && doIndent) { - if (outputFlags & SERIALIZE_INDENT_WITH_TAB) { - for(i=0; inodeType == COMMENT_NODE) { - writeChars(xmlString, chan, "", 3); - if (indent != -1) writeChars (xmlString, chan, "\n", 1); - return; - } - - if (node->nodeType == PROCESSING_INSTRUCTION_NODE) { - writeChars(xmlString, chan, "targetValue, - ((domProcessingInstructionNode*)node)->targetLength); - writeChars(xmlString, chan, " ", 1); - writeChars(xmlString, chan, - ((domProcessingInstructionNode*)node)->dataValue, - ((domProcessingInstructionNode*)node)->dataLength); - writeChars(xmlString, chan, "?>", 2); - if (indent != -1) writeChars (xmlString, chan, "\n", 1); - return; - } - - writeChars(xmlString, chan, "<", 1); - writeChars(xmlString, chan, node->nodeName, -1); - - attrs = node->firstAttr; - while (attrs) { - if (indentAttrs > -1) { - writeChars(xmlString, chan, "\n", 1); - if ((indent != -1) && doIndent) { - if (outputFlags & SERIALIZE_INDENT_WITH_TAB) { - for(i=0; inodeName, -1); - writeChars(xmlString, chan, "=\"", 2); - tcldom_AppendEscaped(xmlString, chan, attrs->nodeValue, - attrs->valueLength, - outputFlags | SERIALIZE_FOR_ATTR); - writeChars(xmlString, chan, "\"", 1); - attrs = attrs->nextSibling; - } - - hasElements = 0; - first = 1; - doIndent = 1; - - if (node->nodeType == ELEMENT_NODE) { - cdataChild = 0; - if (node->ownerDocument->doctype - && node->ownerDocument->doctype->cdataSectionElements) { - if (node->namespace) { - Tcl_DStringInit (&dStr); - Tcl_DStringAppend (&dStr, domNamespaceURI(node), -1); - Tcl_DStringAppend (&dStr, ":", 1); - domSplitQName (node->nodeName, prefix, &localName); - Tcl_DStringAppend (&dStr, localName, -1); - h = Tcl_FindHashEntry ( - node->ownerDocument->doctype->cdataSectionElements, - Tcl_DStringValue (&dStr)); - Tcl_DStringFree (&dStr); - } else { - h = Tcl_FindHashEntry ( - node->ownerDocument->doctype->cdataSectionElements, - node->nodeName); - } - if (h) { - cdataChild = 1; - } - } - child = node->firstChild; - while (child != NULL) { - - if ( (child->nodeType == ELEMENT_NODE) - ||(child->nodeType == PROCESSING_INSTRUCTION_NODE) - ||(child->nodeType == COMMENT_NODE) ) - { - hasElements = 1; - } - if (first) { - writeChars(xmlString, chan, ">", 1); - if ((indent != -1) && hasElements) { - writeChars(xmlString, chan, "\n", 1); - } - } - first = 0; - tcldom_treeAsXML(xmlString, child, indent, level+1, doIndent, - chan, NULL, cdataChild, outputFlags, indentAttrs); - doIndent = 0; - if ( (child->nodeType == ELEMENT_NODE) - ||(child->nodeType == PROCESSING_INSTRUCTION_NODE) - ||(child->nodeType == COMMENT_NODE) ) - { - doIndent = 1; - } - child = child->nextSibling; - } - } - - if (first) { - if (indent != -1) { - if (outputFlags & SERIALIZE_NO_EMPTY_ELEMENT_TAG) { - writeChars (xmlString, chan, ">nodeName, -1); - writeChars(xmlString, chan, ">\n", 2); - } else { - writeChars(xmlString, chan, "/>\n", 3); - } - } else { - if (outputFlags & SERIALIZE_NO_EMPTY_ELEMENT_TAG) { - writeChars (xmlString, chan, ">nodeName, -1); - writeChars(xmlString, chan, ">", 1); - } else { - writeChars(xmlString, chan, "/>", 2); - } - } - } else { - if ((indent != -1) && hasElements) { - if (outputFlags & SERIALIZE_INDENT_WITH_TAB) { - for(i=0; inodeName, -1); - if (indent != -1) { - writeChars(xmlString, chan, ">\n", 2); - } else { - writeChars(xmlString, chan, ">", 1); - } + node = doc->rootNode->firstChild; + } + if (outputFlags & SERIALIZE_INDENT_WITH_TAB) { + indBuf = "\t"; + indBufCount = 1; + } else { + indBuf = " "; + indBufCount = indent; + } + if (outputFlags & SERIALIZE_INDENT_ATTR_WITH_TAB) { + attindBuf = "\t"; + attindBufCount = 1; + } else { + attindBuf = " "; + attindBufCount = indentAttrs; + } + while (node) { + switch (node->nodeType) { + case TEXT_NODE: + if (cdataChild) { + writeChars(xmlString, chan, "nodeValue; + while (i < ((domTextNode*)node)->valueLength) { + if (*p == ']') { + p++; i++;; + if (i >= ((domTextNode*)node)->valueLength) break; + if (*p == ']') { + p++; i++;; + if (i >= ((domTextNode*)node)->valueLength) break; + if (*p == '>') { + writeChars(xmlString, chan, start, p-start); + writeChars(xmlString, chan, "]]>", 13); + start = p+1; + } + } + } + p++; i++;; + } + writeChars(xmlString, chan, start, p-start); + writeChars(xmlString, chan, "]]>", 3); + } else { + if (node->nodeFlags & DISABLE_OUTPUT_ESCAPING) { + writeChars(xmlString, chan, ((domTextNode*)node)->nodeValue, + ((domTextNode*)node)->valueLength); + } else { + tcldom_AppendEscaped(xmlString, chan, + ((domTextNode*)node)->nodeValue, + ((domTextNode*)node)->valueLength, + outputFlags); + } + } + break; + + case CDATA_SECTION_NODE: + writeChars(xmlString, chan, "nodeValue, + ((domTextNode*)node)->valueLength); + writeChars(xmlString, chan, "]]>", 3); + break; + + case COMMENT_NODE: + if (indent != -1) { + for(i=0; inodeValue, + ((domTextNode*)node)->valueLength); + writeChars(xmlString, chan, "-->", 3); + if (indent != -1) writeChars (xmlString, chan, "\n", 1); + break; + + case PROCESSING_INSTRUCTION_NODE: + if (indent != -1) { + for(i=0; itargetValue, + ((domProcessingInstructionNode*)node)->targetLength); + writeChars(xmlString, chan, " ", 1); + writeChars(xmlString, chan, + ((domProcessingInstructionNode*)node)->dataValue, + ((domProcessingInstructionNode*)node)->dataLength); + writeChars(xmlString, chan, "?>", 2); + if (indent != -1) writeChars (xmlString, chan, "\n", 1); + break; + + case ELEMENT_NODE: + if (indent != -1 && + (node->previousSibling == NULL || + ((node->previousSibling->nodeType == ELEMENT_NODE) || + (node->previousSibling->nodeType == PROCESSING_INSTRUCTION_NODE) || + (node->previousSibling->nodeType == COMMENT_NODE))) + ) + { + for(i=0; inodeName, -1); + + attrs = node->firstAttr; + while (attrs) { + if (indentAttrs > -1) { + writeChars(xmlString, chan, "\n", 1); + if (indent != -1) { + for(i=0; inodeName, -1); + writeChars(xmlString, chan, "=\"", 2); + tcldom_AppendEscaped(xmlString, chan, attrs->nodeValue, + attrs->valueLength, + outputFlags | SERIALIZE_FOR_ATTR); + writeChars(xmlString, chan, "\"", 1); + attrs = attrs->nextSibling; + } + + if (node->firstChild) { + writeChars(xmlString, chan, ">", 1); + if (indent != -1 + && ( (node->firstChild->nodeType == ELEMENT_NODE) + ||(node->firstChild->nodeType == PROCESSING_INSTRUCTION_NODE) + ||(node->firstChild->nodeType == COMMENT_NODE) ) + ) { + writeChars(xmlString, chan, "\n", 1); + } + cdataChild = 0; + if (node->ownerDocument->doctype + && node->ownerDocument->doctype->cdataSectionElements) { + if (node->namespace) { + Tcl_DStringInit (&dStr); + Tcl_DStringAppend (&dStr, domNamespaceURI(node), -1); + Tcl_DStringAppend (&dStr, ":", 1); + domSplitQName (node->nodeName, prefix, &localName); + Tcl_DStringAppend (&dStr, localName, -1); + h = Tcl_FindHashEntry ( + node->ownerDocument->doctype->cdataSectionElements, + Tcl_DStringValue (&dStr)); + Tcl_DStringFree (&dStr); + } else { + h = Tcl_FindHashEntry ( + node->ownerDocument->doctype->cdataSectionElements, + node->nodeName); + } + if (h) { + cdataChild = 1; + } + } + if (level < STATIC_STACK_SIZE) { + staticStack[level].node = node; + staticStack[level].cdata = cdataChild; + } else { + if (level >= STATIC_STACK_SIZE + dynStackSize) { + if (dynStackSize) { + dynStack = (asXMLStack *) + REALLOC (dynStack, + sizeof(asXMLStack)*dynStackSize*2); + dynStackSize *= 2; + } else { + dynStack = (asXMLStack *) + MALLOC (sizeof(asXMLStack)*STATIC_STACK_SIZE); + dynStackSize = STATIC_STACK_SIZE; + } + } + dynStack[level-STATIC_STACK_SIZE].node = node; + dynStack[level-STATIC_STACK_SIZE].cdata = cdataChild; + } + level++; + node = node->firstChild; + continue; + } else { + if (indent != -1 && + (node->nextSibling == NULL || + ((node->nextSibling->nodeType == ELEMENT_NODE) || + (node->nextSibling->nodeType == PROCESSING_INSTRUCTION_NODE) || + (node->nextSibling->nodeType == COMMENT_NODE))) + ) + { + if (outputFlags & SERIALIZE_NO_EMPTY_ELEMENT_TAG) { + writeChars (xmlString, chan, ">nodeName, -1); + writeChars(xmlString, chan, ">\n", 2); + } else { + writeChars(xmlString, chan, "/>\n", 3); + } + } else { + if (outputFlags & SERIALIZE_NO_EMPTY_ELEMENT_TAG) { + writeChars (xmlString, chan, ">nodeName, -1); + writeChars(xmlString, chan, ">", 1); + } else { + writeChars(xmlString, chan, "/>", 2); + } + } + } + default: + /* Does not happen */ + ; + } + + if (level == 0) { + if (isDoc) { + node = node->nextSibling; + } else { + break; + } + } else { + if (node->nextSibling) { + node = node->nextSibling; + } else { + while (1) { + level--; + if (level < STATIC_STACK_SIZE) { + node = staticStack[level].node; + cdataChild = staticStack[level].cdata; + } else { + node = dynStack[level-STATIC_STACK_SIZE].node; + cdataChild = dynStack[level-STATIC_STACK_SIZE].cdata; + } + if (indent != -1 + && ( node->firstChild->nodeType == ELEMENT_NODE + || node->firstChild->nodeType == PROCESSING_INSTRUCTION_NODE + || node->firstChild->nodeType == COMMENT_NODE ) + ) { + for(i=0; inodeName, -1); + if (indent != -1) { + writeChars(xmlString, chan, ">\n", 2); + } else { + writeChars(xmlString, chan, ">", 1); + } + if (level > 0) { + if (node->nextSibling) { + cdataChild = staticStack[level-1].cdata; + node = node->nextSibling; + break; + } else { + continue; + } + } else { + if (isDoc) { + node = node->nextSibling; + } else { + node = NULL; + } + break; + } + } + } + } + } + if (dynStackSize) { + FREE (dynStack); } } /*---------------------------------------------------------------------------- | tcldom_AppendEscapedJSON @@ -3492,19 +3550,16 @@ Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] ) { - char *channelId, prefix[MAX_PREFIX_LEN]; - const char *localName; + char *channelId; int indent, mode, bool; int outputFlags = 0; - int optionIndex, cdataChild; + int optionIndex; Tcl_Obj *resultPtr, *encString = NULL; Tcl_Channel chan = (Tcl_Channel) NULL; - Tcl_HashEntry *h; - Tcl_DString dStr; int indentAttrs = -1; static const char *asXMLOptions[] = { "-indent", "-channel", "-escapeNonASCII", "-doctypeDeclaration", "-xmlDeclaration", "-encString", "-escapeAllQuot", "-indentAttrs", @@ -3672,35 +3727,12 @@ } if (indent > 8) indent = 8; if (indent < -1) indent = -1; resultPtr = Tcl_NewStringObj("", 0); - cdataChild = 0; - if (node->nodeType == ELEMENT_NODE - && node->ownerDocument->doctype - && node->ownerDocument->doctype->cdataSectionElements) { - if (node->namespace) { - Tcl_DStringInit (&dStr); - Tcl_DStringAppend (&dStr, domNamespaceURI(node), -1); - Tcl_DStringAppend (&dStr, ":", 1); - domSplitQName (node->nodeName, prefix, &localName); - Tcl_DStringAppend (&dStr, localName, -1); - h = Tcl_FindHashEntry ( - node->ownerDocument->doctype->cdataSectionElements, - Tcl_DStringValue (&dStr)); - Tcl_DStringFree (&dStr); - } else { - h = Tcl_FindHashEntry ( - node->ownerDocument->doctype->cdataSectionElements, - node->nodeName); - } - if (h) { - cdataChild = 1; - } - } - tcldom_treeAsXML(resultPtr, node, indent, 0, 1, chan, encString, - cdataChild, outputFlags, indentAttrs); + tcldom_treeAsXML(resultPtr, node, indent, chan, encString, + outputFlags, indentAttrs); Tcl_SetObjResult(interp, resultPtr); if (encString) { Tcl_DecrRefCount(encString); } return TCL_OK; Index: tests/domDoc.test ================================================================== --- tests/domDoc.test +++ tests/domDoc.test @@ -369,10 +369,24 @@ } {

boo

} +test domDoc-1.39 {asXML -indent tabs w/ comment} { + set doc [dom parse {}] + set result [$doc asXML -indent tabs] + $doc delete + set result +} "\n\t\n\t\t\n\t\t\n\t\t\n\t\n\t\n\n" + +test domDoc-1.40 {asXML -indent tabs w/ processing-instruction} { + set doc [dom parse {}] + set result [$doc asXML -indent tabs] + $doc delete + set result +} "\n\t\n\t\t\n\t\t\n\t\t\n\t\n\t\n\n" + set doc [dom parse ] test domDoc-2.1 {publicId - no publicId there} { $doc publicId } {}