tDOM

Check-in [4cefb1db19]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to [email protected]
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:More hardening and clarification of using a schema cmd inside one of the script arguments evaluated by itself.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | schema
Files: files | file ages | folders
SHA3-256: 4cefb1db197474ee496dd31d9fd761404b7123da026ccedac1a68b9f4acb474f
User & Date: rolf 2019-03-06 13:34:10
Context
2019-03-06
22:47
Further work on calling schema commands in schema definition / text constraint scripts. check-in: 2d5a4f85c8 user: rolf tags: schema
13:34
More hardening and clarification of using a schema cmd inside one of the script arguments evaluated by itself. check-in: 4cefb1db19 user: rolf tags: schema
2019-03-04
12:56
Removed ignored flag in Tcl_EvalObjv() calls. check-in: 7e974a5889 user: rolf tags: schema
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/schema.c.

164
165
166
167
168
169
170



















171
172
173
174
175
176
177
....
1896
1897
1898
1899
1900
1901
1902
1903
1904

1905
1906
1907
1908
1909
1910
1911
....
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
....
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
....
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
....
2167
2168
2169
2170
2171
2172
2173

2174
2175
2176
2177

2178
2179
2180
2181

2182
2183
2184
2185
2186
2187
2188
....
2251
2252
2253
2254
2255
2256
2257

2258
2259
2260
2261
2262
2263
2264
....
2336
2337
2338
2339
2340
2341
2342

2343
2344
2345
2346

2347
2348
2349
2350
2351
2352
2353
....
2361
2362
2363
2364
2365
2366
2367

2368
2369
2370
2371
2372
2373
2374
....
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
....
3840
3841
3842
3843
3844
3845
3846


3847
3848
3849
3850
3851
3852
3853
#define CHECK_TOPLEVEL                                                  \
    if (sdata->defineToplevel) {                                        \
        SetResult("Command not allowed at top level "                   \
                  "in schema define evaluation");                       \
        return TCL_ERROR;                                               \
    }




















#define REMEMBER_PATTERN(pattern)                                       \
    if (sdata->numPatternList == sdata->patternListSize) {              \
        sdata->patternList = (SchemaCP **) REALLOC (                    \
            sdata->patternList,                                         \
            sizeof (SchemaCP*) * sdata->patternListSize * 2);           \
        sdata->patternListSize *= 2;                                    \
    }                                                                   \
................................................................................
    sdata->cp = cp;
    sdata->contentSize = CONTENT_ARRAY_SIZE_INIT;
    sdata->isTextConstraint = 1;
    sdata->textStub[3] = script;
    sdata->currentEvals++;
    result = Tcl_EvalObjv (interp, 4, sdata->textStub, TCL_EVAL_GLOBAL);
    sdata->currentEvals--;
    sdata->isTextConstraint = savedIsTextConstraint;
    /* ... and restore the previously saved sdata states  */

    sdata->cp = savedCP;
    sdata->contentSize = savedContenSize;
    if (sdata->cp && !sdata->isAttributeConstaint && cp->nc) {
        sdata->cp->flags |= CONSTRAINT_TEXT_CHILD;
    }
    return result;
}
................................................................................
        "nrForwardDefinitions",      "state",   "reset", "define",
        "validate",   "domvalidate", "deftext", "info",  "reportcmd",
        NULL
    };
    enum schemaInstanceMethod {
        m_defelement,  m_defpattern,  m_start,   m_event, m_delete,
        m_nrForwardDefinitions,       m_state,   m_reset, m_define,
        m_validate,    m_domvalidate, m_deftext, m_info, m_reportcmd
    };

    static const char *eventKeywords[] = {
        "start", "end", "text", NULL
    };

    enum eventKeyword
