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: minim.c /main/3 1995/11/08 11:25:32 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 /* Minim.c contains procedures relevant to tag minimization */
44 /* M_expecting reports to the user the possible valid content at a particular
45 state in the parse of the document */
46 void m_expecting(M_NOPAR)
48 LOGICAL expstart = TRUE ;
53 LOGICAL required = FALSE ;
54 LOGICAL data = FALSE ;
57 if (m_stacktop->intext) m_expline(&expstart, &data, M_NULLVAL) ;
58 for (stackptr = m_stacktop ; stackptr ; stackptr = stackptr->oldtop) {
59 if (m_explimit && m_expcount > M_EXPLIMIT) return ;
60 if (m_start && ! stackptr->oldtop) return ;
61 /* First check for possible start-tags.
62 Begin by testing if at start of document or not within a
63 CDATA or RCDATA element. */
64 if (! stackptr->oldtop ||
65 m_element[stackptr->element - 1].content == M_REGEXP) {
66 /* Note the following statement, which checks the type of the
67 element at the top of the stack, is not a repeat of the previous
68 one, which checks the type of an element embedded in the stack.
69 The second comparison prevents traversing paths from
70 a parent of an RCDATA or CDATA element and still allows displaying
71 the end-tag of the parent */
72 if (! stackptr->oldtop ||
73 m_element[m_stacktop->element - 1].content == M_REGEXP)
74 for (fsastack = stackptr->fsastack ;
76 fsastack = fsastack->oldtop) {
77 for (pand = fsastack->andgroup ;
79 pand = m_andgroup[pand - 1].next) {
80 for (usedand = fsastack->usedand ;
82 usedand = usedand->next)
83 if (usedand->group == pand) break ;
85 m_expexpand(&expstart, m_andgroup[pand - 1].start, &required,
88 if (required) return ;
89 m_expexpand(&expstart, fsastack->current, &required, &data) ;
90 if (! m_state[fsastack->current - 1].final) return ;
93 else if (m_element[stackptr->element - 1].content == M_CDATA ||
94 m_element[stackptr->element - 1].content == M_RCDATA)
95 m_expline(&expstart, &data, M_NULLVAL) ;
96 if (m_explimit && m_expcount > M_EXPLIMIT) return ;
97 /* Now report the end-tag */
98 m_exptend(&expstart, stackptr) ;
99 if (! m_element[stackptr->element - 1].emin) return ;
103 /* Recursive procedure first called from expecting() to display
104 names of elements reachable from a particular node */
105 void m_expexpand(expstart, node, required, data)
114 for (parc = m_state[node - 1].first ;
116 parc = m_arc[parc - 1].next) {
117 if (m_explimit && m_expcount > M_EXPLIMIT) return ;
118 if (m_arc[parc - 1].group)
119 for (pand = m_arc[parc - 1].group ;
121 pand = m_andgroup[pand - 1].next)
122 m_expexpand(expstart, m_andgroup[pand - 1].start, required, data) ;
124 if (! m_state[node - 1].final) *required = TRUE ;
125 m_expline(expstart, data, m_arc[parc - 1].label) ;
130 /* M_expline writes one line for m_expecting() */
131 void m_expline(expstart, data, label)
136 char buffer[M_NAMELEN + 2*MAXD + 1] ;
138 if (! label && *data) return ;
139 if (m_excluded(label)) return ;
141 sprintf(buffer, "Expecting ") ;
146 sprintf(buffer, " or ") ;
149 if (m_explimit && m_expcount == M_EXPLIMIT) {
150 sprintf(buffer, ". . .\n") ;
156 mb_enptr = MakeMByteString(&m_ename[m_element[label - 1].enptr]);
157 sprintf(buffer, "%s%s%s\n", m_stago, mb_enptr, m_tagc) ;
158 m_free(mb_enptr,"multi-byte string");
162 sprintf(buffer, "data characters\n") ;
169 /* M_exptend is called from m_expecting to inform the user after an
170 error if an end tag is permitted */
171 void m_exptend(expstart, stackptr)
175 char buffer[M_NAMELEN + 2*MAXD + 1] ;
178 sprintf(buffer, "Expecting ") ;
183 sprintf(buffer, " or ") ;
186 if (m_explimit && m_expcount == M_EXPLIMIT) {
187 sprintf(buffer, ". . .\n") ;
190 else if (stackptr->neednet) {
191 sprintf(buffer, "%s\n", m_net) ;
198 MakeMByteString(&m_ename[m_element[stackptr->element - 1].enptr]);
199 sprintf(buffer, "%s%s%s\n", m_etago, mb_enptr, m_tagc) ;
200 m_free(mb_enptr,"multi-byte string");
206 /* M_findunique is used to test for start tag minimization. If the current
207 parse state permits at least one element with explicit start-tag
208 minimization, the left-most such element to occur in the content model
209 is returned. Otherwise, the contextually-required element, if any,
210 is returned. Finally, if the parse state permits a unique valid element,
211 and the flag for conformance to ISO 8879 is not set, the unique valid
212 element is returned by m_findunique.
214 Before returning, m_findunique verifies that the element to be returned
215 permits start-tag minimization. If not, the value is returned only if
216 conformance to ISO 8879 is set.
218 Actually m_findunique returns 1 greater than the index of the unique
219 element, 1 if character data is expected, and 0 (FALSE) if there is
222 M_ELEMENT m_findunique(from, newleft)
227 M_ELEMENT cr = 0, minim = 0;
228 int leftmost = M_BIGINT ;
229 int testleft = M_BIGINT ;
233 for (parc = m_state[from - 1].first ;
235 parc = m_arc[parc - 1].next) {
236 if (m_arc[parc - 1].group) {
238 for (pand = m_arc[parc - 1].group ;
240 pand = m_andgroup[pand - 1].next) {
241 testminim = m_findunique(m_andgroup[pand - 1].start, &testleft) ;
242 if (testminim && testleft < leftmost) {
244 leftmost = testleft ;
250 if (m_arc[parc - 1].minim &&
251 m_arc[parc - 1].minim < leftmost &&
252 ! m_excluded(m_arc[parc - 1].label)) {
253 /* Save the explicitly minimizable element plus its position
254 in the content model */
255 leftmost = m_arc[parc - 1].minim ;
256 minim = m_arc[parc - 1].label + 1 ;
257 } /* End arc.minim > leftmost */
258 else if (m_arc[parc - 1].optional &&
259 parc == m_state[from - 1].first &&
260 ! m_arc[parc - 1].next &&
261 m_element[m_arc[parc - 1].label -1].smin &&
262 ! m_excluded(m_arc[parc - 1].label))
263 /* Save the only element that can occur */
264 cr = m_arc[parc - 1].label ;
265 } /* End if (! m_conform) */
266 /* Save the contextually-required element */
267 if (! m_arc[parc - 1].optional && ! m_excluded(m_arc[parc - 1].label))
268 cr = m_arc[parc - 1].label ;
269 } /* End if (! group) */
271 *newleft = leftmost ;
272 if (minim) return(minim) ;
273 if (cr) return(cr + 1) ;
277 /* M_nullendtag is called when a null end tag is encountered; i.e., at the
278 end of a short element */
279 void m_nullendtag(M_NOPAR)
283 while (m_stacktop->oldtop) {
284 foundnet = m_stacktop->neednet ;
285 if (! foundnet && ! m_element[m_stacktop->element - 1].emin) {
286 m_err1("Missing end tag for %s",
287 m_nameofelt(m_stacktop->element)) ;
290 if (! m_ckend(m_stacktop->element, foundnet)) {
293 wc_found = MakeWideCharString(foundnet ? "Null" : "Implied");
294 m_err2("%s end tag for %s unexpected",
296 m_nameofelt(m_stacktop->element)) ;
297 m_free(wc_found,"wide character string");
300 m_frcend(m_stacktop->element) ;
302 if (foundnet) return ;
304 m_error("Internal error: Invalid stack in Nullendtag") ;
308 /* Tests to see if an end tag may have been omitted at this point in the
310 LOGICAL m_omitend(M_NOPAR)
313 M_OPENFSA *fsastack ;
316 if (! m_stacktop->oldtop) return(FALSE) ;
317 if (m_element[m_stacktop->element - 1].content != M_REGEXP) return(TRUE) ;
318 for (fsastack = m_stacktop->fsastack ;
320 fsastack = fsastack->oldtop) {
321 for (pand = fsastack->andgroup ;
323 pand = m_andgroup[pand - 1].next) {
324 /* Doesn't matter if optional submodel of and-group has occurred */
325 if (m_state[m_andgroup[pand - 1].start - 1].final) continue ;
326 for (usedand = fsastack->usedand ;
328 usedand = usedand->next)
329 if (usedand->group == pand) break ;
330 /* Required submodel of and-group has not occurred */
331 if (! usedand) return(FALSE) ;
333 /* Current FSA not in final state */
334 if (! m_state[fsastack->current - 1].final) return(FALSE) ;
336 *m_nextme = (M_MIN *) m_malloc(sizeof(M_MIN), "end-tag minimization") ;
337 (*m_nextme)->val = m_stacktop->element ;
338 (*m_nextme)->next = NULL ;
339 m_nextme = &(*m_nextme)->next ;
343 /* Tests to see if a start tag may have been omitted at this point of
344 the parse. If so, saves the element name in the MINVAL array*/
345 LOGICAL m_omitstart()
347 M_ELEMENT c = M_NULLVAL ;
349 /* int par ; (used in commented-out code below) */
350 M_OPENFSA *fsastack ;
353 int leftmost = M_BIGINT ;
354 int newleft = M_BIGINT ;
355 M_ELEMENT newc = M_NULLVAL ;
356 LOGICAL required = FALSE ;
359 /* Make sure are in an element that has a content model */
360 if (m_stacktop->oldtop &&
361 m_element[m_stacktop->element - 1].content != M_REGEXP)
364 /* Test for unique element expected, or only allowed token is #PCDATA */
365 for (fsastack = m_stacktop->fsastack ;
367 fsastack = fsastack->oldtop) {
368 for (pand = fsastack->andgroup ;
370 pand = m_andgroup[pand - 1].next) {
371 for (usedand = fsastack->usedand ;
373 usedand = usedand->next)
374 if (usedand->group == pand) break ;
376 if (! m_state[m_andgroup[pand - 1].start - 1].final)
378 newc = m_findunique(m_andgroup[pand - 1].start, &newleft) ;
379 if (newleft < leftmost) {
386 newc = m_findunique(fsastack->current, &newleft) ;
387 if (newleft < leftmost) {
393 if (fsastack == m_stacktop->fsastack && newc) {
397 if (m_conform) return(FALSE) ;
398 if (! m_state[fsastack->current - 1].final) return(FALSE) ;
400 if (! c) return(FALSE) ;
402 /* Have found a unique element. Can its start-tag be omitted? */
404 if (m_element[c - 1].content == M_NONE) return(FALSE) ;
405 if (m_element[c - 1].content == M_CDATA) return(FALSE) ;
406 if (m_element[c - 1].content == M_RCDATA) return(FALSE) ;
408 /* Following code allows start-tag to be omitted only if all required
409 parameters are specified:
410 for (par = m_element[c - 1].parptr ; par ;
411 par = m_parameter[par - 1].next)
412 if (m_parameter[par - 1].deftype == M_REQUIRED) return(FALSE) ;
415 /* Check for recursive sequences of omitted tags */
416 for (min = m_minstart ; min ; min = min->next)
417 if (c == min->val) return(FALSE) ;
419 m_push(c, m_element[c - 1].start, FALSE) ;
420 *m_nextms = (M_MIN *) m_malloc(sizeof(M_MIN), "start-tag minimization") ;
421 (*m_nextms)->val = m_stacktop->element ;
422 (*m_nextms)->next = NULL ;
423 m_nextms = &(*m_nextms)->next ;