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, "<", -1); break; 4824 case '>' : Tcl_DStringAppend(output, ">", -1); break; 4825 case '&' : Tcl_DStringAppend(output, "&", -1); break; 4826 case '"' : Tcl_DStringAppend(output, """, -1); break; 4827 case '\'': Tcl_DStringAppend(output, "'", -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