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