home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Amiga 13 / MA_Cover_13.bin / source / c / tidy26jul99 / pprint.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-12-08  |  35.4 KB  |  1,464 lines

  1. /*
  2.   pprint.c -- pretty print parse tree  (c) 1998 (W3C) MIT, INRIA, Keio University
  3.   See tidy.c for the copyright notice.
  4. */
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include "platform.h"
  10. #include "html.h"
  11.  
  12. /*
  13.   Block-level and unknown elements are printed on
  14.   new lines and their contents indented 2 spaces
  15.  
  16.   Inline elements are printed inline.
  17.  
  18.   Inline content is wrapped on spaces (except in
  19.   attribute values or preformatted text, after
  20.   start tags and before end tags
  21. */
  22.  
  23. #define NORMAL        0
  24. #define PREFORMATTED  1
  25. #define COMMENT       2
  26. #define ATTRIBVALUE   4
  27. #define NOWRAP        8
  28. #define CDATA         16
  29.  
  30. extern int CharEncoding;
  31.  
  32. static uint *linebuf;
  33. static uint lbufsize;
  34. static uint linelen;
  35. static uint wraphere;
  36. static Bool InAttVal;
  37. static Bool InString;
  38.  
  39. static int slide, count;
  40. static Node *slidecontent;
  41.  
  42. int foo;  /* debug */
  43.  
  44. /*
  45.   1010  A
  46.   1011  B
  47.   1100  C
  48.   1101  D
  49.   1110  E
  50.   1111  F
  51. */
  52.  
  53. /* return one less that the number of bytes used by UTF-8 char */
  54. /* str points to 1st byte, *ch initialized to 1st byte */
  55. uint GetUTF8(unsigned char *str, uint *ch)
  56. {
  57.     uint c, n, i, count;
  58.  
  59.     c = str[0];
  60.  
  61.     if ((c & 0xE0) == 0xC0)  /* 110X XXXX  two bytes */
  62.     {
  63.         n = c & 31;
  64.         count = 1;
  65.     }
  66.     else if ((c & 0xF0) == 0xE0)  /* 1110 XXXX  three bytes */
  67.     {
  68.         n = c & 15;
  69.         count = 2;
  70.     }
  71.     else if ((c & 0xF8) == 0xF0)  /* 1111 0XXX  four bytes */
  72.     {
  73.         n = c & 7;
  74.         count = 3;
  75.     }
  76.     else if ((c & 0xFC) == 0xF8)  /* 1111 10XX  five bytes */
  77.     {
  78.         n = c & 3;
  79.         count = 4;
  80.     }
  81.     else if ((c & 0xFE) == 0xFC)       /* 1111 110X  six bytes */
  82.  
  83.     {
  84.         n = c & 1;
  85.         count = 5;
  86.     }
  87.     else  /* 0XXX XXXX one byte */
  88.     {
  89.         *ch = c;
  90.         return 0;
  91.     }
  92.  
  93.     /* successor bytes should have the form 10XX XXXX */
  94.     for (i = 1; i <= count; ++i)
  95.     {
  96.         c = str[i];
  97.         n = (n << 6) | (c & 0x3F);
  98.     }
  99.  
  100.     *ch = n;
  101.     return count;
  102. }
  103.  
  104. void FreePrintBuf(void)
  105. {
  106.     if (linebuf)
  107.         MemFree(linebuf);
  108. }
  109.  
  110. void AddC(uint c, uint index)
  111. {
  112.     if (index + 1 >= lbufsize)
  113.     {
  114.         while (index + 1 >= lbufsize)
  115.         {
  116.             if (lbufsize == 0)
  117.                 lbufsize = 256;
  118.             else
  119.                 lbufsize = lbufsize * 2;
  120.         }
  121.  
  122.        linebuf = (uint *)MemRealloc(linebuf, lbufsize*sizeof(uint));
  123.     }
  124.  
  125.     linebuf[index] = (uint)c;
  126. }
  127.  
  128. void WrapLine(Out *fout, uint indent)
  129. {
  130.     uint i, *p, *q;
  131.  
  132.     if (wraphere == 0)
  133.         return;
  134.  
  135.     for (i = 0; i < indent; ++i)
  136.         outc(' ', fout);
  137.  
  138.     for (i = 0; i < wraphere; ++i)
  139.         outc(linebuf[i], fout);
  140.  
  141.     if (InString)
  142.     {
  143.         outc(' ', fout);
  144.         outc('\\', fout);
  145.     }
  146.  
  147.     outc('\n', fout);
  148.  
  149.     if (linelen > wraphere)
  150.     {
  151.         p = linebuf;
  152.  
  153.         if (linebuf[wraphere] == ' ')
  154.             ++wraphere;
  155.  
  156.         q = linebuf + wraphere;
  157.         AddC('\0', linelen);
  158.  
  159.         while (*p++ = *q++);
  160.             linelen -= wraphere;
  161.     }
  162.     else
  163.         linelen = 0;
  164.  
  165.     wraphere = 0;
  166. }
  167.  
  168. void WrapAttrVal(Out *fout, uint indent, Bool inString)
  169. {
  170.     uint i, *p, *q;
  171.  
  172.     for (i = 0; i < indent; ++i)
  173.         outc(' ', fout);
  174.  
  175.     for (i = 0; i < wraphere; ++i)
  176.         outc(linebuf[i], fout);
  177.  
  178.     outc(' ', fout);
  179.  
  180.     if (inString)
  181.         outc('\\', fout);
  182.  
  183.     outc('\n', fout);
  184.  
  185.     if (linelen > wraphere)
  186.     {
  187.         p = linebuf;
  188.  
  189.         if (linebuf[wraphere] == ' ')
  190.             ++wraphere;
  191.  
  192.         q = linebuf + wraphere;
  193.         AddC('\0', linelen);
  194.  
  195.         while ((*p++ = *q++));
  196.             linelen -= wraphere;
  197.     }
  198.     else
  199.         linelen = 0;
  200.  
  201.     wraphere = 0;
  202. }
  203.  
  204. void PFlushLine(Out *fout, uint indent)
  205. {
  206.     uint i;
  207.  
  208.     if (linelen > 0)
  209.     {
  210.         if (indent + linelen >= wraplen)
  211.             WrapLine(fout, indent);
  212.  
  213.         if (!InAttVal || IndentAttributes)
  214.         {
  215.             for (i = 0; i < indent; ++i)
  216.                 outc(' ', fout);
  217.         }
  218.  
  219.         for (i = 0; i < linelen; ++i)
  220.             outc(linebuf[i], fout);
  221.     }
  222.  
  223.     outc('\n', fout);
  224.     linelen = wraphere = 0;
  225.     InAttVal = no;
  226. }
  227.  
  228. void PCondFlushLine(Out *fout, uint indent)
  229. {
  230.     uint i;
  231.  
  232.     if (linelen > 0)
  233.     {
  234.         if (indent + linelen >= wraplen)
  235.             WrapLine(fout, indent);
  236.  
  237.         if (!InAttVal || IndentAttributes)
  238.         {
  239.             for (i = 0; i < indent; ++i)
  240.                 outc(' ', fout);
  241.         }
  242.  
  243.         for (i = 0; i < linelen; ++i)
  244.             outc(linebuf[i], fout);
  245.  
  246.         outc('\n', fout);
  247.         linelen = wraphere = 0;
  248.         InAttVal = no;
  249.     }
  250. }
  251.  
  252. void PPrintChar(uint c, uint mode)
  253. {
  254.     char *p, entity[128];
  255.  
  256.     if (c == ' ' && !(mode & (PREFORMATTED | COMMENT | ATTRIBVALUE)))
  257.     {
  258.         /* coerce a space character to a non-breaking space */
  259.         if (mode & NOWRAP)
  260.         {
  261.             /* by default XML doesn't define   */
  262.             if (NumEntities || XmlTags)
  263.             {
  264.                 AddC('&', linelen++);
  265.                 AddC('#', linelen++);
  266.                 AddC('1', linelen++);
  267.                 AddC('6', linelen++);
  268.                 AddC('0', linelen++);
  269.                 AddC(';', linelen++);
  270.             }
  271.             else /* otherwise use named entity */
  272.             {
  273.                 AddC('&', linelen++);
  274.                 AddC('n', linelen++);
  275.                 AddC('b', linelen++);
  276.                 AddC('s', linelen++);
  277.                 AddC('p', linelen++);
  278.                 AddC(';', linelen++);
  279.             }
  280.             return;
  281.         }
  282.         else
  283.             wraphere = linelen;
  284.     }
  285.  
  286.     /* comment characters are passed raw */
  287.     if (mode & COMMENT)
  288.     {
  289.         AddC(c, linelen++);
  290.         return;
  291.     }
  292.  
  293.     /* except in CDATA map < to < etc. */
  294.     if (! (mode & CDATA) )
  295.     {
  296.         if (c == '<')
  297.         {
  298.             AddC('&', linelen++);
  299.             AddC('l', linelen++);
  300.             AddC('t', linelen++);
  301.             AddC(';', linelen++);
  302.             return;
  303.         }
  304.             
  305.         if (c == '>')
  306.         {
  307.             AddC('&', linelen++);
  308.             AddC('g', linelen++);
  309.             AddC('t', linelen++);
  310.             AddC(';', linelen++);
  311.             return;
  312.         }
  313.  
  314.         /*
  315.           naked '&' chars can be left alone or
  316.           quoted as & The latter is required
  317.           for XML where naked '&' are illegal.
  318.         */
  319.         if (c == '&' && QuoteAmpersand)
  320.         {
  321.             AddC('&', linelen++);
  322.             AddC('a', linelen++);
  323.             AddC('m', linelen++);
  324.             AddC('p', linelen++);
  325.             AddC(';', linelen++);
  326.             return;
  327.         }
  328.  
  329.         if (c == '"' && QuoteMarks)
  330.         {
  331.             AddC('&', linelen++);
  332.             AddC('q', linelen++);
  333.             AddC('u', linelen++);
  334.             AddC('o', linelen++);
  335.             AddC('t', linelen++);
  336.             AddC(';', linelen++);
  337.             return;
  338.         }
  339.  
  340.         if (c == '\'' && QuoteMarks)
  341.         {
  342.             AddC('&', linelen++);
  343.             AddC('#', linelen++);
  344.             AddC('3', linelen++);
  345.             AddC('9', linelen++);
  346.             AddC(';', linelen++);
  347.             return;
  348.         }
  349.  
  350.         if (c == 160)
  351.         {
  352.             if (QuoteNbsp)
  353.             {
  354.                 AddC('&', linelen++);
  355.  
  356.                 if (NumEntities)
  357.                 {
  358.                     AddC('#', linelen++);
  359.                     AddC('1', linelen++);
  360.                     AddC('6', linelen++);
  361.                     AddC('0', linelen++);
  362.                 }
  363.                 else
  364.                 {
  365.                     AddC('n', linelen++);
  366.                     AddC('b', linelen++);
  367.                     AddC('s', linelen++);
  368.                     AddC('p', linelen++);
  369.                 }
  370.  
  371.                 AddC(';', linelen++);
  372.             }
  373.             else
  374.                 AddC(c, linelen++);
  375.  
  376.             return;
  377.         }
  378.     }
  379.  
  380.     /* otherwise ISO 2022 characters are passed raw */
  381.     if (CharEncoding == ISO2022 || CharEncoding == RAW)
  382.     {
  383.         AddC(c, linelen++);
  384.         return;
  385.     }
  386.  
  387.     /* if preformatted text, map   to space */
  388.     if (c == 160 && (mode & PREFORMATTED))
  389.     {
  390.         AddC(' ', linelen++);
  391.         return;
  392.     }
  393.  
  394.     /*
  395.      Filters from Word and PowerPoint often use smart
  396.      quotes resulting in character codes between 128
  397.      and 159. Unfortunately, the corresponding HTML 4.0
  398.      entities for these are not widely supported. The
  399.      following converts dashes and quotation marks to
  400.      the nearest ASCII equivalent.
  401.     */
  402.  
  403.     if (MakeClean)
  404.     {
  405.         if (c > 126 && c < 160)
  406.         {
  407.             if (c == 8211)
  408.                 c = '-';  /* en dash */
  409.             else if (c == 8212)
  410.                 c = '-';  /* em dash */
  411.             else if (c == 8216)
  412.                 c = '\'';  /* single left quotation mark */
  413.             else if (c == 8217)
  414.                 c = '\'';  /* single right quotation mark */
  415.             else if (c == 8220)
  416.                 c = '"';  /* double left quotation mark */
  417.             else if (c == 8221)
  418.                 c = '"';  /* double right quotation mark */
  419.         }
  420.     }
  421.  
  422.     /* don't map latin-1 chars to entities */
  423.     if (CharEncoding == LATIN1)
  424.     {
  425.         if (c > 255)  /* multi byte chars */
  426.         {
  427.             if (!NumEntities && (p = EntityName(c)) != null)
  428.                 sprintf(entity, "&%s;", p);
  429.             else
  430.                 sprintf(entity, "&#%u;", c);
  431.  
  432.             for (p = entity; *p; ++p)
  433.                 AddC(*p, linelen++);
  434.  
  435.             return;
  436.         }
  437.  
  438.         if (c > 126 && c < 160)
  439.         {
  440.             sprintf(entity, "&#%d;", c);
  441.  
  442.             for (p = entity; *p; ++p)
  443.                 AddC(*p, linelen++);
  444.  
  445.             return;
  446.         }
  447.  
  448.         AddC(c, linelen++);
  449.         return;
  450.     }
  451.  
  452.     /* don't map utf8 chars to entities */
  453.     if (CharEncoding == UTF8)
  454.     {
  455.         AddC(c, linelen++);
  456.         return;
  457.     }
  458.  
  459.     /* use numeric entities only  for XML */
  460.     if (XmlTags)
  461.     {
  462.         /* if ASCII use numeric entities for chars > 127 */
  463.         if (c > 127 && CharEncoding == ASCII)
  464.         {
  465.             sprintf(entity, "&#%u;", c);
  466.  
  467.             for (p = entity; *p; ++p)
  468.                 AddC(*p, linelen++);
  469.  
  470.             return;
  471.         }
  472.  
  473.         /* otherwise output char raw */
  474.         AddC(c, linelen++);
  475.         return;
  476.     }
  477.  
  478.     /* default treatment for ASCII */
  479.     if (c > 126 || (c < ' ' && c != '\t'))
  480.     {
  481.         if (!NumEntities && (p = EntityName(c)) != null)
  482.             sprintf(entity, "&%s;", p);
  483.         else
  484.             sprintf(entity, "&#%u;", c);
  485.  
  486.         for (p = entity; *p; ++p)
  487.             AddC(*p, linelen++);
  488.  
  489.         return;
  490.     }
  491.  
  492.     AddC(c, linelen++);
  493. }
  494.  
  495. /* 
  496.   The line buffer is uint not char so we can
  497.   hold Unicode values unencoded. The translation
  498.   to UTF-8 is deferred to the outc routine called
  499.   to flush the line buffer.
  500. */
  501. void PPrintText(Out *fout, uint mode, uint indent,
  502.                 Lexer *lexer, uint start, uint end)
  503. {
  504.     uint i, c;
  505.  
  506.     for (i = start; i < end; ++i)
  507.     {
  508.         if (indent + linelen >= wraplen)
  509.             WrapLine(fout, indent);
  510.  
  511.         c = (unsigned char)lexer->lexbuf[i];
  512.  
  513.         /* look for UTF-8 multibyte character */
  514.         if (c > 0x7F)
  515.              i += GetUTF8((unsigned char *)lexer->lexbuf + i, &c);
  516.  
  517.         if (c == '\n')
  518.         {
  519.             PFlushLine(fout, indent);
  520.             continue;
  521.         }
  522.  
  523.         PPrintChar(c, mode);
  524.     }
  525. }
  526.  
  527. void PPrintString(Out *fout, uint indent, char *str)
  528. {
  529.     while (*str != '\0')
  530.         AddC(*str++, linelen++);
  531. }
  532.  
  533. void PPrintAttrValue(Out *fout, uint indent, char *value, int delim, Bool wrappable)
  534. {
  535.     uint c;
  536.     Bool wasinstring = no;
  537.  
  538.     if (delim == null)
  539.         delim = '"';
  540.  
  541.     AddC('=', linelen++);
  542.  
  543.     if (indent + linelen < wraplen)
  544.         wraphere = linelen;
  545.  
  546.     if (indent + linelen >= wraplen)
  547.         WrapLine(fout, indent);
  548.  
  549.     if (indent + linelen < wraplen)
  550.         wraphere = linelen;
  551.     else
  552.         PCondFlushLine(fout, indent);
  553.  
  554.     AddC(delim, linelen++);
  555.  
  556.     if (value)
  557.     {
  558.         InString = no;
  559.  
  560.         while (*value != '\0')
  561.         {
  562.             c = (unsigned char)*value;
  563.  
  564.             if (wrappable && c == ' ' && indent + linelen < wraplen)
  565.             {
  566.                 wraphere = linelen;
  567.                 wasinstring = InString;
  568.             }
  569.  
  570.             if (wrappable && wraphere > 0 && indent + linelen >= wraplen)
  571.                 WrapAttrVal(fout, indent, wasinstring);
  572.  
  573.             if (c == (uint)delim)
  574.             {
  575.                 char *entity;
  576.  
  577.                 entity = (c == '"' ? """ : "'");
  578.  
  579.                 while (*entity != '\0')
  580.                     AddC(*entity++, linelen++);
  581.  
  582.                 ++value;
  583.                 continue;
  584.             }
  585.             else if (c == '"')
  586.             {
  587.                 if (QuoteMarks)
  588.                 {
  589.                     AddC('&', linelen++);
  590.                     AddC('q', linelen++);
  591.                     AddC('u', linelen++);
  592.                     AddC('o', linelen++);
  593.                     AddC('t', linelen++);
  594.                     AddC(';', linelen++);
  595.                 }
  596.                 else
  597.                     AddC('"', linelen++);
  598.  
  599.                 if (delim == '\'')
  600.                     InString = (Bool)(!InString);
  601.  
  602.                 ++value;
  603.                 continue;
  604.             }
  605.             else if (c == '\'')
  606.             {
  607.                 if (QuoteMarks)
  608.                 {
  609.                     AddC('&', linelen++);
  610.                     AddC('#', linelen++);
  611.                     AddC('3', linelen++);
  612.                     AddC('9', linelen++);
  613.                     AddC(';', linelen++);
  614.                 }
  615.                 else
  616.                     AddC('\'', linelen++);
  617.  
  618.                 if (delim == '"')
  619.                     InString = (Bool)(!InString);
  620.  
  621.                 ++value;
  622.                 continue;
  623.             }
  624.  
  625.             /* look for UTF-8 multibyte character */
  626.             if (c > 0x7F)
  627.                  value += GetUTF8((unsigned char *)value, &c);
  628.  
  629.             ++value;
  630.  
  631.             if (c == '\n')
  632.             {
  633.                 PFlushLine(fout, indent);
  634.                 continue;
  635.             }
  636.  
  637.             PPrintChar(c, (wrappable ? (NORMAL | ATTRIBVALUE) : (PREFORMATTED | ATTRIBVALUE)));
  638.         }
  639.     }
  640.  
  641.     InString = no;
  642.     AddC(delim, linelen++);
  643. }
  644.  
  645. void PPrintAttribute(Out *fout, uint indent, AttVal *attr)
  646. {
  647.     char *name;
  648.     Bool wrappable = yes;
  649.  
  650.     if (IndentAttributes)
  651.     {
  652.         PFlushLine(fout, indent);
  653.         indent += spaces;
  654.     }
  655.  
  656.     name = attr->attribute;
  657.  
  658.     if (indent + linelen >= wraplen)
  659.         WrapLine(fout, indent);
  660.  
  661.     if (!XmlTags && !XmlOut && attr->dict)
  662.     {
  663.         if (attr->dict->nowrap)
  664.             wrappable = no;
  665.         else if (IsScript(name))
  666.             wrappable = WrapScriptlets;
  667.     }
  668.  
  669.     if (indent + linelen < wraplen)
  670.     {
  671.         wraphere = linelen;
  672.         AddC(' ', linelen++);
  673.     }
  674.     else
  675.         PCondFlushLine(fout, indent);
  676.  
  677.     while (*name != '\0')
  678.         AddC(FoldCase(*name++, UpperCaseAttrs), linelen++);
  679.  
  680.     if (indent + linelen >= wraplen)
  681.         WrapLine(fout, indent);
  682.  
  683.     if (attr->value == null)
  684.     {
  685.         if (XmlTags || XmlOut)
  686.             PPrintAttrValue(fout, indent, attr->attribute, attr->delim, yes);
  687.         else if (!IsBoolAttribute(attr))
  688.             PPrintAttrValue(fout, indent, "", attr->delim, yes);
  689.         else if (indent + linelen < wraplen)
  690.             wraphere = linelen;
  691.  
  692.     }
  693.     else
  694.         PPrintAttrValue(fout, indent, attr->value, attr->delim, wrappable);
  695. }
  696.  
  697. void PPrintAttrs(Out *fout, uint indent, Lexer *lexer, AttVal *attr)
  698. {
  699.     if (attr)
  700.     {
  701.         if (attr->next)
  702.             PPrintAttrs(fout, indent, lexer, attr->next);
  703.  
  704.         if (attr->attribute != null)
  705.             PPrintAttribute(fout, indent, attr);
  706.         else if (attr->asp != null)
  707.         {
  708.             AddC(' ', linelen++);
  709.             PPrintAsp(fout, indent, lexer, attr->asp);
  710.         }
  711.     }
  712. }
  713.  
  714. /*
  715.  Line can be wrapped immediately after inline start tag provided
  716.  if follows a text node ending in a space, or it parent is an
  717.  inline element that that rule applies to. This behaviour was
  718.  reverse engineered from Netscape 3.0
  719. */
  720. Bool AfterSpace(Lexer *lexer, Node *node)
  721. {
  722.     Node *prev;
  723.     uint c;
  724.  
  725.     if (!node || !node->tag || !(node->tag->model & CM_INLINE))
  726.         return yes;
  727.  
  728.     prev = node->prev;
  729.  
  730.     if (prev)
  731.     {
  732.         if (prev->type == TextNode && prev->end > prev->start)
  733.         {
  734.             c = (unsigned char)lexer->lexbuf[prev->end - 1];
  735.  
  736.             if (c == 160 || c == ' ' || c == '\n')
  737.                 return yes;
  738.         }
  739.  
  740.         return no;
  741.     }
  742.  
  743.     return AfterSpace(lexer, node->parent);
  744. }
  745.  
  746. void PPrintTag(Lexer *lexer, Out *fout, uint mode, uint indent, Node *node)
  747. {
  748.     char c, *p;
  749.  
  750.     AddC('<', linelen++);
  751.  
  752.     if (node->type == EndTag)
  753.         AddC('/', linelen++);
  754.  
  755.     for (p = node->element; (c = *p); ++p)
  756.         AddC(FoldCase(c, UpperCaseTags), linelen++);
  757.  
  758.     PPrintAttrs(fout, indent, lexer, node->attributes);
  759.  
  760.     if ((XmlOut == yes || lexer->isvoyager) &&
  761.             (node->type == StartEndTag || node->tag->model & CM_EMPTY ))
  762.     {
  763.         AddC(' ', linelen++);   /* compatibility hack */
  764.         AddC('/', linelen++);
  765.     }
  766.  
  767.     AddC('>', linelen++);;
  768.  
  769.     if (node->type != StartEndTag && !(mode & PREFORMATTED))
  770.     {
  771.         if (indent + linelen >= wraplen)
  772.             WrapLine(fout, indent);
  773.  
  774.         if (indent + linelen < wraplen)
  775.         {
  776.             /*
  777.              avoid wrapping after inline start tag unless
  778.              it or its parent follows a space and its not
  779.              an empty tag (e.g. IMG) followed by </a>
  780.             */
  781.             if (AfterSpace(lexer, node))
  782.             {
  783.                 if (!(mode & NOWRAP) &&
  784.                     !((node->tag->model & CM_EMPTY) && 
  785.                       node->next == null &&
  786.                       node->parent->tag == tag_a))
  787.                 {
  788.                     wraphere = linelen;
  789.                 }
  790.             }
  791.         }
  792.         else
  793.             PCondFlushLine(fout, indent);
  794.     }
  795. }
  796.  
  797. void PPrintEndTag(Out *fout, uint mode, uint indent, Node *node)
  798. {
  799.     char c, *p;
  800.  
  801.    /*
  802.      Netscape ignores SGML standard by not ignoring a
  803.      line break before </A> or </U> etc. To avoid rendering 
  804.      this as an underlined space, I disable line wrapping
  805.      before inline end tags by the #if 0 ... #endif
  806.    */
  807. #if 0
  808.     if (indent + linelen < wraplen && !(mode & NOWRAP))
  809.         wraphere = linelen;
  810. #endif
  811.  
  812.     AddC('<', linelen++);
  813.     AddC('/', linelen++);
  814.  
  815.     for (p = node->element; (c = *p); ++p)
  816.         AddC(FoldCase(c, UpperCaseTags), linelen++);
  817.  
  818.     AddC('>', linelen++);
  819. }
  820.  
  821. void PPrintComment(Out *fout, uint indent,
  822.                    Lexer *lexer, Node *node)
  823. {
  824.     if (indent + linelen < wraplen)
  825.         wraphere = linelen;
  826.  
  827.     AddC('<', linelen++);
  828.     AddC('!', linelen++);
  829.     AddC('-', linelen++);
  830.     AddC('-', linelen++);
  831. #if 0
  832.     if (linelen < wraplen)
  833.         wraphere = linelen;
  834. #endif
  835.     PPrintText(fout, COMMENT, indent,
  836.                     lexer, node->start, node->end);
  837. #if 0
  838.     if (indent + linelen < wraplen)
  839.         wraphere = linelen;
  840. #endif
  841.     AddC('-', linelen++);
  842.     AddC('-', linelen++);
  843.     AddC('>', linelen++);
  844. }
  845.  
  846. void PPrintDocType(Out *fout, uint indent,
  847.                    Lexer *lexer, Node *node)
  848. {
  849.     if (indent + linelen < wraplen)
  850.         wraphere = linelen;
  851.  
  852.     PCondFlushLine(fout, indent);
  853.  
  854.     AddC('<', linelen++);
  855.     AddC('!', linelen++);
  856.     AddC('D', linelen++);
  857.     AddC('O', linelen++);
  858.     AddC('C', linelen++);
  859.     AddC('T', linelen++);
  860.     AddC('Y', linelen++);
  861.     AddC('P', linelen++);
  862.     AddC('E', linelen++);
  863.     AddC(' ', linelen++);
  864.  
  865.     if (indent + linelen < wraplen)
  866.         wraphere = linelen;
  867.  
  868.     PPrintText(fout, null, indent,
  869.                     lexer, node->start, node->end);
  870.  
  871.     if (linelen < wraplen)
  872.         wraphere = linelen;
  873.  
  874.     AddC('>', linelen++);
  875.     PCondFlushLine(fout, indent);
  876. }
  877.  
  878. void PPrintPI(Out *fout, uint indent,
  879.                    Lexer *lexer, Node *node)
  880. {
  881.     if (indent + linelen < wraplen)
  882.         wraphere = linelen;
  883.  
  884.     AddC('<', linelen++);
  885.     AddC('?', linelen++);
  886.  
  887.     /* set CDATA to pass < and > unescaped */
  888.     PPrintText(fout, CDATA, indent,
  889.                     lexer, node->start, node->end);
  890.  
  891.     if (lexer->lexbuf[node->end - 1] != '?')
  892.         AddC('?', linelen++);
  893.  
  894.     AddC('>', linelen++);
  895.     /* PCondFlushLine(fout, indent); */
  896. }
  897.  
  898. void PPrintAsp(Out *fout, uint indent,
  899.                    Lexer *lexer, Node *node)
  900. {
  901.     int savewraplen = wraplen;
  902.  
  903.     /* disable wrapping if so requested */
  904.  
  905.     if (!WrapAsp)
  906.         wraplen = 0xFFFFFF;  /* a very large number */
  907. #if 0
  908.     if (indent + linelen < wraplen)
  909.         wraphere = linelen;
  910. #endif
  911.     AddC('<', linelen++);
  912.     AddC('%', linelen++);
  913.  
  914.     PPrintText(fout, null, indent,
  915.                     lexer, node->start, node->end);
  916.  
  917.     AddC('%', linelen++);
  918.     AddC('>', linelen++);
  919.     /* PCondFlushLine(fout, indent); */
  920.     wraplen = savewraplen;
  921. }
  922.  
  923. Bool ShouldIndent(Node *node)
  924. {
  925.     if (IndentContent == no)
  926.         return no;
  927.  
  928.     if (node->tag->model & CM_NEW)
  929.         foo = 1;
  930.  
  931.     if (SmartIndent)
  932.     {
  933.         if (node->content && (node->tag->model & CM_NO_INDENT))
  934.         {
  935.             for (node = node->content; node; node = node->next)
  936.                 if (node->tag && node->tag->model & CM_BLOCK)
  937.                     return yes;
  938.  
  939.             return no;
  940.         }
  941.  
  942.         if (node->tag->model & CM_HEADING)
  943.             return no;
  944.  
  945.         if (node->tag == tag_p)
  946.             return no;
  947.     }
  948.  
  949.     if (node->tag->model & (CM_FIELD | CM_OBJECT))
  950.         return yes;
  951.  
  952.     if (node->tag == tag_map)
  953.         return yes;
  954.  
  955.     return (Bool)(!(node->tag->model & CM_INLINE));
  956. }
  957.  
  958. void PPrintTree(Out *fout, uint mode, uint indent,
  959.                     Lexer *lexer, Node *node)
  960. {
  961.     Node *content, *last;
  962.  
  963.     if (node == null)
  964.         return;
  965.  
  966.     if (node->type == TextNode)
  967.         PPrintText(fout, mode, indent,
  968.                     lexer, node->start, node->end);
  969.     else if (node->type == CommentTag)
  970.     {
  971.         PPrintComment(fout, indent, lexer, node);
  972.     }
  973.     else if (node->type == RootNode)
  974.     {
  975.         for (content = node->content;
  976.                 content != null;
  977.                 content = content->next)
  978.            PPrintTree(fout, mode, indent, lexer, content);
  979.     }
  980.     else if (node->type == DocTypeTag)
  981.         PPrintDocType(fout, indent, lexer, node);
  982.     else if (node->type == ProcInsTag)
  983.         PPrintPI(fout, indent, lexer, node);
  984.     else if (node->type == AspTag)
  985.         PPrintAsp(fout, indent, lexer, node);
  986.     else if (node->tag->model & CM_EMPTY || node->type == StartEndTag)
  987.     {
  988.         if (!(node->tag->model & CM_INLINE))
  989.             PCondFlushLine(fout, indent);
  990.  
  991.         if (node->tag == tag_br && node->prev && node->prev->tag != tag_br && BreakBeforeBR)
  992.             PFlushLine(fout, indent);
  993.  
  994.         if (MakeClean && node->tag == tag_wbr)
  995.             PPrintString(fout, indent, " ");
  996.         else
  997.             PPrintTag(lexer, fout, mode, indent, node);
  998.  
  999.         if (node->tag == tag_param || node->tag == tag_area)
  1000.             PCondFlushLine(fout, indent);
  1001.         else if (node->tag == tag_br || node->tag == tag_hr)
  1002.             PFlushLine(fout, indent);
  1003.     }
  1004.     else /* some kind of container element */
  1005.     {
  1006.         if (node->tag == tag_pre)
  1007.         {
  1008.             PCondFlushLine(fout, indent);
  1009.  
  1010.             indent = 0;
  1011.             PCondFlushLine(fout, indent);
  1012.             PPrintTag(lexer, fout, mode, indent, node);
  1013.             PFlushLine(fout, indent);
  1014.  
  1015.             for (content = node->content;
  1016.                     content != null;
  1017.                     content = content->next)
  1018.                 PPrintTree(fout, (mode | PREFORMATTED | NOWRAP), indent, lexer, content);
  1019.  
  1020.             PCondFlushLine(fout, indent);
  1021.             PPrintEndTag(fout, mode, indent, node);
  1022.             PFlushLine(fout, indent);
  1023.  
  1024.             if (IndentContent == no && node->next != null)
  1025.                 PFlushLine(fout, indent);
  1026.         }
  1027.         else if (node->tag == tag_style || node->tag == tag_script)
  1028.         {
  1029.             PCondFlushLine(fout, indent);
  1030.  
  1031.             indent = 0;
  1032.             PCondFlushLine(fout, indent);
  1033.             PPrintTag(lexer, fout, mode, indent, node);
  1034.             PFlushLine(fout, indent);
  1035.  
  1036.             for (content = node->content;
  1037.                     content != null;
  1038.                     content = content->next)
  1039.                 PPrintTree(fout, (mode | PREFORMATTED | NOWRAP |CDATA), indent, lexer, content);
  1040.  
  1041.             PCondFlushLine(fout, indent);
  1042.             PPrintEndTag(fout, mode, indent, node);
  1043.             PFlushLine(fout, indent);
  1044.  
  1045.             if (IndentContent == no && node->next != null)
  1046.                 PFlushLine(fout, indent);
  1047.         }
  1048.         else if (node->tag->model & CM_INLINE)
  1049.         {
  1050.             if (MakeClean)
  1051.             {
  1052.                 /* discards <font> and </font> tags */
  1053.                 if (node->tag == tag_font)
  1054.                 {
  1055.                     for (content = node->content;
  1056.                             content != null;
  1057.                             content = content->next)
  1058.                         PPrintTree(fout, mode, indent, lexer, content);
  1059.                     return;
  1060.                 }
  1061.  
  1062.                 /* replace <nobr>...</nobr> by   or   etc. */
  1063.                 if (node->tag == tag_nobr)
  1064.                 {
  1065.                     for (content = node->content;
  1066.                             content != null;
  1067.                             content = content->next)
  1068.                         PPrintTree(fout, mode|NOWRAP, indent, lexer, content);
  1069.                     return;
  1070.                 }
  1071.             }
  1072.  
  1073.             /* otherwise a normal inline element */
  1074.  
  1075.             PPrintTag(lexer, fout, mode, indent, node);
  1076.  
  1077.             /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
  1078.  
  1079.             if (ShouldIndent(node))
  1080.             {
  1081.                 PCondFlushLine(fout, indent);
  1082.                 indent += spaces;
  1083.  
  1084.                 for (content = node->content;
  1085.                         content != null;
  1086.                         content = content->next)
  1087.                     PPrintTree(fout, mode, indent, lexer, content);
  1088.  
  1089.                 PCondFlushLine(fout, indent);
  1090.                 indent -= spaces;
  1091.                 PCondFlushLine(fout, indent);
  1092.             }
  1093.             else
  1094.             {
  1095.  
  1096.                 for (content = node->content;
  1097.                         content != null;
  1098.                         content = content->next)
  1099.                     PPrintTree(fout, mode, indent, lexer, content);
  1100.             }
  1101.  
  1102.             PPrintEndTag(fout, mode, indent, node);
  1103.         }
  1104.         else /* other tags */
  1105.         {
  1106.             if (MakeClean && node->tag == tag_center)
  1107.             {
  1108.                 for (content = node->content;
  1109.                         content != null;
  1110.                         content = content->next)
  1111.                     PPrintTree(fout, mode, indent, lexer, content);
  1112.                 return;
  1113.             }
  1114.  
  1115.             PCondFlushLine(fout, indent);
  1116.  
  1117.             if (SmartIndent && node->prev != null)
  1118.                 PFlushLine(fout, indent);
  1119.  
  1120.             PPrintTag(lexer, fout, mode, indent, node);
  1121.  
  1122.             if (ShouldIndent(node))
  1123.                 PCondFlushLine(fout, indent);
  1124.             else if (node->tag->model & CM_HTML || node->tag == tag_noframes ||
  1125.                         (node->tag->model & CM_HEAD && !(node->tag == tag_title)))
  1126.                 PFlushLine(fout, indent);
  1127.  
  1128.             if (node->tag == tag_body && BurstSlides)
  1129.                 PPrintSlide(fout, mode, (IndentContent ? indent+spaces : indent), lexer);
  1130.             else
  1131.             {
  1132.                 last = null;
  1133.  
  1134.                 for (content = node->content;
  1135.                         content != null; content = content->next)
  1136.                 {
  1137.                     /* kludge for naked text before block level tag */
  1138.                     if (last && !IndentContent && last->type == TextNode &&
  1139.                         content->tag && content->tag->model & CM_BLOCK)
  1140.                     {
  1141.                         PFlushLine(fout, indent);
  1142.                         PFlushLine(fout, indent);
  1143.                     }
  1144.  
  1145.                     PPrintTree(fout, mode,
  1146.                         (ShouldIndent(node) ? indent+spaces : indent), lexer, content);
  1147.  
  1148.                     last = content;
  1149.                 }
  1150.             }
  1151.  
  1152.             /* don't flush line for td and th */
  1153.             if (ShouldIndent(node) ||
  1154.                 ((node->tag->model & CM_HTML || node->tag == tag_noframes ||
  1155.                     (node->tag->model & CM_HEAD && !(node->tag == tag_title)))
  1156.                     && HideEndTags == no))
  1157.             {
  1158.                 PCondFlushLine(fout, (IndentContent ? indent+spaces : indent));
  1159.  
  1160.                 if (HideEndTags == no || !(node->tag->model & CM_OPT))
  1161.                 {
  1162.                     PPrintEndTag(fout, mode, indent, node);
  1163.                     PFlushLine(fout, indent);
  1164.                 }
  1165.             }
  1166.             else
  1167.             {
  1168.                 if (HideEndTags == no || !(node->tag->model & CM_OPT))
  1169.                     PPrintEndTag(fout, mode, indent, node);
  1170.  
  1171.                 PFlushLine(fout, indent);
  1172.             }
  1173.  
  1174.             if (IndentContent == no &&
  1175.                 node->next != null &&
  1176.                 node->tag->model & (CM_BLOCK|CM_LIST|CM_DEFLIST|CM_TABLE))
  1177.             {
  1178.                 PFlushLine(fout, indent);
  1179.             }
  1180.         }
  1181.     }
  1182. }
  1183.  
  1184. void PPrintXMLTree(Out *fout, uint mode, uint indent,
  1185.                     Lexer *lexer, Node *node)
  1186. {
  1187.     if (node == null)
  1188.         return;
  1189.  
  1190.     if (node->type == TextNode)
  1191.     {
  1192.         PPrintText(fout, mode, indent,
  1193.                     lexer, node->start, node->end);
  1194.     }
  1195.     else if (node->type == CommentTag)
  1196.     {
  1197.         PPrintComment(fout, indent, lexer, node);
  1198.     }
  1199.     else if (node->type == RootNode)
  1200.     {
  1201.         Node *content;
  1202.  
  1203.         for (content = node->content;
  1204.                 content != null;
  1205.                 content = content->next)
  1206.            PPrintXMLTree(fout, mode, indent, lexer, content);
  1207.     }
  1208.     else if (node->type == DocTypeTag)
  1209.         PPrintDocType(fout, indent, lexer, node);
  1210.     else if (node->type == ProcInsTag)
  1211.         PPrintPI(fout, indent, lexer, node);
  1212.     else if (node->type == AspTag)
  1213.         PPrintAsp(fout, indent, lexer, node);
  1214.     else if (node->tag->model & CM_EMPTY || node->type == StartEndTag)
  1215.     {
  1216.         PCondFlushLine(fout, indent);
  1217.         PPrintTag(lexer, fout, mode, indent, node);
  1218.         PFlushLine(fout, indent);
  1219.     }
  1220.     else /* some kind of container element */
  1221.     {
  1222.         Node *content;
  1223.         int cindent;
  1224.  
  1225.         PCondFlushLine(fout, indent);
  1226.  
  1227.         if (XMLPreserveWhiteSpace(node))
  1228.         {
  1229.             indent = 0;
  1230.             cindent = 0;
  1231.         }
  1232.         else
  1233.             cindent = indent + spaces;
  1234.  
  1235.         PPrintTag(lexer, fout, mode, indent, node);
  1236.         PFlushLine(fout, indent);
  1237.  
  1238.         for (content = node->content;
  1239.                 content != null;
  1240.                 content = content->next)
  1241.             PPrintXMLTree(fout, mode, cindent, lexer, content);
  1242.  
  1243.         PCondFlushLine(fout, cindent);
  1244.         PPrintEndTag(fout, mode, indent, node);
  1245.         PCondFlushLine(fout, indent);
  1246.     }
  1247. }
  1248.  
  1249. Node *FindBody(Node *root)
  1250. {
  1251.     Node *node;
  1252.  
  1253.     node = root->content;
  1254.  
  1255.     while (node && node->tag != tag_html)
  1256.         node = node->next;
  1257.  
  1258.     if (node == null)
  1259.         return null;
  1260.  
  1261.     node = node->content;
  1262.  
  1263.     while (node && node->tag != tag_body)
  1264.         node = node->next;
  1265.  
  1266.     return node;
  1267. }
  1268.  
  1269. /* split parse tree by h2 elements and output to separate files */
  1270.  
  1271. /* counts number of h2 children belonging to node */
  1272. /* ??? somehow misses last slide ??? */
  1273. int CountSlides(Node *node)
  1274. {
  1275.     int count = 1;
  1276.  
  1277.     for (node = node->content; node; node = node->next)
  1278.         if (node->tag == tag_h2)
  1279.             ++count;
  1280.  
  1281.     return count;
  1282. }
  1283.  
  1284. /*
  1285.    inserts a space gif called "dot.gif" to ensure
  1286.    that the  slide is at least n pixels high
  1287.  */
  1288. void PrintVertSpacer(Out *fout, uint indent)
  1289. {
  1290.     PCondFlushLine(fout, indent);
  1291.     PPrintString(fout, indent , 
  1292.     "<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">");
  1293.     PCondFlushLine(fout, indent);
  1294. }
  1295.  
  1296. void PrintNavBar(Out *fout, uint indent)
  1297. {
  1298.     char buf[128];
  1299.  
  1300.     PCondFlushLine(fout, indent);
  1301.     PPrintString(fout, indent , "<center><small>");
  1302.  
  1303.     if (slide > 1)
  1304.     {
  1305.         sprintf(buf, "<a href=\"slide%d.html\">previous</a> | ", slide-1);
  1306.         PPrintString(fout, indent , buf);
  1307.         PCondFlushLine(fout, indent);
  1308.  
  1309.         if (slide < count)
  1310.             PPrintString(fout, indent , "<a href=\"slide1.html\">start</a> | ");
  1311.         else
  1312.             PPrintString(fout, indent , "<a href=\"slide1.html\">start</a>");
  1313.  
  1314.         PCondFlushLine(fout, indent);
  1315.     }
  1316.  
  1317.     if (slide < count)
  1318.     {
  1319.         sprintf(buf, "<a href=\"slide%d.html\">next</a>", slide+1);
  1320.         PPrintString(fout, indent , buf);
  1321.     }
  1322.  
  1323.     PPrintString(fout, indent , "</small></center>");
  1324.     PCondFlushLine(fout, indent);
  1325. }
  1326.  
  1327. /*
  1328.   Called from PPrintTree to print the content of a slide from
  1329.   the node slidecontent. On return slidecontent points to the
  1330.   node starting the next slide or null. The variables slide
  1331.   and count are used to customise the navigation bar.
  1332. */
  1333. void PPrintSlide(Out *fout, uint mode, uint indent, Lexer *lexer)
  1334. {
  1335.     Node *content, *last;
  1336.  
  1337.     /* first print the h2 element and navbar */
  1338.     if (slidecontent->tag == tag_h2)
  1339.     {
  1340.         PrintNavBar(fout, indent);
  1341.  
  1342.         /* now print an hr after h2 */
  1343.  
  1344.         AddC('<', linelen++);
  1345.  
  1346.  
  1347.         AddC(FoldCase('h', UpperCaseTags), linelen++);
  1348.         AddC(FoldCase('r', UpperCaseTags), linelen++);
  1349.  
  1350.         if (XmlOut == yes)
  1351.             PPrintString(fout, indent , " />");
  1352.         else
  1353.             AddC('>', linelen++);
  1354.  
  1355.  
  1356.         if (IndentContent == yes)
  1357.             PCondFlushLine(fout, indent);
  1358.  
  1359.         /* PrintVertSpacer(fout, indent); */
  1360.  
  1361.         /*PCondFlushLine(fout, indent); */
  1362.  
  1363.         /* print the h2 element */
  1364.         PPrintTree(fout, mode,
  1365.             (IndentContent ? indent+spaces : indent), lexer, slidecontent);
  1366.  
  1367.         slidecontent = slidecontent->next;
  1368.     }
  1369.     
  1370.     /* now continue until we reach the next h2 or hr tag */
  1371.  
  1372.     last = null;
  1373.     content = slidecontent;
  1374.  
  1375.     for (; content != null; content = content->next)
  1376.     {
  1377.         if (content->tag == tag_h2 || content->tag == tag_hr)
  1378.             break;
  1379.  
  1380.         /* kludge for naked text before block level tag */
  1381.         if (last && !IndentContent && last->type == TextNode &&
  1382.             content->tag && content->tag->model & CM_BLOCK)
  1383.         {
  1384.             PFlushLine(fout, indent);
  1385.             PFlushLine(fout, indent);
  1386.         }
  1387.  
  1388.         PPrintTree(fout, mode,
  1389.             (IndentContent ? indent+spaces : indent), lexer, content);
  1390.  
  1391.         last = content;
  1392.     }
  1393.  
  1394.     slidecontent = content;
  1395.  
  1396.     /* now print epilog */
  1397.  
  1398.     PCondFlushLine(fout, indent);
  1399.  
  1400.     PPrintString(fout, indent , "<br clear=\"all\">");
  1401.     PCondFlushLine(fout, indent);
  1402.  
  1403.     AddC('<', linelen++);
  1404.  
  1405.  
  1406.     AddC(FoldCase('h', UpperCaseTags), linelen++);
  1407.     AddC(FoldCase('r', UpperCaseTags), linelen++);
  1408.  
  1409.     if (XmlOut == yes)
  1410.         PPrintString(fout, indent , " />");
  1411.     else
  1412.         AddC('>', linelen++);
  1413.  
  1414.  
  1415.     if (IndentContent == yes)
  1416.         PCondFlushLine(fout, indent);
  1417.  
  1418.     PrintNavBar(fout, indent);
  1419. }
  1420.  
  1421.  
  1422. void CreateSlides(Lexer *lexer, Node *root)
  1423. {
  1424.     Node *body;
  1425.     char buf[128];
  1426.     Out out;
  1427.     FILE *fp;
  1428.  
  1429.     body = FindBody(root);
  1430.     count = CountSlides(body);
  1431.     slidecontent = body->content;
  1432.  
  1433.     for (slide = 1; slide <= count; ++slide)
  1434.     {
  1435.         sprintf(buf, "slide%d.html", slide);
  1436.         out.state = FSM_ASCII;
  1437.         out.encoding = CharEncoding;
  1438.  
  1439.         if ((fp = fopen(buf, "w")))
  1440.         {
  1441.             out.fp = fp;
  1442.             PPrintTree(&out, null, 0, lexer, root);
  1443.             PFlushLine(&out, 0);
  1444.             fclose(fp);
  1445.         }
  1446.     }
  1447.  
  1448.     /*
  1449.      delete superfluous slides by deleting slideN.html
  1450.      for N = count+1, count+2, etc. until no such file
  1451.      is found.     
  1452.     */
  1453.  
  1454.     for (;;)
  1455.     {
  1456.         sprintf(buf, "slide%d.html", slide);
  1457.  
  1458.         if (unlink(buf) != 0)
  1459.             break;
  1460.  
  1461.         ++slide;
  1462.     }
  1463. }
  1464.