2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
23 /* $XConsortium: struct.c /main/3 1995/11/08 09:44:24 rswiston $ */
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.
30 /* Struct.c contains functions relevant to parsing document structure for
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. */
58 /* Check for applicable exclusions */
59 if (m_excluded(val)) return(FALSE) ;
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) ;
70 /* Check content model */
71 if (m_transition(val, TRUE)) return(TRUE) ;
73 /* Check for inclusions */
74 for (stackptr = m_stacktop ;
76 stackptr = stackptr->oldtop)
77 for (except = m_element[stackptr->element - 1].inptr ;
79 except = m_exception[except - 1].next)
80 if (m_exception[except - 1].element == val) return(M_NONCONTEXTUAL) ;
82 /* Nothing left to try, val is not allowed */
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
92 LOGICAL m_ckend(M_ELEMENT val, LOGICAL neednet)
94 LOGICAL m_ckend(val, neednet)
100 M_OPENFSA *fsastack ;
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 ;
116 fsastack = fsastack->oldtop) {
117 if (! m_state[fsastack->current - 1].final) {
118 m_freemin(m_minend, "end-tag minimization") ;
121 for (pand = fsastack->andgroup ;
123 pand = m_andgroup[pand - 1].next) {
124 /* Don't bother checking if optional submodel of an and-group
126 if (m_state[m_andgroup[pand - 1].start - 1].final) continue ;
127 for (usedand = fsastack->usedand ;
129 usedand = usedand->next)
130 if (usedand->group == pand) break ;
132 /* Didn't find a required submodel */
133 m_freemin(m_minend, "end-tag minimization") ;
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 */
146 m_freemin(m_minend, "end-tag minimization") ;
149 for (minend = m_minend ; minend ; ) {
151 minend = minend->next ;
152 m_free(discard, "end-tag minimization") ;
153 if (m_stacktop->neednet && ! neednet) {
156 wc_net = MakeWideCharString(m_net);
157 m_err2("Expecting %s to end %s",
159 m_nameofelt(m_stacktop->element)) ;
160 m_free(wc_net,"wide character string");
163 if (! m_element[m_stacktop->element - 1].emin) {
164 m_err1("Missing end tag for %s", m_nameofelt(m_stacktop->element)) ;
167 m_endtag(m_stacktop->element) ;
169 if (m_stacktop->neednet != neednet) {
170 M_WCHAR *wc_etago, *wc_tagc, *wc_mnet, *wc_stago, *wc_net;
172 wc_etago = MakeWideCharString(m_etago);
173 wc_stago = MakeWideCharString(m_stago);
174 wc_tagc = MakeWideCharString(m_tagc);
175 wc_net = MakeWideCharString(m_net);
177 m_err4("Expecting %s%s%s instead of %s",
179 m_nameofelt(m_stacktop->element),
183 m_err4("Expecting %s to end %s%s%s",
186 m_nameofelt(m_stacktop->element),
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");
198 /* Make a copy of the stack entry at the top of the parse stack in a scratch
200 M_PARSE *m_copystackelt(M_NOPAR)
208 copy = (M_PARSE *) m_malloc(sizeof(M_PARSE), "stack element") ;
209 memcpy((char *) copy, (char *) m_stacktop, sizeof(M_PARSE)) ;
211 for (oldfsa = m_stacktop->fsastack, newfsa = ©->fsastack ;
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 ;
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 ;
227 /* End of document */
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)) ;
239 m_frcend(m_stacktop->element) ;
241 else if (! m_element[lastelt - 1].emin)
242 m_err1("Missing end tag for %s", m_nameofelt(lastelt)) ;
249 /* Process the endtag (implied, abbreviated, or explicit) for element C*/
255 if (m_stacktop->intext) {
256 m_curcon = POUNDCDATA ;
257 if (m_netlevel) m_curcon = NETCDATA ;
261 /* Check that the identified element is not prohibited in the current context
262 by an exclusion exception */
263 LOGICAL m_excluded(elt)
269 if (! elt) return(FALSE) ;
270 for (stackptr = m_stacktop ;
272 stackptr = stackptr->oldtop)
273 for (except = m_element[stackptr->element - 1].exptr ;
275 except = m_exception[except - 1].next)
276 if (m_exception[except - 1].element == elt) return(TRUE) ;
280 /* Free the OPEN FSA substructures associated with an element on
282 void m_freeFSA(stackelt)
285 M_OPENFSA *fsastack ;
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") ;
296 stackelt->fsastack = fsastack->oldtop ;
297 m_free(fsastack, "FSA") ;
301 /* Free storage used for tentative chain of tag minimizations */
302 void m_freemin(min, msg)
311 m_free(discard, msg) ;
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)
323 M_ANDLIST *newgroup ;
328 LOGICAL required = FALSE ;
331 /* Verify currently within an and-group and in final state of this
333 if (! m_state[thisfsa->current - 1].final) return(FALSE) ;
334 if (! thisfsa->oldtop) return(FALSE) ;
335 savefsa = m_stacktop->fsastack ;
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 ;
348 pand = m_andgroup[pand - 1].next) {
349 for (plist = newgroup->next ; plist ; plist = plist->next)
350 if (pand == plist->group) break ;
352 newgroup->group = pand ;
353 m_stacktop->fsastack->current = m_andgroup[pand - 1].start ;
354 if (! m_state[m_stacktop->fsastack->current - 1].final)
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) {
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 ;
377 /* Have all required branches occurred? */
378 if (required) return(FALSE) ;
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") ;
394 if (fsa == thisfsa) last = TRUE ;
398 m_free(m_stacktop->fsastack, "FSA") ;
399 m_stacktop->fsastack = savefsa ;
401 /* Can we continue in a containing and-group? */
402 if (m_nextand(thisfsa->oldtop, label)) return(TRUE) ;
406 /* Pops the parse stack*/
411 if (! m_stacktop->oldtop) {
412 m_error("Program error: attempt to pop empty stack") ;
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) ;
421 if (m_stacktop->neednet) m_netlevel-- ;
422 stackelt = m_stacktop ;
423 m_stacktop = stackelt->oldtop ;
424 m_free(stackelt, "stack element") ;
427 /* Pushes a new item onto the parse stack, setting its element, current,
428 and neednet fields as indicated by the parameters*/
430 void m_push(M_ELEMENT elt, M_STATE current, LOGICAL need)
432 void m_push(elt, current, need)
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 ;
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) ;
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)
483 if (! m_strtproc(M_NULLVAL))
484 if (m_whitespace((M_WCHAR) scanval)) {
485 m_curcon = m_prevcon ;
489 if (m_stacktop->oldtop) {
490 m_err1("Data characters not allowed at this point in %s",
491 m_nameofelt(m_stacktop->element)) ;
495 else if (! m_start) {
496 m_error("Document may not start with data characters") ;
501 m_textaction((M_WCHAR) scanval) ;
502 m_stacktop->firstre = TRUE ;
503 m_stacktop->intext = TRUE ;
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.)
512 LOGICAL m_strtproc(scanval)
520 M_PARSE *starttagomit ;
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 */
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 ;
543 return(m_strtproc(scanval)) ;
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 ;
552 m_strttag(scanval, m_scannet) ;
553 if (check == M_NONCONTEXTUAL) m_stacktop->contextual = FALSE ;
554 else if (scanval) m_stacktop->oldtop->intext = FALSE ;
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 ;
567 if (check = m_checkstart(scanval)) break ;
570 m_freemin(m_minstart, "start-tag minimization") ;
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 ;
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 ;
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 ;
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") ;
605 if (scanval) return(m_strtproc(scanval)) ;
608 for (min = m_minend ; min ;) {
609 if (m_stacktop->neednet) {
612 wc_net = MakeWideCharString(m_net);
613 m_err2("Expecting %s to end %s",
615 m_nameofelt(m_stacktop->element)) ;
616 m_free(wc_net,"wide character string");
619 if (! m_element[m_stacktop->element - 1].emin) {
620 m_err1("Missing end tag for %s", m_nameofelt(m_stacktop->element)) ;
626 m_free(discard, "end-tag minimization") ;
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() ;
636 m_free(discard, "start-tag minimization") ;
638 check = m_checkstart(scanval) ;
639 if (scanval && check != M_NONCONTEXTUAL) {
640 m_stacktop->linestat = M_DCORCET ;
641 m_stacktop->firstre = TRUE ;
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)
649 if (m_element[m_stacktop->element - 1].content == M_RCDATA) {
650 m_curcon = RCDATAEL ;
651 m_stacktop->thisent = m_eopencnt ;
656 /* Processes explicit or implied start tag*/
658 void m_strttag(M_ELEMENT val, LOGICAL net)
660 void m_strttag(val, net)
665 m_transition(val, TRUE) ;
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 ?
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
686 LOGICAL m_textpermitted(M_NOPAR)
689 M_OPENFSA *fsastack ;
691 LOGICAL morebranches = FALSE ;
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
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
702 for (fsastack = m_stacktop->fsastack ;
704 fsastack = fsastack->oldtop) {
705 for (pand = fsastack->andgroup ;
707 pand = m_andgroup[pand - 1].next) {
708 for (usedand = fsastack->usedand ;
710 usedand = usedand->next)
711 if (usedand->group == pand) break ;
713 if (m_state[m_andgroup[pand - 1].start - 1].datacontent)
715 if (! m_state[m_andgroup[pand - 1].start - 1].final)
716 morebranches = TRUE ;
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) ;
724 } /* End m_textpermitted() */
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. */
730 LOGICAL m_transition(M_ELEMENT label, LOGICAL recur)
732 LOGICAL m_transition(label, recur)
741 if (m_stacktop->oldtop &&
742 m_element[m_stacktop->element - 1].content != M_REGEXP)
744 for (parc = m_state[m_stacktop->fsastack->current - 1].first ;
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 ;
766 m_stacktop->fsastack = newfsa->oldtop ;
767 m_free(newfsa, "FSA") ;
769 else if (label == m_arc[parc - 1].label) {
770 m_stacktop->fsastack->current = m_arc[parc - 1].to ;
774 if (recur && m_nextand(m_stacktop->fsastack, label)) return(TRUE) ;
776 } /* End transition */