................................................................................

    if (objc < 2) {
        Tcl_WrongNumArgs (interp, 1, objv, "subcommand ?arguments?");
        return TCL_ERROR;
    }

    if (sdata == NULL) {
        /* Inline defined defelement, defpattern or start */
        sdata = GETASI;
        CHECK_SI;
        if (!sdata->defineToplevel) {
            SetResult ("Command not allowed in nested schema define script");
            return TCL_ERROR;
        }
        i = 1;
    }

    if (Tcl_GetIndexFromObj (interp, objv[1-i], schemaInstanceMethods,
                             "method", 0, &methodIndex)
        != TCL_OK) {
        return TCL_ERROR;
................................................................................
                current = (SchemaCP *) Tcl_GetHashValue (h);
                pattern->next = current;
            }
            REMEMBER_PATTERN (pattern);
            Tcl_SetHashValue (h, pattern);
        }

        if (!sdata->defineToplevel) {
            savedsdata = GETASI;
            if (savedsdata == sdata) {
                SetResult ("Recursive call of schema command is not allowed");
                return TCL_ERROR;
            }
            SETASI(sdata);
        }
        savedDefineToplevel = sdata->defineToplevel;
        savedNamespacePtr = sdata->currentNamespace;
        sdata->defineToplevel = 0;
        sdata->currentNamespace = namespacePtr;
        sdata->cp = pattern;
        sdata->numAttr = 0;
        sdata->numReqAttr = 0;
................................................................................
        sdata->currentNamespace = savedNamespacePtr;
        if (!savedDefineToplevel) {
            SETASI(savedsdata);
        }
        break;

    case m_define:

        if (objc != 3) {
            Tcl_WrongNumArgs (interp, 2, objv, "<definition commands>");
            return TCL_ERROR;
        }

        savedsdata = GETASI;
        if (savedsdata == sdata) {
            SetResult ("Recursive call of schema command is not allowed");
            return TCL_ERROR;

        }
        SETASI(sdata);
        savedNumPatternList = sdata->numPatternList;
        sdata->currentNamespace = 0;
        sdata->cp = NULL;
        sdata->contentSize = 0;
        sdata->defineToplevel = 1;
................................................................................
            }
            sdata->startNamespace =
                tdomstrdup (Tcl_GetString (objv[3-i]));
        }
        break;

    case m_event:

        if (objc < 3) {
            Tcl_WrongNumArgs (interp, 2, objv, "<eventType>"
                              " ?<type specific data>?");
            return TCL_ERROR;
        }
        if (Tcl_GetIndexFromObj (interp, objv[2], eventKeywords,
                                 "keyword", 0, &keywordIndex)
................................................................................
        default:
            SetResult ("Internal error: Invalid validation state");
            return TCL_ERROR;
        }
        break;

    case m_reset:

        schemaReset (sdata);
        break;

    case m_validate:

        if (objc < 3 || objc > 4) {
            Tcl_WrongNumArgs (interp, 2, objv, "<xml> ?resultVarName?");
            return TCL_ERROR;
        }
        xmlstr = Tcl_GetStringFromObj (objv[2], &len);
        if (validateString (interp, sdata, xmlstr, len) == TCL_OK) {
            SetBooleanResult (1);
................................................................................
            }
            SetBooleanResult (0);
        }
        schemaReset (sdata);
        break;

    case m_domvalidate:

        if (objc < 3 || objc > 4) {
            Tcl_WrongNumArgs (interp, 2, objv, "<xml> ?resultVarName?");
            return TCL_ERROR;
        }
        doc = tcldom_getDocumentFromName (interp, Tcl_GetString (objv[2]),
                                          &errMsg);
        if (doc) {
................................................................................
        }
        pattern = initSchemaCP (
            SCHEMA_CTYPE_NAME,
            sdata->currentNamespace,
            Tcl_GetHashKey (hashTable, entryPtr)
            );
        pattern->flags |= LOCAL_DEFINED_ELEMENT;
        evalDefinition (interp, sdata, objv[3], pattern, quant, n, m);
    }
    return TCL_OK;
}

/* Implements the schema definition commands "choice", "group",
 * "interleave" and "mixed" */
