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 CVS Info :
7
8 $Author: arnaud02 $
9 $Date: 2006/12/29 16:31:08 $
10 $Revision: 1.21 $
11
12 */
13
14 #include "tidy-int.h"
15 #include "lexer.h"
16 #include "attrs.h"
17 #include "streamio.h"
18 #include "tmbstr.h"
19
20 /* duplicate attributes */
TY_(DupAttrs)21 AttVal *TY_(DupAttrs)( TidyDocImpl* doc, AttVal *attrs)
22 {
23 AttVal *newattrs;
24
25 if (attrs == NULL)
26 return attrs;
27
28 newattrs = TY_(NewAttribute)(doc);
29 *newattrs = *attrs;
30 newattrs->next = TY_(DupAttrs)( doc, attrs->next );
31 newattrs->attribute = TY_(tmbstrdup)(doc->allocator, attrs->attribute);
32 newattrs->value = TY_(tmbstrdup)(doc->allocator, attrs->value);
33 newattrs->dict = TY_(FindAttribute)(doc, newattrs);
34 newattrs->asp = attrs->asp ? TY_(CloneNode)(doc, attrs->asp) : NULL;
35 newattrs->php = attrs->php ? TY_(CloneNode)(doc, attrs->php) : NULL;
36 return newattrs;
37 }
38
IsNodePushable(Node * node)39 static Bool IsNodePushable( Node *node )
40 {
41 if (node->tag == NULL)
42 return no;
43
44 if (!(node->tag->model & CM_INLINE))
45 return no;
46
47 if (node->tag->model & CM_OBJECT)
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 }
119
PopIStackUntil(TidyDocImpl * doc,TidyTagId tid)120 static void PopIStackUntil( TidyDocImpl* doc, TidyTagId tid )
121 {
122 Lexer* lexer = doc->lexer;
123 IStack *istack;
124
125 while (lexer->istacksize > 0)
126 {
127 PopIStack( doc );
128 istack = &(lexer->istack[lexer->istacksize]);
129 if ( istack->tag->id == tid )
130 break;
131 }
132 }
133
134 /* pop inline stack */
TY_(PopInline)135 void TY_(PopInline)( TidyDocImpl* doc, Node *node )
136 {
137 Lexer* lexer = doc->lexer;
138
139 if (node)
140 {
141 if ( !IsNodePushable(node) )
142 return;
143
144 /* if node is </a> then pop until we find an <a> */
145 if ( nodeIsA(node) )
146 {
147 PopIStackUntil( doc, TidyTag_A );
148 return;
149 }
150 }
151
152 if (lexer->istacksize > 0)
153 {
154 PopIStack( doc );
155
156 /* #427822 - fix by Randy Waki 7 Aug 00 */
157 if (lexer->insert >= lexer->istack + lexer->istacksize)
158 lexer->insert = NULL;
159 }
160 }
161
TY_(IsPushed)162 Bool TY_(IsPushed)( TidyDocImpl* doc, Node *node )
163 {
164 Lexer* lexer = doc->lexer;
165 int i;
166
167 for (i = lexer->istacksize - 1; i >= 0; --i)
168 {
169 if (lexer->istack[i].tag == node->tag)
170 return yes;
171 }
172
173 return no;
174 }
175
176 /*
177 Test whether the last element on the stack has the same type than "node".
178 */
TY_(IsPushedLast)179 Bool TY_(IsPushedLast)( TidyDocImpl* doc, Node *element, Node *node )
180 {
181 Lexer* lexer = doc->lexer;
182
183 if ( element && !IsNodePushable(element) )
184 return no;
185
186 if (lexer->istacksize > 0) {
187 if (lexer->istack[lexer->istacksize - 1].tag == node->tag) {
188 return yes;
189 }
190 }
191
192 return no;
193 }
194
195 /*
196 This has the effect of inserting "missing" inline
197 elements around the contents of blocklevel elements
198 such as P, TD, TH, DIV, PRE etc. This procedure is
199 called at the start of ParseBlock. when the inline
200 stack is not empty, as will be the case in:
201
202 <i><h1>italic heading</h1></i>
203
204 which is then treated as equivalent to
205
206 <h1><i>italic heading</i></h1>
207
208 This is implemented by setting the lexer into a mode
209 where it gets tokens from the inline stack rather than
210 from the input stream.
211 */
TY_(InlineDup)212 int TY_(InlineDup)( TidyDocImpl* doc, Node* node )
213 {
214 Lexer* lexer = doc->lexer;
215 int n;
216
217 if ((n = lexer->istacksize - lexer->istackbase) > 0)
218 {
219 lexer->insert = &(lexer->istack[lexer->istackbase]);
220 lexer->inode = node;
221 }
222
223 return n;
224 }
225
226 /*
227 defer duplicates when entering a table or other
228 element where the inlines shouldn't be duplicated
229 */
TY_(DeferDup)230 void TY_(DeferDup)( TidyDocImpl* doc )
231 {
232 doc->lexer->insert = NULL;
233 doc->lexer->inode = NULL;
234 }
235
TY_(InsertedToken)236 Node *TY_(InsertedToken)( TidyDocImpl* doc )
237 {
238 Lexer* lexer = doc->lexer;
239 Node *node;
240 IStack *istack;
241 uint n;
242
243 /* this will only be NULL if inode != NULL */
244 if (lexer->insert == NULL)
245 {
246 node = lexer->inode;
247 lexer->inode = NULL;
248 return node;
249 }
250
251 /*
252 If this is the "latest" node then update
253 the position, otherwise use current values
254 */
255
256 if (lexer->inode == NULL)
257 {
258 lexer->lines = doc->docIn->curline;
259 lexer->columns = doc->docIn->curcol;
260 }
261
262 node = TY_(NewNode)(doc->allocator, lexer);
263 node->type = StartTag;
264 node->implicit = yes;
265 node->start = lexer->txtstart;
266 /* #431734 [JTidy bug #226261 (was 126261)] - fix by Gary Peskin 20 Dec 00 */
267 node->end = lexer->txtend; /* was : lexer->txtstart; */
268 istack = lexer->insert;
269
270 #if 0 && defined(_DEBUG)
271 if ( lexer->istacksize == 0 )
272 fprintf( stderr, "0-size istack!\n" );
273 #endif
274
275 node->element = TY_(tmbstrdup)(doc->allocator, istack->element);
276 node->tag = istack->tag;
277 node->attributes = TY_(DupAttrs)( doc, istack->attributes );
278
279 /* advance lexer to next item on the stack */
280 n = (uint)(lexer->insert - &(lexer->istack[0]));
281
282 /* and recover state if we have reached the end */
283 if (++n < lexer->istacksize)
284 lexer->insert = &(lexer->istack[n]);
285 else
286 lexer->insert = NULL;
287
288 return node;
289 }
290
291
292 /*
293 We have two CM_INLINE elements pushed ... the first is closing,
294 but, like the browser, the second should be retained ...
295 Like <b>bold <i>bold and italics</b> italics only</i>
296 This function switches the tag positions on the stack,
297 returning 'yes' if both were found in the expected order.
298 */
TY_(SwitchInline)299 Bool TY_(SwitchInline)( TidyDocImpl* doc, Node* element, Node* node )
300 {
301 Lexer* lexer = doc->lexer;
302 if ( lexer
303 && element && element->tag
304 && node && node->tag
305 && TY_(IsPushed)( doc, element )
306 && TY_(IsPushed)( doc, node )
307 && ((lexer->istacksize - lexer->istackbase) >= 2) )
308 {
309 /* we have a chance of succeeding ... */
310 int i;
311 for (i = (lexer->istacksize - lexer->istackbase - 1); i >= 0; --i)
312 {
313 if (lexer->istack[i].tag == element->tag) {
314 /* found the element tag - phew */
315 IStack *istack1 = &lexer->istack[i];
316 IStack *istack2 = NULL;
317 --i; /* back one more, and continue */
318 for ( ; i >= 0; --i)
319 {
320 if (lexer->istack[i].tag == node->tag)
321 {
322 /* found the element tag - phew */
323 istack2 = &lexer->istack[i];
324 break;
325 }
326 }
327 if ( istack2 )
328 {
329 /* perform the swap */
330 IStack tmp_istack = *istack2;
331 *istack2 = *istack1;
332 *istack1 = tmp_istack;
333 return yes;
334 }
335 }
336 }
337 }
338 return no;
339 }
340
341 /*
342 We want to push a specific a specific element on the stack,
343 but it may not be the last element, which InlineDup()
344 would handle. Return yes, if found and inserted.
345 */
TY_(InlineDup1)346 Bool TY_(InlineDup1)( TidyDocImpl* doc, Node* node, Node* element )
347 {
348 Lexer* lexer = doc->lexer;
349 int n, i;
350 if ( element
351 && (element->tag != NULL)
352 && ((n = lexer->istacksize - lexer->istackbase) > 0) )
353 {
354 for ( i = n - 1; i >=0; --i ) {
355 if (lexer->istack[i].tag == element->tag) {
356 /* found our element tag - insert it */
357 lexer->insert = &(lexer->istack[i]);
358 lexer->inode = node;
359 return yes;
360 }
361 }
362 }
363 return no;
364 }
365
366 /*
367 * local variables:
368 * mode: c
369 * indent-tabs-mode: nil
370 * c-basic-offset: 4
371 * eval: (c-set-offset 'substatement-open 0)
372 * end:
373 */
374