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                            &copy[start]);
819#endif
820                    Tcl_CreateHashEntry (attDecl->lookupTable,
821                                                    &copy[start], &newPtr);
822                    entryPtr1 = Tcl_CreateHashEntry (tncdata->notationDecls,
823                                                    &copy[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                            &copy[start]);
839#endif
840                    Tcl_CreateHashEntry (attDecl->lookupTable,
841                                                    &copy[start], &newPtr);
842                    entryPtr1 = Tcl_CreateHashEntry (tncdata->notationDecls,
843                                                    &copy[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 (&copy[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                                                    &copy[start], &newPtr);
882                    FREE (copy);
883                    break;
884                }
885                if (copy[i] == '|') {
886                    copy[i] = '\0';
887                    Tcl_CreateHashEntry (attDecl->lookupTable,
888                                                    &copy[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 (&copy[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                                                         &copy[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 (&copy[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 (&copy[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                                              &copy[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                                              &copy[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