1/*
2 * tdpExpat.c --
3 *
4 * This file contains routines for manipulating DOM
5 * object representations.
6 *
7 *
8 * Copyright (c) 1998 Steve Ball, Zveno Pty Ltd
9 * Modified by Ajuba Solutions
10 * Changes copyright (c) 1999-2000 Ajuba Solutions
11 *
12 * Zveno Pty Ltd makes this software and associated documentation
13 * available free of charge for any purpose.  You may make copies
14 * of the software but you must include all of this notice on any copy.
15 *
16 * Zveno Pty Ltd does not warrant that this software is error free
17 * or fit for any purpose.  Zveno Pty Ltd disclaims any liability for
18 * all claims, expenses, losses, damages and costs any user may incur
19 * as a result of using, copying or modifying the software.
20 *
21 * $Id: tdpExpat.c,v 1.16 2003/03/28 20:41:11 jenglish Exp $
22 *
23 */
24
25#include <string.h>
26#include "tclDomProInt.h"
27#include <expat.h>
28
29/*
30 * Prototypes for procedures defined later in this file:
31 */
32
33static int 	isExtender(int c);
34static int 	isCombiningChar(int c);
35static int 	isBaseChar(int c);
36static int 	isIdeographic(int c);
37static int 	isLetter(int c);
38
39static void	TclDomExpatElementStartHandler _ANSI_ARGS_((void *userdata,
40		    const XML_Char *name, const XML_Char **atts));
41static void	TclDomExpatElementEndHandler _ANSI_ARGS_((void *userData,
42		    const XML_Char *name));
43static void	TclDomExpatCharacterDataHandler _ANSI_ARGS_((void *userData,
44		    const XML_Char *s, int len));
45static void	TclDomExpatProcessingInstructionHandler _ANSI_ARGS_((
46		    void *userData, const XML_Char *target,
47		    const XML_Char *data));
48static int	TclDomExpatExternalEntityRefHandler _ANSI_ARGS_((
49		    XML_Parser parser, const XML_Char *openEntityNames,
50		    const XML_Char *base, const XML_Char *systemId,
51		    const XML_Char *publicId));
52static void	TclDomExpatDefaultHandler _ANSI_ARGS_ ((void *userData,
53		    const XML_Char *s, int len));
54static void	TclDomExpatUnparsedDeclHandler _ANSI_ARGS_ ((void *userData,
55		    const XML_Char *entityname, const XML_Char *base,
56		    const XML_Char *systemId, const XML_Char *publicId,
57		    const XML_Char *notationName));
58static void	TclDomExpatNotationDeclHandler _ANSI_ARGS_ ((void *userData,
59		    const XML_Char *notationName, const XML_Char *base,
60		    const XML_Char *systemId, const XML_Char *publicId));
61static int	TclDomExpatUnknownEncodingHandler _ANSI_ARGS_ ((
62		    void *encodingHandlerData, const XML_Char *name,
63		    XML_Encoding *info));
64static void	SerializeWalk(TclDomNode *nodePtr, Tcl_DString *output);
65static void	RemoveAttributeFromArray(Tcl_Interp *interp,
66		    TclDomInterpData *interpDataPtr, TclDomNode *nodePtr,
67		    TclDomAttributeNode *attributeNodePtr);
68static void	SetAttributeInArray(Tcl_Interp *interp,
69		    TclDomInterpData *interpDataPtr,
70		    TclDomNode *nodePtr,
71		    TclDomAttributeNode *attributeNodePtr);
72static void	SerializeDocumentType(TclDomNode *nodePtr,
73		    Tcl_DString *output);
74static void	SerializeAttribute(TclDomAttributeNode *attributeNodePtr,
75		    Tcl_DString *output);
76
77/* Following added by ericm@scriptics, 1999.6.25 */
78/* Prototype definition for the TclDomExpat comment handler */
79
80static void	TclDomExpatCommentHandler _ANSI_ARGS_ ((void *userData,
81		    const XML_Char *data));
82/* Prototype for TclDomExpat Not Standalone Handler */
83static int	TclDomExpatNotStandaloneHandler _ANSI_ARGS_ ((void *userData));
84
85/* Prototype for TclDomExpat {Start|End}CdataSectionHandler */
86static void	TclDomExpatStartCdataSectionHandler(void *userData);
87static void	TclDomExpatEndCdataSectionHandler(void *userData);
88static void	TclDomExpatStartDoctypeDeclHandler(void *userData,
89		    const XML_Char *doctypeName,
90		    const XML_Char *sysid,
91		    const XML_Char *pubid,
92		    int has_internal_subset);
93static void	TclDomExpatEndDoctypeDeclHandler(void *userData);
94
95
96
97/*
98 *--------------------------------------------------------------
99 *
100 * IsExtender --
101 *
102 *	This procedure determines if a character belongs to the
103 *	Extender class, as defined in the XML specification.
104 *
105 * Results:
106 *	1 if the character is an Extender; otherwise 0.
107 *
108 * Side effects:
109 *	None.
110 *
111 *--------------------------------------------------------------
112 */
113
114static int
115isExtender(int c)
116{
117    if (c < 0xff) return 0;
118    return ((c == 0x00b7)
119	    || (c == 0x02d0)
120	    || (c == 0x02d1)
121	    || (c == 0x0387)
122	    || (c == 0x0640)
123	    || (c == 0x0e46)
124	    || (c == 0x0ec6)
125	    || (c == 0x3005)
126	    || (c >= 0x3031 && c <= 0x3035)
127	    || (c >= 0x309d && c <= 0x309e)
128	    || (c >= 0x30fc && c <= 0x30fe));
129}
130
131
132/*
133 *--------------------------------------------------------------
134 *
135 * IsCombiningChar --
136 *
137 *	This procedure determines if a character belongs to the
138 *	CombiningChar class, as defined in the XML specification.
139 *
140 * Results:
141 *	1 if the character is a CombiningChar; otherwise 0.
142 *
143 * Side effects:
144 *	None.
145 *
146 *--------------------------------------------------------------
147 */
148
149static int
150isCombiningChar(int c)
151{
152	if (c < 0xff) return 0;
153	return (
154		(c >= 0x0300 && c <= 0x0345) ||
155		(c >= 0x0360 && c <= 0x0361) ||
156		(c >= 0x0483 && c <= 0x0486) ||
157		(c >= 0x0591 && c <= 0x05A1) ||
158		(c >= 0x05A3 && c <= 0x05B9) ||
159		(c >= 0x05BB && c <= 0x05BD) ||
160		(c == 0x05BF)				 ||
161		(c >= 0x05C1 && c <= 0x05C2) ||
162		(c == 0x05C4)				 ||
163		(c >= 0x064B && c <= 0x0652) ||
164		(c == 0x0670)				 ||
165		(c >= 0x06D6 && c <= 0x06DC) ||
166		(c >= 0x06DD && c <= 0x06DF) ||
167		(c >= 0x06E0 && c <= 0x06E4) ||
168		(c >= 0x06E7 && c <= 0x06E8) ||
169		(c >= 0x06EA && c <= 0x06ED) ||
170		(c >= 0x0901 && c <= 0x0903) ||
171		(c == 0x093C)				 ||
172		(c >= 0x093E && c <= 0x094C) ||
173		(c == 0x094D)				 ||
174		(c >= 0x0951 && c <= 0x0954) ||
175		(c >= 0x0962 && c <= 0x0963) ||
176		(c >= 0x0981 && c <= 0x0983) ||
177		(c == 0x09BC)				 ||
178		(c == 0x09BE)				 ||
179		(c == 0x09BF)				 ||
180		(c >= 0x09C0 && c <= 0x09C4) ||
181		(c >= 0x09C7 && c <= 0x09C8) ||
182		(c >= 0x09CB && c <= 0x09CD) ||
183		(c == 0x09D7)				 ||
184		(c >= 0x09E2 && c <= 0x09E3) ||
185		(c == 0x0A02)				 ||
186		(c == 0x0A3C)				 ||
187		(c == 0x0A3E)				 ||
188		(c == 0x0A3F)				 ||
189		(c >= 0x0A40 && c <= 0x0A42) ||
190		(c >= 0x0A47 && c <= 0x0A48) ||
191		(c >= 0x0A4B && c <= 0x0A4D) ||
192		(c >= 0x0A70 && c <= 0x0A71) ||
193		(c >= 0x0A81 && c <= 0x0A83) ||
194		(c == 0x0ABC)				 ||
195		(c >= 0x0ABE && c <= 0x0AC5) ||
196		(c >= 0x0AC7 && c <= 0x0AC9) ||
197		(c >= 0x0ACB && c <= 0x0ACD) ||
198		(c >= 0x0B01 && c <= 0x0B03) ||
199		(c == 0x0B3C)				 ||
200		(c >= 0x0B3E && c <= 0x0B43) ||
201		(c >= 0x0B47 && c <= 0x0B48) ||
202		(c >= 0x0B4B && c <= 0x0B4D) ||
203		(c >= 0x0B56 && c <= 0x0B57) ||
204		(c >= 0x0B82 && c <= 0x0B83) ||
205		(c >= 0x0BBE && c <= 0x0BC2) ||
206		(c >= 0x0BC6 && c <= 0x0BC8) ||
207		(c >= 0x0BCA && c <= 0x0BCD) ||
208		(c == 0x0BD7)				 ||
209		(c >= 0x0C01 && c <= 0x0C03) ||
210		(c >= 0x0C3E && c <= 0x0C44) ||
211		(c >= 0x0C46 && c <= 0x0C48) ||
212		(c >= 0x0C4A && c <= 0x0C4D) ||
213		(c >= 0x0C55 && c <= 0x0C56) ||
214		(c >= 0x0C82 && c <= 0x0C83) ||
215		(c >= 0x0CBE && c <= 0x0CC4) ||
216		(c >= 0x0CC6 && c <= 0x0CC8) ||
217		(c >= 0x0CCA && c <= 0x0CCD) ||
218		(c >= 0x0CD5 && c <= 0x0CD6) ||
219		(c >= 0x0D02 && c <= 0x0D03) ||
220		(c >= 0x0D3E && c <= 0x0D43) ||
221		(c >= 0x0D46 && c <= 0x0D48) ||
222		(c >= 0x0D4A && c <= 0x0D4D) ||
223		(c == 0x0D57)				 ||
224		(c == 0x0E31)				 ||
225		(c >= 0x0E34 && c <= 0x0E3A) ||
226		(c >= 0x0E47 && c <= 0x0E4E) ||
227		(c == 0x0EB1)				 ||
228		(c >= 0x0EB4 && c <= 0x0EB9) ||
229		(c >= 0x0EBB && c <= 0x0EBC) ||
230		(c >= 0x0EC8 && c <= 0x0ECD) ||
231		(c >= 0x0F18 && c <= 0x0F19) ||
232		(c == 0x0F35)				 ||
233		(c == 0x0F37)				 ||
234		(c == 0x0F39)				 ||
235		(c == 0x0F3E)				 ||
236		(c == 0x0F3F)				 ||
237		(c >= 0x0F71 && c <= 0x0F84) ||
238		(c >= 0x0F86 && c <= 0x0F8B) ||
239		(c >= 0x0F90 && c <= 0x0F95) ||
240		(c == 0x0F97)				 ||
241		(c >= 0x0F99 && c <= 0x0FAD) ||
242		(c >= 0x0FB1 && c <= 0x0FB7) ||
243		(c == 0x0FB9)				 ||
244		(c >= 0x20D0 && c <= 0x20DC) ||
245		(c == 0x20E1)				 ||
246		(c >= 0x302A && c <= 0x302F) ||
247		(c == 0x3099)				 ||
248		(c == 0x309A)
249	);
250}
251
252
253/*
254 *--------------------------------------------------------------
255 *
256 * IsBaseChar --
257 *
258 *	This procedure determines if a character belongs to the
259 *	BaseChar class, as defined in the XML specification.
260 *
261 * Results:
262 *	1 if the character is a BaseChar; otherwise 0.
263 *
264 * Side effects:
265 *	None.
266 *
267 *--------------------------------------------------------------
268 */
269
270static int
271isBaseChar(int c)
272{
273	return (
274		(c >= 0x0041 && c <= 0x005A) ||
275		(c >= 0x0061 && c <= 0x007A) ||
276		(c >= 0x00C0 && c <= 0x00D6) ||
277		(c >= 0x00D8 && c <= 0x00F6) ||
278		(c >= 0x00F8 && c <= 0x00FF) ||
279		(c >= 0x0100 && c <= 0x0131) ||
280		(c >= 0x0134 && c <= 0x013E) ||
281		(c >= 0x0141 && c <= 0x0148) ||
282		(c >= 0x014A && c <= 0x017E) ||
283		(c >= 0x0180 && c <= 0x01C3) ||
284		(c >= 0x01CD && c <= 0x01F0) ||
285		(c >= 0x01F4 && c <= 0x01F5) ||
286		(c >= 0x01FA && c <= 0x0217) ||
287		(c >= 0x0250 && c <= 0x02A8) ||
288		(c >= 0x02BB && c <= 0x02C1) ||
289		(c == 0x0386)				 ||
290		(c >= 0x0388 && c <= 0x038A) ||
291	    (c == 0x038C)		         ||
292		(c >= 0x038E && c <= 0x03A1) ||
293		(c >= 0x03A3 && c <= 0x03CE) ||
294		(c >= 0x03D0 && c <= 0x03D6) ||
295		(c == 0x03DA)				 ||
296		(c == 0x03DC)				 ||
297		(c == 0x03DE)				 ||
298		(c == 0x03E0)				 ||
299		(c >= 0x03E2 && c <= 0x03F3) ||
300		(c >= 0x0401 && c <= 0x040C) ||
301		(c >= 0x040E && c <= 0x044F) ||
302		(c >= 0x0451 && c <= 0x045C) ||
303		(c >= 0x045E && c <= 0x0481) ||
304		(c >= 0x0490 && c <= 0x04C4) ||
305		(c >= 0x04C7 && c <= 0x04C8) ||
306		(c >= 0x04CB && c <= 0x04CC) ||
307		(c >= 0x04D0 && c <= 0x04EB) ||
308		(c >= 0x04EE && c <= 0x04F5) ||
309		(c >= 0x04F8 && c <= 0x04F9) ||
310		(c >= 0x0531 && c <= 0x0556) ||
311		(c == 0x0559)				 ||
312		(c >= 0x0561 && c <= 0x0586) ||
313		(c >= 0x05D0 && c <= 0x05EA) ||
314		(c >= 0x05F0 && c <= 0x05F2) ||
315		(c >= 0x0621 && c <= 0x063A) ||
316		(c >= 0x0641 && c <= 0x064A) ||
317		(c >= 0x0671 && c <= 0x06B7) ||
318		(c >= 0x06BA && c <= 0x06BE) ||
319		(c >= 0x06C0 && c <= 0x06CE) ||
320		(c >= 0x06D0 && c <= 0x06D3) ||
321		(c == 0x06D5)				 ||
322		(c >= 0x06E5 && c <= 0x06E6) ||
323		(c >= 0x0905 && c <= 0x0939) ||
324		(c == 0x093D)				 ||
325		(c >= 0x0958 && c <= 0x0961) ||
326		(c >= 0x0985 && c <= 0x098C) ||
327		(c >= 0x098F && c <= 0x0990) ||
328		(c >= 0x0993 && c <= 0x09A8) ||
329		(c >= 0x09AA && c <= 0x09B0) ||
330		(c == 0x09B2)				 ||
331		(c >= 0x09B6 && c <= 0x09B9) ||
332		(c >= 0x09DC && c <= 0x09DD) ||
333		(c >= 0x09DF && c <= 0x09E1) ||
334		(c >= 0x09F0 && c <= 0x09F1) ||
335		(c >= 0x0A05 && c <= 0x0A0A) ||
336		(c >= 0x0A0F && c <= 0x0A10) ||
337		(c >= 0x0A13 && c <= 0x0A28) ||
338		(c >= 0x0A2A && c <= 0x0A30) ||
339		(c >= 0x0A32 && c <= 0x0A33) ||
340		(c >= 0x0A35 && c <= 0x0A36) ||
341		(c >= 0x0A38 && c <= 0x0A39) ||
342		(c >= 0x0A59 && c <= 0x0A5C) ||
343		(c == 0x0A5E)				 ||
344		(c >= 0x0A72 && c <= 0x0A74) ||
345		(c >= 0x0A85 && c <= 0x0A8B) ||
346		(c == 0x0A8D)				 ||
347		(c >= 0x0A8F && c <= 0x0A91) ||
348		(c >= 0x0A93 && c <= 0x0AA8) ||
349		(c >= 0x0AAA && c <= 0x0AB0) ||
350		(c >= 0x0AB2 && c <= 0x0AB3) ||
351		(c >= 0x0AB5 && c <= 0x0AB9) ||
352		(c == 0x0ABD)				 ||
353		(c == 0x0AE00)				 ||
354		(c >= 0x0B05 && c <= 0x0B0C) ||
355		(c >= 0x0B0F && c <= 0x0B10) ||
356		(c >= 0x0B13 && c <= 0x0B28) ||
357		(c >= 0x0B2A && c <= 0x0B30) ||
358		(c >= 0x0B32 && c <= 0x0B33) ||
359		(c >= 0x0B36 && c <= 0x0B39) ||
360		(c == 0x0B3D)				 ||
361		(c >= 0x0B5C && c <= 0x0B5D) ||
362		(c >= 0x0B5F && c <= 0x0B61) ||
363		(c >= 0x0B85 && c <= 0x0B8A) ||
364		(c >= 0x0B8E && c <= 0x0B90) ||
365		(c >= 0x0B92 && c <= 0x0B95) ||
366		(c >= 0x0B99 && c <= 0x0B9A) ||
367		(c == 0x0B9C)				 ||
368		(c >= 0x0B9E && c <= 0x0B9F) ||
369		(c >= 0x0BA3 && c <= 0x0BA4) ||
370		(c >= 0x0BA8 && c <= 0x0BAA) ||
371		(c >= 0x0BAE && c <= 0x0BB5) ||
372		(c >= 0x0BB7 && c <= 0x0BB9) ||
373		(c >= 0x0C05 && c <= 0x0C0C) ||
374		(c >= 0x0C0E && c <= 0x0C10) ||
375		(c >= 0x0C12 && c <= 0x0C28) ||
376		(c >= 0x0C2A && c <= 0x0C33) ||
377		(c >= 0x0C35 && c <= 0x0C39) ||
378		(c >= 0x0C60 && c <= 0x0C61) ||
379		(c >= 0x0C85 && c <= 0x0C8C) ||
380		(c >= 0x0C8E && c <= 0x0C90) ||
381		(c >= 0x0C92 && c <= 0x0CA8) ||
382		(c >= 0x0CAA && c <= 0x0CB3) ||
383		(c >= 0x0CB5 && c <= 0x0CB9) ||
384		(c == 0x0CDE)				 ||
385		(c >= 0x0CE0 && c <= 0x0CE1) ||
386		(c >= 0x0D05 && c <= 0x0D0C) ||
387		(c >= 0x0D0E && c <= 0x0D10) ||
388		(c >= 0x0D12 && c <= 0x0D28) ||
389		(c >= 0x0D2A && c <= 0x0D39) ||
390		(c >= 0x0D60 && c <= 0x0D61) ||
391		(c >= 0x0E01 && c <= 0x0E2E) ||
392		(c == 0x0E30)				 ||
393		(c >= 0x0E32 && c <= 0x0E33) ||
394		(c >= 0x0E40 && c <= 0x0E45) ||
395		(c >= 0x0E81 && c <= 0x0E82) ||
396		(c == 0x0E84)				 ||
397		(c >= 0x0E87 && c <= 0x0E88) ||
398		(c == 0x0E8A)				 ||
399		(c == 0x0E8D)				 ||
400		(c >= 0x0E94 && c <= 0x0E97) ||
401		(c >= 0x0E99 && c <= 0x0E9F) ||
402		(c >= 0x0EA1 && c <= 0x0EA3) ||
403		(c == 0x0EA5)				 ||
404		(c == 0x0EA7)				 ||
405		(c >= 0x0EAA && c <= 0x0EAB) ||
406		(c >= 0x0EAD && c <= 0x0EAE) ||
407		(c == 0x0EB0)				 ||
408		(c >= 0x0EB2 && c <= 0x0EB3) ||
409		(c == 0x0EBD)				 ||
410		(c >= 0x0EC0 && c <= 0x0EC4) ||
411		(c >= 0x0F40 && c <= 0x0F47) ||
412		(c >= 0x0F49 && c <= 0x0F69) ||
413		(c >= 0x10A0 && c <= 0x10C5) ||
414		(c >= 0x10D0 && c <= 0x10F6) ||
415		(c == 0x1100)				 ||
416		(c >= 0x1102 && c <= 0x1103) ||
417		(c >= 0x1105 && c <= 0x1107) ||
418		(c == 0x1109)				 ||
419		(c >= 0x110B && c <= 0x110C) ||
420		(c >= 0x110E && c <= 0x1112) ||
421		(c == 0x113C)				 ||
422		(c == 0x113E)				 ||
423		(c == 0x1140)				 ||
424		(c == 0x114C)				 ||
425		(c == 0x114E)		         ||
426		(c == 0x1150)				 ||
427		(c >= 0x1154 && c <= 0x1155) ||
428		(c == 0x1159)				 ||
429		(c >= 0x115F && c <= 0x1161) ||
430		(c == 0x1163)				 ||
431		(c == 0x1165)				 ||
432		(c == 0x1167)				 ||
433		(c == 0x1169)				 ||
434		(c >= 0x116D && c <= 0x116E) ||
435		(c >= 0x1172 && c <= 0x1173) ||
436		(c == 0x1175)				 ||
437		(c == 0x119E)				 ||
438		(c == 0x11A8)				 ||
439		(c == 0x11AB)				 ||
440		(c >= 0x11AE && c <= 0x11AF) ||
441		(c >= 0x11B7 && c <= 0x11B8) ||
442		(c == 0x11BA)				 ||
443		(c >= 0x11BC && c <= 0x11C2) ||
444		(c == 0x11EB)				 ||
445		(c == 0x11F0)				 ||
446		(c == 0x11F9)				 ||
447		(c >= 0x1E00 && c <= 0x1E9B) ||
448		(c >= 0x1EA0 && c <= 0x1EF9) ||
449		(c >= 0x1F00 && c <= 0x1F15) ||
450		(c >= 0x1F18 && c <= 0x1F1D) ||
451		(c >= 0x1F20 && c <= 0x1F45) ||
452		(c >= 0x1F48 && c <= 0x1F4D) ||
453		(c >= 0x1F50 && c <= 0x1F57) ||
454		(c == 0x1F59)				 ||
455		(c == 0x1F5B)				 ||
456		(c == 0x1F5D)				 ||
457		(c >= 0x1F5F && c <= 0x1F7D) ||
458		(c >= 0x1F80 && c <= 0x1FB4) ||
459		(c >= 0x1FB6 && c <= 0x1FBC) ||
460		(c == 0x1FBE)				 ||
461		(c >= 0x1FC2 && c <= 0x1FC4) ||
462		(c >= 0x1FC6 && c <= 0x1FCC) ||
463		(c >= 0x1FD0 && c <= 0x1FD3) ||
464		(c >= 0x1FD6 && c <= 0x1FDB) ||
465		(c >= 0x1FE0 && c <= 0x1FEC) ||
466		(c >= 0x1FF2 && c <= 0x1FF4) ||
467		(c >= 0x1FF6 && c <= 0x1FFC) ||
468		(c == 0x2126)		         ||
469		(c >= 0x212A && c <= 0x212B) ||
470		(c == 0x212E)				 ||
471		(c >= 0x2180 && c <= 0x2182) ||
472		(c >= 0x3041 && c <= 0x3094) ||
473		(c >= 0x30A1 && c <= 0x30FA) ||
474		(c >= 0x3105 && c <= 0x312C) ||
475		(c >= 0xAC00 && c <= 0xD7A3)
476	);
477}
478
479
480/*
481 *--------------------------------------------------------------
482 *
483 * IsIdeographic --
484 *
485 *	This procedure determines if a character belongs to the
486 *	Ideographic class, as defined in the XML specification.
487 *
488 * Results:
489 *	1 if the character is a Ideographic; otherwise 0.
490 *
491 * Side effects:
492 *	None.
493 *
494 *--------------------------------------------------------------
495 */
496
497static int
498isIdeographic(int c)
499{
500	return (
501		    (c >= 0x4e00 && c <= 0x9fa5)
502			|| (c == 0x3007)
503			|| (c >= 0x3021 && c <= 0x3029)
504	);
505}
506
507
508/*
509 *--------------------------------------------------------------
510 *
511 * IsLetter --
512 *
513 *	This procedure determines if a character belongs to the
514 *	Letter class, as defined in the XML specification.
515 *
516 * Results:
517 *	1 if the character is an Letter; otherwise 0.
518 *
519 * Side effects:
520 *	None.
521 *
522 *--------------------------------------------------------------
523 */
524
525static int
526isLetter(int c)
527{
528    return (isBaseChar(c) || isIdeographic(c));
529}
530
531
532/*
533 *--------------------------------------------------------------
534 *
535 * UnlinkChild --
536 *
537 *	Remove a node from the document tree.
538 *
539 * Results:
540 *	1 if the node was in the fragment list; 0 otherwise.
541 *
542 * Side effects:
543 *	Reference nodes of iterators may be updated.
544 *
545 *--------------------------------------------------------------
546 */
547
548static void
549UnlinkChild(TclDomInterpData *interpDataPtr, TclDomNode *childPtr)
550{
551    TclDomNodeIterator *nodeIteratorPtr;
552    TclDomTreeWalker *treeWalkerPtr;
553    Tcl_HashEntry *entry;
554    Tcl_HashSearch search;
555    TclDomNode *testPtr;
556
557    /*
558     * See if this action will cause a reference node to be deleted.
559     * It's not clear that this implementation (i.e., keeping a list
560     * of iterators, and then fixing up the iterators when nodes are
561     * deleted) is the optimal way to do this. For small trees, and
562     * a small number of iterators, it may not matter much. If we
563     * wind up having many iterators, or doing many deletions, this
564     * may change.
565     */
566
567    for (entry = Tcl_FirstHashEntry(&interpDataPtr->iteratorHashTable,
568	         &search); entry; entry = Tcl_NextHashEntry(&search)) {
569	    nodeIteratorPtr = (TclDomNodeIterator *) Tcl_GetHashValue(entry);
570	    if (nodeIteratorPtr->rootPtr && nodeIteratorPtr->rootPtr->containingDocumentPtr
571	        	== childPtr->containingDocumentPtr) {
572	        for (testPtr = nodeIteratorPtr->referencePtr;
573		            testPtr != nodeIteratorPtr->rootPtr->parentNodePtr;
574		            testPtr = testPtr->parentNodePtr) {
575		        if (testPtr == childPtr) {
576		            if (testPtr == nodeIteratorPtr->rootPtr) {
577			            /*
578			             * We're deleting the entire iterated tree, so
579			             * there's no effect on the iterator
580			             */
581			            break;
582		            }
583		            /*
584		             * We're deleting some portion of iterated tree
585		             */
586		            if (nodeIteratorPtr->position
587			                == REFERENCE_IS_BEFORE_ITERATOR) {
588			            TclDomNode *newRefPtr;
589			            TclDomNodeBefore(testPtr, nodeIteratorPtr->rootPtr,
590				                SHOW_ALL, NULL, &newRefPtr);
591			            nodeIteratorPtr->referencePtr = newRefPtr;
592		            } else {
593			            TclDomNode *newRefPtr;
594			            TclDomNodeAfter(testPtr, nodeIteratorPtr->rootPtr,
595				                SHOW_ALL, NULL, &newRefPtr);
596			            if (newRefPtr == NULL) {
597				            /*
598				             * Special case where reference node
599				             * is last node
600				             */
601			                TclDomNodeBefore(testPtr, nodeIteratorPtr->rootPtr,
602				                    SHOW_ALL, NULL, &newRefPtr);
603			            }
604			            nodeIteratorPtr->referencePtr = newRefPtr;
605		            }
606		        }
607	        }
608	    }
609    }
610
611    for (entry = Tcl_FirstHashEntry(&interpDataPtr->treeWalkerHashTable,
612	        &search); entry; entry = Tcl_NextHashEntry(&search)) {
613	    treeWalkerPtr = (TclDomTreeWalker *) Tcl_GetHashValue(entry);
614	    if (treeWalkerPtr->rootPtr && treeWalkerPtr->rootPtr->containingDocumentPtr
615		        == childPtr->containingDocumentPtr) {
616	        for (testPtr = treeWalkerPtr->currentNodePtr;
617		            testPtr != treeWalkerPtr->rootPtr->parentNodePtr;
618		            testPtr = testPtr->parentNodePtr) {
619		        if (testPtr == childPtr) {
620		            if (testPtr == treeWalkerPtr->rootPtr) {
621			            /*
622			             * We're deleting the entire iterated tree, so
623			             * there's no effect on the iterator
624			             */
625			            break;
626		            } else {
627			            /*
628			             * We're deleting some portion of iterated tree
629			             */
630			            TclDomNode *newRefPtr;
631#ifdef UNDEF
632			            TclDomNodeBefore(testPtr, treeWalkerPtr->rootPtr,
633								SHOW_ALL, NULL, &newRefPtr);
634#endif
635                        TclDomTreeWalkerPreviousNode(testPtr, treeWalkerPtr->rootPtr,
636								SHOW_ALL, NULL, &newRefPtr);
637			            treeWalkerPtr->currentNodePtr = newRefPtr;
638		            }
639		        }
640	        }
641	    }
642    }
643
644    if (childPtr->previousSiblingPtr) {
645	    childPtr->previousSiblingPtr->nextSiblingPtr =
646				childPtr->nextSiblingPtr;
647    } else if (childPtr->parentNodePtr) {
648	    childPtr->parentNodePtr->firstChildPtr =
649	    			childPtr->nextSiblingPtr;
650    }
651    if (childPtr->nextSiblingPtr) {
652	    childPtr->nextSiblingPtr->previousSiblingPtr
653	            = childPtr->previousSiblingPtr;
654    } else {
655	    if (childPtr->parentNodePtr) {
656	        childPtr->parentNodePtr->lastChildPtr
657		            = childPtr->previousSiblingPtr;
658	    }
659    }
660}
661
662
663/*
664 *--------------------------------------------------------------
665 *
666 * UnlinkDocumentFragment --
667 *
668 *	Remove a node from the fragment list.
669 *
670 * Results:
671 *	1 if the node was in the fragment list; 0 otherwise.
672 *
673 *--------------------------------------------------------------
674 */
675
676static int
677UnlinkDocumentFragment(
678    TclDomDocument *documentPtr,
679    TclDomNode *nodePtr)
680{
681    TclDomNode *fragmentNodesPtr;
682
683    fragmentNodesPtr = documentPtr->fragmentsPtr;
684    while (fragmentNodesPtr) {
685	    if (fragmentNodesPtr == nodePtr) {
686	        /*
687	         * Remove child from fragment list
688	         */
689
690	        if (nodePtr->previousSiblingPtr) {
691		        nodePtr->previousSiblingPtr->nextSiblingPtr =
692						nodePtr->nextSiblingPtr;
693	        } else {
694		        nodePtr->containingDocumentPtr->fragmentsPtr =
695						nodePtr->nextSiblingPtr;
696	        }
697	        if (nodePtr->nextSiblingPtr) {
698		        nodePtr->nextSiblingPtr->previousSiblingPtr = NULL;
699	        }
700	        break;
701	    }
702	    fragmentNodesPtr = fragmentNodesPtr->nextSiblingPtr;
703    }
704    return (fragmentNodesPtr != NULL) ? 1 : 0;
705}
706
707/*
708 *--------------------------------------------------------------
709 *
710 * AddDocumentFragment --
711 *
712 *	Add a newly-created node to the head
713 *	of the document fragment list.
714 *
715 * Results:
716 *	None.
717 *
718 *--------------------------------------------------------------
719 */
720
721static void
722AddDocumentFragment(
723    TclDomDocument *documentPtr,
724    TclDomNode *nodePtr)
725{
726    nodePtr->nextSiblingPtr = documentPtr->fragmentsPtr;
727    if (documentPtr->fragmentsPtr) {
728	    documentPtr->fragmentsPtr->previousSiblingPtr = nodePtr;
729	    documentPtr->fragmentsPtr = nodePtr;
730    } else {
731	    documentPtr->fragmentsPtr = nodePtr;
732    }
733    return;
734}
735
736
737/*
738 *--------------------------------------------------------------
739 *
740 * TclDomIsName --
741 *
742 *	This procedure determines if a string matches the
743 *	Name[5] production, as defined in the XML
744 *	specification.
745 *
746 * Results:
747 *	1 if the string is a Name; otherwise 0.
748 *
749 * Side effects:
750 *	None.
751 *
752 *--------------------------------------------------------------
753 */
754
755int
756TclDomIsName(
757    char *s)			/* Name string in UTF-8 */
758{
759    Tcl_UniChar uChar;
760    int length;
761
762    length = Tcl_UtfToUniChar(s, &uChar);
763    s += length;
764
765    if (!isLetter(uChar) && (uChar != '_') && (uChar != ':')) {
766	    return 0;
767    }
768
769    while (*s) {
770	    length = Tcl_UtfToUniChar(s, &uChar);
771	    s += length;
772	    if (isLetter(uChar)) continue;
773	    if (isdigit(uChar)) continue;
774	    if (uChar == '.') continue;
775	    if (uChar == '-') continue;
776	    if (uChar == '_') continue;
777	    if (uChar == ':') continue;
778	    if (isCombiningChar(uChar)) continue;
779	    if (isExtender(uChar)) continue;
780	    return 0;
781    }
782
783    return 1;
784}
785
786#ifdef UNDEF
787/*
788 * Names that match the DOM spec
789 */
790static char *typeName[13] = {
791    "",
792    "ELEMENT_NODE",
793    "ATTRIBUTE_NODE",
794    "TEXT_NODE",
795    "CDATA_SECTION_NODE",
796    "ENTITY_REFERENCE_NODE",
797    "ENTITY_NODE",
798    "PROCESSING_INSTRUCTION_NODE",
799    "COMMENT_NODE",
800    "DOCUMENT_NODE",
801    "DOCUMENT_TYPE_NODE",
802    "DOCUMENT_FRAGMENT_NODE",
803    "NOTATION_NODE"
804    };
805#else
806    /*
807     * Names that match the TclDom implementation
808     */
809static char *typeName[13] = {
810    "",
811    "element",
812    "attribute",
813    "textNode",
814    "CDATASection",
815    "entityReference",
816    "entity",
817    "processingInstruction",
818    "comment",
819    "document",
820    "documentType",
821    "documentFragment",
822    "notation"
823    };
824#endif
825
826
827/*
828 *--------------------------------------------------------------
829 *
830 * TclDomTypeName --
831 *
832 *	This procedure converts an internal type to
833 *	a human-readable string representation, and copies
834 *	the string value to the interpreter's result.
835 *
836 * Results:
837 *	TCL_OK if the type is valid; TCL_ERROR otherwise.
838 *
839 * Side effects:
840 *	Updates interpreter's result.
841 *
842 *--------------------------------------------------------------
843 */
844
845int
846TclDomNodeTypeName(
847    Tcl_Interp *interp,	    /* Interpreter whose result is to be updated */
848    TclDomNode *nodePtr)    /* DOM node */
849{
850
851
852    if (nodePtr->nodeType < ELEMENT_NODE || nodePtr->nodeType > NOTATION_NODE) {
853	    Tcl_AppendResult(interp, "invalid node type", (char *) NULL);
854	    return TCL_ERROR;
855    } else {
856	    Tcl_SetObjResult(interp, Tcl_NewStringObj(typeName[nodePtr->nodeType],
857				-1));
858	    return TCL_OK;
859    }
860}
861
862
863/*
864 *--------------------------------------------------------------
865 *
866 * TclDomGetTypeMaskFromName --
867 *
868 *	This procedure returns the node type mask
869 *   used in tree traversal given the text name
870 *   of a node type, or the value "all",
871 *   corresponding to all node types.
872 *
873 * Results:
874 *	TCL_OK if the type is valid; TCL_ERROR otherwise.
875 *
876 * Side effects:
877 *	Update the interpreter's error result if the node
878 *   type is invalid.
879 *
880 *--------------------------------------------------------------
881 */
882
883int
884TclDomGetTypeMaskFromName(
885    Tcl_Interp *interp,
886    char *nodeName,
887    unsigned int *nodeMaskPtr)
888{
889    int i;
890
891    if (strcmp(nodeName, "all") == 0) {
892	    *nodeMaskPtr = 0xffff;
893	    return TCL_OK;
894    }
895
896    for (i = 1; i < 13; i++) {
897	    if (strcmp(nodeName, typeName[i]) == 0) {
898	        *nodeMaskPtr = (1 << (i-1));
899	        return TCL_OK;
900	    }
901    }
902    Tcl_AppendResult(interp, "invalid node type", (char *) NULL);
903    return TCL_ERROR;
904}
905
906
907/*
908 *--------------------------------------------------------------
909 *
910 * TclDomTypeName --
911 *
912 *	This procedure converts the name of a node type
913 *   to a numeric type value.
914 *
915 * Results:
916 *	TCL_OK if the type is valid; TCL_ERROR otherwise.
917 *
918 * Side effects:
919 *	Updates interpreter's result.
920 *
921 *--------------------------------------------------------------
922 */
923
924int
925TclDomGetTypeFromName(
926    Tcl_Interp *interp,
927    char *nodeName,
928    unsigned int *nodeTypePtr)
929{
930    int i;
931
932    for (i = 1; i < 13; i++) {
933	    if (strcmp(nodeName, typeName[i]) == 0) {
934	        *nodeTypePtr = i;
935	        return TCL_OK;
936	    }
937    }
938    Tcl_AppendResult(interp, "invalid node type", (char *) NULL);
939    return TCL_ERROR;
940}
941
942
943/*
944 *--------------------------------------------------------------
945 *
946 * TclDomTypeName --
947 *
948 *	This procedure returns a pointer to a string version
949 *   of a node type.
950 *
951 * Results:
952 *	TCL_OK if the type is valid; TCL_ERROR otherwise.
953 *
954 * Side effects:
955 *	None.
956 *
957 *--------------------------------------------------------------
958 */
959
960int
961TclDomGetNameFromEnum(
962    int nodeType,
963    char **nodeNamePtr)
964{
965    if (nodeType < ELEMENT_NODE || nodeType > NOTATION_NODE) {
966	    *nodeNamePtr = "";
967	    return TCL_ERROR;
968    } else {
969	    *nodeNamePtr = typeName[nodeType];
970	    return TCL_OK;
971    }
972}
973
974
975/*
976 *--------------------------------------------------------------
977 *
978 * TclDomSetNodeValue --
979 *
980 *	This procedure sets the node value for a node.
981 *
982 * Results:
983 *	TDP_OK if the node is writable; otherwise returns
984 *	TDP_NO_MODIFICATION_ALLOWED
985 *
986 * Side effects:
987 *	None.
988 *
989 *--------------------------------------------------------------
990 */
991
992TdpDomError
993TclDomSetNodeValue(
994    TclDomNode *nodePtr,    /* DOM node */
995    char *value)	    /* New value for node */
996{
997    if (nodePtr->nodeType	 == ELEMENT_NODE
998	        || nodePtr->nodeType == ENTITY_REFERENCE_NODE
999	        || nodePtr->nodeType == ENTITY_NODE
1000	        || nodePtr->nodeType == DOCUMENT_NODE
1001	        || nodePtr->nodeType == DOCUMENT_TYPE_NODE
1002	        || nodePtr->nodeType == DOCUMENT_FRAGMENT_NODE
1003	        || nodePtr->nodeType == NOTATION_NODE) {
1004	    return TDP_NO_MODIFICATION_ALLOWED_ERR;
1005    }
1006
1007    if (nodePtr->nodeValue) {
1008	    ckfree(nodePtr->nodeValue);
1009    }
1010    nodePtr->valueLength = strlen(value);
1011    nodePtr->nodeValue = ckalloc(nodePtr->valueLength + 1);
1012    strcpy(nodePtr->nodeValue, value);
1013    return TDP_OK;
1014}
1015
1016
1017/*
1018 *--------------------------------------------------------------
1019 *
1020 * TclDomGetNodeName --
1021 *
1022 *	This procedure returns the name for a node.
1023 *
1024 * Results:
1025 *	TCL_OK
1026 *
1027 * Side effects:
1028 *	Name of node is placed in interpreters result.
1029 *
1030 *--------------------------------------------------------------
1031 */
1032
1033int
1034TclDomGetNodeName(
1035    Tcl_Interp *interp,	    /* Interpreter whose result is to be updated */
1036    TclDomNode *nodePtr)   /* DOM node */
1037{
1038
1039    switch (nodePtr->nodeType) {
1040	    case ELEMENT_NODE:
1041	    case ATTRIBUTE_NODE:
1042	    case ENTITY_REFERENCE_NODE:
1043	    case ENTITY_NODE:
1044	    case PROCESSING_INSTRUCTION_NODE:
1045	    case DOCUMENT_TYPE_NODE:
1046	    case NOTATION_NODE:
1047	        if (nodePtr->nodeName) {
1048		        Tcl_SetObjResult(interp,
1049						Tcl_NewStringObj(nodePtr->nodeName, -1));
1050	        }
1051	        return TCL_OK;
1052
1053	    case TEXT_NODE:
1054	        Tcl_SetResult(interp, "#text", TCL_STATIC);
1055	        return TCL_OK;
1056
1057	    case CDATA_SECTION_NODE:
1058	        Tcl_SetResult(interp, "#cdata-section", TCL_STATIC);
1059	        return TCL_OK;
1060
1061	    case COMMENT_NODE:
1062	        Tcl_SetResult(interp, "#comment", TCL_STATIC);
1063	        return TCL_OK;
1064
1065	    case DOCUMENT_NODE:
1066	        Tcl_SetResult(interp, "#document", TCL_STATIC);
1067	        return TCL_OK;
1068
1069	    case DOCUMENT_FRAGMENT_NODE:
1070	        Tcl_SetResult(interp, "#document-fragment", TCL_STATIC);
1071	        return TCL_OK;
1072
1073	    default:
1074	        return TCL_ERROR;
1075    }
1076    return TCL_OK;
1077}
1078
1079
1080/*
1081 *--------------------------------------------------------------
1082 *
1083 * CloneNode --
1084 *
1085 *	This procedure make a copy of a DOM node. If the
1086 *	"deep" flag is set then all descendants of the node
1087 *	will be copied also. See the DOM specification
1088 *	for further information.
1089 *
1090 * Results:
1091 *	A TclDom node of the appropriate type.
1092 *
1093 * Side effects:
1094 *	A sub-tree is allocated.
1095 *
1096 *--------------------------------------------------------------
1097 */
1098
1099TclDomNode *
1100CloneNode(
1101    Tcl_Interp *interp,			/* Tcl interpreter */
1102    TclDomInterpData *interpDataPtr,	/* Extension state data */
1103    TclDomNode *nodePtr,		/* Node to be cloned */
1104    TclDomDocument *containingDocumentPtr, /* Doc for clone */
1105    int deepFlag)			/* True => copy children */
1106{
1107    TclDomNode *clonedNodePtr = NULL;
1108    TclDomNode *childNodePtr, *clonedChildNodePtr;
1109    TclDomTextNode *textNodePtr, *clonedTextNodePtr;
1110    TclDomAttributeNode *attributeNodePtr, *clonedAttributeNodePtr;
1111    TclDomDocTypeNode *docTypeNodePtr, *clonedDocTypeNodePtr;
1112    int nodeId;
1113
1114    nodeId = ++interpDataPtr->nodeSeed;
1115    switch (nodePtr->nodeType) {
1116	    case ELEMENT_NODE:
1117	        clonedNodePtr = (TclDomNode *) ckalloc(sizeof(TclDomNode));
1118	        memset(clonedNodePtr, 0, sizeof(TclDomNode));
1119	        clonedNodePtr->nodeId = nodeId;
1120	        clonedNodePtr->nodeType = ELEMENT_NODE;
1121	        clonedNodePtr->containingDocumentPtr = containingDocumentPtr;
1122	        if (nodePtr->nodeName) {
1123		        clonedNodePtr->nodeName =
1124						ckalloc(strlen(nodePtr->nodeName) + 1);
1125		        strcpy(clonedNodePtr->nodeName, nodePtr->nodeName);
1126	        }
1127	        if (nodePtr->nodeValue) {
1128		        clonedNodePtr->valueLength = nodePtr->valueLength;
1129		        clonedNodePtr->nodeValue = ckalloc(nodePtr->valueLength + 1);
1130		        strcpy(clonedNodePtr->nodeValue, nodePtr->nodeValue);
1131	        }
1132	        attributeNodePtr = nodePtr->firstAttributePtr;
1133	        while (attributeNodePtr) {
1134		        clonedAttributeNodePtr =
1135						(TclDomAttributeNode *) CloneNode(interp, interpDataPtr,
1136                        (TclDomNode *) attributeNodePtr,
1137						containingDocumentPtr, 0);
1138		        if (clonedNodePtr->firstAttributePtr == NULL) {
1139		            clonedNodePtr->firstAttributePtr =
1140							clonedNodePtr->lastAttributePtr =
1141							clonedAttributeNodePtr;
1142		        } else {
1143		            clonedNodePtr->lastAttributePtr->nextSiblingPtr =
1144						    clonedAttributeNodePtr;
1145		            clonedNodePtr->lastAttributePtr = clonedAttributeNodePtr;
1146		        }
1147		        attributeNodePtr = attributeNodePtr->nextSiblingPtr;
1148	        }
1149
1150	        if (deepFlag) {
1151		        childNodePtr = nodePtr->firstChildPtr;
1152		        while (childNodePtr) {
1153		            clonedChildNodePtr =
1154							CloneNode(interp, interpDataPtr, childNodePtr,
1155                            containingDocumentPtr, 1);
1156		            if (clonedNodePtr->firstChildPtr == NULL) {
1157			            clonedNodePtr->firstChildPtr =
1158						        clonedNodePtr->lastChildPtr =
1159								clonedChildNodePtr;
1160		            } else {
1161			            clonedChildNodePtr->previousSiblingPtr =
1162								clonedNodePtr->lastChildPtr;
1163			            clonedNodePtr->lastChildPtr->nextSiblingPtr =
1164								clonedChildNodePtr;
1165			            clonedNodePtr->lastChildPtr = clonedChildNodePtr;
1166		            }
1167		            childNodePtr = childNodePtr->nextSiblingPtr;
1168		        }
1169	        }
1170	        break;
1171
1172	    case ATTRIBUTE_NODE:
1173	        attributeNodePtr = (TclDomAttributeNode *) nodePtr;
1174	        clonedAttributeNodePtr = (TclDomAttributeNode *)
1175					ckalloc(sizeof(TclDomAttributeNode));
1176	        memset(clonedAttributeNodePtr, 0, sizeof(TclDomAttributeNode));
1177	        clonedAttributeNodePtr->nodeId = nodeId;
1178	        clonedAttributeNodePtr->nodeType = ATTRIBUTE_NODE;
1179	        clonedAttributeNodePtr->containingDocumentPtr =
1180					containingDocumentPtr;
1181	        if (attributeNodePtr->nodeName) {
1182		        clonedAttributeNodePtr->nodeName =
1183						ckalloc(strlen(attributeNodePtr->nodeName) + 1);
1184		        strcpy(clonedAttributeNodePtr->nodeName,
1185						attributeNodePtr->nodeName);
1186	        }
1187	        if (attributeNodePtr->nodeValue) {
1188		        clonedAttributeNodePtr->valueLength =
1189						attributeNodePtr->valueLength;
1190		        clonedAttributeNodePtr->nodeValue =
1191						ckalloc(attributeNodePtr->valueLength + 1);
1192		        strcpy(clonedAttributeNodePtr->nodeValue,
1193						attributeNodePtr->nodeValue);
1194	        }
1195	        clonedNodePtr = (TclDomNode *) clonedAttributeNodePtr;
1196	        break;
1197
1198	    case TEXT_NODE:
1199	    case CDATA_SECTION_NODE:
1200	    case PROCESSING_INSTRUCTION_NODE:
1201	    case COMMENT_NODE:
1202	        textNodePtr = (TclDomTextNode *) nodePtr;
1203	        clonedTextNodePtr = (TclDomTextNode *)
1204					ckalloc(sizeof(TclDomTextNode));
1205	        memset(clonedTextNodePtr, 0, sizeof(TclDomTextNode));
1206	        clonedTextNodePtr->nodeId = nodeId;
1207	        clonedTextNodePtr->nodeType = textNodePtr->nodeType;
1208	        clonedTextNodePtr->containingDocumentPtr = containingDocumentPtr;
1209	        if (textNodePtr->nodeName) {
1210		        clonedTextNodePtr->nodeName =
1211						ckalloc(strlen(textNodePtr->nodeName) + 1);
1212		        strcpy(clonedTextNodePtr->nodeName, textNodePtr->nodeName);
1213	        }
1214	        if (textNodePtr->nodeValue) {
1215		        clonedTextNodePtr->valueLength = textNodePtr->valueLength;
1216		        clonedTextNodePtr->nodeValue =
1217						ckalloc(textNodePtr->valueLength + 1);
1218		        strcpy(clonedTextNodePtr->nodeValue, textNodePtr->nodeValue);
1219	        }
1220	        clonedNodePtr = (TclDomNode *) clonedTextNodePtr;
1221	        break;
1222
1223	    case ENTITY_REFERENCE_NODE:
1224	    case ENTITY_NODE:
1225	    case NOTATION_NODE:
1226	        break;
1227
1228	    case DOCUMENT_NODE:
1229            containingDocumentPtr = TclDomEmptyDocument(interp, interpDataPtr);
1230            clonedNodePtr = containingDocumentPtr->selfPtr;
1231            attributeNodePtr = nodePtr->firstAttributePtr;
1232	        while (attributeNodePtr) {
1233		        clonedAttributeNodePtr =
1234						(TclDomAttributeNode *) CloneNode(interp, interpDataPtr,
1235                        (TclDomNode *) attributeNodePtr,
1236						containingDocumentPtr, 0);
1237		        if (clonedNodePtr->firstAttributePtr == NULL) {
1238		            clonedNodePtr->firstAttributePtr =
1239							clonedNodePtr->lastAttributePtr =
1240							clonedAttributeNodePtr;
1241		        } else {
1242		            clonedNodePtr->lastAttributePtr->nextSiblingPtr =
1243						    clonedAttributeNodePtr;
1244		            clonedNodePtr->lastAttributePtr = clonedAttributeNodePtr;
1245		        }
1246		        attributeNodePtr = attributeNodePtr->nextSiblingPtr;
1247	        }
1248            if (deepFlag) {
1249		        childNodePtr = nodePtr->firstChildPtr;
1250		        while (childNodePtr) {
1251		            clonedChildNodePtr =
1252							CloneNode(interp, interpDataPtr, childNodePtr,
1253                            containingDocumentPtr, 1);
1254		            if (clonedNodePtr->firstChildPtr == NULL) {
1255			            clonedNodePtr->firstChildPtr =
1256						        clonedNodePtr->lastChildPtr =
1257								clonedChildNodePtr;
1258		            } else {
1259			            clonedChildNodePtr->previousSiblingPtr =
1260								clonedNodePtr->lastChildPtr;
1261			            clonedNodePtr->lastChildPtr->nextSiblingPtr =
1262								clonedChildNodePtr;
1263			            clonedNodePtr->lastChildPtr = clonedChildNodePtr;
1264		            }
1265		            childNodePtr = childNodePtr->nextSiblingPtr;
1266		        }
1267	        }
1268            break;
1269
1270	    case DOCUMENT_TYPE_NODE:
1271            docTypeNodePtr = (TclDomDocTypeNode *) nodePtr;
1272	        clonedDocTypeNodePtr = (TclDomDocTypeNode *)
1273					ckalloc(sizeof(TclDomDocTypeNode));
1274	        memset(clonedDocTypeNodePtr, 0, sizeof(TclDomDocTypeNode));
1275	        clonedDocTypeNodePtr->nodeId = nodeId;
1276	        clonedDocTypeNodePtr->nodeType = docTypeNodePtr->nodeType;
1277	        clonedDocTypeNodePtr->containingDocumentPtr = containingDocumentPtr;
1278	        if (docTypeNodePtr->nodeName) {
1279		        clonedDocTypeNodePtr->nodeName =
1280						ckalloc(strlen(docTypeNodePtr->nodeName) + 1);
1281		        strcpy(clonedDocTypeNodePtr->nodeName,
1282						docTypeNodePtr->nodeName);
1283	        }
1284	        if (docTypeNodePtr->nodeValue) {
1285		        clonedDocTypeNodePtr->valueLength = docTypeNodePtr->valueLength;
1286		        clonedDocTypeNodePtr->nodeValue =
1287						ckalloc(docTypeNodePtr->valueLength + 1);
1288		        strcpy(clonedDocTypeNodePtr->nodeValue,
1289						docTypeNodePtr->nodeValue);
1290	        }
1291	        clonedNodePtr = (TclDomNode *) clonedDocTypeNodePtr;
1292	        break;
1293
1294	    case DOCUMENT_FRAGMENT_NODE:
1295	        break;
1296
1297	    default:
1298	        break;
1299    }
1300    return clonedNodePtr;
1301}
1302
1303
1304/*
1305 *--------------------------------------------------------------
1306 *
1307 * TclDomCloneNode --
1308 *
1309 *	This clones a DOM node and returns a handle in the
1310 *	interpreter's result. If the node is a document then
1311 *	a new document is created; otherwise the node is
1312 *	added to the current documents list of fragments.
1313 *
1314 * Results:
1315 *	Returns TCL_OK, or TCL_ERROR (shouldn't happen) if
1316 *	an internal error occurs.
1317 *
1318 * Side effects:
1319 *	Allocates a token for the new node.
1320 *
1321 *--------------------------------------------------------------
1322 */
1323
1324int
1325TclDomCloneNode(
1326    Tcl_Interp *interp,			/* Tcl interpreter */
1327    TclDomInterpData *interpDataPtr,	/* Extension state data */
1328    TclDomNode *nodePtr,		/* Node to be copied */
1329    int deepFlag)			/* True => copy children */
1330{
1331    TclDomNode *clonedNodePtr;
1332
1333    clonedNodePtr = CloneNode(interp, interpDataPtr, nodePtr,
1334            nodePtr->containingDocumentPtr, deepFlag);
1335    if (clonedNodePtr) {
1336	    if (clonedNodePtr->nodeType != DOCUMENT_NODE) {
1337	        /*
1338	         * Add the clone to the fragments list
1339	         */
1340
1341	        if (nodePtr->containingDocumentPtr->fragmentsPtr) {
1342		        clonedNodePtr->nextSiblingPtr =
1343		    		    nodePtr->containingDocumentPtr->fragmentsPtr;
1344		        nodePtr->containingDocumentPtr->fragmentsPtr->previousSiblingPtr =
1345		    		    clonedNodePtr;
1346		        nodePtr->containingDocumentPtr->fragmentsPtr = clonedNodePtr;
1347	        } else {
1348		        nodePtr->containingDocumentPtr->fragmentsPtr = clonedNodePtr;
1349	        }
1350	    }
1351	    TclDomSetNodeResult(interp, interpDataPtr, clonedNodePtr);
1352    }
1353
1354    return TCL_OK;
1355}
1356
1357
1358/*
1359 *--------------------------------------------------------------
1360 *
1361 * TclDomImportNode --
1362 *
1363 *	This procedure imports a node from one document to
1364 *  another. The source node is not altered or removed
1365 *  from the original document; this method creates
1366 *  a new copy of the source node.
1367 *
1368 *
1369 * Results:
1370 *	Returns TCL_OK, or TCL_ERROR if the node is of a type
1371 *  that can't be imported.
1372 *
1373 * Side effects:
1374 *	Allocates a token for the new node.
1375 *
1376 *--------------------------------------------------------------
1377 */
1378
1379TclDomNode *
1380TclDomImportNode(
1381    Tcl_Interp *interp,			/* Tcl interpreter */
1382    TclDomInterpData *interpDataPtr,	/* Extension state data */
1383    TclDomDocument *documentPtr, /* Document copied into */
1384    TclDomNode *nodePtr,		/* Node to be copied */
1385    int deepFlag)			/* True => copy children */
1386{
1387    TclDomNode *clonedNodePtr;
1388
1389    if (nodePtr->nodeType == DOCUMENT_NODE
1390            || nodePtr->nodeType == DOCUMENT_TYPE_NODE) {
1391        Tcl_AppendResult(interp, NOT_SUPPORTED_ERR_TEXT, (char *) NULL);
1392        return NULL;
1393    }
1394
1395    clonedNodePtr = CloneNode(interp, interpDataPtr, nodePtr,
1396            documentPtr, deepFlag);
1397    if (clonedNodePtr) {
1398	    /*
1399	     * Add the clone to the fragments list
1400	     */
1401
1402	    if (documentPtr->fragmentsPtr) {
1403		    clonedNodePtr->nextSiblingPtr = documentPtr->fragmentsPtr;
1404		    documentPtr->fragmentsPtr->previousSiblingPtr = clonedNodePtr;
1405		    documentPtr->fragmentsPtr = clonedNodePtr;
1406	    } else {
1407		    documentPtr->fragmentsPtr = clonedNodePtr;
1408	    }
1409	    TclDomSetNodeResult(interp, interpDataPtr, clonedNodePtr);
1410    }
1411    return clonedNodePtr;
1412}
1413
1414
1415/*
1416 *--------------------------------------------------------------
1417 *
1418 * TclDomRemoveAttribute --
1419 *
1420 *	This procedure removes an attribute from an Element's
1421 *	list of attributes. Implements the Element
1422 *	"removeAttribute" method. See the DOM specification
1423 *	for further information.
1424 *
1425 * Results:
1426 *	Returns TCL_OK; if the attribute was not found,
1427 *	then TCL_ERROR is returned and a string corresponding
1428 *	to NOT_FOUND_ERR is written to the interpreter's result.
1429 *
1430 * Side effects:
1431 *	None.
1432 *
1433 *--------------------------------------------------------------
1434 */
1435
1436int
1437TclDomRemoveAttribute(
1438    Tcl_Interp *interp,			/* Tcl interpreter */
1439    TclDomInterpData *interpDataPtr,	/* Extension state data */
1440    TclDomNode *nodePtr,		/* Node from which to remove attribute */
1441    char *name)				/* Attribute name */
1442{
1443    TclDomAttributeNode *attributeNodePtr, *previousPtr = NULL;
1444
1445    /*
1446     * XXX
1447     *
1448     * Need to do something about default value!
1449     *
1450     */
1451
1452    attributeNodePtr = nodePtr->firstAttributePtr;
1453
1454    while (attributeNodePtr && strcmp(attributeNodePtr->nodeName, name)) {
1455	    previousPtr = attributeNodePtr;
1456	    attributeNodePtr = attributeNodePtr->nextSiblingPtr;
1457    }
1458
1459    if (attributeNodePtr) {
1460	    if (previousPtr) {
1461	        previousPtr->nextSiblingPtr = attributeNodePtr->nextSiblingPtr;
1462	    } else {
1463	        nodePtr->firstAttributePtr = attributeNodePtr->nextSiblingPtr;
1464	    }
1465	    if (attributeNodePtr->nextSiblingPtr == NULL) {
1466	        nodePtr->lastAttributePtr = previousPtr;
1467	    }
1468	    RemoveAttributeFromArray(interp, interpDataPtr, nodePtr,
1469				attributeNodePtr);
1470	    TclDomDeleteNode(interp, interpDataPtr,
1471				(TclDomNode *) attributeNodePtr);
1472    }
1473    return TCL_OK;
1474}
1475
1476
1477/*
1478 *--------------------------------------------------------------
1479 *
1480 * TclDomSetAttribute --
1481 *
1482 *	This procedure adds an attribute to an Element's
1483 *	list of attributes. Implements the Element
1484 *	"setAttribute" method. See the DOM specification
1485 *	for further information.
1486 *
1487 * Results:
1488 *	Returns TCL_OK; if the attribute name is invalid,
1489 *	the returns TCL_ERROR and writes a string
1490 *	corresponding to INVALID_CHARACTER_ERR to the
1491 *	interpreter's result.
1492 *
1493 * Side effects:
1494 *	None.
1495 *
1496 *--------------------------------------------------------------
1497 */
1498
1499int
1500TclDomSetAttribute(
1501    Tcl_Interp *interp,			/* Tcl interpreter */
1502    TclDomInterpData *interpDataPtr,	/* Extension state data */
1503    TclDomNode *nodePtr,		/* Node for which attribute is to be set */
1504    char *name,				/* Attribute's name */
1505    char *value)			/* Attribute's value */
1506{
1507    TclDomAttributeNode *attributeNodePtr;
1508
1509    attributeNodePtr = nodePtr->firstAttributePtr;
1510
1511    while (attributeNodePtr && strcmp(attributeNodePtr->nodeName, name)) {
1512	    attributeNodePtr = attributeNodePtr->nextSiblingPtr;
1513    }
1514
1515    if (attributeNodePtr) {
1516	    ckfree(attributeNodePtr->nodeValue);
1517	    attributeNodePtr->valueLength = strlen(value);
1518	    attributeNodePtr->nodeValue =
1519				ckalloc(attributeNodePtr->valueLength + 1);
1520	    strcpy(attributeNodePtr->nodeValue, value);
1521    } else {
1522	    attributeNodePtr = (TclDomAttributeNode *)
1523				ckalloc(sizeof(TclDomAttributeNode));
1524	    memset(attributeNodePtr, 0, sizeof(TclDomAttributeNode));
1525	    attributeNodePtr->nodeName = ckalloc(strlen(name) + 1);
1526	    strcpy(attributeNodePtr->nodeName, name);
1527	    attributeNodePtr->parentNodePtr = nodePtr;
1528	    attributeNodePtr->valueLength = strlen(value);
1529	    attributeNodePtr->nodeValue =
1530				ckalloc(attributeNodePtr->valueLength + 1);
1531	    strcpy(attributeNodePtr->nodeValue, value);
1532
1533	    if (nodePtr->firstAttributePtr) {
1534	        nodePtr->lastAttributePtr->nextSiblingPtr = attributeNodePtr;
1535	        nodePtr->lastAttributePtr = attributeNodePtr;
1536	    } else {
1537	        nodePtr->firstAttributePtr = nodePtr->lastAttributePtr =
1538					attributeNodePtr;
1539	    }
1540    }
1541    SetAttributeInArray(interp, interpDataPtr, nodePtr, attributeNodePtr);
1542    return TCL_OK;
1543}
1544
1545/*
1546 *--------------------------------------------------------------
1547 *
1548 * TclDomValidateChildType --
1549 *
1550 *	This procedure determines whether a node may legally
1551 *	be appended to another as a child.
1552 *
1553 * Results:
1554 *	Returns TCL_OK if the child's type is valid; otherwise
1555 *	returns TCL_ERROR and writes an appropriate error
1556 *	message to the interpreter's result.
1557 *
1558 * Side effects:
1559 *	None.
1560 *
1561 *--------------------------------------------------------------
1562 */
1563
1564int
1565TclDomValidateChildType(
1566    Tcl_Interp *interp,		/* Tcl interpreter */
1567    TclDomNode *nodePtr,	/* Node to receive child */
1568    TclDomNode *childPtr)	/* Child node to be added */
1569{
1570    /*
1571     * Handle DocumentFragment as a special case -- validate the children of the
1572     * DocumentFragment
1573     */
1574
1575    if (childPtr->nodeType == DOCUMENT_FRAGMENT_NODE) {
1576	    TclDomNode *tempNodePtr = childPtr->firstChildPtr;
1577	    while (tempNodePtr) {
1578	        if (TclDomValidateChildType(interp, nodePtr, tempNodePtr)
1579					!= TCL_OK) {
1580		        return TCL_ERROR;
1581	        }
1582	        tempNodePtr = tempNodePtr->nextSiblingPtr;
1583	    }
1584	    return TCL_OK;
1585    }
1586
1587    switch (nodePtr->nodeType) {
1588	    case ELEMENT_NODE:
1589	        if (childPtr->nodeType != ELEMENT_NODE
1590		            && childPtr->nodeType != TEXT_NODE
1591		            && childPtr->nodeType != COMMENT_NODE
1592		            && childPtr->nodeType != PROCESSING_INSTRUCTION_NODE
1593		            && childPtr->nodeType != CDATA_SECTION_NODE
1594		            && childPtr->nodeType != ENTITY_REFERENCE_NODE) {
1595		        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT,
1596						(char *) NULL);
1597		        return TCL_ERROR;
1598	        }
1599	        break;
1600
1601	    case ATTRIBUTE_NODE:
1602	        if (childPtr->nodeType != TEXT_NODE
1603		            && childPtr->nodeType != ENTITY_REFERENCE_NODE) {
1604		        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT,
1605						(char *) NULL);
1606		        return TCL_ERROR;
1607	        }
1608	        break;
1609
1610	    case TEXT_NODE:
1611	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
1612	        return TCL_ERROR;
1613	        break;
1614
1615	    case CDATA_SECTION_NODE:
1616	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
1617	        return TCL_ERROR;
1618	        break;
1619
1620	    case ENTITY_REFERENCE_NODE:
1621	        if (childPtr->nodeType != ELEMENT_NODE
1622		            && childPtr->nodeType != TEXT_NODE
1623		            && childPtr->nodeType != COMMENT_NODE
1624		            && childPtr->nodeType != PROCESSING_INSTRUCTION_NODE
1625		            && childPtr->nodeType != CDATA_SECTION_NODE
1626		            && childPtr->nodeType != ENTITY_REFERENCE_NODE) {
1627		        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT,
1628						(char *) NULL);
1629		        return TCL_ERROR;
1630	        }
1631	        break;
1632
1633	    case ENTITY_NODE:
1634	        if (childPtr->nodeType != ELEMENT_NODE
1635		            && childPtr->nodeType != TEXT_NODE
1636		            && childPtr->nodeType != COMMENT_NODE
1637		            && childPtr->nodeType != PROCESSING_INSTRUCTION_NODE
1638		            && childPtr->nodeType != CDATA_SECTION_NODE
1639		            && childPtr->nodeType != ENTITY_REFERENCE_NODE) {
1640		        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT,
1641						(char *) NULL);
1642		        return TCL_ERROR;
1643	        }
1644	        break;
1645
1646	    case PROCESSING_INSTRUCTION_NODE:
1647	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
1648	        return TCL_ERROR;
1649	        break;
1650
1651	    case COMMENT_NODE:
1652	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
1653	        return TCL_ERROR;
1654	        break;
1655
1656	    case DOCUMENT_NODE:
1657	        if (childPtr->nodeType != ELEMENT_NODE
1658		            && childPtr->nodeType != COMMENT_NODE
1659		            && childPtr->nodeType != PROCESSING_INSTRUCTION_NODE
1660		            && childPtr->nodeType != DOCUMENT_TYPE_NODE) {
1661		        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT,
1662						(char *) NULL);
1663		        return TCL_ERROR;
1664	        }
1665
1666	        /*
1667	         * Allow only one child that is an element
1668		 * It is however legal to re-insert the root element.
1669	         */
1670
1671	        if (childPtr->nodeType == ELEMENT_NODE) {
1672		        TclDomNode *tempNodePtr = nodePtr->firstChildPtr;
1673		        while (tempNodePtr) {
1674		            if ( tempNodePtr->nodeType == ELEMENT_NODE
1675			         && tempNodePtr != childPtr)
1676			    {
1677				Tcl_AppendResult(interp,
1678					HIERARCHY_REQUEST_ERR_TEXT,
1679					(char *) NULL);
1680			            return TCL_ERROR;
1681		            }
1682		            tempNodePtr = tempNodePtr->nextSiblingPtr;
1683		        }
1684	        }
1685	        break;
1686
1687	    case DOCUMENT_TYPE_NODE:
1688	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
1689	        return TCL_ERROR;
1690	        break;
1691
1692	    case DOCUMENT_FRAGMENT_NODE:
1693	        if (childPtr->nodeType != ELEMENT_NODE
1694		            && childPtr->nodeType != TEXT_NODE
1695		            && childPtr->nodeType != COMMENT_NODE
1696		            && childPtr->nodeType != PROCESSING_INSTRUCTION_NODE
1697		            && childPtr->nodeType != CDATA_SECTION_NODE
1698		            && childPtr->nodeType != ENTITY_REFERENCE_NODE) {
1699		        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT,
1700						(char *) NULL);
1701		        return TCL_ERROR;
1702	        }
1703	        break;
1704
1705
1706	    case NOTATION_NODE:
1707	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
1708	        return TCL_ERROR;
1709	        break;
1710
1711	    default:
1712	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
1713	        return TCL_ERROR;
1714    }
1715    return TCL_OK;
1716}
1717
1718
1719/*
1720 *--------------------------------------------------------------
1721 *
1722 * GetUniqueListVariableName --
1723 *
1724 *	Returns a unique name that may be used to
1725 *	represent a DOM live NodeList or NodeMap.
1726 *
1727 * Results:
1728 *	A Tcl_Obj whose string value is globally unique.
1729 *
1730 * Side effects:
1731 *	None.
1732 *
1733 *--------------------------------------------------------------
1734 */
1735
1736Tcl_Obj *
1737GetUniqueListVariableName(
1738    Tcl_Interp *interp,	 /* Interpreter in which Tcl variable will be created */
1739    char *prefix,		    /* Fixed prefix for name */
1740    int createFlag)		    /* True => implies set variable value to null. */
1741{
1742    char *nameString;
1743    int seed = 0;
1744    int nameLength;
1745    const char *value;
1746    Tcl_Obj *objNamePtr;
1747    Tcl_Obj *listObjPtr;
1748
1749    nameLength = strlen(prefix) + strlen(NAMESPACE_PREFIX) + 64;
1750
1751    nameString = ckalloc(nameLength);
1752
1753    sprintf(nameString, "%s_%s", NAMESPACE_PREFIX, prefix);
1754    while (1) {
1755	    value = Tcl_GetVar(interp, nameString, 0);
1756	    if (value == NULL) {
1757	        break;
1758	    }
1759	    sprintf(nameString, "%s_%s_%d", NAMESPACE_PREFIX, prefix, seed);
1760	    seed++;
1761    }
1762
1763    objNamePtr = Tcl_NewStringObj(nameString, -1);
1764    ckfree(nameString);
1765    if (createFlag) {
1766	    listObjPtr = Tcl_NewListObj(0, NULL);
1767	    Tcl_ObjSetVar2(interp, objNamePtr, NULL, listObjPtr, 0);
1768    }
1769
1770    return objNamePtr;
1771}
1772
1773
1774/*
1775 *--------------------------------------------------------------
1776 *
1777 * TclDomUpdateChildNodeList --
1778 *
1779 *	This procedure updates the global Tcl variable
1780 *	whose value is the list of children of an node.
1781 *
1782 * Results:
1783 *	None.
1784 *
1785 * Side effects:
1786 *	Modifies a Tcl global variable.
1787 *
1788 *--------------------------------------------------------------
1789 */
1790
1791void
1792TclDomUpdateChildNodeList(
1793    Tcl_Interp *interp,			/* Tcl interpreter */
1794    TclDomInterpData *interpDataPtr,	/* Extension state data */
1795    TclDomNode *nodePtr)		/* Node for which child list is to be updated */
1796{
1797    if ((nodePtr->nodeType == ELEMENT_NODE
1798	        || nodePtr->nodeType == DOCUMENT_NODE
1799	        || nodePtr->nodeType == DOCUMENT_FRAGMENT_NODE)
1800			&& nodePtr->childNodeListVarName) {
1801	    Tcl_Obj *newListPtr, *tokenPtr;
1802	    TclDomNode *childPtr;
1803	    int result;
1804
1805	    newListPtr = Tcl_NewListObj(0, NULL);
1806	    childPtr = nodePtr->firstChildPtr;
1807	    while (childPtr) {
1808	        tokenPtr = TclDomGetNodeObj(interpDataPtr, childPtr);
1809	        result = Tcl_ListObjAppendElement(interp, newListPtr, tokenPtr);
1810	        if (result != TCL_OK) {
1811		        Tcl_DecrRefCount(tokenPtr);
1812		        return;
1813	        }
1814	        childPtr = childPtr->nextSiblingPtr;
1815	    }
1816	    Tcl_ObjSetVar2(interp, nodePtr->childNodeListVarName, NULL,
1817				newListPtr, 0);
1818    }
1819}
1820
1821
1822/*
1823 *--------------------------------------------------------------
1824 *
1825 * TclDomGetChildNodeList --
1826 *
1827 *	This procedure creates a global Tcl variable whose
1828 *	value will be the list of children of the specified
1829 *	node. The variable is "live", and implements the
1830 *	NodeList object in the DOM specification.
1831 *
1832 * Results:
1833 *	None.
1834 *
1835 * Side effects:
1836 *	Creates or modifies a Tcl global variable.
1837 *
1838 *--------------------------------------------------------------
1839 */
1840
1841int
1842TclDomGetChildNodeList(
1843    Tcl_Interp *interp,			/* Tcl interpreter */
1844    TclDomInterpData *interpDataPtr,	/* Extension state data */
1845    TclDomNode *nodePtr)		/* Node for which to get child list */
1846{
1847    if (nodePtr->nodeType != ELEMENT_NODE && nodePtr->nodeType != DOCUMENT_NODE
1848	        && nodePtr->nodeType != DOCUMENT_FRAGMENT_NODE) {
1849	    /*
1850	     * This node type has no children;
1851	     * return a variable whose value is an invariant  list.
1852	     */
1853	    if (interpDataPtr->nullNodeListVarName == NULL) {
1854#ifdef UNDEF
1855	        char prefix[64];
1856	        sprintf(prefix, "doc%dEmptyList",
1857		            nodePtr->containingDocumentPtr->selfPtr->nodeId);
1858	        interpDataPtr->nullNodeListVarName =
1859			        GetUniqueListVariableName(interp, prefix, 1);
1860#endif
1861	        interpDataPtr->nullNodeListVarName =
1862			        GetUniqueListVariableName(interp, "emptyList", 1);
1863	        Tcl_IncrRefCount(interpDataPtr->nullNodeListVarName);
1864	    }
1865	    Tcl_ObjSetVar2(interp, interpDataPtr->nullNodeListVarName, NULL,
1866	            Tcl_NewStringObj("", -1), 0);
1867	    Tcl_SetObjResult(interp, interpDataPtr->nullNodeListVarName);
1868	    return TCL_OK;
1869    } else {
1870	    if (nodePtr->childNodeListVarName == NULL) {
1871	        char prefix[64];
1872	        sprintf(prefix, "node%dChildList", nodePtr->nodeId);
1873	        nodePtr->childNodeListVarName = GetUniqueListVariableName(interp,
1874			        prefix, 1);
1875	        Tcl_IncrRefCount(nodePtr->childNodeListVarName);
1876	    }
1877	    TclDomUpdateChildNodeList(interp, interpDataPtr, nodePtr);
1878	    Tcl_SetObjResult(interp, nodePtr->childNodeListVarName);
1879	    return TCL_OK;
1880    }
1881}
1882
1883
1884/*
1885 *--------------------------------------------------------------
1886 *
1887 * TclDomGetChildren --
1888 *
1889 *	Returns a List object containing the children
1890 *	of the specified node.
1891 *
1892 *	This value is *not* "live"; it is used to implement the
1893 *	(nonstandard) dom::node children method.
1894 *
1895 * Results:
1896 *	The new Tcl_ListObj, or NULL if there was an error.
1897 *
1898 * Side effects:
1899 *	None.
1900 *
1901 *--------------------------------------------------------------
1902 */
1903
1904Tcl_Obj *
1905TclDomGetChildren(
1906    Tcl_Interp *interp,			/* Tcl interpreter */
1907    TclDomInterpData *interpDataPtr,	/* Extension state data */
1908    TclDomNode *nodePtr)		/* Node for which to get child list */
1909{
1910    Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
1911    TclDomNode *childPtr =
1912    	  TclDomHasChildren(nodePtr) ? nodePtr->firstChildPtr : NULL;
1913    Tcl_Obj *childObj = NULL;
1914
1915    while (childPtr != NULL)
1916    {
1917	childObj = TclDomGetNodeObj(interpDataPtr, childPtr);
1918	if (Tcl_ListObjAppendElement(interp, listObj, childObj)
1919		!= TCL_OK) goto error;
1920	childObj = NULL;
1921	childPtr = childPtr->nextSiblingPtr;
1922    }
1923    return listObj;
1924error:
1925    if (childObj) Tcl_DecrRefCount(childObj);
1926    Tcl_DecrRefCount(listObj);
1927    return NULL;
1928}
1929
1930/*
1931 *--------------------------------------------------------------
1932 *
1933 * RemoveAttributeFromArray --
1934 *
1935 *	This procedure removes an attribute from the Tcl
1936 *	array variable that represents the NamedNodeMap of
1937 *	attributes of a node. Implements the Node "attributes"
1938 *	attribute.
1939 *
1940 * Results:
1941 *	None.
1942 *
1943 * Side effects:
1944 *	Modifies a Tcl global variable.
1945 *
1946 *--------------------------------------------------------------
1947 */
1948
1949static void
1950RemoveAttributeFromArray(
1951    Tcl_Interp *interp,			    /* Tcl interpreter */
1952    TclDomInterpData *interpDataPtr,	    /* Extension state data */
1953    TclDomNode *nodePtr,		    /* Node from which attribute was removed */
1954    TclDomAttributeNode *attributeNodePtr)  /* Removed attribute */
1955{
1956    if (nodePtr->attributeArrayVarName) {
1957	    char *arrayName = Tcl_GetStringFromObj(nodePtr->attributeArrayVarName,
1958				NULL);
1959	    Tcl_UnsetVar2(interp, arrayName, attributeNodePtr->nodeName, 0);
1960    }
1961}
1962
1963
1964/*
1965 *--------------------------------------------------------------
1966 *
1967 * SetAttributeInArray --
1968 *
1969 *	This procedure updates an attribute in the Tcl
1970 *	array variable that represents the NamedNodeMap of
1971 *	attributes of a node. Implements the Node "attributes"
1972 *	attribute.
1973 *
1974 * Results:
1975 *	None.
1976 *
1977 * Side effects:
1978 *	Modifies a Tcl global variable.
1979 *
1980 *--------------------------------------------------------------
1981 */
1982
1983static void
1984SetAttributeInArray(
1985    Tcl_Interp *interp,			    /* Tcl interpreter */
1986    TclDomInterpData *interpDataPtr,	    /* Extension state data */
1987    TclDomNode *nodePtr,		    /* Element node containing attribute */
1988    TclDomAttributeNode *attributeNodePtr)  /* Attribute node */
1989{
1990    Tcl_Obj *nameObjPtr;
1991    Tcl_Obj *valueObjPtr;
1992    if (nodePtr->attributeArrayVarName) {
1993	    nameObjPtr = Tcl_NewStringObj(attributeNodePtr->nodeName, -1);
1994	    valueObjPtr = Tcl_NewStringObj(attributeNodePtr->nodeValue, -1);
1995	    Tcl_ObjSetVar2(interp, nodePtr->attributeArrayVarName, nameObjPtr,
1996				valueObjPtr, 0);
1997    }
1998}
1999
2000
2001/*
2002 *--------------------------------------------------------------
2003 *
2004 * InitializeAttributeArray --
2005 *
2006 *	This procedure initializes attributes in the Tcl
2007 *	array variable that represents the NamedNodeMap of
2008 *	attributes of a node. Implements the Node "attributes"
2009 *	attribute.
2010 *
2011 * Results:
2012 *	None.
2013 *
2014 * Side effects:
2015 *	Modifies a Tcl global variable.
2016 *
2017 *--------------------------------------------------------------
2018 */
2019
2020static void
2021InitializeAttributeArray(
2022    Tcl_Interp *interp,			/* Tcl interpreter */
2023    TclDomInterpData *interpDataPtr,	/* Extension state data */
2024    TclDomNode *nodePtr)		/* Node containing attributes */
2025{
2026    if (nodePtr->nodeType == ELEMENT_NODE && nodePtr->attributeArrayVarName) {
2027	    TclDomAttributeNode *attributeNodePtr;
2028	    Tcl_Obj *nameObjPtr;
2029	    Tcl_Obj *valueObjPtr;
2030
2031	    attributeNodePtr = nodePtr->firstAttributePtr;
2032
2033	    while (attributeNodePtr) {
2034	        nameObjPtr = Tcl_NewStringObj(attributeNodePtr->nodeName, -1);
2035	        valueObjPtr = Tcl_NewStringObj(attributeNodePtr->nodeValue, -1);
2036
2037	        Tcl_ObjSetVar2(interp, nodePtr->attributeArrayVarName, nameObjPtr,
2038					valueObjPtr, 0);
2039	        attributeNodePtr = attributeNodePtr->nextSiblingPtr;
2040	    }
2041    }
2042}
2043
2044
2045/*
2046 *--------------------------------------------------------------
2047 *
2048 * TclDomAttributeArray --
2049 *
2050 *	This procedure creates and initializes a Tcl global
2051 *	array variable that represents the attributes of a
2052 *	node.  Implements the Node "attributes" attribute. See
2053 *	the DOM specification for futher information.
2054 *
2055 * Results:
2056 *	None.
2057 *
2058 * Side effects:
2059 *	Creates if needed, and then modifies a Tcl global
2060 *	variable.
2061 *
2062 *--------------------------------------------------------------
2063 */
2064
2065int
2066TclDomAttributeArray(
2067    Tcl_Interp *interp,			/* Tcl interpreter */
2068    TclDomInterpData *interpDataPtr,	/* Extension state data */
2069    TclDomNode *nodePtr)		/* Element node containing attributes */
2070{
2071    if (nodePtr->nodeType != ELEMENT_NODE) {
2072	    /*
2073	     * Just return null
2074	     */
2075
2076	    return TCL_OK;
2077    } else {
2078	    if (nodePtr->attributeArrayVarName == NULL) {
2079	        char prefix[128];
2080	        sprintf(prefix, "node%dAttributes", nodePtr->nodeId);
2081	        nodePtr->attributeArrayVarName =
2082					GetUniqueListVariableName(interp, prefix, 0);
2083	        Tcl_IncrRefCount(nodePtr->attributeArrayVarName);
2084	    }
2085	    InitializeAttributeArray(interp, interpDataPtr, nodePtr);
2086	    Tcl_SetObjResult(interp, nodePtr->attributeArrayVarName);
2087	    return TCL_OK;
2088    }
2089}
2090
2091
2092/*
2093 *--------------------------------------------------------------
2094 *
2095 * TclDomAppendChild
2096 *
2097 *	This procedure appends a child node to a nodes list
2098 *	of children. Implements the Node "appendChild"
2099 *	method.	 See the DOM specification for further
2100 *	information.
2101 *
2102 * Results:
2103 *	Returns TCL_OK, or TCL_ERROR if the node can't be added.
2104 *
2105 * Side effects:
2106 *	Will set the interpreter's result to appropriate error
2107 *	text if the action fails. May update the list
2108 *	of document fragments. Updates the parents NodeList
2109 *	if it exists.
2110 *
2111 *--------------------------------------------------------------
2112 */
2113
2114int
2115TclDomAppendChild(
2116    Tcl_Interp *interp,			/* Tcl interpreter */
2117    TclDomInterpData *interpDataPtr,	/* Extension state data */
2118    TclDomNode *nodePtr,		/* Node to append child to */
2119    TclDomNode *childPtr)		/* Child to be appended */
2120{
2121    TclDomNode *tempNodePtr;
2122    int isFragment;
2123
2124    if (nodePtr->containingDocumentPtr != childPtr->containingDocumentPtr) {
2125	    Tcl_AppendResult(interp, WRONG_DOCUMENT_ERR_TEXT, (char *) NULL);
2126	    return TCL_ERROR;
2127    }
2128
2129    if (childPtr->nodeType == DOCUMENT_FRAGMENT_NODE) {
2130	    TclDomNode *fragmentChildPtr = childPtr->firstChildPtr;
2131	    TclDomNode *nextChildPtr;
2132	    while (fragmentChildPtr) {
2133	        /*
2134	         * Need to pick up "nextSiblingPtr" now, as it will be trashed
2135		 * in TclDomAppendChild
2136	         */
2137	        nextChildPtr = fragmentChildPtr->nextSiblingPtr;
2138	        if (TclDomAppendChild(interp, interpDataPtr, nodePtr,
2139				fragmentChildPtr) != TCL_OK) {
2140		        return TCL_ERROR;
2141	        }
2142	        fragmentChildPtr = nextChildPtr;
2143	        /*
2144	         * Need to keep "childPtr->firstChildPtr" valid in case
2145		 * TclDomAppendChild fails for some reason
2146	         */
2147	        childPtr->firstChildPtr = fragmentChildPtr;
2148	    }
2149	    childPtr->lastChildPtr = NULL;
2150	    UnlinkDocumentFragment(nodePtr->containingDocumentPtr, childPtr);
2151	    TclDomDeleteNode(interp, interpDataPtr, childPtr);
2152	    return TCL_OK;
2153    }
2154
2155    /*
2156     * Check that node to be appended is not an ancestor of the node
2157     */
2158
2159    tempNodePtr = nodePtr;
2160    while (tempNodePtr) {
2161	    if (tempNodePtr->parentNodePtr == childPtr) {
2162	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
2163	        return TCL_ERROR;
2164	    }
2165	    tempNodePtr = tempNodePtr->parentNodePtr;
2166    }
2167
2168    /*
2169     * If the child is in the fragment list, remove it
2170     *
2171     */
2172
2173    isFragment = UnlinkDocumentFragment(nodePtr->containingDocumentPtr,
2174		childPtr);
2175
2176    if (isFragment == 0) {
2177	    /*
2178	     * Remove child from document tree
2179	     */
2180
2181	    UnlinkChild(interpDataPtr, childPtr);
2182    }
2183
2184    /*
2185     * Finally, append the child
2186     */
2187
2188    if (nodePtr->lastChildPtr) {
2189	    nodePtr->lastChildPtr->nextSiblingPtr = childPtr;
2190	    childPtr->previousSiblingPtr = nodePtr->lastChildPtr;
2191    } else {
2192	    nodePtr->firstChildPtr = childPtr;
2193	    childPtr->previousSiblingPtr = NULL;
2194    }
2195    nodePtr->lastChildPtr = childPtr;
2196    childPtr->nextSiblingPtr = NULL;
2197    childPtr->parentNodePtr = nodePtr;
2198
2199    /*
2200     * Update the "live" list
2201     */
2202
2203    TclDomUpdateChildNodeList(interp, interpDataPtr, nodePtr);
2204    return TCL_OK;
2205}
2206
2207
2208/*
2209 *--------------------------------------------------------------
2210 *
2211 * TclDomInsertBefore
2212 *
2213 *	This procedure inserts a child node in nodes list
2214 *	of children. Implements the Node "insertBefore"
2215 *	method.	 See the DOM specification for further
2216 *	information.
2217 *
2218 * Results:
2219 *	Returns TCL_OK, or TCL_ERROR if the node can't be added.
2220 *
2221 * Side effects:
2222 *	Will set the interpreter's result to appropriate error
2223 *	text if the action fails. May update the list
2224 *	of document fragments. Updates the parents NodeList
2225 *	if it exists.
2226 *
2227 *--------------------------------------------------------------
2228 */
2229
2230int
2231TclDomInsertBefore(
2232    Tcl_Interp *interp,			/* Tcl interpreter */
2233    TclDomInterpData *interpDataPtr,	/* Extension state data */
2234    TclDomNode *nodePtr,		/* Node in which to insert child */
2235    TclDomNode *childPtr,		/* Child to be inserted */
2236    TclDomNode *refChildPtr)		/* Insert child before this node */
2237{
2238    TclDomNode *tempNodePtr, *locationPtr;
2239    int isFragment;
2240
2241    if (nodePtr->containingDocumentPtr != childPtr->containingDocumentPtr) {
2242	    Tcl_AppendResult(interp, WRONG_DOCUMENT_ERR_TEXT, (char *) NULL);
2243	    return TCL_ERROR;
2244    }
2245
2246    if (childPtr->nodeType == DOCUMENT_FRAGMENT_NODE) {
2247	    TclDomNode *fragmentChildPtr = childPtr->firstChildPtr;
2248	    TclDomNode *nextChildPtr;
2249	    while (fragmentChildPtr) {
2250	        /*
2251	         * Need to pick up "nextSiblingPtr" now, as it will be trashed in
2252			 * TclDomAppendChild
2253	         */
2254	        nextChildPtr = fragmentChildPtr->nextSiblingPtr;
2255	        if (TclDomInsertBefore(interp, interpDataPtr, nodePtr,
2256			        fragmentChildPtr, refChildPtr) != TCL_OK) {
2257		        return TCL_ERROR;
2258	        }
2259	        fragmentChildPtr = nextChildPtr;
2260	        /*
2261	         * Need to keep "childPtr->firstChildPtr" valid in case
2262			 * TclDomAppendChild fails for some reason
2263	         */
2264	        childPtr->firstChildPtr = fragmentChildPtr;
2265	    }
2266	    childPtr->lastChildPtr = NULL;
2267	    UnlinkDocumentFragment(nodePtr->containingDocumentPtr, childPtr);
2268	    TclDomDeleteNode(interp, interpDataPtr, childPtr);
2269	    return TCL_OK;
2270    }
2271
2272    /*
2273     * Check that node to be appended is not an ancestor of the node
2274     */
2275
2276    tempNodePtr = nodePtr;
2277    while (tempNodePtr) {
2278	    if (tempNodePtr->parentNodePtr == childPtr) {
2279	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
2280	        return TCL_ERROR;
2281	    }
2282	    tempNodePtr = tempNodePtr->parentNodePtr;
2283    }
2284
2285    /*
2286     * Validate the reference node
2287     */
2288
2289    locationPtr = nodePtr->firstChildPtr;
2290    while (locationPtr) {
2291	    if (locationPtr == refChildPtr) {
2292	        break;
2293	    }
2294	    locationPtr = locationPtr->nextSiblingPtr;
2295    }
2296
2297    if (locationPtr == NULL) {
2298	    Tcl_AppendResult(interp, NOT_FOUND_ERR_TEXT, (char *) NULL);
2299	    return TCL_OK;
2300    }
2301
2302    /*
2303     * If the child is in the fragment list, remove it
2304     */
2305
2306    isFragment = UnlinkDocumentFragment(nodePtr->containingDocumentPtr,
2307			childPtr);
2308
2309    if (isFragment == 0) {
2310	    /*
2311	     * Remove child from document tree
2312	     */
2313
2314	    UnlinkChild(interpDataPtr, childPtr);
2315    }
2316
2317    /*
2318     * Finally, insert the child
2319     */
2320
2321    childPtr->nextSiblingPtr = refChildPtr;
2322    if (refChildPtr->previousSiblingPtr) {
2323	    childPtr->previousSiblingPtr = refChildPtr->previousSiblingPtr;
2324	    refChildPtr->previousSiblingPtr->nextSiblingPtr = childPtr;
2325    } else {
2326	    nodePtr->firstChildPtr = childPtr;
2327	    childPtr->previousSiblingPtr = NULL;
2328    }
2329    refChildPtr->previousSiblingPtr = childPtr;
2330    childPtr->parentNodePtr = nodePtr;
2331    TclDomUpdateChildNodeList(interp, interpDataPtr, nodePtr);
2332    return TCL_OK;
2333}
2334
2335
2336/*
2337 *--------------------------------------------------------------
2338 *
2339 * TclDomReplaceChild
2340 *
2341 *	This procedure replaces a child node in nodes list
2342 *	of children. Implements the Node "replaceChild"
2343 *	method.	 See the DOM specification for further
2344 *	information.
2345 *
2346 * Results:
2347 *	Returns TCL_OK, or TCL_ERROR if the node can't be added.
2348 *
2349 * Side effects:
2350 *	Will set the interpreter's result to appropriate error
2351 *	text if the action fails. May update the list
2352 *	of document fragments. Updates the parents NodeList
2353 *	if it exists.
2354 *
2355 *--------------------------------------------------------------
2356 */
2357
2358int
2359TclDomReplaceChild(
2360    Tcl_Interp *interp,			/* Tcl interpreter */
2361    TclDomInterpData *interpDataPtr,	/* State data for interpreter */
2362    TclDomNode *nodePtr,		/* Parent of child node */
2363    TclDomNode *newChildPtr,		/* New child */
2364    TclDomNode *oldChildPtr)		/* Child to be replaced */
2365{
2366    TclDomNode *tempNodePtr, *locationPtr;
2367    int isFragment;
2368
2369    if (nodePtr->containingDocumentPtr != newChildPtr->containingDocumentPtr) {
2370	    Tcl_AppendResult(interp, WRONG_DOCUMENT_ERR_TEXT, (char *) NULL);
2371	    return TCL_ERROR;
2372    }
2373
2374    if (newChildPtr->nodeType == DOCUMENT_FRAGMENT_NODE) {
2375	    int haveReplaced = 0;
2376	    TclDomNode *replaceParentPtr;
2377	    TclDomNode *fragmentChildPtr = newChildPtr->firstChildPtr;
2378	    TclDomNode *nextChildPtr;
2379	    replaceParentPtr = oldChildPtr->parentNodePtr;
2380	    while (fragmentChildPtr) {
2381	        /*
2382	         * Need to pick up "nextSiblingPtr" now, as it will be trashed in
2383			 * TclDomAppendChild
2384	         */
2385	        nextChildPtr = fragmentChildPtr->nextSiblingPtr;
2386	        if (haveReplaced == 0) {
2387		        if (TclDomReplaceChild(interp, interpDataPtr, nodePtr,
2388						fragmentChildPtr, oldChildPtr) != TCL_OK) {
2389		            return TCL_ERROR;
2390		        }
2391		        haveReplaced = 1;
2392	        } else {
2393		        if (TclDomAppendChild(interp, interpDataPtr, replaceParentPtr,
2394						fragmentChildPtr) != TCL_OK) {
2395		            return TCL_ERROR;
2396		        }
2397	        }
2398	        fragmentChildPtr = nextChildPtr;
2399	        /*
2400	         * Need to keep "childPtr->firstChildPtr" valid in case
2401		     * TclDomAppendChild fails for some reason
2402	         */
2403	        newChildPtr->firstChildPtr = fragmentChildPtr;
2404	    }
2405	    newChildPtr->lastChildPtr = NULL;
2406	    UnlinkDocumentFragment(nodePtr->containingDocumentPtr, newChildPtr);
2407	    TclDomDeleteNode(interp, interpDataPtr, newChildPtr);
2408	    return TCL_OK;
2409    }
2410
2411    /*
2412     * Check that node to be appended is not an ancestor of the node
2413     */
2414
2415    tempNodePtr = nodePtr;
2416    while (tempNodePtr) {
2417	    if (tempNodePtr->parentNodePtr == newChildPtr) {
2418	        Tcl_AppendResult(interp, HIERARCHY_REQUEST_ERR_TEXT, (char *) NULL);
2419	        return TCL_ERROR;
2420	    }
2421	    tempNodePtr = tempNodePtr->parentNodePtr;
2422    }
2423
2424    /*
2425     * Validate the node to be replaced
2426     */
2427
2428    locationPtr = nodePtr->firstChildPtr;
2429
2430    while (locationPtr) {
2431	    if (locationPtr == oldChildPtr) {
2432	        break;
2433	    }
2434	    locationPtr = locationPtr->nextSiblingPtr;
2435    }
2436
2437    if (locationPtr == NULL) {
2438	    Tcl_AppendResult(interp, NOT_FOUND_ERR_TEXT, (char *) NULL);
2439	    return TCL_OK;
2440    }
2441
2442    /*
2443     * If the child is in the fragment list, remove it
2444     */
2445
2446    isFragment = UnlinkDocumentFragment(nodePtr->containingDocumentPtr,
2447			newChildPtr);
2448
2449    if (isFragment == 0) {
2450	    /*
2451	     * Remove child from document tree
2452	     */
2453
2454	    UnlinkChild(interpDataPtr, newChildPtr);
2455    }
2456
2457    /*
2458     * Finally, replace the old child with the new
2459     */
2460
2461    newChildPtr->nextSiblingPtr = oldChildPtr->nextSiblingPtr;
2462    newChildPtr->previousSiblingPtr = oldChildPtr->previousSiblingPtr;
2463    newChildPtr->parentNodePtr = nodePtr;
2464    if (oldChildPtr->previousSiblingPtr) {
2465	    oldChildPtr->previousSiblingPtr->nextSiblingPtr = newChildPtr;
2466    } else {
2467	    oldChildPtr->parentNodePtr->firstChildPtr = newChildPtr;
2468    }
2469    if (oldChildPtr->nextSiblingPtr) {
2470	    oldChildPtr->nextSiblingPtr->previousSiblingPtr = newChildPtr;
2471    } else {
2472	    oldChildPtr->parentNodePtr->lastChildPtr = newChildPtr;
2473    }
2474
2475    /*
2476     * Add the old child to the fragments list
2477     */
2478
2479    if (oldChildPtr->containingDocumentPtr->fragmentsPtr) {
2480	    oldChildPtr->nextSiblingPtr =
2481				oldChildPtr->containingDocumentPtr->fragmentsPtr;
2482	    oldChildPtr->containingDocumentPtr->fragmentsPtr->previousSiblingPtr =
2483				oldChildPtr;
2484	    oldChildPtr->containingDocumentPtr->fragmentsPtr = oldChildPtr;
2485    } else {
2486	    oldChildPtr->containingDocumentPtr->fragmentsPtr = oldChildPtr;
2487	    oldChildPtr->nextSiblingPtr = NULL;
2488    }
2489    oldChildPtr->previousSiblingPtr = NULL;
2490    oldChildPtr->parentNodePtr = NULL;
2491
2492    TclDomUpdateChildNodeList(interp, interpDataPtr, nodePtr);
2493    return TCL_OK;
2494}
2495
2496
2497/*
2498 *--------------------------------------------------------------
2499 *
2500 * TclDomRemoveChild
2501 *
2502 *	This procedure removes a child node from a node's list
2503 *	of children. Implements the Node "removeChild"
2504 *	method.	 See the DOM specification for further
2505 *	information.
2506 *
2507 * Results:
2508 *	Returns TCL_OK, or TCL_ERROR if the node can't be added.
2509 *
2510 * Side effects:
2511 *	Will set the interpreter's result to appropriate error
2512 *	text if the action fails. May update the list
2513 *	of document fragments. Updates the parents NodeList
2514 *	if it exists.
2515 *
2516 *--------------------------------------------------------------
2517 */
2518
2519int
2520TclDomRemoveChild(
2521    Tcl_Interp *interp,			/* Tcl interpreter */
2522    TclDomInterpData *interpDataPtr,	/* Extension state data */
2523    TclDomNode *nodePtr,		/* Node containing child */
2524    TclDomNode *childPtr)		/* Child to be removed */
2525{
2526    TclDomNode *foundPtr;
2527
2528    /*
2529     * Find the child
2530     */
2531
2532    foundPtr = nodePtr->firstChildPtr;
2533    while (foundPtr && foundPtr != childPtr) {
2534	    foundPtr = foundPtr->nextSiblingPtr;
2535    }
2536
2537    if (foundPtr) {
2538	    /*
2539	     * Remove child
2540	     */
2541	    UnlinkChild(interpDataPtr, childPtr);
2542
2543	    /*
2544	     * Add child to node fragments list
2545	     */
2546
2547	    if (childPtr->containingDocumentPtr->fragmentsPtr) {
2548	        childPtr->nextSiblingPtr =
2549					childPtr->containingDocumentPtr->fragmentsPtr;
2550	        childPtr->containingDocumentPtr->fragmentsPtr->previousSiblingPtr =
2551					childPtr;
2552	        childPtr->containingDocumentPtr->fragmentsPtr = childPtr;
2553	    } else {
2554	        childPtr->containingDocumentPtr->fragmentsPtr = childPtr;
2555	        childPtr->nextSiblingPtr = NULL;
2556	    }
2557
2558	    childPtr->previousSiblingPtr = NULL;
2559	    childPtr->parentNodePtr = NULL;
2560
2561	    return TclDomSetNodeResult(interp, interpDataPtr, childPtr);
2562    }
2563
2564    Tcl_AppendResult(interp, NOT_FOUND_ERR_TEXT, (char *) NULL);
2565    return TCL_ERROR;
2566}
2567
2568
2569/*
2570 *--------------------------------------------------------------
2571 *
2572 * TclDomCreateProcessingInstructionNode
2573 *
2574 *	This procedure implements the Document
2575 *	"createProcessingInstruction" method.  See the
2576 *	DOM specification for further information.
2577 *
2578 * Results:
2579 *	A TclDomNode for the new node.
2580 *
2581 * Side effects:
2582 *	The node is added to the list of document fragments.
2583 *
2584 *--------------------------------------------------------------
2585 */
2586
2587TclDomNode *
2588TclDomCreateProcessingInstructionNode(
2589    Tcl_Interp *interp,			/* Tcl interpreter */
2590    TclDomInterpData *interpDataPtr,	/* State data for extension */
2591    TclDomDocument *documentPtr,	/* Parent document */
2592    char *target,			/* Target part of PI */
2593    char *data)				/* Data for the node */
2594{
2595    TclDomNode *nodePtr;
2596
2597    nodePtr = (TclDomNode *) ckalloc(sizeof(TclDomNode));
2598    memset(nodePtr, 0, sizeof(TclDomNode));
2599    nodePtr->nodeType = PROCESSING_INSTRUCTION_NODE;
2600    nodePtr->containingDocumentPtr = documentPtr;
2601    nodePtr->nodeId = ++interpDataPtr->nodeSeed;
2602    nodePtr->nodeName = ckalloc(strlen(target) + 1);
2603    nodePtr->nodeComplete = 1;
2604    strcpy(nodePtr->nodeName, target);
2605    nodePtr->valueLength = strlen(data);
2606    nodePtr->nodeValue = ckalloc(nodePtr->valueLength + 1);
2607    strcpy(nodePtr->nodeValue, data);
2608    AddDocumentFragment(documentPtr, nodePtr);
2609    return nodePtr;
2610}
2611
2612
2613/*
2614 *--------------------------------------------------------------
2615 *
2616 * TclDomCreateDocType
2617 *
2618 *	This procedure creates a DocType node.
2619 *
2620 * Results:
2621 *	A TclDomNode for the new node.
2622 *
2623 * Side effects:
2624 *	The node is added to the list of document fragments.
2625 *
2626 *--------------------------------------------------------------
2627 */
2628
2629TclDomNode *
2630TclDomCreateDocType(
2631    Tcl_Interp *interp,			/* Tcl interpreter */
2632    TclDomInterpData *interpDataPtr,	/* Extension state data */
2633    TclDomDocument *documentPtr,	/* Parent document */
2634    char *doctypeName,		/* Document name for node */
2635    char *publicId,			/* PublicId for node */
2636    char *systemId)			/* SystemId for node */
2637{
2638    TclDomDocTypeNode *nodePtr;
2639
2640    nodePtr = (TclDomDocTypeNode *) ckalloc(sizeof(TclDomDocTypeNode));
2641    memset(nodePtr, 0, sizeof(TclDomDocTypeNode));
2642    nodePtr->nodeType = DOCUMENT_TYPE_NODE;
2643    nodePtr->containingDocumentPtr = documentPtr;
2644    nodePtr->nodeId = ++interpDataPtr->nodeSeed;
2645
2646    nodePtr->nodeName = ckalloc(strlen(doctypeName) + 1);
2647    strcpy(nodePtr->nodeName, doctypeName);
2648
2649    if (publicId) {
2650    	nodePtr->publicId = ckalloc(strlen(publicId) + 1);
2651    	strcpy(nodePtr->publicId, publicId);
2652    }
2653
2654    if (systemId) {
2655    	nodePtr->systemId = ckalloc(strlen(systemId) + 1);
2656    	strcpy(nodePtr->systemId, systemId);
2657    }
2658
2659    AddDocumentFragment(documentPtr, (TclDomNode*)nodePtr);
2660    return (TclDomNode *) nodePtr;
2661}
2662
2663
2664/*
2665 *--------------------------------------------------------------
2666 *
2667 * TclDomCreateElement
2668 *
2669 *	This procedure implements the Document
2670 *	"createElement" method.	 See the
2671 *	DOM specification for further information.
2672 *
2673 * Results:
2674 *	A TclDomNode for the new node.
2675 *
2676 * Side effects:
2677 *	The node is added to the list of document fragments.
2678 *
2679 *--------------------------------------------------------------
2680 */
2681
2682TclDomNode *
2683TclDomCreateElement(
2684    Tcl_Interp *interp,			/* Tcl interpreter */
2685    TclDomInterpData *interpDataPtr,	/* Extension state data */
2686    TclDomDocument *documentPtr,	/* Parent document */
2687    char *tagName)			/* Tag name for node */
2688{
2689    TclDomNode *nodePtr;
2690
2691    nodePtr = (TclDomNode *) ckalloc(sizeof(TclDomNode));
2692    memset(nodePtr, 0, sizeof(TclDomNode));
2693    nodePtr->nodeType = ELEMENT_NODE;
2694    nodePtr->containingDocumentPtr = documentPtr;
2695    nodePtr->nodeId = ++interpDataPtr->nodeSeed;
2696    nodePtr->nodeComplete = 1;
2697    nodePtr->nodeName = ckalloc(strlen(tagName) + 1);
2698    strcpy(nodePtr->nodeName, tagName);
2699    AddDocumentFragment(documentPtr, nodePtr);
2700    return nodePtr;
2701}
2702
2703
2704/*
2705 *--------------------------------------------------------------
2706 *
2707 * TclDomCreateDocumentFragment
2708 *
2709 *	This procedure implements the Document
2710 *	"createDocumentFragment" method.  See the
2711 *	DOM specification for further information.
2712 *
2713 * Results:
2714 *	A TclDomNode for the new node.
2715 *
2716 * Side effects:
2717 *	The node is added to the list of document fragments.
2718 *
2719 *--------------------------------------------------------------
2720 */
2721
2722TclDomNode *
2723TclDomCreateDocumentFragment(
2724    Tcl_Interp *interp,			/* Tcl interpreter */
2725    TclDomInterpData *interpDataPtr,	/* Extension state data */
2726    TclDomDocument *documentPtr)	/* Parent document */
2727{
2728    TclDomNode *nodePtr;
2729
2730    nodePtr = (TclDomNode *) ckalloc(sizeof(TclDomNode));
2731    memset(nodePtr, 0, sizeof(TclDomNode));
2732    nodePtr->nodeType = DOCUMENT_FRAGMENT_NODE;
2733    nodePtr->containingDocumentPtr = documentPtr;
2734    nodePtr->nodeId = ++interpDataPtr->nodeSeed;
2735    nodePtr->nodeComplete = 1;
2736    if (documentPtr->fragmentsPtr) {
2737	    nodePtr->nextSiblingPtr = documentPtr->fragmentsPtr;
2738	    documentPtr->fragmentsPtr = nodePtr;
2739    } else {
2740	    documentPtr->fragmentsPtr = nodePtr;
2741    }
2742    return nodePtr;
2743}
2744
2745
2746/*
2747 *--------------------------------------------------------------
2748 *
2749 * TclDomCreateCharacterDataNode
2750 *
2751 *	This procedure creates node corresponding to
2752 *	the CharacterData interface as defined in
2753 *	the DOM specification.
2754 *
2755 * Results:
2756 *	A TclDomNode for the new node.
2757 *
2758 * Side effects:
2759 *	The node is added to the list of document fragments.
2760 *
2761 *--------------------------------------------------------------
2762 */
2763
2764static TclDomTextNode *
2765TclDomCreateCharacterDataNode(
2766    Tcl_Interp *interp,			/* Tcl interpreter */
2767    TclDomInterpData *interpDataPtr,	/* Extension state data */
2768    TclDomDocument *documentPtr,	/* Parent document */
2769    TclDomNodeType nodeType,		/* Node type to be created */
2770    char *characterData)		/* Text data for node */
2771{
2772    TclDomTextNode *nodePtr;
2773
2774    nodePtr = (TclDomTextNode *) ckalloc(sizeof(TclDomTextNode));
2775    memset(nodePtr, 0, sizeof(TclDomTextNode));
2776    nodePtr->nodeType = nodeType;
2777    nodePtr->containingDocumentPtr = documentPtr;
2778    nodePtr->nodeId = ++interpDataPtr->nodeSeed;
2779    nodePtr->nodeComplete = 1;
2780    nodePtr->valueLength = strlen(characterData);
2781    nodePtr->nodeValue = ckalloc(nodePtr->valueLength + 1);
2782    strcpy(nodePtr->nodeValue, characterData);
2783    AddDocumentFragment(documentPtr, (TclDomNode*)nodePtr);
2784    return nodePtr;
2785}
2786
2787
2788/*
2789 *--------------------------------------------------------------
2790 *
2791 * TclDomCreateTextNode
2792 *
2793 *	This procedure implements the Document
2794 *	"createTextNode" method.  See the
2795 *	DOM specification for further information.
2796 *
2797 * Results:
2798 *	A TclDomNode for the new node.
2799 *
2800 * Side effects:
2801 *	The node is added to the list of document fragments.
2802 *
2803 *--------------------------------------------------------------
2804 */
2805
2806TclDomTextNode *
2807TclDomCreateTextNode(
2808    Tcl_Interp *interp,			/* Tcl interpreter */
2809    TclDomInterpData *interpDataPtr,	/* Extension state */
2810    TclDomDocument *documentPtr,	/* Parent document */
2811    char *text)
2812{
2813    return TclDomCreateCharacterDataNode(interp, interpDataPtr, documentPtr,
2814			TEXT_NODE, text);
2815}
2816
2817
2818/*
2819 *--------------------------------------------------------------
2820 *
2821 * TclDomCreateCommentNode
2822 *
2823 *	This procedure implements the Document
2824 *	"createComment" method.	 See the
2825 *	DOM specification for further information.
2826 *
2827 * Results:
2828 *	A TclDomNode for the new node.
2829 *
2830 * Side effects:
2831 *	The node is added to the list of document fragments.
2832 *
2833 *--------------------------------------------------------------
2834 */
2835
2836TclDomNode *
2837TclDomCreateCommentNode(
2838    Tcl_Interp *interp,			/* Tcl interpreter */
2839    TclDomInterpData *interpDataPtr,	/* Extension state data */
2840    TclDomDocument *documentPtr,	/* Parent document */
2841    char *text)
2842{
2843    return (TclDomNode *) TclDomCreateCharacterDataNode(interp, interpDataPtr,
2844			documentPtr, COMMENT_NODE, text);
2845}
2846
2847
2848/*
2849 *--------------------------------------------------------------
2850 *
2851 * TclDomCreateCDATANode
2852 *
2853 *	This procedure implements the Document
2854 *	"createCDATASection" method.  See the
2855 *	DOM specification for further information.
2856 *
2857 * Results:
2858 *	A TclDomNode for the new node.
2859 *
2860 * Side effects:
2861 *	The node is added to the list of document fragments.
2862 *
2863 *--------------------------------------------------------------
2864 */
2865
2866TclDomNode *
2867TclDomCreateCDATANode(
2868    Tcl_Interp *interp,			/* Tcl interpreter */
2869    TclDomInterpData *interpDataPtr,	/* Extension stata data */
2870    TclDomDocument *documentPtr,	/* Parent document */
2871    char *text)
2872{
2873    return (TclDomNode *) TclDomCreateCharacterDataNode(interp, interpDataPtr,
2874			documentPtr, CDATA_SECTION_NODE, text);
2875}
2876
2877
2878/*
2879 *--------------------------------------------------------------
2880 *
2881 * TclDomGetNodeFromToken
2882 *
2883 *	This procedure maps a TclDomPro node token
2884 *	into a TclDomNode pointer.
2885 *
2886 * Results:
2887 *	A pointer to the TclDomNode, or null if the
2888 *	token can't be found.
2889 *
2890 * Side effects:
2891 *	None.
2892 *
2893 *--------------------------------------------------------------
2894 */
2895
2896TclDomNode *
2897TclDomGetNodeFromToken(
2898    Tcl_Interp *interp,			/* Tcl interpreter */
2899    TclDomInterpData *interpDataPtr,	/* Extension state data */
2900    Tcl_Obj *nodeTokenPtr)		/* Token string value */
2901{
2902    char *token;
2903    Tcl_HashEntry *entryPtr;
2904    TclDomNode *nodePtr;
2905
2906    token = Tcl_GetStringFromObj(nodeTokenPtr, NULL);
2907
2908    entryPtr = Tcl_FindHashEntry(&interpDataPtr->nodeHashTable, token);
2909    if (entryPtr == NULL) {
2910	    Tcl_AppendResult(interp, "token not found", NULL);
2911	    return NULL;
2912    }
2913    nodePtr = (TclDomNode *) Tcl_GetHashValue(entryPtr);
2914    return nodePtr;
2915}
2916
2917
2918/*
2919 *--------------------------------------------------------------
2920 *
2921 * TclDomGetDocumentFromToken
2922 *
2923 *	This procedure maps a TclDomPro node token
2924 *	into its containing document.
2925 *
2926 * Results:
2927 *	A pointer to the TclDomDocument that contains
2928 *	the node, or NULL if the token can't be found.
2929 *
2930 * Side effects:
2931 *	None.
2932 *
2933 *--------------------------------------------------------------
2934 */
2935
2936
2937TclDomDocument *
2938TclDomGetDocumentFromToken(
2939    Tcl_Interp *interp,			/* Tcl interpreter */
2940    TclDomInterpData *interpDataPtr,	/* Extension state data */
2941    Tcl_Obj *nodeTokenPtr)		/* Token string value */
2942{
2943    TclDomNode *nodePtr;
2944
2945    nodePtr = TclDomGetNodeFromToken(interp, interpDataPtr, nodeTokenPtr);
2946
2947    /*
2948     * XXX
2949     * Steve Ball's tcldom currently doesn't do any type checking; however,
2950     * we should check the DOM spec and do the correct thing here.
2951     */
2952
2953    if (nodePtr) {
2954	    return nodePtr->containingDocumentPtr;
2955    } else {
2956	    return NULL;
2957    }
2958}
2959
2960/*
2961 *--------------------------------------------------------------
2962 *
2963 * TclDomGetDocumentElement --
2964 *
2965 *	Returns the document element node of a TclDomDocument,
2966 *	NULL if none exists.
2967 *
2968 *--------------------------------------------------------------
2969 */
2970
2971TclDomNode *TclDomGetDocumentElement(TclDomDocument *documentPtr)
2972{
2973    TclDomNode *documentNode = documentPtr->selfPtr;
2974    TclDomNode *childPtr = documentNode ? documentNode->firstChildPtr : NULL;
2975
2976    while (childPtr && childPtr->nodeType != ELEMENT_NODE)
2977    	childPtr = childPtr->nextSiblingPtr;
2978
2979    return childPtr;
2980}
2981
2982
2983/*
2984 *--------------------------------------------------------------
2985 *
2986 * TclDomGetDoctypeNode --
2987 *
2988 *	Returns the document type declaration node of a TclDomDocument,
2989 *	NULL if none exists.
2990 *
2991 *--------------------------------------------------------------
2992 */
2993
2994TclDomNode *TclDomGetDoctypeNode(TclDomDocument *documentPtr)
2995{
2996    TclDomNode *documentNode = documentPtr->selfPtr;
2997    TclDomNode *childPtr = documentNode ? documentNode->firstChildPtr : NULL;
2998
2999    while (childPtr && childPtr->nodeType != DOCUMENT_TYPE_NODE)
3000    	childPtr = childPtr->nextSiblingPtr;
3001
3002    return childPtr;
3003}
3004
3005
3006/*
3007 *--------------------------------------------------------------
3008 *
3009 * DestroyDocument
3010 *
3011 *	This procedure deletes a DOM document.
3012 *
3013 * Results:
3014 *	None.
3015 *
3016 * Side effects:
3017 *	Deletes the document and all its children, so
3018 *	memory is released and hash tables are deleted.
3019 *
3020 *--------------------------------------------------------------
3021 */
3022
3023
3024static void
3025DestroyDocument(
3026    char *dataPtr)	/* Document object */
3027{
3028    TclDomNode *nodePtr, *tempNodePtr;
3029    char keyString[32];
3030    Tcl_HashEntry *entryPtr;
3031    TclDomNodeIterator *nodeIteratorPtr;
3032    TclDomTreeWalker *treeWalkerPtr;
3033    Tcl_HashSearch search;
3034
3035
3036    TclDomDocument *documentPtr = (TclDomDocument *) dataPtr;
3037
3038    Tcl_Interp *interp = documentPtr->interp;
3039    TclDomInterpData *interpDataPtr = documentPtr->interpDataPtr;
3040
3041    for (entryPtr = Tcl_FirstHashEntry(&interpDataPtr->iteratorHashTable,
3042	         &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) {
3043	    nodeIteratorPtr = (TclDomNodeIterator *) Tcl_GetHashValue(entryPtr);
3044	    if (nodeIteratorPtr->rootPtr &&
3045				nodeIteratorPtr->rootPtr->containingDocumentPtr
3046				== documentPtr) {
3047            nodeIteratorPtr->rootPtr = NULL;
3048            nodeIteratorPtr->referencePtr = NULL;
3049	    }
3050    }
3051
3052    for (entryPtr = Tcl_FirstHashEntry(&interpDataPtr->treeWalkerHashTable,
3053	        &search); entryPtr; entryPtr = Tcl_NextHashEntry(&search)) {
3054	    treeWalkerPtr = (TclDomTreeWalker *) Tcl_GetHashValue(entryPtr);
3055	    if (treeWalkerPtr->rootPtr
3056				&& treeWalkerPtr->rootPtr->containingDocumentPtr
3057		        == documentPtr) {
3058            treeWalkerPtr->rootPtr = NULL;
3059            treeWalkerPtr->currentNodePtr = NULL;
3060	    }
3061    }
3062
3063    /*
3064     * Delete the node of the document
3065     */
3066    if (documentPtr->selfPtr) {
3067	    sprintf(keyString, "node%u", documentPtr->selfPtr->nodeId);
3068	    TclDomDeleteNode(interp, interpDataPtr, documentPtr->selfPtr);
3069    }
3070
3071    /*
3072     * Delete any dangling document fragments
3073     */
3074
3075    if (documentPtr->fragmentsPtr) {
3076	    nodePtr = documentPtr->fragmentsPtr;
3077	    while (nodePtr) {
3078	        tempNodePtr = nodePtr->nextSiblingPtr;
3079	        TclDomDeleteNode(interp, interpDataPtr, nodePtr);
3080	        nodePtr = tempNodePtr;
3081	    }
3082    }
3083
3084    entryPtr = Tcl_FindHashEntry(&interpDataPtr->documentHashTable, keyString);
3085    if (entryPtr) {
3086	    Tcl_DeleteHashEntry(entryPtr);
3087    }
3088
3089    ckfree((char *) documentPtr);
3090}
3091
3092
3093/*
3094 *--------------------------------------------------------------
3095 *
3096 * TclDomDeleteDocument
3097 *
3098 *	This procedure deletes a DOM document when it is no
3099 *   longer referenced.
3100 *
3101 * Results:
3102 *	None.
3103 *
3104 * Side effects:
3105 *	None.
3106 *
3107 *--------------------------------------------------------------
3108 */
3109
3110void
3111TclDomDeleteDocument(
3112    Tcl_Interp *interp,			/* Tcl interpreter */
3113    TclDomInterpData *interpDataPtr,	/* Extension state data */
3114    TclDomDocument *documentPtr)	/* Document object */
3115{
3116
3117    Tcl_EventuallyFree((ClientData) documentPtr, DestroyDocument);
3118}
3119
3120
3121/*
3122 *--------------------------------------------------------------
3123 *
3124 * TclDomGetNodeObj
3125 *
3126 *	This creates a Tcl_Obj for a token by which a node may
3127 *	be referenced at the Tcl level. Tokens are created on
3128 *	demand, either when returned from Tcl commands, or when
3129 *	needed to populate global Tcl variables corresponding to
3130 *	NodeLists or NodeNameMaps.
3131 *
3132 * Returns:
3133 *	Tcl_Obj corresponding to the node token.
3134 *
3135 * Side effects:
3136 *	An entry is added to a hash table maintained per
3137 *	interpreter, if it doesn't already exist for this node.
3138 *
3139 *--------------------------------------------------------------
3140 */
3141
3142Tcl_Obj *
3143TclDomGetNodeObj(
3144    TclDomInterpData *interpDataPtr,	/* Extension state data */
3145    TclDomNode *nodePtr)		/* Node object */
3146{
3147    char workString[64];
3148    int newFlag;
3149    Tcl_HashEntry *entryPtr;
3150
3151    if (nodePtr != NULL) {
3152	    sprintf(workString, "node%u", nodePtr->nodeId);
3153	    if (nodePtr->entryPtr == NULL) {
3154	        entryPtr = Tcl_CreateHashEntry(&interpDataPtr->nodeHashTable,
3155					workString, &newFlag);
3156	        Tcl_SetHashValue(entryPtr, nodePtr);
3157	        nodePtr->entryPtr = entryPtr;
3158	    }
3159    } else {
3160        *workString = 0;
3161    }
3162
3163    return Tcl_NewStringObj(workString, -1);
3164}
3165
3166
3167/*
3168 *--------------------------------------------------------------
3169 *
3170 * TclDomSetNodeResult
3171 *
3172 *	This procedure sets the interpreter's result to
3173 *	the token value for a node.
3174 *
3175 * Results:
3176 *	Return TCL_OK, or TCL_ERROR if an internal error occurs.
3177 *
3178 * Side effects:
3179 *	May cause a new token to be created.
3180 *
3181 *--------------------------------------------------------------
3182 */
3183
3184int
3185TclDomSetNodeResult(
3186    Tcl_Interp *interp,			/* Tcl interpreter */
3187    TclDomInterpData *interpDataPtr,	/* Extension state data */
3188    TclDomNode *nodePtr)		/* Node object */
3189{
3190    Tcl_Obj *objPtr = TclDomGetNodeObj(interpDataPtr, nodePtr);
3191    Tcl_SetObjResult(interp, objPtr);
3192    return TCL_OK;
3193}
3194
3195
3196/*
3197 *--------------------------------------------------------------
3198 *
3199 * TclDomSetDomError
3200 *
3201 *	This procedure sets the interpreter's result to
3202 *	a DOM error string.
3203 *
3204 * Results:
3205 *  None
3206 *
3207 * Side effects:
3208 *  The interpreter's result is updated.
3209 *
3210 *--------------------------------------------------------------
3211 */
3212
3213void
3214TclDomSetDomError(
3215    Tcl_Interp *interp,			/* Tcl interpreter */
3216	TdpDomError domError)		/* The DOM error to report */
3217{
3218	static char *domErrorString[] = {
3219		"",
3220		INDEX_SIZE_ERR_TEXT,
3221		DOMSTRING_SIZE_ERR_TEXT,
3222		HIERARCHY_REQUEST_ERR_TEXT,
3223		WRONG_DOCUMENT_ERR_TEXT,
3224		INVALID_CHARACTER_ERR_TEXT,
3225		NO_DATA_ALLOWED_ERR_TEXT,
3226		NO_MODIFICATION_ALLOWED_ERR_TEXT,
3227		NOT_FOUND_ERR_TEXT,
3228		NOT_SUPPORTED_ERR_TEXT,
3229		INUSE_ATTRIBUTE_ERR_TEXT,
3230	};
3231
3232	Tcl_AppendResult(interp, domErrorString[domError], (char *) NULL);
3233}
3234
3235
3236
3237/*
3238 *--------------------------------------------------------------
3239 *
3240 * TclDomDeleteNode
3241 *
3242 *	This procedure deletes a node and its children.
3243 *	NodeList and NodeNameMap Tcl global variables are
3244 *	currently just dereferenced; is this the correct
3245 *	behavior?
3246 *
3247 * Results:
3248 *	None.
3249 *
3250 * Side effects:
3251 *	Releases memory and deletes hash tables.
3252 *
3253 *--------------------------------------------------------------
3254 */
3255
3256void
3257TclDomDeleteNode(
3258    Tcl_Interp *interp,			/* Tcl interpreter */
3259    TclDomInterpData *interpDataPtr,	/* Extension state data */
3260    TclDomNode *nodePtr)		/* Node object */
3261{
3262    TclDomNode *childPtr;
3263    TclDomNode *tempNodePtr;
3264    TclDomAttributeNode *attributeNodePtr;
3265
3266    if (nodePtr->nodeType == ELEMENT_NODE
3267	        || nodePtr->nodeType == DOCUMENT_FRAGMENT_NODE
3268	        || nodePtr->nodeType == DOCUMENT_NODE) {
3269	    childPtr = nodePtr->lastChildPtr;
3270	    while (childPtr) {
3271	        tempNodePtr = childPtr->previousSiblingPtr;
3272	        TclDomDeleteNode(interp, interpDataPtr, childPtr);
3273	        childPtr = tempNodePtr;
3274	    }
3275	    attributeNodePtr = nodePtr->firstAttributePtr;
3276	    while (attributeNodePtr) {
3277	        tempNodePtr = (TclDomNode *) attributeNodePtr->nextSiblingPtr;
3278	        TclDomDeleteNode(interp, interpDataPtr,
3279					(TclDomNode *) attributeNodePtr);
3280	        attributeNodePtr = (TclDomAttributeNode *) tempNodePtr;
3281	    }
3282	    if (nodePtr->childNodeListVarName) {
3283	        Tcl_DecrRefCount(nodePtr->childNodeListVarName);
3284	    }
3285    } else if (nodePtr->nodeType == DOCUMENT_TYPE_NODE) {
3286	TclDomDocTypeNode *docTypePtr = (TclDomDocTypeNode *)nodePtr;
3287	if (docTypePtr->systemId) {
3288	    ckfree(docTypePtr->systemId);
3289	}
3290	if (docTypePtr->publicId) {
3291	    ckfree(docTypePtr->publicId);
3292	}
3293	if (docTypePtr->internalSubset) {
3294	    ckfree(docTypePtr->internalSubset);
3295	}
3296    }
3297
3298    if (nodePtr->nodeValue) {
3299	    ckfree(nodePtr->nodeValue);
3300    }
3301
3302    if (nodePtr->nodeName) {
3303	    ckfree(nodePtr->nodeName);
3304    }
3305
3306    if (nodePtr->entryPtr) {
3307	    Tcl_DeleteHashEntry(nodePtr->entryPtr);
3308    }
3309
3310    ckfree((char *) nodePtr);
3311}
3312
3313
3314/*
3315 *--------------------------------------------------------------
3316 *
3317 * TclDomEmptyDocument
3318 *
3319 *	This procedure creates a document with no children.
3320 *	The node "selfPtr" is a container for the children
3321 *	of the document.
3322 *
3323 * Results:
3324 *	Returns TCL_OK, or TCL_ERROR if an internal error occurs.
3325 *
3326 * Side effects:
3327 *	A hash table entry is created for the document.
3328 *
3329 *--------------------------------------------------------------
3330 */
3331
3332TclDomDocument*
3333TclDomEmptyDocument(
3334    Tcl_Interp *interp,			/* Tcl interpreter */
3335    TclDomInterpData *interpDataPtr)	/* Extension state */
3336{
3337    TclDomDocument *documentPtr;
3338    TclDomNode *nodePtr;
3339    char workString[128];
3340    int newFlag;
3341    Tcl_HashEntry *entryPtr;
3342
3343    documentPtr = (TclDomDocument *) ckalloc(sizeof(TclDomDocument));
3344    memset(documentPtr, 0, sizeof(TclDomDocument));
3345
3346    documentPtr->interp = interp;
3347    documentPtr->interpDataPtr = interpDataPtr;
3348
3349    /*
3350     * Create root node
3351     */
3352
3353    nodePtr = (TclDomNode *) ckalloc(sizeof(TclDomNode));
3354    memset(nodePtr, 0, sizeof(TclDomNode));
3355    nodePtr->nodeType = DOCUMENT_NODE;
3356    nodePtr->nodeId = ++interpDataPtr->nodeSeed;
3357    nodePtr->nodeComplete = 1;
3358
3359    nodePtr->nodeName = ckalloc(1);
3360    nodePtr->nodeName[0] = 0;
3361    nodePtr->containingDocumentPtr = documentPtr;
3362
3363    documentPtr->selfPtr = nodePtr;
3364
3365    /*
3366     * Save the root object so we can delete documents on an error exit
3367     */
3368
3369    sprintf(workString, "node%u", documentPtr->selfPtr->nodeId);
3370    entryPtr = Tcl_CreateHashEntry(&interpDataPtr->documentHashTable,
3371			workString, &newFlag);
3372    if (entryPtr == NULL) {
3373	    Tcl_AppendResult(interp, "couldn't create documentElement",
3374				(char *) NULL);
3375        ckfree((char *) nodePtr);
3376        ckfree((char *) documentPtr);
3377	    return NULL;
3378    }
3379    Tcl_SetHashValue(entryPtr, documentPtr);
3380    return documentPtr;
3381}
3382
3383
3384/*
3385 *--------------------------------------------------------------
3386 *
3387 * TclDomCreateEmptyDocumentNode
3388 *
3389 *	This procedure creates an empty document and allocates
3390 *   a token for it.
3391 *
3392 * Results:
3393 *	Returns TCL_OK, or TCL_ERROR if an internal error occurs.
3394 *
3395 * Side effects:
3396 *	A token is allocated for the document.
3397 *
3398 *--------------------------------------------------------------
3399 */
3400
3401int
3402TclDomCreateEmptyDocumentNode(
3403    Tcl_Interp *interp,			/* Tcl interpreter */
3404    TclDomInterpData *interpDataPtr)	/* Extension state */
3405{
3406    TclDomDocument *documentPtr;
3407
3408    documentPtr = TclDomEmptyDocument(interp, interpDataPtr);
3409    if (documentPtr && documentPtr->selfPtr) {
3410        return TclDomSetNodeResult(interp, interpDataPtr, documentPtr->selfPtr);
3411    } else {
3412        return TCL_ERROR;
3413    }
3414}
3415
3416static void
3417SwapShort(unsigned short *p)
3418{
3419    unsigned short low, high;
3420    low = *p & 0xff;
3421    high = *p & 0xff00;
3422    *p = (low << 8) + (high >> 8);
3423}
3424
3425
3426/*
3427 *--------------------------------------------------------------
3428 *
3429 * TclDomReadDocument
3430 *
3431 *	This procedure creates a new document from XML source.
3432 *	An empty document is created, and then the expat
3433 *	parser is invoked to populate it from the source.
3434 *
3435 * Results:
3436 *	TCL_OK, or TCL_ERROR if the XML is not well-formed.
3437 *	If parsing succeeds, the interpreter's result is set
3438 *	to a token for the new document; otherwise the result
3439 *	is set to error information as returned by expat.
3440 *
3441 * Side effects:
3442 *	A document tree and associated data structures are created.
3443 *
3444 *--------------------------------------------------------------
3445 */
3446#define TCLDOM_ERR_WINDOW 8		/* size of xml fragment displayed
3447					 * as error */
3448
3449int
3450TclDomReadDocument(
3451    Tcl_Interp *interp,			/* Tcl interpreter */
3452    TclDomInterpData *interpDataPtr,	/* Extension state */
3453    char *xmlSource,			/* XML source in UTF-8 */
3454    int length,				/* Length of XML source in bytes */
3455    int final,			/* If true, this is the last chunk. */
3456    int trim)			/* If true, then eliminate null text nodes. */
3457{
3458    TclDomDocument *documentPtr;
3459    TclDomNode *selfPtr;
3460    char workString[128];
3461    int newFlag;
3462    Tcl_HashEntry *entryPtr;
3463
3464    if (interpDataPtr->parser == NULL) {
3465	    unsigned short *encodingPtr = (unsigned short *) xmlSource;
3466
3467	    documentPtr = (TclDomDocument *) ckalloc(sizeof(TclDomDocument));
3468	    memset(documentPtr, 0, sizeof(TclDomDocument));
3469
3470        documentPtr->interp = interp;
3471        documentPtr->interpDataPtr = interpDataPtr;
3472
3473	    /*
3474	     * Determine the document encoding; information needed to
3475	     * determine if we can display additional error text, and
3476	     * how to decode XML.
3477	     */
3478
3479	    if (*encodingPtr == 0xfeff) {
3480	        documentPtr->encoding = UTF16;
3481	    } else if (*encodingPtr == 0xfffe) {
3482	        documentPtr->encoding = UTF16SWAPPED;
3483	    } else if (length > 1 && *xmlSource == '<' && *(xmlSource+1) == '?') {
3484	        documentPtr->encoding = UTF8;
3485	    } else {
3486	        documentPtr->encoding = OTHER;
3487	    }
3488
3489	    selfPtr = (TclDomNode *) ckalloc(sizeof(TclDomNode));
3490	    memset(selfPtr, 0, sizeof(TclDomNode));
3491	    selfPtr->nodeType = DOCUMENT_NODE;
3492	    selfPtr->nodeId = ++interpDataPtr->nodeSeed;
3493
3494	    selfPtr->nodeName = ckalloc(1);
3495	    selfPtr->nodeName[0] = 0;
3496	    selfPtr->containingDocumentPtr = documentPtr;
3497	    documentPtr->selfPtr = selfPtr;
3498
3499	    /*
3500	     * Save the root object so we can delete documents on an error exit
3501	     */
3502
3503	    sprintf(workString, "node%u", documentPtr->selfPtr->nodeId);
3504	    entryPtr = Tcl_CreateHashEntry(&interpDataPtr->documentHashTable,
3505				workString, &newFlag);
3506	    if (entryPtr == NULL) {
3507	        Tcl_AppendResult(interp, "couldn't create documentElement",
3508					(char *) NULL);
3509	        return TCL_ERROR;
3510	    }
3511	    Tcl_SetHashValue(entryPtr, documentPtr);
3512
3513	    interpDataPtr->parser = XML_ParserCreate(NULL);
3514
3515	    memset(&interpDataPtr->parserInfo, 0, sizeof(TclDomExpatInfo));
3516
3517	    interpDataPtr->parserInfo.documentPtr = documentPtr;
3518	    interpDataPtr->parserInfo.parser = interpDataPtr->parser;
3519	    interpDataPtr->parserInfo.interpDataPtr = interpDataPtr;
3520	    interpDataPtr->parserInfo.interp = interp;
3521	    interpDataPtr->parserInfo.trim = trim;
3522
3523	    /*
3524	     * Turn on external parameter entity parsing so we can retrieve
3525	     * the systemId and publicId from the DOCTYPE declaration.
3526	     */
3527
3528	    XML_SetParamEntityParsing(interpDataPtr->parser,
3529		    XML_PARAM_ENTITY_PARSING_ALWAYS);
3530
3531	    XML_SetElementHandler(interpDataPtr->parser,
3532		        TclDomExpatElementStartHandler,
3533		TclDomExpatElementEndHandler);
3534	    XML_SetCharacterDataHandler(interpDataPtr->parser,
3535		        TclDomExpatCharacterDataHandler);
3536	    XML_SetProcessingInstructionHandler(interpDataPtr->parser,
3537		    TclDomExpatProcessingInstructionHandler);
3538	    XML_SetDefaultHandler(interpDataPtr->parser,
3539		        TclDomExpatDefaultHandler);
3540
3541	    XML_SetDoctypeDeclHandler(interpDataPtr->parser,
3542		    TclDomExpatStartDoctypeDeclHandler,
3543		    TclDomExpatEndDoctypeDeclHandler);
3544
3545	    XML_SetUnparsedEntityDeclHandler(interpDataPtr->parser,
3546		        TclDomExpatUnparsedDeclHandler);
3547	    XML_SetNotationDeclHandler(interpDataPtr->parser,
3548		        TclDomExpatNotationDeclHandler);
3549	    XML_SetExternalEntityRefHandler(interpDataPtr->parser,
3550		        TclDomExpatExternalEntityRefHandler);
3551	    XML_SetUnknownEncodingHandler(interpDataPtr->parser,
3552		        TclDomExpatUnknownEncodingHandler,
3553		        (void *) &interpDataPtr->parserInfo);
3554
3555	    XML_SetCommentHandler(interpDataPtr->parser,
3556		        TclDomExpatCommentHandler);
3557
3558	    /* Tell expat to use the TclDomExpat "not standalone" handler */
3559	    XML_SetNotStandaloneHandler(interpDataPtr->parser,
3560		        TclDomExpatNotStandaloneHandler);
3561
3562	    /* Tell expat to use the TclDomExpat CdataSection handlers */
3563	    XML_SetCdataSectionHandler(interpDataPtr->parser,
3564		    TclDomExpatStartCdataSectionHandler,
3565		    TclDomExpatEndCdataSectionHandler);
3566
3567	    XML_SetUserData(interpDataPtr->parser, &interpDataPtr->parserInfo);
3568    }
3569
3570    if (!XML_Parse(interpDataPtr->parser, xmlSource, length, final)) {
3571	    int byteIndex;
3572	    TclDomDocumentEncoding encoding;
3573
3574	    documentPtr = interpDataPtr->parserInfo.documentPtr;
3575	    encoding = documentPtr->encoding;
3576
3577	    if (documentPtr) {
3578	        TclDomDeleteDocument(interp, interpDataPtr, documentPtr);
3579	    }
3580
3581	    Tcl_ResetResult(interp);
3582	    sprintf(workString, "%d",
3583                XML_GetCurrentLineNumber(interpDataPtr->parser));
3584	            Tcl_AppendResult(interp, "error \"",
3585		XML_ErrorString(XML_GetErrorCode(interpDataPtr->parser)),
3586		        "\" at line ", workString, " character ", NULL);
3587	    sprintf(workString, "%d",
3588		        XML_GetCurrentColumnNumber(interpDataPtr->parser));
3589	    Tcl_AppendResult(interp, workString, (char *) NULL);
3590	    byteIndex = XML_GetCurrentByteIndex(interpDataPtr->parser);
3591	    if ((encoding != OTHER) && (byteIndex >= 0 && byteIndex < length)) {
3592	        char errorString[4 * TCLDOM_ERR_WINDOW * TCL_UTF_MAX];
3593	        char contextString[4 * TCLDOM_ERR_WINDOW * TCL_UTF_MAX];
3594	        int i, len, contextChars, charPos, contextPos;
3595	        int filterOK = 0;
3596		const char *s;
3597
3598	        charPos = XML_GetCurrentColumnNumber(interpDataPtr->parser);
3599
3600	        /*
3601	         * Generate a string for that includes the error character plus
3602	         * surrounding text.  Filter out CR & LF unless the character
3603	         * generating the error was a CF or LF.
3604	         */
3605
3606	        if (encoding != UTF8) {
3607		        char *utf;
3608		        unsigned short *xmlSourceWS = (unsigned short *) xmlSource;
3609		        unsigned short tempUTF16;
3610
3611		    /*
3612		     * Back up to beginning of window
3613		     */
3614
3615		    contextPos = byteIndex / 2;
3616		    contextChars = TCLDOM_ERR_WINDOW;
3617
3618		    for (i = 0; i < TCLDOM_ERR_WINDOW; contextPos--) {
3619		        if (contextPos == 1) break;
3620		        tempUTF16= *(xmlSourceWS + contextPos);
3621		        if (encoding == UTF16SWAPPED) {
3622			        SwapShort(&tempUTF16);
3623		        }
3624		        Tcl_UniCharToUtf(tempUTF16, contextString);
3625		        if ((i != 0) && (*contextString == '\r'
3626			        || *contextString == '\n')) {
3627			        continue;
3628		        }
3629		        i++;
3630		        contextChars++;
3631		    }
3632
3633		    /*
3634		     * Transfer to utf-8 buffer, filtering out CR & LF
3635		     * We replace the first CR or LF after the error postion
3636		     * with a space, and skip over any successive ones.
3637		     */
3638		    for (utf = contextString, i = 0; i < contextChars;
3639		            contextPos++) {
3640		        if ((2 * contextPos) >= length) break;
3641		        tempUTF16 = *(xmlSourceWS + contextPos);
3642		        if (encoding == UTF16SWAPPED) {
3643			        SwapShort(&tempUTF16);
3644		        }
3645		        len = Tcl_UniCharToUtf(tempUTF16, utf);
3646		        if (*utf == '\r' || *utf == '\n') {
3647			        if (filterOK || (contextPos < byteIndex / 2)) {
3648			            utf += len;
3649			            continue;
3650			        }
3651			        if (contextPos == byteIndex / 2) {
3652			            *utf = ' ';
3653			        }
3654			        filterOK = 1;
3655		        }
3656		        utf += len;
3657		        i++;
3658		    }
3659		    *utf = 0;
3660	    } else {
3661		    const char *s, *next;
3662		    char *d;
3663		    int j;
3664		    int goodIndex;
3665		    int index;
3666		    int len;
3667
3668		    /*
3669		     * This requires that we back up over UTF-8 characters
3670		     */
3671
3672		    s = xmlSource + byteIndex;
3673
3674		    index = goodIndex = byteIndex;
3675
3676		    contextChars = TCLDOM_ERR_WINDOW;
3677
3678		    for (i = 0; i < TCLDOM_ERR_WINDOW; ) {
3679		        if (index == 0) break;
3680		        for (j = 0; j < TCL_UTF_MAX; j++) {
3681			        index--;
3682			        if (Tcl_UtfCharComplete(xmlSource+index, j+1)) break;
3683		        }
3684		        if (!Tcl_UtfCharComplete(xmlSource+index, j+1)) {
3685			        break;
3686		        }
3687		        goodIndex = index;
3688		        if ((*(xmlSource+index) != '\r')
3689						&& (*(xmlSource+index) != '\n')) {
3690			        contextChars++;
3691			        i++;
3692		        }
3693		    }
3694
3695		    contextPos = goodIndex;
3696
3697		    s = xmlSource + contextPos;
3698		    d = contextString;
3699
3700		    /*
3701		     * Transfer to buffer, removing any CR or LF chars
3702		     */
3703		    for (i = 0; i < contextChars ;) {
3704		        if (s >= (xmlSource + length)) break;
3705		        if (*s == '\r' || *s == '\n') {
3706			        if (filterOK || (s < (xmlSource + byteIndex))) {
3707			            s++;
3708			            continue;
3709			        }
3710			        if (s == xmlSource + byteIndex) {
3711			            *d = ' ';
3712			            filterOK = 1;
3713			            s++;
3714			            d++;
3715			            i++;
3716			            continue;
3717			        }
3718			        filterOK = 1;
3719		        }
3720		        next = Tcl_UtfNext(s);
3721		        len = next-s;
3722		        memcpy(d, s, len);
3723		        d += len;
3724		        s = next;
3725		        i++;
3726		    }
3727		    *d = 0;
3728	    }
3729
3730	    /*
3731	     * Generate a buffer containing the code where the error occurred
3732	     */
3733
3734	    if (encoding != UTF8) {
3735		    char *utf;
3736		    int errorPos;
3737		    unsigned short *xmlSourceWS = (unsigned short *) xmlSource;
3738
3739		    errorPos = byteIndex / 2;
3740		    if (errorPos == 0) {
3741		        errorPos++;
3742		    }
3743
3744		    /*
3745		     * Transfer to utf-8 buffer
3746		     */
3747		    if (encoding == UTF16SWAPPED) {
3748		        unsigned short temp;
3749		        for (utf = errorString, i = 0; i < TCLDOM_ERR_WINDOW; i++) {
3750			        temp = *(xmlSourceWS + errorPos + i);
3751			        SwapShort(&temp);
3752			        if ((2 * (errorPos + i)) >= length) break;
3753			        utf += Tcl_UniCharToUtf(temp, utf);
3754		        }
3755		    } else {
3756		        for (utf = errorString, i = 0; i < TCLDOM_ERR_WINDOW; i++) {
3757			        if ((2 * (errorPos + i)) >= length) break;
3758			        utf += Tcl_UniCharToUtf(*(xmlSourceWS + errorPos + i), utf);
3759		        }
3760		    }
3761		    *utf = 0;
3762	    } else {
3763		    int sourceLength;
3764		    sourceLength = TCLDOM_ERR_WINDOW * TCL_UTF_MAX;
3765		    if ((byteIndex + sourceLength) > length) {
3766		        sourceLength = length - byteIndex;
3767		    }
3768
3769		    memcpy(errorString, xmlSource+byteIndex, sourceLength);
3770		    errorString[sourceLength] = 0;
3771	    }
3772
3773	    /*
3774	     * Ignore any characters after the first CR or LF that is not the
3775	     * error character
3776	     */
3777
3778	    for (s = errorString, i = 0; *s && (i < TCLDOM_ERR_WINDOW); i++) {
3779		    if ((i != 0) && ((*s == '\r' || *s == '\n'))) break;
3780		    s = Tcl_UtfNext(s);
3781	        }
3782
3783		errorString[s-errorString] = 0;
3784	        if (strcmp(errorString, contextString) == 0) {
3785		        Tcl_AppendResult(interp, "; at \"", errorString,
3786					    "\"", (char *) NULL);
3787	        } else {
3788		        Tcl_AppendResult(interp, "; at \"", errorString,
3789						"\" within \"", contextString, "\"", (char *) NULL);
3790	        }
3791	    }
3792	    XML_ParserFree(interpDataPtr->parser);
3793	    interpDataPtr->parser = NULL;
3794	    memset(&interpDataPtr->parserInfo, 0, sizeof(TclDomExpatInfo));
3795	    return TCL_ERROR;
3796    }
3797
3798    documentPtr = interpDataPtr->parserInfo.documentPtr;
3799
3800    if (final) {
3801	    documentPtr->selfPtr->nodeComplete = 1;
3802	    XML_ParserFree(interpDataPtr->parser);
3803	    interpDataPtr->parser = NULL;
3804	    memset(&interpDataPtr->parserInfo, 0, sizeof(TclDomExpatInfo));
3805    }
3806
3807    return TclDomSetNodeResult(interp, interpDataPtr, documentPtr->selfPtr);
3808}
3809
3810/*
3811 *----------------------------------------------------------------------------
3812 *
3813 * TclDomExpatElementStartHandler --
3814 *
3815 *	    Called by expat for each start tag.
3816 *
3817 * Results:
3818 *	    None.
3819 *
3820 * Side Effects:
3821 *	    A node is added to the current tree.
3822 *
3823 *----------------------------------------------------------------------------
3824 */
3825
3826static void
3827TclDomExpatElementStartHandler(
3828    void *userData,		    /* Our context for parser */
3829    const char *name,		    /* Element name */
3830    const char **atts)		    /* Array of name, value pairs */
3831{
3832    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
3833    TclDomNode *nodePtr;
3834    TclDomNode *parentNodePtr;
3835    const char **attributePtr;
3836    TclDomAttributeNode *attributeNodePtr;
3837
3838    /*
3839     * Invoke the default handler to get the current width
3840     */
3841
3842    XML_DefaultCurrent(infoPtr->parser);
3843
3844
3845    nodePtr = (TclDomNode *) ckalloc(sizeof(TclDomNode));
3846    memset(nodePtr, 0, sizeof(TclDomNode));
3847    nodePtr->nodeType = ELEMENT_NODE;
3848    nodePtr->nodeId = ++infoPtr->interpDataPtr->nodeSeed;
3849
3850   /*  node->nodeName =	 XXX */
3851    nodePtr->nodeName = ckalloc(strlen(name) + 1);
3852    strcpy(nodePtr->nodeName, name);
3853    nodePtr->containingDocumentPtr = infoPtr->documentPtr;
3854
3855    if (infoPtr->depth == 0) {
3856	    parentNodePtr = infoPtr->documentPtr->selfPtr;
3857    } else {
3858	    parentNodePtr = infoPtr->currentNodePtr;
3859    }
3860    nodePtr->parentNodePtr = parentNodePtr;
3861    if (parentNodePtr->firstChildPtr) {
3862	    parentNodePtr->lastChildPtr->nextSiblingPtr = nodePtr;
3863	    nodePtr->previousSiblingPtr = parentNodePtr->lastChildPtr;
3864	    parentNodePtr->lastChildPtr = nodePtr;
3865    } else {
3866	    parentNodePtr->firstChildPtr = parentNodePtr->lastChildPtr = nodePtr;
3867    }
3868
3869    infoPtr->currentNodePtr = nodePtr;
3870    nodePtr->startLine = XML_GetCurrentLineNumber(infoPtr->parser);
3871    nodePtr->startColumn = XML_GetCurrentColumnNumber(infoPtr->parser);
3872    nodePtr->startWidth = infoPtr->currentWidth;
3873
3874    /*
3875     * Add the attribute nodes
3876     */
3877
3878    for (attributePtr = atts; attributePtr[0] && attributePtr[1];
3879			attributePtr += 2) {
3880	    /* XXX add attribute to hash table */
3881	    attributeNodePtr = (TclDomAttributeNode *)
3882				ckalloc(sizeof(TclDomAttributeNode));
3883	    memset(attributeNodePtr, 0, sizeof(TclDomAttributeNode));
3884	    attributeNodePtr->nodeType = ATTRIBUTE_NODE;
3885	    attributeNodePtr->containingDocumentPtr =
3886				nodePtr->containingDocumentPtr;
3887	    attributeNodePtr->nodeName = ckalloc(strlen(attributePtr[0]) + 1);
3888	    strcpy(attributeNodePtr->nodeName, attributePtr[0]);
3889	    attributeNodePtr->parentNodePtr = nodePtr;
3890	    attributeNodePtr->valueLength = strlen((char *) attributePtr[1]);
3891	    attributeNodePtr->nodeValue = (char *)
3892				ckalloc(attributeNodePtr->valueLength+1);
3893	    strcpy(attributeNodePtr->nodeValue, (char *) attributePtr[1]);
3894
3895	    if (nodePtr->firstAttributePtr) {
3896	        nodePtr->lastAttributePtr->nextSiblingPtr = attributeNodePtr;
3897	        nodePtr->lastAttributePtr = attributeNodePtr;
3898	    } else {
3899	        nodePtr->firstAttributePtr = nodePtr->lastAttributePtr =
3900					attributeNodePtr;
3901	    }
3902    }
3903
3904
3905    infoPtr->depth++;
3906    return;
3907}
3908
3909/*
3910 *----------------------------------------------------------------------------
3911 *
3912 * TclDomExpatElementEndHandler --
3913 *
3914 *	    Called by expat for each end tag.
3915 *
3916 * Results:
3917 *	    None.
3918 *
3919 * Side Effects:
3920 *	    Position information is updated for the Element node.
3921 *
3922 *----------------------------------------------------------------------------
3923 */
3924
3925static void
3926TclDomExpatElementEndHandler(
3927    void *userData,		/* Our context for parser */
3928    CONST char *name)		/* Element name */
3929{
3930    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
3931    TclDomNode *nodePtr;
3932
3933    /*
3934     * Invoke the default handler to get the current width
3935     */
3936
3937    XML_DefaultCurrent(infoPtr->parser);
3938
3939    nodePtr = infoPtr->currentNodePtr;
3940    nodePtr->endLine = XML_GetCurrentLineNumber(infoPtr->parser);
3941    nodePtr->endColumn = XML_GetCurrentColumnNumber(infoPtr->parser);
3942    nodePtr->endWidth = infoPtr->currentWidth;
3943    nodePtr->nodeComplete = 1;
3944
3945    /*
3946     * Now trim any empty text nodes if necessary.
3947     */
3948
3949    if (infoPtr->trim) {
3950	    int empty;
3951	    char *p, *last;
3952	    Tcl_UniChar ch;
3953	    TclDomNode *childPtr, *nextSiblingPtr;
3954
3955        for (childPtr = nodePtr->firstChildPtr; childPtr != NULL;
3956	            childPtr = nextSiblingPtr) {
3957            nextSiblingPtr = childPtr->nextSiblingPtr;
3958	        if (childPtr->nodeType == TEXT_NODE) {
3959		        empty = 1;
3960		        p = childPtr->nodeValue;
3961		        last = p + childPtr->valueLength;
3962		        while (p < last) {
3963		            p += Tcl_UtfToUniChar(p, &ch);
3964		            if (!Tcl_UniCharIsSpace(ch)) {
3965			            empty = 0;
3966			            break;
3967                    }
3968                }
3969		        if (empty) {
3970		            UnlinkChild(infoPtr->interpDataPtr, childPtr);
3971		            TclDomDeleteNode(NULL, infoPtr->interpDataPtr, childPtr);
3972                }
3973            }
3974        }
3975    }
3976
3977    infoPtr->depth--;
3978    if (infoPtr->depth != 0) {
3979	    infoPtr->currentNodePtr = infoPtr->currentNodePtr->parentNodePtr;
3980    }
3981}
3982
3983/*
3984 *----------------------------------------------------------------------------
3985 *
3986 * TclDomExpatCharacterDataHandler --
3987 *	Called by expat for character data.
3988 *
3989 * Side Effects:
3990 *	If the current node is a TEXT or CDATA_SECTION node,
3991 *	appends character data to the current node.
3992 *	Otherwise, begins a new TEXT node.
3993 *
3994 *----------------------------------------------------------------------------
3995 */
3996
3997static void
3998TclDomExpatCharacterDataHandler(
3999     void *userData,		    /* Our context for parser */
4000     CONST char *s,		    /* Character text */
4001     int len)			    /* Text length in bytes */
4002{
4003    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
4004    TclDomNode *parentNodePtr;
4005    TclDomTextNode *nodePtr;
4006
4007    parentNodePtr = infoPtr->currentNodePtr;
4008
4009    if (parentNodePtr->lastChildPtr
4010        && (parentNodePtr->lastChildPtr->nodeType == TEXT_NODE
4011            || (parentNodePtr->lastChildPtr->nodeType == CDATA_SECTION_NODE
4012	         && !parentNodePtr->lastChildPtr->nodeComplete)))
4013    {
4014	    /*
4015	     * Combine with sibling TEXT or CDATA_SECTION node
4016	     */
4017	    nodePtr = (TclDomTextNode *) parentNodePtr->lastChildPtr;
4018	    nodePtr->nodeValue = Tcl_Realloc(nodePtr->nodeValue,
4019		nodePtr->valueLength + len + 1);
4020	    memmove(nodePtr->nodeValue + nodePtr->valueLength, s, len);
4021	    nodePtr->valueLength += len;
4022	    nodePtr->nodeValue[nodePtr->valueLength] = 0;
4023	    nodePtr->startWidth = Tcl_NumUtfChars(nodePtr->nodeValue,
4024		nodePtr->valueLength);
4025    } else {
4026	/*
4027	 * Create a new TEXT node.
4028	 */
4029    	nodePtr = (TclDomTextNode *) ckalloc(sizeof(TclDomTextNode));
4030    	memset(nodePtr, 0, sizeof(TclDomTextNode));
4031    	nodePtr->nodeType = TEXT_NODE;
4032    	nodePtr->nodeId = ++infoPtr->interpDataPtr->nodeSeed;
4033    	nodePtr->valueLength = len;
4034    	nodePtr->nodeValue = (char *) ckalloc(len + 1);
4035    	memmove(nodePtr->nodeValue, s, len);
4036    	nodePtr->nodeValue[len] = 0;
4037
4038    	nodePtr->containingDocumentPtr = infoPtr->documentPtr;
4039    	nodePtr->parentNodePtr = parentNodePtr;
4040
4041    	nodePtr->startLine = XML_GetCurrentLineNumber(infoPtr->parser);
4042    	nodePtr->startColumn = XML_GetCurrentColumnNumber(infoPtr->parser);
4043    	nodePtr->startWidth = Tcl_NumUtfChars(s, len);
4044
4045    	if (parentNodePtr->nodeType == ELEMENT_NODE) {
4046	        if (parentNodePtr->firstChildPtr) {
4047		        parentNodePtr->lastChildPtr->nextSiblingPtr
4048		                = (TclDomNode *) nodePtr;
4049		        nodePtr->previousSiblingPtr = parentNodePtr->lastChildPtr;
4050		        parentNodePtr->lastChildPtr = (TclDomNode *) nodePtr;
4051	        } else {
4052		        parentNodePtr->firstChildPtr
4053		                = parentNodePtr->lastChildPtr = (TclDomNode *) nodePtr;
4054	        }
4055	}
4056	nodePtr->nodeComplete = 1;
4057    }
4058    return;
4059}
4060
4061/*
4062 *----------------------------------------------------------------------------
4063 *
4064 * TclDomExpatProcessingInstructionHandler --
4065 *
4066 *	    Called by expat for processing instructions.
4067 *
4068 * Results:
4069 *	    None.
4070 *
4071 * Side Effects:
4072 *	    A node is added to the tree.
4073 *
4074 *----------------------------------------------------------------------------
4075 */
4076
4077static void
4078TclDomExpatProcessingInstructionHandler(
4079     void *userData,		/* Our context for parse */
4080     CONST char *target,	/* Target name */
4081     CONST char *data)		/* Node data */
4082{
4083    TclDomExpatInfo *infoPtr	= (TclDomExpatInfo*)userData;
4084    TclDomNode *nodePtr		= (TclDomNode*)ckalloc(sizeof(TclDomNode));
4085    TclDomNode *parentNodePtr	= infoPtr->currentNodePtr
4086    				? infoPtr->currentNodePtr
4087				: infoPtr->documentPtr->selfPtr
4088				;
4089
4090    memset(nodePtr, 0, sizeof(TclDomNode));
4091
4092    nodePtr->nodeType = PROCESSING_INSTRUCTION_NODE;
4093    nodePtr->containingDocumentPtr = infoPtr->documentPtr;
4094    nodePtr->nodeId = ++infoPtr->interpDataPtr->nodeSeed;
4095
4096    nodePtr->nodeName = ckalloc(strlen(target) + 1);
4097    strcpy(nodePtr->nodeName, target);
4098
4099    nodePtr->valueLength = strlen(data);
4100    nodePtr->nodeValue = ckalloc(nodePtr->valueLength + 1);
4101    strcpy(nodePtr->nodeValue, data);
4102
4103
4104    nodePtr->startLine = XML_GetCurrentLineNumber(infoPtr->parser);
4105    nodePtr->startColumn = XML_GetCurrentColumnNumber(infoPtr->parser);
4106    /* ??? nodePtr->startWidth = ??? */
4107
4108    nodePtr->parentNodePtr = parentNodePtr;
4109    if (parentNodePtr->firstChildPtr) {
4110	parentNodePtr->lastChildPtr->nextSiblingPtr = nodePtr;
4111	nodePtr->previousSiblingPtr = parentNodePtr->lastChildPtr;
4112	parentNodePtr->lastChildPtr = nodePtr;
4113    } else {
4114	parentNodePtr->firstChildPtr = parentNodePtr->lastChildPtr = nodePtr;
4115    }
4116
4117    nodePtr->nodeComplete = 1;
4118    return;
4119}
4120
4121
4122/*
4123 *----------------------------------------------------------------------------
4124 *
4125 * ParseXMLDecl --
4126 *
4127 *	    This procedure parses an XMLDecl[23] string. We're assuming that it's
4128 *	been through xpat and is well-formed.
4129 *
4130 * Results:
4131 *	    None.
4132 *
4133 * Side Effects:
4134 *	May add a node to the tree.
4135 *
4136 *
4137 *----------------------------------------------------------------------------
4138 */
4139void
4140ParseXMLDecl(
4141    TclDomNode *nodePtr,    /* The document node */
4142    CONST char *s,	    /* The XMLDecl text */
4143    int len)		    /* Length of text */
4144{
4145    int c = *s;
4146    TclDomAttributeNode *attributeNodePtr = NULL;
4147    enum parseState {
4148	    UNDEFINED, VERSION_INFO, ENCODING_DECL, SD_DECL, VALUE
4149    };
4150    enum parseState state = UNDEFINED;
4151
4152    while (len) {
4153	    c = *s;
4154	    if (c == ' ' || c == '\t' || c == '\n' || c == '\r'
4155		        || c == '=') {
4156	        s++;
4157	        len--;
4158	        continue;
4159	    }
4160	    if (strncmp(s, "<?xml", 5) == 0) {
4161	        s += 5;
4162	        len -= 5;
4163	        continue;
4164	    }
4165	    if (strncmp(s, "version", 6) == 0) {
4166	        s += 7;
4167	        len -= 7;
4168	        state = VERSION_INFO;
4169	    } else if (strncmp(s, "encoding", 8) == 0) {
4170	        s += 8;
4171	        len -= 8;
4172	        state = ENCODING_DECL;
4173	    } else if (strncmp(s, "standalone", 10) == 0) {
4174	        s += 10;
4175	        len -= 10;
4176	        state = SD_DECL;
4177	    } else if (c == '\'' || c == '\"') {
4178	        int count;
4179	        char *endChar;
4180	        s++;
4181	        len--;
4182	        count = 0;
4183	        endChar = (char *) s;
4184	        while (count < len) {
4185		        if (*endChar == c) break;
4186		        endChar++;
4187		        count++;
4188	        }
4189	        if (*endChar != c) {
4190		        /*
4191		         * expat should really never get us to this state
4192		         * Just skip over this attribute
4193		         */
4194		        if (attributeNodePtr) {
4195		            if (attributeNodePtr->nodeName) {
4196			            ckfree(attributeNodePtr->nodeName);
4197		            }
4198		            ckfree((char *) attributeNodePtr);
4199		            attributeNodePtr = NULL;
4200		        continue;
4201		        }
4202	        }
4203	        if (attributeNodePtr) {
4204		        attributeNodePtr->valueLength = count;
4205		        attributeNodePtr->nodeValue = (char *) ckalloc(count+1);
4206		        memcpy(attributeNodePtr->nodeValue, s, count);
4207		        attributeNodePtr->nodeValue[count] = 0;
4208		        if (nodePtr->firstAttributePtr) {
4209		            nodePtr->lastAttributePtr->nextSiblingPtr =
4210							attributeNodePtr;
4211		            nodePtr->lastAttributePtr = attributeNodePtr;
4212		        } else {
4213		            nodePtr->firstAttributePtr = nodePtr->lastAttributePtr =
4214							attributeNodePtr;
4215		        }
4216		        attributeNodePtr = NULL;
4217	        }
4218	        len -= (count+1);
4219	        s += (count+1);
4220	        continue;
4221	    }
4222
4223	    if (state == VERSION_INFO || state == ENCODING_DECL
4224				|| state == SD_DECL) {
4225	        attributeNodePtr = (TclDomAttributeNode *)
4226					ckalloc(sizeof(TclDomAttributeNode));
4227	        memset(attributeNodePtr, 0, sizeof(TclDomAttributeNode));
4228	        attributeNodePtr->nodeType = ATTRIBUTE_NODE;
4229	        attributeNodePtr->containingDocumentPtr =
4230					nodePtr->containingDocumentPtr;
4231	        if (state == VERSION_INFO) {
4232		        attributeNodePtr->nodeName = ckalloc(8);
4233		        strcpy(attributeNodePtr->nodeName, "version");
4234	        } else if (state == ENCODING_DECL) {
4235		        attributeNodePtr->nodeName = ckalloc(9);
4236		        strcpy(attributeNodePtr->nodeName, "encoding");
4237	        } else {
4238		        attributeNodePtr->nodeName = ckalloc(11);
4239		        strcpy(attributeNodePtr->nodeName, "standalone");
4240	        }
4241	        attributeNodePtr->parentNodePtr = nodePtr;
4242	        state = VALUE;
4243	    }
4244	    len--;
4245	    s++;
4246    }
4247}
4248
4249
4250/*
4251 *----------------------------------------------------------------------------
4252 *
4253 * TclDomExpatDefaultHandler --
4254 *
4255 *	    Called by expat for processing data which has no other handler.
4256 *	If the node is an XMLDecl[23], then we save the version info,
4257 *	etc. If the node is a doctypedecl[28], then we do likewise.
4258 *	Otherwise, this handler has been explicitly invoked by an
4259 *	Element, Text, etc., handler, and we compute width information
4260 *	for the node.
4261 *
4262 * Results:
4263 *	    None.
4264 *
4265 * Side Effects:
4266 *	May add a node to the tree.
4267 *
4268 *
4269 *----------------------------------------------------------------------------
4270 */
4271
4272static void
4273TclDomExpatDefaultHandler(
4274     void *userData,	    /* Our context for parser */
4275     CONST char *s,	    /* String value of token */
4276     int len)		    /* String length */
4277{
4278    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
4279
4280    if (strncmp("<?xml", s, 5) == 0) {
4281	    ParseXMLDecl(infoPtr->documentPtr->selfPtr, s, len);
4282    }
4283
4284    /*
4285     * Set the width information
4286     */
4287    infoPtr->currentWidth = Tcl_NumUtfChars(s, len);
4288    return;
4289}
4290
4291/*
4292 *----------------------------------------------------------------------
4293 *
4294 * TclDomExpatStartDoctypeDeclHandler --
4295 *
4296 *	Called by expat to process the doctype declaration.
4297 *
4298 * Results:
4299 *	None.
4300 *
4301 * Side effects:
4302 *	Sets the doctype.
4303 *
4304 *----------------------------------------------------------------------
4305 */
4306
4307static void
4308TclDomExpatStartDoctypeDeclHandler(
4309    void *userData,		 /* Our context for parser. */
4310    const XML_Char *doctypeName, /* The root element of the document. */
4311    const XML_Char *sysid,	 /* SYSTEM identifier */
4312    const XML_Char *pubid,	 /* PUBLIC identifier */
4313    int has_internal_subset)
4314{
4315    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
4316    TclDomNode *parentNodePtr;
4317    TclDomDocTypeNode *nodePtr;
4318
4319    if (infoPtr->currentNodePtr) {
4320	parentNodePtr = infoPtr->currentNodePtr;
4321    } else {
4322	parentNodePtr = infoPtr->documentPtr->selfPtr;
4323    }
4324
4325    nodePtr = (TclDomDocTypeNode *) ckalloc(sizeof(TclDomDocTypeNode));
4326    memset(nodePtr, 0, sizeof(TclDomDocTypeNode));
4327    nodePtr->nodeType = DOCUMENT_TYPE_NODE;
4328    nodePtr->nodeId = ++infoPtr->interpDataPtr->nodeSeed;
4329
4330    nodePtr->containingDocumentPtr = infoPtr->documentPtr;
4331    nodePtr->parentNodePtr = parentNodePtr;
4332
4333    infoPtr->currentNodePtr = (TclDomNode *) nodePtr;
4334    nodePtr->startLine = XML_GetCurrentLineNumber(infoPtr->parser);
4335    nodePtr->startColumn = XML_GetCurrentColumnNumber(infoPtr->parser);
4336
4337    nodePtr->nodeName = ckalloc(strlen(doctypeName)+1);
4338    strcpy(nodePtr->nodeName, doctypeName);
4339
4340    nodePtr->systemId = nodePtr->publicId = nodePtr->internalSubset = NULL;
4341    if (sysid) {
4342	nodePtr->systemId = ckalloc(strlen(sysid)+1);
4343	strcpy(nodePtr->systemId, sysid);
4344    }
4345    if (pubid) {
4346	nodePtr->publicId = ckalloc(strlen(pubid)+1);
4347	strcpy(nodePtr->publicId, pubid);
4348    }
4349
4350    if (parentNodePtr->firstChildPtr) {
4351	parentNodePtr->lastChildPtr->nextSiblingPtr = (TclDomNode *) nodePtr;
4352	nodePtr->previousSiblingPtr = parentNodePtr->lastChildPtr;
4353	parentNodePtr->lastChildPtr = (TclDomNode *) nodePtr;
4354    } else {
4355	parentNodePtr->firstChildPtr
4356	    = parentNodePtr->lastChildPtr = (TclDomNode *) nodePtr;
4357    }
4358}
4359
4360/*
4361 *----------------------------------------------------------------------
4362 *
4363 * TclDomExpatEndDoctypeDeclHandler --
4364 *
4365 *	Called by expat to process the end of the doctype declaration.
4366 *
4367 * Results:
4368 *	None.
4369 *
4370 * Side effects:
4371 *	Sets the doctype.
4372 *
4373 *----------------------------------------------------------------------
4374 */
4375
4376static void
4377TclDomExpatEndDoctypeDeclHandler(
4378    void *userData)		/* Our context for parser */
4379{
4380    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
4381    TclDomNode *nodePtr;
4382
4383    nodePtr = infoPtr->currentNodePtr;
4384    nodePtr->endLine = XML_GetCurrentLineNumber(infoPtr->parser);
4385    nodePtr->endColumn = XML_GetCurrentColumnNumber(infoPtr->parser);
4386    nodePtr->nodeComplete = 1;
4387    infoPtr->currentNodePtr = NULL;
4388}
4389
4390/*
4391 *----------------------------------------------------------------------------
4392 *
4393 * TclDomExpatUnparsedDeclHandler --
4394 *
4395 *	    Called by expat for processing an unparsed entity references.
4396 *
4397 * Results:
4398 *	    None.
4399 *
4400 * Side Effects:
4401 *
4402 *	Currently none.
4403 *
4404 *----------------------------------------------------------------------------
4405 */
4406
4407static void
4408TclDomExpatUnparsedDeclHandler(
4409     void *userData,		/* Our context for parser */
4410     CONST char *entityname,	/* Name of entity */
4411     CONST char *base,		/* Base string	*/
4412     CONST char *systemId,	/* System id string */
4413     CONST char *publicId,	/* Public id string */
4414     CONST char *notationName)	/* Notation name string */
4415{
4416    TclDomExpatInfo *expat = (TclDomExpatInfo *) userData;
4417    return;
4418}
4419
4420/*
4421 *----------------------------------------------------------------------------
4422 *
4423 * TclDomExpatNotationDeclHandler --
4424 *
4425 *	    Called by expat for processing a notation declaration.
4426 *
4427 * Results:
4428 *	    None.
4429 *
4430 * Side Effects:
4431 *	    Currently none.
4432 *
4433 *----------------------------------------------------------------------------
4434 */
4435
4436static void
4437TclDomExpatNotationDeclHandler(
4438     void *userData,		    /* Our context for parser */
4439     CONST char *notationName,	    /* Notation name string */
4440     CONST char *base,		    /* Base string */
4441     CONST char *systemId,	    /* System Id string */
4442     CONST char *publicId)	    /* Public id string */
4443{
4444    TclDomExpatInfo *expat = (TclDomExpatInfo *) userData;
4445    return;
4446}
4447
4448/*
4449 *----------------------------------------------------------------------------
4450 *
4451 * TclDomExpatUnknownEncodingHandler --
4452 *
4453 *	    Called by expat for processing a reference to a character in an
4454 *	    unknown encoding.
4455 *
4456 * Results:
4457 *	    None.
4458 *
4459 * Side Effects:
4460 *	    Currently none.
4461 *
4462 *----------------------------------------------------------------------------
4463 */
4464
4465static int
4466TclDomExpatUnknownEncodingHandler(
4467     void *encodingHandlerData,		/* Our context for parser */
4468     CONST char *name,			/* Character */
4469     XML_Encoding *info)		/* Encoding info */
4470{
4471    TclDomExpatInfo *expat = (TclDomExpatInfo *) encodingHandlerData;
4472    return 0;
4473}
4474
4475/*
4476 *----------------------------------------------------------------------------
4477 *
4478 * TclDomExpatExternalEntityRefHandler --
4479 *
4480 *	    Called by expat for processing external entity references.
4481 *
4482 * Results:
4483 *	    None.
4484 *
4485 * Side Effects:
4486 *	    Currently none.
4487 *
4488 *----------------------------------------------------------------------------
4489 */
4490
4491static int
4492TclDomExpatExternalEntityRefHandler(
4493     XML_Parser parser,		    /* Our context for parser */
4494     CONST char *openEntityNames,   /* Open entities */
4495     CONST char *base,		    /* Base */
4496     CONST char *systemId,	    /* System id string */
4497     CONST char *publicId)	    /* Public id string */
4498{
4499    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) XML_GetUserData(parser);
4500    TclDomDocTypeNode *nodePtr = (TclDomDocTypeNode *) infoPtr->currentNodePtr;
4501
4502    return 1;
4503}
4504
4505/*
4506 *----------------------------------------------------------------------------
4507 *
4508 * TclDomExpatCommentHandler --
4509 *
4510 *	    Called by expat to handle comments encountered while parsing
4511 *	Added by ericm@scriptics.com, 1999.6.25.
4512 *
4513 * Results:
4514 *	    None.
4515 *
4516 * Side Effects:
4517 *	    A node is added to the tree.
4518 *
4519 *----------------------------------------------------------------------------
4520 */
4521static void
4522TclDomExpatCommentHandler(
4523    void *userData,		    /* Our context for parser */
4524    const char *data)		    /* Comment string */
4525{
4526    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
4527    TclDomNode *parentNodePtr;
4528    TclDomTextNode *nodePtr;
4529    int len = strlen(data);
4530
4531    /*
4532     * Invoke the default handler to get the current width
4533     */
4534
4535    XML_DefaultCurrent(infoPtr->parser);
4536
4537    if (infoPtr->currentNodePtr) {
4538	    parentNodePtr = infoPtr->currentNodePtr;
4539    } else {
4540	    parentNodePtr = infoPtr->documentPtr->selfPtr;
4541    }
4542
4543    nodePtr = (TclDomTextNode *) ckalloc(sizeof(TclDomTextNode));
4544    memset(nodePtr, 0, sizeof(TclDomTextNode));
4545    nodePtr->nodeType = COMMENT_NODE;
4546    nodePtr->nodeId = ++infoPtr->interpDataPtr->nodeSeed;
4547    nodePtr->valueLength = len;
4548    nodePtr->nodeValue = (char *) ckalloc(len + 1);
4549    memmove(nodePtr->nodeValue, data, len);
4550    nodePtr->nodeValue[len] = 0;
4551
4552    nodePtr->containingDocumentPtr = infoPtr->documentPtr;
4553    nodePtr->parentNodePtr = parentNodePtr;
4554    nodePtr->startLine = nodePtr->endLine =
4555			XML_GetCurrentLineNumber(infoPtr->parser);
4556    nodePtr->startColumn = nodePtr->endLine =
4557			XML_GetCurrentColumnNumber(infoPtr->parser);
4558    nodePtr->startWidth = nodePtr->endWidth = infoPtr->currentWidth;
4559    nodePtr->nodeComplete = 1;
4560
4561    if (parentNodePtr->nodeType == ELEMENT_NODE
4562	        || parentNodePtr->nodeType == DOCUMENT_NODE
4563	        || parentNodePtr->nodeType == DOCUMENT_FRAGMENT_NODE
4564	        || parentNodePtr->nodeType == ENTITY_REFERENCE_NODE
4565	        || parentNodePtr->nodeType == ENTITY_NODE) {
4566	    if (parentNodePtr->firstChildPtr) {
4567	        parentNodePtr->lastChildPtr->nextSiblingPtr =
4568					(TclDomNode *) nodePtr;
4569	        nodePtr->previousSiblingPtr = parentNodePtr->lastChildPtr;
4570	        parentNodePtr->lastChildPtr = (TclDomNode *) nodePtr;
4571	    } else {
4572	        parentNodePtr->firstChildPtr = parentNodePtr->lastChildPtr =
4573					(TclDomNode *) nodePtr;
4574	    }
4575    } else {
4576    }
4577    return;
4578}
4579
4580/*
4581 *----------------------------------------------------------------------------
4582 *
4583 * TclDomExpatNotStandaloneHandler --
4584 *
4585 *	    Called by expat to handle "not standalone" documents (ie, documents
4586 *	that have an external subset or a reference to a parameter entity,
4587 *	but do not have standalone="yes")
4588 *	Added by ericm@scriptics.com, 1999.6.25.
4589 *
4590 * Results:
4591 *	    None.
4592 *
4593 * Side Effects:
4594 *	    Currently none.
4595 *
4596 *----------------------------------------------------------------------------
4597 */
4598static int
4599TclDomExpatNotStandaloneHandler(
4600    void *userData)		    /* Data */
4601{
4602    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
4603    return 1;
4604}
4605
4606/*
4607 *----------------------------------------------------------------------------
4608 *
4609 * TclDomExpatStartCdataSectionHandler --
4610 *
4611 *	Called by expat to handle CDATA section starts.
4612 *	Added by ericm@scriptics.com, 1999.6.25.
4613 *
4614 * Side Effects:
4615 *	Begins a new CDATA_SECTION node.
4616 *
4617 *----------------------------------------------------------------------------
4618 */
4619static void
4620TclDomExpatStartCdataSectionHandler(void *userData)
4621{
4622    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
4623    TclDomNode      *parentNodePtr;
4624    TclDomTextNode  *nodePtr;
4625
4626    parentNodePtr = infoPtr->currentNodePtr;
4627
4628    /* Allocate a new text node */
4629    nodePtr = (TclDomTextNode *) ckalloc(sizeof(TclDomTextNode));
4630    memset(nodePtr, 0, sizeof(TclDomTextNode));
4631    nodePtr->nodeType = CDATA_SECTION_NODE;
4632    nodePtr->nodeId = ++infoPtr->interpDataPtr->nodeSeed;
4633
4634    /* configure for our tree */
4635    nodePtr->containingDocumentPtr = infoPtr->documentPtr;
4636    nodePtr->parentNodePtr = parentNodePtr;
4637    nodePtr->startLine = XML_GetCurrentLineNumber(infoPtr->parser);
4638    nodePtr->startColumn = XML_GetCurrentColumnNumber(infoPtr->parser);
4639
4640    /* insert into the tree */
4641    if (parentNodePtr->nodeType == ELEMENT_NODE) {
4642	if (parentNodePtr->firstChildPtr) {
4643	    parentNodePtr->lastChildPtr->nextSiblingPtr
4644		    = (TclDomNode *) nodePtr;
4645	    nodePtr->previousSiblingPtr = parentNodePtr->lastChildPtr;
4646	    parentNodePtr->lastChildPtr = (TclDomNode *) nodePtr;
4647	} else {
4648	    parentNodePtr->firstChildPtr
4649		    = parentNodePtr->lastChildPtr = (TclDomNode *) nodePtr;
4650	}
4651    } else {
4652    }
4653}
4654
4655
4656/*
4657 *----------------------------------------------------------------------------
4658 *
4659 * TclDomExpatEndCdataSectionHandler
4660 *	Called by expat to handle CDATA section ends
4661 *
4662 * Side Effects:
4663 *	Finishes the current CDATA_SECTION_NODE.
4664 *
4665 *----------------------------------------------------------------------------
4666 */
4667static void
4668TclDomExpatEndCdataSectionHandler(void *userData)
4669{
4670    TclDomExpatInfo *infoPtr = (TclDomExpatInfo *) userData;
4671    TclDomNode *parentNodePtr = infoPtr->currentNodePtr;
4672    TclDomNode *nodePtr = parentNodePtr->lastChildPtr;
4673
4674    nodePtr->endLine = XML_GetCurrentLineNumber(infoPtr->parser);
4675    nodePtr->endColumn = XML_GetCurrentColumnNumber(infoPtr->parser);
4676    nodePtr->nodeComplete = 1;
4677    return;
4678}
4679
4680
4681/*
4682 *----------------------------------------------------------------------------
4683 *
4684 * SerializeDocument
4685 *
4686 *	    This procedure serializes a Document node.
4687 *
4688 * Results:
4689 *	    None.
4690 *
4691 * Side Effects:
4692 *	    None.
4693 *
4694 *----------------------------------------------------------------------------
4695 */
4696
4697static void
4698SerializeDocument(
4699    TclDomNode *nodePtr,	    /* Node to be serialized */
4700    Tcl_DString *output)	    /* Output string to append to */
4701{
4702    TclDomNode *childPtr;
4703    TclDomAttributeNode *attributeNodePtr;
4704
4705    if (nodePtr->firstAttributePtr) {
4706	    Tcl_DStringAppend(output, "<?xml", 5);
4707	    for (attributeNodePtr = nodePtr->firstAttributePtr; attributeNodePtr;
4708		        attributeNodePtr = attributeNodePtr->nextSiblingPtr) {
4709	        SerializeAttribute(attributeNodePtr, output);
4710	    }
4711	    Tcl_DStringAppend(output, "?>", 2);
4712    } else {
4713	    Tcl_DStringAppend(output, "<?xml version='1.0'?>", -1);
4714    }
4715    Tcl_DStringAppend(output, "\n", 1);
4716
4717
4718    if (TclDomGetDoctypeNode(nodePtr->containingDocumentPtr) == NULL) {
4719	/*
4720	 * Fabricate docType from first element
4721	 */
4722	TclDomNode *documentElementPtr
4723		= TclDomGetDocumentElement(nodePtr->containingDocumentPtr);
4724	if (documentElementPtr && documentElementPtr->nodeName) {
4725	    Tcl_DStringAppend(output, "<!DOCTYPE ", -1);
4726	    Tcl_DStringAppend(output, documentElementPtr->nodeName, -1);
4727	    Tcl_DStringAppend(output, ">", 1);
4728	}
4729	Tcl_DStringAppend(output, "\n", 1);
4730    }
4731
4732    for (childPtr = nodePtr->firstChildPtr; childPtr;
4733			childPtr = childPtr->nextSiblingPtr) {
4734	    SerializeWalk(childPtr, output);
4735    }
4736}
4737
4738
4739/*
4740 *----------------------------------------------------------------------------
4741 *
4742 * SerializeElement
4743 *
4744 *	    This procedure serializes an Element node.
4745 *
4746 * Results:
4747 *	    None.
4748 *
4749 * Side Effects:
4750 *	    None.
4751 *
4752 *----------------------------------------------------------------------------
4753 */
4754
4755static void
4756SerializeElement(
4757    TclDomNode *nodePtr,	    /* Node to be serialized */
4758    Tcl_DString *output)	    /* Output string to append to */
4759{
4760    TclDomNode *childPtr;
4761    TclDomAttributeNode *attributeNodePtr;
4762    int isDocumentElement = nodePtr->parentNodePtr != NULL
4763			&& nodePtr->parentNodePtr->nodeType == DOCUMENT_NODE;
4764
4765    if (nodePtr->firstChildPtr || isDocumentElement)
4766    {
4767	    Tcl_DStringAppend(output, "<", 1);
4768	    Tcl_DStringAppend(output, nodePtr->nodeName, -1);
4769	    for (attributeNodePtr = nodePtr->firstAttributePtr; attributeNodePtr;
4770		        attributeNodePtr = attributeNodePtr->nextSiblingPtr) {
4771	        SerializeAttribute(attributeNodePtr, output);
4772	    }
4773	    Tcl_DStringAppend(output, ">", 1);
4774	    for (childPtr = nodePtr->firstChildPtr; childPtr;
4775				childPtr = childPtr->nextSiblingPtr) {
4776	        SerializeWalk(childPtr, output);
4777	    }
4778	    Tcl_DStringAppend(output, "</", 2);
4779	    Tcl_DStringAppend(output, nodePtr->nodeName, -1);
4780	    Tcl_DStringAppend(output, ">", 1);
4781    } else {
4782	    Tcl_DStringAppend(output, "<", 1);
4783	    Tcl_DStringAppend(output, nodePtr->nodeName, -1);
4784	    for (attributeNodePtr = nodePtr->firstAttributePtr; attributeNodePtr;
4785		        attributeNodePtr = attributeNodePtr->nextSiblingPtr) {
4786	        SerializeAttribute(attributeNodePtr, output);
4787	    }
4788	    Tcl_DStringAppend(output, "/>", 2);
4789    }
4790}
4791
4792
4793/*
4794 *----------------------------------------------------------------------------
4795 *
4796 * EscapeText --
4797 *	Helper function for SerializeAttribute() and SerializeText().
4798 *	Appends text to output DString buffer, replacing XML markup
4799 *	characters '<', '&', and '>' with appropriate entity references.
4800 *
4801 *	If the 'escapeAll' flag is set, also replaces ' and ".
4802 *
4803 * BUGS:
4804 *	This is not UNICODE-aware.
4805 *
4806 *----------------------------------------------------------------------------
4807 */
4808
4809static void
4810EscapeText(Tcl_DString *output, TclDomString s, int escapeAll)
4811{
4812    char *escapeChars = escapeAll ? "<>&\"'" : "<>&";
4813
4814    while (*s) {
4815    	char *t = strpbrk(s, escapeChars);
4816	if (!t) { /* No escapable characters left */
4817	    Tcl_DStringAppend(output,s,-1);
4818	    break;
4819	}
4820	if (t > s)
4821	    Tcl_DStringAppend(output,s,t-s);
4822	switch (*t) {
4823	    case '<' : Tcl_DStringAppend(output, "&lt;", -1); break;
4824	    case '>' : Tcl_DStringAppend(output, "&gt;", -1); break;
4825	    case '&' : Tcl_DStringAppend(output, "&amp;", -1); break;
4826	    case '"' : Tcl_DStringAppend(output, "&quot;", -1); break;
4827	    case '\'': Tcl_DStringAppend(output, "&apos;", -1); break;
4828	    default:   Tcl_DStringAppend(output, t, 1);
4829	}
4830	s = t+1;
4831    }
4832}
4833
4834
4835/*
4836 *----------------------------------------------------------------------------
4837 *
4838 * SerializeAttribute
4839 *	This procedure serializes an attribute node.
4840 *
4841 * Side Effects:
4842 *	Appends an attribute value specification " attname = 'attval'"
4843 *	to the output buffer.
4844 *
4845 *----------------------------------------------------------------------------
4846 */
4847
4848static void
4849SerializeAttribute(
4850    TclDomAttributeNode *attributeNodePtr,	/* Node to be serialized */
4851    Tcl_DString *output)			/* Output string to append to */
4852{
4853    Tcl_DStringAppend(output, " ", 1);
4854    Tcl_DStringAppend(output, attributeNodePtr->nodeName, -1);
4855    Tcl_DStringAppend(output, "=\'", 2);
4856    EscapeText(output, attributeNodePtr->nodeValue, 1);
4857    Tcl_DStringAppend(output, "\'", 1);
4858}
4859
4860
4861/*
4862 *----------------------------------------------------------------------------
4863 *
4864 * SerializeText
4865 *	This procedure serializes a Text node.
4866 *
4867 * Side Effects:
4868 *	Appends character data to the output buffer.
4869 *	with XML markup characters replaced by entity references.
4870 *
4871 *----------------------------------------------------------------------------
4872 */
4873
4874static void
4875SerializeText(
4876    TclDomNode *nodePtr,			/* Node to be serialized */
4877    Tcl_DString *output)			/* Output string to append to */
4878{
4879    TclDomTextNode *textNodePtr = (TclDomTextNode *) nodePtr;
4880    EscapeText(output, textNodePtr->nodeValue, 0);
4881}
4882
4883
4884/*
4885 *----------------------------------------------------------------------------
4886 *
4887 * SerializeComment
4888 *
4889 *	    This procedure serializes a Comment node.
4890 *
4891 * Results:
4892 *	    None.
4893 *
4894 * Side Effects:
4895 *	    None.
4896 *
4897 *----------------------------------------------------------------------------
4898 */
4899
4900static void
4901SerializeComment(
4902    TclDomNode *nodePtr,			/* Node to be serialized */
4903    Tcl_DString *output)			/* Output string to append to */
4904{
4905    TclDomTextNode *commentNodePtr = (TclDomTextNode *) nodePtr;
4906
4907    Tcl_DStringAppend(output, "<!--", 4);
4908    Tcl_DStringAppend(output, commentNodePtr->nodeValue, -1);
4909    Tcl_DStringAppend(output, "-->", 3);
4910}
4911
4912
4913/*
4914 *----------------------------------------------------------------------------
4915 *
4916 * SerializeProcessingInstruction
4917 *
4918 *	    This procedure serializes a ProcessingInstruction node.
4919 *
4920 * Results:
4921 *	    None.
4922 *
4923 * Side Effects:
4924 *	    None.
4925 *
4926 *----------------------------------------------------------------------------
4927 */
4928
4929static void
4930SerializeProcessingInstruction(
4931    TclDomNode *nodePtr,			/* Node to be serialized */
4932    Tcl_DString *output)			/* Output string to append to */
4933{
4934    Tcl_DStringAppend(output, "<?", 2);
4935    Tcl_DStringAppend(output, nodePtr->nodeName, -1);
4936    Tcl_DStringAppend(output, " ", 1);
4937    Tcl_DStringAppend(output, nodePtr->nodeValue, -1);
4938    Tcl_DStringAppend(output, "?>", 2);
4939}
4940
4941
4942/*
4943 *----------------------------------------------------------------------------
4944 *
4945 * SerializeEntity
4946 *
4947 *	    This procedure serializes an Entity node.
4948 *
4949 * Results:
4950 *	    None.
4951 *
4952 * Side Effects:
4953 *	    None.
4954 *
4955 *----------------------------------------------------------------------------
4956 */
4957
4958static void
4959SerializeEntity(
4960    TclDomNode *nodePtr,			/* Node to be serialized */
4961    Tcl_DString *output)			/* Output string to append to */
4962{
4963}
4964
4965
4966
4967/*
4968 *----------------------------------------------------------------------------
4969 *
4970 * SerializeNotation
4971 *
4972 *	    This procedure serializes a Notation node.
4973 *
4974 * Results:
4975 *	    None.
4976 *
4977 * Side Effects:
4978 *	    None.
4979 *
4980 *----------------------------------------------------------------------------
4981 */
4982
4983static void
4984SerializeNotation(
4985    TclDomNode *nodePtr,			/* Node to be serialized */
4986    Tcl_DString *output)			/* Output string to append to */
4987{
4988}
4989
4990
4991
4992/*
4993 *----------------------------------------------------------------------------
4994 *
4995 * SerializeDocumentType
4996 *
4997 *	    This procedure serializes a DocumentType node.
4998 *
4999 * Results:
5000 *	    None.
5001 *
5002 * Side Effects:
5003 *	    None.
5004 *
5005 *----------------------------------------------------------------------------
5006 */
5007
5008
5009static void
5010SerializeDocumentType(
5011    TclDomNode *nodePtr,		    /* Node to be serialized */
5012    Tcl_DString *output)		    /* Output string to append to */
5013{
5014    TclDomDocTypeNode *docTypeNodePtr = (TclDomDocTypeNode *) nodePtr;
5015    Tcl_DStringAppend(output, "<!DOCTYPE", -1);
5016	if (docTypeNodePtr->nodeName) {
5017	    Tcl_DStringAppend(output, " ", 1);
5018	    Tcl_DStringAppend(output, docTypeNodePtr->nodeName, -1);
5019		if (docTypeNodePtr->publicId && docTypeNodePtr->systemId) {
5020	    	Tcl_DStringAppend(output, " PUBLIC ", 1);
5021	    	Tcl_DStringAppend(output, docTypeNodePtr->publicId, -1);
5022	    	Tcl_DStringAppend(output, " ", 1);
5023	     	Tcl_DStringAppend(output, docTypeNodePtr->systemId, -1);
5024		} else if (docTypeNodePtr->systemId) {
5025	    	Tcl_DStringAppend(output, " SYSTEM ", 1);
5026	     	Tcl_DStringAppend(output, docTypeNodePtr->systemId, -1);
5027		}
5028	} else if (docTypeNodePtr->nodeValue) {
5029	    int c0 = *docTypeNodePtr->nodeValue;
5030	    if (c0 != ' ' && c0 != '\t' && c0 != '\n' && c0 != '\r') {
5031	        Tcl_DStringAppend(output, " ", 1);
5032	    }
5033	    Tcl_DStringAppend(output, docTypeNodePtr->nodeValue, -1);
5034    }
5035    Tcl_DStringAppend(output, ">\n", 2);
5036}
5037
5038
5039
5040/*
5041 *----------------------------------------------------------------------------
5042 *
5043 * SerializeEntityReference
5044 *
5045 *	    This procedure serializes an EntityReference node.
5046 *
5047 * Results:
5048 *	    None.
5049 *
5050 * Side Effects:
5051 *	    None.
5052 *
5053 *----------------------------------------------------------------------------
5054 */
5055
5056static void
5057SerializeEntityReference(
5058    TclDomNode *nodePtr,			/* Node to be serialized */
5059    Tcl_DString *output)			/* Output string to append to */
5060{
5061}
5062
5063
5064
5065/*
5066 *----------------------------------------------------------------------------
5067 *
5068 * SerializeCDATA
5069 *
5070 *	    This procedure serializes a CDATA Section node.
5071 *
5072 * Results:
5073 *	    None.
5074 *
5075 * Side Effects:
5076 *	    None.
5077 *
5078 *----------------------------------------------------------------------------
5079 */
5080
5081static void
5082SerializeCDATA(
5083    TclDomNode *nodePtr,		    /* Node to be serialized */
5084    Tcl_DString *output)		    /* Output string to append to */
5085{
5086    Tcl_DStringAppend(output, "<![CDATA[", 9);
5087    Tcl_DStringAppend(output, nodePtr->nodeValue, -1);
5088    Tcl_DStringAppend(output, "]]>", 3);
5089}
5090
5091
5092
5093/*
5094 *----------------------------------------------------------------------------
5095 *
5096 * SerializeWalk
5097 *
5098 *	    This procedure walks the tree to serialize a node.
5099 *
5100 * Results:
5101 *	    None.
5102 *
5103 * Side Effects:
5104 *	    None.
5105 *
5106 *----------------------------------------------------------------------------
5107 */
5108
5109static void
5110SerializeWalk(
5111    TclDomNode *nodePtr,		/* Node to be serialized */
5112    Tcl_DString *output)		/* Output string to append to */
5113{
5114    switch (nodePtr->nodeType) {
5115	    case ELEMENT_NODE:
5116	        SerializeElement(nodePtr, output);
5117	        break;
5118
5119	    case TEXT_NODE:
5120	        SerializeText(nodePtr, output);
5121	        break;
5122
5123	    case CDATA_SECTION_NODE:
5124	        SerializeCDATA(nodePtr, output);
5125	        break;
5126
5127	    case ENTITY_REFERENCE_NODE:
5128	        SerializeEntityReference(nodePtr, output);
5129	        break;
5130
5131	    case ENTITY_NODE:
5132	        SerializeEntity(nodePtr, output);
5133	        break;
5134
5135	    case PROCESSING_INSTRUCTION_NODE:
5136	        SerializeProcessingInstruction(nodePtr, output);
5137	        break;
5138
5139	    case COMMENT_NODE:
5140	        SerializeComment(nodePtr, output);
5141	        break;
5142
5143	    case DOCUMENT_NODE:
5144	        SerializeDocument(nodePtr, output);
5145	        break;
5146
5147	    case DOCUMENT_TYPE_NODE:
5148	        SerializeDocumentType(nodePtr, output);
5149	        break;
5150
5151	    case DOCUMENT_FRAGMENT_NODE:
5152	        /*
5153	         * Shouldn't occur in a document
5154	         */
5155	        break;
5156
5157	    case NOTATION_NODE:
5158	        SerializeNotation(nodePtr, output);
5159	        break;
5160
5161	    default:
5162	        break;
5163    }
5164}
5165
5166
5167
5168/*
5169 *----------------------------------------------------------------------------
5170 *
5171 * TclDomSerialize
5172 *
5173 *	    This procedure serializes a node and returns the
5174 *	XML to the interpreter's output.
5175 *
5176 * Results:
5177 *	    None.
5178 *
5179 * Side Effects:
5180 *	    None.
5181 *
5182 *----------------------------------------------------------------------------
5183 */
5184
5185int
5186TclDomSerialize(
5187    Tcl_Interp *interp,		    /* Tcl intepreter to output to */
5188    TclDomNode *nodePtr)    /* Document to serialize */
5189{
5190    Tcl_DString output;
5191
5192    if (nodePtr->nodeType == DOCUMENT_NODE
5193	&& TclDomGetDocumentElement(nodePtr->containingDocumentPtr) == NULL) {
5194	    Tcl_AppendResult(interp, "document has no document element",
5195				(char *) NULL);
5196	    return TCL_ERROR;
5197    }
5198
5199    Tcl_DStringInit(&output);
5200
5201    SerializeWalk(nodePtr, &output);
5202
5203    Tcl_DStringResult(interp, &output);
5204    return TCL_OK;
5205}
5206
5207int TclDomHasChildren(TclDomNode *nodePtr)
5208{
5209    int hasChildren = ((nodePtr->nodeType == ELEMENT_NODE
5210		    || nodePtr->nodeType == DOCUMENT_NODE)
5211            && (nodePtr->firstChildPtr != NULL));
5212    return hasChildren;
5213}
5214
5215
5216