~ubuntu-branches/ubuntu/gutsy/tidy/gutsy

« back to all changes in this revision

Viewing changes to src/pprint.c

  • Committer: Bazaar Package Importer
  • Author(s): Jason Thomas
  • Date: 2002-03-08 10:58:30 UTC
  • Revision ID: james.westby@ubuntu.com-20020308105830-he5azqenf1sq4jak
Tags: 20020301-1
* New upstream version.
* fix '--add-xml-decl yes --add-xml-space yes' crash bug
  thanks to  Cesar Eduardo Barros <cesarb@nitnet.com.br>
  and upstream.
  (closes: #137124)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  pprint.c -- pretty print parse tree  
 
3
  
 
4
  (c) 1998-2001 (W3C) MIT, INRIA, Keio University
 
5
  See tidy.c for the copyright notice.
 
6
  
 
7
  CVS Info :
 
8
 
 
9
    $Author: creitzel $ 
 
10
    $Date: 2002/03/01 03:40:37 $ 
 
11
    $Revision: 1.35 $ 
 
12
 
 
13
*/
 
14
 
 
15
#include <stdio.h>
 
16
#include <stdlib.h>
 
17
#include <string.h>
 
18
#include "platform.h"
 
19
#include "html.h"
 
20
 
 
21
/*
 
22
  Block-level and unknown elements are printed on
 
23
  new lines and their contents indented 2 spaces
 
24
 
 
25
  Inline elements are printed inline.
 
26
 
 
27
  Inline content is wrapped on spaces (except in
 
28
  attribute values or preformatted text, after
 
29
  start tags and before end tags
 
30
*/
 
31
 
 
32
static void PPrintAsp(Out *fout, uint indent,
 
33
                   Lexer *lexer, Node *node);
 
34
static void PPrintJste(Out *fout, uint indent,
 
35
                   Lexer *lexer, Node *node);
 
36
static void PPrintPhp(Out *fout, uint indent,
 
37
                   Lexer *lexer, Node *node);
 
38
 
 
39
 
 
40
#define NORMAL        0
 
41
#define PREFORMATTED  1
 
42
#define COMMENT       2
 
43
#define ATTRIBVALUE   4
 
44
#define NOWRAP        8
 
45
#define CDATA         16
 
46
 
 
47
extern int CharEncoding;
 
48
extern int inCharEncoding;
 
49
extern int outCharEncoding;
 
50
 
 
51
static uint *linebuf;
 
52
static uint lbufsize;
 
53
static uint linelen;
 
54
static uint wraphere;
 
55
static Bool InAttVal;
 
56
static Bool InString;
 
57
 
 
58
static int slide, count;
 
59
static Node *slidecontent;
 
60
 
 
61
#define AddAsciiString( s, llen )\
 
62
do {\
 
63
  char* cp;\
 
64
  for (cp=s; *cp; ++cp)\
 
65
    AddC( (uint) *cp, llen++ );\
 
66
} while (0)
 
67
 
 
68
#if SUPPORT_ASIAN_ENCODINGS
 
69
 
 
70
/* #431953 - start RJ Wraplen adjusted for smooth international ride */
 
71
uint CWrapLen(uint ind)
 
72
{
 
73
 
 
74
    if ( !wstrcasecmp(Language, "zh") )
 
75
        /* Chinese characters take two positions on a fixed-width screen */ 
 
76
        /* It would be more accurate to keep a parallel linelen and wraphere
 
77
           incremented by 2 for Chinese characters and 1 otherwise, but this
 
78
           is way simpler.
 
79
        */
 
80
        return (ind + (( wraplen - ind ) / 2)) ; 
 
81
    
 
82
    if ( !wstrcasecmp(Language, "ja") )
 
83
        /* average Japanese text is 30% kanji */
 
84
        return (ind + ((( wraplen - ind ) * 7) / 10)) ; 
 
85
    
 
86
    return (wraplen);
 
87
}
 
88
/* #431953 - end RJ */
 
89
 
 
90
#endif
 
91
 
 
92
/* return one less than the number of bytes used by the UTF-8 byte sequence */
 
93
/* str points to the UTF-8 byte sequence */
 
94
/* the Unicode char is returned in *ch */
 
95
uint GetUTF8(unsigned char *str, uint *ch)
 
96
{
 
97
    uint n;
 
98
    int bytes;
 
99
 
 
100
#if 0
 
101
    uint i, c = str[0];
 
102
 
 
103
    if ((c & 0xE0) == 0xC0)  /* 110X XXXX  two bytes */
 
104
    {
 
105
        n = c & 31;
 
106
        bytes = 2;
 
107
    }
 
108
    else if ((c & 0xF0) == 0xE0)  /* 1110 XXXX  three bytes */
 
109
    {
 
110
        n = c & 15;
 
111
        bytes = 3;
 
112
    }
 
113
    else if ((c & 0xF8) == 0xF0)  /* 1111 0XXX  four bytes */
 
114
    {
 
115
        n = c & 7;
 
116
        bytes = 4;
 
117
    }
 
118
    else if ((c & 0xFC) == 0xF8)  /* 1111 10XX  five bytes */
 
119
    {
 
120
        n = c & 3;
 
121
        bytes = 5;
 
122
    }
 
123
    else if ((c & 0xFE) == 0xFC)       /* 1111 110X  six bytes */
 
124
 
 
125
    {
 
126
        n = c & 1;
 
127
        bytes = 6;
 
128
    }
 
129
    else  /* 0XXX XXXX one byte */
 
130
    {
 
131
        *ch = c;
 
132
        return 0;
 
133
    }
 
134
 
 
135
    /* successor bytes should have the form 10XX XXXX */
 
136
    for (i = 1; i < bytes; ++i)
 
137
    {
 
138
        c = str[i];
 
139
        n = (n << 6) | (c & 0x3F);
 
140
    }
 
141
#else
 
142
    int err;
 
143
    
 
144
    bytes = 0;
 
145
    
 
146
    /* first byte "str[0]" is passed in separately from the */
 
147
    /* rest of the UTF-8 byte sequence starting at "str[1]" */
 
148
    err = DecodeUTF8BytesToChar(&n, str[0], (unsigned char *)&str[1], null, null, &bytes);
 
149
    if (err)
 
150
    {
 
151
#if 0
 
152
        extern FILE* errout; /* debug */
 
153
 
 
154
        tidy_out(errout, "pprint UTF-8 decoding error for U+%x : ", n); /* debug */
 
155
#endif
 
156
        n = 0xFFFD; /* replacement char */
 
157
    }
 
158
#endif
 
159
 
 
160
    *ch = n;
 
161
    return bytes - 1;
 
162
}
 
163
 
 
164
/* store char c as UTF-8 encoded byte stream */
 
165
char *PutUTF8(char *buf, uint c)
 
166
{
 
167
#if 0
 
168
    if (c < 128)
 
169
        *buf++ = c;
 
170
    else if (c <= 0x7FF)
 
171
    {
 
172
        *buf++ =  (0xC0 | (c >> 6));
 
173
        *buf++ = (0x80 | (c & 0x3F));
 
174
    }
 
175
    else if (c <= 0xFFFF)
 
176
    {
 
177
        *buf++ =  (0xE0 | (c >> 12));
 
178
        *buf++ =  (0x80 | ((c >> 6) & 0x3F));
 
179
        *buf++ =  (0x80 | (c & 0x3F));
 
180
    }
 
181
    else if (c <= 0x1FFFFF)
 
182
    {
 
183
        *buf++ =  (0xF0 | (c >> 18));
 
184
        *buf++ =  (0x80 | ((c >> 12) & 0x3F));
 
185
        *buf++ =  (0x80 | ((c >> 6) & 0x3F));
 
186
        *buf++ =  (0x80 | (c & 0x3F));
 
187
    }
 
188
    else
 
189
    {
 
190
        *buf++ =  (0xF8 | (c >> 24));
 
191
        *buf++ =  (0x80 | ((c >> 18) & 0x3F));
 
192
        *buf++ =  (0x80 | ((c >> 12) & 0x3F));
 
193
        *buf++ =  (0x80 | ((c >> 6) & 0x3F));
 
194
        *buf++ =  (0x80 | (c & 0x3F));
 
195
    }
 
196
#else
 
197
    int err, count = 0;
 
198
        
 
199
    err = EncodeCharToUTF8Bytes(c, (unsigned char *)buf, null, null, &count);
 
200
    if (err)
 
201
    {
 
202
#if 0
 
203
        extern FILE* errout; /* debug */
 
204
 
 
205
        tidy_out(errout, "pprint UTF-8 encoding error for U+%x : ", c); /* debug */
 
206
#endif
 
207
        /* replacement char 0xFFFD encoded as UTF-8 */
 
208
        buf[0] = (char) 0xEF;
 
209
        buf[1] = (char) 0xBF;
 
210
        buf[2] = (char) 0xBD;
 
211
        count = 3;
 
212
    }
 
213
    
 
214
    buf += count;
 
215
#endif
 
216
 
 
217
    return buf;
 
218
}
 
219
 
 
220
 
 
221
void FreePrintBuf(void)
 
222
{
 
223
    if (linebuf)
 
224
        MemFree(linebuf);
 
225
 
 
226
    linebuf = null;
 
227
    lbufsize = 0;
 
228
}
 
229
 
 
230
static void AddC(uint c, uint index)
 
231
{
 
232
    if (index + 1 >= lbufsize)
 
233
    {
 
234
        while (index + 1 >= lbufsize)
 
235
        {
 
236
            if (lbufsize == 0)
 
237
                lbufsize = 256;
 
238
            else
 
239
                lbufsize = lbufsize * 2;
 
240
        }
 
241
 
 
242
       linebuf = (uint *)MemRealloc(linebuf, lbufsize*sizeof(uint));
 
243
    }
 
244
 
 
245
    linebuf[index] = (uint)c;
 
246
}
 
247
 
 
248
static void WrapLine(Out *fout, uint indent)
 
249
{
 
250
    uint i, *p, *q;
 
251
 
 
252
    if (wraphere == 0)
 
253
        return;
 
254
 
 
255
    for (i = 0; i < indent; ++i)
 
256
        outc(' ', fout);
 
257
 
 
258
    for (i = 0; i < wraphere; ++i)
 
259
        outc(linebuf[i], fout);
 
260
 
 
261
    if (InString)
 
262
    {
 
263
        outc(' ', fout);
 
264
        outc('\\', fout);
 
265
    }
 
266
 
 
267
    outc('\n', fout);
 
268
 
 
269
    if (linelen > wraphere)
 
270
    {
 
271
        p = linebuf;
 
272
 
 
273
        if (linebuf[wraphere] == ' ')
 
274
            ++wraphere;
 
275
 
 
276
        q = linebuf + wraphere;
 
277
        AddC('\0', linelen);
 
278
 
 
279
        p = linebuf; q = linebuf + wraphere; /* 433856 - fix by Terry Teague 23 Jun 00 */
 
280
        
 
281
        while ((*p++ = *q++));
 
282
 
 
283
        linelen -= wraphere;
 
284
    }
 
285
    else
 
286
        linelen = 0;
 
287
 
 
288
    wraphere = 0;
 
289
}
 
290
 
 
291
static void WrapAttrVal(Out *fout, uint indent, Bool inString)
 
292
{
 
293
    uint i, *p, *q;
 
294
 
 
295
    for (i = 0; i < indent; ++i)
 
296
        outc(' ', fout);
 
297
 
 
298
    for (i = 0; i < wraphere; ++i)
 
299
        outc(linebuf[i], fout);
 
300
 
 
301
    outc(' ', fout);
 
302
 
 
303
    if (inString)
 
304
        outc('\\', fout);
 
305
 
 
306
    outc('\n', fout);
 
307
 
 
308
    if (linelen > wraphere)
 
309
    {
 
310
        p = linebuf;
 
311
 
 
312
        if (linebuf[wraphere] == ' ')
 
313
            ++wraphere;
 
314
 
 
315
        q = linebuf + wraphere;
 
316
        AddC('\0', linelen);
 
317
 
 
318
        p = linebuf; q = linebuf + wraphere; /* 433856 - fix by Terry Teague 23 Jun 00 */
 
319
        
 
320
        while ((*p++ = *q++));
 
321
 
 
322
        linelen -= wraphere;
 
323
    }
 
324
    else
 
325
        linelen = 0;
 
326
 
 
327
    wraphere = 0;
 
328
}
 
329
 
 
330
void PFlushLine(Out *fout, uint indent)
 
331
{
 
332
    uint i;
 
333
 
 
334
    if (linelen > 0)
 
335
    {
 
336
        if (indent + linelen >= wraplen)
 
337
            WrapLine(fout, indent);
 
338
 
 
339
        if (!InAttVal || IndentAttributes)
 
340
        {
 
341
            for (i = 0; i < indent; ++i)
 
342
                outc(' ', fout);
 
343
        }
 
344
 
 
345
        for (i = 0; i < linelen; ++i)
 
346
            outc(linebuf[i], fout);
 
347
    }
 
348
 
 
349
    outc('\n', fout);
 
350
    linelen = wraphere = 0;
 
351
    InAttVal = no;
 
352
}
 
353
 
 
354
void PCondFlushLine(Out *fout, uint indent)
 
355
{
 
356
    uint i;
 
357
 
 
358
    if (linelen > 0)
 
359
    {
 
360
        if (indent + linelen >= wraplen)
 
361
            WrapLine(fout, indent);
 
362
 
 
363
        if (!InAttVal || IndentAttributes)
 
364
        {
 
365
            for (i = 0; i < indent; ++i)
 
366
                outc(' ', fout);
 
367
        }
 
368
 
 
369
        for (i = 0; i < linelen; ++i)
 
370
            outc(linebuf[i], fout);
 
371
 
 
372
        outc('\n', fout);
 
373
        linelen = wraphere = 0;
 
374
        InAttVal = no;
 
375
    }
 
376
}
 
377
 
 
378
static void PPrintChar(uint c, uint mode)
 
379
{
 
380
    char *p, entity[128];
 
381
    Bool breakable=no; /* #431953 - RJ */
 
382
 
 
383
    if (c == ' ' && !(mode & (PREFORMATTED | COMMENT | ATTRIBVALUE | CDATA)))
 
384
    {
 
385
        /* coerce a space character to a non-breaking space */
 
386
        if (mode & NOWRAP)
 
387
        {
 
388
            /* by default XML doesn't define &nbsp; */
 
389
            if (NumEntities || XmlTags)
 
390
            {
 
391
                AddC('&', linelen++);
 
392
                AddC('#', linelen++);
 
393
                AddC('1', linelen++);
 
394
                AddC('6', linelen++);
 
395
                AddC('0', linelen++);
 
396
                AddC(';', linelen++);
 
397
            }
 
398
            else /* otherwise use named entity */
 
399
            {
 
400
                AddC('&', linelen++);
 
401
                AddC('n', linelen++);
 
402
                AddC('b', linelen++);
 
403
                AddC('s', linelen++);
 
404
                AddC('p', linelen++);
 
405
                AddC(';', linelen++);
 
406
            }
 
407
            return;
 
408
        }
 
409
        else
 
410
            wraphere = linelen;
 
411
    }
 
412
 
 
413
    /* comment characters are passed raw */
 
414
    if (mode & (COMMENT | CDATA))
 
415
    {
 
416
        AddC(c, linelen++);
 
417
        return;
 
418
    }
 
419
 
 
420
    /* except in CDATA map < to &lt; etc. */
 
421
    if (! (mode & CDATA) )
 
422
    {
 
423
        if (c == '<')
 
424
        {
 
425
            AddC('&', linelen++);
 
426
            AddC('l', linelen++);
 
427
            AddC('t', linelen++);
 
428
            AddC(';', linelen++);
 
429
            return;
 
430
        }
 
431
            
 
432
        if (c == '>')
 
433
        {
 
434
            AddC('&', linelen++);
 
435
            AddC('g', linelen++);
 
436
            AddC('t', linelen++);
 
437
            AddC(';', linelen++);
 
438
            return;
 
439
        }
 
440
 
 
441
        /*
 
442
          naked '&' chars can be left alone or
 
443
          quoted as &amp; The latter is required
 
444
          for XML where naked '&' are illegal.
 
445
        */
 
446
        if (c == '&' && QuoteAmpersand)
 
447
        {
 
448
            AddC('&', linelen++);
 
449
            AddC('a', linelen++);
 
450
            AddC('m', linelen++);
 
451
            AddC('p', linelen++);
 
452
            AddC(';', linelen++);
 
453
            return;
 
454
        }
 
455
 
 
456
        if (c == '"' && QuoteMarks)
 
457
        {
 
458
            AddC('&', linelen++);
 
459
            AddC('q', linelen++);
 
460
            AddC('u', linelen++);
 
461
            AddC('o', linelen++);
 
462
            AddC('t', linelen++);
 
463
            AddC(';', linelen++);
 
464
            return;
 
465
        }
 
466
 
 
467
        if (c == '\'' && QuoteMarks)
 
468
        {
 
469
            AddC('&', linelen++);
 
470
            AddC('#', linelen++);
 
471
            AddC('3', linelen++);
 
472
            AddC('9', linelen++);
 
473
            AddC(';', linelen++);
 
474
            return;
 
475
        }
 
476
 
 
477
        if (c == 160 && outCharEncoding != RAW)
 
478
        {
 
479
            if (MakeBare)
 
480
                AddC(' ', linelen++);
 
481
            else if (QuoteNbsp)
 
482
            {
 
483
                AddC('&', linelen++);
 
484
 
 
485
                if (NumEntities || XmlTags)
 
486
                {
 
487
                    AddC('#', linelen++);
 
488
                    AddC('1', linelen++);
 
489
                    AddC('6', linelen++);
 
490
                    AddC('0', linelen++);
 
491
                }
 
492
                else
 
493
                {
 
494
                    AddC('n', linelen++);
 
495
                    AddC('b', linelen++);
 
496
                    AddC('s', linelen++);
 
497
                    AddC('p', linelen++);
 
498
                }
 
499
 
 
500
                AddC(';', linelen++);
 
501
            }
 
502
            else
 
503
                AddC(c, linelen++);
 
504
 
 
505
            return;
 
506
        }
 
507
    }
 
508
 
 
509
#if SUPPORT_ASIAN_ENCODINGS
 
510
 
 
511
    /* #431953 - start RJ */
 
512
    /* Handle encoding-specific issues */
 
513
    switch (outCharEncoding)
 
514
    {
 
515
    case UTF8:
 
516
    /* Chinese doesn't have spaces, so it needs other kinds of breaks */
 
517
    /* This will also help documents using nice Unicode punctuation */
 
518
    /* But we leave the ASCII range punctuation untouched */
 
519
 
 
520
    /* Break after any punctuation or spaces characters */
 
521
    if ((c >= 0x2000) && !(mode & PREFORMATTED))
 
522
    {
 
523
        if(((c >= 0x2000) && ( c<= 0x2006 ))
 
524
        || ((c >= 0x2008) && ( c<= 0x2010 ))
 
525
        || ((c >= 0x2011) && ( c<= 0x2046 ))
 
526
        || ((c >= 0x207D) && ( c<= 0x207E )) 
 
527
        || ((c >= 0x208D) && ( c<= 0x208E )) 
 
528
        || ((c >= 0x2329) && ( c<= 0x232A )) 
 
529
        || ((c >= 0x3001) && ( c<= 0x3003 )) 
 
530
        || ((c >= 0x3008) && ( c<= 0x3011 )) 
 
531
        || ((c >= 0x3014) && ( c<= 0x301F )) 
 
532
        || ((c >= 0xFD3E) && ( c<= 0xFD3F )) 
 
533
        || ((c >= 0xFE30) && ( c<= 0xFE44 )) 
 
534
        || ((c >= 0xFE49) && ( c<= 0xFE52 )) 
 
535
        || ((c >= 0xFE54) && ( c<= 0xFE61 )) 
 
536
        || ((c >= 0xFE6A) && ( c<= 0xFE6B )) 
 
537
        || ((c >= 0xFF01) && ( c<= 0xFF03 )) 
 
538
        || ((c >= 0xFF05) && ( c<= 0xFF0A )) 
 
539
        || ((c >= 0xFF0C) && ( c<= 0xFF0F )) 
 
540
        || ((c >= 0xFF1A) && ( c<= 0xFF1B )) 
 
541
        || ((c >= 0xFF1F) && ( c<= 0xFF20 )) 
 
542
        || ((c >= 0xFF3B) && ( c<= 0xFF3D )) 
 
543
        || ((c >= 0xFF61) && ( c<= 0xFF65 )))
 
544
        {
 
545
            wraphere = linelen + 2; /* 2, because AddChar is not till later */
 
546
            breakable = yes;
 
547
        } 
 
548
        else switch (c)
 
549
        {
 
550
            case 0xFE63:
 
551
            case 0xFE68:
 
552
            case 0x3030:
 
553
            case 0x30FB:
 
554
            case 0xFF3F:
 
555
            case 0xFF5B:
 
556
            case 0xFF5D:
 
557
                wraphere = linelen + 2;
 
558
                breakable = yes;
 
559
        }
 
560
        /* but break before a left punctuation */
 
561
        if (breakable == yes)
 
562
        { 
 
563
            if (((c >= 0x201A) && (c <= 0x201C)) ||
 
564
                ((c >= 0x201E) && (c <= 0x201F)))
 
565
            {
 
566
                wraphere--;
 
567
            }
 
568
            else switch (c)
 
569
            {
 
570
            case 0x2018:
 
571
            case 0x2039:
 
572
            case 0x2045:
 
573
            case 0x207D:
 
574
            case 0x208D:
 
575
            case 0x2329:
 
576
            case 0x3008:
 
577
            case 0x300A:
 
578
            case 0x300C:
 
579
            case 0x300E:
 
580
            case 0x3010:
 
581
            case 0x3014:
 
582
            case 0x3016:
 
583
            case 0x3018:
 
584
            case 0x301A:
 
585
            case 0x301D:
 
586
            case 0xFD3E:
 
587
            case 0xFE35:
 
588
            case 0xFE37:
 
589
            case 0xFE39:
 
590
            case 0xFE3B:
 
591
            case 0xFE3D:
 
592
            case 0xFE3F:
 
593
            case 0xFE41:
 
594
            case 0xFE43:
 
595
            case 0xFE59:
 
596
            case 0xFE5B:
 
597
            case 0xFE5D:
 
598
            case 0xFF08:
 
599
            case 0xFF3B:
 
600
            case 0xFF5B:
 
601
            case 0xFF62:
 
602
                wraphere--; 
 
603
            }
 
604
        }
 
605
    }
 
606
    break;
 
607
 
 
608
    case BIG5:
 
609
    /* Allow linebreak at Chinese punctuation characters */
 
610
    /* There are not many spaces in Chinese */
 
611
    AddC(c, linelen++);
 
612
    if (((c & 0xFF00) == 0xA100) & !(mode & PREFORMATTED))
 
613
    {
 
614
        wraphere = linelen;
 
615
        /* opening brackets have odd codes: break before them */
 
616
        if ((c > 0x5C) && (c < 0xAD) && ((c & 1) == 1)) 
 
617
            wraphere--; 
 
618
    }
 
619
    return;
 
620
 
 
621
    case SHIFTJIS:
 
622
    case ISO2022: /* ISO 2022 characters are passed raw */
 
623
    case RAW:
 
624
        AddC(c, linelen++);
 
625
        return;
 
626
    }
 
627
    /* #431953 - end RJ */
 
628
 
 
629
#else
 
630
 
 
631
    /* otherwise ISO 2022 characters are passed raw */
 
632
    if (outCharEncoding == ISO2022 || outCharEncoding == RAW)
 
633
    {
 
634
        AddC(c, linelen++);
 
635
        return;
 
636
    }
 
637
 
 
638
#endif
 
639
 
 
640
    /* if preformatted text, map &nbsp; to space */
 
641
    if (c == 160 && (mode & PREFORMATTED))
 
642
    {
 
643
        AddC(' ', linelen++);
 
644
        return;
 
645
    }
 
646
 
 
647
    /*
 
648
     Filters from Word and PowerPoint often use smart
 
649
     quotes resulting in character codes between 128
 
650
     and 159. Unfortunately, the corresponding HTML 4.0
 
651
     entities for these are not widely supported. The
 
652
     following converts dashes and quotation marks to
 
653
     the nearest ASCII equivalent. My thanks to
 
654
     Andrzej Novosiolov for his help with this code.
 
655
    */
 
656
 
 
657
    if ( (MakeClean && AsciiChars) || MakeBare )
 
658
    {
 
659
        if (c >= 0x2013 && c <= 0x201E)
 
660
        {
 
661
            switch (c) {
 
662
              case 0x2013: /* en dash */
 
663
              case 0x2014: /* em dash */
 
664
                c = '-';
 
665
                break;
 
666
              case 0x2018: /* left single  quotation mark */
 
667
              case 0x2019: /* right single quotation mark */
 
668
              case 0x201A: /* single low-9 quotation mark */
 
669
                c = '\'';
 
670
                break;
 
671
              case 0x201C: /* left double  quotation mark */
 
672
              case 0x201D: /* right double quotation mark */
 
673
              case 0x201E: /* double low-9 quotation mark */
 
674
                c = '"';
 
675
                break;
 
676
              }
 
677
        }
 
678
    }
 
679
 
 
680
    /* don't map latin-1 chars to entities */
 
681
    if (outCharEncoding == LATIN1)
 
682
    {
 
683
        if (c > 255)  /* multi byte chars */
 
684
        {
 
685
            if (!NumEntities && (p = EntityName(c)) != null)
 
686
                sprintf(entity, "&%s;", p);
 
687
            else
 
688
                sprintf(entity, "&#%u;", c);
 
689
 
 
690
            for (p = entity; *p; ++p)
 
691
                AddC(*p, linelen++);
 
692
 
 
693
            return;
 
694
        }
 
695
 
 
696
        if (c > 126 && c < 160)
 
697
        {
 
698
            sprintf(entity, "&#%d;", c);
 
699
 
 
700
            for (p = entity; *p; ++p)
 
701
                AddC(*p, linelen++);
 
702
 
 
703
            return;
 
704
        }
 
705
 
 
706
        AddC(c, linelen++);
 
707
        return;
 
708
    }
 
709
 
 
710
    /* don't map UTF-8 chars to entities */
 
711
    if (outCharEncoding == UTF8)
 
712
    {
 
713
        AddC(c, linelen++);
 
714
        return;
 
715
    }
 
716
 
 
717
#if SUPPORT_UTF16_ENCODINGS
 
718
 
 
719
    /* don't map UTF-16 chars to entities */
 
720
    if (outCharEncoding == UTF16 || outCharEncoding == UTF16LE || outCharEncoding == UTF16BE)
 
721
    {
 
722
        AddC(c, linelen++);
 
723
        return;
 
724
    }
 
725
 
 
726
#endif
 
727
 
 
728
    /* use numeric entities only  for XML */
 
729
    if (XmlTags)
 
730
    {
 
731
        /* if ASCII use numeric entities for chars > 127 */
 
732
        if (c > 127 && outCharEncoding == ASCII)
 
733
        {
 
734
            sprintf(entity, "&#%u;", c);
 
735
 
 
736
            for (p = entity; *p; ++p)
 
737
                AddC(*p, linelen++);
 
738
 
 
739
            return;
 
740
        }
 
741
 
 
742
        /* otherwise output char raw */
 
743
        AddC(c, linelen++);
 
744
        return;
 
745
    }
 
746
 
 
747
    /* default treatment for ASCII */
 
748
    if ((outCharEncoding == ASCII) && (c > 126 || (c < ' ' && c != '\t')))
 
749
    {
 
750
        if (!NumEntities && (p = EntityName(c)) != null)
 
751
            sprintf(entity, "&%s;", p);
 
752
        else
 
753
            sprintf(entity, "&#%u;", c);
 
754
 
 
755
        for (p = entity; *p; ++p)
 
756
            AddC(*p, linelen++);
 
757
 
 
758
        return;
 
759
    }
 
760
 
 
761
    AddC(c, linelen++);
 
762
}
 
763
 
 
764
/* 
 
765
  The line buffer is uint not char so we can
 
766
  hold Unicode values unencoded. The translation
 
767
  to UTF-8 is deferred to the outc routine called
 
768
  to flush the line buffer.
 
769
*/
 
770
static void PPrintText(Out *fout, uint mode, uint indent,
 
771
                Lexer *lexer, uint start, uint end)
 
772
{
 
773
    uint i, c;
 
774
 
 
775
    for (i = start; i < end; ++i)
 
776
    {
 
777
        if (indent + linelen >= wraplen)
 
778
            WrapLine(fout, indent);
 
779
 
 
780
        c = (unsigned char)lexer->lexbuf[i];
 
781
 
 
782
        /* look for UTF-8 multibyte character */
 
783
        if (c > 0x7F)
 
784
             i += GetUTF8((unsigned char *)lexer->lexbuf + i, &c);
 
785
 
 
786
        if (c == '\n')
 
787
        {
 
788
            PFlushLine(fout, indent);
 
789
            continue;
 
790
        }
 
791
 
 
792
        PPrintChar(c, mode);
 
793
    }
 
794
}
 
795
 
 
796
static void PPrintString(Out *fout, uint indent, char *str)
 
797
{
 
798
    while (*str != '\0')
 
799
        AddC(*str++, linelen++);
 
800
}
 
801
 
 
802
static void PPrintAttrValue(Out *fout, uint indent,
 
803
                            char *value, int delim, Bool wrappable)
 
804
{
 
805
    uint c;
 
806
    Bool wasinstring = no;
 
807
 
 
808
    int mode = (wrappable ? (NORMAL | ATTRIBVALUE) : (PREFORMATTED | ATTRIBVALUE));
 
809
 
 
810
    /* look for ASP, Tango or PHP instructions for computed attribute value */
 
811
    if (value && value[0] == '<')
 
812
    {
 
813
        if (value[1] == '%' || value[1] == '@'|| wstrncmp(value, "<?php", 5) == 0)
 
814
            mode |= CDATA;
 
815
    }
 
816
 
 
817
    if (delim == null)
 
818
        delim = '"';
 
819
 
 
820
    AddC('=', linelen++);
 
821
 
 
822
    /* don't wrap after "=" for xml documents */
 
823
    if (!XmlOut)
 
824
    {
 
825
        if (indent + linelen < wraplen)
 
826
            wraphere = linelen;
 
827
 
 
828
        if (indent + linelen >= wraplen)
 
829
            WrapLine(fout, indent);
 
830
 
 
831
        if (indent + linelen < wraplen)
 
832
            wraphere = linelen;
 
833
        else
 
834
            PCondFlushLine(fout, indent);
 
835
    }
 
836
 
 
837
    AddC(delim, linelen++);
 
838
 
 
839
    if (value)
 
840
    {
 
841
        InString = no;
 
842
 
 
843
        while (*value != '\0')
 
844
        {
 
845
            c = (unsigned char)*value;
 
846
 
 
847
            if (wrappable && c == ' ' && indent + linelen < wraplen)
 
848
            {
 
849
                wraphere = linelen;
 
850
                wasinstring = InString;
 
851
            }
 
852
 
 
853
            if (wrappable && wraphere > 0 && indent + linelen >= wraplen)
 
854
                WrapAttrVal(fout, indent, wasinstring);
 
855
 
 
856
            if (c == (uint)delim)
 
857
            {
 
858
                char *entity;
 
859
 
 
860
                entity = (c == '"' ? "&quot;" : "&#39;");
 
861
 
 
862
                while (*entity != '\0')
 
863
                    AddC(*entity++, linelen++);
 
864
 
 
865
                ++value;
 
866
                continue;
 
867
            }
 
868
            else if (c == '"')
 
869
            {
 
870
                if (QuoteMarks)
 
871
                {
 
872
                    AddC('&', linelen++);
 
873
                    AddC('q', linelen++);
 
874
                    AddC('u', linelen++);
 
875
                    AddC('o', linelen++);
 
876
                    AddC('t', linelen++);
 
877
                    AddC(';', linelen++);
 
878
                }
 
879
                else
 
880
                    AddC('"', linelen++);
 
881
 
 
882
                if (delim == '\'')
 
883
                    InString = (Bool)(!InString);
 
884
 
 
885
                ++value;
 
886
                continue;
 
887
            }
 
888
            else if (c == '\'')
 
889
            {
 
890
                if (QuoteMarks)
 
891
                {
 
892
                    AddC('&', linelen++);
 
893
                    AddC('#', linelen++);
 
894
                    AddC('3', linelen++);
 
895
                    AddC('9', linelen++);
 
896
                    AddC(';', linelen++);
 
897
                }
 
898
                else
 
899
                    AddC('\'', linelen++);
 
900
 
 
901
                if (delim == '"')
 
902
                    InString = (Bool)(!InString);
 
903
 
 
904
                ++value;
 
905
                continue;
 
906
            }
 
907
 
 
908
            /* look for UTF-8 multibyte character */
 
909
            if (c > 0x7F)
 
910
                 value += GetUTF8((unsigned char *)value, &c);
 
911
 
 
912
            ++value;
 
913
 
 
914
            if (c == '\n')
 
915
            {
 
916
                PFlushLine(fout, indent);
 
917
                continue;
 
918
            }
 
919
 
 
920
            PPrintChar(c, mode);
 
921
        }
 
922
    }
 
923
 
 
924
    InString = no;
 
925
    AddC(delim, linelen++);
 
926
}
 
927
 
 
928
static void PPrintAttribute(Out *fout, uint indent,
 
929
                            Node *node, AttVal *attr)
 
930
{
 
931
    char *name;
 
932
    Bool wrappable = no;
 
933
 
 
934
    if (IndentAttributes)
 
935
    {
 
936
        PFlushLine(fout, indent);
 
937
        indent += spaces;
 
938
    }
 
939
 
 
940
    name = attr->attribute;
 
941
 
 
942
    if (indent + linelen >= wraplen)
 
943
        WrapLine(fout, indent);
 
944
 
 
945
    if (!XmlTags && !XmlOut && attr->dict)
 
946
    {
 
947
        if (IsScript(name))
 
948
            wrappable = WrapScriptlets;
 
949
        else if (!attr->dict->nowrap && WrapAttVals)
 
950
            wrappable = yes;
 
951
    }
 
952
 
 
953
    if (indent + linelen < wraplen)
 
954
    {
 
955
        wraphere = linelen;
 
956
        AddC(' ', linelen++);
 
957
    }
 
958
    else
 
959
    {
 
960
        PCondFlushLine(fout, indent);
 
961
        AddC(' ', linelen++);
 
962
    }
 
963
 
 
964
    while (*name != '\0')
 
965
        AddC(FoldCase(*name++, UpperCaseAttrs), linelen++);
 
966
 
 
967
    if (indent + linelen >= wraplen)
 
968
        WrapLine(fout, indent);
 
969
 
 
970
    if (attr->value == null)
 
971
    {
 
972
        if (XmlTags || XmlOut)
 
973
            PPrintAttrValue(fout, indent,
 
974
              IsBool(attr->attribute) ? attr->attribute : "",
 
975
              attr->delim, yes);
 
976
        else if (!IsBoolAttribute(attr) && !IsNewNode(node))
 
977
            PPrintAttrValue(fout, indent, "", attr->delim, yes);
 
978
        else if (indent + linelen < wraplen)
 
979
            wraphere = linelen;
 
980
 
 
981
    }
 
982
    else
 
983
        PPrintAttrValue(fout, indent, attr->value, attr->delim, wrappable);
 
984
}
 
985
 
 
986
static void PPrintAttrs(Out *fout, uint indent,
 
987
                        Lexer *lexer, Node *node, AttVal *attr)
 
988
{
 
989
    Attribute *attribute;
 
990
 
 
991
    if (attr)
 
992
    {
 
993
        if (attr->next)
 
994
            PPrintAttrs(fout, indent, lexer, node, attr->next);
 
995
 
 
996
        if (attr->attribute != null)
 
997
        {
 
998
            attribute = attr->dict;
 
999
 
 
1000
            if (!DropPropAttrs ||
 
1001
                !(attribute == null ||
 
1002
                    (attribute->versions & VERS_PROPRIETARY)))
 
1003
            PPrintAttribute(fout, indent, node, attr);
 
1004
        }
 
1005
        else if (attr->asp != null)
 
1006
        {
 
1007
            AddC(' ', linelen++);
 
1008
            PPrintAsp(fout, indent, lexer, attr->asp);
 
1009
        }
 
1010
        else if (attr->php != null)
 
1011
        {
 
1012
            AddC(' ', linelen++);
 
1013
            PPrintPhp(fout, indent, lexer, attr->php);
 
1014
        }
 
1015
    }
 
1016
 
 
1017
    /* add xml:space attribute to pre and other elements */
 
1018
    if (XmlOut == yes &&
 
1019
            XmlSpace &&
 
1020
            XMLPreserveWhiteSpace (node) &&
 
1021
            !GetAttrByName(node, "xml:space"))
 
1022
        PPrintString(fout, indent, " xml:space=\"preserve\"");
 
1023
}
 
1024
 
 
1025
/*
 
1026
 Line can be wrapped immediately after inline start tag provided
 
1027
 if follows a text node ending in a space, or it parent is an
 
1028
 inline element that that rule applies to. This behaviour was
 
1029
 reverse engineered from Netscape 3.0
 
1030
*/
 
1031
static Bool AfterSpace(Lexer *lexer, Node *node)
 
1032
{
 
1033
    Node *prev;
 
1034
    uint c;
 
1035
 
 
1036
    if (!node || !node->tag || !(node->tag->model & CM_INLINE))
 
1037
        return yes;
 
1038
 
 
1039
    prev = node->prev;
 
1040
 
 
1041
    if (prev)
 
1042
    {
 
1043
        if (prev->type == TextNode && prev->end > prev->start)
 
1044
        {
 
1045
            c = (unsigned char)lexer->lexbuf[prev->end - 1];
 
1046
 
 
1047
            if (c == 160 || c == ' ' || c == '\n')
 
1048
                return yes;
 
1049
        }
 
1050
 
 
1051
        return no;
 
1052
    }
 
1053
 
 
1054
    return AfterSpace(lexer, node->parent);
 
1055
}
 
1056
 
 
1057
static void PPrintTag(Lexer *lexer, Out *fout,
 
1058
                      uint mode, uint indent, Node *node)
 
1059
{
 
1060
    char c, *p;
 
1061
 
 
1062
    AddC('<', linelen++);
 
1063
 
 
1064
    if (node->type == EndTag)
 
1065
        AddC('/', linelen++);
 
1066
 
 
1067
    for (p = node->element; (c = *p); ++p)
 
1068
        AddC(FoldCase(c, UpperCaseTags), linelen++);
 
1069
 
 
1070
    PPrintAttrs(fout, indent, lexer, node, node->attributes);
 
1071
 
 
1072
    if ( (XmlOut || xHTML) &&
 
1073
         (node->type == StartEndTag || node->tag->model & CM_EMPTY))
 
1074
    {
 
1075
        AddC(' ', linelen++);   /* Space is NS compatibility hack <br /> */
 
1076
        AddC('/', linelen++);   /* Required end tag marker */
 
1077
    }
 
1078
 
 
1079
    AddC('>', linelen++);
 
1080
 
 
1081
    if ((node->type != StartEndTag || xHTML) && !(mode & PREFORMATTED))
 
1082
    {
 
1083
        if (indent + linelen >= wraplen)
 
1084
            WrapLine(fout, indent);
 
1085
 
 
1086
        if (indent + linelen < wraplen)
 
1087
        {
 
1088
            /*
 
1089
             wrap after start tag if is <br/> or if it's not
 
1090
             inline or it is an empty tag followed by </a>
 
1091
            */
 
1092
            if (AfterSpace(lexer, node))
 
1093
            {
 
1094
                if (!(mode & NOWRAP) &&
 
1095
                    (!(node->tag->model & CM_INLINE) ||
 
1096
                      (node->tag == tag_br) ||
 
1097
                      ((node->tag->model & CM_EMPTY) && 
 
1098
                      node->next == null &&
 
1099
                      node->parent->tag == tag_a)))
 
1100
                {
 
1101
                    wraphere = linelen;
 
1102
                }
 
1103
            }
 
1104
        }
 
1105
        else
 
1106
            PCondFlushLine(fout, indent);
 
1107
    }
 
1108
}
 
1109
 
 
1110
static void PPrintEndTag(Out *fout, uint mode, uint indent, Node *node)
 
1111
{
 
1112
    char c, *p;
 
1113
 
 
1114
   /*
 
1115
     Netscape ignores SGML standard by not ignoring a
 
1116
     line break before </A> or </U> etc. To avoid rendering 
 
1117
     this as an underlined space, I disable line wrapping
 
1118
     before inline end tags by the #if 0 ... #endif
 
1119
   */
 
1120
#if 0
 
1121
    if (indent + linelen < wraplen && !(mode & NOWRAP))
 
1122
        wraphere = linelen;
 
1123
#endif
 
1124
 
 
1125
    AddC('<', linelen++);
 
1126
    AddC('/', linelen++);
 
1127
 
 
1128
    for (p = node->element; (c = *p); ++p)
 
1129
        AddC(FoldCase(c, UpperCaseTags), linelen++);
 
1130
 
 
1131
    AddC('>', linelen++);
 
1132
}
 
1133
 
 
1134
static void PPrintComment(Out *fout, uint indent,
 
1135
                   Lexer *lexer, Node *node)
 
1136
{
 
1137
    if (HideComments)
 
1138
        return;
 
1139
 
 
1140
    if (indent + linelen < wraplen)
 
1141
        wraphere = linelen;
 
1142
 
 
1143
    AddC('<', linelen++);
 
1144
    AddC('!', linelen++);
 
1145
    AddC('-', linelen++);
 
1146
    AddC('-', linelen++);
 
1147
#if 0
 
1148
    if (linelen < wraplen)
 
1149
        wraphere = linelen;
 
1150
#endif
 
1151
    PPrintText(fout, COMMENT, indent,
 
1152
                    lexer, node->start, node->end);
 
1153
#if 0
 
1154
    if (indent + linelen < wraplen)
 
1155
        wraphere = linelen;
 
1156
    AddC('-', linelen++);
 
1157
    AddC('-', linelen++);
 
1158
#endif
 
1159
    AddC('>', linelen++);
 
1160
 
 
1161
    if (node->linebreak)
 
1162
        PFlushLine(fout, indent);
 
1163
}
 
1164
 
 
1165
static void PPrintDocType(Out *fout, uint indent,
 
1166
                          Lexer *lexer, Node *node)
 
1167
{
 
1168
    uint i, c, mode = 0;
 
1169
    Bool q = QuoteMarks;
 
1170
 
 
1171
    QuoteMarks = no;
 
1172
 
 
1173
    if (indent + linelen < wraplen)
 
1174
        wraphere = linelen;
 
1175
 
 
1176
    PCondFlushLine(fout, indent);
 
1177
 
 
1178
    AddC('<', linelen++);
 
1179
    AddC('!', linelen++);
 
1180
    AddC('D', linelen++);
 
1181
    AddC('O', linelen++);
 
1182
    AddC('C', linelen++);
 
1183
    AddC('T', linelen++);
 
1184
    AddC('Y', linelen++);
 
1185
    AddC('P', linelen++);
 
1186
    AddC('E', linelen++);
 
1187
    AddC(' ', linelen++);
 
1188
 
 
1189
    if (indent + linelen < wraplen)
 
1190
        wraphere = linelen;
 
1191
 
 
1192
    for (i = node->start; i < node->end; ++i)
 
1193
    {
 
1194
        if (indent + linelen >= wraplen)
 
1195
            WrapLine(fout, indent);
 
1196
 
 
1197
        c = (unsigned char)lexer->lexbuf[i];
 
1198
 
 
1199
        /* inDTDSubset? */
 
1200
        if ( mode & CDATA ) {
 
1201
            if (c == ']')
 
1202
                mode &= ~CDATA;
 
1203
        }
 
1204
        else if (c == '[')
 
1205
            mode |= CDATA;
 
1206
 
 
1207
        /* look for UTF-8 multibyte character */
 
1208
        if (c > 0x7F)
 
1209
             i += GetUTF8((unsigned char *)lexer->lexbuf + i, &c);
 
1210
 
 
1211
        if (c == '\n')
 
1212
        {
 
1213
            PFlushLine(fout, indent);
 
1214
            continue;
 
1215
        }
 
1216
 
 
1217
        PPrintChar(c, mode);
 
1218
    }
 
1219
 
 
1220
    if (linelen < wraplen)
 
1221
        wraphere = linelen;
 
1222
 
 
1223
    AddC('>', linelen++);
 
1224
    QuoteMarks = q;
 
1225
    PCondFlushLine(fout, indent);
 
1226
}
 
1227
 
 
1228
static void PPrintPI(Out *fout, uint indent,
 
1229
                   Lexer *lexer, Node *node)
 
1230
{
 
1231
    if (indent + linelen < wraplen)
 
1232
        wraphere = linelen;
 
1233
 
 
1234
    AddC('<', linelen++);
 
1235
    AddC('?', linelen++);
 
1236
 
 
1237
    /* set CDATA to pass < and > unescaped */
 
1238
    PPrintText(fout, CDATA, indent,
 
1239
                    lexer, node->start, node->end);
 
1240
 
 
1241
    if (lexer->lexbuf[node->end - 1] != '?')
 
1242
        AddC('?', linelen++);
 
1243
 
 
1244
    AddC('>', linelen++);
 
1245
 
 
1246
    PCondFlushLine(fout, indent);
 
1247
}
 
1248
 
 
1249
static void PPrintXmlDecl(Out *fout, uint indent,
 
1250
                   Lexer *lexer, Node *node)
 
1251
{
 
1252
    if (indent + linelen < wraplen)
 
1253
        wraphere = linelen;
 
1254
 
 
1255
    AddC('<', linelen++);
 
1256
    AddC('?', linelen++);
 
1257
    AddC('x', linelen++);
 
1258
    AddC('m', linelen++);
 
1259
    AddC('l', linelen++);
 
1260
 
 
1261
    PPrintAttrs(fout, indent, lexer, node, node->attributes);
 
1262
 
 
1263
    if (lexer->lexbuf[node->end - 1] != '?')
 
1264
        AddC('?', linelen++);
 
1265
 
 
1266
    AddC('>', linelen++);
 
1267
 
 
1268
    PCondFlushLine(fout, indent);
 
1269
}
 
1270
 
 
1271
/* note ASP and JSTE share <% ... %> syntax */
 
1272
static void PPrintAsp(Out *fout, uint indent,
 
1273
                   Lexer *lexer, Node *node)
 
1274
{
 
1275
    int savewraplen = wraplen;
 
1276
 
 
1277
    /* disable wrapping if so requested */
 
1278
 
 
1279
    if (!WrapAsp || !WrapJste)
 
1280
        wraplen = 0xFFFFFF;  /* a very large number */
 
1281
 
 
1282
#if 0
 
1283
    if (indent + linelen < wraplen)
 
1284
        wraphere = linelen;
 
1285
#endif
 
1286
    AddC('<', linelen++);
 
1287
    AddC('%', linelen++);
 
1288
 
 
1289
    PPrintText(fout, (WrapAsp ? CDATA : COMMENT), indent,
 
1290
                    lexer, node->start, node->end);
 
1291
 
 
1292
    AddC('%', linelen++);
 
1293
    AddC('>', linelen++);
 
1294
    /* PCondFlushLine(fout, indent); */
 
1295
    wraplen = savewraplen;
 
1296
}
 
1297
 
 
1298
/* JSTE also supports <# ... #> syntax */
 
1299
static void PPrintJste(Out *fout, uint indent,
 
1300
                   Lexer *lexer, Node *node)
 
1301
{
 
1302
    int savewraplen = wraplen;
 
1303
 
 
1304
    /* disable wrapping if so requested */
 
1305
 
 
1306
    if (!WrapAsp)
 
1307
        wraplen = 0xFFFFFF;  /* a very large number */
 
1308
 
 
1309
    AddC('<', linelen++);
 
1310
    AddC('#', linelen++);
 
1311
 
 
1312
    PPrintText(fout, (WrapJste ? CDATA : COMMENT), indent,
 
1313
                    lexer, node->start, node->end);
 
1314
 
 
1315
    AddC('#', linelen++);
 
1316
    AddC('>', linelen++);
 
1317
    /* PCondFlushLine(fout, indent); */
 
1318
    wraplen = savewraplen;
 
1319
}
 
1320
 
 
1321
/* PHP is based on XML processing instructions */
 
1322
static void PPrintPhp(Out *fout, uint indent,
 
1323
                   Lexer *lexer, Node *node)
 
1324
{
 
1325
    int savewraplen = wraplen;
 
1326
 
 
1327
    /* disable wrapping if so requested */
 
1328
 
 
1329
    if (!WrapPhp)
 
1330
        wraplen = 0xFFFFFF;  /* a very large number */
 
1331
 
 
1332
#if 0
 
1333
    if (indent + linelen < wraplen)
 
1334
        wraphere = linelen;
 
1335
#endif
 
1336
    AddC('<', linelen++);
 
1337
    AddC('?', linelen++);
 
1338
 
 
1339
    PPrintText(fout, (WrapPhp ? CDATA : COMMENT), indent,
 
1340
                    lexer, node->start, node->end);
 
1341
 
 
1342
    AddC('?', linelen++);
 
1343
    AddC('>', linelen++);
 
1344
    /* PCondFlushLine(fout, indent); */
 
1345
    wraplen = savewraplen;
 
1346
}
 
1347
 
 
1348
static void PPrintCDATA(Out *fout, uint indent,
 
1349
                   Lexer *lexer, Node *node)
 
1350
{
 
1351
    int savewraplen = wraplen;
 
1352
 
 
1353
    if (!IndentCdata)
 
1354
        indent = 0;
 
1355
 
 
1356
    PCondFlushLine(fout, indent);
 
1357
 
 
1358
    /* disable wrapping */
 
1359
 
 
1360
    wraplen = 0xFFFFFF;  /* a very large number */
 
1361
 
 
1362
    AddC('<', linelen++);
 
1363
    AddC('!', linelen++);
 
1364
    AddC('[', linelen++);
 
1365
    AddC('C', linelen++);
 
1366
    AddC('D', linelen++);
 
1367
    AddC('A', linelen++);
 
1368
    AddC('T', linelen++);
 
1369
    AddC('A', linelen++);
 
1370
    AddC('[', linelen++);
 
1371
 
 
1372
    PPrintText(fout, COMMENT, indent,
 
1373
                    lexer, node->start, node->end);
 
1374
 
 
1375
    AddC(']', linelen++);
 
1376
    AddC(']', linelen++);
 
1377
    AddC('>', linelen++);
 
1378
    PCondFlushLine(fout, indent);
 
1379
    wraplen = savewraplen;
 
1380
}
 
1381
 
 
1382
static void PPrintSection(Out *fout, uint indent,
 
1383
                   Lexer *lexer, Node *node)
 
1384
{
 
1385
    int savewraplen = wraplen;
 
1386
 
 
1387
    /* disable wrapping if so requested */
 
1388
 
 
1389
    if (!WrapSection)
 
1390
        wraplen = 0xFFFFFF;  /* a very large number */
 
1391
 
 
1392
#if 0
 
1393
    if (indent + linelen < wraplen)
 
1394
        wraphere = linelen;
 
1395
#endif
 
1396
    AddC('<', linelen++);
 
1397
    AddC('!', linelen++);
 
1398
    AddC('[', linelen++);
 
1399
 
 
1400
    PPrintText(fout, (WrapSection ? CDATA : COMMENT), indent,
 
1401
                    lexer, node->start, node->end);
 
1402
 
 
1403
    AddC(']', linelen++);
 
1404
    AddC('>', linelen++);
 
1405
    /* PCondFlushLine(fout, indent); */
 
1406
    wraplen = savewraplen;
 
1407
}
 
1408
 
 
1409
 
 
1410
#if 0
 
1411
/*
 
1412
** Print script and style elements. For XHTML, wrap the content as follows:
 
1413
**     JavaScript:
 
1414
**         //<![CDATA[
 
1415
**             content
 
1416
**         //]]>
 
1417
**     VBScript:
 
1418
**         '<![CDATA[
 
1419
**             content
 
1420
**         ']]>
 
1421
**     CSS:
 
1422
**         /*<![CDATA[*/
 
1423
**             content
 
1424
**         /*]]>*/
 
1425
**     other:
 
1426
**         <![CDATA[
 
1427
**             content
 
1428
**         ]]>
 
1429
*/
 
1430
#endif
 
1431
 
 
1432
static char* CDATA_START           = "<![CDATA[";
 
1433
static char* CDATA_END             = "]]>";
 
1434
static char* JS_COMMENT_START      = "//";
 
1435
static char* JS_COMMENT_END        = "";
 
1436
static char* VB_COMMENT_START      = "\'";
 
1437
static char* VB_COMMENT_END        = "";
 
1438
static char* CSS_COMMENT_START     = "/*";
 
1439
static char* CSS_COMMENT_END       = "*/";
 
1440
static char* DEFAULT_COMMENT_START = "";
 
1441
static char* DEFAULT_COMMENT_END   = "";
 
1442
 
 
1443
 
 
1444
static Bool HasCDATA( Lexer* lexer, Node* node )
 
1445
{
 
1446
    /* Scan forward through the textarray. Since the characters we're
 
1447
    ** looking for are < 0x7f, we don't have to do any UTF-8 decoding.
 
1448
    */
 
1449
    char* start = lexer->lexbuf + node->start;
 
1450
    int len = node->end - node->start + 1;
 
1451
 
 
1452
    if ( node->type != TextNode )
 
1453
        return no;
 
1454
 
 
1455
    return wsubstrn( start, len, CDATA_START );
 
1456
}
 
1457
 
 
1458
 
 
1459
#if 0 
 
1460
static Bool StartsWithCDATA( Lexer* lexer, Node* node, char* commentStart )
 
1461
{
 
1462
    /* Scan forward through the textarray. Since the characters we're
 
1463
    ** looking for are < 0x7f, we don't have to do any UTF-8 decoding.
 
1464
    */
 
1465
    int i = node->start, j, end = node->end;
 
1466
 
 
1467
    if ( node->type != TextNode )
 
1468
        return no;
 
1469
 
 
1470
    /* Skip whitespace. */
 
1471
    while ( i < end && lexer->lexbuf[i] <= ' ' )
 
1472
        ++i;
 
1473
 
 
1474
    /* Check for starting comment delimiter. */
 
1475
    for ( j = 0; j < wstrlen(commentStart); ++j )
 
1476
    {
 
1477
        if ( i >= end || lexer->lexbuf[i] != commentStart[j] )
 
1478
            return no;
 
1479
        ++i;
 
1480
    }
 
1481
 
 
1482
    /* Skip whitespace. */
 
1483
    while ( i < end && lexer->lexbuf[i] <= ' ' )
 
1484
        ++i;
 
1485
 
 
1486
    /* Check for "<![CDATA[". */
 
1487
    for ( j = 0; j < wstrlen(CDATA_START); ++j )
 
1488
    {
 
1489
        if (i >= end || lexer->lexbuf[i] != CDATA_START[j])
 
1490
            return no;
 
1491
        ++i;
 
1492
    }
 
1493
 
 
1494
    return yes;
 
1495
}
 
1496
 
 
1497
 
 
1498
static Bool EndsWithCDATA( Lexer* lexer, Node* node, 
 
1499
                           char* commentStart, char* commentEnd )
 
1500
{
 
1501
    /* Scan backward through the buff. Since the characters we're
 
1502
    ** looking for are < 0x7f, we don't have do any UTF-8 decoding. Note
 
1503
    ** that this is true even though we are scanning backwards because every
 
1504
    ** byte of a UTF-8 multibyte character is >= 0x80.
 
1505
    */
 
1506
 
 
1507
    int i = node->end - 1, j, start = node->start;
 
1508
 
 
1509
    if ( node->type != TextNode )
 
1510
        return no;
 
1511
 
 
1512
    /* Skip whitespace. */
 
1513
    while ( i >= start && (lexer->lexbuf[i] & 0xff) <= ' ' )
 
1514
        --i;
 
1515
 
 
1516
    /* Check for ending comment delimiter. */
 
1517
    for ( j = wstrlen(commentEnd) - 1; j >= 0; --j )
 
1518
    {
 
1519
        if (i < start || lexer->lexbuf[i] != commentEnd[j])
 
1520
            return no;
 
1521
        --i;
 
1522
    }
 
1523
 
 
1524
    /* Skip whitespace. */
 
1525
    while (i >= start && (lexer->lexbuf[i] & 0xff) <= ' ')
 
1526
        --i;
 
1527
 
 
1528
    /* Check for "]]>". */
 
1529
    for (j = wstrlen(CDATA_END) - 1; j >= 0; j--)
 
1530
    {
 
1531
        if (i < start || lexer->lexbuf[i] != CDATA_END[j])
 
1532
            return no;
 
1533
        --i;
 
1534
    }
 
1535
 
 
1536
    /* Skip whitespace. */
 
1537
    while (i >= start && lexer->lexbuf[i] <= ' ')
 
1538
        --i;
 
1539
 
 
1540
    /* Check for starting comment delimiter. */
 
1541
    for ( j = wstrlen(commentStart) - 1; j >= 0; --j )
 
1542
    {
 
1543
        if ( i < start || lexer->lexbuf[i] != commentStart[j] )
 
1544
            return no;
 
1545
        --i;
 
1546
    }
 
1547
 
 
1548
    return yes;
 
1549
}
 
1550
#endif /* 0 */
 
1551
 
 
1552
void PPrintScriptStyle( Out* fout, uint mode, uint indent,
 
1553
                        Lexer* lexer, Node* node )
 
1554
{
 
1555
    Node* content;
 
1556
    char* commentStart = DEFAULT_COMMENT_START;
 
1557
    char* commentEnd = DEFAULT_COMMENT_END;
 
1558
    Bool  hasCData = no;
 
1559
 
 
1560
    PCondFlushLine(fout, indent);
 
1561
 
 
1562
    indent = 0;
 
1563
    PPrintTag(lexer, fout, mode, indent, node);
 
1564
    PFlushLine(fout, indent);
 
1565
 
 
1566
    if (xHTML && node->content != null)
 
1567
    {
 
1568
        AttVal* type = GetAttrByName(node, "type");
 
1569
        if (type != null)
 
1570
        {
 
1571
            if (wstrcasecmp(type->value, "text/javascript") == 0)
 
1572
            {
 
1573
                commentStart = JS_COMMENT_START;
 
1574
                commentEnd = JS_COMMENT_END;
 
1575
            }
 
1576
            else if (wstrcasecmp(type->value, "text/css") == 0)
 
1577
            {
 
1578
                commentStart = CSS_COMMENT_START;
 
1579
                commentEnd = CSS_COMMENT_END;
 
1580
            }
 
1581
            else if (wstrcasecmp(type->value, "text/vbscript") == 0)
 
1582
            {
 
1583
                commentStart = VB_COMMENT_START;
 
1584
                commentEnd = VB_COMMENT_END;
 
1585
            }
 
1586
        }
 
1587
 
 
1588
        hasCData = HasCDATA( lexer, node->content );
 
1589
        if ( ! hasCData )
 
1590
        {
 
1591
            /* disable wrapping */
 
1592
            uint savewraplen = wraplen;
 
1593
            wraplen = 0xFFFFFF;  /* a very large number */
 
1594
 
 
1595
            AddAsciiString( commentStart, linelen );
 
1596
            AddAsciiString( CDATA_START,  linelen );
 
1597
            AddAsciiString( commentEnd,   linelen );
 
1598
            PCondFlushLine( fout, indent );
 
1599
 
 
1600
            /* restore wrapping */
 
1601
            wraplen = savewraplen;
 
1602
        }
 
1603
    }
 
1604
 
 
1605
    for ( content = node->content;
 
1606
          content != null;
 
1607
          content = content->next )
 
1608
    {
 
1609
        PPrintTree( fout, (mode | PREFORMATTED | NOWRAP |CDATA), 
 
1610
                    indent, lexer, content );
 
1611
    }
 
1612
 
 
1613
    PCondFlushLine(fout, indent);
 
1614
 
 
1615
    if (xHTML && node->content != null)
 
1616
    {
 
1617
        if ( ! hasCData )
 
1618
        {
 
1619
            /* disable wrapping */
 
1620
            uint savewraplen = wraplen;
 
1621
            wraplen = 0xFFFFFF;  /* a very large number */
 
1622
 
 
1623
            AddAsciiString( commentStart, linelen );
 
1624
            AddAsciiString( CDATA_END,    linelen );
 
1625
            AddAsciiString( commentEnd,   linelen );
 
1626
 
 
1627
            /* restore wrapping */
 
1628
            wraplen = savewraplen;
 
1629
            PCondFlushLine(fout, indent);
 
1630
        }
 
1631
    }
 
1632
 
 
1633
    PPrintEndTag(fout, mode, indent, node);
 
1634
    PFlushLine(fout, indent);
 
1635
 
 
1636
    if (IndentContent == no && node->next != null)
 
1637
        PFlushLine(fout, indent);
 
1638
}
 
1639
 
 
1640
 
 
1641
 
 
1642
static Bool ShouldIndent(Node *node)
 
1643
{
 
1644
    if (IndentContent == no)
 
1645
        return no;
 
1646
 
 
1647
    if (SmartIndent)
 
1648
    {
 
1649
        if (node->content && (node->tag->model & CM_NO_INDENT))
 
1650
        {
 
1651
            for (node = node->content; node; node = node->next)
 
1652
                if (node->tag && node->tag->model & CM_BLOCK)
 
1653
                    return yes;
 
1654
 
 
1655
            return no;
 
1656
        }
 
1657
 
 
1658
        if (node->tag->model & CM_HEADING)
 
1659
            return no;
 
1660
 
 
1661
        if (node->tag == tag_p)
 
1662
            return no;
 
1663
 
 
1664
        if (node->tag == tag_title)
 
1665
            return no;
 
1666
    }
 
1667
 
 
1668
    if (node->tag->model & (CM_FIELD | CM_OBJECT))
 
1669
        return yes;
 
1670
 
 
1671
    if (node->tag == tag_map)
 
1672
        return yes;
 
1673
 
 
1674
    return (Bool)(!(node->tag->model & CM_INLINE));
 
1675
}
 
1676
 
 
1677
/*
 
1678
 Feature request #434940 - fix by Dave Raggett/Ignacio Vazquez-Abrams 21 Jun 01
 
1679
 print just the content of the body element.
 
1680
 useful when you want to reuse material from
 
1681
 other documents.
 
1682
 
 
1683
 -- Sebastiano Vigna <vigna@dsi.unimi.it>
 
1684
*/
 
1685
void PrintBody(Out *fout, Lexer *lexer, Node *root)
 
1686
{
 
1687
    Node *content, *body = FindBody(root);
 
1688
 
 
1689
    if (body != null)
 
1690
    {
 
1691
        for (content = body->content;
 
1692
            content != null;
 
1693
            content = content->next)
 
1694
            PPrintTree(fout, null, 0, lexer, content);
 
1695
    }
 
1696
}
 
1697
 
 
1698
void PPrintTree(Out *fout, uint mode, uint indent,
 
1699
                    Lexer *lexer, Node *node)
 
1700
{
 
1701
    Node *content, *last;
 
1702
 
 
1703
    if (node == null)
 
1704
        return;
 
1705
 
 
1706
    if (node->type == TextNode ||
 
1707
        (node->type == CDATATag && EscapeCdata))
 
1708
        PPrintText(fout, mode, indent,
 
1709
                    lexer, node->start, node->end);
 
1710
    else if (node->type == CommentTag)
 
1711
    {
 
1712
        PPrintComment(fout, indent, lexer, node);
 
1713
    }
 
1714
    else if (node->type == RootNode)
 
1715
    {
 
1716
        for (content = node->content;
 
1717
                content != null;
 
1718
                content = content->next)
 
1719
           PPrintTree(fout, mode, indent, lexer, content);
 
1720
    }
 
1721
    else if (node->type == DocTypeTag)
 
1722
        PPrintDocType(fout, indent, lexer, node);
 
1723
    else if (node->type == ProcInsTag)
 
1724
        PPrintPI(fout, indent, lexer, node);
 
1725
    else if (node->type == XmlDecl)
 
1726
        PPrintXmlDecl(fout, indent, lexer, node);
 
1727
    else if (node->type == CDATATag)
 
1728
        PPrintCDATA(fout, indent, lexer, node);
 
1729
    else if (node->type == SectionTag)
 
1730
        PPrintSection(fout, indent, lexer, node);
 
1731
    else if (node->type == AspTag)
 
1732
        PPrintAsp(fout, indent, lexer, node);
 
1733
    else if (node->type == JsteTag)
 
1734
        PPrintJste(fout, indent, lexer, node);
 
1735
    else if (node->type == PhpTag)
 
1736
        PPrintPhp(fout, indent, lexer, node);
 
1737
    else if (node->tag->model & CM_EMPTY || (node->type == StartEndTag && !xHTML))
 
1738
    {
 
1739
        if (!(node->tag->model & CM_INLINE))
 
1740
            PCondFlushLine(fout, indent);
 
1741
 
 
1742
        if (node->tag == tag_br && node->prev && node->prev->tag != tag_br && BreakBeforeBR)
 
1743
            PFlushLine(fout, indent);
 
1744
 
 
1745
        if (MakeClean && node->tag == tag_wbr)
 
1746
            PPrintString(fout, indent, " ");
 
1747
        else
 
1748
            PPrintTag(lexer, fout, mode, indent, node);
 
1749
 
 
1750
        if (node->tag == tag_param || node->tag == tag_area)
 
1751
            PCondFlushLine(fout, indent);
 
1752
        else if (node->tag == tag_br || node->tag == tag_hr)
 
1753
            PFlushLine(fout, indent);
 
1754
    }
 
1755
    else /* some kind of container element */
 
1756
    {
 
1757
        if (node->tag && node->tag->parser == ParsePre)
 
1758
        {
 
1759
            PCondFlushLine(fout, indent);
 
1760
 
 
1761
            indent = 0;
 
1762
            PCondFlushLine(fout, indent);
 
1763
            PPrintTag(lexer, fout, mode, indent, node);
 
1764
            PFlushLine(fout, indent);
 
1765
 
 
1766
            for (content = node->content;
 
1767
                    content != null;
 
1768
                    content = content->next)
 
1769
                PPrintTree(fout, (mode | PREFORMATTED | NOWRAP), indent, lexer, content);
 
1770
 
 
1771
            PCondFlushLine(fout, indent);
 
1772
            PPrintEndTag(fout, mode, indent, node);
 
1773
            PFlushLine(fout, indent);
 
1774
 
 
1775
            if (IndentContent == no && node->next != null)
 
1776
                PFlushLine(fout, indent);
 
1777
        }
 
1778
        else if (node->tag == tag_style || node->tag == tag_script)
 
1779
        {
 
1780
            PPrintScriptStyle( fout, (mode | PREFORMATTED | NOWRAP |CDATA),
 
1781
                               indent, lexer, node );
 
1782
        }
 
1783
        else if (node->tag->model & CM_INLINE)
 
1784
        {
 
1785
            if (MakeClean)
 
1786
            {
 
1787
                /* discards <font> and </font> tags */
 
1788
                if (node->tag == tag_font)
 
1789
                {
 
1790
                    for (content = node->content;
 
1791
                            content != null;
 
1792
                            content = content->next)
 
1793
                        PPrintTree(fout, mode, indent, lexer, content);
 
1794
                    return;
 
1795
                }
 
1796
 
 
1797
                /* replace <nobr>...</nobr> by &nbsp; or &#160; etc. */
 
1798
                if (node->tag == tag_nobr)
 
1799
                {
 
1800
                    for (content = node->content;
 
1801
                            content != null;
 
1802
                            content = content->next)
 
1803
                        PPrintTree(fout, mode|NOWRAP, indent, lexer, content);
 
1804
                    return;
 
1805
                }
 
1806
            }
 
1807
 
 
1808
            /* otherwise a normal inline element */
 
1809
 
 
1810
            PPrintTag(lexer, fout, mode, indent, node);
 
1811
 
 
1812
            /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
 
1813
 
 
1814
            if (ShouldIndent(node))
 
1815
            {
 
1816
                PCondFlushLine(fout, indent);
 
1817
                indent += spaces;
 
1818
 
 
1819
                for (content = node->content;
 
1820
                        content != null;
 
1821
                        content = content->next)
 
1822
                    PPrintTree(fout, mode, indent, lexer, content);
 
1823
 
 
1824
                PCondFlushLine(fout, indent);
 
1825
                indent -= spaces;
 
1826
                PCondFlushLine(fout, indent);
 
1827
            }
 
1828
            else
 
1829
            {
 
1830
 
 
1831
                for (content = node->content;
 
1832
                        content != null;
 
1833
                        content = content->next)
 
1834
                    PPrintTree(fout, mode, indent, lexer, content);
 
1835
            }
 
1836
 
 
1837
            PPrintEndTag(fout, mode, indent, node);
 
1838
        }
 
1839
        else /* other tags */
 
1840
        {
 
1841
            PCondFlushLine(fout, indent);
 
1842
 
 
1843
            if (SmartIndent && node->prev != null)
 
1844
                PFlushLine(fout, indent);
 
1845
 
 
1846
            /* do not omit elements with attributes */
 
1847
            if (HideEndTags == no ||
 
1848
                !(node->tag && (node->tag->model & CM_OMITST)) ||
 
1849
                node->attributes != null)
 
1850
            {
 
1851
                PPrintTag(lexer, fout, mode, indent, node);
 
1852
 
 
1853
                if (ShouldIndent(node))
 
1854
                    PCondFlushLine(fout, indent);
 
1855
                else if (node->tag->model & CM_HTML || node->tag == tag_noframes ||
 
1856
                            (node->tag->model & CM_HEAD && !(node->tag == tag_title)))
 
1857
                    PFlushLine(fout, indent);
 
1858
            }
 
1859
 
 
1860
            if (node->tag == tag_body && BurstSlides)
 
1861
                PPrintSlide(fout, mode, (IndentContent ? indent+spaces : indent), lexer);
 
1862
            else
 
1863
            {
 
1864
                last = null;
 
1865
 
 
1866
                for (content = node->content;
 
1867
                        content != null; content = content->next)
 
1868
                {
 
1869
                    /* kludge for naked text before block level tag */
 
1870
                    if (last && !IndentContent && last->type == TextNode &&
 
1871
                        content->tag && content->tag->model & CM_BLOCK)
 
1872
                    {
 
1873
                        PFlushLine(fout, indent);
 
1874
                        PFlushLine(fout, indent);
 
1875
                    }
 
1876
 
 
1877
                    PPrintTree(fout, mode,
 
1878
                        (ShouldIndent(node) ? indent+spaces : indent), lexer, content);
 
1879
 
 
1880
                    last = content;
 
1881
                }
 
1882
            }
 
1883
 
 
1884
            /* don't flush line for td and th */
 
1885
            if (ShouldIndent(node) ||
 
1886
                ((node->tag->model & CM_HTML || node->tag == tag_noframes ||
 
1887
                    (node->tag->model & CM_HEAD && !(node->tag == tag_title)))
 
1888
                    && HideEndTags == no))
 
1889
            {
 
1890
                PCondFlushLine(fout, (IndentContent ? indent+spaces : indent));
 
1891
 
 
1892
                if (HideEndTags == no || !(node->tag->model & CM_OPT))
 
1893
                {
 
1894
                    PPrintEndTag(fout, mode, indent, node);
 
1895
                    PFlushLine(fout, indent);
 
1896
                }
 
1897
            }
 
1898
            else
 
1899
            {
 
1900
                if (HideEndTags == no || !(node->tag->model & CM_OPT))
 
1901
                    PPrintEndTag(fout, mode, indent, node);
 
1902
 
 
1903
                PFlushLine(fout, indent);
 
1904
            }
 
1905
 
 
1906
            if (IndentContent == no &&
 
1907
                node->next != null &&
 
1908
                HideEndTags == no &&
 
1909
                (node->tag->model & (CM_BLOCK|CM_LIST|CM_DEFLIST|CM_TABLE)))
 
1910
            {
 
1911
                PFlushLine(fout, indent);
 
1912
            }
 
1913
        }
 
1914
    }
 
1915
}
 
1916
 
 
1917
void PPrintXMLTree(Out *fout, uint mode, uint indent,
 
1918
                    Lexer *lexer, Node *node)
 
1919
{
 
1920
    if (node == null)
 
1921
        return;
 
1922
 
 
1923
    if (node->type == TextNode  ||
 
1924
        (node->type == CDATATag && EscapeCdata))
 
1925
    {
 
1926
        PPrintText(fout, mode, indent,
 
1927
                    lexer, node->start, node->end);
 
1928
    }
 
1929
    else if (node->type == CommentTag)
 
1930
    {
 
1931
        PCondFlushLine(fout, indent);
 
1932
        PPrintComment(fout, 0, lexer, node);
 
1933
        PCondFlushLine(fout, 0);
 
1934
    }
 
1935
    else if (node->type == RootNode)
 
1936
    {
 
1937
        Node *content;
 
1938
 
 
1939
        for (content = node->content;
 
1940
                content != null;
 
1941
                content = content->next)
 
1942
           PPrintXMLTree(fout, mode, indent, lexer, content);
 
1943
    }
 
1944
    else if (node->type == DocTypeTag)
 
1945
        PPrintDocType(fout, indent, lexer, node);
 
1946
    else if (node->type == ProcInsTag)
 
1947
        PPrintPI(fout, indent, lexer, node);
 
1948
    else if (node->type == XmlDecl)
 
1949
        PPrintXmlDecl(fout, indent, lexer, node);
 
1950
    else if (node->type == CDATATag)
 
1951
        PPrintCDATA(fout, indent, lexer, node);
 
1952
    else if (node->type == SectionTag)
 
1953
        PPrintSection(fout, indent, lexer, node);
 
1954
    else if (node->type == AspTag)
 
1955
        PPrintAsp(fout, indent, lexer, node);
 
1956
    else if (node->type == JsteTag)
 
1957
        PPrintJste(fout, indent, lexer, node);
 
1958
    else if (node->type == PhpTag)
 
1959
        PPrintPhp(fout, indent, lexer, node);
 
1960
    else if (node->tag->model & CM_EMPTY || (node->type == StartEndTag && !xHTML))
 
1961
    {
 
1962
        PCondFlushLine(fout, indent);
 
1963
        PPrintTag(lexer, fout, mode, indent, node);
 
1964
        PFlushLine(fout, indent);
 
1965
 
 
1966
        if (node->next)
 
1967
            PFlushLine(fout, indent);
 
1968
    }
 
1969
    else /* some kind of container element */
 
1970
    {
 
1971
        Node *content;
 
1972
        Bool mixed = no;
 
1973
        int cindent;
 
1974
 
 
1975
        for (content = node->content; content; content = content->next)
 
1976
        {
 
1977
            if (content->type == TextNode)
 
1978
            {
 
1979
                mixed = yes;
 
1980
                break;
 
1981
            }
 
1982
        }
 
1983
 
 
1984
        PCondFlushLine(fout, indent);
 
1985
 
 
1986
        if (XMLPreserveWhiteSpace(node))
 
1987
        {
 
1988
            indent = 0;
 
1989
            cindent = 0;
 
1990
            mixed = no;
 
1991
        }
 
1992
        else if (mixed)
 
1993
            cindent = indent;
 
1994
        else
 
1995
            cindent = indent + spaces;
 
1996
 
 
1997
        PPrintTag(lexer, fout, mode, indent, node);
 
1998
 
 
1999
        if (!mixed)
 
2000
            PFlushLine(fout, indent);
 
2001
 
 
2002
        for (content = node->content;
 
2003
                content != null;
 
2004
                content = content->next)
 
2005
            PPrintXMLTree(fout, mode, cindent, lexer, content);
 
2006
 
 
2007
        if (!mixed)
 
2008
            PCondFlushLine(fout, cindent);
 
2009
 
 
2010
        PPrintEndTag(fout, mode, indent, node);
 
2011
        PCondFlushLine(fout, indent);
 
2012
 
 
2013
        if (node->next)
 
2014
            PFlushLine(fout, indent);
 
2015
    }
 
2016
}
 
2017
 
 
2018
/* split parse tree by h2 elements and output to separate files */
 
2019
 
 
2020
/* counts number of h2 children (if any) belonging to node */
 
2021
int CountSlides(Node *node)
 
2022
{
 
2023
    int n = 1; /* assume minimum of 1 slide */
 
2024
 
 
2025
    /* #431716 - fix by Terry Teague 01 Jul 01 */
 
2026
    if (node && node->content && node->content->tag == tag_h2)
 
2027
        n--; /* "first" slide is empty, so ignore it */
 
2028
 
 
2029
    for (node = node->content; node; node = node->next)
 
2030
        if (node->tag == tag_h2)
 
2031
            ++n;
 
2032
 
 
2033
    return n;
 
2034
}
 
2035
 
 
2036
/*
 
2037
   inserts a space gif called "dot.gif" to ensure
 
2038
   that the  slide is at least n pixels high
 
2039
 */
 
2040
static void PrintVertSpacer(Out *fout, uint indent)
 
2041
{
 
2042
    PCondFlushLine(fout, indent);
 
2043
    PPrintString(fout, indent , 
 
2044
    "<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">");
 
2045
    PCondFlushLine(fout, indent);
 
2046
}
 
2047
 
 
2048
static void PrintNavBar(Out *fout, uint indent)
 
2049
{
 
2050
    char buf[128];
 
2051
 
 
2052
    PCondFlushLine(fout, indent);
 
2053
    PPrintString(fout, indent , "<center><small>");
 
2054
 
 
2055
    if (slide > 1)
 
2056
    {
 
2057
        sprintf(buf, "<a href=\"slide%03d.html\">previous</a> | ", slide-1); /* #427666 - fix by Eric Rossen 02 Aug 00 */
 
2058
        PPrintString(fout, indent , buf);
 
2059
        PCondFlushLine(fout, indent);
 
2060
 
 
2061
        if (slide < count)
 
2062
            PPrintString(fout, indent , "<a href=\"slide001.html\">start</a> | "); /* #427666 - fix by Eric Rossen 02 Aug 00 */
 
2063
        else
 
2064
            PPrintString(fout, indent , "<a href=\"slide001.html\">start</a>"); /* #427666 - fix by Eric Rossen 02 Aug 00 */
 
2065
 
 
2066
        PCondFlushLine(fout, indent);
 
2067
    }
 
2068
 
 
2069
    if (slide < count)
 
2070
    {
 
2071
        sprintf(buf, "<a href=\"slide%03d.html\">next</a>", slide+1); /* #427666 - fix by Eric Rossen 02 Aug 00 */
 
2072
        PPrintString(fout, indent , buf);
 
2073
    }
 
2074
 
 
2075
    PPrintString(fout, indent , "</small></center>");
 
2076
    PCondFlushLine(fout, indent);
 
2077
}
 
2078
 
 
2079
/*
 
2080
  Called from PPrintTree to print the content of a slide from
 
2081
  the node slidecontent. On return slidecontent points to the
 
2082
  node starting the next slide or null. The variables slide
 
2083
  and count are used to customise the navigation bar.
 
2084
*/
 
2085
void PPrintSlide(Out *fout, uint mode, uint indent, Lexer *lexer)
 
2086
{
 
2087
    Node *content, *last;
 
2088
    char buf[256];
 
2089
 
 
2090
    /* insert div for onclick handler */
 
2091
    sprintf(buf, "<div onclick=\"document.location='slide%03d.html'\">", /* #427666 - fix by Eric Rossen 02 Aug 00 */
 
2092
                    (slide < count ? slide + 1 : 1));
 
2093
    PPrintString(fout, indent, buf);
 
2094
    PCondFlushLine(fout, indent);
 
2095
 
 
2096
    /* first print the h2 element and navbar */
 
2097
    if (slidecontent && slidecontent->tag == tag_h2) /* #431716 - fix by Terry Teague 01 Jul 01 */
 
2098
    {
 
2099
        PrintNavBar(fout, indent);
 
2100
 
 
2101
        /* now print an hr after h2 */
 
2102
 
 
2103
        AddC('<', linelen++);
 
2104
 
 
2105
 
 
2106
        AddC(FoldCase('h', UpperCaseTags), linelen++);
 
2107
        AddC(FoldCase('r', UpperCaseTags), linelen++);
 
2108
 
 
2109
        if (XmlOut == yes)
 
2110
            PPrintString(fout, indent , " />");
 
2111
        else
 
2112
            AddC('>', linelen++);
 
2113
 
 
2114
 
 
2115
        if (IndentContent == yes)
 
2116
            PCondFlushLine(fout, indent);
 
2117
 
 
2118
        /* PrintVertSpacer(fout, indent); */
 
2119
 
 
2120
        /*PCondFlushLine(fout, indent); */
 
2121
 
 
2122
        /* print the h2 element */
 
2123
        PPrintTree(fout, mode,
 
2124
            (IndentContent ? indent+spaces : indent), lexer, slidecontent);
 
2125
 
 
2126
        slidecontent = slidecontent->next;
 
2127
    }
 
2128
    
 
2129
    /* now continue until we reach the next h2 (or end) */
 
2130
 
 
2131
    last = null;
 
2132
    content = slidecontent;
 
2133
 
 
2134
    for (; content != null; content = content->next)
 
2135
    {
 
2136
        if (content->tag == tag_h2)
 
2137
            break;
 
2138
 
 
2139
        /* kludge for naked text before block level tag */
 
2140
        if (last && !IndentContent && last->type == TextNode &&
 
2141
            content->tag && content->tag->model & CM_BLOCK)
 
2142
        {
 
2143
            PFlushLine(fout, indent);
 
2144
            PFlushLine(fout, indent);
 
2145
        }
 
2146
 
 
2147
        PPrintTree(fout, mode,
 
2148
            (IndentContent ? indent+spaces : indent), lexer, content);
 
2149
 
 
2150
        last = content;
 
2151
    }
 
2152
 
 
2153
    slidecontent = content;
 
2154
 
 
2155
    /* now print epilog */
 
2156
 
 
2157
    PCondFlushLine(fout, indent);
 
2158
 
 
2159
    PPrintString(fout, indent , "<br clear=\"all\">");
 
2160
    PCondFlushLine(fout, indent);
 
2161
 
 
2162
    AddC('<', linelen++);
 
2163
 
 
2164
 
 
2165
    AddC(FoldCase('h', UpperCaseTags), linelen++);
 
2166
    AddC(FoldCase('r', UpperCaseTags), linelen++);
 
2167
 
 
2168
    if (XmlOut == yes)
 
2169
        PPrintString(fout, indent , " />");
 
2170
    else
 
2171
        AddC('>', linelen++);
 
2172
 
 
2173
 
 
2174
    if (IndentContent == yes)
 
2175
        PCondFlushLine(fout, indent);
 
2176
 
 
2177
    PrintNavBar(fout, indent);
 
2178
 
 
2179
    /* end tag for div */
 
2180
    PPrintString(fout, indent, "</div>");
 
2181
    PCondFlushLine(fout, indent);
 
2182
}
 
2183
 
 
2184
/*
 
2185
Add meta element for page transition effect, this works on IE but not NS
 
2186
*/
 
2187
 
 
2188
void AddTransitionEffect(Lexer *lexer, Node *root, int effect, float duration)
 
2189
{
 
2190
    Node *head = FindHEAD(root);
 
2191
    char transition[128];
 
2192
 
 
2193
    if (0 <= effect && effect <= 23)
 
2194
        sprintf(transition, "revealTrans(Duration=%g,Transition=%d)", duration, effect);
 
2195
    else
 
2196
        sprintf(transition, "blendTrans(Duration=%g)", duration);
 
2197
 
 
2198
    if (head)
 
2199
    {
 
2200
        Node *meta = InferredTag(lexer, "meta");
 
2201
        AddAttribute(meta, "http-equiv", "Page-Enter");
 
2202
        AddAttribute(meta, "content", transition);
 
2203
        InsertNodeAtStart(head, meta);
 
2204
    }
 
2205
}
 
2206
 
 
2207
void CreateSlides(Lexer *lexer, Node *root)
 
2208
{
 
2209
    Node *body;
 
2210
    char buf[128];
 
2211
    Out out;
 
2212
    FILE *fp;
 
2213
 
 
2214
    body = FindBody(root);
 
2215
    count = CountSlides(body);
 
2216
    slidecontent = body->content;
 
2217
    AddTransitionEffect(lexer, root, EFFECT_BLEND, 3.0);
 
2218
 
 
2219
    for (slide = 1; slide <= count; ++slide)
 
2220
    {
 
2221
        sprintf(buf, "slide%03d.html", slide); /* #427666 - fix by Eric Rossen 02 Aug 00 */
 
2222
        out.state = FSM_ASCII;
 
2223
        out.encoding = outCharEncoding;
 
2224
 
 
2225
        if ((fp = fopen(buf, "w")))
 
2226
        {
 
2227
            out.fp = fp;
 
2228
            PPrintTree(&out, null, 0, lexer, root);
 
2229
            PFlushLine(&out, 0);
 
2230
            fclose(fp);
 
2231
        }
 
2232
    }
 
2233
 
 
2234
    /*
 
2235
     delete superfluous slides by deleting slideN.html
 
2236
     for N = count+1, count+2, etc. until no such file
 
2237
     is found.     
 
2238
    */
 
2239
 
 
2240
    for (;;)
 
2241
    {
 
2242
        sprintf(buf, "slide%03d.html", slide); /* #427666 - fix by Eric Rossen 02 Aug 00 */
 
2243
 
 
2244
        if (unlink(buf) != 0)
 
2245
            break;
 
2246
 
 
2247
        ++slide;
 
2248
    }
 
2249
}