static int
................................................................................
    Tcl_Interp *interp
    )
{
    /* Inline definition commands. */
    Tcl_CreateObjCommand (interp, "tdom::schema::defelement",
                          schemaInstanceCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::defpattern",


                          schemaInstanceCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::start",
                          schemaInstanceCmd, NULL, NULL);

    /* The "any" definition command. */
    Tcl_CreateObjCommand (interp, "tdom::schema::any",
                          AnyPatternObjCmd, NULL, NULL);






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<

>







 







|







 







|


|
<
<
<







 







<
<
<
<
<
<
|
<







 







>




>
|
|
|
|
>







 







>







 







>




>







 







>







 







|







 







>
>







164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
....
1915
1916
1917
1918
1919
1920
1921

1922
1923
1924
1925
1926
1927
1928
1929
1930
....
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
....
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074



2075
2076
2077
2078
2079
2080
2081
....
2139
2140
2141
2142
2143
2144
2145






2146

2147
2148
2149
2150
2151
2152
2153
....
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
....
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
....
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
....
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
....
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
....
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
#define CHECK_TOPLEVEL                                                  \
    if (sdata->defineToplevel) {                                        \
        SetResult("Command not allowed at top level "                   \
                  "in schema define evaluation");                       \
        return TCL_ERROR;                                               \
    }

#define CHECK_TOPLEVEL_CMD                                              \
    if (!sdata->defineToplevel && sdata->currentEvals > 1) {            \
        SetResult ("Command not allowed in nested schema define script"); \
        return TCL_ERROR;                                               \
    }                                                                   \
    if (clientData != NULL) {                                           \
        savedsdata = GETASI;                                            \
        if (savedsdata == sdata) {                                      \
            SetResult ("This recursive call is not allowed"); \
            return TCL_ERROR;                                           \
        }                                                               \
    }
      
#define CHECK_EVAL                                                      \
    if (sdata->currentEvals) {                                          \
        SetResult ("Method not allowed in nested schema define script"); \
        return TCL_ERROR;                                               \
    }
    
#define REMEMBER_PATTERN(pattern)                                       \
    if (sdata->numPatternList == sdata->patternListSize) {              \
        sdata->patternList = (SchemaCP **) REALLOC (                    \
            sdata->patternList,                                         \
            sizeof (SchemaCP*) * sdata->patternListSize * 2);           \
        sdata->patternListSize *= 2;                                    \
    }                                                                   \
................................................................................
    sdata->cp = cp;
    sdata->contentSize = CONTENT_ARRAY_SIZE_INIT;
    sdata->isTextConstraint = 1;
    sdata->textStub[3] = script;
    sdata->currentEvals++;
    result = Tcl_EvalObjv (interp, 4, sdata->textStub, TCL_EVAL_GLOBAL);
    sdata->currentEvals--;

    /* ... and restore the previously saved sdata states  */
    sdata->isTextConstraint = savedIsTextConstraint;
    sdata->cp = savedCP;
    sdata->contentSize = savedContenSize;
    if (sdata->cp && !sdata->isAttributeConstaint && cp->nc) {
        sdata->cp->flags |= CONSTRAINT_TEXT_CHILD;
    }
    return result;
}
................................................................................
        "nrForwardDefinitions",      "state",   "reset", "define",
        "validate",   "domvalidate", "deftext", "info",  "reportcmd",
        NULL
    };
    enum schemaInstanceMethod {
        m_defelement,  m_defpattern,  m_start,   m_event, m_delete,
        m_nrForwardDefinitions,       m_state,   m_reset, m_define,
        m_validate,    m_domvalidate, m_deftext, m_info,  m_reportcmd
    };

    static const char *eventKeywords[] = {
        "start", "end", "text", NULL
    };

    enum eventKeyword
................................................................................

    if (objc < 2) {
        Tcl_WrongNumArgs (interp, 1, objv, "subcommand ?arguments?");
        return TCL_ERROR;
    }

    if (sdata == NULL) {
        /* Inline defined defelement, defpattern, deftext or start */
        sdata = GETASI;
        CHECK_SI;
        CHECK_TOPLEVEL_CMD



        i = 1;
    }

    if (Tcl_GetIndexFromObj (interp, objv[1-i], schemaInstanceMethods,
                             "method", 0, &methodIndex)
        != TCL_OK) {
        return TCL_ERROR;
................................................................................
                current = (SchemaCP *) Tcl_GetHashValue (h);
                pattern->next = current;
            }
            REMEMBER_PATTERN (pattern);
            Tcl_SetHashValue (h, pattern);
        }







        SETASI(sdata);

        savedDefineToplevel = sdata->defineToplevel;
        savedNamespacePtr = sdata->currentNamespace;
        sdata->defineToplevel = 0;
        sdata->currentNamespace = namespacePtr;
        sdata->cp = pattern;
        sdata->numAttr = 0;
        sdata->numReqAttr = 0;
................................................................................
        sdata->currentNamespace = savedNamespacePtr;
        if (!savedDefineToplevel) {
            SETASI(savedsdata);
        }
        break;

    case m_define:
        CHECK_EVAL
        if (objc != 3) {
            Tcl_WrongNumArgs (interp, 2, objv, "<definition commands>");
            return TCL_ERROR;
        }
        if (clientData) {
            savedsdata = GETASI;
            if (savedsdata == sdata) {
                SetResult ("Recursive call of schema command is not allowed");
                return TCL_ERROR;
            }
        }
        SETASI(sdata);
        savedNumPatternList = sdata->numPatternList;
        sdata->currentNamespace = 0;
        sdata->cp = NULL;
        sdata->contentSize = 0;
        sdata->defineToplevel = 1;
