1/* This code implements a set of tDOM C Handlers that can be 2 dynamically loaded into a tclsh with already loaded tDOM 3 package. This parser extension does some tests according to the DTD 4 against the data within an XML document. 5 6 Copyright (c) 2001-2003 Rolf Ade */ 7 8#include <tdom.h> 9#include <string.h> 10#include <stdlib.h> 11 12/* 13 * Beginning with 8.4, Tcl API is CONST'ified 14 */ 15#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION <= 3) 16# define CONST84 17#endif 18 19#ifndef TCL_THREADS 20# define TDomThreaded(x) 21#else 22# define TDomThreaded(x) x 23#endif 24 25/* The inital stack sizes must be at least 1 */ 26#define TNC_INITCONTENTSTACKSIZE 512 27 28/*---------------------------------------------------------------------------- 29| local globals 30| 31\---------------------------------------------------------------------------*/ 32/* Counter to generate unique validateCmd names */ 33static int uniqueCounter = 0; 34TDomThreaded(static Tcl_Mutex counterMutex;) /* Protect the counter */ 35 36/* To enable some debugging output at stdout use this. 37 But beware: this debugging output isn't systematic 38 and only understandable, if you know the internals 39 of tnc. */ 40/* #define TNC_DEBUG */ 41 42/* The elements of TNC_Content carry exactly the same information 43 as expats XML_Content. But the element is identified by his 44 Tcl_HashEntry entry within the "tagNames" Hashtable (see TNC_Data) 45 and not the element name. This should be much more efficient. */ 46typedef struct TNC_cp TNC_Content; 47typedef struct TNC_elemAttInfo TNC_ElemAttInfo; 48 49struct TNC_cp 50{ 51 enum XML_Content_Type type; 52 enum XML_Content_Quant quant; 53 Tcl_HashEntry *nameId; 54 unsigned int numchildren; 55 TNC_Content *children; 56 TNC_ElemAttInfo *attInfo; 57}; 58 59typedef struct TNC_contentStack 60{ 61 TNC_Content *model; 62 int activeChild; 63 int deep; 64 int alreadymatched; 65} TNC_ContentStack; 66 67 68typedef struct TNC_data 69{ 70 char *doctypeName; /* From DOCTYPE declaration */ 71 int ignoreWhiteCDATAs; /* Flag: white space allowed in 72 current content model? */ 73 int ignorePCDATA; /* Flag: currently mixed content 74 model? */ 75 Tcl_HashTable *tagNames; /* Hash table of all ELEMENT 76 declarations of the DTD. 77 Element name is the key. 78 While parsing, entry points 79 to the XML_Content of that 80 Element, after finishing of 81 DTD parsing, entry holds a 82 pointer to the TNC_Content 83 of that element. */ 84 TNC_ElemAttInfo *elemAttInfo; /* TncElementStartCommand stores 85 the elemAttInfo pointer of 86 the current element here for 87 DOM validation, to avoid two 88 element name lookups. */ 89 int elemContentsRewriten; /* Signals, if the tagNames 90 entries point to 91 TNC_Contents */ 92 int status; /* While used with expat obj: 93 1 after successful parsed 94 DTD, 0 otherwise. 95 For validateCmd used for 96 error report during 97 validation: 0 OK, 1 validation 98 error. */ 99 int idCheck; /* Flag: check IDREF resolution*/ 100 Tcl_HashTable *attDefsTables; /* Used to store ATTLIST 101 declarations while parsing. 102 Keys are the element names. */ 103 Tcl_HashTable *entityDecls; /* Used to store ENTITY 104 declarations. */ 105 Tcl_HashTable *notationDecls; /* Used to store NOTATION 106 declarations. */ 107 Tcl_HashTable *ids; /* Used to track IDs */ 108 Tcl_Interp *interp; 109 Tcl_Obj *expatObj; /* If != NULL, points to the 110 parserCmd structure. NULL 111 for ValidateCmds. Used, to 112 distinguish between SAX 113 and DOM validation. */ 114 int contentStackSize; /* Current size of the content 115 stack */ 116 int contentStackPtr; /* Points to the currently active 117 content model on the stack */ 118 TNC_ContentStack *contentStack; /* Stack for the currently 119 nested open content models. */ 120} TNC_Data; 121 122typedef enum TNC_attType { 123 TNC_ATTTYPE_CDATA, 124 TNC_ATTTYPE_ID, 125 TNC_ATTTYPE_IDREF, 126 TNC_ATTTYPE_IDREFS, 127 TNC_ATTTYPE_ENTITY, 128 TNC_ATTTYPE_ENTITIES, 129 TNC_ATTTYPE_NMTOKEN, 130 TNC_ATTTYPE_NMTOKENS, 131 TNC_ATTTYPE_NOTATION, 132 TNC_ATTTYPE_ENUMERATION, 133} TNC_AttType; 134 135struct TNC_elemAttInfo 136{ 137 Tcl_HashTable *attributes; 138 int nrOfreq; 139 int nrOfIdAtts; 140}; 141 142typedef struct TNC_attDecl 143{ 144 TNC_AttType att_type; 145 char *dflt; 146 int isrequired; 147 Tcl_HashTable *lookupTable; /* either NotationTypes or enum values */ 148} TNC_AttDecl; 149 150typedef struct TNC_entityInfo 151{ 152 int is_notation; 153 char *notationName; 154} TNC_EntityInfo; 155 156typedef Tcl_HashEntry TNC_NameId; 157 158static char tnc_usage[] = 159 "Usage tnc <expat parser obj> <subCommand>, where subCommand can be: \n" 160 " enable \n" 161 " remove \n" 162 " getValidateCmd ?cmdName?\n" 163 ; 164 165static char validateCmd_usage[] = 166 "Usage validateCmd <method> <args>, where method can be: \n" 167 " validateDocument <domDocument> \n" 168 " validateTree <domNode> \n" 169 " validateAttributes <domNode> \n" 170 " delete \n" 171 ; 172 173enum TNC_Error { 174 TNC_ERROR_NONE, 175 TNC_ERROR_DUPLICATE_ELEMENT_DECL, 176 TNC_ERROR_DUPLICATE_MIXED_ELEMENT, 177 TNC_ERROR_UNKNOWN_ELEMENT, 178 TNC_ERROR_EMPTY_ELEMENT, 179 TNC_ERROR_DISALLOWED_PCDATA, 180 TNC_ERROR_DISALLOWED_CDATA, 181 TNC_ERROR_NO_DOCTYPE_DECL, 182 TNC_ERROR_WRONG_ROOT_ELEMENT, 183 TNC_ERROR_NO_ATTRIBUTES, 184 TNC_ERROR_UNKOWN_ATTRIBUTE, 185 TNC_ERROR_WRONG_FIXED_ATTVALUE, 186 TNC_ERROR_MISSING_REQUIRED_ATTRIBUTE, 187 TNC_ERROR_MORE_THAN_ONE_ID_ATT, 188 TNC_ERROR_ID_ATT_DEFAULT, 189 TNC_ERROR_DUPLICATE_ID_VALUE, 190 TNC_ERROR_UNKOWN_ID_REFERRED, 191 TNC_ERROR_ENTITY_ATTRIBUTE, 192 TNC_ERROR_ENTITIES_ATTRIBUTE, 193 TNC_ERROR_ATT_ENTITY_DEFAULT_MUST_BE_DECLARED, 194 TNC_ERROR_NOTATION_REQUIRED, 195 TNC_ERROR_NOTATION_MUST_BE_DECLARED, 196 TNC_ERROR_IMPOSSIBLE_DEFAULT, 197 TNC_ERROR_ENUM_ATT_WRONG_VALUE, 198 TNC_ERROR_NMTOKEN_REQUIRED, 199 TNC_ERROR_NAME_REQUIRED, 200 TNC_ERROR_NAMES_REQUIRED, 201 TNC_ERROR_ELEMENT_NOT_ALLOWED_HERE, 202 TNC_ERROR_ELEMENT_CAN_NOT_END_HERE, 203 TNC_ERROR_ONLY_THREE_BYTE_UTF8, 204 TNC_ERROR_UNKNOWN_NODE_TYPE 205}; 206 207const char * 208TNC_ErrorString (int code) 209{ 210 static const char *message[] = { 211 "No error.", 212 "Element declared more than once.", 213 "The same name must not appear more than once in \n\tone mixed-content declaration.", 214 "No declaration for this element.", 215 "Element is declared to be empty, but isn't.", 216 "PCDATA not allowed here.", 217 "CDATA section not allowed here.", 218 "No DOCTYPE declaration.", 219 "Root element doesn't match DOCTYPE name.", 220 "No attributes defined for this element.", 221 "Unknown attribute for this element.", 222 "Attribute value must match the FIXED default.", 223 "Required attribute missing.", 224 "Only one attribute with type ID allowed.", 225 "No default value allowed for attribute type ID.", 226 "ID attribute values must be unique within the document.", 227 "Unknown ID referred.", 228 "Attribute value has to be a unparsed entity.", 229 "Attribute value has to be a sequence of unparsed entities.", 230 "The defaults of attributes with type ENTITY or ENTITIES\nhas to be unparsed entities.", 231 "Attribute value has to be one of the allowed notations.", 232 "Every used NOTATION must be declared.", 233 "Attribute default is not one of the allowed values", 234 "Attribute hasn't one of the allowed values.", 235 "Attribute value has to be a NMTOKEN.", 236 "Attribute value has to be a Name.", 237 "Attribute value has to match production Names.", 238 "Element is not allowed here.", 239 "Element can not end here (required element(s) missing).", 240 "Can only handle UTF8 chars up to 3 bytes length." 241 "Unknown or unexpected dom node type." 242 }; 243/* if (code > 0 && code < sizeof(message)/sizeof(message[0])) */ 244 return message[code]; 245 return 0; 246} 247 248#define CHECK_UTF_CHARLEN(d) if (!(d)) { \ 249 signalNotValid (userData, TNC_ERROR_ONLY_THREE_BYTE_UTF8);\ 250 return;\ 251 } 252 253#define CHECK_UTF_CHARLENR(d) if (!(d)) { \ 254 signalNotValid (userData, TNC_ERROR_ONLY_THREE_BYTE_UTF8);\ 255 return 0;\ 256 } 257 258#define CHECK_UTF_CHARLEN_COPY(d) if (!(d)) { \ 259 signalNotValid (userData, TNC_ERROR_ONLY_THREE_BYTE_UTF8);\ 260 FREE (copy);\ 261 return;\ 262 } 263 264#define SetResult(str) Tcl_ResetResult(interp); \ 265 Tcl_SetStringObj(Tcl_GetObjResult(interp), (str), -1) 266 267#define SetBooleanResult(i) Tcl_ResetResult(interp); \ 268 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (i)) 269 270extern char *Tdom_InitStubs (Tcl_Interp *interp, char *version, int exact); 271 272static void 273signalNotValid (userData, code) 274 void *userData; 275 int code; 276{ 277 TNC_Data *tncdata = (TNC_Data *) userData; 278 TclGenExpatInfo *expat; 279 char s[1000]; 280 281 if (tncdata->expatObj) { 282 expat = GetExpatInfo (tncdata->interp, tncdata->expatObj); 283 sprintf (s, "Validation error at line %ld, character %ld: %s", 284 XML_GetCurrentLineNumber (expat->parser), 285 XML_GetCurrentColumnNumber (expat->parser), 286 TNC_ErrorString (code)); 287 expat->status = TCL_ERROR; 288 expat->result = Tcl_NewStringObj (s, -1); 289 Tcl_IncrRefCount (expat->result); 290 } else { 291 tncdata->status = 1; 292 Tcl_SetResult (tncdata->interp, (char *)TNC_ErrorString (code), 293 TCL_VOLATILE); 294 } 295} 296 297/* 298 *---------------------------------------------------------------------------- 299 * 300 * FindUniqueCmdName -- 301 * 302 * Generate new command name. Used for getValidateCmd. 303 * 304 * Results: 305 * Returns newly allocated Tcl object containing name. 306 * 307 * Side effects: 308 * Allocates Tcl object. 309 * 310 *---------------------------------------------------------------------------- 311 */ 312 313static void 314FindUniqueCmdName( 315 Tcl_Interp *interp, 316 char *s 317 ) 318{ 319 Tcl_CmdInfo info; 320 321 TDomThreaded(Tcl_MutexLock(&counterMutex);) 322 do { 323 sprintf(s, "DTDvalidator%d", uniqueCounter++); 324 325 } while (Tcl_GetCommandInfo(interp, s, &info)); 326 TDomThreaded(Tcl_MutexUnlock(&counterMutex);) 327} 328 329/* 330 *---------------------------------------------------------------------------- 331 * 332 * TncStartDoctypeDeclHandler -- 333 * 334 * This procedure is called for the start of the DOCTYPE 335 * declaration. 336 * 337 * Results: 338 * None. 339 * 340 * Side effects: 341 * Stores the doctype Name in the TNC_data. 342 * 343 *---------------------------------------------------------------------------- 344 */ 345 346void 347TncStartDoctypeDeclHandler (userData, doctypeName, sysid, pubid, has_internal_subset) 348 void *userData; 349 const char *doctypeName; 350 const char *sysid; 351 const char *pubid; 352 int has_internal_subset; 353{ 354 TNC_Data *tncdata = (TNC_Data *) userData; 355 356#ifdef TNC_DEBUG 357 printf ("TncStartDoctypeDeclHandler start\n"); 358#endif 359 tncdata->doctypeName = tdomstrdup (doctypeName); 360} 361 362 363/* 364 *---------------------------------------------------------------------------- 365 * 366 * TncFreeTncModel -- 367 * 368 * This helper procedure frees recursively TNC_Contents. 369 * 370 * Results: 371 * None. 372 * 373 * Side effects: 374 * Frees memory. 375 * 376 *---------------------------------------------------------------------------- 377 */ 378 379static void 380TncFreeTncModel (tmodel) 381 TNC_Content *tmodel; 382{ 383 unsigned int i; 384 385 if (tmodel->children) { 386 for (i = 0; i < tmodel->numchildren; i++) { 387 TncFreeTncModel (&tmodel->children[i]); 388 } 389 FREE ((char *) tmodel->children); 390 } 391} 392 393 394/* 395 *---------------------------------------------------------------------------- 396 * 397 * TncRewriteModel -- 398 * 399 * This helper procedure creates recursively a TNC_Content from 400 * a XML_Content. 401 * 402 * Results: 403 * None. 404 * 405 * Side effects: 406 * Allocates memory for the TNC_Content models. 407 * 408 *---------------------------------------------------------------------------- 409 */ 410 411static void 412TncRewriteModel (emodel, tmodel, tagNames) 413 XML_Content *emodel; 414 TNC_Content *tmodel; 415 Tcl_HashTable *tagNames; 416{ 417 Tcl_HashEntry *entryPtr; 418 unsigned int i; 419 420 tmodel->type = emodel->type; 421 tmodel->quant = emodel->quant; 422 tmodel->numchildren = emodel->numchildren; 423 tmodel->children = NULL; 424 tmodel->nameId = NULL; 425 switch (emodel->type) { 426 case XML_CTYPE_MIXED: 427 if (emodel->quant == XML_CQUANT_REP) { 428 tmodel->children = (TNC_Content *) 429 MALLOC (sizeof (TNC_Content) * emodel->numchildren); 430 for (i = 0; i < emodel->numchildren; i++) { 431 TncRewriteModel (&emodel->children[i], &tmodel->children[i], 432 tagNames); 433 } 434 } 435 break; 436 case XML_CTYPE_ANY: 437 case XML_CTYPE_EMPTY: 438 /* do nothing */ 439 break; 440 case XML_CTYPE_SEQ: 441 case XML_CTYPE_CHOICE: 442 tmodel->children = (TNC_Content *) 443 MALLOC (sizeof (TNC_Content) * emodel->numchildren); 444 for (i = 0; i < emodel->numchildren; i++) { 445 TncRewriteModel (&emodel->children[i], &tmodel->children[i], 446 tagNames); 447 } 448 break; 449 case XML_CTYPE_NAME: 450 entryPtr = Tcl_FindHashEntry (tagNames, emodel->name); 451 /* Notice, that it is possible for entryPtr to be NULL. 452 This means, a content model uses a not declared element. 453 This is legal even in valid documents. (Of course, if the 454 undeclared element actually shows up in the document 455 that would make the document invalid.) See rec 3.2 456 457 QUESTION: Should there be a flag to enable a warning, 458 when a declaration contains an element type for which 459 no declaration is provided, as rec 3.2 metioned? 460 This would be the appropriated place to omit the 461 warning. */ 462 tmodel->nameId = entryPtr; 463 } 464} 465 466 467/* 468 *---------------------------------------------------------------------------- 469 * 470 * TncEndDoctypeDeclHandler -- 471 * 472 * This procedure is called at the end of the DOCTYPE 473 * declaration, after processing any external subset. 474 * It rewrites the XML_Content models to TNC_Content 475 * models and frees the XML_Content models. 476 * 477 * Results: 478 * None. 479 * 480 * Side effects: 481 * Rewrites the XML_Content models to TNC_Content 482 * models. 483 * 484 *---------------------------------------------------------------------------- 485 */ 486 487void 488TncEndDoctypeDeclHandler (userData) 489 void *userData; 490{ 491 TNC_Data *tncdata = (TNC_Data *) userData; 492 Tcl_HashEntry *entryPtr, *ePtr1; 493 Tcl_HashSearch search; 494 XML_Content *emodel; 495 TNC_Content *tmodel = NULL; 496 char *elementName; 497 498 entryPtr = Tcl_FirstHashEntry (tncdata->tagNames, &search); 499 while (entryPtr != NULL) { 500#ifdef TNC_DEBUG 501 printf ("name: %-20s nameId: %p\n", 502 Tcl_GetHashKey (tncdata->tagNames, entryPtr), 503 entryPtr); 504#endif 505 emodel = (XML_Content*) Tcl_GetHashValue (entryPtr); 506 tmodel = (TNC_Content*) MALLOC (sizeof (TNC_Content)); 507 TncRewriteModel (emodel, tmodel, tncdata->tagNames); 508 elementName = Tcl_GetHashKey (tncdata->tagNames, entryPtr); 509 ePtr1 = Tcl_FindHashEntry (tncdata->attDefsTables, elementName); 510 if (ePtr1) { 511 tmodel->attInfo = (TNC_ElemAttInfo *) Tcl_GetHashValue (ePtr1); 512 } else { 513 tmodel->attInfo = NULL; 514 } 515 Tcl_SetHashValue (entryPtr, tmodel); 516 entryPtr = Tcl_NextHashEntry (&search); 517 } 518 tncdata->elemContentsRewriten = 1; 519 /* Checks, if every used notation name is in deed declared */ 520 entryPtr = Tcl_FirstHashEntry (tncdata->notationDecls, &search); 521 while (entryPtr != NULL) { 522#ifdef TNC_DEBUG 523 printf ("check notation name %s\n", 524 Tcl_GetHashKey (tncdata->notationDecls, entryPtr)); 525 printf ("value %p\n", Tcl_GetHashValue (entryPtr)); 526#endif 527 if (!Tcl_GetHashValue (entryPtr)) { 528 signalNotValid (userData, TNC_ERROR_NOTATION_MUST_BE_DECLARED); 529 return; 530 } 531 entryPtr = Tcl_NextHashEntry (&search); 532 } 533 /* Checks, if every used entity name is indeed declared */ 534 entryPtr = Tcl_FirstHashEntry (tncdata->entityDecls, &search); 535 while (entryPtr != NULL) { 536 if (!Tcl_GetHashValue (entryPtr)) { 537 signalNotValid (userData, 538 TNC_ERROR_ATT_ENTITY_DEFAULT_MUST_BE_DECLARED); 539 return; 540 } 541 entryPtr = Tcl_NextHashEntry (&search); 542 } 543 tncdata->status = 1; 544} 545 546 547/* 548 *---------------------------------------------------------------------------- 549 * 550 * TncEntityDeclHandler -- 551 * 552 * This procedure is called for every entity declaration. 553 * 554 * Results: 555 * None. 556 * 557 * Side effects: 558 * Stores either the name of the entity and 559 * type information in a lookup table. 560 * 561 *---------------------------------------------------------------------------- 562 */ 563 564void 565TncEntityDeclHandler (userData, entityName, is_parameter_entity, value, 566 value_length, base, systemId, publicId, notationName) 567 void *userData; 568 const char *entityName; 569 int is_parameter_entity; 570 const char *value; 571 int value_length; 572 const char *base; 573 const char *systemId; 574 const char *publicId; 575 const char *notationName; 576{ 577 TNC_Data *tncdata = (TNC_Data *) userData; 578 Tcl_HashEntry *entryPtr, *entryPtr1; 579 int newPtr; 580 TNC_EntityInfo *entityInfo; 581 582 583 /* expat collects entity definitions internaly by himself. So this is 584 maybe superfluous, if it possible to access the expat internal 585 represention. To study this is left to the reader. */ 586 587 if (is_parameter_entity) return; 588 entryPtr = Tcl_CreateHashEntry (tncdata->entityDecls, entityName, &newPtr); 589 /* multiple declaration of the same entity are allowed; first 590 definition wins (rec. 4.2) */ 591 if (!newPtr) { 592 /* Eventually, an attribute declaration with type ENTITY or ENTITIES 593 has used this (up to the attribute declaration undeclared) ENTITY 594 within his default value. In this case, the hash value have to 595 be NULL and the entity must be a unparsed entity. */ 596 if (!Tcl_GetHashValue (entryPtr)) { 597 if (notationName == NULL) { 598 signalNotValid (userData, 599 TNC_ERROR_ATT_ENTITY_DEFAULT_MUST_BE_DECLARED); 600 return; 601 } 602 newPtr = 1; 603 } 604 } 605 if (newPtr) { 606 entityInfo = (TNC_EntityInfo *) MALLOC (sizeof (TNC_EntityInfo)); 607 if (notationName != NULL) { 608 entityInfo->is_notation = 1; 609 entryPtr1 = Tcl_CreateHashEntry (tncdata->notationDecls, 610 notationName, &newPtr); 611 entityInfo->notationName = tdomstrdup (notationName); 612 } 613 else { 614 entityInfo->is_notation = 0; 615 } 616 Tcl_SetHashValue (entryPtr, entityInfo); 617 } 618} 619 620/* 621 *---------------------------------------------------------------------------- 622 * 623 * TncNotationDeclHandler -- 624 * 625 * This procedure is called for every notation declaration. 626 * 627 * Results: 628 * None. 629 * 630 * Side effects: 631 * Stores the notationName in the notationDecls table with value 632 * one. 633 * 634 *---------------------------------------------------------------------------- 635 */ 636 637void 638TncNotationDeclHandler (userData, notationName, base, systemId, publicId) 639 void *userData; 640 const char *notationName; 641 const char *base; 642 const char *systemId; 643 const char *publicId; 644{ 645 TNC_Data *tncdata = (TNC_Data *) userData; 646 Tcl_HashEntry *entryPtr; 647 int newPtr; 648 649 entryPtr = Tcl_CreateHashEntry (tncdata->notationDecls, 650 notationName, 651 &newPtr); 652#ifdef TNC_DEBUG 653 printf ("Notation %s declared\n", notationName); 654#endif 655 Tcl_SetHashValue (entryPtr, (char *) 1); 656} 657 658 659 660/* 661 *---------------------------------------------------------------------------- 662 * 663 * TncElementDeclCommand -- 664 * 665 * This procedure is called for every element declaration. 666 * 667 * Results: 668 * None. 669 * 670 * Side effects: 671 * Stores the tag name of the element in a lookup table. 672 * 673 *---------------------------------------------------------------------------- 674 */ 675 676void 677TncElementDeclCommand (userData, name, model) 678 void *userData; 679 const char *name; 680 XML_Content *model; 681{ 682 TNC_Data *tncdata = (TNC_Data *) userData; 683 Tcl_HashEntry *entryPtr; 684 int newPtr; 685 unsigned int i, j; 686 687 entryPtr = Tcl_CreateHashEntry (tncdata->tagNames, name, &newPtr); 688 /* "No element type may be declared more than once." (rec. 3.2) */ 689 if (!newPtr) { 690 signalNotValid (userData, TNC_ERROR_DUPLICATE_ELEMENT_DECL); 691 return; 692 } 693 /* "The same name must not appear more than once in a 694 single mixed-content declaration." (rec. 3.2.2) 695 NOTE: OK, OK, doing it this way may not be optimal or even fast 696 in some cases. Please step in with a more fancy solution, if you 697 feel the need. */ 698 if (model->type == XML_CTYPE_MIXED && model->quant == XML_CQUANT_REP) { 699 for (i = 0; i < model->numchildren; i++) { 700 for (j = i + 1; j < model->numchildren; j++) { 701 if (strcmp ((&model->children[i])->name, 702 (&model->children[j])->name) == 0) { 703 signalNotValid (userData, 704 TNC_ERROR_DUPLICATE_MIXED_ELEMENT); 705 return; 706 } 707 } 708 } 709 } 710 Tcl_SetHashValue (entryPtr, model); 711 return; 712} 713 714 715/* 716 *---------------------------------------------------------------------------- 717 * 718 * TncAttDeclCommand -- 719 * 720 * This procedure is called for *each* attribute in an XML 721 * ATTLIST declaration. It stores the attribute definition in 722 * an element specific hash table. 723 * 724 * Results: 725 * None. 726 * 727 * Side effects: 728 * Stores the tag name of the element in a lookup table. 729 * 730 *---------------------------------------------------------------------------- 731 */ 732 733void 734TncAttDeclCommand (userData, elname, attname, att_type, dflt, isrequired) 735 void *userData; 736 const char *elname; 737 const char *attname; 738 const char *att_type; 739 const char *dflt; 740 int isrequired; 741{ 742 TNC_Data *tncdata = (TNC_Data *) userData; 743 Tcl_HashEntry *entryPtr, *entryPtr1; 744 Tcl_HashTable *elemAtts; 745 TNC_ElemAttInfo *elemAttInfo; 746 TNC_AttDecl *attDecl; 747 TNC_EntityInfo *entityInfo; 748 int newPtr, start, i, clen; 749 char *copy; 750 751 entryPtr = Tcl_CreateHashEntry (tncdata->attDefsTables, elname, &newPtr); 752 if (newPtr) { 753 elemAttInfo = (TNC_ElemAttInfo *) MALLOC (sizeof (TNC_ElemAttInfo)); 754 elemAtts = (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable)); 755 Tcl_InitHashTable (elemAtts, TCL_STRING_KEYS); 756 elemAttInfo->attributes = elemAtts; 757 elemAttInfo->nrOfreq = 0; 758 elemAttInfo->nrOfIdAtts = 0; 759 Tcl_SetHashValue (entryPtr, elemAttInfo); 760 } else { 761 elemAttInfo = (TNC_ElemAttInfo *) Tcl_GetHashValue (entryPtr); 762 elemAtts = elemAttInfo->attributes; 763 } 764 entryPtr = Tcl_CreateHashEntry (elemAtts, attname, &newPtr); 765 /* Multiple Attribute declarations are allowed, but later declarations 766 are ignored. See rec 3.3. */ 767 if (newPtr) { 768 attDecl = (TNC_AttDecl *) MALLOC (sizeof (TNC_AttDecl)); 769 if (strcmp (att_type, "CDATA") == 0) { 770 attDecl->att_type = TNC_ATTTYPE_CDATA; 771 } 772 else if (strcmp (att_type, "ID") == 0) { 773 if (elemAttInfo->nrOfIdAtts) { 774 signalNotValid (userData, TNC_ERROR_MORE_THAN_ONE_ID_ATT); 775 return; 776 } 777 elemAttInfo->nrOfIdAtts++; 778 if (dflt != NULL) { 779 signalNotValid (userData, TNC_ERROR_ID_ATT_DEFAULT); 780 return; 781 } 782 attDecl->att_type = TNC_ATTTYPE_ID; 783 } 784 else if (strcmp (att_type, "IDREF") == 0) { 785 attDecl->att_type = TNC_ATTTYPE_IDREF; 786 } 787 else if (strcmp (att_type, "IDREFS") == 0) { 788 attDecl->att_type = TNC_ATTTYPE_IDREFS; 789 } 790 else if (strcmp (att_type, "ENTITY") == 0) { 791 attDecl->att_type = TNC_ATTTYPE_ENTITY; 792 } 793 else if (strcmp (att_type, "ENTITIES") == 0) { 794 attDecl->att_type = TNC_ATTTYPE_ENTITIES; 795 } 796 else if (strcmp (att_type, "NMTOKEN") == 0) { 797 attDecl->att_type = TNC_ATTTYPE_NMTOKEN; 798 } 799 else if (strcmp (att_type, "NMTOKENS") == 0) { 800 attDecl->att_type = TNC_ATTTYPE_NMTOKENS; 801 } 802 else if (strncmp (att_type, "NOTATION(", 9) == 0) { 803 /* This is a bit puzzling. expat returns something like 804 <!NOTATION gif PUBLIC "gif"> 805 <!ATTLIST c type NOTATION (gif) #IMPLIED> 806 as att_type "NOTATION(gif)". */ 807 attDecl->att_type = TNC_ATTTYPE_NOTATION; 808 attDecl->lookupTable = 809 (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable)); 810 Tcl_InitHashTable (attDecl->lookupTable, TCL_STRING_KEYS); 811 copy = tdomstrdup (att_type); 812 start = i = 9; 813 while (i) { 814 if (copy[i] == ')') { 815 copy[i] = '\0'; 816#ifdef TNC_DEBUG 817 printf ("att type NOTATION: notation %s allowed\n", 818 ©[start]); 819#endif 820 Tcl_CreateHashEntry (attDecl->lookupTable, 821 ©[start], &newPtr); 822 entryPtr1 = Tcl_CreateHashEntry (tncdata->notationDecls, 823 ©[start], &newPtr); 824#ifdef TNC_DEBUG 825 if (newPtr) { 826 printf ("up to now unknown NOTATION\n"); 827 } else { 828 printf ("NOTATION already known\n"); 829 } 830#endif 831 FREE (copy); 832 break; 833 } 834 if (copy[i] == '|') { 835 copy[i] = '\0'; 836#ifdef TNC_DEBUG 837 printf ("att type NOTATION: notation %s allowed\n", 838 ©[start]); 839#endif 840 Tcl_CreateHashEntry (attDecl->lookupTable, 841 ©[start], &newPtr); 842 entryPtr1 = Tcl_CreateHashEntry (tncdata->notationDecls, 843 ©[start], &newPtr); 844#ifdef TNC_DEBUG 845 if (newPtr) { 846 printf ("up to now unknown NOTATION\n"); 847 } else { 848 printf ("NOTATION already known\n"); 849 } 850#endif 851 start = ++i; 852 continue; 853 } 854 clen = UTF8_CHAR_LEN (copy[i]); 855 CHECK_UTF_CHARLEN_COPY (clen); 856 if (!UTF8_GET_NAMING_NMTOKEN (©[i], clen)) { 857 signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 858 FREE (copy); 859 return; 860 } 861 i += clen; 862 } 863 } 864 else { 865 /* expat returns something like 866 <!ATTLIST a type ( numbered 867 |bullets ) #IMPLIED> 868 as att_type "(numbered|bullets)", e.g. in some 869 "non-official" normalized way. 870 Makes things easier for us. */ 871 attDecl->att_type = TNC_ATTTYPE_ENUMERATION; 872 attDecl->lookupTable = 873 (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable)); 874 Tcl_InitHashTable (attDecl->lookupTable, TCL_STRING_KEYS); 875 copy = tdomstrdup (att_type); 876 start = i = 1; 877 while (1) { 878 if (copy[i] == ')') { 879 copy[i] = '\0'; 880 Tcl_CreateHashEntry (attDecl->lookupTable, 881 ©[start], &newPtr); 882 FREE (copy); 883 break; 884 } 885 if (copy[i] == '|') { 886 copy[i] = '\0'; 887 Tcl_CreateHashEntry (attDecl->lookupTable, 888 ©[start], &newPtr); 889 start = ++i; 890 continue; 891 } 892 clen = UTF8_CHAR_LEN (copy[i]); 893 CHECK_UTF_CHARLEN_COPY (clen); 894 if (!UTF8_GET_NAMING_NMTOKEN (©[i], clen)) { 895 signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 896 FREE (copy); 897 return; 898 } 899 i += clen; 900 } 901 } 902 if (dflt != NULL) { 903 switch (attDecl->att_type) { 904 case TNC_ATTTYPE_ENTITY: 905 case TNC_ATTTYPE_IDREF: 906 clen = UTF8_CHAR_LEN (*dflt); 907 CHECK_UTF_CHARLEN (clen); 908 if (!UTF8_GET_NAME_START (dflt, clen)) { 909 signalNotValid (userData, TNC_ERROR_NAME_REQUIRED); 910 return; 911 } 912 i = clen; 913 while (1) { 914 if (dflt[i] == '\0') { 915 break; 916 } 917 clen = UTF8_CHAR_LEN (dflt[i]); 918 CHECK_UTF_CHARLEN (clen); 919 if (!UTF8_GET_NAMING_NMTOKEN (&dflt[i], clen)) { 920 signalNotValid (userData, TNC_ERROR_NAME_REQUIRED); 921 return; 922 } 923 i += clen; 924 } 925 if (attDecl->att_type == TNC_ATTTYPE_ENTITY) { 926 entryPtr1 = Tcl_CreateHashEntry (tncdata->entityDecls, 927 dflt, &newPtr); 928 if (!newPtr) { 929 entityInfo = 930 (TNC_EntityInfo *) Tcl_GetHashValue (entryPtr1); 931 if (!entityInfo->is_notation) { 932 signalNotValid (userData,TNC_ERROR_ATT_ENTITY_DEFAULT_MUST_BE_DECLARED); 933 } 934 } 935 } 936 break; 937 case TNC_ATTTYPE_IDREFS: 938 start = i = 0; 939 while (1) { 940 if (dflt[i] == '\0') { 941 break; 942 } 943 if (dflt[i] == ' ') { 944 start = ++i; 945 } 946 if (start == i) { 947 clen = UTF8_CHAR_LEN (dflt[i]); 948 CHECK_UTF_CHARLEN (clen); 949 if (!UTF8_GET_NAME_START (&dflt[i], clen)) { 950 signalNotValid (userData, TNC_ERROR_NAME_REQUIRED); 951 return; 952 } 953 i += clen; 954 } 955 else { 956 clen = UTF8_CHAR_LEN (dflt[i]); 957 CHECK_UTF_CHARLEN (clen); 958 if (!UTF8_GET_NAMING_NMTOKEN (&dflt[i], clen)) { 959 signalNotValid (userData, TNC_ERROR_NAME_REQUIRED); 960 return; 961 } 962 i += clen; 963 } 964 } 965 break; 966 case TNC_ATTTYPE_ENTITIES: 967 copy = tdomstrdup (dflt); 968 start = i = 0; 969 while (1) { 970 if (copy[i] == '\0') { 971 FREE (copy); 972 break; 973 } 974 if (copy[i] == ' ') { 975 copy[i] = '\0'; 976 entryPtr1 = Tcl_CreateHashEntry (tncdata->entityDecls, 977 ©[start], 978 &newPtr); 979 if (!newPtr) { 980 entityInfo = 981 (TNC_EntityInfo *) Tcl_GetHashValue (entryPtr1); 982 if (!entityInfo->is_notation) { 983 signalNotValid (userData,TNC_ERROR_ATT_ENTITY_DEFAULT_MUST_BE_DECLARED); 984 } 985 } 986 start = ++i; 987 } 988 if (start == i) { 989 clen = UTF8_CHAR_LEN (copy[i]); 990 CHECK_UTF_CHARLEN_COPY (clen); 991 if (!UTF8_GET_NAME_START (©[i], clen)) { 992 signalNotValid (userData, TNC_ERROR_NAME_REQUIRED); 993 FREE (copy); 994 return; 995 } 996 i += clen; 997 } 998 else { 999 clen = UTF8_CHAR_LEN (copy[i]); 1000 CHECK_UTF_CHARLEN_COPY (clen); 1001 if (!UTF8_GET_NAMING_NMTOKEN (©[i], clen)) { 1002 signalNotValid (userData, TNC_ERROR_NAME_REQUIRED); 1003 FREE (copy); 1004 return; 1005 } 1006 i += clen; 1007 } 1008 } 1009 break; 1010 case TNC_ATTTYPE_NMTOKEN: 1011 i = 0; 1012 while (1) { 1013 if (dflt[i] == '\0') { 1014 break; 1015 } 1016 clen = UTF8_CHAR_LEN (dflt[i]); 1017 CHECK_UTF_CHARLEN (clen); 1018 if (!UTF8_GET_NAMING_NMTOKEN (&dflt[i], clen)) { 1019 signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 1020 return; 1021 } 1022 i += clen; 1023 } 1024 if (!i) signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 1025 break; 1026 case TNC_ATTTYPE_NMTOKENS: 1027 i = 0; 1028 while (1) { 1029 if (dflt[i] == '\0') { 1030 break; 1031 } 1032 if (dflt[i] == ' ') { 1033 i++; 1034 } 1035 clen = UTF8_CHAR_LEN (dflt[i]); 1036 CHECK_UTF_CHARLEN (clen); 1037 if (!UTF8_GET_NAMING_NMTOKEN (&dflt[i], clen)) { 1038 signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 1039 return; 1040 } 1041 i += clen; 1042 } 1043 if (!i) signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 1044 break; 1045 case TNC_ATTTYPE_NOTATION: 1046 if (!Tcl_FindHashEntry (attDecl->lookupTable, dflt)) { 1047 signalNotValid (userData, TNC_ERROR_IMPOSSIBLE_DEFAULT); 1048 return; 1049 } 1050 case TNC_ATTTYPE_ENUMERATION: 1051 if (!Tcl_FindHashEntry (attDecl->lookupTable, dflt)) { 1052 signalNotValid (userData, TNC_ERROR_IMPOSSIBLE_DEFAULT); 1053 return; 1054 } 1055 case TNC_ATTTYPE_CDATA: 1056 case TNC_ATTTYPE_ID: 1057 /* This both cases are only there, to pacify -Wall. 1058 CDATA may have any allowed characters (and 1059 everything else is detected by extpat). ID's not 1060 allowed to have defaults (handled above). */ 1061 ; 1062 } 1063 attDecl->dflt = tdomstrdup (dflt); 1064 } 1065 else { 1066 attDecl->dflt = NULL; 1067 } 1068 if (isrequired) { 1069 elemAttInfo->nrOfreq++; 1070 } 1071 attDecl->isrequired = isrequired; 1072 Tcl_SetHashValue (entryPtr, attDecl); 1073 } 1074} 1075 1076#ifdef TNC_DEBUG 1077 1078void 1079printNameIDs (TNC_Data *tncdata) 1080{ 1081 Tcl_HashEntry *entryPtr; 1082 Tcl_HashSearch search; 1083 1084 for (entryPtr = Tcl_FirstHashEntry (tncdata->tagNames, &search); 1085 entryPtr != NULL; 1086 entryPtr = Tcl_NextHashEntry (&search)) { 1087 printf ("name: %-20s nameId: %p\n", 1088 Tcl_GetHashKey (tncdata->tagNames, entryPtr), 1089 entryPtr); 1090 } 1091} 1092 1093void 1094printStackElm (TNC_ContentStack *stackelm) 1095{ 1096 if (stackelm->model->type == XML_CTYPE_NAME) { 1097 printf ("\tmodel %p\tNAME: %p\n\tactiveChild %d\n\tdeep %d\n\talreadymatched %d\n", 1098 stackelm->model, stackelm->model->nameId, 1099 stackelm->activeChild, stackelm->deep, stackelm->alreadymatched); 1100 } 1101 else { 1102 printf ("\tmodel %p\n\tactiveChild %d\n\tdeep %d\n\talreadymatched %d\n", 1103 stackelm->model, stackelm->activeChild, stackelm->deep, 1104 stackelm->alreadymatched); 1105 } 1106} 1107 1108void 1109printTNC_Content (TNC_Content *model) 1110 { 1111 printf ("TNC_Content..\n\ttype %d\n\tquant %d\n\tnameId %p\n\tnumchildren %d\n\tchildren %p\n", model->type, model->quant, model->nameId, 1112 model->numchildren, model->children); 1113} 1114 1115void 1116printContentStack (TNC_Data *tncdata) 1117{ 1118 TNC_ContentStack stackelm; 1119 int i; 1120 1121 printf ("Current contentStack state (used stack slots %d):\n", 1122 tncdata->contentStackPtr); 1123 for (i = 0; i < tncdata->contentStackPtr; i++) { 1124 stackelm = tncdata->contentStack[i]; 1125 printf ("%3d:\n", i); 1126 printStackElm (&stackelm); 1127 } 1128} 1129#endif /* TNC_DEBUG */ 1130 1131 1132/* 1133 *---------------------------------------------------------------------------- 1134 * 1135 * TncProbeElement -- 1136 * 1137 * This function checks, if the element match the 1138 * topmost content model on the content stack. 1139 * 1140 * Results: 1141 * 1 if the element match, 1142 * 0 if not. 1143 * -1 if not, but this isn't a validation error 1144 * 1145 * Side effects: 1146 * Eventually pushes data to the contentStack (even in 1147 * recurive calls). 1148 * 1149 *---------------------------------------------------------------------------- 1150 */ 1151 1152static int 1153TncProbeElement (nameId, tncdata) 1154 TNC_NameId *nameId; 1155 TNC_Data *tncdata; 1156{ 1157 TNC_ContentStack *stackelm; 1158 TNC_Content *activeModel; 1159 int myStackPtr, zeroMatchPossible, result; 1160 unsigned int i, seqstartindex; 1161 1162#ifdef TNC_DEBUG 1163 printf ("TncProbeElement start\n"); 1164 printContentStack (tncdata); 1165#endif 1166 myStackPtr = tncdata->contentStackPtr - 1; 1167 stackelm = &(tncdata->contentStack)[myStackPtr]; 1168 switch (stackelm->model->type) { 1169 case XML_CTYPE_MIXED: 1170#ifdef TNC_DEBUG 1171 printf ("TncProbeElement XML_CTYPE_MIXED\n"); 1172#endif 1173 for (i = 0; i < stackelm->model->numchildren; i++) { 1174 if ((&stackelm->model->children[i])->nameId == nameId) { 1175 return 1; 1176 } 1177 } 1178 return 0; 1179 case XML_CTYPE_ANY: 1180#ifdef TNC_DEBUG 1181 printf ("TncProbeElement XML_CTYPE_ANY\n"); 1182#endif 1183 return 1; 1184 case XML_CTYPE_EMPTY: 1185#ifdef TNC_DEBUG 1186 printf ("TncProbeElement XML_CTYPE_EMPTY\n"); 1187#endif 1188 return 0; 1189 case XML_CTYPE_CHOICE: 1190#ifdef TNC_DEBUG 1191 printf ("TncProbeElement XML_CTYPE_CHOICE\n"); 1192#endif 1193 if (stackelm->alreadymatched) { 1194 activeModel = &stackelm->model->children[stackelm->activeChild]; 1195 if (activeModel->type == XML_CTYPE_NAME) { 1196 /* so this stackelement must be the topmost */ 1197 if (activeModel->quant == XML_CQUANT_REP 1198 || activeModel->quant == XML_CQUANT_PLUS) { 1199 /* the last matched element is multiple, maybe it 1200 matches again */ 1201 if (nameId == activeModel->nameId) { 1202#ifdef TNC_DEBUG 1203 printf ("-->matched! child Nr. %d\n", 1204 stackelm->activeChild); 1205#endif 1206 /* stack and activeChild nr. are already OK, just 1207 report success. */ 1208 return 1; 1209 } 1210 } 1211 } 1212 /* The active child is a SEQ or CHOICE. */ 1213 if (stackelm->model->quant == XML_CQUANT_NONE || 1214 stackelm->model->quant == XML_CQUANT_OPT) { 1215 /*The child cp's type SEQ or CHOICE keep track by 1216 themselve about if they are repeated. Because we are 1217 here, they don't. Since the current cp has already 1218 matched and isn't multiple, the current cp as a whole 1219 is done. But no contradiction detected, so return 1220 "search futher" */ 1221 return -1; 1222 } 1223 } 1224 1225 /* If one of the alternatives within the CHOICE cp is quant 1226 REP or OPT, it isn't a contradition to the document structure, 1227 if the cp doesn't match, even if it is quant 1228 NONE or PLUS, because of the "zero time" match of this one 1229 alternative. We use zeroMatchPossible, to know about this.*/ 1230 zeroMatchPossible = 0; 1231 for (i = 0; i < stackelm->model->numchildren; i++) { 1232 if ((&stackelm->model->children[i])->type == XML_CTYPE_NAME) { 1233#ifdef TNC_DEBUG 1234 printf ("child is type NAME\n"); 1235#endif 1236 if ((&stackelm->model->children[i])->nameId == nameId) { 1237#ifdef TNC_DEBUG 1238 printf ("-->matched! child Nr. %d\n",i); 1239#endif 1240 (&tncdata->contentStack[myStackPtr])->activeChild = i; 1241 (&tncdata->contentStack[myStackPtr])->alreadymatched = 1; 1242 return 1; 1243 } 1244 else { 1245 /* If the name child is optional, we have a 1246 candidat for "zero match". */ 1247 if ((&stackelm->model->children[i])->quant 1248 == XML_CQUANT_OPT || 1249 (&stackelm->model->children[i])->quant 1250 == XML_CQUANT_REP) { 1251#ifdef TNC_DEBUG 1252 printf ("zero match possible\n"); 1253#endif 1254 zeroMatchPossible = 1; 1255 } 1256 } 1257 } 1258 else { 1259#ifdef TNC_DEBUG 1260 printf ("complex child type\n"); 1261#endif 1262 if (tncdata->contentStackPtr == tncdata->contentStackSize) { 1263 tncdata->contentStack = (TNC_ContentStack *) 1264 Tcl_Realloc ((char *)tncdata->contentStack, 1265 sizeof (TNC_Content *) * 2 * 1266 tncdata->contentStackSize); 1267 tncdata->contentStackSize *= 2; 1268 } 1269 (&tncdata->contentStack[tncdata->contentStackPtr])->model 1270 = &stackelm->model->children[i]; 1271 tncdata->contentStack[tncdata->contentStackPtr].activeChild 1272 = 0; 1273 tncdata->contentStack[tncdata->contentStackPtr].deep 1274 = stackelm->deep + 1; 1275 tncdata->contentStack[tncdata->contentStackPtr].alreadymatched 1276 = 0; 1277 tncdata->contentStackPtr++; 1278 result = TncProbeElement (nameId, tncdata); 1279 if (result == 1) { 1280#ifdef TNC_DEBUG 1281 printf ("-->matched! child nr. %d\n",i); 1282#endif 1283 (&tncdata->contentStack[myStackPtr])->activeChild = i; 1284 (&tncdata->contentStack[myStackPtr])->alreadymatched = 1; 1285 return 1; 1286 } 1287 /* The child cp says, it doesn't has matched, but says 1288 also, it's perfectly OK, if it doesn't at all. So we 1289 have a candidat for "zero match". */ 1290 if (result == -1) { 1291 zeroMatchPossible = 1; 1292 } 1293 tncdata->contentStackPtr--; 1294 } 1295 } 1296 /* OK, nobody has claimed a match. Question is: try futher or is 1297 this a document structure error. */ 1298 if (zeroMatchPossible || 1299 stackelm->alreadymatched || 1300 stackelm->model->quant == XML_CQUANT_REP || 1301 stackelm->model->quant == XML_CQUANT_OPT) { 1302 return -1; 1303 } 1304#ifdef TNC_DEBUG 1305 printf ("validation error\n"); 1306#endif 1307 return 0; 1308 case XML_CTYPE_SEQ: 1309#ifdef TNC_DEBUG 1310 printf ("TncProbeElement XML_CTYPE_SEQ\n"); 1311#endif 1312 if (stackelm->alreadymatched) { 1313 activeModel = &stackelm->model->children[stackelm->activeChild]; 1314 if (activeModel->type == XML_CTYPE_NAME) { 1315 /* so this stackelement must be the topmost */ 1316 if (activeModel->quant == XML_CQUANT_REP 1317 || activeModel->quant == XML_CQUANT_PLUS) { 1318 /* the last matched element is multiple, maybe it 1319 matches again */ 1320 if (nameId == activeModel->nameId) { 1321#ifdef TNC_DEBUG 1322 printf ("-->matched! child Nr. %d\n", 1323 stackelm->activeChild); 1324#endif 1325 /* stack and activeChild nr. are already OK, just 1326 report success. */ 1327 return 1; 1328 } 1329 } 1330 } 1331 } 1332 1333 if (stackelm->alreadymatched) { 1334 seqstartindex = stackelm->activeChild + 1; 1335 } 1336 else { 1337 seqstartindex = 0; 1338 } 1339 /* This time zeroMatchPossible flags, if every of the remaining 1340 childs - that may every child, if !alreadymatched - doesn't 1341 must occur. We assume, the (outstanding childs of, in case 1342 of alreadymatched) current stackelement model has only 1343 optional childs, and set to wrong, if we find any 1344 non-optional child */ 1345 zeroMatchPossible = 1; 1346 for (i = seqstartindex; i < stackelm->model->numchildren; i++) { 1347 if ((&stackelm->model->children[i])->type == XML_CTYPE_NAME) { 1348 if ((&stackelm->model->children[i])->nameId == nameId) { 1349#ifdef TNC_DEBUG 1350 printf ("-->matched! child Nr. %d\n",i); 1351#endif 1352 (&tncdata->contentStack[myStackPtr])->activeChild = i; 1353 (&tncdata->contentStack[myStackPtr])->alreadymatched = 1; 1354 return 1; 1355 } else if ((&stackelm->model->children[i])->quant 1356 == XML_CQUANT_NONE 1357 || (&stackelm->model->children[i])->quant 1358 == XML_CQUANT_PLUS) { 1359 zeroMatchPossible = 0; 1360 break; 1361 } 1362 } else { 1363 if (tncdata->contentStackPtr == tncdata->contentStackSize) { 1364 tncdata->contentStack = (TNC_ContentStack *) 1365 Tcl_Realloc ((char *)tncdata->contentStack, 1366 sizeof (TNC_Content *) * 2 * 1367 tncdata->contentStackSize); 1368 tncdata->contentStackSize *= 2; 1369 } 1370 (&tncdata->contentStack[tncdata->contentStackPtr])->model = 1371 &stackelm->model->children[i]; 1372 tncdata->contentStack[tncdata->contentStackPtr].activeChild 1373 = 0; 1374 tncdata->contentStack[tncdata->contentStackPtr].deep 1375 = stackelm->deep + 1; 1376 tncdata->contentStack[tncdata->contentStackPtr].alreadymatched 1377 = 0; 1378 tncdata->contentStackPtr++; 1379 result = TncProbeElement (nameId, tncdata); 1380 if (result == 1) { 1381 (&tncdata->contentStack[myStackPtr])->activeChild = i; 1382 (&tncdata->contentStack[myStackPtr])->alreadymatched = 1; 1383 return 1; 1384 } 1385 tncdata->contentStackPtr--; 1386 if (result == 0) { 1387 zeroMatchPossible = 0; 1388 break; 1389 } 1390 } 1391 } 1392 if (!stackelm->alreadymatched) { 1393 if (zeroMatchPossible) { 1394 /* The stackelm hasn't matched, but don't have to 1395 after all. Return try futher */ 1396 return -1; 1397 } else { 1398 /* No previous match, but at least one child is 1399 necessary. Return depends of the quant of the 1400 entire seq */ 1401 if (stackelm->model->quant == XML_CQUANT_NONE || 1402 stackelm->model->quant == XML_CQUANT_PLUS) { 1403 /* DTD claims, the seq as to be there, but isn't */ 1404 return 0; 1405 } else { 1406 /* The seq is optional */ 1407 return -1; 1408 } 1409 } 1410 } 1411 if (stackelm->alreadymatched) { 1412 if (!zeroMatchPossible) { 1413 /* Some child at the start of the seq has matched in 1414 the past, but since zeroMatchPossible has changed 1415 to zero, there must be a non-matching non-optional 1416 child later. Error in document structure. */ 1417 return 0; 1418 } else { 1419 /* OK, SEQ has matched befor. But after the last match, there 1420 where no required (quant NONE or PLUS) childs. */ 1421 if (stackelm->model->quant == XML_CQUANT_NONE || 1422 stackelm->model->quant == XML_CQUANT_OPT) { 1423 /* The entire seq isn't multiple. Just look futher. */ 1424 return -1; 1425 } 1426 } 1427 } 1428 /* The last untreated case is alreadymatched true, 1429 zeroMatchPossible (of the rest of the seq childs after the 1430 last match) true and the entire seq may be 1431 multiple. Therefore start again with activeChild = 0, to 1432 see, if the current nameId starts a repeated match of the 1433 seq. By the way: zeroMatchPossible still has inital value 1434 1, therefor no second initaliation is needed */ 1435 for (i = 0; i < seqstartindex; i++) { 1436 if ((&stackelm->model->children[i])->type == XML_CTYPE_NAME) { 1437 if ((&stackelm->model->children[i])->nameId == nameId) { 1438#ifdef TNC_DEBUG 1439 printf ("-->matched! child Nr. %d\n",i); 1440#endif 1441 (&tncdata->contentStack[myStackPtr])->activeChild = i; 1442 (&tncdata->contentStack[myStackPtr])->alreadymatched = 1; 1443 return 1; 1444 } else if ((&stackelm->model->children[i])->quant 1445 == XML_CQUANT_NONE 1446 || (&stackelm->model->children[i])->quant 1447 == XML_CQUANT_PLUS) { 1448 zeroMatchPossible = 0; 1449 break; 1450 } 1451 } else { 1452 if (tncdata->contentStackPtr == tncdata->contentStackSize) { 1453 tncdata->contentStack = (TNC_ContentStack *) 1454 Tcl_Realloc ((char *)tncdata->contentStack, 1455 sizeof (TNC_Content *) * 2 * 1456 tncdata->contentStackSize); 1457 tncdata->contentStackSize *= 2; 1458 } 1459 (&tncdata->contentStack[tncdata->contentStackPtr])->model = 1460 &stackelm->model->children[i]; 1461 tncdata->contentStack[tncdata->contentStackPtr].activeChild 1462 = 0; 1463 tncdata->contentStack[tncdata->contentStackPtr].deep 1464 = stackelm->deep + 1; 1465 tncdata->contentStack[tncdata->contentStackPtr].alreadymatched 1466 = 0; 1467 tncdata->contentStackPtr++; 1468 result = TncProbeElement (nameId, tncdata); 1469 if (result) { 1470 (&tncdata->contentStack[myStackPtr])->activeChild = i; 1471 /* alreadymatched is already 1 */ 1472 return 1; 1473 } 1474 tncdata->contentStackPtr--; 1475 if (result == 0) { 1476 /* OK, the seq doesn't match again. But since it have 1477 already matched, this isn't return 0 but.. */ 1478 return -1; 1479 } 1480 } 1481 } 1482 /* seq doesn't match again and every seq child from the very first 1483 up to (not including) the last match aren't required. This last 1484 fact may be nice to know, but after all since the entire seq have 1485 matched already ... */ 1486 return -1; 1487 case XML_CTYPE_NAME: 1488 /* NAME type dosen't occur at top level of a content model and is 1489 handled in some "shotcut" way directly in the CHOICE and SEQ cases. 1490 It's only here to pacify gcc -Wall. */ 1491 printf ("error!!! - in TncProbeElement: XML_CTYPE_NAME shouldn't reached in any case.\n"); 1492 default: 1493 printf ("error!!! - in TncProbeElement: unknown content type: %d\n", 1494 stackelm->model->type); 1495 } 1496 /* not reached */ 1497 printf ("error!!! - in TncProbeElement: end of function reached.\n"); 1498 return 0; 1499} 1500 1501/* 1502 *---------------------------------------------------------------------------- 1503 * 1504 * TncProbeAttribute -- 1505 * 1506 * This function checks, if the given attribute 1507 * and it's value are allowed for this element. 1508 * 1509 * Results: 1510 * 1 if the attribute name/value is OK, 1511 * 0 if not. 1512 * 1513 * Side effects: 1514 * Eventually increments the required attributes counter. 1515 * 1516 *---------------------------------------------------------------------------- 1517 */ 1518 1519static int 1520TncProbeAttribute (userData, elemAtts, attrName, attrValue, nrOfreq) 1521 void *userData; 1522 Tcl_HashTable *elemAtts; 1523 char *attrName; 1524 char *attrValue; 1525 int *nrOfreq; 1526{ 1527 TNC_Data *tncdata = (TNC_Data *) userData; 1528 Tcl_HashEntry *entryPtr; 1529 TNC_AttDecl *attDecl; 1530 char *pc, *copy, save; 1531 int clen, i, start, hnew; 1532 TNC_EntityInfo *entityInfo; 1533 1534 entryPtr = Tcl_FindHashEntry (elemAtts, attrName); 1535 if (!entryPtr) { 1536 signalNotValid (userData, TNC_ERROR_UNKOWN_ATTRIBUTE); 1537 return 0; 1538 } 1539 /* NOTE: attribute uniqueness per element is a wellformed 1540 constrain and therefor done by expat. */ 1541 attDecl = (TNC_AttDecl *) Tcl_GetHashValue (entryPtr); 1542 switch (attDecl->att_type) { 1543 case TNC_ATTTYPE_CDATA: 1544 if (attDecl->isrequired && attDecl->dflt) { 1545 if (strcmp (attDecl->dflt, attrValue) != 0) { 1546 signalNotValid (userData, 1547 TNC_ERROR_WRONG_FIXED_ATTVALUE); 1548 return 0; 1549 } 1550 } 1551 break; 1552 1553 case TNC_ATTTYPE_ID: 1554 pc = (char*)attrValue; 1555 clen = UTF8_CHAR_LEN (*pc); 1556 CHECK_UTF_CHARLENR (clen); 1557 if (!UTF8_GET_NAME_START (pc, clen)) { 1558 signalNotValid (userData, TNC_ERROR_NAME_REQUIRED); 1559 } 1560 pc += clen; 1561 while (1) { 1562 if (*pc == '\0') { 1563 break; 1564 } 1565 clen = UTF8_CHAR_LEN (*pc); 1566 CHECK_UTF_CHARLENR (clen); 1567 if (!UTF8_GET_NAMING_NMTOKEN (pc, clen)) { 1568 signalNotValid (userData, TNC_ERROR_NAME_REQUIRED); 1569 return 0; 1570 } 1571 pc += clen; 1572 } 1573 entryPtr = Tcl_CreateHashEntry (tncdata->ids, attrValue, &hnew); 1574 if (!hnew) { 1575 if (Tcl_GetHashValue (entryPtr)) { 1576 signalNotValid (userData, 1577 TNC_ERROR_DUPLICATE_ID_VALUE); 1578 return 0; 1579 } 1580 } 1581 Tcl_SetHashValue (entryPtr, (char *) 1); 1582 break; 1583 1584 case TNC_ATTTYPE_IDREF: 1585 /* Name type constraint "implicit" checked. If the 1586 referenced ID exists, the type must be OK, because the 1587 type of the ID's within the document are checked. 1588 If there isn't such an ID, it's an error anyway. */ 1589 if (attrValue[0] == '\0') { 1590 signalNotValid (userData, TNC_ERROR_NAME_REQUIRED); 1591 return 0; 1592 } 1593 entryPtr = Tcl_CreateHashEntry (tncdata->ids, attrValue, &hnew); 1594 break; 1595 1596 case TNC_ATTTYPE_IDREFS: 1597 if (attrValue[0] == '\0') { 1598 signalNotValid (userData, TNC_ERROR_NAMES_REQUIRED); 1599 return 0; 1600 } 1601 /* Due to attribute value normalization (xml rec 3.3.3) this 1602 is a simple list "ref ref ref ..." without leading or 1603 trailing spaces and exact one space between the refs. */ 1604 start = i = 0; 1605 while (attrValue[i]) { 1606 if (attrValue[i] == ' ') { 1607 save = attrValue[i]; 1608 attrValue[i] = '\0'; 1609 entryPtr = Tcl_CreateHashEntry (tncdata->ids, 1610 &attrValue[start], &hnew); 1611 attrValue[i] = save; 1612 start = ++i; 1613 continue; 1614 } 1615 i++; 1616 } 1617 entryPtr = Tcl_CreateHashEntry (tncdata->ids, &attrValue[start], 1618 &hnew); 1619 break; 1620 1621 case TNC_ATTTYPE_ENTITY: 1622 /* There is a validity constraint requesting entity attributes 1623 values to be type Name. But if there would be an entity 1624 declaration that doesn't fit this constraint, expat would 1625 have already complained about the definition. So we go the 1626 easy way and just look up the att value. If it's declared, 1627 type must be OK, if not, it's an error anyway. */ 1628 entryPtr = Tcl_FindHashEntry (tncdata->entityDecls, attrValue); 1629 if (!entryPtr) { 1630 signalNotValid (userData, TNC_ERROR_ENTITY_ATTRIBUTE); 1631 return 0; 1632 } 1633 entityInfo = (TNC_EntityInfo *) Tcl_GetHashValue (entryPtr); 1634 if (!entityInfo->is_notation) { 1635 signalNotValid (userData, TNC_ERROR_ENTITY_ATTRIBUTE); 1636 return 0; 1637 } 1638 break; 1639 1640 case TNC_ATTTYPE_ENTITIES: 1641 /* Normalized by exapt; for type see comment to 1642 TNC_ATTTYPE_ENTITY */ 1643 copy = tdomstrdup (attrValue); 1644 start = i = 0; 1645 while (1) { 1646 if (copy[i] == '\0') { 1647 entryPtr = Tcl_FindHashEntry (tncdata->entityDecls, 1648 ©[start]); 1649 if (!entryPtr) { 1650 signalNotValid (userData, TNC_ERROR_ENTITIES_ATTRIBUTE); 1651 FREE (copy); 1652 return 0; 1653 } 1654 entityInfo = (TNC_EntityInfo *) Tcl_GetHashValue (entryPtr); 1655 if (!entityInfo->is_notation) { 1656 signalNotValid (userData, TNC_ERROR_ENTITIES_ATTRIBUTE); 1657 FREE (copy); 1658 return 0; 1659 } 1660 FREE (copy); 1661 break; 1662 } 1663 if (copy[i] == ' ') { 1664 copy[i] = '\0'; 1665 entryPtr = Tcl_FindHashEntry (tncdata->entityDecls, 1666 ©[start]); 1667 if (!entryPtr) { 1668 signalNotValid (userData, TNC_ERROR_ENTITIES_ATTRIBUTE); 1669 FREE (copy); 1670 return 0; 1671 } 1672 entityInfo = (TNC_EntityInfo *) Tcl_GetHashValue (entryPtr); 1673 if (!entityInfo->is_notation) { 1674 signalNotValid (userData, TNC_ERROR_ENTITIES_ATTRIBUTE); 1675 FREE (copy); 1676 return 0; 1677 } 1678 start = ++i; 1679 continue; 1680 } 1681 i++; 1682 } 1683 break; 1684 1685 case TNC_ATTTYPE_NMTOKEN: 1686 /* We assume, that the UTF-8 representation of the value is 1687 valid (no partial chars, minimum encoding). This makes 1688 things a little more easy and faster. I guess (but 1689 haven't deeply checked - QUESTION -), expat would have 1690 already complained otherwise. */ 1691 pc = (char*)attrValue; 1692 clen = 0; 1693 while (1) { 1694 if (*pc == '\0') { 1695 break; 1696 } 1697 clen = UTF8_CHAR_LEN (*pc); 1698 CHECK_UTF_CHARLENR (clen); 1699 if (!UTF8_GET_NAMING_NMTOKEN (pc, clen)) { 1700 signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 1701 return 0; 1702 } 1703 pc += clen; 1704 } 1705 if (!clen) 1706 signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 1707 break; 1708 1709 case TNC_ATTTYPE_NMTOKENS: 1710 pc = (char*)attrValue; 1711 clen = 0; 1712 while (1) { 1713 if (*pc == '\0') { 1714 break; 1715 } 1716 /* NMTOKENS are normalized by expat, so this should 1717 be secure. */ 1718 if (*pc == ' ') { 1719 pc++; 1720 } 1721 clen = UTF8_CHAR_LEN (*pc); 1722 CHECK_UTF_CHARLENR (clen); 1723 if (!UTF8_GET_NAMING_NMTOKEN (pc, clen)) { 1724 signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 1725 return 0; 1726 } 1727 pc += clen; 1728 } 1729 if (!clen) 1730 signalNotValid (userData, TNC_ERROR_NMTOKEN_REQUIRED); 1731 break; 1732 1733 case TNC_ATTTYPE_NOTATION: 1734 entryPtr = Tcl_FindHashEntry (attDecl->lookupTable, attrValue); 1735 if (!entryPtr) { 1736 signalNotValid (userData, TNC_ERROR_NOTATION_REQUIRED); 1737 return 0; 1738 } 1739 break; 1740 1741 case TNC_ATTTYPE_ENUMERATION: 1742 if (!Tcl_FindHashEntry (attDecl->lookupTable, attrValue)) { 1743 signalNotValid (userData, TNC_ERROR_ENUM_ATT_WRONG_VALUE); 1744 return 0; 1745 } 1746 break; 1747 } 1748 1749 if (attDecl->isrequired) { 1750 (*nrOfreq)++; 1751 } 1752 1753 return 1; 1754} 1755 1756/* 1757 *---------------------------------------------------------------------------- 1758 * 1759 * TncElementStartCommand -- 1760 * 1761 * This procedure is called for every element start event 1762 * while parsing XML Data with a "tnc" enabled tclexpat 1763 * parser. Checks, if the element can occur here and if it 1764 * has an acceptable set of attributes. 1765 * 1766 * Results: 1767 * None. 1768 * 1769 * Side effects: 1770 * Eventually signals application error. 1771 * 1772 *---------------------------------------------------------------------------- 1773 */ 1774 1775void 1776TncElementStartCommand (userData, name, atts) 1777 void *userData; 1778 const char *name; 1779 const char **atts; 1780{ 1781 TNC_Data *tncdata = (TNC_Data *) userData; 1782 Tcl_HashEntry *entryPtr; 1783 Tcl_HashTable *elemAtts; 1784 const char **atPtr; 1785 TNC_ElemAttInfo *elemAttInfo; 1786 TNC_Content *model; 1787 int result, nrOfreq, acceptNoDoctype = 0; 1788 1789#ifdef TNC_DEBUG 1790 printf ("TncElementStartCommand name: %s\n", name); 1791#endif 1792 1793 /* If the document doesn't have a doctype declaration, but the 1794 user have used the -useForeignDTD 1 feature, the collected 1795 data out of the provided DTD isn't postprocessed by 1796 TncElementStartCommand. We do this now. 1797 NOTE: Since there wasn't a doctype declaration, there is no 1798 information avaliable which element is expected to be the 1799 document element. Eventually it would be desirable, to set 1800 this somehow. For now, this means, that every valid subtree 1801 of the given DTD information is accepted. */ 1802 if (!tncdata->contentStackPtr && !tncdata->elemContentsRewriten) { 1803 TncEndDoctypeDeclHandler (userData); 1804 acceptNoDoctype = 1; 1805 } 1806 1807 entryPtr = Tcl_FindHashEntry (tncdata->tagNames, name); 1808 if (!entryPtr) { 1809 signalNotValid (userData, TNC_ERROR_UNKNOWN_ELEMENT); 1810 return; 1811 } 1812 model = (TNC_Content *) Tcl_GetHashValue (entryPtr); 1813 1814 switch (model->type) { 1815 case XML_CTYPE_MIXED: 1816 case XML_CTYPE_ANY: 1817 tncdata->ignoreWhiteCDATAs = 1; 1818 tncdata->ignorePCDATA = 1; 1819 break; 1820 case XML_CTYPE_EMPTY: 1821 tncdata->ignoreWhiteCDATAs = 0; 1822 break; 1823 case XML_CTYPE_CHOICE: 1824 case XML_CTYPE_SEQ: 1825 tncdata->ignoreWhiteCDATAs = 1; 1826 tncdata->ignorePCDATA = 0; 1827 break; 1828 case XML_CTYPE_NAME: 1829 break; 1830 } 1831 1832 if (tncdata->contentStackPtr) { 1833 /* This is the normal case, within some content, 1834 at least the root element content. */ 1835 while (1) { 1836 result = TncProbeElement (entryPtr, tncdata); 1837 if (result == -1) { 1838 if (tncdata->contentStack[tncdata->contentStackPtr - 1].deep 1839 == 0) { 1840 signalNotValid (userData, 1841 TNC_ERROR_ELEMENT_NOT_ALLOWED_HERE); 1842 return; 1843 } 1844 tncdata->contentStackPtr--; 1845 continue; 1846 } 1847 if (result) { 1848 break; 1849 } 1850 if (!result) { 1851 signalNotValid (userData, TNC_ERROR_ELEMENT_NOT_ALLOWED_HERE); 1852 return; 1853 } 1854 } 1855 if (tncdata->contentStackPtr == tncdata->contentStackSize) { 1856 tncdata->contentStackSize *= 2; 1857 tncdata->contentStack = (TNC_ContentStack *) 1858 Tcl_Realloc ((char *)tncdata->contentStack, 1859 sizeof (TNC_Content *)*tncdata->contentStackSize); 1860 } 1861 (&tncdata->contentStack[tncdata->contentStackPtr])->model = model; 1862 (&tncdata->contentStack[tncdata->contentStackPtr])->activeChild = 0; 1863 (&tncdata->contentStack[tncdata->contentStackPtr])->deep = 0; 1864 (&tncdata->contentStack[tncdata->contentStackPtr])->alreadymatched = 0; 1865 tncdata->contentStackPtr++; 1866 } else { 1867 /* This is only in case of the root element */ 1868 if (atts) { 1869 if (!tncdata->doctypeName) { 1870 if (!acceptNoDoctype) { 1871 signalNotValid (userData, TNC_ERROR_NO_DOCTYPE_DECL); 1872 return; 1873 } 1874 } else { 1875 if (strcmp (tncdata->doctypeName, name) != 0) { 1876 signalNotValid (userData, TNC_ERROR_WRONG_ROOT_ELEMENT); 1877 return; 1878 } 1879 } 1880 } 1881 (&(tncdata->contentStack)[0])->model = model; 1882 (&(tncdata->contentStack)[0])->activeChild = 0; 1883 (&(tncdata->contentStack)[0])->deep = 0; 1884 (&(tncdata->contentStack)[0])->alreadymatched = 0; 1885 tncdata->contentStackPtr++; 1886 } 1887 1888 if (atts) { 1889 elemAttInfo = model->attInfo; 1890 if (!elemAttInfo) { 1891 if (atts[0] != NULL) { 1892 signalNotValid (userData, TNC_ERROR_NO_ATTRIBUTES); 1893 return; 1894 } 1895 } else { 1896 elemAtts = elemAttInfo->attributes; 1897 nrOfreq = 0; 1898 for (atPtr = atts; atPtr[0]; atPtr += 2) { 1899 if (!TncProbeAttribute (userData, elemAtts, (char *) atPtr[0], 1900 (char *) atPtr[1], &nrOfreq)) 1901 return; 1902 } 1903 if (nrOfreq != elemAttInfo->nrOfreq) { 1904 signalNotValid (userData, 1905 TNC_ERROR_MISSING_REQUIRED_ATTRIBUTE); 1906 return; 1907 } 1908 } 1909 } else { 1910 tncdata->elemAttInfo = model->attInfo; 1911 } 1912 1913#ifdef TNC_DEBUG 1914 printf ("TncElementStartCommand end\n"); 1915#endif 1916} 1917 1918 1919/* 1920 *---------------------------------------------------------------------------- 1921 * 1922 * TncProbeElementEnd -- 1923 * 1924 * This procedure checks, if the current content allows the 1925 * the element to end here. 1926 * 1927 * Results: 1928 * 1 if element end is OK, 1929 * 0 if not. 1930 * 1931 * Side effects: 1932 * Let the contentStackPtr point to the last current content 1933 * model before the element had started. 1934 * 1935 *---------------------------------------------------------------------------- 1936 */ 1937 1938static int 1939TncProbeElementEnd (tncdata) 1940 TNC_Data *tncdata; 1941{ 1942 TNC_ContentStack stackelm; 1943 unsigned int i; 1944 int zeroMatchPossible, seqstartindex; 1945 1946 stackelm = tncdata->contentStack[tncdata->contentStackPtr - 1]; 1947 switch (stackelm.model->type) { 1948 case XML_CTYPE_MIXED: 1949 case XML_CTYPE_ANY: 1950 case XML_CTYPE_EMPTY: 1951 return 1; 1952 case XML_CTYPE_CHOICE: 1953 if (stackelm.alreadymatched) { 1954 return 1; 1955 } 1956 1957 if (stackelm.model->quant == XML_CQUANT_REP || 1958 stackelm.model->quant == XML_CQUANT_OPT) { 1959 return 1; 1960 } 1961 zeroMatchPossible = 0; 1962 for (i = 0; i < stackelm.model->numchildren; i++) { 1963 if ((&stackelm.model->children[i])->type == XML_CTYPE_NAME) { 1964 if ((&stackelm.model->children[i])->quant == XML_CQUANT_OPT || 1965 (&stackelm.model->children[i])->quant == XML_CQUANT_REP) { 1966 zeroMatchPossible = 1; 1967 break; 1968 } 1969 } 1970 else { 1971 if (tncdata->contentStackPtr == tncdata->contentStackSize) { 1972 tncdata->contentStack = (TNC_ContentStack *) 1973 Tcl_Realloc ((char *)tncdata->contentStack, 1974 sizeof (TNC_Content *) * 2 * 1975 tncdata->contentStackSize); 1976 tncdata->contentStackSize *= 2; 1977 } 1978 (&tncdata->contentStack[tncdata->contentStackPtr])->model 1979 = &stackelm.model->children[i]; 1980 tncdata->contentStack[tncdata->contentStackPtr].activeChild 1981 = 0; 1982 tncdata->contentStack[tncdata->contentStackPtr].deep 1983 = stackelm.deep + 1; 1984 tncdata->contentStack[tncdata->contentStackPtr].alreadymatched 1985 = 0; 1986 tncdata->contentStackPtr++; 1987 if (TncProbeElementEnd (tncdata)) { 1988 zeroMatchPossible = 1; 1989 tncdata->contentStackPtr--; 1990 break; 1991 } 1992 tncdata->contentStackPtr--; 1993 } 1994 } 1995 if (zeroMatchPossible) { 1996 return 1; 1997 } else { 1998 return 0; 1999 } 2000 case XML_CTYPE_SEQ: 2001 if (!stackelm.alreadymatched) { 2002 if (stackelm.model->quant == XML_CQUANT_REP || 2003 stackelm.model->quant == XML_CQUANT_OPT) { 2004 return 1; 2005 } 2006 } 2007 if (!stackelm.alreadymatched) { 2008 seqstartindex = 0; 2009 } 2010 else { 2011 seqstartindex = stackelm.activeChild + 1; 2012 } 2013 for (i = seqstartindex; i < stackelm.model->numchildren; i++) { 2014 if ((&stackelm.model->children[i])->type == XML_CTYPE_NAME) { 2015 if ((&stackelm.model->children[i])->quant == XML_CQUANT_OPT || 2016 (&stackelm.model->children[i])->quant == XML_CQUANT_REP) { 2017 continue; 2018 } else { 2019 return 0; 2020 } 2021 } else { 2022 if (tncdata->contentStackPtr == tncdata->contentStackSize) { 2023 tncdata->contentStack = (TNC_ContentStack *) 2024 Tcl_Realloc ((char *)tncdata->contentStack, 2025 sizeof (TNC_Content *) * 2 * 2026 tncdata->contentStackSize); 2027 tncdata->contentStackSize *= 2; 2028 } 2029 (&tncdata->contentStack[tncdata->contentStackPtr])->model 2030 = &stackelm.model->children[i]; 2031 tncdata->contentStack[tncdata->contentStackPtr].activeChild 2032 = 0; 2033 tncdata->contentStack[tncdata->contentStackPtr].deep 2034 = stackelm.deep + 1; 2035 tncdata->contentStack[tncdata->contentStackPtr].alreadymatched 2036 = 0; 2037 tncdata->contentStackPtr++; 2038 if (TncProbeElementEnd (tncdata)) { 2039 tncdata->contentStackPtr--; 2040 continue; 2041 } 2042 else { 2043 tncdata->contentStackPtr--; 2044 return 0; 2045 } 2046 } 2047 } 2048 return 1; 2049 case XML_CTYPE_NAME: 2050 /* NAME type dosen't occur at top level of a content model and is 2051 handled in some "shotcut" way directly in the CHOICE and SEQ cases. 2052 It's only here to pacify gcc -Wall. */ 2053 fprintf (stderr, "error!!! - in TncProbeElementEnd: XML_CTYPE_NAME " 2054 "shouldn't be reached in any case.\n"); 2055 default: 2056 fprintf (stderr, "error!!! - in TncProbeElementEnd: unknown content " 2057 "type: %d\n", stackelm.model->type); 2058 return 1; 2059 } 2060} 2061 2062 2063/* 2064 *---------------------------------------------------------------------------- 2065 * 2066 * TncElementEndCommand -- 2067 * 2068 * This procedure is called for every element end event 2069 * while parsing XML Data with a "tnc" enabled tclexpat 2070 * parser. Checks, if the content model allows the element 2071 * to end at this point. 2072 * 2073 * Results: 2074 * None. 2075 * 2076 * Side effects: 2077 * Eventually signals application error. 2078 * 2079 *---------------------------------------------------------------------------- 2080 */ 2081 2082void 2083TncElementEndCommand (userData, name) 2084 void *userData; 2085 const char *name; 2086{ 2087 TNC_Data *tncdata = (TNC_Data *) userData; 2088 Tcl_HashEntry *entryPtr; 2089 Tcl_HashSearch search; 2090 2091#ifdef TNC_DEBUG 2092 printf ("TncElementEndCommand start\n"); 2093 printContentStack (tncdata); 2094#endif 2095 while (1) { 2096 if (!TncProbeElementEnd (tncdata, 0)) { 2097 signalNotValid (userData, TNC_ERROR_ELEMENT_CAN_NOT_END_HERE); 2098 return; 2099 } 2100 if (tncdata->contentStack[tncdata->contentStackPtr - 1].deep == 0) { 2101 break; 2102 } 2103 tncdata->contentStackPtr--; 2104 } 2105 /* Remove the content model of the closed element from the stack */ 2106 tncdata->contentStackPtr--; 2107#ifdef TNC_DEBUG 2108 printf ("after removing ended element from the stack\n"); 2109 printContentStack (tncdata); 2110#endif 2111 if (tncdata->contentStackPtr) { 2112 switch ((&tncdata->contentStack[tncdata->contentStackPtr - 1])->model->type) { 2113 case XML_CTYPE_MIXED: 2114 case XML_CTYPE_ANY: 2115 tncdata->ignoreWhiteCDATAs = 1; 2116 tncdata->ignorePCDATA = 1; 2117 break; 2118 case XML_CTYPE_EMPTY: 2119 tncdata->ignoreWhiteCDATAs = 0; 2120 break; 2121 case XML_CTYPE_CHOICE: 2122 case XML_CTYPE_SEQ: 2123 case XML_CTYPE_NAME: 2124 tncdata->ignoreWhiteCDATAs = 1; 2125 tncdata->ignorePCDATA = 0; 2126 break; 2127 } 2128 } else { 2129 /* This means, the root element is closed, 2130 therefor the place to check, if every IDREF points 2131 to a ID. */ 2132 if (tncdata->idCheck) { 2133 for (entryPtr = Tcl_FirstHashEntry (tncdata->ids, &search); 2134 entryPtr != NULL; 2135 entryPtr = Tcl_NextHashEntry (&search)) { 2136#ifdef TNC_DEBUG 2137 printf ("check id value %s\n", 2138 Tcl_GetHashKey (tncdata->ids, entryPtr)); 2139 printf ("value %p\n", Tcl_GetHashValue (entryPtr)); 2140#endif 2141 if (!Tcl_GetHashValue (entryPtr)) { 2142 signalNotValid (userData, TNC_ERROR_UNKOWN_ID_REFERRED); 2143 return; 2144 } 2145 } 2146 } 2147 } 2148} 2149 2150/* 2151 *---------------------------------------------------------------------------- 2152 * 2153 * TncCharacterdataCommand -- 2154 * 2155 * This procedure is called with a piece of CDATA found in 2156 * document. 2157 * 2158 * Results: 2159 * None. 2160 * 2161 * Side effects: 2162 * Eventually signals application error. 2163 * 2164 *---------------------------------------------------------------------------- 2165 */ 2166 2167void 2168TncCharacterdataCommand (userData, data, len) 2169 void *userData; 2170 const char *data; 2171 int len; 2172{ 2173 TNC_Data *tncdata = (TNC_Data *) userData; 2174 int i; 2175 char *pc; 2176 2177 if (!tncdata->ignoreWhiteCDATAs && len > 0) { 2178 signalNotValid (userData, TNC_ERROR_EMPTY_ELEMENT); 2179 return; 2180 } 2181 if (!tncdata->ignorePCDATA) { 2182 for (i = 0, pc = (char*)data; i < len; i++, pc++) { 2183 if ( (*pc == ' ') || 2184 (*pc == '\n') || 2185 (*pc == '\r') || 2186 (*pc == '\t') ) { 2187 continue; 2188 } 2189 signalNotValid (userData, TNC_ERROR_DISALLOWED_PCDATA); 2190 return; 2191 } 2192 } 2193} 2194 2195/* 2196 *---------------------------------------------------------------------------- 2197 * 2198 * TncStartCdataSectionHandler -- 2199 * 2200 * This procedure is called at the start of a CDATA section 2201 * within the document. 2202 * 2203 * Results: 2204 * None. 2205 * 2206 * Side effects: 2207 * Eventually signals application error. 2208 * 2209 *---------------------------------------------------------------------------- 2210 */ 2211 2212void 2213TncStartCdataSectionHandler (userData) 2214 void *userData; 2215{ 2216 TNC_Data *tncdata = (TNC_Data *) userData; 2217 2218 if (!tncdata->ignorePCDATA) { 2219 signalNotValid (userData, TNC_ERROR_DISALLOWED_CDATA); 2220 } 2221} 2222 2223/* 2224 *---------------------------------------------------------------------------- 2225 * 2226 * validateNodeAttributes -- 2227 * 2228 * Validates the attributes of the given domNode. The domNode must be 2229 * an ELEMENT_NODE. 2230 * 2231 * Results: 2232 * 1 if OK, 0 for validation error. 2233 * 2234 * Side effects: 2235 * None. 2236 * 2237 *---------------------------------------------------------------------------- 2238 */ 2239 2240static int 2241validateNodeAttributes ( 2242 TNC_Data *tncdata, 2243 TNC_ElemAttInfo *elemAttInfo, 2244 domNode *node 2245 ) 2246{ 2247 int nrOfreq; 2248 domAttrNode *attr; 2249 2250 if (!elemAttInfo) { 2251 if (node->firstAttr) { 2252 signalNotValid (tncdata, TNC_ERROR_NO_ATTRIBUTES); 2253 return 0; 2254 } 2255 } else { 2256 attr = node->firstAttr; 2257 nrOfreq = 0; 2258 while (attr) { 2259 if (!TncProbeAttribute (tncdata, 2260 elemAttInfo->attributes, 2261 attr->nodeName, 2262 attr->nodeValue, 2263 &nrOfreq)) 2264 return 0; 2265 attr = attr->nextSibling; 2266 } 2267 if (nrOfreq != elemAttInfo->nrOfreq) { 2268 signalNotValid (tncdata, 2269 TNC_ERROR_MISSING_REQUIRED_ATTRIBUTE); 2270 return 0; 2271 } 2272 } 2273 return 1; 2274} 2275 2276/* 2277 *---------------------------------------------------------------------------- 2278 * 2279 * validateTree -- 2280 * 2281 * Validates a complete DOM (sub-)tree against a the DTD informations in 2282 * the given tncdata structure. The node argument acts as root of the 2283 * (sub-)tree. 2284 * 2285 * Results: 2286 * 1 if OK, 0 for validation error. 2287 * 2288 * Side effects: 2289 * May alter the context state part of the tnc clientData (and even 2290 * mallocs additional memory for them). 2291 * 2292 *---------------------------------------------------------------------------- 2293 */ 2294static int validateTree ( 2295 TNC_Data *tncdata, 2296 domNode *node 2297 ) 2298{ 2299 domNode *child; 2300 2301 switch (node->nodeType) { 2302 case ELEMENT_NODE: 2303 TncElementStartCommand (tncdata, node->nodeName, NULL); 2304 if (tncdata->status) return 0; 2305 if (!validateNodeAttributes (tncdata, tncdata->elemAttInfo, node)) 2306 return 0; 2307 if (node->firstChild) { 2308 child = node->firstChild; 2309 while (child) { 2310 if (!validateTree (tncdata, child)) return 0; 2311 child = child->nextSibling; 2312 } 2313 } 2314 TncElementEndCommand (tncdata, node->nodeName); 2315 if (tncdata->status) return 0; 2316 break; 2317 case TEXT_NODE: 2318 case CDATA_SECTION_NODE: 2319 TncCharacterdataCommand (tncdata, ((domTextNode*)node)->nodeValue, 2320 ((domTextNode*)node)->valueLength); 2321 if (tncdata->status) return 0; 2322 break; 2323 case COMMENT_NODE: 2324 case PROCESSING_INSTRUCTION_NODE: 2325 break; 2326 default: 2327 signalNotValid (tncdata, TNC_ERROR_UNKNOWN_NODE_TYPE); 2328 return 0; 2329 } 2330 return 1; 2331} 2332 2333/* 2334 *---------------------------------------------------------------------------- 2335 * 2336 * tnc_ValidateObjCmd 2337 * 2338 * Implements the validateObjCmds. See the user documentation 2339 * for details. 2340 * 2341 * Results: 2342 * A standard Tcl result. 2343 * 2344 * Side effects: 2345 * May alter some parts of the tnc_ValidateObjCmd clientData 2346 * structure. 2347 * 2348 *---------------------------------------------------------------------------- 2349 */ 2350 2351static int 2352tnc_ValidateObjCmd ( 2353 ClientData clientData, 2354 Tcl_Interp *interp, 2355 int objc, 2356 Tcl_Obj *CONST objv[] 2357 ) 2358{ 2359 TNC_Data *tncdata = (TNC_Data*) clientData; 2360 int methodIndex, result = 1; 2361 domNode *node; 2362 char *errMsg = NULL; 2363 Tcl_HashEntry *entryPtr; 2364 TNC_Content *model; 2365 2366 static CONST84 char *validateMethods[] = { 2367 "validateTree", "validateDocument", "validateAttributes", 2368 "delete", 2369 NULL 2370 }; 2371 enum validateMethod { 2372 m_validateTree, m_validateDocument, m_validateAttributes, 2373 m_delete 2374 }; 2375 2376 if (objc < 2 || objc > 4) { 2377 SetResult (validateCmd_usage); 2378 return TCL_ERROR; 2379 } 2380 2381 if (Tcl_GetIndexFromObj (interp, objv[1], validateMethods, "method", 0, 2382 &methodIndex) != TCL_OK) { 2383 return TCL_ERROR; 2384 } 2385 2386 2387 switch ((enum validateMethod) methodIndex) { 2388 2389 case m_validateTree: 2390 if (objc < 3 || objc > 4) { 2391 SetResult (validateCmd_usage); 2392 return TCL_ERROR; 2393 } 2394 node = tcldom_getNodeFromName ( 2395 interp, Tcl_GetStringFromObj(objv[2], NULL), &errMsg 2396 ); 2397 if (!node || (node->nodeType != ELEMENT_NODE)) { 2398 SetResult ("The validateTree method needs a domNode as argument."); 2399 return TCL_ERROR; 2400 } 2401 tncdata->status = 0; 2402 tncdata->idCheck = 0; 2403 if (tncdata->ids->numEntries) { 2404 Tcl_DeleteHashTable (tncdata->ids); 2405 Tcl_InitHashTable (tncdata->ids, TCL_STRING_KEYS); 2406 } 2407 tncdata->contentStackPtr = 0; 2408 Tcl_ResetResult (interp); 2409 result = validateTree (tncdata, node); 2410 if (objc == 4) { 2411 if (Tcl_ObjSetVar2(interp, objv[3], NULL, 2412 Tcl_GetObjResult(interp), 0) == NULL) { 2413 Tcl_ResetResult(interp); 2414 Tcl_AppendToObj(Tcl_GetObjResult(interp), 2415 "couldn't save msg in variable", -1); 2416 return TCL_ERROR; 2417 } 2418 } 2419 if (result) { 2420 SetBooleanResult (1); 2421 } else { 2422 SetBooleanResult (0); 2423 } 2424 break; 2425 2426 case m_validateDocument: 2427 if (objc < 3 || objc > 4) { 2428 SetResult (validateCmd_usage); 2429 return TCL_ERROR; 2430 } 2431 node = (domNode *) tcldom_getDocumentFromName ( 2432 interp, Tcl_GetStringFromObj (objv[2], NULL), &errMsg 2433 ); 2434 if (!node) { 2435 SetResult ("The validateDocument method needs a domDocument as argument."); 2436 return TCL_ERROR; 2437 } 2438 node = ((domDocument *) node)->documentElement; 2439 if (!tncdata->doctypeName) { 2440 signalNotValid (tncdata, TNC_ERROR_NO_DOCTYPE_DECL); 2441 if (objc == 4) { 2442 if (Tcl_ObjSetVar2(interp, objv[3], NULL, 2443 Tcl_GetObjResult(interp), 0) == NULL) { 2444 Tcl_ResetResult(interp); 2445 Tcl_AppendToObj(Tcl_GetObjResult(interp), 2446 "couldn't save msg in variable", -1); 2447 return TCL_ERROR; 2448 } 2449 } 2450 SetBooleanResult (0); 2451 return TCL_OK; 2452 } 2453 if (strcmp (tncdata->doctypeName, node->nodeName) != 0) { 2454 signalNotValid (tncdata, TNC_ERROR_WRONG_ROOT_ELEMENT); 2455 if (objc == 4) { 2456 if (Tcl_ObjSetVar2(interp, objv[3], NULL, 2457 Tcl_GetObjResult(interp), 0) == NULL) { 2458 Tcl_ResetResult(interp); 2459 Tcl_AppendToObj(Tcl_GetObjResult(interp), 2460 "couldn't save msg in variable", -1); 2461 return TCL_ERROR; 2462 } 2463 } 2464 SetBooleanResult (0); 2465 return TCL_OK; 2466 } 2467 tncdata->status = 0; 2468 tncdata->idCheck = 1; 2469 if (tncdata->ids->numEntries) { 2470 Tcl_DeleteHashTable (tncdata->ids); 2471 Tcl_InitHashTable (tncdata->ids, TCL_STRING_KEYS); 2472 } 2473 tncdata->contentStackPtr = 0; 2474 Tcl_ResetResult (interp); 2475 result = validateTree (tncdata, node); 2476 if (objc == 4) { 2477 if (Tcl_ObjSetVar2(interp, objv[3], NULL, 2478 Tcl_GetObjResult(interp), 0) == NULL) { 2479 Tcl_ResetResult(interp); 2480 Tcl_AppendToObj(Tcl_GetObjResult(interp), 2481 "couldn't save msg in variable", -1); 2482 return TCL_ERROR; 2483 } 2484 } 2485 if (result) { 2486 SetBooleanResult (1); 2487 } else { 2488 SetBooleanResult (0); 2489 } 2490 break; 2491 2492 case m_validateAttributes: 2493 if (objc < 3 || objc > 4) { 2494 SetResult (validateCmd_usage); 2495 return TCL_ERROR; 2496 } 2497 node = tcldom_getNodeFromName ( 2498 interp, Tcl_GetStringFromObj(objv[2], NULL), &errMsg 2499 ); 2500 if (!node || (node->nodeType != ELEMENT_NODE)) { 2501 SetResult ("The validateAttributes method needs a domNode as argument."); 2502 return TCL_ERROR; 2503 } 2504 entryPtr = Tcl_FindHashEntry (tncdata->tagNames, node->nodeName); 2505 if (!entryPtr) { 2506 signalNotValid (tncdata, TNC_ERROR_UNKNOWN_ELEMENT); 2507 if (objc == 4) { 2508 if (Tcl_ObjSetVar2(interp, objv[3], NULL, 2509 Tcl_GetObjResult(interp), 0) == NULL) { 2510 Tcl_ResetResult(interp); 2511 Tcl_AppendToObj(Tcl_GetObjResult(interp), 2512 "couldn't save msg in variable", -1); 2513 return TCL_ERROR; 2514 } 2515 } 2516 SetBooleanResult (0); 2517 return TCL_OK; 2518 } 2519 model = (TNC_Content *) Tcl_GetHashValue (entryPtr); 2520 tncdata->status = 0; 2521 tncdata->idCheck = 0; 2522 if (tncdata->ids->numEntries) { 2523 Tcl_DeleteHashTable (tncdata->ids); 2524 Tcl_InitHashTable (tncdata->ids, TCL_STRING_KEYS); 2525 } 2526 Tcl_ResetResult (interp); 2527 result = validateNodeAttributes (tncdata, model->attInfo, node); 2528 if (objc == 4) { 2529 if (Tcl_ObjSetVar2(interp, objv[3], NULL, 2530 Tcl_GetObjResult(interp), 0) == NULL) { 2531 Tcl_ResetResult(interp); 2532 Tcl_AppendToObj(Tcl_GetObjResult(interp), 2533 "couldn't save msg in variable", -1); 2534 return TCL_ERROR; 2535 } 2536 } 2537 if (result) { 2538 SetBooleanResult (1); 2539 } else { 2540 SetBooleanResult (0); 2541 } 2542 break; 2543 2544 case m_delete: 2545 if (objc != 2) { 2546 SetResult (validateCmd_usage); 2547 return TCL_ERROR; 2548 } 2549 Tcl_DeleteCommand (interp, Tcl_GetStringFromObj (objv[0], NULL)); 2550 SetResult (""); 2551 break; 2552 } 2553 2554 return TCL_OK; 2555} 2556 2557/* 2558 *---------------------------------------------------------------------------- 2559 * 2560 * FreeTncData 2561 * 2562 * Helper proc, used from TncResetProc and TncFreeProc. Frees all 2563 * collected DTD data and the id table. 2564 * 2565 * Results: 2566 * None. 2567 * 2568 * Side effects: 2569 * Frees memory. 2570 * 2571 *---------------------------------------------------------------------------- */ 2572static void 2573FreeTncData (tncdata) 2574 TNC_Data *tncdata; 2575{ 2576 Tcl_HashEntry *entryPtr, *attentryPtr; 2577 Tcl_HashSearch search, attsearch; 2578 TNC_Content *model; 2579 TNC_ElemAttInfo *elemAttInfo; 2580 TNC_EntityInfo *entityInfo; 2581 TNC_AttDecl *attDecl; 2582 2583 if (tncdata->elemContentsRewriten) { 2584 entryPtr = Tcl_FirstHashEntry (tncdata->tagNames, &search); 2585 while (entryPtr) { 2586 model = Tcl_GetHashValue (entryPtr); 2587 if (model) { 2588 TncFreeTncModel (model); 2589 FREE ((char *) model); 2590 } 2591 entryPtr = Tcl_NextHashEntry (&search); 2592 } 2593 } 2594 Tcl_DeleteHashTable (tncdata->tagNames); 2595 entryPtr = Tcl_FirstHashEntry (tncdata->attDefsTables, &search); 2596 while (entryPtr) { 2597 elemAttInfo = Tcl_GetHashValue (entryPtr); 2598 if (!elemAttInfo) { 2599 entryPtr = Tcl_NextHashEntry (&search); 2600 continue; 2601 } 2602 attentryPtr = Tcl_FirstHashEntry (elemAttInfo->attributes, &attsearch); 2603 while (attentryPtr) { 2604 attDecl = Tcl_GetHashValue (attentryPtr); 2605 if (attDecl) { 2606 if (attDecl->att_type == TNC_ATTTYPE_NOTATION || 2607 attDecl->att_type == TNC_ATTTYPE_ENUMERATION) { 2608 Tcl_DeleteHashTable (attDecl->lookupTable); 2609 FREE ((char *) attDecl->lookupTable); 2610 } 2611 if (attDecl->dflt) { 2612 FREE (attDecl->dflt); 2613 } 2614 FREE ((char *) attDecl); 2615 } 2616 attentryPtr = Tcl_NextHashEntry (&attsearch); 2617 } 2618 Tcl_DeleteHashTable (elemAttInfo->attributes); 2619 FREE ((char *) elemAttInfo->attributes); 2620 FREE ((char *) elemAttInfo); 2621 entryPtr = Tcl_NextHashEntry (&search); 2622 } 2623 Tcl_DeleteHashTable (tncdata->attDefsTables); 2624 entryPtr = Tcl_FirstHashEntry (tncdata->entityDecls, &search); 2625 while (entryPtr) { 2626 entityInfo = Tcl_GetHashValue (entryPtr); 2627 if (entityInfo) { 2628 if (entityInfo->is_notation) { 2629 FREE (entityInfo->notationName); 2630 } 2631 FREE ((char *) entityInfo); 2632 } 2633 entryPtr = Tcl_NextHashEntry (&search); 2634 } 2635 Tcl_DeleteHashTable (tncdata->entityDecls); 2636 Tcl_DeleteHashTable (tncdata->notationDecls); 2637 Tcl_DeleteHashTable (tncdata->ids); 2638 if (tncdata->doctypeName) { 2639 FREE (tncdata->doctypeName); 2640 } 2641} 2642 2643/* 2644 *---------------------------------------------------------------------------- 2645 * 2646 * TncResetProc 2647 * 2648 * Called for C handler set specific reset actions in case of 2649 * parser reset. 2650 * 2651 * Results: 2652 * None. 2653 * 2654 * Side effects: 2655 * Resets the "userData" of the C handler set parser extension. 2656 * 2657 *---------------------------------------------------------------------------- 2658 */ 2659 2660void 2661TncResetProc (interp, userData) 2662 Tcl_Interp *interp; 2663 void *userData; 2664{ 2665 TNC_Data *tncdata = (TNC_Data *) userData; 2666 2667 FreeTncData (tncdata); 2668 Tcl_InitHashTable (tncdata->tagNames, TCL_STRING_KEYS); 2669 tncdata->elemContentsRewriten = 0; 2670 tncdata->status = 0; 2671 tncdata->idCheck = 1; 2672 Tcl_InitHashTable (tncdata->attDefsTables, TCL_STRING_KEYS); 2673 Tcl_InitHashTable (tncdata->entityDecls, TCL_STRING_KEYS); 2674 Tcl_InitHashTable (tncdata->notationDecls, TCL_STRING_KEYS); 2675 Tcl_InitHashTable (tncdata->ids, TCL_STRING_KEYS); 2676 tncdata->doctypeName = NULL; 2677 tncdata->ignoreWhiteCDATAs = 1; 2678 tncdata->ignorePCDATA = 0; 2679 tncdata->contentStackPtr = 0; 2680} 2681 2682/* 2683 *---------------------------------------------------------------------------- 2684 * 2685 * createTncData -- 2686 * 2687 * Helper proc. Allocates a TNC_Data structure and initializes it. 2688 * 2689 * Results: 2690 * None. 2691 * 2692 * Side effects: 2693 * Memory allocation and initialization. 2694 * 2695 *---------------------------------------------------------------------------- 2696 */ 2697 2698static TNC_Data * 2699createTncData ( 2700 Tcl_Interp *interp, 2701 Tcl_Obj *expatObj 2702 ) 2703{ 2704 TNC_Data *tncdata; 2705 2706 tncdata = (TNC_Data *) MALLOC (sizeof (TNC_Data)); 2707 tncdata->tagNames = (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable)); 2708 Tcl_InitHashTable (tncdata->tagNames, TCL_STRING_KEYS); 2709 tncdata->elemContentsRewriten = 0; 2710 tncdata->status = 0; 2711 tncdata->idCheck = 1; 2712 tncdata->attDefsTables = 2713 (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable)); 2714 Tcl_InitHashTable (tncdata->attDefsTables, TCL_STRING_KEYS); 2715 tncdata->entityDecls = 2716 (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable)); 2717 Tcl_InitHashTable (tncdata->entityDecls, TCL_STRING_KEYS); 2718 tncdata->notationDecls = 2719 (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable)); 2720 Tcl_InitHashTable (tncdata->notationDecls, TCL_STRING_KEYS); 2721 tncdata->ids = (Tcl_HashTable *) MALLOC (sizeof (Tcl_HashTable)); 2722 Tcl_InitHashTable (tncdata->ids, TCL_STRING_KEYS); 2723 tncdata->doctypeName = NULL; 2724 tncdata->interp = interp; 2725 tncdata->expatObj = expatObj; 2726 tncdata->ignoreWhiteCDATAs = 1; 2727 tncdata->ignorePCDATA = 0; 2728 tncdata->contentStack = (TNC_ContentStack *) 2729 MALLOC (sizeof (TNC_ContentStack) * TNC_INITCONTENTSTACKSIZE); 2730 tncdata->contentStackSize = TNC_INITCONTENTSTACKSIZE; 2731 tncdata->contentStackPtr = 0; 2732 2733 return tncdata; 2734} 2735 2736/* 2737 *---------------------------------------------------------------------------- 2738 * 2739 * TncFreeProc 2740 * 2741 * Called for C handler set specific cleanup in case of parser 2742 * delete. 2743 * 2744 * Results: 2745 * None. 2746 * 2747 * Side effects: 2748 * C handler set specific userData gets free'd. 2749 * 2750 *---------------------------------------------------------------------------- 2751 */ 2752 2753void 2754TncFreeProc (interp, userData) 2755 Tcl_Interp *interp; 2756 void *userData; 2757{ 2758 TNC_Data *tncdata = (TNC_Data *) userData; 2759 2760 FreeTncData (tncdata); 2761 FREE ((char *) tncdata->tagNames); 2762 FREE ((char *) tncdata->attDefsTables); 2763 FREE ((char *) tncdata->entityDecls); 2764 FREE ((char *) tncdata->notationDecls); 2765 FREE ((char *) tncdata->ids); 2766 FREE ((char *) tncdata->contentStack); 2767 FREE ((char *) tncdata); 2768} 2769 2770/* 2771 *---------------------------------------------------------------------------- 2772 * 2773 * tnc_ValidateObjDeleteCmd 2774 * 2775 * Called when a validateObjCmd is deleted. It's infact nothing 2776 * but a wrapper for TncFreeProc. 2777 * 2778 * Results: 2779 * None. 2780 * 2781 * Side effects: 2782 * The clientData structure will be freed, during cleanup routine calls. 2783 * 2784 *---------------------------------------------------------------------------- 2785 */ 2786 2787static void 2788tnc_ValidateObjDeleteCmd ( 2789 ClientData clientData 2790 ) 2791{ 2792 TNC_Data *tncdata = (TNC_Data*) clientData; 2793 2794 TncFreeProc (tncdata->interp, tncdata); 2795 2796} 2797 2798/* 2799 *---------------------------------------------------------------------------- 2800 * 2801 * TclTncObjCmd -- 2802 * 2803 * This procedure is invoked to process the "tnc" command. 2804 * 2805 * Results: 2806 * A standard Tcl result. 2807 * 2808 * Side effects: 2809 * The expat parser object provided as argument is enhanced by 2810 * by the "tnc" handler set. 2811 * 2812 *---------------------------------------------------------------------------- 2813 */ 2814 2815int 2816TclTncObjCmd(dummy, interp, objc, objv) 2817 ClientData dummy; 2818 Tcl_Interp *interp; 2819 int objc; 2820 Tcl_Obj *CONST objv[]; 2821{ 2822 char *method, *cmdName, s[20]; 2823 CHandlerSet *handlerSet; 2824 int methodIndex, result; 2825 TNC_Data *tncdata; 2826 2827 static CONST84 char *tncMethods[] = { 2828 "enable", "remove", "getValidateCmd", 2829 NULL 2830 }; 2831 enum tncMethod { 2832 m_enable, m_remove, m_getValidateCmd 2833 }; 2834 2835 if (!CheckExpatParserObj (interp, objv[1])) { 2836 SetResult ("First argument has to be a expat parser object"); 2837 return TCL_ERROR; 2838 } 2839 2840 method = Tcl_GetStringFromObj (objv[2], NULL); 2841 if (Tcl_GetIndexFromObj (interp, objv[2], tncMethods, "method", 0, 2842 &methodIndex) != TCL_OK) 2843 { 2844 return TCL_ERROR; 2845 } 2846 2847 switch ((enum tncMethod) methodIndex) { 2848 2849 case m_enable: 2850 if (objc != 3) { 2851 Tcl_WrongNumArgs (interp, 1, objv, tnc_usage); 2852 return TCL_ERROR; 2853 } 2854 handlerSet = CHandlerSetCreate ("tnc"); 2855 handlerSet->userData = createTncData (interp, objv[1]); 2856 handlerSet->ignoreWhiteCDATAs = 0; 2857 handlerSet->resetProc = TncResetProc; 2858 handlerSet->freeProc = TncFreeProc; 2859 handlerSet->elementDeclCommand = TncElementDeclCommand; 2860 handlerSet->attlistDeclCommand = TncAttDeclCommand; 2861 handlerSet->entityDeclCommand = TncEntityDeclHandler; 2862 handlerSet->notationcommand = TncNotationDeclHandler; 2863 handlerSet->elementstartcommand = TncElementStartCommand; 2864 handlerSet->elementendcommand = TncElementEndCommand; 2865 handlerSet->datacommand = TncCharacterdataCommand; 2866 handlerSet->startCdataSectionCommand = TncStartCdataSectionHandler; 2867 handlerSet->startDoctypeDeclCommand = TncStartDoctypeDeclHandler; 2868 handlerSet->endDoctypeDeclCommand = TncEndDoctypeDeclHandler; 2869 2870 result = CHandlerSetInstall (interp, objv[1], handlerSet); 2871 if (result != 0) { 2872 SetResult ("already have tnc C handler set"); 2873 TncFreeProc (interp, handlerSet->userData); 2874 FREE (handlerSet->name); 2875 FREE ((char *) handlerSet); 2876 return TCL_ERROR; 2877 } 2878 return TCL_OK; 2879 2880 case m_remove: 2881 if (objc != 3) { 2882 Tcl_WrongNumArgs (interp, 1, objv, tnc_usage); 2883 return TCL_ERROR; 2884 } 2885 result = CHandlerSetRemove (interp, objv[1], "tnc"); 2886 if (result == 1) { 2887 /* This should not happen if CheckExpatParserObj() is used. */ 2888 SetResult ("argument has to be a expat parser object"); 2889 return TCL_ERROR; 2890 } 2891 if (result == 2) { 2892 SetResult("expat parser obj hasn't a C handler set named \"tnc\""); 2893 return TCL_ERROR; 2894 } 2895 return TCL_OK; 2896 2897 case m_getValidateCmd: 2898 if (objc != 3 && objc != 4) { 2899 Tcl_WrongNumArgs (interp, 1, objv, tnc_usage); 2900 return TCL_ERROR; 2901 } 2902 handlerSet = CHandlerSetGet (interp, objv[1], "tnc"); 2903 if (!handlerSet) { 2904 SetResult("expat parser obj hasn't a C handler set named \"tnc\""); 2905 return TCL_ERROR; 2906 } 2907 tncdata = (TNC_Data *) handlerSet->userData; 2908 if (!tncdata->status) { 2909 SetResult ("No complete and error free DTD data available."); 2910 return TCL_ERROR; 2911 } 2912 /* After we finished, the validator structure is its own command, 2913 there isn't a parser cmd anymore. */ 2914 tncdata->expatObj = NULL; 2915 tncdata->status = 0; 2916 handlerSet->userData = createTncData (interp, objv[1]); 2917 if (objc == 4) { 2918 cmdName = Tcl_GetStringFromObj (objv[3], NULL); 2919 } else { 2920 FindUniqueCmdName (interp, s); 2921 cmdName = s; 2922 } 2923 Tcl_CreateObjCommand (interp, cmdName, tnc_ValidateObjCmd, tncdata, 2924 tnc_ValidateObjDeleteCmd); 2925 Tcl_SetResult (interp, cmdName, TCL_VOLATILE); 2926 return TCL_OK; 2927 2928 default: 2929 Tcl_SetResult (interp, "unknown method", NULL); 2930 return TCL_ERROR; 2931 } 2932 2933} 2934 2935#ifdef BUILD_tnc 2936# undef TCL_STORAGE_CLASS 2937# define TCL_STORAGE_CLASS DLLEXPORT 2938#endif 2939 2940 2941/* 2942 *---------------------------------------------------------------------------- 2943 * 2944 * Tnc_Init -- 2945 * 2946 * Initialization routine for loadable module 2947 * 2948 * Results: 2949 * None. 2950 * 2951 * Side effects: 2952 * Defines "tnc" enhancement command for expat parser obj 2953 * 2954 *---------------------------------------------------------------------------- 2955 */ 2956 2957#if defined(_MSC_VER) 2958# undef TCL_STORAGE_CLASS 2959# define TCL_STORAGE_CLASS DLLEXPORT 2960#endif 2961 2962EXTERN int 2963Tnc_Init (interp) 2964 Tcl_Interp *interp; 2965{ 2966#ifdef USE_TCL_STUBS 2967 if (Tcl_InitStubs(interp, "8", 0) == NULL) { 2968 return TCL_ERROR; 2969 } 2970#endif 2971#ifdef USE_TDOM_STUBS 2972 if (Tdom_InitStubs(interp, "0.8", 0) == NULL) { 2973 return TCL_ERROR; 2974 } 2975#endif 2976 Tcl_PkgRequire (interp, "tdom", "0.8.0", 0); 2977 Tcl_CreateObjCommand (interp, "tnc", TclTncObjCmd, NULL, NULL ); 2978 Tcl_PkgProvide (interp, "tnc", PACKAGE_VERSION); 2979 return TCL_OK; 2980} 2981 2982