Coverage Report

Created: 2025-11-11 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tidy-html5/src/istack.c
Line
Count
Source
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 */
15
AttVal *TY_(DupAttrs)( TidyDocImpl* doc, AttVal *attrs)
16
2.12M
{
17
2.12M
    AttVal *newattrs;
18
19
2.12M
    if (attrs == NULL)
20
1.85M
        return attrs;
21
22
275k
    newattrs = TY_(NewAttribute)(doc);
23
275k
    *newattrs = *attrs;
24
275k
    newattrs->next = TY_(DupAttrs)( doc, attrs->next );
25
275k
    newattrs->attribute = TY_(tmbstrdup)(doc->allocator, attrs->attribute);
26
275k
    newattrs->value = TY_(tmbstrdup)(doc->allocator, attrs->value);
27
275k
    newattrs->dict = TY_(FindAttribute)(doc, newattrs);
28
275k
    newattrs->asp = attrs->asp ? TY_(CloneNode)(doc, attrs->asp) : NULL;
29
275k
    newattrs->php = attrs->php ? TY_(CloneNode)(doc, attrs->php) : NULL;
30
275k
    return newattrs;
31
2.12M
}
32
33
static Bool IsNodePushable( Node *node )
34
231k
{
35
231k
    if (node->tag == NULL)
36
0
        return no;
37
38
231k
    if (!(node->tag->model & CM_INLINE))
39
767
        return no;
40
41
230k
    if (node->tag->model & CM_OBJECT)
42
487
        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
230k
    if (nodeIsINS(node) || nodeIsDEL(node))
48
3.60k
        return no;
49
50
226k
    return yes;
51
230k
}
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
*/
69
void TY_(PushInline)( TidyDocImpl* doc, Node *node )
70
1.88M
{
71
1.88M
    Lexer* lexer = doc->lexer;
72
1.88M
    IStack *istack;
73
74
1.88M
    if (node->implicit)
75
1.69M
        return;
76
77
187k
    if ( !IsNodePushable(node) )
78
2.94k
        return;
79
80
184k
    if ( !nodeIsFONT(node) && TY_(IsPushed)(doc, node) )
81
30.2k
        return;
82
83
    /* make sure there is enough space for the stack */
84
154k
    if (lexer->istacksize + 1 > lexer->istacklength)
85
6.59k
    {
86
6.59k
        if (lexer->istacklength == 0)
87
5.59k
            lexer->istacklength = 6;   /* this is perhaps excessive */
88
89
6.59k
        lexer->istacklength = lexer->istacklength * 2;
90
6.59k
        lexer->istack = (IStack *)TidyDocRealloc(doc, lexer->istack,
91
6.59k
                            sizeof(IStack)*(lexer->istacklength));
92
6.59k
    }
93
94
154k
    istack = &(lexer->istack[lexer->istacksize]);
95
154k
    istack->tag = node->tag;
96
97
154k
    istack->element = TY_(tmbstrdup)(doc->allocator, node->element);
98
154k
    istack->attributes = TY_(DupAttrs)( doc, node->attributes );
99
154k
    ++(lexer->istacksize);
100
154k
}
101
102
static void PopIStack( TidyDocImpl* doc )
103
154k
{
104
154k
    Lexer* lexer = doc->lexer;
105
154k
    IStack *istack;
106
154k
    AttVal *av;
107
108
154k
    --(lexer->istacksize);
109
154k
    istack = &(lexer->istack[lexer->istacksize]);
110
111
164k
    while (istack->attributes)
112
9.42k
    {
113
9.42k
        av = istack->attributes;
114
9.42k
        istack->attributes = av->next;
115
9.42k
        TY_(FreeAttribute)( doc, av );
116
9.42k
    }
117
154k
    TidyDocFree(doc, istack->element);
118
154k
    istack->element = NULL; /* remove the freed element */
119
154k
}
120
121
static void PopIStackUntil( TidyDocImpl* doc, TidyTagId tid )
122
11.9k
{
123
11.9k
    Lexer* lexer = doc->lexer;
124
11.9k
    IStack *istack;
125
126
108k
    while (lexer->istacksize > 0)
127
105k
    {
128
105k
        PopIStack( doc );
129
105k
        istack = &(lexer->istack[lexer->istacksize]);
130
105k
        if ( istack->tag->id == tid )
131
8.97k
            break;
132
105k
    }
133
11.9k
}
134
135
/* pop inline stack */
136
void TY_(PopInline)( TidyDocImpl* doc, Node *node )
137
63.8k
{
138
63.8k
    Lexer* lexer = doc->lexer;
139
140
63.8k
    if (node)
141
21.0k
    {
142
21.0k
        if ( !IsNodePushable(node) )
143
833
            return;
144
145
        /* if node is </a> then pop until we find an <a> */
146
20.2k
        if ( nodeIsA(node) )
147
11.9k
        {
148
11.9k
            PopIStackUntil( doc, TidyTag_A );
149
11.9k
            return;
150
11.9k
        }
151
20.2k
    }
152
153
51.0k
    if (lexer->istacksize > 0)
154
49.0k
    {
155
49.0k
        PopIStack( doc );
156
157
        /* #427822 - fix by Randy Waki 7 Aug 00 */
158
49.0k
        if (lexer->insert >= lexer->istack + lexer->istacksize)
159
217
            lexer->insert = NULL;
160
49.0k
    }
161
51.0k
}
162
163
Bool TY_(IsPushed)( TidyDocImpl* doc, Node *node )
164
4.56M
{
165
4.56M
    Lexer* lexer = doc->lexer;
166
4.56M
    int i;
167
168
306M
    for (i = lexer->istacksize - 1; i >= 0; --i)
169
305M
    {
170
305M
        if (lexer->istack[i].tag == node->tag)
171
3.53M
            return yes;
172
305M
    }
173
174
1.02M
    return no;
175
4.56M
}
176
177
/*
178
   Test whether the last element on the stack has the same type than "node".
179
*/
180
Bool TY_(IsPushedLast)( TidyDocImpl* doc, Node *element, Node *node )
181
22.5k
{
182
22.5k
    Lexer* lexer = doc->lexer;
183
184
22.5k
    if ( element && !IsNodePushable(element) )
185
1.07k
        return no;
186
187
21.4k
    if (lexer->istacksize > 0) {
188
21.0k
        if (lexer->istack[lexer->istacksize - 1].tag == node->tag) {
189
0
            return yes;
190
0
        }
191
21.0k
    }
192
193
21.4k
    return no;
194
21.4k
}
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
*/
213
int TY_(InlineDup)( TidyDocImpl* doc, Node* node )
214
331k
{
215
331k
    Lexer* lexer = doc->lexer;
216
331k
    int n;
217
218
331k
    if ((n = lexer->istacksize - lexer->istackbase) > 0)
219
135k
    {
220
135k
        lexer->insert = &(lexer->istack[lexer->istackbase]);
221
135k
        lexer->inode = node;
222
135k
    }
223
224
331k
    return n;
225
331k
}
226
227
/*
228
 defer duplicates when entering a table or other
229
 element where the inlines shouldn't be duplicated
230
*/
231
void TY_(DeferDup)( TidyDocImpl* doc )
232
28.5k
{
233
28.5k
    doc->lexer->insert = NULL;
234
28.5k
    doc->lexer->inode = NULL;
235
28.5k
}
236
237
Node *TY_(InsertedToken)( TidyDocImpl* doc )
238
1.69M
{
239
1.69M
    Lexer* lexer = doc->lexer;
240
1.69M
    Node *node;
241
1.69M
    IStack *istack;
242
1.69M
    uint n;
243
244
    /* this will only be NULL if inode != NULL */
245
1.69M
    if (lexer->insert == NULL)
246
3.89k
    {
247
3.89k
        node = lexer->inode;
248
3.89k
        lexer->inode = NULL;
249
3.89k
        return node;
250
3.89k
    }
251
252
    /*
253
      If this is the "latest" node then update
254
      the position, otherwise use current values
255
    */
256
257
1.69M
    if (lexer->inode == NULL)
258
1.64M
    {
259
1.64M
        lexer->lines = doc->docIn->curline;
260
1.64M
        lexer->columns = doc->docIn->curcol;
261
1.64M
    }
262
263
1.69M
    node = TY_(NewNode)(doc->allocator, lexer);
264
1.69M
    node->type = StartTag;
265
1.69M
    node->implicit = yes;
266
1.69M
    node->start = lexer->txtstart;
267
    /* #431734 [JTidy bug #226261 (was 126261)] - fix by Gary Peskin 20 Dec 00 */ 
268
1.69M
    node->end = lexer->txtend; /* was : lexer->txtstart; */
269
1.69M
    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
1.69M
    node->element = TY_(tmbstrdup)(doc->allocator, istack->element);
280
1.69M
    node->tag = istack->tag;
281
1.69M
    node->attributes = TY_(DupAttrs)( doc, istack->attributes );
282
283
    /* advance lexer to next item on the stack */
284
1.69M
    n = (uint)(lexer->insert - &(lexer->istack[0]));
285
286
    /* and recover state if we have reached the end */
287
1.69M
    if (++n < lexer->istacksize)
288
1.57M
        lexer->insert = &(lexer->istack[n]);
289
121k
    else
290
121k
        lexer->insert = NULL;
291
292
1.69M
    return node;
293
1.69M
}
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
*/
303
Bool TY_(SwitchInline)( TidyDocImpl* doc, Node* element, Node* node )
304
2.54k
{
305
2.54k
    Lexer* lexer = doc->lexer;
306
2.54k
    if ( lexer
307
2.54k
         && element && element->tag
308
2.54k
         && node && node->tag
309
2.54k
         && TY_(IsPushed)( doc, element )
310
2.54k
         && TY_(IsPushed)( doc, node ) 
311
2.54k
         && ((lexer->istacksize - lexer->istackbase) >= 2) )
312
2.24k
    {
313
        /* we have a chance of succeeding ... */
314
2.24k
        int i;
315
15.6k
        for (i = (lexer->istacksize - lexer->istackbase - 1); i >= 0; --i)
316
14.2k
        {
317
14.2k
            if (lexer->istack[i].tag == element->tag) {
318
                /* found the element tag - phew */
319
1.85k
                IStack *istack1 = &lexer->istack[i];
320
1.85k
                IStack *istack2 = NULL;
321
1.85k
                --i; /* back one more, and continue */
322
39.4k
                for ( ; i >= 0; --i)
323
38.4k
                {
324
38.4k
                    if (lexer->istack[i].tag == node->tag)
325
908
                    {
326
                        /* found the element tag - phew */
327
908
                        istack2 = &lexer->istack[i];
328
908
                        break;
329
908
                    }
330
38.4k
                }
331
1.85k
                if ( istack2 )
332
908
                {
333
                    /* perform the swap */
334
908
                    IStack tmp_istack = *istack2;
335
908
                    *istack2 = *istack1;
336
908
                    *istack1 = tmp_istack;
337
908
                    return yes;
338
908
                }
339
1.85k
            }
340
14.2k
        }
341
2.24k
    }
342
1.63k
    return no;
343
2.54k
}
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
*/
350
Bool TY_(InlineDup1)( TidyDocImpl* doc, Node* node, Node* element )
351
908
{
352
908
    Lexer* lexer = doc->lexer;
353
908
    int n, i;
354
908
    if ( element
355
908
         && (element->tag != NULL)
356
908
         && ((n = lexer->istacksize - lexer->istackbase) > 0) )
357
908
    {
358
8.58k
        for ( i = n - 1; i >=0; --i ) {
359
8.58k
            if (lexer->istack[i].tag == element->tag) {
360
                /* found our element tag - insert it */
361
908
                lexer->insert = &(lexer->istack[i]);
362
908
                lexer->inode = node;
363
908
                return yes;
364
908
            }
365
8.58k
        }
366
908
    }
367
0
    return no;
368
908
}
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
 */