diff --git a/cifti/CMakeLists.txt b/cifti/CMakeLists.txt index ec8a4307..e13e7bdf 100644 --- a/cifti/CMakeLists.txt +++ b/cifti/CMakeLists.txt @@ -50,4 +50,13 @@ if(NIFTI_BUILD_TESTING AND NIFTI_BUILD_APPLICATIONS) -eval_cext -eval_type show_summary ) set_tests_properties( ${TEST_PREFIX}_tool_unterminated_cext PROPERTIES PASS_REGULAR_EXPRESSION "MapName : demo" ) + + # Nesting past AXML_MAX_DEPTH must stay inside the skip block rather than + # walk off the fixed stack; the regex pins that the rest still parses. + add_test( NAME ${TEST_PREFIX}_deep_nesting + COMMAND $ + -as_cext -eval_cext -eval_type show_summary + -input ${CMAKE_CURRENT_LIST_DIR}/testdata/deep_nesting.xml ) + set_tests_properties( ${TEST_PREFIX}_deep_nesting + PROPERTIES PASS_REGULAR_EXPRESSION "BrainModel" ) endif() diff --git a/cifti/afni_xml.c b/cifti/afni_xml.c index 45b72e52..1c9a5956 100644 --- a/cifti/afni_xml.c +++ b/cifti/afni_xml.c @@ -734,8 +734,9 @@ static int epush(afni_xml_control * xd, const char * ename, const char ** attr) if( xd->verb > 3 ) show_attrs(xd, attr, 1); } - /* determine whether we should go into a skip block */ - if( errs ) xd->dskip = xd->depth; + /* determine whether we should go into a skip block; keep the outermost + such depth, since that is the one whose pop ends the skip */ + if( errs && ! xd->dskip ) xd->dskip = xd->depth; /* if we are in a skip block, do nothing but monitor stack */ if( xd->dskip ) { @@ -767,16 +768,16 @@ static int epop(afni_xml_control * xd, const char * ename) if( xd->wkeep ) xd->wkeep = 0; /* clear storage continuation */ if( xd->dskip ) { - if( xd->dskip == xd->depth ) xd->dskip = 0; /* clear */ - if( xd->verb > 3 ) fprintf(stderr,"-- skip=%d, depth=%d, skipping pop element '%s'\n", xd->dskip, xd->depth, ename); + + /* clear only after the element has been skipped, so that the stack + is not touched at a depth that was never pushed onto it */ + if( xd->dskip == xd->depth ) xd->dskip = 0; } else { process_popped_element(xd, ename); - } - if( ! xd->dskip ) { xd->stack[xd->depth-1] = NULL; /* should be irrelevant */ if( xd->verb > 4 ) { @@ -793,7 +794,21 @@ static int epop(afni_xml_control * xd, const char * ename) static int process_popped_element(afni_xml_control * xd, const char * ename) { afni_xml_t * ax; + + /* a stack slot is filled by the matching epush(). An element that was + skipped never fills one, so do not assume this slot holds a struct + with a name. */ + if( xd->depth <= 0 || xd->depth > AXML_MAX_DEPTH ) { + if( gAXD.verb ) fprintf(stderr,"** pop at depth %d!\n", xd->depth); + return 1; + } + ax = xd->stack[xd->depth-1]; + if( ! ax || ! ax->name ) { + if( gAXD.verb ) fprintf(stderr,"** pop of unfilled element '%s'!\n", + ename ? ename : "NULL"); + return 1; + } if( strcmp(ename, ax->name) ) { if( gAXD.verb ) fprintf(stderr,"** pop mismatch!\n"); diff --git a/cifti/testdata/deep_nesting.xml b/cifti/testdata/deep_nesting.xml new file mode 100644 index 00000000..cf26ee24 --- /dev/null +++ b/cifti/testdata/deep_nesting.xml @@ -0,0 +1,8 @@ + + + + + + + +