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

  1. /*
  2.   tidy.c - HTML parser and pretty printer
  3.  
  4.   Copyright (c) 1998 World Wide Web Consortium (Massachusetts
  5.   Institute of Technology, Institut National de Recherche en
  6.   Informatique et en Automatique, Keio University). All Rights
  7.   Reserved.
  8.  
  9.   Contributing Author(s):
  10.  
  11.      Dave Raggett <dsr@w3.org>
  12.  
  13.   The contributing author(s) would like to thank all those who
  14.   helped with testing, bug fixes, and patience.  This wouldn't
  15.   have been possible without all of you.
  16.  
  17.   COPYRIGHT NOTICE:
  18.  
  19.   This software and documentation is provided "as is," and
  20.   the copyright holders and contributing author(s) make no
  21.   representations or warranties, express or implied, including
  22.   but not limited to, warranties of merchantability or fitness
  23.   for any particular purpose or that the use of the software or
  24.   documentation will not infringe any third party patents,
  25.   copyrights, trademarks or other rights. 
  26.  
  27.   The copyright holders and contributing author(s) will not be
  28.   liable for any direct, indirect, special or consequential damages
  29.   arising out of any use of the software or documentation, even if
  30.   advised of the possibility of such damage.
  31.  
  32.   Permission is hereby granted to use, copy, modify, and distribute
  33.   this source code, or portions hereof, documentation and executables,
  34.   for any purpose, without fee, subject to the following restrictions:
  35.  
  36.   1. The origin of this source code must not be misrepresented.
  37.   2. Altered versions must be plainly marked as such and must
  38.      not be misrepresented as being the original source.
  39.   3. This Copyright notice may not be removed or altered from any
  40.      source or altered source distribution.
  41.  
  42.   The copyright holders and contributing author(s) specifically
  43.   permit, without fee, and encourage the use of this source code
  44.   as a component for supporting the Hypertext Markup Language in
  45.   commercial products. If you use this source code in a product,
  46.   acknowledgment is not required but would be appreciated.
  47. */
  48.  
  49. #include "platform.h"
  50. #include "html.h"
  51.  
  52. void InitTidy(void);
  53. void DeInitTidy(void);
  54.  
  55. extern char *release_date;
  56.  
  57. Bool        debug_flag = no;
  58. Node       *debug_element = null;
  59. Lexer      *debug_lexer = null;
  60. uint       totalerrors = 0;
  61. uint       totalwarnings = 0;
  62.  
  63. jmp_buf error_exit;  /* Address for long jump to jump to */
  64. FILE *errout;  /* set to stderr or stdout */
  65.  
  66. void FatalError(char *msg)
  67. {
  68.     fprintf(stderr, "Fatal error: %s\n", msg);
  69.     DeInitTidy();
  70.     longjmp(error_exit, -1);
  71. }
  72.  
  73. void *MemAlloc(uint size)
  74. {
  75.     void *p;
  76.  
  77.     p = malloc(size);
  78.  
  79.     if (!p)
  80.         FatalError("Out of memory!");
  81.  
  82.     return p;
  83. }
  84.  
  85. void *MemRealloc(void *mem, uint newsize)
  86. {
  87.     void *p;
  88.  
  89.     if (mem == (void *)null)
  90.         return MemAlloc(newsize);
  91.  
  92.     p = realloc(mem, newsize);
  93.  
  94.     if (!p)
  95.         FatalError("Out of memory!");
  96.  
  97.     return p;
  98. }
  99.  
  100. void MemFree(void *mem)
  101. {
  102.     if (mem != (void *)null)
  103.         free(mem);
  104. }
  105.  
  106. void ClearMemory(void *mem, uint size)
  107. {
  108.     memset(mem, 0, size);
  109. }
  110.  
  111. StreamIn *OpenInput(FILE *fp)
  112. {
  113.     StreamIn *in;
  114.  
  115.     in = (StreamIn *)MemAlloc(sizeof(StreamIn));
  116.     in->file = fp;
  117.     in->pushed = no;
  118.     in->c = '\0';
  119.     in->tabs = 0;
  120.     in->curline = 1;
  121.     in->curcol = 1;
  122.     in->encoding = CharEncoding;
  123.     in->state = FSM_ASCII;
  124.  
  125.     return in;
  126. }
  127.  
  128. /* read char from stream */
  129. int ReadCharFromStream(StreamIn *in)
  130. {
  131.     uint n, c, i, count;
  132.  
  133.     if (feof(in->file))
  134.         return -1;
  135.  
  136.     c = getc(in->file);
  137.  
  138.     /*
  139.        A document in ISO-2022 based encoding uses some ESC sequences
  140.        called "designator" to switch character sets. The designators
  141.        defined and used in ISO-2022-JP are:
  142.  
  143.         "ESC" + "(" + ?     for ISO646 variants
  144.  
  145.         "ESC" + "$" + ?     and
  146.         "ESC" + "$" + "(" + ?   for multibyte character sets
  147.  
  148.        Where ? stands for a single character used to indicate the
  149.        character set for multibyte characters.
  150.  
  151.        Tidy handles this by preserving the escape sequence and
  152.        setting the top bit of each byte for non-ascii chars. This
  153.        bit is then cleared on output. The input stream keeps track
  154.        of the state to determine when to set/clear the bit.
  155.     */
  156.  
  157.     if (in->encoding == ISO2022)
  158.     {
  159.         if (c == 0x1b)  /* ESC */
  160.         {
  161.             in->state = FSM_ESC;
  162.             return c;
  163.         }
  164.  
  165.         switch (in->state)
  166.         {
  167.         case FSM_ESC:
  168.             if (c == '$')
  169.                 in->state = FSM_ESCD;
  170.             else if (c == '(')
  171.                 in->state = FSM_ESCP;
  172.             else
  173.                 in->state = FSM_ASCII;
  174.             break;
  175.  
  176.         case FSM_ESCD:
  177.             if (c == '(')
  178.                 in->state = FSM_ESCDP;
  179.             else
  180.                 in->state = FSM_NONASCII;
  181.             break;
  182.  
  183.         case FSM_ESCDP:
  184.             in->state = FSM_NONASCII;
  185.             break;
  186.  
  187.         case FSM_ESCP:
  188.             in->state = FSM_ASCII;
  189.             break;
  190.  
  191.         case FSM_NONASCII:
  192.             c |= 0x80;
  193.             break;
  194.         }
  195.  
  196.         return c;
  197.     }
  198.  
  199.     if (in->encoding != UTF8)
  200.         return c;
  201.  
  202.     /* deal with UTF-8 encoded char */
  203.  
  204.     if ((c & 0xE0) == 0xC0)  /* 110X XXXX  two bytes */
  205.     {
  206.         n = c & 31;
  207.         count = 1;
  208.     }
  209.     else if ((c & 0xF0) == 0xE0)  /* 1110 XXXX  three bytes */
  210.     {
  211.         n = c & 15;
  212.         count = 2;
  213.     }
  214.     else if ((c & 0xF8) == 0xF0)  /* 1111 0XXX  four bytes */
  215.     {
  216.         n = c & 7;
  217.         count = 3;
  218.     }
  219.     else if ((c & 0xFC) == 0xF8)  /* 1111 10XX  five bytes */
  220.     {
  221.         n = c & 3;
  222.         count = 4;
  223.     }
  224.     else if ((c & 0xFE) == 0xFC)       /* 1111 110X  six bytes */
  225.     {
  226.         n = c & 1;
  227.         count = 5;
  228.     }
  229.     else  /* 0XXX XXXX one byte */
  230.         return c;
  231.  
  232.     /* successor bytes should have the form 10XX XXXX */
  233.     for (i = 1; i <= count; ++i)
  234.     {
  235.         if (feof(in->file))
  236.             return -1;
  237.  
  238.         c = getc(in->file);
  239.  
  240.         n = (n << 6) | (c & 0x3F);
  241.     }
  242.  
  243.     return n;
  244. }
  245.  
  246. int ReadChar(StreamIn *in)
  247. {
  248.     int c;
  249.  
  250.     if (in->pushed)
  251.     {
  252.         in->pushed = no;
  253.         c =  in->c;
  254.  
  255.         if (c == '\n')
  256.         {
  257.             in->curcol = 1;
  258.             in->curline++;
  259.             return c;
  260.         }
  261.  
  262.         in->curcol++;
  263.         return c;
  264.     }
  265.  
  266.     in->lastcol = in->curcol;
  267.  
  268.     if (in->tabs > 0)
  269.     {
  270.         in->curcol++;
  271.         in->tabs--;
  272.         return ' ';
  273.     }
  274.     
  275.     for (;;)
  276.     {
  277.         c = ReadCharFromStream(in);
  278.  
  279.         if (c < 0)
  280.             return EndOfStream;
  281.  
  282.         if (c == '\n')
  283.         {
  284.             in->curcol = 1;
  285.             in->curline++;
  286.             break;
  287.         }
  288.  
  289.         if (c == '\t')
  290.         {
  291.             in->tabs = tabsize - ((in->curcol - 1) % tabsize) - 1;
  292.             in->curcol++;
  293.             c = ' ';
  294.             break;
  295.         }
  296.  
  297.         /* strip control characters, except for Esc */
  298.  
  299.         if (c == '\033')
  300.             break;
  301.  
  302.         if (0 < c && c < 32)
  303.             continue;
  304.  
  305.         /* watch out for IS02022 */
  306.  
  307.         if (in->encoding == RAW || in->encoding == ISO2022)
  308.         {
  309.             in->curcol++;
  310.             break;
  311.         }
  312.  
  313.         /* produced e.g. as a side-effect of smart quotes in Word */
  314.  
  315.         if (127 < c && c < 160)
  316.         {
  317.             ReportEncodingError(in->lexer, WINDOWS_CHARS, c);
  318.  
  319.             if (c == 150)
  320.                 c = 8211;  /* en dash */
  321.             else if (c == 151)
  322.                 c = 8212;  /* em dash */
  323.             else if (c == 138)
  324.                 c = 352;  /* latin capital letter S with caron */
  325.             else if (c == 154)
  326.                 c = 353;  /* latin small letter s with caron */
  327.             else if (c == 159)
  328.                 c = 376;  /* latin capital letter Y with diaeresis */
  329.             else if (c == 140)
  330.                 c = 338;  /* latin capital ligature OE */
  331.             else if (c == 156)
  332.                 c = 339;  /* latin capital ligature OE */
  333.             else if (c == 153)
  334.                 c = 8482;  /* TM */
  335.             else if (c == 134)
  336.                 c = 8224;  /* dagger */
  337.             else if (c == 135)
  338.                 c = 8225;  /* double dagger */
  339.             else if (c == 137)
  340.                 c = 8240;  /* per mille sign */
  341.             else if (c == 130)
  342.                 c = 8218;  /* single low quotation mark */
  343.             else if (c == 132)
  344.                 c = 8222;  /* double low quotation mark */
  345.             else if (c == 145)
  346.                 c = 8216;  /* single left quotation mark */
  347.             else if (c == 146)
  348.                 c = 8217;  /* single right quotation mark */
  349.             else if (c == 147)
  350.                 c = 8220;  /* double left quotation mark */
  351.             else if (c == 148)
  352.                 c = 8221;  /* double right quotation mark */
  353.             else if (c == 139)
  354.                 c = 8249;  /* single left-pointing angle quotation mark */
  355.             else if (c == 155)
  356.                 c = 8250;  /* single right-pointing angle quotation mark */
  357.             else
  358.                 continue;
  359.         }
  360.  
  361.         in->curcol++;
  362.         break;
  363.     }
  364.  
  365.     return c;
  366. }
  367.  
  368. void UngetChar(int c, StreamIn *in)
  369. {
  370.     in->pushed = yes;
  371.     in->c = c;
  372.  
  373.     if (c == '\n')
  374.         --(in->curline);
  375.  
  376.     in->curcol = in->lastcol;
  377. }
  378.  
  379. /* like strdup but using MemAlloc */
  380. char *wstrdup(char *str)
  381. {
  382.     char *s, *p;
  383.     int len;
  384.  
  385.     if (str == null)
  386.         return null;
  387.  
  388.     for (len = 0; str[len] != '\0'; ++len);
  389.  
  390.     s = (char *)MemAlloc(sizeof(char)*(1+len));
  391.     for (p = s; *p++ = *str++;);
  392.     return s;
  393. }
  394.  
  395. /* like strndup but using MemAlloc */
  396. char *wstrndup(char *str, int len)
  397. {
  398.     char *s, *p;
  399.  
  400.     if (str == null || len < 0)
  401.         return null;
  402.  
  403.     s = (char *)MemAlloc(sizeof(char)*(1+len));
  404.  
  405.     p = s;
  406.  
  407.     while (len-- > 0 && (*p++ = *str++));
  408.  
  409.     *p = '\0';
  410.     return s;
  411. }
  412.  
  413. /* exactly same as strncpy */
  414. void wstrncpy(char *s1, char *s2, int size)
  415. {
  416.     if (s1 != null && s2 != null)
  417.     {
  418.         if (size >= 0)
  419.         {
  420.             while (size--)
  421.                 *s1++ = *s2++;
  422.         }
  423.         else
  424.             while (*s1++ = *s2++);
  425.     }
  426. }
  427.  
  428. void wstrcpy(char *s1, char *s2)
  429. {
  430.     while (*s1++ = *s2++);
  431. }
  432.  
  433. /* exactly same as strcmp */
  434. int wstrcmp(char *s1, char *s2)    
  435. {
  436.     int c;
  437.  
  438.     while ((c = *s1) == *s2)
  439.     {
  440.         if (c == '\0')
  441.             return 0;
  442.  
  443.         ++s1;
  444.         ++s2;
  445.     }
  446.  
  447.     return (*s1 > *s2 ? 1 : -1);
  448. }
  449.  
  450. /* returns byte count, not char count */
  451. int wstrlen(char *str)
  452. {
  453.     int len = 0;
  454.  
  455.     while(*str++)
  456.         ++len;
  457.  
  458.     return len;
  459. }
  460.  
  461. /*
  462.  MS C 4.2 doesn't include strcasecmp.
  463.  Note that tolower and toupper won't
  464.  work on chars > 127
  465. */
  466. int wstrcasecmp(char *s1, char *s2)    
  467. {
  468.     uint c;
  469.  
  470.     while (c = (uint)(*s1), ToLower(c) == ToLower((uint)(*s2)))
  471.     {
  472.         if (c == '\0')
  473.             return 0;
  474.  
  475.         ++s1;
  476.         ++s2;
  477.     }
  478.  
  479.     return (*s1 > *s2 ? 1 : -1);
  480. }
  481.  
  482. int wstrncmp(char *s1, char *s2, int n)    
  483. {
  484.     int c;
  485.  
  486.     while ((c = *s1) == *s2)
  487.     {
  488.         if (c == '\0')
  489.             return 0;
  490.  
  491.         if (n == 0)
  492.             return 0;
  493.  
  494.         ++s1;
  495.         ++s2;
  496.         --n;
  497.     }
  498.  
  499.     if (n == 0)
  500.         return 0;
  501.  
  502.     return (*s1 > *s2 ? 1 : -1);
  503. }
  504.  
  505. int wstrncasecmp(char *s1, char *s2, int n)    
  506. {
  507.     int c;
  508.  
  509.     while (c = *s1, tolower(c) == tolower(*s2))
  510.     {
  511.         if (c == '\0')
  512.             return 0;
  513.  
  514.         if (n == 0)
  515.             return 0;
  516.  
  517.         ++s1;
  518.         ++s2;
  519.         --n;
  520.     }
  521.  
  522.     if (n == 0)
  523.         return 0;
  524.  
  525.     return (*s1 > *s2 ? 1 : -1);
  526. }
  527.  
  528. Bool wsubstr(char *s1, char *s2)
  529. {
  530.     int i, len1 = wstrlen(s1), len2 = wstrlen(s2);
  531.  
  532.     for (i = 0; i <= len1 - len2; ++i)
  533.     {
  534.         if (wstrncasecmp(s1+i, s2, len2) == 0)
  535.             return yes;
  536.     }
  537.  
  538.     return no;
  539. }
  540.  
  541.  
  542. void outc(uint c, Out *out)
  543. {
  544.     uint ch;
  545.  
  546.     if (out->encoding == UTF8)
  547.     {
  548.         if (c < 128)
  549.             putc(c, out->fp);
  550.         else if (c <= 0x7FF)
  551.         {
  552.             ch = (0xC0 | (c >> 6)); putc(ch, out->fp);
  553.             ch = (0x80 | (c & 0x3F)); putc(ch, out->fp);
  554.         }
  555.         else if (c <= 0xFFFF)
  556.         {
  557.             ch = (0xE0 | (c >> 12)); putc(ch, out->fp);
  558.             ch = (0x80 | ((c >> 6) & 0x3F)); putc(ch, out->fp);
  559.             ch = (0x80 | (c & 0x3F)); putc(ch, out->fp);
  560.         }
  561.         else if (c <= 0x1FFFFF)
  562.         {
  563.             ch = (0xF0 | (c >> 18)); putc(ch, out->fp);
  564.             ch = (0x80 | ((c >> 12) & 0x3F)); putc(ch, out->fp);
  565.             ch = (0x80 | ((c >> 6) & 0x3F)); putc(ch, out->fp);
  566.             ch = (0x80 | (c & 0x3F)); putc(ch, out->fp);
  567.         }
  568.         else
  569.         {
  570.             ch = (0xF8 | (c >> 24)); putc(ch, out->fp);
  571.             ch = (0x80 | ((c >> 18) & 0x3F)); putc(ch, out->fp);
  572.             ch = (0x80 | ((c >> 12) & 0x3F)); putc(ch, out->fp);
  573.             ch = (0x80 | ((c >> 6) & 0x3F)); putc(ch, out->fp);
  574.             ch = (0x80 | (c & 0x3F)); putc(ch, out->fp);
  575.         }
  576.     }
  577.     else if (out->encoding == ISO2022)
  578.     {
  579.         if (c == 0x1b)  /* ESC */
  580.             out->state = FSM_ESC;
  581.         else
  582.         {
  583.             switch (out->state)
  584.             {
  585.             case FSM_ESC:
  586.                 if (c == '$')
  587.                     out->state = FSM_ESCD;
  588.                 else if (c == '(')
  589.                     out->state = FSM_ESCP;
  590.                 else
  591.                     out->state = FSM_ASCII;
  592.                 break;
  593.  
  594.             case FSM_ESCD:
  595.                 if (c == '(')
  596.                     out->state = FSM_ESCDP;
  597.                 else
  598.                     out->state = FSM_NONASCII;
  599.                 break;
  600.  
  601.             case FSM_ESCDP:
  602.                 out->state = FSM_NONASCII;
  603.                 break;
  604.  
  605.             case FSM_ESCP:
  606.                 out->state = FSM_ASCII;
  607.                 break;
  608.  
  609.             case FSM_NONASCII:
  610.                 c &= 0x7F;
  611.                 break;
  612.             }
  613.         }
  614.  
  615.         putc(c, out->fp);
  616.     }
  617.     else
  618.         putc(c, out->fp);
  619. }
  620.  
  621. /*
  622.   first time initialization which should
  623.   precede reading the command line
  624. */
  625. void InitTidy(void)
  626. {
  627.     InitMap();
  628.     InitAttrs();
  629.     InitTags();
  630.     InitEntities();
  631.     InitConfig();
  632.  
  633.     totalerrors = totalwarnings = 0;
  634.     XmlTags = XmlOut = HideEndTags = UpperCaseTags =
  635.     MakeClean = writeback = OnlyErrors = no;
  636.  
  637.     errfile = null;
  638.     errout = stderr;
  639.  
  640. #ifdef CONFIG_FILE
  641.     ParseConfigFile(CONFIG_FILE);
  642. #endif
  643. }
  644.  
  645. /*
  646.   call this when you have finished with tidy
  647.   to free the hash tables and other resources
  648. */
  649. void DeInitTidy(void)
  650. {
  651.     FreeTags();
  652.     FreeAttrTable();
  653.     FreeEntities();
  654.     FreeConfig();
  655.     FreePrintBuf();
  656. }
  657.  
  658. int main(int argc, char **argv)
  659. {
  660.     char *file, *prog;
  661.     FILE *fp = null;
  662.     Node *node;
  663.     Lexer *lexer;
  664.     char *s, c, *arg, *current_errorfile = "stderr";
  665.     int jmpret;
  666.     Out out;   /* normal output stream */
  667.  
  668.     /*
  669.       set up for long jump back to here on severe errors
  670.     */
  671.     jmpret = setjmp(error_exit);
  672.  
  673.     /*
  674.       return on a severe error after long jump
  675.     */
  676.  
  677.     if (jmpret != 0)
  678.     {
  679.         /* ensure input is closed */
  680.         if (fp && fp != stdin)
  681.             fclose(fp);
  682.  
  683.         /* 2 signifies a serious error */
  684.         return 2;
  685.     }
  686.  
  687.     InitTidy();
  688.  
  689.     /* look for env var "HTML_TIDY" */
  690.  
  691.     if ((file = getenv("HTML_TIDY")))
  692.         ParseConfigFile(file);
  693.  
  694.     /* read command line */
  695.  
  696.     prog = argv[0];
  697.  
  698.     while (argc > 0)
  699.     {
  700.         if (argc > 1 && argv[1][0] == '-')
  701.         {
  702.             /* support -foo and --foo */
  703.             arg = argv[1] + 1;
  704.  
  705.             if (arg[0] == '-')
  706.                 ++arg;
  707.  
  708.             if (strcmp(arg, "indent") == 0)
  709.                 IndentContent = yes;
  710.             else if (strcmp(arg, "xml") == 0)
  711.                 XmlTags = yes;
  712.             else if (strcmp(arg, "asxml") == 0)
  713.                 xHTML = yes;
  714.             else if (strcmp(arg, "indent") == 0)
  715.                 IndentContent = yes;
  716.             else if (strcmp(arg, "omit") == 0)
  717.                 HideEndTags = yes;
  718.             else if (strcmp(arg, "upper") == 0)
  719.                 UpperCaseTags = yes;
  720.             else if (strcmp(arg, "clean") == 0)
  721.                 MakeClean = yes;
  722.             else if (strcmp(arg, "raw") == 0)
  723.                 CharEncoding = RAW;
  724.             else if (strcmp(arg, "ascii") == 0)
  725.                 CharEncoding = ASCII;
  726.             else if (strcmp(arg, "latin1") == 0)
  727.                 CharEncoding = LATIN1;
  728.             else if (strcmp(arg, "utf8") == 0)
  729.                 CharEncoding = UTF8;
  730.             else if (strcmp(arg, "iso2022") == 0)
  731.                 CharEncoding = ISO2022;
  732.             else if (strcmp(arg, "numeric") == 0)
  733.                 NumEntities = yes;
  734.             else if (strcmp(arg, "modify") == 0)
  735.                 writeback = yes;
  736.             else if (strcmp(arg, "change") == 0)  /* obsolete */
  737.                 writeback = yes;
  738.             else if (strcmp(arg, "update") == 0)  /* obsolete */
  739.                 writeback = yes;
  740.             else if (strcmp(arg, "errors") == 0)
  741.                 OnlyErrors = yes;
  742.             else if (strcmp(arg, "slides") == 0)
  743.                 BurstSlides = yes;
  744.             else if (strcmp(arg, "help") == 0 ||
  745.                      argv[1][1] == '?'|| argv[1][1] == 'h')
  746.             {
  747.                 HelpText(stdout, prog);
  748.                 return 1;
  749.             }
  750.             else if (strcmp(arg, "config") == 0)
  751.             {
  752.                 if (argc >= 3)
  753.                 {
  754.                     ParseConfigFile(argv[2]);
  755.                     --argc;
  756.                     ++argv;
  757.                 }
  758.             }
  759.             else if (strcmp(argv[1], "-file") == 0 ||
  760.                      strcmp(argv[1], "--file") == 0 ||
  761.                         strcmp(argv[1], "-f") == 0)
  762.             {
  763.                 if (argc >= 3)
  764.                 {
  765.                     /* create copy that can be freed by FreeConfig() */
  766.                     errfile = wstrdup(argv[2]);
  767.                     --argc;
  768.                     ++argv;
  769.                 }
  770.             }
  771.             else if (strcmp(argv[1], "-wrap") == 0 ||
  772.                         strcmp(argv[1], "--wrap") == 0 ||
  773.                         strcmp(argv[1], "-w") == 0)
  774.             {
  775.                 if (argc >= 3)
  776.                 {
  777.                     sscanf(argv[2], "%d", &wraplen);
  778.                     --argc;
  779.                     ++argv;
  780.                 }
  781.             }
  782.             else
  783.             {
  784.                 s = argv[1];
  785.  
  786.                 while ((c = *++s))
  787.                 {
  788.                     if (c == 'i')
  789.                         IndentContent = yes;
  790.                     else if (c == 'o')
  791.                         HideEndTags = yes;
  792.                     else if (c == 'u')
  793.                         UpperCaseTags = yes;
  794.                     else if (c == 'c')
  795.                         MakeClean = yes;
  796.                     else if (c == 'n')
  797.                         NumEntities = yes;
  798.                     else if (c == 'm')
  799.                         writeback = yes;
  800.                     else if (c == 'e')
  801.                         OnlyErrors = yes;
  802.                     else
  803.                         UnknownOption(stderr, c);
  804.                 }
  805.             }
  806.  
  807.             --argc;
  808.             ++argv;
  809.             continue;
  810.         }
  811.  
  812.         /* ensure config is self-consistent */
  813.         AdjustConfig();
  814.  
  815.         /* user specified error file */
  816.         if (errfile)
  817.         {
  818.             /* is it same as the currently opened file? */
  819.             if (wstrcmp(errfile, current_errorfile) != 0)
  820.             {
  821.                 /* no so close previous error file */
  822.  
  823.                 if (errout != stderr)
  824.                     fclose(errout);
  825.  
  826.                 /* and try to open the new error file */
  827.                 fp = fopen(errfile, "w");
  828.  
  829.                 if (fp != null)
  830.                 {
  831.                     errout = fp;
  832.                     current_errorfile = errfile;
  833.                 }
  834.                 else /* can't be opened so fall back to stderr */
  835.                 {
  836.                     errout = stderr;
  837.                     current_errorfile = "stderr";
  838.                 }
  839.             }
  840.         }
  841.  
  842.         if (argc > 1)
  843.         {
  844.             file = argv[1];
  845.             fp = fopen(file, "r");
  846.         }
  847.         else
  848.         {
  849.             fp = stdin;
  850.             file = "stdin";
  851.         }
  852.  
  853.         if (fp != null)
  854.         {
  855.             lexer = NewLexer(OpenInput(fp));
  856.             lexer->errout = errout;
  857.  
  858.             /*
  859.               store pointer to lexer in input stream
  860.               to allow character encoding errors to be
  861.               reported
  862.             */
  863.             lexer->in->lexer = lexer;
  864.  
  865.             /* Tidy doesn't alter the doctype for generic XML docs */
  866.             if (XmlTags)
  867.                 node = ParseXMLDocument(lexer);
  868.             else
  869.             {
  870.                 lexer->warnings = 0;
  871.                 HelloMessage(errout, release_date, file);
  872.                 node = ParseDocument(lexer);
  873.  
  874.                 /* replaces i by em and b by strong */
  875.                 if (LogicalEmphasis)
  876.                     EmFromI(node);
  877.  
  878.                 /* replaces presentational markup by style rules */
  879.                 if (MakeClean)
  880.                     CleanTree(lexer, node);
  881.  
  882.                 if (node->content)
  883.                 {
  884.                     if (xHTML)
  885.                         SetXHTMLDocType(lexer, node);
  886.                     else
  887.                         FixDocType(lexer, node);
  888.                 }
  889.  
  890.                 /* ensure presence of initial <?XML version="1.0"?> */
  891.                 if (XmlOut && XmlPi)
  892.                     FixXMLPI(lexer, node);
  893.  
  894.                 totalwarnings += lexer->warnings;
  895.                 totalerrors += lexer->errors;
  896.  
  897.                 if(node->content)
  898.                 {
  899.                     ReportVersion(errout, file, HTMLVersionName(lexer));
  900.                     ReportNumWarnings(errout, lexer);
  901.                 }
  902.             }
  903.  
  904.             if (fp != stdin)
  905.                 fclose(fp);
  906.  
  907.             MemFree(lexer->in);
  908.  
  909.             if (lexer->errors > 0)
  910.                 NeedsAuthorIntervention(errout);
  911.  
  912.             out.state = FSM_ASCII;
  913.             out.encoding = CharEncoding;
  914.  
  915.             if (!OnlyErrors && lexer->errors == 0)
  916.             {
  917.                 if (BurstSlides)
  918.                 {
  919.                     Node *body;
  920.  
  921.                     /*
  922.                        remove doctype to avoid potential clash with
  923.                        markup introduced when bursting into slides
  924.                     */
  925.                     DiscardDocType(node);
  926.  
  927.                     /* slides use transitional features */
  928.                     lexer->versions |= VERS_LOOSE;
  929.  
  930.                     /* and patch up doctype to match */
  931.                     if (xHTML)
  932.                         SetXHTMLDocType(lexer, node);
  933.                     else
  934.                         FixDocType(lexer, node);
  935.  
  936.  
  937.                     /* find the body element which may be implicit */
  938.                     body = FindBody(node);
  939.  
  940.                     if (body)
  941.                     {
  942.                         ReportNumberOfSlides(errout, CountSlides(body));
  943.                         CreateSlides(lexer, node);
  944.                     }
  945.                     else
  946.                         MissingBody(errout);
  947.                 }
  948.                 else if (writeback && (fp = fopen(file, "w")))
  949.                 {
  950.                     out.fp = fp;
  951.  
  952.                     if (XmlTags)
  953.                         PPrintXMLTree(&out, null, 0, lexer, node);
  954.                     else
  955.                         PPrintTree(&out, null, 0, lexer, node);
  956.  
  957.                     PFlushLine(&out, 0);
  958.                     fclose(fp);
  959.                 }
  960.                 else
  961.                 {
  962.                     out.fp = stdout;
  963.  
  964.                     if (XmlTags)
  965.                         PPrintXMLTree(&out, null, 0, lexer, node);
  966.                     else
  967.                         PPrintTree(&out, null, 0, lexer, node);
  968.  
  969.                     PFlushLine(&out, 0);
  970.                 }
  971.  
  972.             }
  973.  
  974.             ErrorSummary(lexer);
  975.             FreeNode(node);
  976.             FreeLexer(lexer);
  977.         }
  978.         else
  979.             UnknownFile(errout, prog, file);
  980.  
  981.         --argc;
  982.         ++argv;
  983.  
  984.         if (argc <= 1)
  985.             break;
  986.     }
  987.  
  988.     if (totalerrors + totalwarnings > 0)
  989.         GeneralInfo(errout);
  990.  
  991.     if (errout != stderr)
  992.         fclose(errout);
  993.  
  994.     /* called to free hash tables etc. */
  995.     DeInitTidy();
  996.  
  997.     /* return status can be used by scripts */
  998.  
  999.     if (totalerrors > 0)
  1000.         return 2;
  1001.  
  1002.     if (totalwarnings > 0)
  1003.         return 1;
  1004.  
  1005.     /* 0 signifies all is ok */
  1006.     return 0;
  1007. }
  1008.  
  1009.