740 lines
25 KiB
C
740 lines
25 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/* $XConsortium: struct.c /main/3 1995/11/08 10:59:57 rswiston $ */
|
|
/*
|
|
Copyright 1986 Tandem Computers Incorporated.
|
|
This product and information is proprietary of Tandem Computers Incorporated.
|
|
Copyright 1986, 1987, 1988, 1989 Hewlett-Packard Co.
|
|
*/
|
|
|
|
/* Struct.c contains functions relevant to parsing document structure for
|
|
program PARSER */
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "basic.h"
|
|
#include "trie.h"
|
|
#include "dtdext.h"
|
|
#include "delim.h"
|
|
#include "context.h"
|
|
#include "parser.h"
|
|
#include "entext.h"
|
|
#include "if.h"
|
|
|
|
/* M_checkstart tests to see if the element (or #PCDATA) indicated by VAL
|
|
is valid input. It returns TRUE, FALSE, or M_NONCONTEXTUAL respectively
|
|
if the element is allowed by content, not allowed, or allowed by an
|
|
inclusion exception. */
|
|
int m_checkstart(M_ELEMENT val)
|
|
{
|
|
M_PARSE *stackptr ;
|
|
int except ;
|
|
|
|
/* Check for applicable exclusions */
|
|
if (m_excluded(val)) return(FALSE) ;
|
|
|
|
/* Check for declared content in element currently at top of stack */
|
|
if (m_stacktop->oldtop) {
|
|
if (m_element[m_stacktop->element - 1].content == M_ANY) return(TRUE) ;
|
|
if (m_element[m_stacktop->element - 1].content == M_CDATA ||
|
|
m_element[m_stacktop->element - 1].content == M_RCDATA)
|
|
if (! val) return(TRUE) ;
|
|
else return(FALSE) ;
|
|
}
|
|
|
|
/* Check content model */
|
|
if (m_transition(val, TRUE)) return(TRUE) ;
|
|
|
|
/* Check for inclusions */
|
|
for (stackptr = m_stacktop ;
|
|
stackptr->oldtop ;
|
|
stackptr = stackptr->oldtop)
|
|
for (except = m_element[stackptr->element - 1].inptr ;
|
|
except ;
|
|
except = m_exception[except - 1].next)
|
|
if (m_exception[except - 1].element == val) return(M_NONCONTEXTUAL) ;
|
|
|
|
/* Nothing left to try, val is not allowed */
|
|
return(FALSE) ;
|
|
}
|
|
|
|
/* M_ckend verifies that element VAL can be legally ended at the
|
|
current state of the parse, by an end tag or NET as indicated by NEEDNET.
|
|
If VAL is not the element at the top of the parse stack, m_ckend
|
|
checks to see if the end of VAL can validly end nested
|
|
elements as well. */
|
|
LOGICAL m_ckend(M_ELEMENT val, LOGICAL neednet)
|
|
{
|
|
M_PARSE *stackptr ;
|
|
M_OPENFSA *fsastack ;
|
|
M_ANDLIST *usedand ;
|
|
M_ANDGROUP pand ;
|
|
M_MIN *minend ;
|
|
M_MIN *discard ;
|
|
|
|
m_minend = NULL ;
|
|
m_nextme = &m_minend ;
|
|
/* Go down the stack, checking that each element can end until
|
|
element val occurs */
|
|
for (stackptr = m_stacktop ; stackptr ; stackptr = stackptr->oldtop) {
|
|
/* If the element at stackptr has a content model, make sure each
|
|
open fsa is in a final state and that all required submodels of
|
|
open and-groups have occurred */
|
|
for (fsastack = stackptr->fsastack ;
|
|
fsastack ;
|
|
fsastack = fsastack->oldtop) {
|
|
if (! m_state[fsastack->current - 1].final) {
|
|
m_freemin(m_minend, "end-tag minimization") ;
|
|
return(FALSE) ;
|
|
}
|
|
for (pand = fsastack->andgroup ;
|
|
pand ;
|
|
pand = m_andgroup[pand - 1].next) {
|
|
/* Don't bother checking if optional submodel of an and-group
|
|
has occurred */
|
|
if (m_state[m_andgroup[pand - 1].start - 1].final) continue ;
|
|
for (usedand = fsastack->usedand ;
|
|
usedand ;
|
|
usedand = usedand->next)
|
|
if (usedand->group == pand) break ;
|
|
if (! usedand) {
|
|
/* Didn't find a required submodel */
|
|
m_freemin(m_minend, "end-tag minimization") ;
|
|
return(FALSE) ;
|
|
}
|
|
} /* End for pand */
|
|
} /* End for fsastack */
|
|
/* Have confirmed that the element indicated by stackptr can end now */
|
|
if (stackptr->element == val) break ;
|
|
*m_nextme = (M_MIN *) m_malloc(sizeof(M_MIN), "end-tag minimization") ;
|
|
(*m_nextme)->next = NULL ;
|
|
(*m_nextme)->val = stackptr->element ;
|
|
m_nextme = &(*m_nextme)->next ;
|
|
} /* End for stackptr */
|
|
if (! stackptr) {
|
|
m_freemin(m_minend, "end-tag minimization") ;
|
|
return(FALSE) ;
|
|
}
|
|
for (minend = m_minend ; minend ; ) {
|
|
discard = minend ;
|
|
minend = minend->next ;
|
|
m_free(discard, "end-tag minimization") ;
|
|
if (m_stacktop->neednet && ! neednet) {
|
|
M_WCHAR *wc_net;
|
|
|
|
wc_net = MakeWideCharString(m_net);
|
|
m_err2("Expecting %s to end %s",
|
|
wc_net,
|
|
m_nameofelt(m_stacktop->element)) ;
|
|
m_free(wc_net,"wide character string");
|
|
m_showcurelt() ;
|
|
}
|
|
if (! m_element[m_stacktop->element - 1].emin) {
|
|
m_err1("Missing end tag for %s", m_nameofelt(m_stacktop->element)) ;
|
|
m_showcurelt() ;
|
|
}
|
|
m_endtag(m_stacktop->element) ;
|
|
}
|
|
if (m_stacktop->neednet != neednet) {
|
|
M_WCHAR *wc_etago, *wc_tagc, *wc_mnet, *wc_stago, *wc_net;
|
|
|
|
wc_etago = MakeWideCharString(m_etago);
|
|
wc_stago = MakeWideCharString(m_stago);
|
|
wc_tagc = MakeWideCharString(m_tagc);
|
|
wc_net = MakeWideCharString(m_net);
|
|
if (neednet)
|
|
m_err4("Expecting %s%s%s instead of %s",
|
|
wc_etago,
|
|
m_nameofelt(m_stacktop->element),
|
|
wc_tagc,
|
|
wc_net) ;
|
|
else
|
|
m_err4("Expecting %s to end %s%s%s",
|
|
wc_net,
|
|
wc_stago,
|
|
m_nameofelt(m_stacktop->element),
|
|
wc_tagc) ;
|
|
m_showcurelt() ;
|
|
m_free(wc_etago,"wide character string");
|
|
m_free(wc_stago,"wide character string");
|
|
m_free(wc_tagc,"wide character string");
|
|
m_free(wc_net,"wide character string");
|
|
}
|
|
m_endtag(val) ;
|
|
return(TRUE) ;
|
|
} /*end m_ckend*/
|
|
|
|
/* Make a copy of the stack entry at the top of the parse stack in a scratch
|
|
area */
|
|
M_PARSE *m_copystackelt(void)
|
|
{
|
|
M_OPENFSA *oldfsa ;
|
|
M_OPENFSA **newfsa ;
|
|
M_ANDLIST *oldand ;
|
|
M_ANDLIST **newand ;
|
|
M_PARSE *copy ;
|
|
|
|
copy = (M_PARSE *) m_malloc(sizeof(M_PARSE), "stack element") ;
|
|
memcpy((char *) copy, (char *) m_stacktop, sizeof(M_PARSE)) ;
|
|
copy->param = NULL ;
|
|
for (oldfsa = m_stacktop->fsastack, newfsa = ©->fsastack ;
|
|
oldfsa ;
|
|
oldfsa = oldfsa->oldtop, newfsa = &(*newfsa)->oldtop) {
|
|
*newfsa = (M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
|
|
memcpy((char *) *newfsa, (char *) oldfsa, sizeof(M_OPENFSA)) ;
|
|
for (oldand = oldfsa->usedand, newand = &(*newfsa)->usedand ;
|
|
oldand ;
|
|
oldand = oldand->next, newand = &(*newand)->next) {
|
|
*newand = (M_ANDLIST *) m_malloc(sizeof(M_ANDLIST), "and list") ;
|
|
(*newand)->group = oldand->group ;
|
|
(*newand)->next = NULL ;
|
|
}
|
|
}
|
|
return(copy) ;
|
|
}
|
|
|
|
/* End of document */
|
|
void m_done(void)
|
|
{
|
|
M_ELEMENT lastelt ;
|
|
|
|
while (m_stacktop->oldtop) {
|
|
lastelt = m_stacktop->element ;
|
|
if (! m_ckend(m_stacktop->element, FALSE)) {
|
|
m_err1("More content expected in element %s at end of document",
|
|
m_nameofelt(m_stacktop->element)) ;
|
|
m_expecting() ;
|
|
m_showcurelt() ;
|
|
m_frcend(m_stacktop->element) ;
|
|
}
|
|
else if (! m_element[lastelt - 1].emin)
|
|
m_err1("Missing end tag for %s", m_nameofelt(lastelt)) ;
|
|
}
|
|
m_endcase(1) ;
|
|
m_globes() ;
|
|
m_exit(m_errexit) ;
|
|
}
|
|
|
|
/* Process the endtag (implied, abbreviated, or explicit) for element C*/
|
|
void m_endtag(M_ELEMENT c)
|
|
{
|
|
m_endaction(c) ;
|
|
m_pop() ;
|
|
if (m_stacktop->intext) {
|
|
m_curcon = POUNDCDATA ;
|
|
if (m_netlevel) m_curcon = NETCDATA ;
|
|
}
|
|
}
|
|
|
|
/* Check that the identified element is not prohibited in the current context
|
|
by an exclusion exception */
|
|
LOGICAL m_excluded(M_ELEMENT elt)
|
|
{
|
|
M_PARSE *stackptr ;
|
|
int except ;
|
|
|
|
if (! elt) return(FALSE) ;
|
|
for (stackptr = m_stacktop ;
|
|
stackptr->oldtop ;
|
|
stackptr = stackptr->oldtop)
|
|
for (except = m_element[stackptr->element - 1].exptr ;
|
|
except ;
|
|
except = m_exception[except - 1].next)
|
|
if (m_exception[except - 1].element == elt) return(TRUE) ;
|
|
return(FALSE) ;
|
|
}
|
|
|
|
/* Free the OPEN FSA substructures associated with an element on
|
|
the parse stack */
|
|
void m_freeFSA(M_PARSE *stackelt)
|
|
{
|
|
M_OPENFSA *fsastack ;
|
|
M_ANDLIST *usedand ;
|
|
|
|
while (stackelt->fsastack) {
|
|
fsastack = stackelt->fsastack ;
|
|
if (fsastack == &m_botfsa) return ;
|
|
while (fsastack->usedand) {
|
|
usedand = fsastack->usedand ;
|
|
fsastack->usedand = usedand->next ;
|
|
m_free(usedand, "and list") ;
|
|
}
|
|
stackelt->fsastack = fsastack->oldtop ;
|
|
m_free(fsastack, "FSA") ;
|
|
}
|
|
}
|
|
|
|
/* Free storage used for tentative chain of tag minimizations */
|
|
void m_freemin(M_MIN *min, char *msg)
|
|
{
|
|
M_MIN *discard ;
|
|
|
|
for ( ; min ;) {
|
|
discard = min ;
|
|
min = min->next ;
|
|
m_free(discard, msg) ;
|
|
}
|
|
}
|
|
|
|
/* M_nextand returns TRUE iff element label is allowed at the current point
|
|
in the current content model by starting a new submodel of the and-group
|
|
indicated by fsastack, or (if the and-group is within a seq-group) by
|
|
continuing past the and-group */
|
|
LOGICAL m_nextand(M_OPENFSA *thisfsa, M_ELEMENT label)
|
|
{
|
|
M_ANDLIST *newgroup ;
|
|
M_ANDGROUP pand ;
|
|
M_ANDLIST *plist ;
|
|
M_OPENFSA *savefsa ;
|
|
M_OPENFSA *fsa ;
|
|
LOGICAL required = FALSE ;
|
|
LOGICAL last ;
|
|
|
|
/* Verify currently within an and-group and in final state of this
|
|
branch */
|
|
if (! m_state[thisfsa->current - 1].final) return(FALSE) ;
|
|
if (! thisfsa->oldtop) return(FALSE) ;
|
|
savefsa = m_stacktop->fsastack ;
|
|
|
|
/* Check possibility of starting a new branch*/
|
|
m_stacktop->fsastack =
|
|
(M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
|
|
m_stacktop->fsastack->oldtop = thisfsa->oldtop ;
|
|
m_stacktop->fsastack->andgroup = M_NULLVAL ;
|
|
m_stacktop->fsastack->usedand = NULL ;
|
|
newgroup = (M_ANDLIST *) m_malloc(sizeof(M_ANDLIST), "and list") ;
|
|
newgroup->next = thisfsa->oldtop->usedand ;
|
|
thisfsa->oldtop->usedand = newgroup ;
|
|
for (pand = thisfsa->oldtop->andgroup ;
|
|
pand ;
|
|
pand = m_andgroup[pand - 1].next) {
|
|
for (plist = newgroup->next ; plist ; plist = plist->next)
|
|
if (pand == plist->group) break ;
|
|
if (! plist) {
|
|
newgroup->group = pand ;
|
|
m_stacktop->fsastack->current = m_andgroup[pand - 1].start ;
|
|
if (! m_state[m_stacktop->fsastack->current - 1].final)
|
|
required = TRUE ;
|
|
if (m_transition(label, FALSE)) {
|
|
for (fsa = savefsa ; TRUE ; fsa = fsa->oldtop) {
|
|
for (plist = fsa->usedand ; plist ; plist = plist->next)
|
|
m_free(plist, "and list") ;
|
|
if (fsa == thisfsa) {
|
|
m_free(fsa, "FSA") ;
|
|
break;
|
|
}
|
|
m_free(fsa, "FSA") ;
|
|
}
|
|
return(TRUE) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Couldn't start a new branch. Restore parse stack */
|
|
thisfsa->oldtop->usedand = newgroup->next ;
|
|
m_free(newgroup, "and list") ;
|
|
m_free(m_stacktop->fsastack, "FSA") ;
|
|
m_stacktop->fsastack = savefsa ;
|
|
|
|
/* Have all required branches occurred? */
|
|
if (required) return(FALSE) ;
|
|
|
|
/* Can we continue past this and-group in a containing seq-group? */
|
|
m_stacktop->fsastack =
|
|
(M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
|
|
m_stacktop->fsastack->oldtop = thisfsa->oldtop->oldtop ;
|
|
m_stacktop->fsastack->andgroup = M_NULLVAL ;
|
|
m_stacktop->fsastack->usedand = NULL ;
|
|
m_stacktop->fsastack->current = thisfsa->oldtop->current ;
|
|
if (m_transition(label, FALSE)) {
|
|
/* Free temporary FSA storage used to test transition */
|
|
for (fsa = savefsa, last = FALSE ; TRUE ; fsa = fsa->oldtop) {
|
|
for (plist = fsa->usedand ; plist ; plist = plist->next)
|
|
m_free(plist, "and list") ;
|
|
m_free(fsa, "FSA") ;
|
|
if (last) break ;
|
|
if (fsa == thisfsa) last = TRUE ;
|
|
}
|
|
return(TRUE) ;
|
|
}
|
|
m_free(m_stacktop->fsastack, "FSA") ;
|
|
m_stacktop->fsastack = savefsa ;
|
|
|
|
/* Can we continue in a containing and-group? */
|
|
if (m_nextand(thisfsa->oldtop, label)) return(TRUE) ;
|
|
return(FALSE) ;
|
|
}
|
|
|
|
/* Pops the parse stack*/
|
|
void m_pop(void)
|
|
{
|
|
M_PARSE *stackelt ;
|
|
|
|
if (! m_stacktop->oldtop) {
|
|
m_error("Program error: attempt to pop empty stack") ;
|
|
m_exit(TRUE) ;
|
|
}
|
|
|
|
if (m_stacktop->map && m_stacktop->map != m_stacktop->oldtop->map)
|
|
m_free(m_stacktop->map, "short reference map") ;
|
|
m_freeparam(m_stacktop) ;
|
|
m_freeFSA(m_stacktop) ;
|
|
|
|
if (m_stacktop->neednet) m_netlevel-- ;
|
|
stackelt = m_stacktop ;
|
|
m_stacktop = stackelt->oldtop ;
|
|
m_free(stackelt, "stack element") ;
|
|
}
|
|
|
|
/* Pushes a new item onto the parse stack, setting its element, current,
|
|
and neednet fields as indicated by the parameters*/
|
|
void m_push(M_ELEMENT elt, M_STATE current, LOGICAL need)
|
|
{
|
|
M_PARSE *newstack ;
|
|
|
|
m_aftereod = FALSE ;
|
|
newstack = (M_PARSE *) m_malloc(sizeof(M_PARSE), "stack element") ;
|
|
newstack->oldtop = m_stacktop ;
|
|
newstack->element = elt ;
|
|
newstack->param = NULL ;
|
|
if (m_element[elt - 1].content == M_REGEXP) {
|
|
newstack->fsastack = (M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
|
|
newstack->fsastack->oldtop = NULL ;
|
|
newstack->fsastack->current = current ;
|
|
newstack->fsastack->andgroup = M_NULLVAL ;
|
|
newstack->fsastack->usedand = NULL ;
|
|
}
|
|
else newstack->fsastack = NULL ;
|
|
newstack->thisent = 0 ;
|
|
newstack->neednet = need ;
|
|
newstack->firstre = FALSE ;
|
|
newstack->contextual = TRUE ;
|
|
newstack->intext = FALSE ;
|
|
newstack->linestat = M_NOTHING ;
|
|
newstack->holdre = FALSE ;
|
|
newstack->map = m_stacktop->map ;
|
|
newstack->cdcase = m_stacktop->cdcase ;
|
|
newstack->picase = m_stacktop->picase ;
|
|
newstack->stccase = m_stacktop->stccase ;
|
|
newstack->cdparam = m_stacktop->cdparam ;
|
|
newstack->piparam = m_stacktop->piparam ;
|
|
newstack->stparam = m_stacktop->stparam ;
|
|
newstack->file = m_stacktop->file ;
|
|
newstack->line = m_stacktop->line ;
|
|
newstack->ifdata = NULL ;
|
|
m_stacktop = newstack ;
|
|
if (m_element[elt - 1].srefptr)
|
|
m_setmap(m_element[elt - 1].srefptr,
|
|
(LOGICAL) m_element[elt - 1].useoradd) ;
|
|
}
|
|
|
|
/* Process first character of a segment of character data. The first
|
|
character is treated differently so that if character data is not
|
|
allowed in the current context, an error message is issued with the
|
|
first character only and not with every character. */
|
|
void m_strtcdata(int scanval)
|
|
{
|
|
if (! m_strtproc(M_NULLVAL))
|
|
if (m_whitespace((M_WCHAR) scanval)) {
|
|
m_curcon = m_prevcon ;
|
|
return ;
|
|
}
|
|
else {
|
|
if (m_stacktop->oldtop) {
|
|
m_err1("Data characters not allowed at this point in %s",
|
|
m_nameofelt(m_stacktop->element)) ;
|
|
m_expecting() ;
|
|
m_showcurelt() ;
|
|
}
|
|
else if (! m_start) {
|
|
m_error("Document may not start with data characters") ;
|
|
m_expecting() ;
|
|
}
|
|
}
|
|
m_start = TRUE ;
|
|
m_textaction((M_WCHAR) scanval) ;
|
|
m_stacktop->firstre = TRUE ;
|
|
m_stacktop->intext = TRUE ;
|
|
}
|
|
|
|
/* M_strtproc checks that the next starttag or beginning of the next
|
|
#PCDATA segment is valid, processing omitted start and endtags as needed.
|
|
(Since m_endtag may reset the current context if the stack is popped down
|
|
to an element that was within #PCDATA, m_strtproc saves the current context
|
|
and restores it after returning from the last call to m_endtag.)
|
|
*/
|
|
LOGICAL m_strtproc(M_ELEMENT scanval)
|
|
{
|
|
int check ;
|
|
M_PARSE *original ;
|
|
int savecontext ;
|
|
int savenet ;
|
|
M_PARSE *savestack ;
|
|
M_PARSE *starttagomit ;
|
|
M_MIN *min ;
|
|
M_MIN *discard ;
|
|
|
|
/* The algorithms used here involve making a copy of the stack entry
|
|
at the top of the stack before testing for the possibility of
|
|
start-tag omission. Values of cdparam, piparam, and stparam
|
|
are not used while testing for markup minimization and therefore
|
|
are not set. However, the original entry and the copy may differ
|
|
in the accuracy of these values, so care must be taken to keep
|
|
the version in which they are correct when the stack is manipulated
|
|
for the final time */
|
|
|
|
/* Is scanval allowed without tag omission? */
|
|
savestack = m_stacktop ;
|
|
original = m_stacktop ;
|
|
m_stacktop = m_copystackelt() ;
|
|
if (check = m_checkstart(scanval)) {
|
|
if (scanval && m_stacktop->holdre && check != M_NONCONTEXTUAL) {
|
|
m_freeFSA(m_stacktop) ;
|
|
m_free(m_stacktop, "stack element") ;
|
|
m_stacktop = original ;
|
|
m_holdproc() ;
|
|
return(m_strtproc(scanval)) ;
|
|
}
|
|
m_freeFSA(m_stacktop) ;
|
|
m_free(m_stacktop, "stack element") ;
|
|
m_stacktop = original ;
|
|
if (scanval && check != M_NONCONTEXTUAL) {
|
|
m_stacktop->linestat = M_DCORCET ;
|
|
m_stacktop->firstre = TRUE ;
|
|
}
|
|
m_strttag(scanval, m_scannet) ;
|
|
if (check == M_NONCONTEXTUAL) m_stacktop->contextual = FALSE ;
|
|
else if (scanval) m_stacktop->oldtop->intext = FALSE ;
|
|
return(TRUE) ;
|
|
}
|
|
|
|
/* Check for start- and end-tag omission */
|
|
savecontext = m_curcon ;
|
|
savenet = m_netlevel ;
|
|
m_minstart = m_minend = NULL ;
|
|
m_nextms = &m_minstart ;
|
|
m_nextme = &m_minend ;
|
|
starttagomit = m_stacktop ;
|
|
while (TRUE) {
|
|
if (m_omitstart()) {
|
|
if (check = m_checkstart(scanval)) break ;
|
|
else continue ;
|
|
}
|
|
m_freemin(m_minstart, "start-tag minimization") ;
|
|
m_minstart = NULL ;
|
|
m_nextms = &m_minstart ;
|
|
while (m_stacktop != starttagomit) m_pop() ;
|
|
m_freeFSA(m_stacktop) ;
|
|
m_free(m_stacktop, "stack element") ;
|
|
m_stacktop = original ;
|
|
if (m_omitend()) {
|
|
original = m_stacktop->oldtop ;
|
|
m_stacktop = m_stacktop->oldtop ;
|
|
m_stacktop = m_copystackelt() ;
|
|
starttagomit = m_stacktop ;
|
|
if (check = m_checkstart(scanval)) break ;
|
|
else continue ;
|
|
}
|
|
m_freemin(m_minend, "end-tag minimization") ;
|
|
m_freemin(m_minstart, "start-tag minimization") ;
|
|
m_stacktop = savestack ;
|
|
m_netlevel = savenet ;
|
|
m_curcon = savecontext ;
|
|
return(FALSE) ;
|
|
}
|
|
|
|
/* Have determined a sequence of omitted tags. Process them */
|
|
/* Undo all stack changes that were made tentatively, so they can
|
|
be redone with invocation of interface as appropriate */
|
|
while (m_stacktop != starttagomit) m_pop() ;
|
|
m_freeFSA(m_stacktop) ;
|
|
m_free(m_stacktop, "stack element") ;
|
|
m_stacktop = savestack ;
|
|
m_netlevel = savenet ;
|
|
if (m_minend) m_stacktop->holdre = FALSE ;
|
|
else if (m_stacktop->holdre && check != M_NONCONTEXTUAL) {
|
|
m_freemin(m_minstart, "start-tag minimization") ;
|
|
m_holdproc() ;
|
|
if (scanval) return(m_strtproc(scanval)) ;
|
|
else return(TRUE) ;
|
|
}
|
|
for (min = m_minend ; min ;) {
|
|
if (m_stacktop->neednet) {
|
|
M_WCHAR *wc_net;
|
|
|
|
wc_net = MakeWideCharString(m_net);
|
|
m_err2("Expecting %s to end %s",
|
|
wc_net,
|
|
m_nameofelt(m_stacktop->element)) ;
|
|
m_free(wc_net,"wide character string");
|
|
m_showcurelt() ;
|
|
}
|
|
if (! m_element[m_stacktop->element - 1].emin) {
|
|
m_err1("Missing end tag for %s", m_nameofelt(m_stacktop->element)) ;
|
|
m_showcurelt() ;
|
|
}
|
|
m_endtag(min->val) ;
|
|
discard = min ;
|
|
min = min->next ;
|
|
m_free(discard, "end-tag minimization") ;
|
|
}
|
|
for (min = m_minstart ; min ;) {
|
|
m_checkstart(min->val) ;
|
|
m_strttag(min->val, FALSE) ;
|
|
if (! m_element[min->val - 1].smin)
|
|
m_err1("Missing start tag for %s", m_nameofelt(min->val)) ;
|
|
m_stkdefaultparams() ;
|
|
discard = min ;
|
|
min = min->next ;
|
|
m_free(discard, "start-tag minimization") ;
|
|
}
|
|
check = m_checkstart(scanval) ;
|
|
if (scanval && check != M_NONCONTEXTUAL) {
|
|
m_stacktop->linestat = M_DCORCET ;
|
|
m_stacktop->firstre = TRUE ;
|
|
}
|
|
m_strttag(scanval, m_scannet) ;
|
|
if (check == M_NONCONTEXTUAL) m_stacktop->contextual = FALSE ;
|
|
else if (scanval) m_stacktop->oldtop->intext = FALSE ;
|
|
m_curcon = savecontext ;
|
|
if (m_element[m_stacktop->element - 1].content == M_CDATA)
|
|
m_curcon = CDATAEL ;
|
|
if (m_element[m_stacktop->element - 1].content == M_RCDATA) {
|
|
m_curcon = RCDATAEL ;
|
|
m_stacktop->thisent = m_eopencnt ;
|
|
}
|
|
return(TRUE) ;
|
|
}
|
|
|
|
/* Processes explicit or implied start tag*/
|
|
void m_strttag(M_ELEMENT val, LOGICAL net)
|
|
{
|
|
m_transition(val, TRUE) ;
|
|
if (val) {
|
|
m_push(val, m_element[val - 1].start, net) ;
|
|
if (net) m_netlevel++ ;
|
|
if (m_element[val - 1].content == M_CDATA ||
|
|
m_element[val - 1].content == M_RCDATA) {
|
|
m_stacktop->intext = TRUE ;
|
|
m_curcon = m_element[val - 1].content == M_CDATA ?
|
|
CDATAEL : RCDATAEL ;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Returns indication of whether or not parsed data characters are allowed
|
|
(without tag minimization) in the current context. Used to distinguish
|
|
mixed content from element content. (Assuming the definition of
|
|
mixed content is a context where #PCDATA can occur, not that the current
|
|
content model contains #PCDATA at some point. The former definition
|
|
makes more sense, is used by MARKUP, and is under consideration by the
|
|
Standards committee; the latter is the current definition in the Standard
|
|
*/
|
|
LOGICAL m_textpermitted(void)
|
|
{
|
|
M_ANDGROUP pand ;
|
|
M_OPENFSA *fsastack ;
|
|
M_ANDLIST *usedand ;
|
|
LOGICAL morebranches = FALSE ;
|
|
|
|
if (! m_stacktop->oldtop) return(FALSE) ;
|
|
/* If element has declared content (other than EMPTY), data is allowed.
|
|
But EMPTY elements don't stay on the stack long enough to call this
|
|
function */
|
|
if (m_element[m_stacktop->element - 1].content != M_REGEXP) return(TRUE) ;
|
|
/* If within #PCDATA, more text can be entered */
|
|
if (m_stacktop->intext) return(TRUE) ;
|
|
/* If current state emits an arc labelled #PCDATA, text can be
|
|
entered */
|
|
for (fsastack = m_stacktop->fsastack ;
|
|
fsastack ;
|
|
fsastack = fsastack->oldtop) {
|
|
for (pand = fsastack->andgroup ;
|
|
pand ;
|
|
pand = m_andgroup[pand - 1].next) {
|
|
for (usedand = fsastack->usedand ;
|
|
usedand ;
|
|
usedand = usedand->next)
|
|
if (usedand->group == pand) break ;
|
|
if (! usedand) {
|
|
if (m_state[m_andgroup[pand - 1].start - 1].datacontent)
|
|
return(TRUE) ;
|
|
if (! m_state[m_andgroup[pand - 1].start - 1].final)
|
|
morebranches = TRUE ;
|
|
}
|
|
}
|
|
if (morebranches) return(FALSE) ;
|
|
if (m_state[fsastack->current - 1].datacontent) return(TRUE) ;
|
|
if (! m_state[fsastack->current - 1].final) return(FALSE) ;
|
|
}
|
|
return(FALSE) ;
|
|
} /* End m_textpermitted() */
|
|
|
|
/* Returns TRUE iff LABEL allowed in the current state of the current
|
|
element (without expanding any minimization). May result in changes
|
|
to the stack of FSA's for this element if and-groups open or close. */
|
|
LOGICAL m_transition(M_ELEMENT label, LOGICAL recur)
|
|
{
|
|
M_ARC parc ;
|
|
M_OPENFSA *newfsa ;
|
|
M_ANDGROUP pand ;
|
|
|
|
if (m_stacktop->oldtop &&
|
|
m_element[m_stacktop->element - 1].content != M_REGEXP)
|
|
return(FALSE) ;
|
|
for (parc = m_state[m_stacktop->fsastack->current - 1].first ;
|
|
parc ;
|
|
parc = m_arc[parc - 1].next) {
|
|
if (m_arc[parc - 1].group) {
|
|
newfsa = (M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
|
|
newfsa->oldtop = m_stacktop->fsastack ;
|
|
newfsa->andgroup = M_NULLVAL ;
|
|
newfsa->usedand = NULL ;
|
|
m_stacktop->fsastack = newfsa ;
|
|
for (pand = m_arc[parc - 1].group ; pand ;
|
|
pand = m_andgroup[pand - 1].next) {
|
|
newfsa->current = m_andgroup[pand - 1].start ;
|
|
if (m_transition(label, FALSE)) {
|
|
newfsa->oldtop->andgroup = m_arc[parc - 1].group ;
|
|
newfsa->oldtop->usedand =
|
|
(M_ANDLIST *) m_malloc(sizeof(M_ANDLIST), "and list") ;
|
|
newfsa->oldtop->usedand->group = pand ;
|
|
newfsa->oldtop->usedand->next = NULL ;
|
|
newfsa->oldtop->current = m_arc[parc - 1].to ;
|
|
return(TRUE) ;
|
|
}
|
|
}
|
|
m_stacktop->fsastack = newfsa->oldtop ;
|
|
m_free(newfsa, "FSA") ;
|
|
}
|
|
else if (label == m_arc[parc - 1].label) {
|
|
m_stacktop->fsastack->current = m_arc[parc - 1].to ;
|
|
return(TRUE) ;
|
|
}
|
|
} /* End for parc */
|
|
if (recur && m_nextand(m_stacktop->fsastack, label)) return(TRUE) ;
|
|
return(FALSE) ;
|
|
} /* End transition */
|
|
|