hello world to
hello world Trims initial space, by moving it before the start tag, or if this element is the first in parent's content, then by discarding the space */ static void TrimInitialSpace( TidyDocImpl* doc, Node *element, Node *text ) { Lexer* lexer = doc->lexer; Node *prev, *node; if ( TY_(nodeIsText)(text) && lexer->lexbuf[text->start] == ' ' && text->start < text->end ) { if ( (element->tag->model & CM_INLINE) && !(element->tag->model & CM_FIELD) ) { prev = element->prev; if (TY_(nodeIsText)(prev)) { if (prev->end == 0 || lexer->lexbuf[prev->end - 1] != ' ') lexer->lexbuf[(prev->end)++] = ' '; ++(element->start); } else /* create new node */ { node = TY_(NewNode)(lexer->allocator, lexer); node->start = (element->start)++; node->end = element->start; lexer->lexbuf[node->start] = ' '; TY_(InsertNodeBeforeElement)(element ,node); #if !defined(NDEBUG) && defined(_MSC_VER) SPRTF("TrimInitialSpace: Created text node, inserted before <%s>\n", (element->element ? element->element : "unknown")); #endif } } /* discard the space in current node */ ++(text->start); } } static Bool IsPreDescendant(Node* node) { Node *parent = node->parent; while (parent) { if (parent->tag && parent->tag->parser == TY_(ParsePre)) return yes; parent = parent->parent; } return no; } static Bool CleanTrailingWhitespace(TidyDocImpl* doc, Node* node) { Node* next; if (!TY_(nodeIsText)(node)) return no; if (node->parent->type == DocTypeTag) return no; if (IsPreDescendant(node)) return no; if (node->parent->tag && node->parent->tag->parser == TY_(ParseScript)) return no; next = node->next; /*
...
*/ if (!next && !TY_(nodeHasCM)(node->parent, CM_INLINE)) return yes; /*...
...
*/ if (next->type == StartTag) return yes; /* ...*/ if (next->type == StartEndTag) return yes; /* evil adjacent text nodes, Tidy should not generate these :-( */ if (TY_(nodeIsText)(next) && next->start < next->end && TY_(IsWhite)(doc->lexer->lexbuf[next->start])) return yes; return no; } static Bool CleanLeadingWhitespace(TidyDocImpl* ARG_UNUSED(doc), Node* node) { if (!TY_(nodeIsText)(node)) return no; if (node->parent->type == DocTypeTag) return no; if (IsPreDescendant(node)) return no; if (node->parent->tag && node->parent->tag->parser == TY_(ParseScript)) return no; /*
...
......
...
*/ if (node->prev == NULL && !TY_(nodeHasCM)(node->parent, CM_INLINE)) return yes; /*...
... */ if (node->prev && !TY_(nodeHasCM)(node->prev, CM_INLINE) && TY_(nodeIsElement)(node->prev)) return yes; /*...
*/ if (!node->prev && !node->parent->prev && !TY_(nodeHasCM)(node->parent->parent, CM_INLINE)) return yes; return no; } static void CleanSpaces(TidyDocImpl* doc, Node* node) { Node* next; while (node) { next = node->next; if (TY_(nodeIsText)(node) && CleanLeadingWhitespace(doc, node)) while (node->start < node->end && TY_(IsWhite)(doc->lexer->lexbuf[node->start])) ++(node->start); if (TY_(nodeIsText)(node) && CleanTrailingWhitespace(doc, node)) while (node->end > node->start && TY_(IsWhite)(doc->lexer->lexbuf[node->end - 1])) --(node->end); if (TY_(nodeIsText)(node) && !(node->start < node->end)) { TY_(RemoveNode)(node); TY_(FreeNode)(doc, node); node = next; continue; } if (node->content) CleanSpaces(doc, node->content); node = next; } } /* Move initial and trailing space out. This routine maps: hello world to hello world and hello world to hello world */ static void TrimSpaces( TidyDocImpl* doc, Node *element) { Node* text = element->content; if (nodeIsPRE(element) || IsPreDescendant(element)) return; if (TY_(nodeIsText)(text)) TrimInitialSpace(doc, element, text); text = element->last; if (TY_(nodeIsText)(text)) TrimTrailingSpace(doc, element, text); } static Bool DescendantOf( Node *element, TidyTagId tid ) { Node *parent; for ( parent = element->parent; parent != NULL; parent = parent->parent ) { if ( TagIsId(parent, tid) ) return yes; } return no; } static Bool InsertMisc(Node *element, Node *node) { if (node->type == CommentTag || node->type == ProcInsTag || node->type == CDATATag || node->type == SectionTag || node->type == AspTag || node->type == JsteTag || node->type == PhpTag ) { TY_(InsertNodeAtEnd)(element, node); return yes; } if ( node->type == XmlDecl ) { Node* root = element; while ( root && root->parent ) root = root->parent; if ( root && !(root->content && root->content->type == XmlDecl)) { TY_(InsertNodeAtStart)( root, node ); return yes; } } /* Declared empty tags seem to be slipping through ** the cracks. This is an experiment to figure out ** a decent place to pick them up. */ if ( node->tag && TY_(nodeIsElement)(node) && TY_(nodeCMIsEmpty)(node) && TagId(node) == TidyTag_UNKNOWN && (node->tag->versions & VERS_PROPRIETARY) != 0 ) { TY_(InsertNodeAtEnd)(element, node); return yes; } return no; } static void ParseTag( TidyDocImpl* doc, Node *node, GetTokenMode mode ) { Lexer* lexer = doc->lexer; if (node->tag == NULL) /* [i_a]2 prevent crash for active content (php, asp) docs */ return; /* Fix by GLP 2000-12-21. Need to reset insertspace if this is both a non-inline and empty tag (base, link, meta, isindex, hr, area). */ if (node->tag->model & CM_EMPTY) { lexer->waswhite = no; if (node->tag->parser == NULL) return; } else if (!(node->tag->model & CM_INLINE)) lexer->insertspace = no; if (node->tag->parser == NULL) return; if (node->type == StartEndTag) return; lexer->parent = node; /* [i_a]2 added this - not sure why - CHECKME: */ (*node->tag->parser)( doc, node, mode ); } /* the doctype has been found after other tags, and needs moving to before the html element */ static void InsertDocType( TidyDocImpl* doc, Node *element, Node *doctype ) { Node* existing = TY_(FindDocType)( doc ); if ( existing ) { TY_(ReportError)(doc, element, doctype, DISCARDING_UNEXPECTED ); TY_(FreeNode)( doc, doctype ); } else { TY_(ReportError)(doc, element, doctype, DOCTYPE_AFTER_TAGS ); while ( !nodeIsHTML(element) ) element = element->parent; TY_(InsertNodeBeforeElement)( element, doctype ); } } /* move node to the head, where element is used as starting point in hunt for head. normally called during parsing */ static void MoveToHead( TidyDocImpl* doc, Node *element, Node *node ) { Node *head; TY_(RemoveNode)( node ); /* make sure that node is isolated */ if ( TY_(nodeIsElement)(node) ) { TY_(ReportError)(doc, element, node, TAG_NOT_ALLOWED_IN ); head = TY_(FindHEAD)(doc); assert(head != NULL); TY_(InsertNodeAtEnd)(head, node); if ( node->tag->parser ) ParseTag( doc, node, IgnoreWhitespace ); } else { TY_(ReportError)(doc, element, node, DISCARDING_UNEXPECTED); TY_(FreeNode)( doc, node ); } } /* moves given node to end of body element */ static void MoveNodeToBody( TidyDocImpl* doc, Node* node ) { Node* body = TY_(FindBody)( doc ); if ( body ) { TY_(RemoveNode)( node ); TY_(InsertNodeAtEnd)( body, node ); } } static void AddClassNoIndent( TidyDocImpl* doc, Node *node ) { ctmbstr sprop = "padding-left: 2ex; margin-left: 0ex" "; margin-top: 0ex; margin-bottom: 0ex"; if ( !cfgBool(doc, TidyDecorateInferredUL) ) return; if ( cfgBool(doc, TidyMakeClean) ) TY_(AddStyleAsClass)( doc, node, sprop ); else TY_(AddStyleProperty)( doc, node, sprop ); } /* element is node created by the lexer upon seeing the start tag, or by the parser when the start tag is inferred */ void TY_(ParseBlock)( TidyDocImpl* doc, Node *element, GetTokenMode mode) { #if !defined(NDEBUG) && defined(_MSC_VER) static int in_parse_block = 0; static int parse_block_cnt = 0; #endif Lexer* lexer = doc->lexer; Node *node; Bool checkstack = yes; uint istackbase = 0; #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_block++; parse_block_cnt++; SPRTF("Entering ParseBlock %d... %d\n",in_parse_block,parse_block_cnt); #endif if ( element->tag->model & CM_EMPTY ) { #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_block--; SPRTF("Exit ParseBlockL 1 %d...\n",in_parse_block); #endif return; } if ( nodeIsFORM(element) && DescendantOf(element, TidyTag_FORM) ) TY_(ReportError)(doc, element, NULL, ILLEGAL_NESTING ); /* InlineDup() asks the lexer to insert inline emphasis tags currently pushed on the istack, but take care to avoid propagating inline emphasis inside OBJECT or APPLET. For these elements a fresh inline stack context is created and disposed of upon reaching the end of the element. They thus behave like table cells in this respect. */ if (element->tag->model & CM_OBJECT) { istackbase = lexer->istackbase; lexer->istackbase = lexer->istacksize; } if (!(element->tag->model & CM_MIXED)) TY_(InlineDup)( doc, NULL ); /*\ * Issue #212 - If it is likely that it may be necessary * to move a leading space into a text node before this * element, then keep the mode MixedContent to keep any * leading space \*/ if ( !(element->tag->model & CM_INLINE) || (element->tag->model & CM_FIELD ) ) { mode = IgnoreWhitespace; } else if (mode == IgnoreWhitespace) { /* Issue #212 - Further fix in case ParseBlock() is called with 'IgnoreWhitespace' when such a leading space may need to be inserted before this element to preverve the browser view */ mode = MixedContent; } while ((node = TY_(GetToken)(doc, mode /*MixedContent*/)) != NULL) { /* end tag for this element */ if (node->type == EndTag && node->tag && (node->tag == element->tag || element->was == node->tag)) { TY_(FreeNode)( doc, node ); if (element->tag->model & CM_OBJECT) { /* pop inline stack */ while (lexer->istacksize > lexer->istackbase) TY_(PopInline)( doc, NULL ); lexer->istackbase = istackbase; } element->closed = yes; TrimSpaces( doc, element ); #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_block--; SPRTF("Exit ParseBlock 2 %d...\n",in_parse_block); #endif return; } if ( nodeIsBODY( node ) && DescendantOf( element, TidyTag_HEAD )) { /* If we're in the HEAD, close it before proceeding. This is an extremely rare occurance, but has been observed. */ TY_(UngetToken)( doc ); break; } if ( nodeIsHTML(node) || nodeIsHEAD(node) || nodeIsBODY(node) ) { if ( TY_(nodeIsElement)(node) ) TY_(ReportError)(doc, element, node, DISCARDING_UNEXPECTED ); TY_(FreeNode)( doc, node ); continue; } if (node->type == EndTag) { if (node->tag == NULL) { TY_(ReportError)(doc, element, node, DISCARDING_UNEXPECTED ); TY_(FreeNode)( doc, node ); continue; } else if ( nodeIsBR(node) ) node->type = StartTag; else if ( nodeIsP(node) ) { /* Cannot have a block inside a paragraph, so no checking for an ancestor is necessary -- but we _can_ have paragraphs inside a block, so change it to an implicit empty paragraph, to be dealt with according to the user's options */ node->type = StartEndTag; node->implicit = yes; #if OBSOLETE TY_(CoerceNode)(doc, node, TidyTag_BR, no, no); TY_(FreeAttrs)( doc, node ); /* discard align attribute etc. */ TY_(InsertNodeAtEnd)( element, node ); node = InferredTag(doc, TidyTag_BR); #endif } else if (DescendantOf( element, node->tag->id )) { /* if this is the end tag for an ancestor element then infer end tag for this element */ TY_(UngetToken)( doc ); break; #if OBSOLETE Node *parent; for ( parent = element->parent; parent != NULL; parent = parent->parent ) { if (node->tag == parent->tag) { if (!(element->tag->model & CM_OPT)) TY_(ReportError)(doc, element, node, MISSING_ENDTAG_BEFORE ); TY_(UngetToken)( doc ); if (element->tag->model & CM_OBJECT) { /* pop inline stack */ while (lexer->istacksize > lexer->istackbase) TY_(PopInline)( doc, NULL ); lexer->istackbase = istackbase; } TrimSpaces( doc, element ); return; } } #endif } else { /* special case etc. for stuff moved in front of table */ if ( lexer->exiled && (TY_(nodeHasCM)(node, CM_TABLE) || nodeIsTABLE(node)) ) { TY_(UngetToken)( doc ); TrimSpaces( doc, element ); #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_block--; SPRTF("Exit ParseBlock 2 %d...\n",in_parse_block); #endif return; } } } /* mixed content model permits text */ if (TY_(nodeIsText)(node)) { if ( checkstack ) { checkstack = no; if (!(element->tag->model & CM_MIXED)) { if ( TY_(InlineDup)(doc, node) > 0 ) continue; } } TY_(InsertNodeAtEnd)(element, node); mode = MixedContent; /* HTML4 strict doesn't allow mixed content for elements with %block; as their content model */ /* But only body, map, blockquote, form and noscript have content model %block; */ if ( nodeIsBODY(element) || nodeIsMAP(element) || nodeIsBLOCKQUOTE(element) || nodeIsFORM(element) || nodeIsNOSCRIPT(element) ) TY_(ConstrainVersion)( doc, ~VERS_HTML40_STRICT ); continue; } if ( InsertMisc(element, node) ) continue; /* allow PARAM elements? */ if ( nodeIsPARAM(node) ) { if ( TY_(nodeHasCM)(element, CM_PARAM) && TY_(nodeIsElement)(node) ) { TY_(InsertNodeAtEnd)(element, node); continue; } /* otherwise discard it */ TY_(ReportError)(doc, element, node, DISCARDING_UNEXPECTED ); TY_(FreeNode)( doc, node ); continue; } /* allow AREA elements? */ if ( nodeIsAREA(node) ) { if ( nodeIsMAP(element) && TY_(nodeIsElement)(node) ) { TY_(InsertNodeAtEnd)(element, node); continue; } /* otherwise discard it */ TY_(ReportError)(doc, element, node, DISCARDING_UNEXPECTED ); TY_(FreeNode)( doc, node ); continue; } /* ignore unknown start/end tags */ if ( node->tag == NULL ) { TY_(ReportError)(doc, element, node, DISCARDING_UNEXPECTED ); TY_(FreeNode)( doc, node ); continue; } /* Allow CM_INLINE elements here. Allow CM_BLOCK elements here unless lexer->excludeBlocks is yes. LI and DD are special cased. Otherwise infer end tag for this element. */ if ( !TY_(nodeHasCM)(node, CM_INLINE) ) { if ( !TY_(nodeIsElement)(node) ) { if ( nodeIsFORM(node) ) BadForm( doc ); TY_(ReportError)(doc, element, node, DISCARDING_UNEXPECTED ); TY_(FreeNode)( doc, node ); continue; } /* #427671 - Fix by Randy Waki - 10 Aug 00 */ /* If an LI contains an illegal FRAME, FRAMESET, OPTGROUP, or OPTION start tag, discard the start tag and let the subsequent content get parsed as content of the enclosing LI. This seems to mimic IE and Netscape, and avoids an infinite loop: without this check, ParseBlock (which is parsing the LI's content) and ParseList (which is parsing the LI's parent's content) repeatedly defer to each other to parse the illegal start tag, each time inferring a missing or*/ if ( nodeIsBR(node) ) TrimSpaces( doc, element ); TY_(InsertNodeAtEnd)(element, node); if (node->implicit) TY_(ReportError)(doc, element, node, INSERTING_TAG ); /* Issue #212 - WHY is this hard coded to 'IgnoreWhitespace' while an effort has been made above to set a 'MixedContent' mode in some cases? WHY IS THE 'mode' VARIABLE NOT USED HERE???? */ ParseTag( doc, node, IgnoreWhitespace /*MixedContent*/ ); continue; } /* discard unexpected tags */ if (node->type == EndTag) TY_(PopInline)( doc, node ); /* if inline end tag */ TY_(ReportError)(doc, element, node, DISCARDING_UNEXPECTED ); TY_(FreeNode)( doc, node ); continue; } if (!(element->tag->model & CM_OPT)) TY_(ReportError)(doc, element, node, MISSING_ENDTAG_FOR); if (element->tag->model & CM_OBJECT) { /* pop inline stack */ while ( lexer->istacksize > lexer->istackbase ) TY_(PopInline)( doc, NULL ); lexer->istackbase = istackbase; } TrimSpaces( doc, element ); #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_block--; SPRTF("Exit ParseBlock 10 %d...\n",in_parse_block); #endif } /* [i_a] svg / math */ struct MatchingDescendantData { Node *found_node; Bool *passed_marker_node; /* input: */ TidyTagId matching_tagId; Node *node_to_find; Node *marker_node; }; static NodeTraversalSignal FindDescendant_cb(TidyDocImpl* ARG_UNUSED(doc), Node* node, void *propagate) { struct MatchingDescendantData *cb_data = (struct MatchingDescendantData *)propagate; if (TagId(node) == cb_data->matching_tagId) { /* make sure we match up 'unknown' tags exactly! */ if (cb_data->matching_tagId != TidyTag_UNKNOWN || (node->element != NULL && cb_data->node_to_find != NULL && cb_data->node_to_find->element != NULL && 0 == TY_(tmbstrcmp)(cb_data->node_to_find->element, node->element))) { cb_data->found_node = node; return ExitTraversal; } } if (cb_data->passed_marker_node && node == cb_data->marker_node) *cb_data->passed_marker_node = yes; return VisitParent; } /* Search the parent chain (from 'parent' upwards up to the root) for a node matching the given 'node'. When the search passes beyond the 'marker_node' (which is assumed to sit in the parent chain), this will be flagged by setting the boolean referenced by 'is_parent_of_marker' to yes. 'is_parent_of_marker' and 'marker_node' are optional parameters and may be NULL. */ static Node *FindMatchingDescendant( Node *parent, Node *node, Node *marker_node, Bool *is_parent_of_marker ) { struct MatchingDescendantData cb_data = { 0 }; cb_data.matching_tagId = TagId(node); cb_data.node_to_find = node; cb_data.marker_node = marker_node; assert(node); if (is_parent_of_marker) *is_parent_of_marker = no; TY_(TraverseNodeTree)(NULL, parent, FindDescendant_cb, &cb_data); return cb_data.found_node; } /* Act as a generic XML (sub)tree parser: collect each node and add it to the DOM, without any further validation. TODO : add schema- or other-hierarchy-definition-based validation of the subtree here... */ void TY_(ParseNamespace)(TidyDocImpl* doc, Node *basenode, GetTokenMode mode) { Lexer* lexer = doc->lexer; Node *node; Node *parent = basenode; uint istackbase; AttVal* av; /* #130 MathML attr and entity fix! */ /* a la
or | */ TY_(InsertNodeAtEnd)(row, node); exclude_state = lexer->excludeBlocks; lexer->excludeBlocks = no; ParseTag( doc, node, IgnoreWhitespace); lexer->excludeBlocks = exclude_state; /* pop inline stack */ while ( lexer->istacksize > lexer->istackbase ) TY_(PopInline)( doc, NULL ); } } void TY_(ParseRowGroup)(TidyDocImpl* doc, Node *rowgroup, GetTokenMode ARG_UNUSED(mode)) { Lexer* lexer = doc->lexer; Node *node, *parent; if (rowgroup->tag->model & CM_EMPTY) return; while ((node = TY_(GetToken)(doc, IgnoreWhitespace)) != NULL) { if (node->tag == rowgroup->tag) { if (node->type == EndTag) { rowgroup->closed = yes; TY_(FreeNode)( doc, node); return; } TY_(UngetToken)( doc ); return; } /* if |
---|
into each disallowed child whereis allowed in order to replicate some browsers behaivour, but there are a lot of exceptions, e.g. Internet Explorer does not propagateinto table cells while Mozilla does. Opera 6 never propagatesinto blocklevel elements while Opera 7 behaves much like Mozilla. Tidy behaves thus mostly like Opera 6 except for nestedelements which are handled like Mozilla takes them (Opera6 closes allafter the first). There are similar issues like replacingin
with
, for example...(Input)...
...(Tidy)
......(Opera 7 and Internet Explorer)
......(Opera 6 and Mozilla)
......(Input)...
......(Tidy, BUG!)
.........(Internet Explorer)
...
......(Mozilla, Opera 6)
...
......(Opera 7) or something similar, they could also be closing the
...
...and propagate theinto the newly opened. Todo: IMG, OBJECT, APPLET, BIG, SMALL, SUB, SUP, FONT, and BASEFONT are dissallowed in
, Tidy neither detects this nor does it perform any cleanup operation. Tidy should at least issue a warning if it encounters such constructs. Todo: discarding is abviously a bug, it should be replaced by
. */ TY_(InsertNodeAfterElement)(pre, node); TY_(ReportError)(doc, pre, node, MISSING_ENDTAG_BEFORE); ParseTag(doc, node, IgnoreWhitespace); newnode = TY_(InferredTag)(doc, TidyTag_PRE); TY_(ReportError)(doc, pre, newnode, INSERTING_TAG); pre = newnode; TY_(InsertNodeAfterElement)(node, pre); continue; } if ( nodeIsP(node) ) { if (node->type == StartTag) { TY_(ReportError)(doc, pre, node, USING_BR_INPLACE_OF); /* trim white space beforein
*/ TrimSpaces(doc, pre); /* coerce bothand
to
*/ TY_(CoerceNode)(doc, node, TidyTag_BR, no, no); TY_(FreeAttrs)( doc, node ); /* discard align attribute etc. */ TY_(InsertNodeAtEnd)( pre, node ); } else { TY_(ReportError)(doc, pre, node, DISCARDING_UNEXPECTED); TY_(FreeNode)( doc, node); } continue; } if ( TY_(nodeIsElement)(node) ) { /* trim white space before
*/ if ( nodeIsBR(node) ) TrimSpaces(doc, pre); TY_(InsertNodeAtEnd)(pre, node); ParseTag(doc, node, Preformatted); continue; } /* discard unexpected tags */ TY_(ReportError)(doc, pre, node, DISCARDING_UNEXPECTED); TY_(FreeNode)( doc, node); } TY_(ReportError)(doc, pre, node, MISSING_ENDTAG_FOR); } void TY_(ParseOptGroup)(TidyDocImpl* doc, Node *field, GetTokenMode ARG_UNUSED(mode)) { Lexer* lexer = doc->lexer; Node *node; lexer->insert = NULL; /* defer implicit inline start tags */ while ((node = TY_(GetToken)(doc, IgnoreWhitespace)) != NULL) { if (node->tag == field->tag && node->type == EndTag) { TY_(FreeNode)( doc, node); field->closed = yes; TrimSpaces(doc, field); return; } /* deal with comments etc. */ if (InsertMisc(field, node)) continue; if ( node->type == StartTag && (nodeIsOPTION(node) || nodeIsOPTGROUP(node)) ) { if ( nodeIsOPTGROUP(node) ) TY_(ReportError)(doc, field, node, CANT_BE_NESTED); TY_(InsertNodeAtEnd)(field, node); ParseTag(doc, node, MixedContent); continue; } /* discard unexpected tags */ TY_(ReportError)(doc, field, node, DISCARDING_UNEXPECTED ); TY_(FreeNode)( doc, node); } } void TY_(ParseSelect)(TidyDocImpl* doc, Node *field, GetTokenMode ARG_UNUSED(mode)) { #if !defined(NDEBUG) && defined(_MSC_VER) static int in_parse_select = 0; #endif Lexer* lexer = doc->lexer; Node *node; lexer->insert = NULL; /* defer implicit inline start tags */ #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_select++; SPRTF("Entering ParseSelect %d...\n",in_parse_select); #endif while ((node = TY_(GetToken)(doc, IgnoreWhitespace)) != NULL) { if (node->tag == field->tag && node->type == EndTag) { TY_(FreeNode)( doc, node); field->closed = yes; TrimSpaces(doc, field); #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_select--; SPRTF("Exit ParseSelect 1 %d...\n",in_parse_select); #endif return; } /* deal with comments etc. */ if (InsertMisc(field, node)) continue; if ( node->type == StartTag && ( nodeIsOPTION(node) || nodeIsOPTGROUP(node) || nodeIsDATALIST(node) || nodeIsSCRIPT(node)) ) { TY_(InsertNodeAtEnd)(field, node); ParseTag(doc, node, IgnoreWhitespace); continue; } /* discard unexpected tags */ TY_(ReportError)(doc, field, node, DISCARDING_UNEXPECTED); TY_(FreeNode)( doc, node); } TY_(ReportError)(doc, field, node, MISSING_ENDTAG_FOR); #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_select--; SPRTF("Exit ParseSelect 2 %d...\n",in_parse_select); #endif } /* HTML5 */ void TY_(ParseDatalist)(TidyDocImpl* doc, Node *field, GetTokenMode ARG_UNUSED(mode)) { #if !defined(NDEBUG) && defined(_MSC_VER) static int in_parse_datalist = 0; #endif Lexer* lexer = doc->lexer; Node *node; lexer->insert = NULL; /* defer implicit inline start tags */ #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_datalist++; SPRTF("Entering ParseDatalist %d...\n",in_parse_datalist); #endif while ((node = TY_(GetToken)(doc, IgnoreWhitespace)) != NULL) { if (node->tag == field->tag && node->type == EndTag) { TY_(FreeNode)( doc, node); field->closed = yes; TrimSpaces(doc, field); #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_datalist--; SPRTF("Exit ParseDatalist 1 %d...\n",in_parse_datalist); #endif return; } /* deal with comments etc. */ if (InsertMisc(field, node)) continue; if ( node->type == StartTag && ( nodeIsOPTION(node) || nodeIsOPTGROUP(node) || nodeIsDATALIST(node) || nodeIsSCRIPT(node)) ) { TY_(InsertNodeAtEnd)(field, node); ParseTag(doc, node, IgnoreWhitespace); continue; } /* discard unexpected tags */ TY_(ReportError)(doc, field, node, DISCARDING_UNEXPECTED); TY_(FreeNode)( doc, node); } TY_(ReportError)(doc, field, node, MISSING_ENDTAG_FOR); #if !defined(NDEBUG) && defined(_MSC_VER) in_parse_datalist--; SPRTF("Exit ParseDatalist 2 %d...\n",in_parse_datalist); #endif } void TY_(ParseText)(TidyDocImpl* doc, Node *field, GetTokenMode mode) { Lexer* lexer = doc->lexer; Node *node; lexer->insert = NULL; /* defer implicit inline start tags */ if ( nodeIsTEXTAREA(field) ) mode = Preformatted; else mode = MixedContent; /* kludge for font tags */ while ((node = TY_(GetToken)(doc, mode)) != NULL) { if (node->tag == field->tag && node->type == EndTag) { TY_(FreeNode)( doc, node); field->closed = yes; TrimSpaces(doc, field); return; } /* deal with comments etc. */ if (InsertMisc(field, node)) continue; if (TY_(nodeIsText)(node)) { /* only called for 1st child */ if (field->content == NULL && !(mode & Preformatted)) TrimSpaces(doc, field); if (node->start >= node->end) { TY_(FreeNode)( doc, node); continue; } TY_(InsertNodeAtEnd)(field, node); continue; } /* for textarea should all cases of < and & be escaped? */ /* discard inline tags e.g. font */ if ( node->tag && node->tag->model & CM_INLINE && !(node->tag->model & CM_FIELD)) /* #487283 - fix by Lee Passey 25 Jan 02 */ { TY_(ReportError)(doc, field, node, DISCARDING_UNEXPECTED); TY_(FreeNode)( doc, node); continue; } /* terminate element on other tags */ if (!(field->tag->model & CM_OPT)) TY_(ReportError)(doc, field, node, MISSING_ENDTAG_BEFORE); TY_(UngetToken)( doc ); TrimSpaces(doc, field); return; } if (!(field->tag->model & CM_OPT)) TY_(ReportError)(doc, field, node, MISSING_ENDTAG_FOR); } void TY_(ParseTitle)(TidyDocImpl* doc, Node *title, GetTokenMode ARG_UNUSED(mode)) { Node *node; while ((node = TY_(GetToken)(doc, MixedContent)) != NULL) { if (node->tag == title->tag && node->type == StartTag && cfgBool(doc, TidyCoerceEndTags) ) { TY_(ReportError)(doc, title, node, COERCE_TO_ENDTAG); node->type = EndTag; TY_(UngetToken)( doc ); continue; } else if (node->tag == title->tag && node->type == EndTag) { TY_(FreeNode)( doc, node); title->closed = yes; TrimSpaces(doc, title); return; } if (TY_(nodeIsText)(node)) { /* only called for 1st child */ if (title->content == NULL) TrimInitialSpace(doc, title, node); if (node->start >= node->end) { TY_(FreeNode)( doc, node); continue; } TY_(InsertNodeAtEnd)(title, node); continue; } /* deal with comments etc. */ if (InsertMisc(title, node)) continue; /* discard unknown tags */ if (node->tag == NULL) { TY_(ReportError)(doc, title, node, DISCARDING_UNEXPECTED); TY_(FreeNode)( doc, node); continue; } /* pushback unexpected tokens */ TY_(ReportError)(doc, title, node, MISSING_ENDTAG_BEFORE); TY_(UngetToken)( doc ); TrimSpaces(doc, title); return; } TY_(ReportError)(doc, title, node, MISSING_ENDTAG_FOR); } /* This isn't quite right for CDATA content as it recognises tags within the content and parses them accordingly. This will unfortunately screw up scripts which include < + letter, < + !, < + ? or < + / + letter */ void TY_(ParseScript)(TidyDocImpl* doc, Node *script, GetTokenMode ARG_UNUSED(mode)) { Node *node; doc->lexer->parent = script; node = TY_(GetToken)(doc, CdataContent); doc->lexer->parent = NULL; if (node) { TY_(InsertNodeAtEnd)(script, node); } else { /* handle e.g. a document like "