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$
9    $Date$
10    $Revision$
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 */
21AttVal *TY_(DupAttrs)( TidyDocImpl* doc, AttVal *attrs)
22{
23    AttVal *newattrs;
24
25    if (attrs == NULL)
26        return attrs;
27
28    newattrs = TY_(NewAttribute)();
29    *newattrs = *attrs;
30    newattrs->next = TY_(DupAttrs)( doc, attrs->next );
31    newattrs->attribute = TY_(tmbstrdup)(attrs->attribute);
32    newattrs->value = TY_(tmbstrdup)(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
39static 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*/
69void 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 *)MemRealloc(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)(node->element);
98    istack->attributes = TY_(DupAttrs)( doc, node->attributes );
99    ++(lexer->istacksize);
100}
101
102static 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    MemFree(istack->element);
118}
119
120static 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 */
135void 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
162Bool 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*/
179Bool 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*/
212int 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*/
230void TY_(DeferDup)( TidyDocImpl* doc )
231{
232    doc->lexer->insert = NULL;
233    doc->lexer->inode = NULL;
234}
235
236Node *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)(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)(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 * local variables:
293 * mode: c
294 * indent-tabs-mode: nil
295 * c-basic-offset: 4
296 * eval: (c-set-offset 'substatement-open 0)
297 * end:
298 */
299