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

  1. /*
  2.   parser.c - HTML Parser
  3.  
  4.   (c) 1998 (W3C) MIT, INRIA, Keio University
  5.   See tidy.c for the copyright notice.
  6. */
  7.  
  8. #include "platform.h"   /* platform independent stuff */
  9. #include "html.h"       /* to pull in definition of nodes */
  10.  
  11. int SeenBodyEndTag;  /* could be moved into lexer structure */
  12.  
  13. void InsertNode(Node *element, Node *node)
  14. {
  15.     node->parent = element;
  16.     node->prev = element->last;
  17.  
  18.     if (element->last != null)
  19.         element->last->next = node;
  20.     else
  21.         element->content = node;
  22.  
  23.     element->last = node;
  24. }
  25.  
  26. void InsertNodeBeforeElement(Node *element, Node *node)
  27. {
  28.     Node *parent;
  29.  
  30.     parent = element->parent;
  31.     node->parent = parent;
  32.     node->next = element;
  33.     node->prev = element->prev;
  34.     element->prev = node;
  35.  
  36.     if (node->prev)
  37.         node->prev->next = node;
  38.  
  39.     if (parent->content == element)
  40.         parent->content = node;
  41. }
  42.  
  43. void InsertNodeAfterElement(Node *element, Node *node)
  44. {
  45.     Node *parent;
  46.  
  47.     parent = element->parent;
  48.     node->parent = parent;
  49.  
  50.     if (parent->last == element)
  51.         parent->last = node;
  52.     else
  53.         node->next = element->next;
  54.  
  55.     element->next = node;
  56.     node->prev = element;
  57. }
  58.  
  59. void DiscardElement(Lexer *lexer, Node *element)
  60. {
  61.     Node *parent;
  62.  
  63.     parent = element->parent;
  64.  
  65.     if (parent->last == element)
  66.         parent->last = element->prev;
  67.  
  68.     if (parent->content == element)
  69.         parent->content = element->next;
  70.  
  71.     if (element->prev)
  72.         element->prev->next = element->next;
  73.  
  74.     if (element->next)
  75.         element->next->prev = element->prev;
  76.  
  77.     element->next = null;
  78.     FreeNode(element);
  79. }
  80.  
  81. void TrimEmptyElement(Lexer *lexer, Node *element)
  82. {
  83.     if (element->content == null &&
  84.         (element->tag != tag_a || element->attributes == null))
  85.     {
  86.         if (element->type == TextNode ||
  87.                 (element->tag != null &&
  88.                  element->tag != tag_layer &&
  89.                 !(element->tag->model & CM_ROW)))
  90.         {
  91.  
  92.            if (element->type != TextNode)
  93.                 ReportWarning(lexer, element, null, TRIM_EMPTY_ELEMENT);
  94.  
  95.             DiscardElement(lexer, element);
  96.         }
  97.     }
  98. }
  99.  
  100. /*
  101.   If last child of element is a text node
  102.   then trim trailing white space character.
  103. */
  104. void TrimSpace(Lexer *lexer, Node *last)
  105. {
  106.     unsigned char c;
  107.  
  108.     if (last != null && last->type == TextNode && last->end > last->start)
  109.     {
  110.         while (last->end > last->start)
  111.         {
  112.             c = (unsigned char)lexer->lexbuf[last->end - 1];
  113.  
  114.             if (c == 160)  /* non breaking space */
  115.             {
  116.                 if (last->parent->tag == tag_td || last->parent->tag == tag_th)
  117.                 {
  118.                     if (last->end > last->start + 1)
  119.                         last->end -= 1;
  120.                     else
  121.                         break;
  122.                 }
  123.                 else
  124.                     last->end -= 1;
  125.             }
  126.             else if (c == ' ')
  127.                 last->end -= 1;
  128.             else
  129.                 break;
  130.         }
  131.  
  132.         if (last->end < last->start)
  133.             tidy_out(lexer->errout, "TrimSpace: screwed up text node\n");
  134.  
  135.        /* if empty string then delete from parse tree */
  136.         if (last->start == last->end)
  137.             TrimEmptyElement(lexer, last);
  138.     }
  139. }
  140.  
  141. /*
  142.   This maps 
  143.        <em>hello </em><strong>world</strong>
  144.   to
  145.        <em>hello</em> <strong>world</strong>
  146.  
  147.   If last child of element is a text node
  148.   then trim trailing white space character
  149.   moving it to after element's end tag.
  150. */
  151. void TrimTrailingSpace(Lexer *lexer, Node *last)
  152. {
  153.     unsigned char c;
  154.  
  155.     if (last != null && last->type == TextNode && last->end > last->start)
  156.     {
  157.         c = (unsigned char)lexer->lexbuf[last->end - 1];
  158.  
  159.         if (c == ' ' || c == 160)
  160.         {
  161.             last->end -= 1;
  162.  
  163.             if (last->parent->tag->model & CM_INLINE)
  164.                 lexer->insertspace = yes;
  165.         }
  166.  
  167.         /* if empty string then delete from parse tree */
  168.         if (last->start == last->end)
  169.             TrimEmptyElement(lexer, last);
  170.     }
  171. }
  172.  
  173. /*
  174.   This maps 
  175.        <p>hello<em> world</em>
  176.   to
  177.        <p>hello <em>world</em>
  178.  
  179.   Trims initial space, by moving it before the
  180.   start tag, or if this element is the first in
  181.   parent's content, then by discarding the space
  182. */
  183. void TrimInitialSpace(Lexer *lexer, Node *element, Node *text)
  184. {
  185.     Node *prev, *node;
  186.  
  187.     if (text->type == TextNode && lexer->lexbuf[text->start] == ' ')
  188.     {
  189.         if (element->tag->model & CM_INLINE &&
  190.             element->parent->content != element)
  191.         {
  192.             prev = element->prev;
  193.  
  194.             if (prev->type == TextNode)
  195.             {
  196.                 if (lexer->lexbuf[prev->end - 1] != ' ')
  197.                     lexer->lexbuf[(prev->end)++] = ' ';
  198.  
  199.                 ++(element->start);
  200.             }
  201.             else /* create new node */
  202.             {
  203.                 node = NewNode();
  204.                 node->start = (element->start)++;
  205.                 node->end = element->start;
  206.                 lexer->lexbuf[node->start] = ' ';
  207.                 node->prev = prev;
  208.                 prev->next = node;
  209.                 node->next = element;
  210.                 element->prev = node;
  211.                 node->parent = element->parent;
  212.             }
  213.         }
  214.  
  215.         /* discard the space  in current node */
  216.         ++(text->start);
  217.     }
  218. }
  219.  
  220. Bool DescendantOf(Node *element, Dict *tag)
  221. {
  222.     Node *parent;
  223.  
  224.     for (parent = element->parent;
  225.             parent != null; parent = parent->parent)
  226.     {
  227.         if (parent->tag == tag)
  228.             return yes;
  229.     }
  230.  
  231.     return no;
  232. }
  233.  
  234. void ParseTag(Lexer *lexer, Node *node, uint mode)
  235. {
  236.     if (node->tag->model & CM_EMPTY)
  237.     {
  238.         lexer->waswhite = no;
  239.         return;
  240.     }
  241.     else if (!(node->tag->model & CM_INLINE))
  242.         lexer->insertspace = no;
  243.  
  244.     if (node->tag->parser == null || node->type == StartEndTag)
  245.         return;
  246.  
  247.     (*node->tag->parser)(lexer, node, mode);
  248. }
  249.  
  250. /*
  251.  the doctype has been found after other tags,
  252.  and needs moving to before the html element
  253. */
  254. void InsertDocType(Lexer *lexer, Node *element, Node *doctype)
  255. {
  256.     ReportWarning(lexer, element, doctype, DOCTYPE_AFTER_TAGS);
  257.  
  258.     while (element->tag != tag_html)
  259.         element = element->parent;
  260.  
  261.     InsertNodeBeforeElement(element, doctype);
  262. }
  263.  
  264. void MoveToHead(Lexer *lexer, Node *element, Node *node)
  265. {
  266.     Node *head;
  267.  
  268.  
  269.     if (node->type == StartTag || node->type == StartEndTag)
  270.     {
  271.         ReportWarning(lexer, element, node, TAG_NOT_ALLOWED_IN);
  272.  
  273.         while (element->tag != tag_html)
  274.             element = element->parent;
  275.  
  276.         for (head = element->content; head; head = head->next)
  277.         {
  278.             if (head->tag == tag_head)
  279.             {
  280.                 InsertNode(head, node);
  281.                 break;
  282.             }
  283.         }
  284.  
  285.         if (node->tag->parser)
  286.             ParseTag(lexer, node, IgnoreWhitespace);
  287.     }
  288.     else
  289.     {
  290.         ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  291.         FreeNode(node);
  292.     }
  293. }
  294.  
  295. /*
  296.    element is node created by the lexer
  297.    upon seeing the start tag, or by the
  298.    parser when the start tag is inferred
  299. */
  300. void ParseBlock(Lexer *lexer, Node *element, uint mode)
  301. {
  302.     Node *node, *parent;
  303.     Bool checkstack;
  304.     uint istackbase;
  305.  
  306.     checkstack = yes;
  307.  
  308.     if (element->tag->model & CM_EMPTY)
  309.         return;
  310.  
  311.     if (element->tag == tag_form && DescendantOf(element, tag_form))
  312.         ReportWarning(lexer, element, null, ILLEGAL_NESTING);
  313.  
  314.     /*
  315.      InlineDup() asks the lexer to insert inline emphasis tags
  316.      currently pushed on the istack, but take care to avoid
  317.      propagating inline emphasis inside OBJECT or APPLET.
  318.      For these elements a fresh inline stack context is created
  319.      and disposed of upon reaching the end of the element.
  320.      They thus behave like table cells in this respect.
  321.     */
  322.     if (element->tag->model & CM_OBJECT)
  323.     {
  324.         istackbase = lexer->istackbase;
  325.         lexer->istackbase = lexer->istacksize;
  326.     }
  327.  
  328.     if (!(element->tag->model & CM_MIXED))
  329.         InlineDup(lexer, null);
  330.  
  331.     mode = IgnoreWhitespace;
  332.  
  333.     while ((node = GetToken(lexer, mode /*MixedContent*/)) != null)
  334.     {
  335.         /* end tag for this element */
  336.         if (node->type == EndTag && 
  337.             (node->tag == element->tag || element->was == node->tag))
  338.         {
  339.             FreeNode(node);
  340.  
  341.             if (element->tag->model & CM_OBJECT)
  342.             {
  343.                 /* pop inline stack */
  344.                 while (lexer->istacksize > lexer->istackbase)
  345.                     PopInline(lexer, null);
  346.                 lexer->istackbase = istackbase;
  347.             }
  348.  
  349.             TrimSpace(lexer, element->last);
  350.             TrimEmptyElement(lexer, element);
  351.             return;
  352.         }
  353.  
  354.         if (node->tag == tag_html || node->tag == tag_head || node->tag == tag_body)
  355.         {
  356.             if (node->type == StartTag || node->type == StartEndTag)
  357.                 ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  358.  
  359.             FreeNode(node);
  360.             continue;
  361.         }
  362.  
  363.         /* 
  364.           if this is the end tag for an ancestor element
  365.           then infer end tag for this element
  366.         */
  367.         if (node->type == EndTag)
  368.         {
  369.             for (parent = element->parent;
  370.                     parent != null; parent = parent->parent)
  371.             {
  372.                 if (node->tag == parent->tag)
  373.                 {
  374.                     if (!(element->tag->model & CM_OPT))
  375.                         ReportWarning(lexer, element, node, MISSING_ENDTAG_BEFORE);
  376.  
  377.                     UngetToken(lexer);
  378.  
  379.                     if (element->tag->model & CM_OBJECT)
  380.                     {
  381.                         /* pop inline stack */
  382.                         while (lexer->istacksize > lexer->istackbase)
  383.                             PopInline(lexer, null);
  384.                         lexer->istackbase = istackbase;
  385.                     }
  386.  
  387.                     TrimSpace(lexer, element->last);
  388.                     TrimEmptyElement(lexer, element);
  389.                     return;
  390.                 }
  391.             }
  392.         }
  393.  
  394.         /* mixed content model permits text */
  395.         if (node->type == TextNode)
  396.         {
  397.             if (checkstack)
  398.             {
  399.                 checkstack = no;
  400.  
  401.                 if (!(element->tag->model & CM_MIXED))
  402.                 {
  403.                     if (InlineDup(lexer, node) > 0)
  404.                         continue;
  405.                 }
  406.             }
  407.  
  408.             InsertNode(element, node);
  409.             mode = MixedContent;
  410.             continue;
  411.         }
  412.  
  413.         if (node->type == CommentTag ||
  414.             node->type == ProcInsTag ||
  415.             node->type == AspTag)
  416.         {
  417.             InsertNode(element, node);
  418.             continue;
  419.         }
  420.  
  421.         /* allow PARAM elements? */
  422.         if (node->tag == tag_param)
  423.         {
  424.             if ((element->tag->model & CM_PARAM) &&
  425.                     node->type == StartTag)
  426.             {
  427.                 InsertNode(element, node);
  428.                 continue;
  429.             }
  430.  
  431.             /* otherwise discard it */
  432.             ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  433.             FreeNode(node);
  434.             continue;
  435.         }
  436.  
  437.         /* allow AREA elements? */
  438.         if (node->tag == tag_area)
  439.         {
  440.             if ((element->tag == tag_map) &&
  441.                     (node->type == StartTag || node->type == StartEndTag))
  442.             {
  443.                 InsertNode(element, node);
  444.                 continue;
  445.             }
  446.  
  447.             /* otherwise discard it */
  448.             ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  449.             FreeNode(node);
  450.             continue;
  451.         }
  452.  
  453.         /* ignore unknown start/end tags */
  454.         if (node->tag == null)
  455.         {
  456.             ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  457.             FreeNode(node);
  458.             continue;
  459.         }
  460.  
  461.         /*
  462.           Allow CM_INLINE elements here.
  463.  
  464.           Allow CM_BLOCK elements here unless
  465.           lexer->excludeBlocks is yes.
  466.  
  467.           LI and DD are special cased.
  468.  
  469.           Otherwise infer end tag for this element.
  470.         */
  471.  
  472.         if (!(node->tag->model & CM_INLINE))
  473.         {
  474.             if (node->type != StartTag && node->type != StartEndTag)
  475.             {
  476.                 ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  477.                 continue;
  478.             }
  479.  
  480.             if (element->tag == tag_td || element->tag == tag_th)
  481.             {
  482.                 /* if parent is a table cell, avoid inferring the end of the cell */
  483.  
  484.                 if (node->tag->model & CM_HEAD)
  485.                 {
  486.                     MoveToHead(lexer, element, node);
  487.                     continue;
  488.                 }
  489.  
  490.                 if (node->tag->model & CM_LIST)
  491.                 {
  492.                     UngetToken(lexer);
  493.                     node = InferredTag(lexer, "ul");
  494.                     lexer->excludeBlocks = yes;
  495.                 }
  496.                 else if (node->tag->model & CM_DEFLIST)
  497.                 {
  498.                     UngetToken(lexer);
  499.                     node = InferredTag(lexer, "dl");
  500.                     lexer->excludeBlocks = yes;
  501.                 }
  502.  
  503.                 /* infer end of current table cell */
  504.                 if (!(node->tag->model & CM_BLOCK))
  505.                 {
  506.                     UngetToken(lexer);
  507.                     TrimSpace(lexer, element->last);
  508.                     TrimEmptyElement(lexer, element);
  509.                     return;
  510.                 }
  511.             }
  512.             else if (node->tag->model & CM_BLOCK)
  513.             {
  514.                 if (lexer->excludeBlocks)
  515.                 {
  516.                     if (!(element->tag->model & CM_OPT))
  517.                         ReportWarning(lexer, element, node, MISSING_ENDTAG_BEFORE);
  518.  
  519.                     UngetToken(lexer);
  520.  
  521.                     if (element->tag->model & CM_OBJECT)
  522.                         lexer->istackbase = istackbase;
  523.  
  524.                     TrimSpace(lexer, element->last);
  525.                     TrimEmptyElement(lexer, element);
  526.                     return;
  527.                 }
  528.             }
  529.             else /* things like list items */
  530.             {
  531.                 if (!(element->tag->model & CM_OPT))
  532.                     ReportWarning(lexer, element, node, MISSING_ENDTAG_BEFORE);
  533.  
  534.                 if (node->tag->model & CM_HEAD)
  535.                 {
  536.                     MoveToHead(lexer, element, node);
  537.                     continue;
  538.                 }
  539.  
  540.                 UngetToken(lexer);
  541.  
  542.                 if (node->tag->model & CM_LIST)
  543.                 {
  544.                     if (element->parent->tag == tag_ul || element->parent->tag == tag_ol)
  545.                     {
  546.                         TrimSpace(lexer, element->last);
  547.                         TrimEmptyElement(lexer, element);
  548.                         return;
  549.                     }
  550.  
  551.                     node = InferredTag(lexer, "ul");
  552.                 }
  553.                 else if (node->tag->model & CM_DEFLIST)
  554.                 {
  555.                     if (element->parent->tag == tag_dl)
  556.                     {
  557.                         TrimSpace(lexer, element->last);
  558.                         TrimEmptyElement(lexer, element);
  559.                         return;
  560.                     }
  561.  
  562.                     node = InferredTag(lexer, "dl");
  563.                 }
  564.                 else if (node->tag->model & CM_TABLE || node->tag->model & CM_ROW)
  565.                 {
  566.                     node = InferredTag(lexer, "table");
  567.                 }
  568.                 else if (element->tag->model & CM_OBJECT)
  569.                 {
  570.                     /* pop inline stack */
  571.                     while (lexer->istacksize > lexer->istackbase)
  572.                         PopInline(lexer, null);
  573.                     lexer->istackbase = istackbase;
  574.                     TrimSpace(lexer, element->last);
  575.                     TrimEmptyElement(lexer, element);
  576.                     return;
  577.  
  578.                 }
  579.                 else
  580.                 {
  581.                     TrimSpace(lexer, element->last);
  582.                     TrimEmptyElement(lexer, element);
  583.                     return;
  584.                 }
  585.             }
  586.         }
  587.  
  588.         /* parse known element */
  589.         if (node->type == StartTag || node->type == StartEndTag)
  590.         {
  591.             if (node->tag->model & CM_INLINE)
  592.             {
  593.                 if (checkstack && !node->implicit)
  594.                 {
  595.                     checkstack = no;
  596.  
  597.                     if (InlineDup(lexer, node) > 0)
  598.                         continue;
  599.                 }
  600.  
  601.                 mode = MixedContent;
  602.             }
  603.             else
  604.             {
  605.                 checkstack = yes;
  606.                 mode = IgnoreWhitespace;
  607.             }
  608.  
  609.             /* trim white space before <br> */
  610.             if (node->tag == tag_br)
  611.                 TrimSpace(lexer, element->last);
  612.  
  613.             InsertNode(element, node);
  614.             
  615.             if (node->implicit)
  616.                 ReportWarning(lexer, element, node, INSERTING_TAG);
  617.  
  618.             ParseTag(lexer, node, IgnoreWhitespace /*MixedContent*/);
  619.             continue;
  620.         }
  621.  
  622.         /* discard unexpected tags */
  623.         if (node->type == EndTag)
  624.             PopInline(lexer, node);  /* if inline end tag */
  625.  
  626.         ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  627.         FreeNode(node);
  628.     }
  629.  
  630.     if (!(element->tag->model & CM_OPT))
  631.         ReportWarning(lexer, element, node, MISSING_ENDTAG_FOR);
  632.  
  633.     if (element->tag->model & CM_OBJECT)
  634.     {
  635.         /* pop inline stack */
  636.         while (lexer->istacksize > lexer->istackbase)
  637.             PopInline(lexer, null);
  638.         lexer->istackbase = istackbase;
  639.     }
  640.  
  641.     TrimSpace(lexer, element->last);
  642.     TrimEmptyElement(lexer, element);
  643. }
  644.  
  645. void ParseInline(Lexer *lexer, Node *element, uint mode)
  646. {
  647.     Node *node, *parent;
  648.  
  649.     if (element->tag->model & CM_EMPTY)
  650.         return;
  651.  
  652.     if (element->tag == tag_a)
  653.     {
  654.         if (element->attributes == null)
  655.         {
  656.             ReportWarning(lexer, element->parent, element, DISCARDING_UNEXPECTED);
  657.             DiscardElement(lexer, element);
  658.             return;
  659.         }
  660.     }
  661.  
  662.     /*
  663.      ParseInline is used for some block level elements like H1 to H6
  664.      For such elements we need to insert inline emphasis tags currently
  665.      on the inline stack. For Inline elements, we normally push them
  666.      onto the inline stack provided they aren't implicit or OBJECT/APPLET.
  667.      This test is carried out in PushInline and PopInline, see istack.c
  668.     */
  669.     if (element->tag->model & CM_BLOCK)
  670.         InlineDup(lexer, null);
  671.     else if (element->tag->model & CM_INLINE)
  672.         PushInline(lexer, element);
  673.  
  674.     if (element->tag == tag_nobr)
  675.         lexer->badLayout |= USING_NOBR;
  676.     else if (element->tag == tag_font)
  677.         lexer->badLayout |= USING_FONT;
  678.  
  679.     /* Inline elements may or may not be within a preformatted element */
  680.     if (mode != Preformatted)
  681.         mode = MixedContent;
  682.  
  683.     while ((node = GetToken(lexer, mode)) != null)
  684.     {
  685.         /* end tag for current element */
  686.         if (node->tag == element->tag && node->type == EndTag)
  687.         {
  688.             if (element->tag->model & CM_INLINE)
  689.                 PopInline(lexer, node);
  690.  
  691.             FreeNode(node);
  692.             TrimTrailingSpace(lexer, element->last);
  693.  
  694.             /*
  695.              if a font element wraps an anchor and nothing else
  696.              then move the font element inside the anchor since
  697.              otherwise it won't alter the anchor text color
  698.             */
  699.             if (element->tag == tag_font && element->content && element->content == element->last)
  700.             {
  701.                 Node *child = element->content;
  702.  
  703.                 if (child->tag == tag_a)
  704.                 {
  705.                     child->parent = element->parent;
  706.                     child->next = element->next;
  707.                     child->prev = element->prev;
  708.  
  709.                     if (child->prev)
  710.                         child->prev->next = child;
  711.                     else
  712.                         child->parent->content = child;
  713.  
  714.                     if (child->next)
  715.                         child->next->prev = child;
  716.                     else
  717.                         child->parent->last = child;
  718.  
  719.                     element->prev = element->last = null;
  720.                     element->parent = child;
  721.                     element->content = child->content;
  722.                     child->content = child->last = element;
  723.                 }
  724.             }
  725.  
  726.             TrimEmptyElement(lexer, element);
  727.             return;
  728.         }
  729.  
  730.         if (node->type == TextNode)
  731.         {
  732.             /* only called for 1st child */
  733.             if (element->content == null)
  734.                 TrimInitialSpace(lexer, element, node);
  735.  
  736.             if (node->start >= node->end)
  737.             {
  738.                 FreeNode(node);
  739.                 continue;
  740.             }
  741.  
  742.             InsertNode(element, node);
  743.             continue;
  744.         }
  745.  
  746.         /* mixed content model so allow text */
  747.         if (node->type == CommentTag || 
  748.             node->type == ProcInsTag ||
  749.             node->type == AspTag)
  750.         {
  751.             InsertNode(element, node);
  752.             continue;
  753.         }
  754.  
  755.         /* deal with HTML tags */
  756.         if (node->tag == tag_html)
  757.         {
  758.             if (node->type == StartTag || node->type == StartEndTag)
  759.             {
  760.                 ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  761.                 FreeNode(node);
  762.                 continue;
  763.             }
  764.  
  765.             /* otherwise infer end of inline element */
  766.             UngetToken(lexer);
  767.             TrimTrailingSpace(lexer, element->last);
  768.             TrimEmptyElement(lexer, element);
  769.             return;
  770.         }
  771.  
  772.         /* ignore unknown and PARAM tags */
  773.         if (node->tag == null || node->tag == tag_param)
  774.         {
  775.             ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  776.             FreeNode(node);
  777.             continue;
  778.         }
  779.  
  780.         /* allow any inline end tag to end current element */
  781.         if (node->type == EndTag && node->tag->model & CM_INLINE
  782.                                 && !(node->tag->model & CM_OBJECT)
  783.                                 && element->tag->model & CM_INLINE)
  784.         {
  785.             PopInline(lexer, element);
  786.  
  787.             if (node->tag == tag_a && node->tag != element->tag)
  788.             {
  789.                ReportWarning(lexer, element, node, MISSING_ENDTAG_BEFORE);
  790.                UngetToken(lexer);
  791.             }
  792.             else
  793.             {
  794.                 ReportWarning(lexer, element, node, NON_MATCHING_ENDTAG);
  795.                 FreeNode(node);
  796.             }
  797.  
  798.             TrimTrailingSpace(lexer, element->last);
  799.             TrimEmptyElement(lexer, element);
  800.             return;
  801.         }
  802.  
  803.         /* allow any header tag to end current header */
  804.         if (node->tag->model & CM_HEADING && element->tag->model & CM_HEADING)
  805.         {
  806.             ReportWarning(lexer, element, node, NON_MATCHING_ENDTAG);
  807.             FreeNode(node);
  808.             TrimTrailingSpace(lexer, element->last);
  809.             TrimEmptyElement(lexer, element);
  810.             return;
  811.         }
  812.  
  813.         /*
  814.            an <A> tag to ends any open <A> element
  815.            but <A href=...> is mapped to </A><A href=...>
  816.         */
  817.         if (node->tag == tag_a && !node->implicit && IsPushed(lexer, node))
  818.         {
  819.          /* coerce <a> to </a> unless it has some attributes */
  820.             if (node->attributes == null)
  821.             {
  822.                 node->type = EndTag;
  823.                 ReportWarning(lexer, element, node, FORCED_END_ANCHOR);
  824.                 PopInline(lexer, node);
  825.                 UngetToken(lexer);
  826.                 continue;
  827.             }
  828.  
  829.             UngetToken(lexer);
  830.             ReportWarning(lexer, element, node, MISSING_ENDTAG_BEFORE);
  831.             PopInline(lexer, element);
  832.             TrimTrailingSpace(lexer, element->last);
  833.             TrimEmptyElement(lexer, element);
  834.             return;
  835.         }
  836.  
  837.         /* 
  838.           if this is the end tag for an ancestor element
  839.           then infer end tag for this element
  840.         */
  841.         if (node->type == EndTag)
  842.         {
  843.             for (parent = element->parent;
  844.                     parent != null; parent = parent->parent)
  845.             {
  846.                 if (node->tag == parent->tag)
  847.                 {
  848.                     if (!(element->tag->model & CM_OPT) && !element->implicit)
  849.                         ReportWarning(lexer, element, node, MISSING_ENDTAG_BEFORE);
  850.  
  851.                     if (element->tag == tag_a)
  852.                         PopInline(lexer, element);
  853.  
  854.                     UngetToken(lexer);
  855.                     TrimTrailingSpace(lexer, element->last);
  856.                     TrimEmptyElement(lexer, element);
  857.                     return;
  858.                 }
  859.             }
  860.         }
  861.  
  862.         if (node->tag == tag_hr && (element->tag->model & CM_HEADING))
  863.         {
  864.             ReportWarning(lexer, element, node, TAG_NOT_ALLOWED_IN);
  865.  
  866.             /*
  867.             if the HR is the 1st thing in the heading then
  868.               simply insert it before the heading element
  869.             */
  870.  
  871.             if (element->content == null)
  872.             {
  873.                 parent = element->parent;
  874.  
  875.                 if (parent->content == element)
  876.                 {
  877.                     parent->content = node;
  878.                     node->prev = null;
  879.                 }
  880.                 else /* element isn't first node in parent's content */
  881.                 {
  882.                     element->prev->next = node;
  883.                     node->prev = element->prev;
  884.                 }
  885.  
  886.                 node->parent = parent;
  887.                 node->next = element;
  888.                 element->prev = node;
  889.                 continue;
  890.             }
  891.  
  892.             /*
  893.              otherwise close the heading, insert the HR
  894.              and then continue with a new heading element
  895.             */
  896.  
  897.             element->next = node;
  898.             node->prev = element;
  899.             node->parent = element->parent;
  900.             TrimSpace(lexer, element->last);
  901.  
  902.             element = CloneNode(lexer, element);
  903.             element->prev = node;
  904.             element->parent = node->parent;
  905.             node->next = element;
  906.             node->parent->last = element;
  907.             continue;
  908.         }
  909.  
  910.         /* block level tags end this element */
  911.         if (!(node->tag->model & CM_INLINE))
  912.         {
  913.             if (node->type != StartTag)
  914.             {
  915.                 ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  916.                 continue;
  917.             }
  918.  
  919.             if (!(element->tag->model & CM_OPT))
  920.                 ReportWarning(lexer, element, node, MISSING_ENDTAG_BEFORE);
  921.  
  922.             if (node->tag->model & CM_HEAD && !(node->tag->model & CM_BLOCK))
  923.             {
  924.                 MoveToHead(lexer, element, node);
  925.                 continue;
  926.             }
  927.  
  928.             /*
  929.                prevent anchors from propagating into block tags
  930.                except for headings h1 to h6
  931.             */
  932.             if (element->tag == tag_a)
  933.             {
  934.                 if (node->tag && !(node->tag->model & CM_HEADING))
  935.                     PopInline(lexer, element);
  936.                 else if (!(element->content))
  937.                 {
  938.                     DiscardElement(lexer, element);
  939.                     UngetToken(lexer);
  940.                     return;
  941.                 }
  942.             }
  943.  
  944.             UngetToken(lexer);
  945.             TrimTrailingSpace(lexer, element->last);
  946.             TrimEmptyElement(lexer, element);
  947.             return;
  948.         }
  949.  
  950.         /* parse inline element */
  951.         if (node->type == StartTag || node->type == StartEndTag)
  952.         {
  953.             if (node->implicit)
  954.                 ReportWarning(lexer, element, node, INSERTING_TAG);
  955.  
  956.             /* trim white space before <br> */
  957.             if (node->tag == tag_br)
  958.                 TrimSpace(lexer, element->last);
  959.             
  960.             InsertNode(element, node);
  961.             ParseTag(lexer, node, mode);
  962.             continue;
  963.         }
  964.  
  965.         /* discard unexpected tags */
  966.         ReportWarning(lexer, element, node, DISCARDING_UNEXPECTED);
  967.         FreeNode(node);
  968.     }
  969.  
  970.     if (!(element->tag->model & CM_OPT))
  971.         ReportWarning(lexer, element, node, MISSING_ENDTAG_FOR);
  972.  
  973.     TrimEmptyElement(lexer, element);
  974. }
  975.  
  976. void ParseDefList(Lexer *lexer, Node *list, uint mode)
  977. {
  978.     Node *node, *parent;
  979.  
  980.     if (list->tag->model & CM_EMPTY)
  981.         return;
  982.  
  983.     lexer->insert = null;  /* defer implicit inline start tags */
  984.  
  985.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  986.     {
  987.         if (node->tag == list->tag && node->type == EndTag)
  988.         {
  989.             FreeNode(node);
  990.             TrimEmptyElement(lexer, list);
  991.             return;
  992.         }
  993.  
  994.         /* deal with comments */
  995.         if (node->type == CommentTag ||
  996.             node->type == ProcInsTag ||
  997.             node->type == AspTag)
  998.         {
  999.             InsertNode(list, node);
  1000.             continue;
  1001.         }
  1002.  
  1003.         if (node->type == TextNode)
  1004.         {
  1005.             UngetToken(lexer);
  1006.             node = InferredTag(lexer, "dd");
  1007.             ReportWarning(lexer, list, node, MISSING_STARTTAG);
  1008.         }
  1009.  
  1010.         if (node->tag == null)
  1011.         {
  1012.             ReportWarning(lexer, list, node, DISCARDING_UNEXPECTED);
  1013.             FreeNode(node);
  1014.             continue;
  1015.         }
  1016.  
  1017.         /* 
  1018.           if this is the end tag for an ancestor element
  1019.           then infer end tag for this element
  1020.         */
  1021.         if (node->type == EndTag)
  1022.         {
  1023.             if (node->tag == tag_form)
  1024.             {
  1025.                 lexer->badForm = yes;
  1026.                 ReportWarning(lexer, list, node, DISCARDING_UNEXPECTED);
  1027.                 continue;
  1028.             }
  1029.  
  1030.             for (parent = list->parent;
  1031.                     parent != null; parent = parent->parent)
  1032.             {
  1033.                 if (node->tag == parent->tag)
  1034.                 {
  1035.                     ReportWarning(lexer, list, node, MISSING_ENDTAG_BEFORE);
  1036.  
  1037.                     UngetToken(lexer);
  1038.                     TrimEmptyElement(lexer, list);
  1039.                     return;
  1040.                 }
  1041.             }
  1042.         }
  1043.  
  1044.         if (!(node->tag == tag_dt || node->tag == tag_dd))
  1045.         {
  1046.             UngetToken(lexer);
  1047.  
  1048.             if (!(node->tag->model & (CM_BLOCK | CM_INLINE)))
  1049.             {
  1050.                 ReportWarning(lexer, list, node, TAG_NOT_ALLOWED_IN);
  1051.                 TrimEmptyElement(lexer, list);
  1052.                 return;
  1053.             }
  1054.  
  1055.             /* if DD appeared directly in BODY then exclude blocks */
  1056.             if (!(node->tag->model & CM_INLINE) && lexer->excludeBlocks)
  1057.             {
  1058.                 TrimEmptyElement(lexer, list);
  1059.                 return;
  1060.             }
  1061.  
  1062.             node = InferredTag(lexer, "dd");
  1063.             ReportWarning(lexer, list, node, MISSING_STARTTAG);
  1064.         }
  1065.  
  1066.         if (node->type == EndTag)
  1067.         {
  1068.             ReportWarning(lexer, list, node, DISCARDING_UNEXPECTED);
  1069.             continue;
  1070.         }
  1071.         
  1072.         /* node should be <DT> or <DD>*/
  1073.         InsertNode(list, node);
  1074.         ParseTag(lexer, node, IgnoreWhitespace);
  1075.     }
  1076.  
  1077.     ReportWarning(lexer, list, node, MISSING_ENDTAG_FOR);
  1078.     TrimEmptyElement(lexer, list);
  1079. }
  1080.  
  1081. void ParseList(Lexer *lexer, Node *list, uint mode)
  1082. {
  1083.     Node *node, *parent;
  1084.     Bool first = yes;
  1085.  
  1086.     if (list->tag->model & CM_EMPTY)
  1087.         return;
  1088.  
  1089.  
  1090.     lexer->insert = null;  /* defer implicit inline start tags */
  1091.  
  1092.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  1093.     {
  1094.         if (node->tag == list->tag && node->type == EndTag)
  1095.         {
  1096.             FreeNode(node);
  1097.  
  1098.             /*
  1099.              a bit of a hack, this code is made more
  1100.              complex by the fact that ReportWarning takes
  1101.              2 nodes rather than a node and a string
  1102.             */
  1103.             if (MakeClean && (list->tag->model & CM_OBSOLETE))
  1104.             {
  1105.                 /* create a ul node for the error message */
  1106.                 node = CloneNode(lexer, list);
  1107.                 MemFree(node->element);
  1108.                 node->element = wstrdup("ul");
  1109.                 node->tag = tag_ul;
  1110.                 ReportWarning(lexer, list, node, OBSOLETE_ELEMENT);
  1111.  
  1112.                 node->parent = node->prev = node->next = null;
  1113.                 FreeNode(node);  /* only used for error message */
  1114.  
  1115.                 /* and coerce listing to pre */
  1116.                 MemFree(list->element);
  1117.                 list->element = wstrdup("ul");
  1118.                 list->tag = tag_ul;
  1119.             }
  1120.  
  1121.             TrimEmptyElement(lexer, list);
  1122.             return;
  1123.         }
  1124.  
  1125.         if (node->type == TextNode)
  1126.         {
  1127.             UngetToken(lexer);
  1128.  
  1129.             /*
  1130.               the illegal form <ul>some text</ul> is sometimes used to get an
  1131.               indent; map it to <blockquote class="indent">some text</blockquote>
  1132.             */
  1133.  
  1134.             if (first)
  1135.             {
  1136.                 ReportWarning(lexer, list, node, TAG_NOT_ALLOWED_IN);
  1137.                 MemFree(list->element);
  1138.                 list->element = wstrdup("blockquote");
  1139.                 list->was = list->tag;
  1140.                 list->tag = tag_blockquote;
  1141.                 list->implicit = yes;
  1142.                 ParseBlock(lexer, list, mode);
  1143.                 return;
  1144.             }
  1145.  
  1146.             node = InferredTag(lexer, "li");
  1147.             ReportWarning(lexer, list, node, MISSING_STARTTAG);
  1148.         }
  1149.  
  1150.         /* deal with comments */
  1151.         if (node->type == CommentTag ||
  1152.             node->type == ProcInsTag ||
  1153.             node->type == AspTag)
  1154.         {
  1155.             InsertNode(list, node);
  1156.             continue;
  1157.         }
  1158.  
  1159.         if (node->tag == null)
  1160.         {
  1161.             ReportWarning(lexer, list, node, DISCARDING_UNEXPECTED);
  1162.             FreeNode(node);
  1163.             continue;
  1164.         }
  1165.  
  1166.         /* 
  1167.           if this is the end tag for an ancestor element
  1168.           then infer end tag for this element
  1169.         */
  1170.         if (node->type == EndTag)
  1171.         {
  1172.             if (node->tag == tag_form)
  1173.             {
  1174.                 lexer->badForm = yes;
  1175.                 ReportWarning(lexer, list, node, DISCARDING_UNEXPECTED);
  1176.                 continue;
  1177.             }
  1178.  
  1179.             for (parent = list->parent;
  1180.                     parent != null; parent = parent->parent)
  1181.             {
  1182.                 if (node->tag == parent->tag)
  1183.                 {
  1184.                     ReportWarning(lexer, list, node, MISSING_ENDTAG_BEFORE);
  1185.  
  1186.                     UngetToken(lexer);
  1187.                     TrimEmptyElement(lexer, list);
  1188.                     return;
  1189.                 }
  1190.             }
  1191.         }
  1192.  
  1193.         if (!(node->tag == tag_li))
  1194.         {
  1195.             /*
  1196.                if node is <ul> or <ol> and we have at least one <li> 
  1197.                then parse node and append to the previous <li>
  1198.             */
  1199.  
  1200.             if (node->type != EndTag &&
  1201.                 (node->tag->parser == ParseList ||
  1202.                  node->tag->parser == ParseDefList))
  1203.             {
  1204.                 Node *blockquote;
  1205.  
  1206.                 if (first)
  1207.                 {
  1208.                     ReportWarning(lexer, list, node, TAG_NOT_ALLOWED_IN);
  1209.                     blockquote = InferredTag(lexer, "blockquote");
  1210.                     InsertNodeBeforeElement(list, blockquote);
  1211.                     InsertNode(blockquote, node);
  1212.                     ParseTag(lexer, node, mode);
  1213.                     continue;
  1214.                 }
  1215.  
  1216.                 ReportWarning(lexer, list, node, TAG_NOT_ALLOWED_IN);
  1217.                 InsertNode(list->last, node);
  1218.                 ParseTag(lexer, node, IgnoreWhitespace);
  1219.                 continue;
  1220.            }
  1221.  
  1222.  
  1223.             UngetToken(lexer);
  1224.  
  1225.             if (!(node->tag->model & (CM_BLOCK | CM_INLINE)))
  1226.             {
  1227.                 ReportWarning(lexer, list, node, TAG_NOT_ALLOWED_IN);
  1228.                 TrimEmptyElement(lexer, list);
  1229.                 return;
  1230.             }
  1231.  
  1232.             /* if LI appeared directly in BODY then exclude blocks */
  1233.             if (!(node->tag->model & CM_INLINE) && lexer->excludeBlocks)
  1234.             {   
  1235.                 TrimEmptyElement(lexer, list);
  1236.                 return;
  1237.             }
  1238.  
  1239.             if (node->type == EndTag)
  1240.             {
  1241.                 ReportWarning(lexer, list, node, DISCARDING_UNEXPECTED);
  1242.                 continue;
  1243.             }
  1244.  
  1245.             /*
  1246.               the illegal form <ul>some text</ul> is sometimes used to get an
  1247.               indent; map it to <blockquote class="indent">some text</blockquote>
  1248.             */
  1249.  
  1250.             if (first)
  1251.             {
  1252.                 ReportWarning(lexer, list, node, TAG_NOT_ALLOWED_IN);
  1253.                 MemFree(list->element);
  1254.                 list->element = wstrdup("blockquote");
  1255.                 list->was = list->tag;
  1256.                 list->tag = tag_blockquote;
  1257.                 list->implicit = yes;
  1258.                 ParseBlock(lexer, list, mode);
  1259.                 return;
  1260.             }
  1261.  
  1262.             node = InferredTag(lexer, "li");
  1263.             ReportWarning(lexer, list, node, MISSING_STARTTAG);
  1264.         }
  1265.  
  1266.         if (node->type == EndTag)
  1267.         {
  1268.             ReportWarning(lexer, list, node, DISCARDING_UNEXPECTED);
  1269.             continue;
  1270.         }
  1271.  
  1272.         /* node should be <LI> */
  1273.         InsertNode(list,node);
  1274.         ParseTag(lexer, node, IgnoreWhitespace);
  1275.         first = no;
  1276.     }
  1277.  
  1278.     ReportWarning(lexer, list, node, MISSING_ENDTAG_FOR);
  1279.     TrimEmptyElement(lexer, list);
  1280. }
  1281.  
  1282. /*
  1283.  unexpected content in table row is moved to just before
  1284.  the table in accordance with Netscape and IE. This code
  1285.  assumes that node hasn't been inserted into the row.
  1286. */
  1287. void MoveBeforeTable(Node *row, Node *node)
  1288. {
  1289.     Node *table;
  1290.  
  1291.     /* first find the table element */
  1292.     for (table = row->parent; table; table = table->parent)
  1293.     {
  1294.         if (table->tag == tag_table)
  1295.         {
  1296.             node->prev = table->prev;
  1297.             node->next = table;
  1298.             table->prev = node;
  1299.             node->parent = table->parent;
  1300.         
  1301.             if (node->prev)
  1302.                 node->prev->next = node;
  1303.  
  1304.             break;
  1305.         }
  1306.     }
  1307. }
  1308.  
  1309. void ParseRow(Lexer *lexer, Node *row, uint mode)
  1310. {
  1311.     Node *node, *parent;
  1312.     Bool exclude_state;
  1313.  
  1314.     if (row->tag->model & CM_EMPTY)
  1315.         return;
  1316.  
  1317.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  1318.     {
  1319.         if (node->tag == row->tag)
  1320.         {
  1321.             if (node->type == EndTag)
  1322.             {
  1323.                 FreeNode(node);
  1324.                 TrimEmptyElement(lexer, row);
  1325.                 return;
  1326.             }
  1327.  
  1328.             UngetToken(lexer);
  1329.             TrimEmptyElement(lexer, row);
  1330.             return;
  1331.         }
  1332.  
  1333.         /* 
  1334.           if this is the end tag for an ancestor element
  1335.           then infer end tag for this element
  1336.         */
  1337.         if (node->type == EndTag)
  1338.         {
  1339.             if (node->tag == tag_form)
  1340.             {
  1341.                 lexer->badForm = yes;
  1342.                 ReportWarning(lexer, row, node, DISCARDING_UNEXPECTED);
  1343.                 continue;
  1344.             }
  1345.  
  1346.             if (node->tag == tag_td || node->tag == tag_th)
  1347.             {
  1348.                 ReportWarning(lexer, row, node, DISCARDING_UNEXPECTED);
  1349.                 FreeNode(node);
  1350.                 continue;
  1351.             }
  1352.  
  1353.             for (parent = row->parent;
  1354.                     parent != null; parent = parent->parent)
  1355.             {
  1356.                 if (node->tag == parent->tag)
  1357.                 {
  1358.                     UngetToken(lexer);
  1359.                     TrimEmptyElement(lexer, row);
  1360.                     return;
  1361.                 }
  1362.             }
  1363.         }
  1364.  
  1365.         /* deal with comments */
  1366.         if (node->type == CommentTag ||
  1367.             node->type == ProcInsTag ||
  1368.             node->type == AspTag)
  1369.         {
  1370.             InsertNode(row, node);
  1371.             continue;
  1372.         }
  1373.  
  1374.         /* discard unknown tags */
  1375.         if (node->tag == null && node->type != TextNode)
  1376.         {
  1377.             ReportWarning(lexer, row, node, DISCARDING_UNEXPECTED);
  1378.             FreeNode(node);
  1379.             continue;
  1380.         }
  1381.  
  1382.         /* discard unexpected <table> element */
  1383.         if (node->tag == tag_table)
  1384.         {
  1385.             ReportWarning(lexer, row, node, DISCARDING_UNEXPECTED);
  1386.             FreeNode(node);
  1387.             continue;
  1388.         }
  1389.  
  1390.         /* THEAD, TFOOT or TBODY */
  1391.         if (node->tag && (node->tag->model & CM_ROWGRP))
  1392.         {
  1393.             UngetToken(lexer);
  1394.             TrimEmptyElement(lexer, row);
  1395.             return;
  1396.         }
  1397.  
  1398.         if (node->type == EndTag)
  1399.         {
  1400.             ReportWarning(lexer, row, node, DISCARDING_UNEXPECTED);
  1401.             continue;
  1402.         }
  1403.  
  1404.         if (!(node->tag == tag_td || node->tag == tag_th))
  1405.         {
  1406.             MoveBeforeTable(row, node);
  1407.             ReportWarning(lexer, row, node, TAG_NOT_ALLOWED_IN);
  1408.  
  1409.             if (node->type != TextNode)
  1410.                 ParseTag(lexer, node, IgnoreWhitespace);
  1411.  
  1412.             continue;
  1413. #if 0
  1414.             /* previous code inferred either </tr> or <td> */
  1415.             if (!(node->tag->model & (CM_BLOCK | CM_INLINE)))
  1416.             {
  1417.                 ReportWarning(lexer, row, node, MISSING_ENDTAG_BEFORE);
  1418.                 TrimEmptyElement(lexer, row);
  1419.                 return;
  1420.             }
  1421.  
  1422.             node = InferredTag(lexer, "td");
  1423.             ReportWarning(lexer, row, node, MISSING_STARTTAG);
  1424. #endif
  1425.         }
  1426.         
  1427.         /* node should be <TD> or <TH> */
  1428.         InsertNode(row, node);
  1429.         exclude_state = lexer->excludeBlocks;
  1430.         lexer->excludeBlocks = no;
  1431.         ParseTag(lexer, node, IgnoreWhitespace);
  1432.         lexer->excludeBlocks = exclude_state;
  1433.  
  1434.         /* pop inline stack */
  1435.  
  1436.         while (lexer->istacksize > lexer->istackbase)
  1437.             PopInline(lexer, null);
  1438.     }
  1439.  
  1440.     TrimEmptyElement(lexer, row);
  1441. }
  1442.  
  1443. void ParseRowGroup(Lexer *lexer, Node *rowgroup, uint mode)
  1444. {
  1445.     Node *node, *parent;
  1446.  
  1447.     if (rowgroup->tag->model & CM_EMPTY)
  1448.         return;
  1449.  
  1450.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  1451.     {
  1452.         if (node->tag == rowgroup->tag)
  1453.         {
  1454.             if (node->type == EndTag)
  1455.             {
  1456.                 TrimEmptyElement(lexer, rowgroup);
  1457.                 FreeNode(node);
  1458.                 return;
  1459.             }
  1460.  
  1461.             UngetToken(lexer);
  1462.             return;
  1463.         }
  1464.  
  1465.         /* if </table> infer end tag */
  1466.         if (node->tag == tag_table && node->type == EndTag)
  1467.         {
  1468.             UngetToken(lexer);
  1469.             TrimEmptyElement(lexer, rowgroup);
  1470.             return;
  1471.         }
  1472.  
  1473.         /* discard unknown tags */
  1474.         if (node->tag == null && node->type != TextNode)
  1475.         {
  1476.             ReportWarning(lexer, rowgroup, node, DISCARDING_UNEXPECTED);
  1477.             FreeNode(node);
  1478.             continue;
  1479.         }
  1480.  
  1481.         /* if TD or TH or text or inline or block then infer <TR> */
  1482.  
  1483.         if (node->type == StartTag &&
  1484.              (node->tag == tag_td || 
  1485.                node->tag == tag_th || 
  1486.                (node->tag->model & (CM_BLOCK | CM_INLINE)))
  1487.                || node->type == TextNode)
  1488.         {
  1489.             UngetToken(lexer);
  1490.             node = InferredTag(lexer, "tr");
  1491.             ReportWarning(lexer, rowgroup, node, MISSING_STARTTAG);
  1492.         }
  1493.  
  1494.         if (node->type == CommentTag ||
  1495.             node->type == ProcInsTag ||
  1496.             node->type == AspTag)
  1497.         {
  1498.             InsertNode(rowgroup, node);
  1499.             continue;
  1500.         }
  1501.  
  1502.         /* 
  1503.           if this is the end tag for ancestor element
  1504.           then infer end tag for this element
  1505.         */
  1506.         if (node->type == EndTag)
  1507.         {
  1508.             if (node->tag == tag_form)
  1509.             {
  1510.                 lexer->badForm = yes;
  1511.                 ReportWarning(lexer, rowgroup, node, DISCARDING_UNEXPECTED);
  1512.                 continue;
  1513.             }
  1514.  
  1515.             if (node->tag == tag_tr || node->tag == tag_td || node->tag == tag_th)
  1516.             {
  1517.                 ReportWarning(lexer, rowgroup, node, DISCARDING_UNEXPECTED);
  1518.                 FreeNode(node);
  1519.                 continue;
  1520.             }
  1521.  
  1522.             for (parent = rowgroup->parent;
  1523.                     parent != null; parent = parent->parent)
  1524.             {
  1525.                 if (node->tag == parent->tag)
  1526.                 {
  1527.                     UngetToken(lexer);
  1528.                     TrimEmptyElement(lexer, rowgroup);
  1529.                     return;
  1530.                 }
  1531.             }
  1532.         }
  1533.  
  1534.         /*
  1535.           if THEAD, TFOOT or TBODY then implied end tag
  1536.  
  1537.         */
  1538.         if (node->tag->model & CM_ROWGRP)
  1539.         {
  1540.             if (node->type != EndTag)
  1541.                 UngetToken(lexer);
  1542.  
  1543.             TrimEmptyElement(lexer, rowgroup);
  1544.             return;
  1545.         }
  1546.  
  1547.         if (node->type == EndTag)
  1548.         {
  1549.             ReportWarning(lexer, rowgroup, node, DISCARDING_UNEXPECTED);
  1550.             continue;
  1551.         }
  1552.         
  1553.         if (!(node->tag == tag_tr))
  1554.         {
  1555.             node = InferredTag(lexer, "tr");
  1556.             ReportWarning(lexer, rowgroup, node, MISSING_STARTTAG);
  1557.             UngetToken(lexer);
  1558.         }
  1559.  
  1560.        /* node should be <TR> */
  1561.         InsertNode(rowgroup, node);
  1562.         ParseTag(lexer, node, IgnoreWhitespace);
  1563.     }
  1564.  
  1565.     TrimEmptyElement(lexer, rowgroup);
  1566. }
  1567.  
  1568. void ParseColGroup(Lexer *lexer, Node *colgroup, uint mode)
  1569. {
  1570.     Node *node, *parent;
  1571.  
  1572.     if (colgroup->tag->model & CM_EMPTY)
  1573.         return;
  1574.  
  1575.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  1576.     {
  1577.         if (node->tag == colgroup->tag && node->type == EndTag)
  1578.         {
  1579.             FreeNode(node);
  1580.             return;
  1581.         }
  1582.  
  1583.         /* 
  1584.           if this is the end tag for an ancestor element
  1585.           then infer end tag for this element
  1586.         */
  1587.         if (node->type == EndTag)
  1588.         {
  1589.             if (node->tag == tag_form)
  1590.             {
  1591.                 lexer->badForm = yes;
  1592.                 ReportWarning(lexer, colgroup, node, DISCARDING_UNEXPECTED);
  1593.                 continue;
  1594.             }
  1595.  
  1596.             for (parent = colgroup->parent;
  1597.                     parent != null; parent = parent->parent)
  1598.             {
  1599.  
  1600.                 if (node->tag == parent->tag)
  1601.                 {
  1602.                     UngetToken(lexer);
  1603.                     return;
  1604.                 }
  1605.             }
  1606.         }
  1607.  
  1608.         if (node->type == TextNode)
  1609.         {
  1610.             UngetToken(lexer);
  1611.             return;
  1612.         }
  1613.  
  1614.         /* deal with comments */
  1615.         if (node->type == CommentTag ||
  1616.             node->type == ProcInsTag ||
  1617.             node->type == AspTag)
  1618.         {
  1619.             InsertNode(colgroup, node);
  1620.             continue;
  1621.         }
  1622.  
  1623.         /* discard unknown tags */
  1624.         if (node->tag == null)
  1625.         {
  1626.             ReportWarning(lexer, colgroup, node, DISCARDING_UNEXPECTED);
  1627.             FreeNode(node);
  1628.             continue;
  1629.         }
  1630.  
  1631.         if (node->tag != tag_col)
  1632.         {
  1633.             UngetToken(lexer);
  1634.             return;
  1635.         }
  1636.  
  1637.         if (node->type == EndTag)
  1638.         {
  1639.             ReportWarning(lexer, colgroup, node, DISCARDING_UNEXPECTED);
  1640.             continue;
  1641.         }
  1642.         
  1643.         /* node should be <COL> */
  1644.         InsertNode(colgroup, node);
  1645.         ParseTag(lexer, node, IgnoreWhitespace);
  1646.     }
  1647. }
  1648.  
  1649. void ParseTableTag(Lexer *lexer, Node *table, uint mode)
  1650. {
  1651.     Node *node, *parent;
  1652.     uint istackbase;
  1653.  
  1654.     istackbase = lexer->istackbase;
  1655.     lexer->istackbase = lexer->istacksize;
  1656.     
  1657.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  1658.     {
  1659.         if (node->tag == table->tag && node->type == EndTag)
  1660.         {
  1661.             FreeNode(node);
  1662.             lexer->istackbase = istackbase;
  1663.             TrimEmptyElement(lexer, table);
  1664.             return;
  1665.         }
  1666.  
  1667.         if (node->type == CommentTag ||
  1668.             node->type == ProcInsTag ||
  1669.             node->type == AspTag)
  1670.         {
  1671.             InsertNode(table, node);
  1672.             continue;
  1673.         }
  1674.  
  1675.         /* discard unknown tags */
  1676.         if (node->tag == null && node->type != TextNode)
  1677.         {
  1678.             ReportWarning(lexer, table, node, DISCARDING_UNEXPECTED);
  1679.             FreeNode(node);
  1680.             continue;
  1681.         }
  1682.  
  1683.         /* if TD or TH or text or inline or block then infer <TR> */
  1684.  
  1685.         if (node->type != EndTag)
  1686.         {
  1687.             if (node->tag == tag_td || 
  1688.                 node->tag == tag_th || 
  1689.                 node->type == TextNode || 
  1690.                 (node->tag->model & (CM_BLOCK | CM_INLINE)))
  1691.             {
  1692.                 UngetToken(lexer);
  1693.                 node = InferredTag(lexer, "tr");
  1694.                 ReportWarning(lexer, table, node, MISSING_STARTTAG);
  1695.             }
  1696.         }
  1697.  
  1698.         /* 
  1699.           if this is the end tag for an ancestor element
  1700.           then infer end tag for this element
  1701.         */
  1702.         if (node->type == EndTag)
  1703.         {
  1704.             if (node->tag == tag_form)
  1705.             {
  1706.                 lexer->badForm = yes;
  1707.                 ReportWarning(lexer, table, node, DISCARDING_UNEXPECTED);
  1708.                 continue;
  1709.             }
  1710.  
  1711.             if (node->tag && node->tag->model & (CM_TABLE|CM_ROW))
  1712.             {
  1713.                 ReportWarning(lexer, table, node, DISCARDING_UNEXPECTED);
  1714.                 FreeNode(node);
  1715.                 continue;
  1716.             }
  1717.  
  1718.             for (parent = table->parent;
  1719.                     parent != null; parent = parent->parent)
  1720.             {
  1721.                 if (node->tag == parent->tag)
  1722.                 {
  1723.                     ReportWarning(lexer, table, node, MISSING_ENDTAG_BEFORE);
  1724.                     UngetToken(lexer);
  1725.                     lexer->istackbase = istackbase;
  1726.                     TrimEmptyElement(lexer, table);
  1727.                     return;
  1728.                 }
  1729.             }
  1730.         }
  1731.  
  1732.         if (!(node->tag->model & CM_TABLE))
  1733.         {
  1734.             UngetToken(lexer);
  1735.             ReportWarning(lexer, table, node, TAG_NOT_ALLOWED_IN);
  1736.             lexer->istackbase = istackbase;
  1737.             TrimEmptyElement(lexer, table);
  1738.             return;
  1739.         }
  1740.  
  1741.         if (node->type == StartTag || node->type == StartEndTag)
  1742.         {
  1743.             InsertNode(table, node);;
  1744.             ParseTag(lexer, node, IgnoreWhitespace);
  1745.             continue;
  1746.         }
  1747.  
  1748.         /* discard unexpected text nodes and end tags */
  1749.         ReportWarning(lexer, table, node, DISCARDING_UNEXPECTED);
  1750.         FreeNode(node);
  1751.     }
  1752.  
  1753.     ReportWarning(lexer, table, node, MISSING_ENDTAG_FOR);
  1754.     TrimEmptyElement(lexer, table);
  1755.     lexer->istackbase = istackbase;
  1756. }
  1757.  
  1758. void ParsePre(Lexer *lexer, Node *pre, uint mode)
  1759. {
  1760.     Node *node, *parent;
  1761.  
  1762.     if (pre->tag->model & CM_EMPTY)
  1763.         return;
  1764.  
  1765.     /*
  1766.      a bit of a hack, this code is made more
  1767.      complex by the fact that ReportWarning takes
  1768.      2 nodes rather than a node and a string
  1769.     */
  1770.     if (pre->tag->model & CM_OBSOLETE)
  1771.     {
  1772.         /* create a pre node for the error message */
  1773.         node = CloneNode(lexer, pre);
  1774.         MemFree(node->element);
  1775.         node->element = wstrdup("pre");
  1776.         node->tag = tag_pre;
  1777.         ReportWarning(lexer, pre, node, OBSOLETE_ELEMENT);
  1778.  
  1779.         node->parent = node->prev = node->next = null;
  1780.         FreeNode(node);  /* only used for error message */
  1781.  
  1782.         /* and coerce listing to pre */
  1783.         MemFree(pre->element);
  1784.         pre->element = wstrdup("pre");
  1785.         pre->tag = tag_pre;
  1786.     }
  1787.  
  1788.     InlineDup(lexer, null); /* tell lexer to insert inlines if needed */
  1789.  
  1790.     while ((node = GetToken(lexer, Preformatted)) != null)
  1791.     {
  1792.         if (node->tag == pre->tag && node->type == EndTag)
  1793.         {
  1794.             FreeNode(node);
  1795.             TrimSpace(lexer, pre);
  1796.             TrimEmptyElement(lexer, pre);
  1797.             return;
  1798.         }
  1799.  
  1800.         if (node->tag == tag_html)
  1801.         {
  1802.             if (node->type == StartTag || node->type == StartEndTag)
  1803.                 ReportWarning(lexer, pre, node, DISCARDING_UNEXPECTED);
  1804.  
  1805.             FreeNode(node);
  1806.             continue;
  1807.         }
  1808.  
  1809.         if (node->type == TextNode)
  1810.         {
  1811.             /* if first check for inital newline */
  1812.             if (pre->content == null)
  1813.             {
  1814.                 if (lexer->lexbuf[node->start] == '\n')
  1815.                     ++(node->start);
  1816.  
  1817.                 if (node->start >= node->end)
  1818.                 {
  1819.                     FreeNode(node);
  1820.                     continue;
  1821.                 }
  1822.             }
  1823.  
  1824.             InsertNode(pre, node);
  1825.             continue;
  1826.         }
  1827.  
  1828.         if (node->type == CommentTag || 
  1829.             node->type == ProcInsTag ||
  1830.             node->type == AspTag)
  1831.         {
  1832.             InsertNode(pre, node);
  1833.             continue;
  1834.         }
  1835.  
  1836.         /* discard unknown  and PARAM tags */
  1837.         if (node->tag == null || node->tag == tag_param)
  1838.         {
  1839.             ReportWarning(lexer, pre, node, DISCARDING_UNEXPECTED);
  1840.             FreeNode(node);
  1841.             continue;
  1842.         }
  1843.  
  1844.         if (node->tag == tag_p && node->type == StartTag)
  1845.         {
  1846.             ReportWarning(lexer, pre, node, USING_BR_INPLACE_OF);
  1847.  
  1848.             /* trim white space before <p> in <pre>*/
  1849.             TrimSpace(lexer, pre->last);
  1850.             
  1851.             /* coerce <p> to <br> */
  1852.             node->tag = tag_br;
  1853.             MemFree(node->element);
  1854.             node->element = wstrdup("br");
  1855.             InsertNode(pre, node);
  1856.             continue;
  1857.         }
  1858.  
  1859.         if (node->tag->model & CM_HEAD && !(node->tag->model & CM_BLOCK))
  1860.         {
  1861.             MoveToHead(lexer, pre, node);
  1862.             continue;
  1863.         }
  1864.  
  1865.         /* 
  1866.           if this is the end tag for an ancestor element
  1867.           then infer end tag for this element
  1868.         */
  1869.         if (node->type == EndTag)
  1870.         {
  1871.             if (node->tag == tag_form)
  1872.             {
  1873.                 lexer->badForm = yes;
  1874.                 ReportWarning(lexer, pre, node, DISCARDING_UNEXPECTED);
  1875.                 continue;
  1876.             }
  1877.  
  1878.             for (parent = pre->parent;
  1879.                     parent != null; parent = parent->parent)
  1880.             {
  1881.                 if (node->tag == parent->tag)
  1882.                 {
  1883.                     ReportWarning(lexer, pre, node, MISSING_ENDTAG_BEFORE);
  1884.  
  1885.                     UngetToken(lexer);
  1886.                     TrimSpace(lexer, pre);
  1887.                     TrimEmptyElement(lexer, pre);
  1888.                     return;
  1889.                 }
  1890.             }
  1891.         }
  1892.  
  1893.         /* what about head content, HEAD, BODY tags etc? */
  1894.         if (!(node->tag->model & CM_INLINE))
  1895.         {
  1896.             if (node->type != StartTag)
  1897.             {
  1898.                 ReportWarning(lexer, pre, node, DISCARDING_UNEXPECTED);
  1899.                 continue;
  1900.             }
  1901.  
  1902.             ReportWarning(lexer, pre, node, MISSING_ENDTAG_BEFORE);
  1903.             lexer->excludeBlocks = yes;
  1904.  
  1905.             /* check if we need to infer a container */
  1906.             if (node->tag->model & CM_LIST)
  1907.             {
  1908.                 UngetToken(lexer);
  1909.                 node = InferredTag(lexer, "ul");
  1910.             }
  1911.             else if (node->tag->model & CM_DEFLIST)
  1912.             {
  1913.                 UngetToken(lexer);
  1914.                 node = InferredTag(lexer, "dl");
  1915.             }
  1916.             else if (node->tag->model & CM_TABLE)
  1917.             {
  1918.                 UngetToken(lexer);
  1919.                 node = InferredTag(lexer, "table");
  1920.             }
  1921.  
  1922.             InsertNodeAfterElement(pre, node);
  1923.             pre = InferredTag(lexer, "pre");
  1924.             InsertNodeAfterElement(node, pre);
  1925.             ParseTag(lexer, node, IgnoreWhitespace);
  1926.             lexer->excludeBlocks = no;
  1927.             continue;
  1928.         }
  1929. #if 0
  1930.         if (!(node->tag->model & CM_INLINE))
  1931.         {
  1932.             ReportWarning(lexer, pre, node, MISSING_ENDTAG_BEFORE);
  1933.             UngetToken(lexer);
  1934.             return;
  1935.         }
  1936. #endif
  1937.         if (node->type == StartTag || node->type == StartEndTag)
  1938.         {
  1939.             /* trim white space before <br> */
  1940.             if (node->tag == tag_br)
  1941.                 TrimSpace(lexer, pre->last);
  1942.             
  1943.             InsertNode(pre, node);
  1944.             ParseTag(lexer, node, Preformatted);
  1945.             continue;
  1946.         }
  1947.  
  1948.         /* discard unexpected tags */
  1949.         ReportWarning(lexer, pre, node, DISCARDING_UNEXPECTED);
  1950.         FreeNode(node);
  1951.     }
  1952.  
  1953.     ReportWarning(lexer, pre, node, MISSING_ENDTAG_FOR);
  1954.     TrimEmptyElement(lexer, pre);
  1955. }
  1956.  
  1957. void ParseOptGroup(Lexer *lexer, Node *field, uint mode)
  1958. {
  1959.     Node *node;
  1960.  
  1961.     lexer->insert = null;  /* defer implicit inline start tags */
  1962.  
  1963.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  1964.     {
  1965.         if (node->tag == field->tag && node->type == EndTag)
  1966.         {
  1967.             FreeNode(node);
  1968.             TrimSpace(lexer, field->last);
  1969.             return;
  1970.         }
  1971.  
  1972.         if (node->type == CommentTag ||
  1973.             node->type == ProcInsTag ||
  1974.             node->type == AspTag)
  1975.         {
  1976.             InsertNode(field, node);
  1977.             continue;
  1978.         }
  1979.  
  1980.         if (node->type == StartTag && 
  1981.              (node->tag == tag_option || node->tag == tag_optgroup))
  1982.         {
  1983.             if (node->tag == tag_optgroup)
  1984.                 ReportWarning(lexer, field, node, CANT_BE_NESTED);
  1985.  
  1986.             InsertNode(field, node);
  1987.             ParseTag(lexer, node, MixedContent);
  1988.             continue;
  1989.         }
  1990.  
  1991.         /* discard unexpected tags */
  1992.         ReportWarning(lexer, field, node, DISCARDING_UNEXPECTED);
  1993.         FreeNode(node);
  1994.     }
  1995. }
  1996.  
  1997.  
  1998. void ParseSelect(Lexer *lexer, Node *field, uint mode)
  1999. {
  2000.     Node *node;
  2001.  
  2002.     lexer->insert = null;  /* defer implicit inline start tags */
  2003.  
  2004.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  2005.     {
  2006.         if (node->tag == field->tag && node->type == EndTag)
  2007.         {
  2008.             FreeNode(node);
  2009.             TrimSpace(lexer, field->last);
  2010.             return;
  2011.         }
  2012.  
  2013.         if (node->type == CommentTag ||
  2014.             node->type == ProcInsTag ||
  2015.             node->type == AspTag)
  2016.         {
  2017.             InsertNode(field, node);
  2018.             continue;
  2019.         }
  2020.  
  2021.         if (node->type == StartTag && 
  2022.              (node->tag == tag_option || node->tag == tag_optgroup))
  2023.         {
  2024.             InsertNode(field, node);
  2025.             ParseTag(lexer, node, IgnoreWhitespace);
  2026.             continue;
  2027.         }
  2028.  
  2029.         /* discard unexpected tags */
  2030.         ReportWarning(lexer, field, node, DISCARDING_UNEXPECTED);
  2031.         FreeNode(node);
  2032.     }
  2033.  
  2034.     ReportWarning(lexer, field, node, MISSING_ENDTAG_FOR);
  2035. }
  2036.  
  2037. void ParseText(Lexer *lexer, Node *field, uint mode)
  2038. {
  2039.     Node *node;
  2040.  
  2041.     lexer->insert = null;  /* defer implicit inline start tags */
  2042.  
  2043.     while ((node = GetToken(lexer, Preformatted)) != null)
  2044.     {
  2045.         if (node->tag == field->tag && node->type == EndTag)
  2046.         {
  2047.             FreeNode(node);
  2048.             TrimSpace(lexer, field->last);
  2049.             return;
  2050.         }
  2051.  
  2052.         if (node->type == TextNode || 
  2053.             node->type == CommentTag ||
  2054.             node->type == ProcInsTag ||
  2055.             node->type == AspTag)
  2056.         {
  2057.             InsertNode(field, node);
  2058.             continue;
  2059.         }
  2060.  
  2061.         if (node->tag == tag_font)
  2062.         {
  2063.             ReportWarning(lexer, field, node, DISCARDING_UNEXPECTED);
  2064.             FreeNode(node);
  2065.             continue;
  2066.         }
  2067.  
  2068.         /* terminate element on other tags */
  2069.         if (!(field->tag->model & CM_OPT))
  2070.                 ReportWarning(lexer, field, node, MISSING_ENDTAG_BEFORE);
  2071.  
  2072.         UngetToken(lexer);
  2073.         TrimSpace(lexer, field->last);
  2074.         return;
  2075.     }
  2076.  
  2077.     if (!(field->tag->model & CM_OPT))
  2078.         ReportWarning(lexer, field, node, MISSING_ENDTAG_FOR);
  2079. }
  2080.  
  2081.  
  2082. void ParseTitle(Lexer *lexer, Node *title, uint mode)
  2083. {
  2084.     Node *node;
  2085.  
  2086.     while ((node = GetToken(lexer, MixedContent)) != null)
  2087.     {
  2088.         if (node->tag == title->tag && node->type == EndTag)
  2089.         {
  2090.             FreeNode(node);
  2091.             TrimSpace(lexer, title->last);
  2092.             return;
  2093.         }
  2094.  
  2095.         if (node->type == TextNode)
  2096.         {
  2097.             /* only called for 1st child */
  2098.             if (title->content == null)
  2099.                 TrimInitialSpace(lexer, title, node);
  2100.  
  2101.             if (node->start >= node->end)
  2102.             {
  2103.                 FreeNode(node);
  2104.                 continue;
  2105.             }
  2106.  
  2107.             InsertNode(title, node);
  2108.             continue;
  2109.         }
  2110.  
  2111.         if (node->type == CommentTag || 
  2112.             node->type == ProcInsTag ||
  2113.             node->type == AspTag)
  2114.         {
  2115.             InsertNode(title, node);
  2116.             continue;
  2117.         }
  2118.  
  2119.         /* discard unknown tags */
  2120.         if (node->tag == null)
  2121.         {
  2122.             ReportWarning(lexer, title, node, DISCARDING_UNEXPECTED);
  2123.             FreeNode(node);
  2124.             continue;
  2125.         }
  2126.  
  2127.         /* pushback unexpected tokens */
  2128.         ReportWarning(lexer, title, node, MISSING_ENDTAG_BEFORE);
  2129.         UngetToken(lexer);
  2130.         TrimSpace(lexer, title->last);
  2131.         return;
  2132.     }
  2133.  
  2134.     ReportWarning(lexer, title, node, MISSING_ENDTAG_FOR);
  2135. }
  2136.  
  2137. /*
  2138.   This isn't quite right for CDATA content as it recognises
  2139.   tags within the content and parses them accordingly.
  2140.   This will unfortunately screw up scripts which include
  2141.   < + letter,  < + !, < + ?  or  < + / + letter
  2142. */
  2143.  
  2144. void ParseScript(Lexer *lexer, Node *script, uint mode)
  2145. {
  2146.     Node *node;
  2147.  
  2148.     node = GetCDATA(lexer, script);
  2149.  
  2150.     if (node)
  2151.         InsertNode(script, node);
  2152. }
  2153.  
  2154. Bool IsJavaScript(Node *node)
  2155. {
  2156.     Bool result = no;
  2157.     AttVal *attr;
  2158.  
  2159.     if (node->attributes == null)
  2160.         return yes;
  2161.  
  2162.     for (attr = node->attributes; attr; attr = attr->next)
  2163.     {
  2164.         if ( (wstrcasecmp(attr->attribute, "language") == 0
  2165.                 || wstrcasecmp(attr->attribute, "type") == 0)
  2166.                 && wsubstr(attr->value, "javascript"))
  2167.             result = yes;
  2168.     }
  2169.  
  2170.     return result;
  2171. }
  2172.  
  2173. void ParseHead(Lexer *lexer, Node *head, uint mode)
  2174. {
  2175.     Node *node;
  2176.     Bool HasTitle = no;
  2177.  
  2178.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  2179.     {
  2180.         if (node->tag == head->tag && node->type == EndTag)
  2181.         {
  2182.             FreeNode(node);
  2183.             break;
  2184.         }
  2185.  
  2186.         if (node->type == TextNode)
  2187.         {
  2188.             UngetToken(lexer);
  2189.             break;
  2190.         }
  2191.  
  2192.         if (node->type == CommentTag ||
  2193.             node->type == ProcInsTag ||
  2194.             node->type == AspTag)
  2195.         {
  2196.             InsertNode(head, node);
  2197.             continue;
  2198.         }
  2199.  
  2200.         if (node->type == DocTypeTag)
  2201.         {
  2202.             InsertDocType(lexer, head, node);
  2203.             continue;
  2204.         }
  2205.  
  2206.         /* discard unknown tags */
  2207.         if (node->tag == null)
  2208.         {
  2209.             ReportWarning(lexer, head, node, DISCARDING_UNEXPECTED);
  2210.             FreeNode(node);
  2211.             continue;
  2212.         }
  2213.         
  2214.         if (!(node->tag->model & CM_HEAD))
  2215.         {
  2216.             UngetToken(lexer);
  2217.             break;
  2218.         }
  2219.  
  2220.         if (node->type == StartTag || node->type == StartEndTag)
  2221.         {
  2222.             if (node->tag == tag_title)
  2223.                 HasTitle = yes;
  2224.             else if (node->tag == tag_noscript)
  2225.                 ReportWarning(lexer, head, node, TAG_NOT_ALLOWED_IN);
  2226.  
  2227.             InsertNode(head, node);
  2228.             ParseTag(lexer, node, IgnoreWhitespace);
  2229.             continue;
  2230.         }
  2231.  
  2232.         /* discard unexpected text nodes and end tags */
  2233.         ReportWarning(lexer, head, node, DISCARDING_UNEXPECTED);
  2234.         FreeNode(node);
  2235.     }
  2236.   
  2237.     if (!HasTitle)
  2238.     {
  2239.         ReportWarning(lexer, head, null, MISSING_TITLE_ELEMENT);
  2240.         InsertNode(head, InferredTag(lexer, "title"));
  2241.     }
  2242. }
  2243.  
  2244. void ParseBody(Lexer *lexer, Node *body, uint mode)
  2245. {
  2246.     Node *node;
  2247.     Bool checkstack, iswhitenode;
  2248.  
  2249.     mode = IgnoreWhitespace;
  2250.     checkstack = yes;
  2251.  
  2252.     while ((node = GetToken(lexer, mode)) != null)
  2253.     {
  2254.         if (node->tag == body->tag && node->type == EndTag)
  2255.         {
  2256.             TrimSpace(lexer, body->last);
  2257.             FreeNode(node);
  2258.             SeenBodyEndTag = 1;
  2259.             mode = IgnoreWhitespace;
  2260.  
  2261.             if (body->parent->tag == tag_noframes)
  2262.                 break;
  2263.  
  2264.             continue;
  2265.         }
  2266.         
  2267.         if (node->tag == tag_html)
  2268.         {
  2269.             if (node->type == StartTag || node->type == StartEndTag)
  2270.                 ReportWarning(lexer, body, node, DISCARDING_UNEXPECTED);
  2271.  
  2272.             FreeNode(node);
  2273.             continue;
  2274.         }
  2275.  
  2276.         iswhitenode = no;
  2277.  
  2278.         if (node->type == TextNode &&
  2279.                node->end <= node->start + 1 &&
  2280.                lexer->lexbuf[node->start] == ' ')
  2281.             iswhitenode = yes;
  2282.  
  2283.         if (node->type == CommentTag ||
  2284.             node->type == ProcInsTag ||
  2285.             node->type == AspTag)
  2286.         {
  2287.             InsertNode(body, node);
  2288.             continue;
  2289.         }
  2290.  
  2291.         if (SeenBodyEndTag == 1 && !iswhitenode)
  2292.         {
  2293.             ++SeenBodyEndTag;
  2294.             ReportWarning(lexer, body, node, CONTENT_AFTER_BODY);
  2295.         }
  2296.  
  2297.         /* mixed content model permits text */
  2298.         if (node->type == TextNode)
  2299.         {
  2300.             if (iswhitenode && mode == IgnoreWhitespace)
  2301.             {
  2302.                 FreeNode(node);
  2303.                 continue;
  2304.             }
  2305.  
  2306.             if (checkstack)
  2307.             {
  2308.                 checkstack = no;
  2309.  
  2310.                 if (InlineDup(lexer, node) > 0)
  2311.                     continue;
  2312.             }
  2313.  
  2314.             InsertNode(body, node);
  2315.             mode = MixedContent;
  2316.             continue;
  2317.         }
  2318.  
  2319.         if (node->type == DocTypeTag)
  2320.         {
  2321.             InsertDocType(lexer, body, node);
  2322.             continue;
  2323.         }
  2324.         /* discard unknown  and PARAM tags */
  2325.         if (node->tag == null || node->tag == tag_param)
  2326.         {
  2327.             ReportWarning(lexer, body, node, DISCARDING_UNEXPECTED);
  2328.             FreeNode(node);
  2329.             continue;
  2330.         }
  2331.  
  2332.         /*
  2333.           Netscape allows LI and DD directly in BODY
  2334.           We infer UL or DL respectively and use this
  2335.           Bool to exclude block-level elements so as
  2336.           to match Netscape's observed behaviour.
  2337.         */
  2338.         lexer->excludeBlocks = no;
  2339.         
  2340.         if (!(node->tag->model & CM_BLOCK) &&
  2341.             !(node->tag->model & CM_INLINE))
  2342.         {
  2343.             /* avoid this error message being issued twice */
  2344.             if (!node->tag->model & CM_HEAD)
  2345.                 ReportWarning(lexer, body, node, TAG_NOT_ALLOWED_IN);
  2346.  
  2347.             if (node->tag->model & CM_HTML)
  2348.             {
  2349.                 /* copy body attributes if current body was inferred */
  2350.                 if (node->tag == tag_body && body->implicit 
  2351.                                     && body->attributes == null)
  2352.                 {
  2353.                     body->attributes = node->attributes;
  2354.                     node->attributes = null;
  2355.                 }
  2356.  
  2357.                 FreeNode(node);
  2358.                 continue;
  2359.             }
  2360.  
  2361.             if (node->tag->model & CM_HEAD)
  2362.             {
  2363.                 MoveToHead(lexer, body, node);
  2364.                 continue;
  2365.             }
  2366.  
  2367.             if (node->tag->model & CM_LIST)
  2368.             {
  2369.                 UngetToken(lexer);
  2370.                 node = InferredTag(lexer, "ul");
  2371.                 lexer->excludeBlocks = yes;
  2372.             }
  2373.             else if (node->tag->model & CM_DEFLIST)
  2374.             {
  2375.                 UngetToken(lexer);
  2376.                 node = InferredTag(lexer, "dl");
  2377.                 lexer->excludeBlocks = yes;
  2378.             }
  2379.             else if (node->tag->model & (CM_TABLE | CM_ROWGRP | CM_ROW))
  2380.             {
  2381.                 UngetToken(lexer);
  2382.                 node = InferredTag(lexer, "table");
  2383.                 lexer->excludeBlocks = yes;
  2384.             }
  2385.             else
  2386.             {
  2387.                 if (!node->tag->model & (CM_ROW | CM_FIELD))
  2388.                 {
  2389.                     UngetToken(lexer);
  2390.                     return;
  2391.                 }
  2392.  
  2393.                 /* ignore </td> </th> <option> etc. */
  2394.                 continue;
  2395.             }
  2396.         }
  2397.  
  2398.         if (node->type == StartTag || node->type == StartEndTag)
  2399.         {
  2400.             if ((node->tag->model & CM_INLINE) && !(node->tag->model & CM_MIXED))
  2401.             {
  2402.                 if (checkstack && !node->implicit)
  2403.                 {
  2404.                     checkstack = no;
  2405.  
  2406.                     if (InlineDup(lexer, node) > 0)
  2407.                         continue;
  2408.                 }
  2409.  
  2410.                 mode = MixedContent;
  2411.             }
  2412.             else
  2413.             {
  2414.                 checkstack = yes;
  2415.                 mode = IgnoreWhitespace;
  2416.             }
  2417.  
  2418.             if (node->implicit)
  2419.                 ReportWarning(lexer, body, node, INSERTING_TAG);
  2420.  
  2421.             InsertNode(body, node);
  2422.             ParseTag(lexer, node, mode /* IgnoreWhitespace /*MixedContent*/);
  2423.             continue;
  2424.         }
  2425.         else if (node->type == EndTag)
  2426.             PopInline(lexer, node);  /* if inline end tag */
  2427.  
  2428.         /* discard unexpected end tags */
  2429.         ReportWarning(lexer, body, node, DISCARDING_UNEXPECTED);
  2430.         FreeNode(node);
  2431.     }
  2432. }
  2433.  
  2434. void ParseNoFrames(Lexer *lexer, Node *noframes, uint mode)
  2435. {
  2436.     Node *node;
  2437.     Bool checkstack;
  2438.  
  2439.     lexer->badAccess |=  USING_NOFRAMES;
  2440.     mode = IgnoreWhitespace;
  2441.     checkstack = yes;
  2442.  
  2443.     while ((node = GetToken(lexer, mode)) != null)
  2444.     {
  2445.         if (node->tag == noframes->tag && node->type == EndTag)
  2446.         {
  2447.             FreeNode(node);
  2448.             TrimSpace(lexer, noframes->last);
  2449.             return;
  2450.         }
  2451.  
  2452.         if (node->tag == tag_html)
  2453.         {
  2454.             if (node->type == StartTag || node->type == StartEndTag)
  2455.                 ReportWarning(lexer, noframes, node, DISCARDING_UNEXPECTED);
  2456.  
  2457.             FreeNode(node);
  2458.             continue;
  2459.         }
  2460.  
  2461.         if (node->type == CommentTag ||
  2462.             node->type == ProcInsTag ||
  2463.             node->type == AspTag)
  2464.         {
  2465.             InsertNode(noframes, node);
  2466.             continue;
  2467.         }
  2468.  
  2469.         if (node->tag == tag_body && node->type == StartTag)
  2470.         {
  2471.             InsertNode(noframes, node);
  2472.             ParseTag(lexer, node, IgnoreWhitespace /*MixedContent*/);
  2473.             continue;
  2474.         }
  2475.  
  2476.         if (node->type == StartTag || node->type == StartEndTag || node->type == TextNode)
  2477.         {
  2478.             UngetToken(lexer);
  2479.             node = InferredTag(lexer, "body");
  2480.             ReportWarning(lexer, noframes, node, INSERTING_TAG);
  2481.             InsertNode(noframes, node);
  2482.             ParseTag(lexer, node, IgnoreWhitespace /*MixedContent*/);
  2483.             continue;
  2484.         }
  2485. #if 0
  2486.         /* mixed content model permits text */
  2487.         if (node->type == TextNode)
  2488.         {
  2489.             if (checkstack)
  2490.             {
  2491.                 checkstack = no;
  2492.  
  2493.                 if (InlineDup(lexer, node) > 0)
  2494.                     continue;
  2495.             }
  2496.  
  2497.             InsertNode(noframes, node);
  2498.             mode = MixedContent;
  2499.             continue;
  2500.         }
  2501.  
  2502.         /* discard unknown  and PARAM tags */
  2503.         if (node->tag == null || node->tag == tag_param)
  2504.         {
  2505.             ReportWarning(lexer, noframes, node, DISCARDING_UNEXPECTED);
  2506.             FreeNode(node);
  2507.             continue;
  2508.         }
  2509.  
  2510.         /*
  2511.           Treat LI and DD etc. in same way as in BODY
  2512.         */
  2513.         lexer->excludeBlocks = no;
  2514.         
  2515.         if (!(node->tag->model & CM_BLOCK) &&
  2516.             !(node->tag->model & CM_INLINE))
  2517.         {
  2518.             ReportWarning(lexer, noframes, node, TAG_NOT_ALLOWED_IN);
  2519.  
  2520.             if (node->tag->model & CM_HTML)
  2521.             {
  2522.                 FreeNode(node);
  2523.                 continue;
  2524.             }
  2525.  
  2526.             if (node->tag->model & CM_HEAD)
  2527.             {
  2528.                 MoveToHead(lexer, noframes, node);
  2529.                 continue;
  2530.             }
  2531.  
  2532.             UngetToken(lexer);
  2533.  
  2534.             if (node->tag->model & CM_LIST)
  2535.             {
  2536.                 node = InferredTag(lexer, "ul");
  2537.                 lexer->excludeBlocks = yes;
  2538.             }
  2539.             else if (node->tag->model & CM_DEFLIST)
  2540.             {
  2541.                 node = InferredTag(lexer, "dl");
  2542.                 lexer->excludeBlocks = yes;
  2543.             }
  2544.             else if (node->tag->model & CM_TABLE)
  2545.             {
  2546.                 node = InferredTag(lexer, "table");
  2547.                 lexer->excludeBlocks = yes;
  2548.             }
  2549.             else
  2550.                 return;
  2551.         }
  2552.  
  2553.         if (node->type == StartTag || node->type == StartEndTag)
  2554.         {
  2555.             if (node->tag->model & CM_INLINE)
  2556.             {
  2557.                 if (checkstack && !node->implicit)
  2558.                 {
  2559.                     checkstack = no;
  2560.  
  2561.                     if (InlineDup(lexer, node) > 0)
  2562.                         continue;
  2563.                 }
  2564.  
  2565.                 mode = MixedContent;
  2566.             }
  2567.             else
  2568.             {
  2569.                 checkstack = yes;
  2570.                 mode = IgnoreWhitespace;
  2571.             }
  2572.  
  2573.             if (node->implicit)
  2574.                 ReportWarning(lexer, noframes, node, INSERTING_TAG);
  2575.  
  2576.             InsertNode(noframes, node);
  2577.             ParseTag(lexer, node, IgnoreWhitespace /*MixedContent*/);
  2578.             continue;
  2579.         }
  2580.         else if (node->type == EndTag)
  2581.             PopInline(lexer, node);  /* if inline end tag */
  2582. #endif
  2583.         /* discard unexpected end tags */
  2584.         ReportWarning(lexer, noframes, node, DISCARDING_UNEXPECTED);
  2585.         FreeNode(node);
  2586.     }
  2587.  
  2588.     ReportWarning(lexer, noframes, node, MISSING_ENDTAG_FOR);
  2589. }
  2590.  
  2591. void ParseFrameSet(Lexer *lexer, Node *frameset, uint mode)
  2592. {
  2593.     Node *node;
  2594.  
  2595.     lexer->badAccess |=  USING_FRAMES;
  2596.  
  2597.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  2598.     {
  2599.         if (node->tag == frameset->tag && node->type == EndTag)
  2600.         {
  2601.             FreeNode(node);
  2602.             TrimSpace(lexer, frameset->last);
  2603.             return;
  2604.         }
  2605.  
  2606.         if (node->type == CommentTag ||
  2607.             node->type == ProcInsTag ||
  2608.             node->type == AspTag)
  2609.         {
  2610.             InsertNode(frameset, node);
  2611.             continue;
  2612.         }
  2613.  
  2614.         if (node->tag == null)
  2615.         {
  2616.             ReportWarning(lexer, frameset, node, DISCARDING_UNEXPECTED);
  2617.             FreeNode(node);
  2618.             continue; 
  2619.         }
  2620.  
  2621.         if (node->tag == tag_body)
  2622.         {
  2623.             UngetToken(lexer);
  2624.             node = InferredTag(lexer, "noframes");
  2625.             ReportWarning(lexer, frameset, node, INSERTING_TAG);
  2626.         }
  2627.  
  2628.         if (node->type == StartTag && node->tag->model & CM_FRAMES)
  2629.         {
  2630.             InsertNode(frameset, node);
  2631.             lexer->excludeBlocks = no;
  2632.             ParseTag(lexer, node, MixedContent);
  2633.             continue;
  2634.         }
  2635.  
  2636.  
  2637.         /* discard unexpected tags */
  2638.         ReportWarning(lexer, frameset, node, DISCARDING_UNEXPECTED);
  2639.         FreeNode(node);
  2640.     }
  2641.  
  2642.     ReportWarning(lexer, frameset, node, MISSING_ENDTAG_FOR);
  2643. }
  2644.  
  2645. void ParseHTML(Lexer *lexer, Node *html, uint mode)
  2646. {
  2647.     Node *node, *head;
  2648.     Node *frameset = null;
  2649.     Node *noframes = null;
  2650.  
  2651.     XmlTags = no;
  2652.     SeenBodyEndTag = 0;
  2653.  
  2654.     for (;;)
  2655.     {
  2656.         node = GetToken(lexer, IgnoreWhitespace);
  2657.  
  2658.         if (node == null)
  2659.         {
  2660.             node = InferredTag(lexer, "head");
  2661.             break;
  2662.         }
  2663.  
  2664.         if (node->tag == tag_head)
  2665.             break;
  2666.  
  2667.         if (node->tag == html->tag && node->type == EndTag)
  2668.         {
  2669.             ReportWarning(lexer, html, node, DISCARDING_UNEXPECTED);
  2670.             FreeNode(node);
  2671.             continue;
  2672.         }
  2673.  
  2674.         if (node->type == CommentTag ||
  2675.             node->type == ProcInsTag ||
  2676.             node->type == AspTag)
  2677.         {
  2678.             InsertNode(html, node);
  2679.             continue;
  2680.         }
  2681.  
  2682.         UngetToken(lexer);
  2683.         node = InferredTag(lexer, "head");
  2684.         break;
  2685.     }
  2686.  
  2687.     head = node;
  2688.     InsertNode(html, head);
  2689.     ParseHead(lexer, head, mode);
  2690.  
  2691.     for (;;)
  2692.     {
  2693.         node = GetToken(lexer, IgnoreWhitespace);
  2694.  
  2695.         if (node == null)
  2696.         {
  2697.             if (frameset == null) /* create an empty body */
  2698.                 node = InferredTag(lexer, "body");
  2699.  
  2700.             return;
  2701.         }
  2702.  
  2703.         /* robustly handle html tags */
  2704.         if (node->tag == html->tag)
  2705.         {
  2706.             if (node->type != StartTag && frameset == null)
  2707.                 ReportWarning(lexer, html, node, DISCARDING_UNEXPECTED);
  2708.  
  2709.             FreeNode(node);
  2710.             continue;
  2711.         }
  2712.  
  2713.         if (node->type == CommentTag ||
  2714.             node->type == ProcInsTag ||
  2715.             node->type == AspTag)
  2716.         {
  2717.             InsertNode(html, node);
  2718.             continue;
  2719.         }
  2720.  
  2721.         /* if frameset document coerce <body> to <noframes> */
  2722.         if (node->tag == tag_body)
  2723.         {
  2724.             if (node->type != StartTag)
  2725.             {
  2726.                 ReportWarning(lexer, html, node, DISCARDING_UNEXPECTED);
  2727.                 FreeNode(node);
  2728.                 continue;
  2729.             }
  2730.  
  2731.             if (frameset != null)
  2732.             {
  2733.                 UngetToken(lexer);
  2734.  
  2735.                 if (noframes == null)
  2736.                 {
  2737.                     noframes = InferredTag(lexer, "noframes");
  2738.                     InsertNode(frameset, noframes);
  2739.                     ReportWarning(lexer, html, noframes, INSERTING_TAG);
  2740.                 }
  2741.  
  2742.                 ParseTag(lexer, noframes, mode);
  2743.                 continue;
  2744.             }
  2745.  
  2746.             break;  /* to parse body */
  2747.         }
  2748.  
  2749.         /* flag an error if we see more than one frameset */
  2750.         if (node->tag == tag_frameset)
  2751.         {
  2752.             if (node->type != StartTag)
  2753.             {
  2754.                 ReportWarning(lexer, html, node, DISCARDING_UNEXPECTED);
  2755.                 FreeNode(node);
  2756.                 continue;
  2757.             }
  2758.  
  2759.             if (frameset != null)
  2760.                 ReportError(lexer, html, node, DUPLICATE_FRAMESET);
  2761.             else
  2762.                 frameset = node;
  2763.  
  2764.             InsertNode(html, node);
  2765.             ParseTag(lexer, node, mode);
  2766.  
  2767.             /*
  2768.               see if it includes a noframes element so
  2769.               that we can merge subsequent noframes elements
  2770.             */
  2771.  
  2772.             for (node = frameset->content; node; node = node->next)
  2773.             {
  2774.                 if (node->tag == tag_noframes)
  2775.                     noframes = node;
  2776.             }
  2777.             continue;
  2778.         }
  2779.  
  2780.         /* if not a frameset document coerce <noframes> to <body> */
  2781.         if (node->tag == tag_noframes)
  2782.         {
  2783.             if (node->type != StartTag)
  2784.             {
  2785.                 ReportWarning(lexer, html, node, DISCARDING_UNEXPECTED);
  2786.                 FreeNode(node);
  2787.                 continue;
  2788.             }
  2789.  
  2790.             if (frameset == null)
  2791.             {
  2792.                 ReportWarning(lexer, html, node, DISCARDING_UNEXPECTED);
  2793.                 FreeNode(node);
  2794.                 node = InferredTag(lexer, "body");
  2795.                 break;
  2796.             }
  2797.  
  2798.             if (noframes == null)
  2799.             {
  2800.                 noframes = node;
  2801.                 InsertNode(frameset, noframes);
  2802.             }
  2803.             else
  2804.                 FreeNode(node);
  2805.  
  2806.             ParseTag(lexer, noframes, mode);
  2807.             continue;
  2808.         }
  2809.  
  2810.         UngetToken(lexer);
  2811.  
  2812.         /* insert other content into noframes element */
  2813.  
  2814.         if (frameset)
  2815.         {
  2816.             if (noframes == null)
  2817.             {
  2818.                 noframes = InferredTag(lexer, "noframes");
  2819.                 InsertNode(frameset, noframes);
  2820.             }
  2821.             else
  2822.                 ReportWarning(lexer, html, node, NOFRAMES_CONTENT);
  2823.  
  2824.             ParseTag(lexer, noframes, mode);
  2825.             continue;
  2826.         }
  2827.  
  2828.         node = InferredTag(lexer, "body");
  2829.         break;
  2830.     }
  2831.  
  2832.     /* node must be body */
  2833.  
  2834.     InsertNode(html, node);
  2835.     ParseTag(lexer, node, mode);
  2836. }
  2837.  
  2838. /*
  2839.   HTML is the top level element
  2840. */
  2841. Node *ParseDocument(Lexer *lexer)
  2842. {
  2843.     Node *node, *document, *html;
  2844.  
  2845.     document = NewNode();
  2846.     document->type = RootNode;
  2847.  
  2848.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  2849.     {
  2850.         if (node->type == CommentTag || 
  2851.             node->type == ProcInsTag ||
  2852.             node->type == AspTag ||
  2853.             node->type == DocTypeTag)
  2854.         {
  2855.             InsertNode(document, node);
  2856.             continue;
  2857.         }
  2858.  
  2859.         if (node->type == EndTag)
  2860.         {
  2861.             ReportWarning(lexer, RootNode, node, DISCARDING_UNEXPECTED);
  2862.             FreeNode(node);
  2863.             continue;
  2864.         }
  2865.  
  2866.         if (node->type != StartTag || node->tag != tag_html)
  2867.         {
  2868.             UngetToken(lexer);
  2869.             html = InferredTag(lexer, "html");
  2870.         }
  2871.         else
  2872.             html = node;
  2873.  
  2874.         InsertNode(document, html);
  2875.         ParseHTML(lexer, html, no);
  2876.         break;
  2877.     }
  2878.  
  2879.     return document;
  2880. }
  2881.  
  2882. Bool XMLPreserveWhiteSpace(Node *element)
  2883. {
  2884.     AttVal *attribute;
  2885.  
  2886.     /* search attributes for xml:space */
  2887.     for (attribute = element->attributes; attribute; attribute = attribute->next)
  2888.     {
  2889.         if (wstrcmp(attribute->attribute, "xml:space") == 0)
  2890.         {
  2891.             if (wstrcmp(attribute->value, "preserve") == 0)
  2892.                 return yes;
  2893.  
  2894.             return no;
  2895.         }
  2896.     }
  2897.  
  2898.     /* kludge for html docs without explicit xml:space attribute */
  2899.     if (wstrcasecmp(element->element, "pre") == 0
  2900.         || wstrcasecmp(element->element, "script") == 0
  2901.         || wstrcasecmp(element->element, "style") == 0)
  2902.         return yes;
  2903.  
  2904.     /* kludge for XSL docs */
  2905.     if (wstrcasecmp(element->element, "xsl:text") == 0)
  2906.         return yes;
  2907.  
  2908.     return no;
  2909. }
  2910.  
  2911. /*
  2912.   XML documents
  2913. */
  2914. void ParseXMLElement(Lexer *lexer, Node *element, uint mode)
  2915. {
  2916.     Node *node;
  2917.  
  2918.     /* Jeff Young's kludge for XSL docs */
  2919.  
  2920.     if (wstrcasecmp(element->element, "xsl:text") == 0)
  2921.         return;
  2922.  
  2923.     /* if node is pre or has xml:space="preserve" then do so */
  2924.  
  2925.     if (XMLPreserveWhiteSpace(element))
  2926.         mode = Preformatted;
  2927.  
  2928.     while ((node = GetToken(lexer, mode)) != null)
  2929.     {
  2930.         if (node->type == EndTag && wstrcmp(node->element, element->element) == 0)
  2931.         {
  2932.             FreeNode(node);
  2933.             break;
  2934.         }
  2935.  
  2936.         /* discard unexpected end tags */
  2937.         if (node->type == EndTag)
  2938.         {
  2939.             ReportWarning(lexer, element, node, UNEXPECTED_ENDTAG);
  2940.             FreeNode(node);
  2941.             continue;
  2942.         }
  2943.  
  2944.         /* parse content on seeing start tag */
  2945.         if (node->type == StartTag)
  2946.             ParseXMLElement(lexer, node, mode);
  2947.  
  2948.         InsertNode(element, node);
  2949.     }
  2950.  
  2951.     /*
  2952.      if first child is text then trim initial space and
  2953.      delete text node if it is empty.
  2954.     */
  2955.  
  2956.     node = element->content;
  2957.  
  2958.     if (node && node->type == TextNode && mode != Preformatted)
  2959.     {
  2960.         if (lexer->lexbuf[node->start] == ' ')
  2961.         {
  2962.             node->start++;
  2963.  
  2964.             if (node->start >= node->end)
  2965.                 DiscardElement(lexer, node);
  2966.         }
  2967.     }
  2968.  
  2969.     /*
  2970.      if last child is text then trim final space and
  2971.      delete the text node if it is empty
  2972.     */
  2973.  
  2974.     node = element->last;
  2975.  
  2976.     if (node && node->type == TextNode && mode != Preformatted)
  2977.     {
  2978.         if (lexer->lexbuf[node->end - 1] == ' ')
  2979.         {
  2980.             node->end--;
  2981.  
  2982.             if (node->start >= node->end)
  2983.                 DiscardElement(lexer, node);
  2984.         }
  2985.     }
  2986. }
  2987.  
  2988. Node *ParseXMLDocument(Lexer *lexer)
  2989. {
  2990.     Node *node, *document, *last;
  2991.  
  2992.     document = NewNode();
  2993.     document->type = RootNode;
  2994.     last = null;
  2995.     XmlTags = yes;
  2996.  
  2997.     while ((node = GetToken(lexer, IgnoreWhitespace)) != null)
  2998.     {
  2999.         /* discard unexpected end tags */
  3000.         if (node->type == EndTag)
  3001.         {
  3002.             ReportWarning(lexer, null, node, UNEXPECTED_ENDTAG);
  3003.             FreeNode(node);
  3004.             continue;
  3005.         }
  3006.  
  3007.         /* if start tag then parse element's content */
  3008.         if (node->type == StartTag)
  3009.             ParseXMLElement(lexer, node, IgnoreWhitespace);
  3010.  
  3011.         if (last != null)
  3012.             last->next = node;
  3013.         else
  3014.             document->content = node;
  3015.  
  3016.         last = node;
  3017.     }
  3018.  
  3019.     if (doctype_mode == doctype_omit)
  3020.         DiscardDocType(document);
  3021.  
  3022.     /* ensure presence of initial <?XML version="1.0"?> */
  3023.     if (XmlPi)
  3024.         FixXMLPI(lexer, document);
  3025.  
  3026.     return document;
  3027. }
  3028.  
  3029.