................................................................................
            }
            sdata->startNamespace =
                tdomstrdup (Tcl_GetString (objv[3-i]));
        }
        break;

    case m_event:
        CHECK_EVAL
        if (objc < 3) {
            Tcl_WrongNumArgs (interp, 2, objv, "<eventType>"
                              " ?<type specific data>?");
            return TCL_ERROR;
        }
        if (Tcl_GetIndexFromObj (interp, objv[2], eventKeywords,
                                 "keyword", 0, &keywordIndex)
................................................................................
        default:
            SetResult ("Internal error: Invalid validation state");
            return TCL_ERROR;
        }
        break;

    case m_reset:
        CHECK_EVAL
        schemaReset (sdata);
        break;

    case m_validate:
        CHECK_EVAL
        if (objc < 3 || objc > 4) {
            Tcl_WrongNumArgs (interp, 2, objv, "<xml> ?resultVarName?");
            return TCL_ERROR;
        }
        xmlstr = Tcl_GetStringFromObj (objv[2], &len);
        if (validateString (interp, sdata, xmlstr, len) == TCL_OK) {
            SetBooleanResult (1);
................................................................................
            }
            SetBooleanResult (0);
        }
        schemaReset (sdata);
        break;

    case m_domvalidate:
        CHECK_EVAL
        if (objc < 3 || objc > 4) {
            Tcl_WrongNumArgs (interp, 2, objv, "<xml> ?resultVarName?");
            return TCL_ERROR;
        }
        doc = tcldom_getDocumentFromName (interp, Tcl_GetString (objv[2]),
                                          &errMsg);
        if (doc) {
................................................................................
        }
        pattern = initSchemaCP (
            SCHEMA_CTYPE_NAME,
            sdata->currentNamespace,
            Tcl_GetHashKey (hashTable, entryPtr)
            );
        pattern->flags |= LOCAL_DEFINED_ELEMENT;
        return evalDefinition (interp, sdata, objv[3], pattern, quant, n, m);
    }
    return TCL_OK;
}

/* Implements the schema definition commands "choice", "group",
 * "interleave" and "mixed" */
static int
................................................................................
    Tcl_Interp *interp
    )
{
    /* Inline definition commands. */
    Tcl_CreateObjCommand (interp, "tdom::schema::defelement",
                          schemaInstanceCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::defpattern",
                          schemaInstanceCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::deftext",
                          schemaInstanceCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::start",
                          schemaInstanceCmd, NULL, NULL);

    /* The "any" definition command. */
    Tcl_CreateObjCommand (interp, "tdom::schema::any",
                          AnyPatternObjCmd, NULL, NULL);

Changes to tests/schema.test.

296
297
298
299
300
301
302



















303


304






305

306























307




308





























309
310
311
312
313
314
315
...
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
...
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
...
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
...
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
test schema-1.18 {delete schema cmd in definition script} {
    tdom::schema create s
    s define {
        defelement e {
            s delete
            element e1 1 {



















                s delete


            }






        }

    }























    info commands s




} {}






























test schema-2.1 {grammar definition: ref} {
    tdom::schema create grammar
    grammar defpattern thisPattern {
        element a
        element b
    }
................................................................................
            group 2 {
                element e1
                element e2
            }
            element e2 *
        }
        foreach e {e1 e2} {
            grammar defelement $e {}
        }
    }
    dom parse -validateCmd grammar {
        <doc><e1/><e1/><e2/><e1/><e2/><e2/><e2/><e2/></doc>
    } doc
    $doc delete
    grammar delete
................................................................................
                    element e2
                }
                element e2
            }
            element e2 *
        }
        foreach e {e1 e2} {
            grammar defelement $e {}
        }
    }
    dom parse -validateCmd grammar {
        <doc><e1/><e1><e2/></e1><e2/><e1><e2/></e1><e2/><e2/><e2/><e2/></doc>
    } doc
    $doc delete
    grammar delete
