1 /* istack.c -- inline stack for compatibility with Mosaic
2 
3   (c) 1998-2006 (W3C) MIT, ERCIM, Keio University
4   See tidy.h for the copyright notice.
5 
6 */
7 
8 #include "tidy-int.h"
9 #include "lexer.h"
10 #include "attrs.h"
11 #include "streamio.h"
12 #include "tmbstr.h"
13 
14 /* duplicate attributes */
TY_(DupAttrs)15 AttVal *TY_(DupAttrs)( TidyDocImpl* doc, AttVal *attrs)
16 {
17     AttVal *newattrs;
18 
19     if (attrs == NULL)
20         return attrs;
21 
22     newattrs = TY_(NewAttribute)(doc);
23     *newattrs = *attrs;
24     newattrs->next = TY_(DupAttrs)( doc, attrs->next );
25     newattrs->attribute = TY_(tmbstrdup)(doc->allocator, attrs->attribute);
26     newattrs->value = TY_(tmbstrdup)(doc->allocator, attrs->value);
27     newattrs->dict = TY_(FindAttribute)(doc, newattrs);
28     newattrs->asp = attrs->asp ? TY_(CloneNode)(doc, attrs->asp) : NULL;
29     newattrs->php = attrs->php ? TY_(CloneNode)(doc, attrs->php) : NULL;
30     return newattrs;
31 }
32 
IsNodePushable(Node * node)33 static Bool IsNodePushable( Node *node )
34 {
35     if (node->tag == NULL)
36         return no;
37 
38     if (!(node->tag->model & CM_INLINE))
39         return no;
40 
41     if (node->tag->model & CM_OBJECT)
42         return no;
43 
44     /*\ Issue #92: OLD problem of ins and del which are marked as both
45      *  inline and block, thus should NOT ever be 'inserted'
46     \*/
47     if (nodeIsINS(node) || nodeIsDEL(node))
48         return no;
49 
50     return yes;
51 }
52 
53 /*
54   push a copy of an inline node onto stack
55   but don't push if implicit or OBJECT or APPLET
56   (implicit tags are ones generated from the istack)
57 
58   One issue arises with pushing inlines when
59   the tag is already pushed. For instance:
60 
61       <p><em>text
62       <p><em>more text
63 
64   Shouldn't be mapped to
65 
66       <p><em>text</em></p>
67       <p><em><em>more text</em></em>
68 */
TY_(PushInline)69 void TY_(PushInline)( TidyDocImpl* doc, Node *node )
70 {
71     Lexer* lexer = doc->lexer;
72     IStack *istack;
73 
74     if (node->implicit)
75         return;
76 
77     if ( !IsNodePushable(node) )
78         return;
79 
80     if ( !nodeIsFONT(node) && TY_(IsPushed)(doc, node) )
81         return;
82 
83     /* make sure there is enough space for the stack */
84     if (lexer->istacksize + 1 > lexer->istacklength)
85     {
86         if (lexer->istacklength == 0)
87             lexer->istacklength = 6;   /* this is perhaps excessive */
88 
89         lexer->istacklength = lexer->istacklength * 2;
90         lexer->istack = (IStack *)TidyDocRealloc(doc, lexer->istack,
91                             sizeof(IStack)*(lexer->istacklength));
92     }
93 
94     istack = &(lexer->istack[lexer->istacksize]);
95     istack->tag = node->tag;
96 
97     istack->element = TY_(tmbstrdup)(doc->allocator, node->element);
98     istack->attributes = TY_(DupAttrs)( doc, node->attributes );
99     ++(lexer->istacksize);
100 }
101 
PopIStack(TidyDocImpl * doc)102 static void PopIStack( TidyDocImpl* doc )
103 {
104     Lexer* lexer = doc->lexer;
105     IStack *istack;
106     AttVal *av;
107 
108     --(lexer->istacksize);
109     istack = &(lexer->istack[lexer->istacksize]);
110 
111     while (istack->attributes)
112     {
113         av = istack->attributes;
114         istack->attributes = av->next;
115         TY_(FreeAttribute)( doc, av );
116     }
117     TidyDocFree(doc, istack->element);
118     istack->element = NULL; /* remove the freed element */
119 }
120 
PopIStackUntil(TidyDocImpl * doc,TidyTagId tid)121 static void PopIStackUntil( TidyDocImpl* doc, TidyTagId tid )
122 {
123     Lexer* lexer = doc->lexer;
124     IStack *istack;
125 
126     while (lexer->istacksize > 0)
127     {
128         PopIStack( doc );
129         istack = &(lexer->istack[lexer->istacksize]);
130         if ( istack->tag->id == tid )
131             break;
132     }
133 }
134 
135 /* pop inline stack */
TY_(PopInline)136 void TY_(PopInline)( TidyDocImpl* doc, Node *node )
137 {
138     Lexer* lexer = doc->lexer;
139 
140     if (node)
141     {
142         if ( !IsNodePushable(node) )
143             return;
144 
145         /* if node is </a> then pop until we find an <a> */
146         if ( nodeIsA(node) )
147         {
148             PopIStackUntil( doc, TidyTag_A );
149             return;
150         }
151     }
152 
153     if (lexer->istacksize > 0)
154     {
155         PopIStack( doc );
156 
157         /* #427822 - fix by Randy Waki 7 Aug 00 */
158         if (lexer->insert >= lexer->istack + lexer->istacksize)
159             lexer->insert = NULL;
160     }
161 }
162 
TY_(IsPushed)163 Bool TY_(IsPushed)( TidyDocImpl* doc, Node *node )
164 {
165     Lexer* lexer = doc->lexer;
166     int i;
167 
168     for (i = lexer->istacksize - 1; i >= 0; --i)
169     {
170         if (lexer->istack[i].tag == node->tag)
171             return yes;
172     }
173 
174     return no;
175 }
176 
177 /*
178    Test whether the last element on the stack has the same type than "node".
179 */
TY_(IsPushedLast)180 Bool TY_(IsPushedLast)( TidyDocImpl* doc, Node *element, Node *node )
181 {
182     Lexer* lexer = doc->lexer;
183 
184     if ( element && !IsNodePushable(element) )
185         return no;
186 
187     if (lexer->istacksize > 0) {
188         if (lexer->istack[lexer->istacksize - 1].tag == node->tag) {
189             return yes;
190         }
191     }
192 
193     return no;
194 }
195 
196 /*
197   This has the effect of inserting "missing" inline
198   elements around the contents of blocklevel elements
199   such as P, TD, TH, DIV, PRE etc. This procedure is
200   called at the start of ParseBlock. when the inline
201   stack is not empty, as will be the case in:
202 
203     <i><h1>italic heading</h1></i>
204 
205   which is then treated as equivalent to
206 
207     <h1><i>italic heading</i></h1>
208 
209   This is implemented by setting the lexer into a mode
210   where it gets tokens from the inline stack rather than
211   from the input stream.
212 */
TY_(InlineDup)213 int TY_(InlineDup)( TidyDocImpl* doc, Node* node )
214 {
215     Lexer* lexer = doc->lexer;
216     int n;
217 
218     if ((n = lexer->istacksize - lexer->istackbase) > 0)
219     {
220         lexer->insert = &(lexer->istack[lexer->istackbase]);
221         lexer->inode = node;
222     }
223 
224     return n;
225 }
226 
227 /*
228  defer duplicates when entering a table or other
229  element where the inlines shouldn't be duplicated
230 */
TY_(DeferDup)231 void TY_(DeferDup)( TidyDocImpl* doc )
232 {
233     doc->lexer->insert = NULL;
234     doc->lexer->inode = NULL;
235 }
236 
TY_(InsertedToken)237 Node *TY_(InsertedToken)( TidyDocImpl* doc )
238 {
239     Lexer* lexer = doc->lexer;
240     Node *node;
241     IStack *istack;
242     uint n;
243 
244     /* this will only be NULL if inode != NULL */
245     if (lexer->insert == NULL)
246     {
247         node = lexer->inode;
248         lexer->inode = NULL;
249         return node;
250     }
251 
252     /*
253       If this is the "latest" node then update
254       the position, otherwise use current values
255     */
256 
257     if (lexer->inode == NULL)
258     {
259         lexer->lines = doc->docIn->curline;
260         lexer->columns = doc->docIn->curcol;
261     }
262 
263     node = TY_(NewNode)(doc->allocator, lexer);
264     node->type = StartTag;
265     node->implicit = yes;
266     node->start = lexer->txtstart;
267     /* #431734 [JTidy bug #226261 (was 126261)] - fix by Gary Peskin 20 Dec 00 */
268     node->end = lexer->txtend; /* was : lexer->txtstart; */
269     istack = lexer->insert;
270 
271 /* #if 0 && defined(_DEBUG) */
272 #if definedENABLE_DEBUG_LOG
273     if ( lexer->istacksize == 0 )
274     {
275         SPRTF( "WARNING: ZERO sized istack!\n" );
276     }
277 #endif
278 
279     node->element = TY_(tmbstrdup)(doc->allocator, istack->element);
280     node->tag = istack->tag;
281     node->attributes = TY_(DupAttrs)( doc, istack->attributes );
282 
283     /* advance lexer to next item on the stack */
284     n = (uint)(lexer->insert - &(lexer->istack[0]));
285 
286     /* and recover state if we have reached the end */
287     if (++n < lexer->istacksize)
288         lexer->insert = &(lexer->istack[n]);
289     else
290         lexer->insert = NULL;
291 
292     return node;
293 }
294 
295 
296 /*
297    We have two CM_INLINE elements pushed ... the first is closing,
298    but, like the browser, the second should be retained ...
299    Like <b>bold <i>bold and italics</b> italics only</i>
300    This function switches the tag positions on the stack,
301    returning 'yes' if both were found in the expected order.
302 */
TY_(SwitchInline)303 Bool TY_(SwitchInline)( TidyDocImpl* doc, Node* element, Node* node )
304 {
305     Lexer* lexer = doc->lexer;
306     if ( lexer
307          && element && element->tag
308          && node && node->tag
309          && TY_(IsPushed)( doc, element )
310          && TY_(IsPushed)( doc, node )
311          && ((lexer->istacksize - lexer->istackbase) >= 2) )
312     {
313         /* we have a chance of succeeding ... */
314         int i;
315         for (i = (lexer->istacksize - lexer->istackbase - 1); i >= 0; --i)
316         {
317             if (lexer->istack[i].tag == element->tag) {
318                 /* found the element tag - phew */
319                 IStack *istack1 = &lexer->istack[i];
320                 IStack *istack2 = NULL;
321                 --i; /* back one more, and continue */
322                 for ( ; i >= 0; --i)
323                 {
324                     if (lexer->istack[i].tag == node->tag)
325                     {
326                         /* found the element tag - phew */
327                         istack2 = &lexer->istack[i];
328                         break;
329                     }
330                 }
331                 if ( istack2 )
332                 {
333                     /* perform the swap */
334                     IStack tmp_istack = *istack2;
335                     *istack2 = *istack1;
336                     *istack1 = tmp_istack;
337                     return yes;
338                 }
339             }
340         }
341     }
342     return no;
343 }
344 
345 /*
346   We want to push a specific a specific element on the stack,
347   but it may not be the last element, which InlineDup()
348   would handle. Return yes, if found and inserted.
349 */
TY_(InlineDup1)350 Bool TY_(InlineDup1)( TidyDocImpl* doc, Node* node, Node* element )
351 {
352     Lexer* lexer = doc->lexer;
353     int n, i;
354     if ( element
355          && (element->tag != NULL)
356          && ((n = lexer->istacksize - lexer->istackbase) > 0) )
357     {
358         for ( i = n - 1; i >=0; --i ) {
359             if (lexer->istack[i].tag == element->tag) {
360                 /* found our element tag - insert it */
361                 lexer->insert = &(lexer->istack[i]);
362                 lexer->inode = node;
363                 return yes;
364             }
365         }
366     }
367     return no;
368 }
369 
370 /*
371  * local variables:
372  * mode: c
373  * indent-tabs-mode: nil
374  * c-basic-offset: 4
375  * eval: (c-set-offset 'substatement-open 0)
376  * end:
377  */
378