Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dthelp / parser / canon1 / parser / struct.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: struct.c /main/3 1995/11/08 09:44:24 rswiston $ */
24 /*
25               Copyright 1986 Tandem Computers Incorporated.
26 This product and information is proprietary of Tandem Computers Incorporated.
27                    Copyright 1986, 1987, 1988, 1989 Hewlett-Packard Co.
28 */
29
30 /* Struct.c contains functions relevant to parsing document structure for
31    program PARSER */
32
33 #include <string.h>
34 #include <stdio.h>
35 #include <malloc.h>
36 #if defined(MSDOS)
37 #include <process.h>
38 #endif
39 #include "basic.h"
40 #include "trie.h"
41 #include "dtdext.h"
42 #include "delim.h"
43 #include "context.h"
44 #include "parser.h"
45 #include "entext.h"
46 #include "if.h"
47
48 /* M_checkstart tests to see if the element (or #PCDATA) indicated by VAL
49    is valid input. It returns TRUE, FALSE, or M_NONCONTEXTUAL respectively
50    if the element is allowed by content, not allowed, or allowed by an
51    inclusion exception. */
52 int m_checkstart(val)
53   M_ELEMENT val ;
54   {
55     M_PARSE *stackptr ;
56     int except ;
57
58     /* Check for applicable exclusions */
59     if (m_excluded(val)) return(FALSE) ;
60
61     /* Check for declared content in element currently at top of stack */
62     if (m_stacktop->oldtop) {
63       if (m_element[m_stacktop->element - 1].content == M_ANY) return(TRUE) ;
64       if (m_element[m_stacktop->element - 1].content == M_CDATA ||
65           m_element[m_stacktop->element - 1].content == M_RCDATA)
66         if (! val) return(TRUE) ;
67         else return(FALSE) ;
68       }
69
70     /* Check content model */
71     if (m_transition(val, TRUE)) return(TRUE) ;
72
73     /* Check for inclusions */
74     for (stackptr = m_stacktop ;
75          stackptr->oldtop ;
76          stackptr = stackptr->oldtop)
77       for (except = m_element[stackptr->element - 1].inptr ;
78            except ;
79            except = m_exception[except - 1].next)
80       if (m_exception[except - 1].element == val) return(M_NONCONTEXTUAL) ;
81
82     /* Nothing left to try, val is not allowed */
83     return(FALSE) ;
84     }
85
86 /* M_ckend verifies that element VAL can be legally ended at the
87    current state of the parse, by an end tag or NET as indicated by NEEDNET.
88    If VAL is not the element at the top of the parse stack, m_ckend
89    checks to see if the end of VAL can validly end nested
90    elements as well. */
91 #if defined(M_PROTO)
92 LOGICAL m_ckend(M_ELEMENT val, LOGICAL neednet)
93 #else
94 LOGICAL m_ckend(val, neednet)
95   M_ELEMENT val ;
96   LOGICAL neednet ;
97   #endif
98 {
99     M_PARSE *stackptr ;
100     M_OPENFSA *fsastack ;
101     M_ANDLIST *usedand ;
102     M_ANDGROUP pand ;
103     M_MIN *minend ;
104     M_MIN *discard ;
105
106     m_minend = NULL ;
107     m_nextme = &m_minend ;
108     /* Go down the stack, checking that each element can end until
109        element val occurs */       
110     for (stackptr = m_stacktop ; stackptr ; stackptr = stackptr->oldtop) {
111       /* If the element at stackptr has a content model, make sure each
112          open fsa is in a final state and that all required submodels of
113          open and-groups have occurred */
114       for (fsastack = stackptr->fsastack ;
115            fsastack ;
116            fsastack = fsastack->oldtop) {
117         if (! m_state[fsastack->current - 1].final) {
118           m_freemin(m_minend, "end-tag minimization") ;
119           return(FALSE) ;
120           }
121         for (pand = fsastack->andgroup ;
122              pand ;
123              pand = m_andgroup[pand - 1].next) {
124           /* Don't bother checking if optional submodel of an and-group
125              has occurred */
126           if (m_state[m_andgroup[pand - 1].start - 1].final) continue ;
127           for (usedand = fsastack->usedand ;
128                usedand ;
129                usedand = usedand->next)
130             if (usedand->group == pand) break ;
131           if (! usedand) {
132             /* Didn't find a required submodel */
133             m_freemin(m_minend, "end-tag minimization") ;
134             return(FALSE) ;
135             }  
136           } /* End for pand */
137         } /* End for fsastack */
138       /* Have confirmed that the element indicated by stackptr can end now */
139       if (stackptr->element == val) break ;
140       *m_nextme = (M_MIN *) m_malloc(sizeof(M_MIN), "end-tag minimization") ;
141       (*m_nextme)->next = NULL ;
142       (*m_nextme)->val = stackptr->element ;
143       m_nextme = &(*m_nextme)->next ;
144       } /* End for stackptr */
145     if (! stackptr) {
146       m_freemin(m_minend, "end-tag minimization") ;
147       return(FALSE) ;
148       }
149     for (minend = m_minend ; minend ; ) {
150       discard = minend ;
151       minend = minend->next ;
152       m_free(discard, "end-tag minimization") ;
153       if (m_stacktop->neednet && ! neednet) {
154         M_WCHAR *wc_net;
155
156         wc_net = MakeWideCharString(m_net);
157         m_err2("Expecting %s to end %s",
158                wc_net,
159                m_nameofelt(m_stacktop->element)) ;
160         m_free(wc_net,"wide character string");
161         m_showcurelt() ;
162         }
163       if (! m_element[m_stacktop->element - 1].emin) {
164         m_err1("Missing end tag for %s", m_nameofelt(m_stacktop->element)) ;
165         m_showcurelt() ;
166         }
167       m_endtag(m_stacktop->element) ;
168       }
169     if (m_stacktop->neednet != neednet) {
170       M_WCHAR *wc_etago, *wc_tagc, *wc_mnet, *wc_stago, *wc_net;
171
172       wc_etago = MakeWideCharString(m_etago);
173       wc_stago = MakeWideCharString(m_stago);
174       wc_tagc  = MakeWideCharString(m_tagc);
175       wc_net   = MakeWideCharString(m_net);
176       if (neednet)
177         m_err4("Expecting %s%s%s instead of %s",
178                wc_etago,
179                m_nameofelt(m_stacktop->element),
180                wc_tagc,
181                wc_net) ;
182       else
183         m_err4("Expecting %s to end %s%s%s",
184                wc_net,
185                wc_stago,
186                m_nameofelt(m_stacktop->element),
187                wc_tagc) ;
188       m_showcurelt() ;
189       m_free(wc_etago,"wide character string");
190       m_free(wc_stago,"wide character string");
191       m_free(wc_tagc,"wide character string");
192       m_free(wc_net,"wide character string");
193       }
194     m_endtag(val) ;
195     return(TRUE) ;
196     } /*end m_ckend*/
197
198 /* Make a copy of the stack entry at the top of the parse stack in a scratch
199    area */
200 M_PARSE *m_copystackelt(M_NOPAR)
201   {
202     M_OPENFSA *oldfsa ;
203     M_OPENFSA **newfsa ;
204     M_ANDLIST *oldand ;
205     M_ANDLIST **newand ;
206     M_PARSE *copy ;
207
208     copy = (M_PARSE *) m_malloc(sizeof(M_PARSE), "stack element") ;
209     memcpy((char *) copy, (char *) m_stacktop, sizeof(M_PARSE)) ;
210     copy->param = NULL ;
211     for (oldfsa = m_stacktop->fsastack, newfsa = &copy->fsastack ;
212          oldfsa ;
213          oldfsa = oldfsa->oldtop, newfsa = &(*newfsa)->oldtop) {
214       *newfsa = (M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
215       memcpy((char *) *newfsa, (char *) oldfsa, sizeof(M_OPENFSA)) ;
216       for (oldand = oldfsa->usedand, newand = &(*newfsa)->usedand ;
217            oldand ;
218            oldand = oldand->next, newand = &(*newand)->next) {
219         *newand = (M_ANDLIST *) m_malloc(sizeof(M_ANDLIST), "and list") ;
220         (*newand)->group = oldand->group ;
221         (*newand)->next = NULL ;
222         }
223       }
224     return(copy) ;
225     }
226
227 /* End of document */
228 void m_done(M_NOPAR)
229   {
230     M_ELEMENT lastelt ;
231
232     while (m_stacktop->oldtop) {
233       lastelt = m_stacktop->element ;
234       if (! m_ckend(m_stacktop->element, FALSE)) {
235         m_err1("More content expected in element %s at end of document",
236                m_nameofelt(m_stacktop->element)) ;
237         m_expecting() ;
238         m_showcurelt() ;
239         m_frcend(m_stacktop->element) ;
240         }
241       else if (! m_element[lastelt - 1].emin)
242         m_err1("Missing end tag for %s", m_nameofelt(lastelt)) ;
243       }
244     m_endcase(1) ;
245     m_globes() ;
246     m_exit(m_errexit) ;
247     }
248
249 /* Process the endtag (implied, abbreviated, or explicit) for element C*/
250 void m_endtag(c)
251   M_ELEMENT c ;
252   {
253     m_endaction(c) ;
254     m_pop() ;
255     if (m_stacktop->intext) {
256       m_curcon = POUNDCDATA ;
257       if (m_netlevel) m_curcon = NETCDATA ;
258       }
259     }
260
261 /* Check that the identified element is not prohibited in the current context
262    by an exclusion exception */
263 LOGICAL m_excluded(elt)
264   M_ELEMENT elt ;
265   {
266     M_PARSE *stackptr ;
267     int except ;
268
269     if (! elt) return(FALSE) ;
270     for (stackptr = m_stacktop ;
271          stackptr->oldtop ;
272          stackptr = stackptr->oldtop)
273       for (except = m_element[stackptr->element - 1].exptr ;
274            except ;
275            except = m_exception[except - 1].next)
276       if (m_exception[except - 1].element == elt) return(TRUE) ;
277     return(FALSE) ;
278     }
279
280 /* Free the OPEN FSA substructures associated with an element on
281    the parse stack */
282 void m_freeFSA(stackelt)
283   M_PARSE *stackelt ;
284   {
285     M_OPENFSA *fsastack ;
286     M_ANDLIST *usedand ;
287
288     while (stackelt->fsastack) {
289       fsastack = stackelt->fsastack ;
290       if (fsastack == &m_botfsa) return ;
291       while (fsastack->usedand) {
292         usedand = fsastack->usedand ;
293         fsastack->usedand = usedand->next ;
294         m_free(usedand, "and list") ;
295         }
296       stackelt->fsastack = fsastack->oldtop ;
297       m_free(fsastack, "FSA") ;
298       }
299     }
300
301 /* Free storage used for tentative chain of tag minimizations */
302 void m_freemin(min, msg)
303   M_MIN *min ;
304   char *msg ;
305   {
306     M_MIN *discard ;
307
308     for ( ; min ;) {
309       discard = min ;
310       min = min->next ;
311       m_free(discard, msg) ;
312       }
313     }
314
315 /* M_nextand returns TRUE iff element label is allowed at the current point
316    in the current content model by starting a new submodel of the and-group
317    indicated by fsastack, or (if the and-group is within a seq-group) by
318    continuing past the and-group */
319 LOGICAL m_nextand(thisfsa, label)
320   M_OPENFSA *thisfsa ;
321   M_ELEMENT label ;
322   {
323     M_ANDLIST *newgroup ;
324     M_ANDGROUP pand ;
325     M_ANDLIST *plist ;
326     M_OPENFSA *savefsa ;
327     M_OPENFSA *fsa ;
328     LOGICAL required = FALSE ;
329     LOGICAL last ;
330
331     /* Verify currently within an and-group and in final state of this
332        branch */
333     if (! m_state[thisfsa->current - 1].final) return(FALSE) ;
334     if (! thisfsa->oldtop) return(FALSE) ;
335     savefsa = m_stacktop->fsastack ;
336
337     /* Check possibility of starting a new branch*/
338     m_stacktop->fsastack =
339       (M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
340     m_stacktop->fsastack->oldtop = thisfsa->oldtop ;
341     m_stacktop->fsastack->andgroup = M_NULLVAL ;
342     m_stacktop->fsastack->usedand = NULL ;
343     newgroup = (M_ANDLIST *) m_malloc(sizeof(M_ANDLIST), "and list") ;
344     newgroup->next = thisfsa->oldtop->usedand ;
345     thisfsa->oldtop->usedand = newgroup ;
346     for (pand = thisfsa->oldtop->andgroup ;
347          pand ;
348          pand = m_andgroup[pand - 1].next) {
349       for (plist = newgroup->next ; plist ; plist = plist->next)
350         if (pand == plist->group) break ;
351       if (! plist) {
352         newgroup->group = pand ;
353         m_stacktop->fsastack->current = m_andgroup[pand - 1].start ;
354         if (! m_state[m_stacktop->fsastack->current - 1].final)
355           required = TRUE ;
356         if (m_transition(label, FALSE)) {
357           for (fsa = savefsa ; TRUE ; fsa = fsa->oldtop) {
358             for (plist = fsa->usedand ; plist ; plist = plist->next)
359               m_free(plist, "and list") ;
360             if (fsa == thisfsa) {
361               m_free(fsa, "FSA") ;
362               break;
363               }
364             m_free(fsa, "FSA") ;
365             }
366           return(TRUE) ;
367           }
368         }
369       }
370   
371     /* Couldn't start a new branch.  Restore parse stack */
372     thisfsa->oldtop->usedand = newgroup->next ;
373     m_free(newgroup, "and list") ;
374     m_free(m_stacktop->fsastack, "FSA") ;
375     m_stacktop->fsastack = savefsa ;
376
377     /* Have all required branches occurred? */
378     if (required) return(FALSE) ;
379
380     /* Can we continue past this and-group in a containing seq-group? */
381     m_stacktop->fsastack =
382       (M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
383     m_stacktop->fsastack->oldtop = thisfsa->oldtop->oldtop ;
384     m_stacktop->fsastack->andgroup = M_NULLVAL ;
385     m_stacktop->fsastack->usedand = NULL ;
386     m_stacktop->fsastack->current = thisfsa->oldtop->current ;
387     if (m_transition(label, FALSE)) {
388       /* Free temporary FSA storage used to test transition */
389       for (fsa = savefsa, last = FALSE ; TRUE ; fsa = fsa->oldtop) {
390         for (plist = fsa->usedand ; plist ; plist = plist->next)
391           m_free(plist, "and list") ;
392         m_free(fsa, "FSA") ;
393         if (last) break ;
394         if (fsa == thisfsa) last = TRUE ;
395         }
396       return(TRUE) ;
397       }
398     m_free(m_stacktop->fsastack, "FSA") ;
399     m_stacktop->fsastack = savefsa ;
400
401     /* Can we continue in a containing and-group? */
402     if (m_nextand(thisfsa->oldtop, label)) return(TRUE) ;
403     return(FALSE) ;
404     }
405
406 /* Pops the parse stack*/
407 void m_pop(M_NOPAR)
408   {
409     M_PARSE *stackelt ;
410
411     if (! m_stacktop->oldtop) {
412       m_error("Program error: attempt to pop empty stack") ;
413       m_exit(TRUE) ;
414       }
415
416     if (m_stacktop->map && m_stacktop->map != m_stacktop->oldtop->map)
417       m_free(m_stacktop->map, "short reference map") ;
418     m_freeparam(m_stacktop) ;
419     m_freeFSA(m_stacktop) ;
420
421     if (m_stacktop->neednet) m_netlevel-- ;
422     stackelt = m_stacktop ;
423     m_stacktop = stackelt->oldtop ;
424     m_free(stackelt, "stack element") ;
425     }
426
427 /* Pushes a new item onto the parse stack, setting its element, current,
428    and neednet fields as indicated by the parameters*/
429 #if defined(M_PROTO)
430 void m_push(M_ELEMENT elt, M_STATE current, LOGICAL need)
431 #else
432 void m_push(elt, current, need)
433   M_ELEMENT elt ;
434   M_STATE current ;
435   LOGICAL need ;
436   #endif
437 {
438     M_PARSE *newstack ;
439
440     m_aftereod = FALSE ;
441     newstack = (M_PARSE *) m_malloc(sizeof(M_PARSE), "stack element") ;
442     newstack->oldtop = m_stacktop ;
443     newstack->element = elt ;
444     newstack->param = NULL ;
445     if (m_element[elt - 1].content == M_REGEXP) {
446       newstack->fsastack = (M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
447       newstack->fsastack->oldtop = NULL ;
448       newstack->fsastack->current = current ;
449       newstack->fsastack->andgroup = M_NULLVAL ;
450       newstack->fsastack->usedand = NULL ;
451       }
452     else newstack->fsastack = NULL ;
453     newstack->thisent = 0 ;
454     newstack->neednet = need ;
455     newstack->firstre = FALSE ;
456     newstack->contextual = TRUE ;
457     newstack->intext = FALSE ;
458     newstack->linestat = M_NOTHING ;
459     newstack->holdre = FALSE ;
460     newstack->map = m_stacktop->map ;
461     newstack->cdcase = m_stacktop->cdcase ;
462     newstack->picase = m_stacktop->picase ;
463     newstack->stccase = m_stacktop->stccase ;
464     newstack->cdparam = m_stacktop->cdparam ;
465     newstack->piparam = m_stacktop->piparam ;
466     newstack->stparam = m_stacktop->stparam ;
467     newstack->file = m_stacktop->file ;
468     newstack->line = m_stacktop->line ;
469     newstack->ifdata = NULL ;
470     m_stacktop = newstack ;
471     if (m_element[elt - 1].srefptr)
472       m_setmap(m_element[elt - 1].srefptr,
473                (LOGICAL) m_element[elt - 1].useoradd) ;
474     }
475
476 /* Process first character of a segment of character data.  The first
477    character is treated differently so that if character data is not
478    allowed in the current context, an error message is issued with the
479    first character only and not with every character. */
480 void m_strtcdata(scanval)
481   int scanval ;
482   {
483     if (! m_strtproc(M_NULLVAL))
484       if (m_whitespace((M_WCHAR) scanval)) {
485         m_curcon = m_prevcon ;
486         return ;
487         }
488       else {
489         if (m_stacktop->oldtop) {
490           m_err1("Data characters not allowed at this point in %s",
491                  m_nameofelt(m_stacktop->element)) ;
492           m_expecting() ;
493           m_showcurelt() ;
494           }
495         else if (! m_start) {
496           m_error("Document may not start with data characters") ;
497           m_expecting() ;
498           }
499         }
500     m_start = TRUE ;
501     m_textaction((M_WCHAR) scanval) ;
502     m_stacktop->firstre = TRUE ;
503     m_stacktop->intext = TRUE ;
504     }
505
506 /* M_strtproc checks that the next starttag or beginning of the next
507    #PCDATA segment is valid, processing omitted start and endtags as needed.
508    (Since m_endtag may reset the current context if the stack is popped down
509    to an element that was within #PCDATA, m_strtproc saves the current context
510    and restores it after returning from the last call to m_endtag.)
511 */
512 LOGICAL m_strtproc(scanval)
513   M_ELEMENT scanval ;
514   {
515     int check ;
516     M_PARSE *original ;
517     int savecontext ;
518     int savenet ;
519     M_PARSE *savestack ;
520     M_PARSE *starttagomit ;
521     M_MIN *min ;
522     M_MIN *discard ;
523
524     /* The algorithms used here involve making a copy of the stack entry
525        at the top of the stack before testing for the possibility of
526        start-tag omission.  Values of cdparam, piparam, and stparam
527        are not used while testing for markup minimization and therefore
528        are not set.  However, the original entry and the copy may differ
529        in the accuracy of these values, so care must be taken to keep
530        the version in which they are correct when the stack is manipulated
531        for the final time */
532
533     /* Is scanval allowed without tag omission? */
534     savestack = m_stacktop ;
535     original = m_stacktop ;
536     m_stacktop = m_copystackelt() ;
537     if (check = m_checkstart(scanval)) {
538       if (scanval && m_stacktop->holdre && check != M_NONCONTEXTUAL) {
539         m_freeFSA(m_stacktop) ;
540         m_free(m_stacktop, "stack element") ;
541         m_stacktop = original ;
542         m_holdproc() ;
543         return(m_strtproc(scanval)) ;
544         }
545       m_freeFSA(m_stacktop) ;
546       m_free(m_stacktop, "stack element") ;
547       m_stacktop = original ;
548       if (scanval && check != M_NONCONTEXTUAL) {
549         m_stacktop->linestat = M_DCORCET ;
550         m_stacktop->firstre = TRUE ;
551         }
552       m_strttag(scanval, m_scannet) ;
553       if (check == M_NONCONTEXTUAL) m_stacktop->contextual = FALSE ;
554       else if (scanval) m_stacktop->oldtop->intext = FALSE ;
555       return(TRUE) ;
556       }
557     
558     /* Check for start- and end-tag omission */
559     savecontext = m_curcon ;
560     savenet = m_netlevel ;
561     m_minstart = m_minend = NULL ;
562     m_nextms = &m_minstart ;
563     m_nextme = &m_minend ;
564     starttagomit = m_stacktop ;
565     while (TRUE) {
566       if (m_omitstart()) {
567         if (check = m_checkstart(scanval)) break ;
568         else continue ;
569         }
570       m_freemin(m_minstart, "start-tag minimization") ;
571       m_minstart = NULL ;
572       m_nextms = &m_minstart ;
573       while (m_stacktop != starttagomit) m_pop() ;
574       m_freeFSA(m_stacktop) ;
575       m_free(m_stacktop, "stack element") ;
576       m_stacktop = original ;
577       if (m_omitend()) {
578         original = m_stacktop->oldtop ;
579         m_stacktop = m_stacktop->oldtop ;
580         m_stacktop = m_copystackelt() ;
581         starttagomit = m_stacktop ;
582         if (check = m_checkstart(scanval)) break ;
583         else continue ;
584         }
585       m_freemin(m_minend, "end-tag minimization") ;
586       m_freemin(m_minstart, "start-tag minimization") ;
587       m_stacktop = savestack ;
588       m_netlevel = savenet ;
589       m_curcon = savecontext ;
590       return(FALSE) ;
591       }
592
593     /* Have determined a sequence of omitted tags.  Process them */
594     /* Undo all stack changes that were made tentatively, so they can
595        be redone with invocation of interface as appropriate */
596     while (m_stacktop != starttagomit) m_pop() ;
597     m_freeFSA(m_stacktop) ;
598     m_free(m_stacktop, "stack element") ;
599     m_stacktop = savestack ;
600     m_netlevel = savenet ;
601     if (m_minend) m_stacktop->holdre = FALSE ;
602     else if (m_stacktop->holdre && check != M_NONCONTEXTUAL) {
603       m_freemin(m_minstart, "start-tag minimization") ;
604       m_holdproc() ;
605       if (scanval) return(m_strtproc(scanval)) ;
606       else return(TRUE) ;
607       }
608     for (min = m_minend ; min ;) {
609       if (m_stacktop->neednet) {
610         M_WCHAR *wc_net;
611
612         wc_net = MakeWideCharString(m_net);
613         m_err2("Expecting %s to end %s",
614                wc_net,
615                m_nameofelt(m_stacktop->element)) ;
616         m_free(wc_net,"wide character string");
617         m_showcurelt() ;
618         }
619       if (! m_element[m_stacktop->element - 1].emin) {
620         m_err1("Missing end tag for %s", m_nameofelt(m_stacktop->element)) ;
621         m_showcurelt() ;
622         }
623       m_endtag(min->val) ;
624       discard = min ;
625       min = min->next ;
626       m_free(discard, "end-tag minimization") ;
627       }
628     for (min = m_minstart ; min ;) {
629       m_checkstart(min->val) ;
630       m_strttag(min->val, FALSE) ;
631       if (! m_element[min->val - 1].smin)
632         m_err1("Missing start tag for %s", m_nameofelt(min->val)) ;
633       m_stkdefaultparams() ;
634       discard = min ;
635       min = min->next ;
636       m_free(discard, "start-tag minimization") ;
637       }
638     check = m_checkstart(scanval) ;
639     if (scanval && check != M_NONCONTEXTUAL) {
640       m_stacktop->linestat = M_DCORCET ;
641       m_stacktop->firstre = TRUE ;
642       }
643     m_strttag(scanval, m_scannet) ;
644     if (check == M_NONCONTEXTUAL) m_stacktop->contextual = FALSE ;
645     else if (scanval) m_stacktop->oldtop->intext = FALSE ;
646     m_curcon = savecontext ;
647     if (m_element[m_stacktop->element - 1].content == M_CDATA)
648       m_curcon = CDATAEL ;
649     if (m_element[m_stacktop->element - 1].content == M_RCDATA) {
650       m_curcon = RCDATAEL ;
651       m_stacktop->thisent = m_eopencnt ;
652       }
653     return(TRUE) ;
654     }
655
656 /* Processes explicit or implied start tag*/
657 #if defined(M_PROTO)
658 void m_strttag(M_ELEMENT val, LOGICAL net)
659 #else
660 void m_strttag(val, net)
661   M_ELEMENT val ;
662   LOGICAL net ;
663 #endif
664 {
665     m_transition(val, TRUE) ;
666     if (val) {
667       m_push(val, m_element[val - 1].start, net) ;
668       if (net) m_netlevel++ ;
669       if (m_element[val - 1].content == M_CDATA ||
670           m_element[val - 1].content == M_RCDATA) {
671         m_stacktop->intext = TRUE ;
672         m_curcon = m_element[val - 1].content == M_CDATA ?
673                    CDATAEL : RCDATAEL ;
674         }
675       }
676     }
677
678 /* Returns indication of whether or not parsed data characters are allowed
679    (without tag minimization) in the current context.  Used to distinguish
680    mixed content from element content. (Assuming the definition of
681    mixed content is a context where #PCDATA can occur, not that the current
682    content model contains #PCDATA at some point.  The former definition
683    makes more sense, is used by MARKUP, and is under consideration by the
684    Standards committee; the latter is the current definition in the Standard
685    */
686 LOGICAL m_textpermitted(M_NOPAR)
687   {
688     M_ANDGROUP pand ;
689     M_OPENFSA *fsastack ;
690     M_ANDLIST *usedand ;
691     LOGICAL morebranches = FALSE ;
692
693     if (! m_stacktop->oldtop) return(FALSE) ;
694     /* If element has declared content (other than EMPTY), data is allowed.
695        But EMPTY elements don't stay on the stack long enough to call this
696        function */
697     if (m_element[m_stacktop->element - 1].content != M_REGEXP) return(TRUE) ;
698     /* If within #PCDATA, more text can be entered */
699     if (m_stacktop->intext) return(TRUE) ;
700     /* If current state emits an arc labelled #PCDATA, text can be
701        entered */
702     for (fsastack = m_stacktop->fsastack ;
703          fsastack ;
704          fsastack = fsastack->oldtop) {
705       for (pand = fsastack->andgroup ;
706            pand ;
707            pand = m_andgroup[pand - 1].next) {
708         for (usedand = fsastack->usedand ;
709              usedand ;
710              usedand = usedand->next)
711           if (usedand->group == pand) break ;
712         if (! usedand) {
713           if (m_state[m_andgroup[pand - 1].start - 1].datacontent)
714             return(TRUE) ;
715           if (! m_state[m_andgroup[pand - 1].start - 1].final)
716             morebranches = TRUE ;
717           }
718         }
719       if (morebranches) return(FALSE) ; 
720       if (m_state[fsastack->current - 1].datacontent) return(TRUE) ;
721       if (! m_state[fsastack->current - 1].final) return(FALSE) ;
722       }
723     return(FALSE) ;
724     } /* End m_textpermitted() */
725
726 /* Returns TRUE iff LABEL allowed in the current state of the current
727    element (without expanding any minimization).  May result in changes
728    to the stack of FSA's for this element if and-groups open or close. */
729 #if defined(M_PROTO)
730 LOGICAL m_transition(M_ELEMENT label, LOGICAL recur)
731 #else
732 LOGICAL m_transition(label, recur)
733   M_ELEMENT label ;
734   LOGICAL recur ;
735 #endif
736 {
737     M_ARC parc ;
738     M_OPENFSA *newfsa ;
739     M_ANDGROUP pand ;
740
741     if (m_stacktop->oldtop &&
742         m_element[m_stacktop->element - 1].content != M_REGEXP)
743       return(FALSE) ;
744     for (parc = m_state[m_stacktop->fsastack->current - 1].first ;
745          parc ;
746          parc = m_arc[parc - 1].next) {
747       if (m_arc[parc - 1].group) {
748         newfsa = (M_OPENFSA *) m_malloc(sizeof(M_OPENFSA), "FSA") ;
749         newfsa->oldtop = m_stacktop->fsastack ;
750         newfsa->andgroup = M_NULLVAL ;
751         newfsa->usedand = NULL ;
752         m_stacktop->fsastack = newfsa ;
753         for (pand = m_arc[parc - 1].group ; pand ;
754              pand = m_andgroup[pand - 1].next) {
755           newfsa->current = m_andgroup[pand - 1].start ; 
756           if (m_transition(label, FALSE)) {
757             newfsa->oldtop->andgroup = m_arc[parc - 1].group ;
758             newfsa->oldtop->usedand =
759               (M_ANDLIST *) m_malloc(sizeof(M_ANDLIST), "and list") ;
760             newfsa->oldtop->usedand->group = pand ;
761             newfsa->oldtop->usedand->next = NULL ;
762             newfsa->oldtop->current = m_arc[parc - 1].to ;
763             return(TRUE) ;
764             }
765           }
766         m_stacktop->fsastack = newfsa->oldtop ;
767         m_free(newfsa, "FSA") ;
768         }
769       else if (label == m_arc[parc - 1].label) {
770         m_stacktop->fsastack->current = m_arc[parc - 1].to ;
771         return(TRUE) ;
772         }
773       } /* End for parc */
774     if (recur && m_nextand(m_stacktop->fsastack, label)) return(TRUE) ;
775     return(FALSE) ;
776     } /* End transition */
777