................................................................................
                element e1 1 {
                    element e2
                }
                element e2
            }
        }
        foreach e {e1 e2} {
            grammar defelement $e {}
        }
    }
    dom parse -validateCmd grammar {
        <doc><e1/><e1><e2/></e1><e2/></doc>
    } doc
    $doc delete
    grammar delete
................................................................................
                element e1 1 {
                    element e2
                }
                element e2
            }
        }
        foreach e {e1 e2} {
            grammar defelement $e {}
        }
    }
    ::xml::parser p -validateCmd grammar
    p parse {
        <doc><e1/><e1><e2/></e1><e2/></doc>
    }
    p delete






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
|
>
>
>
>
>
>
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







|







 







|







 







|







296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
...
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
...
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
...
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
...
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
test schema-1.18 {delete schema cmd in definition script} {
    tdom::schema create s
    s define {
        defelement e {
            s delete
            element e1 1 {
                set ::result [catch {s delete}]
            }
        }
    }
    lappend result [info commands s]
} {1 {}}

test schema-1.19 {call top level schema cmd in definition script} {
    tdom::schema create s
    set result [catch {s define {
        defelement e {
            element e1 1 {
                deftext foo {minLength 1}
                defelement bar {}
            }
            element bar
        }
    }}]
    lappend result [info commands s]
    s delete
    set result
} {1 s}

test schema-1.20 {call top level schema cmd in definition script} {
    tdom::schema create s
    set result [catch {s defelement e {
            element e1 1 {
                deftext foo {minLength 1}
                defelement bar {}
            }
            element bar
        }
    }]
    lappend result [info commands s]
    s delete
    set result
} {1 s}

test schema-1.21 {Create other schema cmd in definition script} {
    tdom::schema create s1
    s1 defelement e {
        element e1 1 {}
        element e1 1 {
            ::tdom::schema create ::s2
            ::s2 define {
                defelement s2 {
                    element s2e
                    element s2ee
                }
                foreach e {s2e s2ee} {
                    defelement $e {text}
                }
            }
        }
    }
    set result [info commands s1]
    lappend result [info commands s2]
    foreach xml {
        <e><e1/><e1/></e>
        <s2><s2e>foo</s2e><s2ee/></s2>
    } {
        lappend result [s1 validate $xml]
        lappend result [s2 validate $xml]
    }
    s2 delete
    s1 delete
    set result
} {s1 s2 1 0 0 1}

test schema-1.22 {Call not toplevel cmd methods in definition script} {
    tdom::schema create s
    set result [list]
    s define {
        defelement e {
            lappend ::result [s nrForwardDefinitions]
            element e1
            lappend ::result [s nrForwardDefinitions]
            element e1
            lappend ::result [s nrForwardDefinitions]
            element e2
            lappend ::result [s nrForwardDefinitions]
        }
        foreach e {e1 e2} {
            defelement $e {text}
            lappend ::result [s nrForwardDefinitions]
        }
    }
    s delete
    set result
} {0 1 1 2 1 0}

test schema-2.1 {grammar definition: ref} {
    tdom::schema create grammar
    grammar defpattern thisPattern {
        element a
        element b
    }
................................................................................
            group 2 {
                element e1
                element e2
            }
            element e2 *
        }
        foreach e {e1 e2} {
            defelement $e {}
        }
    }
    dom parse -validateCmd grammar {
        <doc><e1/><e1/><e2/><e1/><e2/><e2/><e2/><e2/></doc>
    } doc
    $doc delete
    grammar delete
................................................................................
                    element e2
                }
                element e2
            }
            element e2 *
        }
        foreach e {e1 e2} {
            defelement $e {}
        }
    }
    dom parse -validateCmd grammar {
        <doc><e1/><e1><e2/></e1><e2/><e1><e2/></e1><e2/><e2/><e2/><e2/></doc>
    } doc
    $doc delete
    grammar delete
................................................................................
                element e1 1 {
                    element e2
                }
                element e2
            }
        }
        foreach e {e1 e2} {
            defelement $e {}
        }
    }
    dom parse -validateCmd grammar {
        <doc><e1/><e1><e2/></e1><e2/></doc>
    } doc
    $doc delete
    grammar delete
................................................................................
                element e1 1 {
                    element e2
                }
                element e2
            }
        }
        foreach e {e1 e2} {
            defelement $e {}
        }
    }
    ::xml::parser p -validateCmd grammar
    p parse {
        <doc><e1/><e1><e2/></e1><e2/></doc>
    }
    p delete