"Fossies" - the Fresh Open Source Software Archive

Member "tidy-html5-5.8.0/src/pprint.c" (16 Jul 2021, 75058 Bytes) of package /linux/www/tidy-html5-5.8.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pprint.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.7.28_vs_5.8.0.

    1 /*
    2   pprint.c -- pretty print parse tree  
    3   
    4   (c) 1998-2007 (W3C) MIT, ERCIM, Keio University
    5   See tidy.h for the copyright notice.
    6 
    7 */
    8 
    9 #include <stdio.h>
   10 #include <stdlib.h>
   11 #include <string.h>
   12 
   13 #include "pprint.h"
   14 #include "tidy-int.h"
   15 #include "parser.h"
   16 #include "entities.h"
   17 #include "tmbstr.h"
   18 #include "utf8.h"
   19 
   20 /* *** FOR DEBUG ONLY *** */
   21 /* #define DEBUG_PPRINT */
   22 /* #define DEBUG_INDENT */
   23 #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_PPRINT)
   24 extern void dbg_show_node( TidyDocImpl* doc, Node *node, int caller, int indent );
   25 #endif
   26 
   27 /*
   28   Block-level and unknown elements are printed on
   29   new lines and their contents indented 2 spaces
   30 
   31   Inline elements are printed inline.
   32 
   33   Inline content is wrapped on spaces (except in
   34   attribute values or preformatted text, after
   35   start tags and before end tags
   36 */
   37 
   38 static void PPrintAsp( TidyDocImpl* doc, uint indent, Node* node );
   39 static void PPrintJste( TidyDocImpl* doc, uint indent, Node* node );
   40 static void PPrintPhp( TidyDocImpl* doc, uint indent, Node* node );
   41 static int  TextEndsWithNewline( Lexer *lexer, Node *node, uint mode );
   42 static int  TextStartsWithWhitespace( Lexer *lexer, Node *node, uint start, uint mode );
   43 static Bool InsideHead( TidyDocImpl* doc, Node *node );
   44 static Bool ShouldIndent( TidyDocImpl* doc, Node *node );
   45 
   46 /*\
   47  * Issue #228 20150715 - macros to access --vertical-space tri state configuration parameter
   48 \*/
   49 #define TidyClassicVS ((cfgAutoBool( doc, TidyVertSpace ) == TidyYesState) ? yes : no)
   50 #define TidyAddVS ((cfgAutoBool( doc, TidyVertSpace ) == TidyAutoState) ? no : yes )
   51 
   52 /*\
   53  * 20150515 - support using tabs instead of spaces - Issue #108
   54  * GH: https://github.com/htacg/tidy-html5/issues/108 - Keep indent with tabs #108
   55  * SF: https://sourceforge.net/p/tidy/feature-requests/3/ - #3 tabs in place of spaces
   56 \*/
   57 static uint indent_char = ' ';
   58 void TY_(PPrintTabs)(void)
   59 {
   60     indent_char = '\t';
   61 }
   62 void TY_(PPrintSpaces)(void)
   63 {
   64     indent_char = ' ';
   65 }
   66 
   67 /* #431953 - start RJ Wraplen adjusted for smooth international ride */
   68 
   69 typedef enum
   70 {
   71   UC00, /* None                       */
   72   UCPC, /* Punctuation, Connector     */
   73   UCPD, /* Punctuation, Dash          */
   74   UCPE, /* Punctuation, Close         */
   75   UCPS, /* Punctuation, Open          */
   76   UCPI, /* Punctuation, Initial quote */
   77   UCPF, /* Punctuation, Final quote   */
   78   UCPO, /* Punctuation, Other         */
   79   UCZS, /* Separator, Space           */
   80   UCZL, /* Separator, Line            */
   81   UCZP  /* Separator, Paragraph       */
   82 } UnicodeCategory;
   83 
   84 /*
   85   From the original code, the following characters are removed:
   86 
   87     U+2011 (non-breaking hyphen)
   88     U+202F (narrow non-break space)
   89     U+2044 (fraction slash) 
   90     U+200B (zero width space)
   91     ...... (bidi formatting control characters)
   92 
   93   U+2011 and U+202F are non-breaking, U+2044 is a Sm character,
   94   U+200B is a non-visible space, wrapping after it would make
   95   this space visible, bidi should be done using HTML features
   96   and the characters are neither Px or Zx.
   97 
   98   The following Unicode 3.0 punctuation characters are added:
   99 
  100     U+2048 (question exclamation mark)
  101     U+2049 (exclamation question mark)
  102     U+204A (tironian sign et)
  103     U+204B (reversed pilcrow sign)
  104     U+204C (black leftwards bullet)
  105     U+204D (black rightwards bullet)
  106     U+3030 (wavy dash)
  107     U+30FB (katakana middle dot)
  108     U+FE63 (small hyphen-minus)
  109     U+FE68 (small reverse solidus)
  110     U+FF3F (fullwidth low line)
  111     U+FF5B (fullwidth left curly bracket)
  112     U+FF5D (fullwidth right curly bracket)
  113 
  114   Other additional characters were not included in Unicode 3.0.
  115   The table is based on Unicode 4.0. It must include only those
  116   characters marking a wrapping point, "before" if the general
  117   category is UCPS or UCPI, otherwise "after".
  118 */
  119 static struct _unicode4cat
  120 {
  121   unsigned long code;
  122   UnicodeCategory category;
  123 } const unicode4cat[] =
  124 {
  125   { 0x2000, UCZS }, { 0x2001, UCZS }, { 0x2002, UCZS }, { 0x2003, UCZS },
  126   { 0x2004, UCZS }, { 0x2005, UCZS }, { 0x2006, UCZS }, { 0x2008, UCZS },
  127   { 0x2009, UCZS }, { 0x200A, UCZS }, { 0x2010, UCPD }, { 0x2012, UCPD },
  128   { 0x2013, UCPD }, { 0x2014, UCPD }, { 0x2015, UCPD }, { 0x2016, UCPO },
  129   { 0x2017, UCPO }, { 0x2018, UCPI }, { 0x2019, UCPF }, { 0x201A, UCPS },
  130   { 0x201B, UCPI }, { 0x201C, UCPI }, { 0x201D, UCPF }, { 0x201E, UCPS },
  131   { 0x201F, UCPI }, { 0x2020, UCPO }, { 0x2021, UCPO }, { 0x2022, UCPO },
  132   { 0x2023, UCPO }, { 0x2024, UCPO }, { 0x2025, UCPO }, { 0x2026, UCPO },
  133   { 0x2027, UCPO }, { 0x2028, UCZL }, { 0x2029, UCZP }, { 0x2030, UCPO },
  134   { 0x2031, UCPO }, { 0x2032, UCPO }, { 0x2033, UCPO }, { 0x2034, UCPO },
  135   { 0x2035, UCPO }, { 0x2036, UCPO }, { 0x2037, UCPO }, { 0x2038, UCPO },
  136   { 0x2039, UCPI }, { 0x203A, UCPF }, { 0x203B, UCPO }, { 0x203C, UCPO },
  137   { 0x203D, UCPO }, { 0x203E, UCPO }, { 0x203F, UCPC }, { 0x2040, UCPC },
  138   { 0x2041, UCPO }, { 0x2042, UCPO }, { 0x2043, UCPO }, { 0x2045, UCPS },
  139   { 0x2046, UCPE }, { 0x2047, UCPO }, { 0x2048, UCPO }, { 0x2049, UCPO },
  140   { 0x204A, UCPO }, { 0x204B, UCPO }, { 0x204C, UCPO }, { 0x204D, UCPO },
  141   { 0x204E, UCPO }, { 0x204F, UCPO }, { 0x2050, UCPO }, { 0x2051, UCPO },
  142   { 0x2053, UCPO }, { 0x2054, UCPC }, { 0x2057, UCPO }, { 0x205F, UCZS },
  143   { 0x207D, UCPS }, { 0x207E, UCPE }, { 0x208D, UCPS }, { 0x208E, UCPE },
  144   { 0x2329, UCPS }, { 0x232A, UCPE }, { 0x23B4, UCPS }, { 0x23B5, UCPE },
  145   { 0x23B6, UCPO }, { 0x2768, UCPS }, { 0x2769, UCPE }, { 0x276A, UCPS },
  146   { 0x276B, UCPE }, { 0x276C, UCPS }, { 0x276D, UCPE }, { 0x276E, UCPS },
  147   { 0x276F, UCPE }, { 0x2770, UCPS }, { 0x2771, UCPE }, { 0x2772, UCPS },
  148   { 0x2773, UCPE }, { 0x2774, UCPS }, { 0x2775, UCPE }, { 0x27E6, UCPS },
  149   { 0x27E7, UCPE }, { 0x27E8, UCPS }, { 0x27E9, UCPE }, { 0x27EA, UCPS },
  150   { 0x27EB, UCPE }, { 0x2983, UCPS }, { 0x2984, UCPE }, { 0x2985, UCPS },
  151   { 0x2986, UCPE }, { 0x2987, UCPS }, { 0x2988, UCPE }, { 0x2989, UCPS },
  152   { 0x298A, UCPE }, { 0x298B, UCPS }, { 0x298C, UCPE }, { 0x298D, UCPS },
  153   { 0x298E, UCPE }, { 0x298F, UCPS }, { 0x2990, UCPE }, { 0x2991, UCPS },
  154   { 0x2992, UCPE }, { 0x2993, UCPS }, { 0x2994, UCPE }, { 0x2995, UCPS },
  155   { 0x2996, UCPE }, { 0x2997, UCPS }, { 0x2998, UCPE }, { 0x29D8, UCPS },
  156   { 0x29D9, UCPE }, { 0x29DA, UCPS }, { 0x29DB, UCPE }, { 0x29FC, UCPS },
  157   { 0x29FD, UCPE }, { 0x3001, UCPO }, { 0x3002, UCPO }, { 0x3003, UCPO },
  158   { 0x3008, UCPS }, { 0x3009, UCPE }, { 0x300A, UCPS }, { 0x300B, UCPE },
  159   { 0x300C, UCPS }, { 0x300D, UCPE }, { 0x300E, UCPS }, { 0x300F, UCPE },
  160   { 0x3010, UCPS }, { 0x3011, UCPE }, { 0x3014, UCPS }, { 0x3015, UCPE },
  161   { 0x3016, UCPS }, { 0x3017, UCPE }, { 0x3018, UCPS }, { 0x3019, UCPE },
  162   { 0x301A, UCPS }, { 0x301B, UCPE }, { 0x301C, UCPD }, { 0x301D, UCPS },
  163   { 0x301E, UCPE }, { 0x301F, UCPE }, { 0x3030, UCPD }, { 0x303D, UCPO },
  164   { 0x30A0, UCPD }, { 0x30FB, UCPC }, { 0xFD3E, UCPS }, { 0xFD3F, UCPE },
  165   { 0xFE30, UCPO }, { 0xFE31, UCPD }, { 0xFE32, UCPD }, { 0xFE33, UCPC },
  166   { 0xFE34, UCPC }, { 0xFE35, UCPS }, { 0xFE36, UCPE }, { 0xFE37, UCPS },
  167   { 0xFE38, UCPE }, { 0xFE39, UCPS }, { 0xFE3A, UCPE }, { 0xFE3B, UCPS },
  168   { 0xFE3C, UCPE }, { 0xFE3D, UCPS }, { 0xFE3E, UCPE }, { 0xFE3F, UCPS },
  169   { 0xFE40, UCPE }, { 0xFE41, UCPS }, { 0xFE42, UCPE }, { 0xFE43, UCPS },
  170   { 0xFE44, UCPE }, { 0xFE45, UCPO }, { 0xFE46, UCPO }, { 0xFE47, UCPS },
  171   { 0xFE48, UCPE }, { 0xFE49, UCPO }, { 0xFE4A, UCPO }, { 0xFE4B, UCPO },
  172   { 0xFE4C, UCPO }, { 0xFE4D, UCPC }, { 0xFE4E, UCPC }, { 0xFE4F, UCPC },
  173   { 0xFE50, UCPO }, { 0xFE51, UCPO }, { 0xFE52, UCPO }, { 0xFE54, UCPO },
  174   { 0xFE55, UCPO }, { 0xFE56, UCPO }, { 0xFE57, UCPO }, { 0xFE58, UCPD },
  175   { 0xFE59, UCPS }, { 0xFE5A, UCPE }, { 0xFE5B, UCPS }, { 0xFE5C, UCPE },
  176   { 0xFE5D, UCPS }, { 0xFE5E, UCPE }, { 0xFE5F, UCPO }, { 0xFE60, UCPO },
  177   { 0xFE61, UCPO }, { 0xFE63, UCPD }, { 0xFE68, UCPO }, { 0xFE6A, UCPO },
  178   { 0xFE6B, UCPO }, { 0xFF01, UCPO }, { 0xFF02, UCPO }, { 0xFF03, UCPO },
  179   { 0xFF05, UCPO }, { 0xFF06, UCPO }, { 0xFF07, UCPO }, { 0xFF08, UCPS },
  180   { 0xFF09, UCPE }, { 0xFF0A, UCPO }, { 0xFF0C, UCPO }, { 0xFF0D, UCPD },
  181   { 0xFF0E, UCPO }, { 0xFF0F, UCPO }, { 0xFF1A, UCPO }, { 0xFF1B, UCPO },
  182   { 0xFF1F, UCPO }, { 0xFF20, UCPO }, { 0xFF3B, UCPS }, { 0xFF3C, UCPO },
  183   { 0xFF3D, UCPE }, { 0xFF3F, UCPC }, { 0xFF5B, UCPS }, { 0xFF5D, UCPE },
  184   { 0xFF5F, UCPS }, { 0xFF60, UCPE }, { 0xFF61, UCPO }, { 0xFF62, UCPS },
  185   { 0xFF63, UCPE }, { 0xFF64, UCPO }, { 0xFF65, UCPC }, { 0x10100,UCPO },
  186   { 0x10101,UCPO }, { 0x1039F,UCPO },
  187 
  188   /* final entry */
  189   { 0x0000, UC00 }
  190 };
  191 
  192 typedef enum
  193 {
  194     NoWrapPoint,
  195     WrapBefore,
  196     WrapAfter
  197 } WrapPoint;
  198 
  199 /*
  200   If long lines of text have no white space as defined in HTML 4
  201   (U+0009, U+000A, U+000D, U+000C, U+0020) other characters could
  202   be used to determine a wrap point. Since user agents would
  203   normalize the inserted newline character to a space character,
  204   this wrapping behaviour would insert visual whitespace into the
  205   document.
  206 
  207   Characters of the General Category Pi and Ps in the Unicode
  208   character database (opening punctuation and intial quote
  209   characters) mark a wrapping point before the character, other
  210   punctuation characters (Pc, Pd, Pe, Pf, and Po), breakable
  211   space characters (Zs), and paragraph and line separators
  212   (Zl, Zp) mark a wrap point after the character. Using this
  213   function Tidy can for example pretty print
  214 
  215     <p>....................&ldquo;...quote...&rdquo;...</p>
  216   as
  217     <p>....................\n&ldquo;...quote...&rdquo;...</p>
  218   or
  219     <p>....................&ldquo;...quote...&rdquo;\n...</p>
  220 
  221   if the next normal wrapping point would exceed the user
  222   chosen wrapping column.
  223 */
  224 static WrapPoint CharacterWrapPoint(tchar c)
  225 {
  226     int i;
  227     for (i = 0; unicode4cat[i].code && unicode4cat[i].code <= c; ++i)
  228         if (unicode4cat[i].code == c)
  229         {
  230             /* wrapping before opening punctuation and initial quotes */
  231             if (unicode4cat[i].category == UCPS ||
  232                 unicode4cat[i].category == UCPI)
  233                 return WrapBefore;
  234             /* else wrapping after this character */
  235             else
  236                 return WrapAfter;
  237         }
  238     /* character has no effect on line wrapping */
  239     return NoWrapPoint;
  240 }
  241 
  242 static WrapPoint Big5WrapPoint(tchar c)
  243 {
  244     if ((c & 0xFF00) == 0xA100)
  245     { 
  246         /* opening brackets have odd codes: break before them */ 
  247         if ( c > 0xA15C && c < 0xA1AD && (c & 1) == 1 ) 
  248             return WrapBefore;
  249         return WrapAfter;
  250     }
  251     return NoWrapPoint;
  252 }
  253 
  254 
  255 static void InitIndent( TidyIndent* ind )
  256 {
  257     ind->spaces = -1;
  258     ind->attrValStart = -1;
  259     ind->attrStringStart = -1;
  260 }
  261 
  262 void TY_(InitPrintBuf)( TidyDocImpl* doc )
  263 {
  264     TidyClearMemory( &doc->pprint, sizeof(TidyPrintImpl) );
  265     InitIndent( &doc->pprint.indent[0] );
  266     InitIndent( &doc->pprint.indent[1] );
  267     doc->pprint.allocator = doc->allocator;
  268     doc->pprint.line = 0;
  269 }
  270 
  271 void TY_(FreePrintBuf)( TidyDocImpl* doc )
  272 {
  273     TidyDocFree( doc, doc->pprint.linebuf );
  274     TY_(InitPrintBuf)( doc );
  275 }
  276 
  277 static void expand( TidyPrintImpl* pprint, uint len )
  278 {
  279     uint* ip;
  280     uint buflen = pprint->lbufsize;
  281 
  282     if ( buflen == 0 )
  283         buflen = 256;
  284     while ( len >= buflen )
  285         buflen *= 2;
  286 
  287     ip = (uint*) TidyRealloc( pprint->allocator, pprint->linebuf, buflen*sizeof(uint) );
  288     if ( ip )
  289     {
  290       TidyClearMemory( ip+pprint->lbufsize, 
  291                        (buflen-pprint->lbufsize)*sizeof(uint) );
  292       pprint->lbufsize = buflen;
  293       pprint->linebuf = ip;
  294     }
  295 }
  296 
  297 static uint GetSpaces( TidyPrintImpl* pprint )
  298 {
  299     int spaces = pprint->indent[ 0 ].spaces;
  300     return ( spaces < 0 ? 0U : (uint) spaces );
  301 }
  302 static int ClearInString( TidyPrintImpl* pprint )
  303 {
  304     TidyIndent *ind = pprint->indent + pprint->ixInd;
  305     return ind->attrStringStart = -1;
  306 }
  307 static int ToggleInString( TidyPrintImpl* pprint )
  308 {
  309     TidyIndent *ind = pprint->indent + pprint->ixInd;
  310     Bool inString = ( ind->attrStringStart >= 0 );
  311     return ind->attrStringStart = ( inString ? -1 : (int) pprint->linelen );
  312 }
  313 static Bool IsInString( TidyPrintImpl* pprint )
  314 {
  315     TidyIndent *ind = pprint->indent + 0; /* Always 1st */
  316     return ( ind->attrStringStart >= 0 && 
  317              ind->attrStringStart < (int) pprint->linelen );
  318 }
  319 static Bool IsWrapInString( TidyPrintImpl* pprint )
  320 {
  321     TidyIndent *ind = pprint->indent + 0; /* Always 1st */
  322     int wrap = (int) pprint->wraphere;
  323     return ( ind->attrStringStart == 0 ||
  324              (ind->attrStringStart > 0 && ind->attrStringStart < wrap) );
  325 }
  326 
  327 static Bool HasMixedContent (Node *element)
  328 {
  329     Node * node;
  330 
  331     if (!element)
  332         return no;
  333 
  334     for (node = element->content; node; node = node->next)
  335         if ( TY_(nodeIsText)(node) )
  336              return yes;
  337 
  338     return no;
  339 }
  340 
  341 static void ClearInAttrVal( TidyPrintImpl* pprint )
  342 {
  343     TidyIndent *ind = pprint->indent + pprint->ixInd;
  344     ind->attrValStart = -1;
  345 }
  346 static int SetInAttrVal( TidyPrintImpl* pprint )
  347 {
  348     TidyIndent *ind = pprint->indent + pprint->ixInd;
  349     return ind->attrValStart = (int) pprint->linelen;
  350 }
  351 static Bool IsWrapInAttrVal( TidyPrintImpl* pprint )
  352 {
  353     TidyIndent *ind = pprint->indent + 0; /* Always 1st */
  354     int wrap = (int) pprint->wraphere;
  355     return ( ind->attrValStart == 0 ||
  356              (ind->attrValStart > 0 && ind->attrValStart < wrap) );
  357 }
  358 
  359 static Bool WantIndent( TidyDocImpl* doc )
  360 {
  361     TidyPrintImpl* pprint = &doc->pprint;
  362     Bool wantIt = GetSpaces(pprint) > 0;
  363     if ( wantIt )
  364     {
  365         Bool indentAttrs = cfgBool( doc, TidyIndentAttributes );
  366         wantIt = ( ( !IsWrapInAttrVal(pprint) || indentAttrs ) &&
  367                    !IsWrapInString(pprint) );
  368     }
  369     return wantIt;
  370 }
  371 
  372 
  373 static uint  WrapOff( TidyDocImpl* doc )
  374 {
  375     uint saveWrap = cfg( doc, TidyWrapLen );
  376     TY_(SetOptionInt)( doc, TidyWrapLen, 0xFFFFFFFF );  /* very large number */
  377     return saveWrap;
  378 }
  379 
  380 static void  WrapOn( TidyDocImpl* doc, uint saveWrap )
  381 {
  382     TY_(SetOptionInt)( doc, TidyWrapLen, saveWrap );
  383 }
  384 
  385 static uint  WrapOffCond( TidyDocImpl* doc, Bool onoff )
  386 {
  387     if ( onoff )
  388         return WrapOff( doc );
  389     return cfg( doc, TidyWrapLen );
  390 }
  391 
  392 
  393 static void AddC( TidyPrintImpl* pprint, uint c, uint string_index)
  394 {
  395     if ( string_index + 1 >= pprint->lbufsize )
  396         expand( pprint, string_index + 1 );
  397     pprint->linebuf[string_index] = c;
  398 }
  399 
  400 static uint AddChar( TidyPrintImpl* pprint, uint c )
  401 {
  402     AddC( pprint, c, pprint->linelen );
  403     return ++pprint->linelen;
  404 }
  405 
  406 static uint AddAsciiString( TidyPrintImpl* pprint, ctmbstr str, uint string_index )
  407 {
  408     uint ix, len = TY_(tmbstrlen)( str );
  409     if ( string_index + len >= pprint->lbufsize )
  410         expand( pprint, string_index + len );
  411 
  412     for ( ix=0; ix<len; ++ix )
  413         pprint->linebuf[string_index + ix] = str[ ix ];
  414     return string_index + len;
  415 }
  416 
  417 static uint AddString( TidyPrintImpl* pprint, ctmbstr str )
  418 {
  419    return pprint->linelen = AddAsciiString( pprint, str, pprint->linelen );
  420 }
  421 
  422 /* Saves current output point as the wrap point,
  423 ** but only if indentation would NOT overflow 
  424 ** the current line.  Otherwise keep previous wrap point.
  425 */
  426 static Bool SetWrap( TidyDocImpl* doc, uint indent )
  427 {
  428     TidyPrintImpl* pprint = &doc->pprint;
  429     Bool wrap = ( indent + pprint->linelen < cfg(doc, TidyWrapLen) );
  430     if ( wrap )
  431     {
  432         if ( pprint->indent[0].spaces < 0 )
  433             pprint->indent[0].spaces = indent;
  434         pprint->wraphere = pprint->linelen;
  435     }
  436     else if ( pprint->ixInd == 0 )
  437     {
  438         /* Save indent 1st time we pass the the wrap line */
  439         pprint->indent[ 1 ].spaces = indent;
  440         pprint->ixInd = 1;
  441     }
  442     return wrap;
  443 }
  444 
  445 static void CarryOver( int* valTo, int* valFrom, uint wrapPoint )
  446 {
  447   if ( *valFrom > (int) wrapPoint )
  448   {
  449     *valTo = *valFrom - wrapPoint;
  450     *valFrom = -1;
  451   }
  452 }
  453 
  454 
  455 static Bool SetWrapAttr( TidyDocImpl* doc,
  456                          uint indent, int attrStart, int strStart )
  457 {
  458     TidyPrintImpl* pprint = &doc->pprint;
  459     TidyIndent *ind = pprint->indent + 0;
  460 
  461     Bool wrap = ( indent + pprint->linelen < cfg(doc, TidyWrapLen) );
  462     if ( wrap )
  463     {
  464         if ( ind[0].spaces < 0 )
  465             ind[0].spaces = indent;
  466         pprint->wraphere = pprint->linelen;
  467     }
  468     else if ( pprint->ixInd == 0 )
  469     {
  470         /* Save indent 1st time we pass the the wrap line */
  471         pprint->indent[ 1 ].spaces = indent;
  472         pprint->ixInd = 1;
  473 
  474         /* Carry over string state */
  475         CarryOver( &ind[1].attrStringStart, &ind[0].attrStringStart, pprint->wraphere );
  476         CarryOver( &ind[1].attrValStart, &ind[0].attrValStart, pprint->wraphere );
  477     }
  478     ind += doc->pprint.ixInd;
  479     ind->attrValStart = attrStart;
  480     ind->attrStringStart = strStart;
  481     return wrap;
  482 }
  483 
  484 
  485 /* Reset indent state after flushing a new line
  486 */
  487 static void ResetLine( TidyPrintImpl* pprint )
  488 {
  489     TidyIndent* ind = pprint->indent + 0;
  490     if ( pprint->ixInd > 0 )
  491     {
  492         ind[0] = ind[1];
  493         InitIndent( &ind[1] );
  494     }
  495 
  496     if ( pprint->wraphere > 0 )
  497     {
  498         int wrap = (int) pprint->wraphere;
  499         if ( ind[0].attrStringStart > wrap )
  500             ind[0].attrStringStart -= wrap;
  501         if ( ind[0].attrValStart > wrap )
  502             ind[0].attrValStart -= wrap;
  503     }
  504     else
  505     {
  506         if ( ind[0].attrStringStart > 0 )
  507             ind[0].attrStringStart = 0;
  508         if ( ind[0].attrValStart > 0 )
  509             ind[0].attrValStart = 0;
  510     }
  511     pprint->wraphere = pprint->ixInd = 0;
  512 }
  513 
  514 /* Shift text after wrap point to
  515 ** beginning of next line.
  516 */
  517 static void ResetLineAfterWrap( TidyPrintImpl* pprint )
  518 {
  519     if ( pprint->linelen > pprint->wraphere )
  520     {
  521         uint *p = pprint->linebuf;
  522         uint *q = p + pprint->wraphere;
  523         uint *end = p + pprint->linelen;
  524 
  525         if ( ! IsWrapInAttrVal(pprint) )
  526         {
  527             while ( q < end && *q == ' ' )
  528                 ++q, ++pprint->wraphere;
  529         }
  530 
  531         while ( q < end )
  532             *p++ = *q++;
  533 
  534         pprint->linelen -= pprint->wraphere;
  535     }
  536     else
  537     {
  538         pprint->linelen = 0;
  539     }
  540 
  541     ResetLine( pprint );
  542 }
  543 
  544 /*\
  545  *  Write the 'indent' char to output
  546  *  Issue #335 - The GetSpaces() returns the number of spaces to be
  547  *  used for the indent. This is fine if ouputting spaces.
  548  *  However, if outputting 'tab' chars, then the number of tabs 
  549  *  output should euivalent to spaces divided by 'tab-size'
  550 \*/
  551 static void WriteIndentChar(TidyDocImpl* doc )
  552 {
  553     TidyPrintImpl* pprint = &doc->pprint;
  554     uint i;
  555     uint spaces = GetSpaces(pprint);
  556     uint tabsize = cfg(doc, TidyTabSize);
  557     if (spaces && (indent_char == '\t') && tabsize)
  558     {
  559         spaces /= tabsize;  // set number of tabs to output
  560         if (spaces == 0)    // with a minimum of one
  561             spaces = 1;
  562     }
  563     for (i = 0; i < spaces; i++)
  564         TY_(WriteChar)(indent_char, doc->docOut); /* 20150515 - Issue #108 */
  565 
  566 }
  567 
  568 /* Goes ahead with writing current line up to
  569 ** previously saved wrap point.  Shifts unwritten
  570 ** text in output buffer to beginning of next line.
  571 */
  572 static void WrapLine( TidyDocImpl* doc )
  573 {
  574     TidyPrintImpl* pprint = &doc->pprint;
  575     uint i;
  576 
  577     if ( pprint->wraphere == 0 )
  578         return;
  579 
  580     if ( WantIndent(doc) )
  581         WriteIndentChar(doc);
  582 
  583     for ( i = 0; i < pprint->wraphere; ++i )
  584         TY_(WriteChar)( pprint->linebuf[i], doc->docOut );
  585 
  586     if ( IsWrapInString(pprint) )
  587         TY_(WriteChar)( '\\', doc->docOut );
  588 
  589     TY_(WriteChar)( '\n', doc->docOut );
  590     pprint->line++;
  591     ResetLineAfterWrap( pprint );
  592 }
  593 
  594 /* Checks current output line length along with current indent.
  595 ** If combined they overflow output line length, go ahead
  596 ** and flush output up to the current wrap point.
  597 */
  598 static Bool CheckWrapLine( TidyDocImpl* doc )
  599 {
  600     TidyPrintImpl* pprint = &doc->pprint;
  601     if ( GetSpaces(pprint) + pprint->linelen >= cfg(doc, TidyWrapLen) )
  602     {
  603         WrapLine( doc );
  604         return yes;
  605     }
  606     return no;
  607 }
  608 
  609 static Bool CheckWrapIndent( TidyDocImpl* doc, uint indent )
  610 {
  611     TidyPrintImpl* pprint = &doc->pprint;
  612     if ( GetSpaces(pprint) + pprint->linelen >= cfg(doc, TidyWrapLen) )
  613     {
  614         WrapLine( doc );
  615         if ( pprint->indent[ 0 ].spaces < 0 )
  616         {
  617 #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_INDENT)
  618             SPRTF("%s Indent from %d to %d\n", __FUNCTION__, pprint->indent[ 0 ].spaces, indent );
  619 #endif  
  620             pprint->indent[ 0 ].spaces = indent;
  621         }
  622         return yes;
  623     }
  624     return no;
  625 }
  626 
  627 static void WrapAttrVal( TidyDocImpl* doc )
  628 {
  629     TidyPrintImpl* pprint = &doc->pprint;
  630     uint i;
  631 
  632     /* assert( IsWrapInAttrVal(pprint) ); */
  633     if ( WantIndent(doc) )
  634         WriteIndentChar(doc);
  635 
  636     for ( i = 0; i < pprint->wraphere; ++i )
  637         TY_(WriteChar)( pprint->linebuf[i], doc->docOut );
  638 
  639     if ( IsWrapInString(pprint) )
  640         TY_(WriteChar)( '\\', doc->docOut );
  641     else
  642         TY_(WriteChar)( ' ', doc->docOut );
  643 
  644     TY_(WriteChar)( '\n', doc->docOut );
  645     pprint->line++;
  646     ResetLineAfterWrap( pprint );
  647 }
  648 
  649 static void PFlushLineImpl( TidyDocImpl* doc )
  650 {
  651     TidyPrintImpl* pprint = &doc->pprint;
  652 
  653     uint i;
  654 
  655     CheckWrapLine( doc );
  656 
  657     if ( WantIndent(doc) )
  658         WriteIndentChar(doc);
  659 
  660     for ( i = 0; i < pprint->linelen; ++i )
  661         TY_(WriteChar)( pprint->linebuf[i], doc->docOut );
  662 
  663     if ( IsInString(pprint) )
  664         TY_(WriteChar)( '\\', doc->docOut );
  665     ResetLine( pprint );
  666     pprint->linelen = 0;
  667 }
  668 
  669 void TY_(PFlushLine)( TidyDocImpl* doc, uint indent )
  670 {
  671     TidyPrintImpl* pprint = &doc->pprint;
  672 
  673     if ( pprint->linelen > 0 )
  674         PFlushLineImpl( doc );
  675 
  676     TY_(WriteChar)( '\n', doc->docOut );
  677     pprint->line++;
  678 
  679     if (pprint->indent[ 0 ].spaces != (int)indent )
  680     {
  681 #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_INDENT)
  682         SPRTF("%s Indent from %d to %d\n", __FUNCTION__, pprint->indent[ 0 ].spaces, indent );
  683 #endif  
  684         pprint->indent[ 0 ].spaces = indent;
  685     }
  686 }
  687 
  688 static void PCondFlushLine( TidyDocImpl* doc, uint indent )
  689 {
  690     TidyPrintImpl* pprint = &doc->pprint;
  691 
  692     if ( pprint->linelen > 0 )
  693     {
  694          PFlushLineImpl( doc );
  695 
  696          TY_(WriteChar)( '\n', doc->docOut );
  697          pprint->line++;
  698     }
  699 
  700     /* Issue #390 - Whether chars to flush or not, set new indent */
  701     if ( pprint->indent[ 0 ].spaces != (int)indent )
  702     {
  703 #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_INDENT)
  704         SPRTF("%s Indent from %d to %d\n", __FUNCTION__, pprint->indent[ 0 ].spaces, indent );
  705 #endif  
  706          pprint->indent[ 0 ].spaces = indent;
  707     }
  708 }
  709 
  710 /**
  711  * Two additional "smart" flush line functions which only
  712  * write a newline if `vertical-space no`. See issues #163 and #227.
  713  * These need to be used in the right place. In same cases `PFlushLine`
  714  * and `PCondFlushLine` should still be used.
  715  */
  716 static void TY_(PFlushLineSmart)( TidyDocImpl* doc, uint indent )
  717 {
  718     TidyPrintImpl* pprint = &doc->pprint;
  719 
  720     if ( pprint->linelen > 0 )
  721         PFlushLineImpl( doc );
  722 
  723     /* Issue #228 - cfgBool( doc, TidyVertSpace ); */
  724     if(TidyAddVS) {
  725         TY_(WriteChar)( '\n', doc->docOut );
  726         pprint->line++;
  727     }
  728 
  729     if ( pprint->indent[ 0 ].spaces != (int)indent )
  730     {
  731 #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_INDENT)
  732         SPRTF("%s Indent from %d to %d\n", __FUNCTION__, pprint->indent[ 0 ].spaces, indent );
  733 #endif  
  734         pprint->indent[ 0 ].spaces = indent;
  735     }
  736 }
  737 
  738 static void PCondFlushLineSmart( TidyDocImpl* doc, uint indent )
  739 {
  740     TidyPrintImpl* pprint = &doc->pprint;
  741 
  742     if ( pprint->linelen > 0 )
  743     {
  744          PFlushLineImpl( doc );
  745 
  746          /* Issue #228 - cfgBool( doc, TidyVertSpace ); */
  747          if(TidyAddVS) {
  748             TY_(WriteChar)( '\n', doc->docOut );
  749             pprint->line++;
  750          }
  751     }
  752 
  753     /*\
  754      *  Issue #390 - Must still deal with fixing indent!
  755      *  If TidyOmitOptionalTags, then in cerain circumstances no PrintEndTag
  756      *  will be done, so linelen will be 0...
  757     \*/
  758     if (pprint->indent[ 0 ].spaces != (int)indent)
  759     {
  760 #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_INDENT)
  761         SPRTF("%s Indent from %d to %d\n", __FUNCTION__, pprint->indent[ 0 ].spaces, indent );
  762 #endif  
  763         pprint->indent[ 0 ].spaces = indent;
  764     }
  765 }
  766 
  767 static void PPrintChar( TidyDocImpl* doc, uint c, uint mode )
  768 {
  769     tmbchar entity[128];
  770     ctmbstr p;
  771     TidyPrintImpl* pprint  = &doc->pprint;
  772     uint outenc = cfg( doc, TidyOutCharEncoding );
  773     Bool qmark = cfgBool( doc, TidyQuoteMarks );
  774 
  775     if ( c == ' ' && !(mode & (PREFORMATTED | COMMENT | ATTRIBVALUE | CDATA)))
  776     {
  777         /* coerce a space character to a non-breaking space */
  778         if (mode & NOWRAP)
  779         {
  780             ctmbstr ent = "&nbsp;";
  781             /* by default XML doesn't define &nbsp; */
  782             if ( cfgBool(doc, TidyNumEntities) || cfgBool(doc, TidyXmlTags) )
  783                 ent = "&#160;";
  784             AddString( pprint, ent );
  785             return;
  786         }
  787         else
  788             pprint->wraphere = pprint->linelen;
  789     }
  790 
  791     /* comment characters are passed raw */
  792     if ( mode & (COMMENT | CDATA) )
  793     {
  794         AddChar( pprint, c );
  795         return;
  796     }
  797 
  798     /* except in CDATA map < to &lt; etc. */
  799     if ( !(mode & CDATA) )
  800     {
  801         if ( c == '<')
  802         {
  803             AddString( pprint, "&lt;" );
  804             return;
  805         }
  806             
  807         if ( c == '>')
  808         {
  809             AddString( pprint, "&gt;" );
  810             return;
  811         }
  812 
  813         /*
  814           naked '&' chars can be left alone or
  815           quoted as &amp; The latter is required
  816           for XML where naked '&' are illegal.
  817         */
  818         if ( c == '&' && cfgBool(doc, TidyQuoteAmpersand)
  819              && !cfgBool(doc, TidyPreserveEntities)
  820              && ( mode != OtherNamespace) ) /* #130 MathML attr and entity fix! */
  821         {
  822             AddString( pprint, "&amp;" );
  823             return;
  824         }
  825 
  826         if ( c == '"' && qmark )
  827         {
  828             AddString( pprint, "&quot;" );
  829             return;
  830         }
  831 
  832         if ( c == '\'' && qmark )
  833         {
  834             AddString( pprint, "&#39;" );
  835             return;
  836         }
  837 
  838         if ( c == 160 && outenc != RAW )
  839         {
  840             if ( cfgBool(doc, TidyQuoteNbsp) )
  841             {
  842                 if ( cfgBool(doc, TidyNumEntities) ||
  843                      cfgBool(doc, TidyXmlTags) )
  844                     AddString( pprint, "&#160;" );
  845                 else
  846                     AddString( pprint, "&nbsp;" );
  847             }
  848             else
  849                 AddChar( pprint, c );
  850             return;
  851         }
  852     }
  853 
  854     /* #431953 - start RJ */
  855     /* Handle encoding-specific issues */
  856     switch ( outenc )
  857     {
  858     case UTF8:
  859     case UTF16:
  860     case UTF16LE:
  861     case UTF16BE:
  862         if (!(mode & PREFORMATTED) && cfg(doc, TidyPunctWrap))
  863         {
  864             WrapPoint wp = CharacterWrapPoint(c);
  865             if (wp == WrapBefore)
  866                 pprint->wraphere = pprint->linelen;
  867             else if (wp == WrapAfter)
  868                 pprint->wraphere = pprint->linelen + 1;
  869         }
  870         break;
  871 
  872     case BIG5:
  873         /* Allow linebreak at Chinese punctuation characters */
  874         /* There are not many spaces in Chinese */
  875         AddChar( pprint, c );
  876         if (!(mode & PREFORMATTED)  && cfg(doc, TidyPunctWrap))
  877         {
  878             WrapPoint wp = Big5WrapPoint(c);
  879             if (wp == WrapBefore)
  880                 pprint->wraphere = pprint->linelen;
  881             else if (wp == WrapAfter)
  882                 pprint->wraphere = pprint->linelen + 1;
  883         }
  884         return;
  885 
  886     case SHIFTJIS:
  887 #ifndef NO_NATIVE_ISO2022_SUPPORT
  888     case ISO2022: /* ISO 2022 characters are passed raw */
  889 #endif
  890     case RAW:
  891         AddChar( pprint, c );
  892         return;
  893     }
  894 
  895     /* don't map latin-1 chars to entities */
  896     if ( outenc == LATIN1 )
  897     {
  898         if (c > 255)  /* multi byte chars */
  899         {
  900             uint vers = TY_(HTMLVersion)( doc );
  901             if ( !cfgBool(doc, TidyNumEntities) && (p = TY_(EntityName)(c, vers)) )
  902                 TY_(tmbsnprintf)(entity, sizeof(entity), "&%s;", p);
  903             else
  904                 TY_(tmbsnprintf)(entity, sizeof(entity), "&#%u;", c);
  905 
  906             AddString( pprint, entity );
  907             return;
  908         }
  909 
  910         if (c > 126 && c < 160)
  911         {
  912             TY_(tmbsnprintf)(entity, sizeof(entity), "&#%u;", c);
  913             AddString( pprint, entity );
  914             return;
  915         }
  916 
  917         AddChar( pprint, c );
  918         return;
  919     }
  920 
  921     /* don't map UTF-8 chars to entities */
  922     if ( outenc == UTF8 )
  923     {
  924         AddChar( pprint, c );
  925         return;
  926     }
  927 
  928     /* don't map UTF-16 chars to entities */
  929     if ( outenc == UTF16 || outenc == UTF16LE || outenc == UTF16BE )
  930     {
  931         AddChar( pprint, c );
  932         return;
  933     }
  934 
  935     /* use numeric entities only  for XML */
  936     if ( cfgBool(doc, TidyXmlTags) )
  937     {
  938         /* if ASCII use numeric entities for chars > 127 */
  939         if ( c > 127 && outenc == ASCII )
  940         {
  941             TY_(tmbsnprintf)(entity, sizeof(entity), "&#%u;", c);
  942             AddString( pprint, entity );
  943             return;
  944         }
  945 
  946         /* otherwise output char raw */
  947         AddChar( pprint, c );
  948         return;
  949     }
  950 
  951     /* default treatment for ASCII */
  952     if ( outenc == ASCII && (c > 126 || (c < ' ' && c != '\t')) )
  953     {
  954         uint vers = TY_(HTMLVersion)( doc );
  955         if (!cfgBool(doc, TidyNumEntities) && (p = TY_(EntityName)(c, vers)) )
  956             TY_(tmbsnprintf)(entity, sizeof(entity), "&%s;", p);
  957         else
  958             TY_(tmbsnprintf)(entity, sizeof(entity), "&#%u;", c);
  959 
  960         AddString( pprint, entity );
  961         return;
  962     }
  963 
  964     AddChar( pprint, c );
  965 }
  966 
  967 static uint IncrWS( uint start, uint end, uint indent, int ixWS )
  968 {
  969   if ( ixWS > 0 )
  970   {
  971     uint st = start + MIN( (uint)ixWS, indent );
  972     start = MIN( st, end );
  973   }
  974   return start;
  975 }
  976 /* 
  977   The line buffer is uint not char so we can
  978   hold Unicode values unencoded. The translation
  979   to UTF-8 is deferred to the TY_(WriteChar)() routine called
  980   to flush the line buffer.
  981 */
  982 static void PPrintText( TidyDocImpl* doc, uint mode, uint indent,
  983                         Node* node  )
  984 {
  985     uint start = node->start;
  986     uint end = node->end;
  987     uint ix, c = 0;
  988     int  ixNL = TextEndsWithNewline( doc->lexer, node, mode );
  989     int  ixWS = TextStartsWithWhitespace( doc->lexer, node, start, mode );
  990     if ( ixNL > 0 )
  991       end -= ixNL;
  992     start = IncrWS( start, end, indent, ixWS );
  993 
  994     for ( ix = start; ix < end; ++ix )
  995     {
  996         CheckWrapIndent( doc, indent );
  997         /*
  998         if ( CheckWrapIndent(doc, indent) )
  999         {
 1000             ixWS = TextStartsWithWhitespace( doc->lexer, node, ix );
 1001             ix = IncrWS( ix, end, indent, ixWS );
 1002         }
 1003         */
 1004         c = (byte) doc->lexer->lexbuf[ix];
 1005 
 1006         /* look for UTF-8 multibyte character */
 1007         if ( c > 0x7F )
 1008              ix += TY_(GetUTF8)( doc->lexer->lexbuf + ix, &c );
 1009 
 1010         if ( c == '\n' )
 1011         {
 1012             TY_(PFlushLine)( doc, indent );
 1013             ixWS = TextStartsWithWhitespace( doc->lexer, node, ix+1, mode );
 1014             ix = IncrWS( ix, end, indent, ixWS );
 1015         }
 1016         else if (( c == '&' ) && (TY_(HTMLVersion)(doc) == HT50) &&
 1017             (((ix + 1) == end) || (((ix + 1) < end) && (isspace(doc->lexer->lexbuf[ix+1] & 0xff)))) )
 1018         {
 1019             /*\
 1020              * Issue #207 - This is an unambiguous ampersand need not be 'quoted' in HTML5
 1021              * Issue #379 - Ensure only 0 to 255 passed to 'isspace' to avoid debug assert
 1022             \*/
 1023             PPrintChar( doc, c, (mode | CDATA) );
 1024         }
 1025         else
 1026         {
 1027             PPrintChar( doc, c, mode );
 1028         }
 1029     }
 1030 }
 1031 
 1032 
 1033 static void PPrintAttrValue( TidyDocImpl* doc, uint indent,
 1034                              ctmbstr value, uint delim, Bool wrappable, Bool scriptAttr )
 1035 {
 1036     TidyPrintImpl* pprint = &doc->pprint;
 1037     Bool scriptlets = cfgBool(doc, TidyWrapScriptlets);
 1038 
 1039     uint mode = PREFORMATTED | ATTRIBVALUE;
 1040     if ( wrappable )
 1041         mode = NORMAL | ATTRIBVALUE;
 1042 
 1043     /* look for ASP, Tango or PHP instructions for computed attribute value */
 1044     if ( value && value[0] == '<' )
 1045     {
 1046         if ( value[1] == '%' || value[1] == '@'||
 1047              TY_(tmbstrncmp)(value, "<?php", 5) == 0 )
 1048             mode |= CDATA;
 1049     }
 1050 
 1051     if ( delim == 0 )
 1052         delim = '"';
 1053 
 1054     AddChar( pprint, '=' );
 1055 
 1056     /* don't wrap after "=" for xml documents */
 1057     if ( !cfgBool(doc, TidyXmlOut) || cfgBool(doc, TidyXhtmlOut) )
 1058     {
 1059         SetWrap( doc, indent );
 1060         CheckWrapIndent( doc, indent );
 1061         /*
 1062         if ( !SetWrap(doc, indent) )
 1063             PCondFlushLine( doc, indent );
 1064         */
 1065     }
 1066 
 1067     AddChar( pprint, delim );
 1068 
 1069     if ( value )
 1070     {
 1071         uint wraplen = cfg( doc, TidyWrapLen );
 1072         int attrStart = SetInAttrVal( pprint );
 1073         int strStart = ClearInString( pprint );
 1074 
 1075         while (*value != '\0')
 1076         {
 1077             uint c = *value;
 1078 
 1079             if ( wrappable && c == ' ' )
 1080                 SetWrapAttr( doc, indent, attrStart, strStart );
 1081 
 1082             if ( wrappable && pprint->wraphere > 0 &&
 1083                  GetSpaces(pprint) + pprint->linelen >= wraplen )
 1084                 WrapAttrVal( doc );
 1085 
 1086             if ( c == delim )
 1087             {
 1088                 ctmbstr entity = (c == '"' ? "&quot;" : "&#39;");
 1089                 AddString( pprint, entity );
 1090                 ++value;
 1091                 continue;
 1092             }
 1093             else if (c == '"')
 1094             {
 1095                 if ( cfgBool(doc, TidyQuoteMarks) )
 1096                     AddString( pprint, "&quot;" );
 1097                 else
 1098                     AddChar( pprint, c );
 1099 
 1100                 if ( delim == '\'' && scriptAttr && scriptlets )
 1101                     strStart = ToggleInString( pprint );
 1102 
 1103                 ++value;
 1104                 continue;
 1105             }
 1106             else if ( c == '\'' )
 1107             {
 1108                 if ( cfgBool(doc, TidyQuoteMarks) )
 1109                     AddString( pprint, "&#39;" );
 1110                 else
 1111                     AddChar( pprint, c );
 1112 
 1113                 if ( delim == '"' && scriptAttr && scriptlets )
 1114                     strStart = ToggleInString( pprint );
 1115 
 1116                 ++value;
 1117                 continue;
 1118             }
 1119 
 1120             /* look for UTF-8 multibyte character */
 1121             if ( c > 0x7F )
 1122                  value += TY_(GetUTF8)( value, &c );
 1123             ++value;
 1124 
 1125             if ( c == '\n' )
 1126             {
 1127                 /* No indent inside Javascript literals */
 1128                 TY_(PFlushLine)( doc, (strStart < 0
 1129                                        && !cfgBool(doc, TidyLiteralAttribs) ?
 1130                                        indent : 0) );
 1131                 continue;
 1132             }
 1133             PPrintChar( doc, c, mode );
 1134         }
 1135         ClearInAttrVal( pprint );
 1136         ClearInString( pprint );
 1137     }
 1138     AddChar( pprint, delim );
 1139 }
 1140 
 1141 static uint AttrIndent( TidyDocImpl* doc, Node* node, AttVal* ARG_UNUSED(attr) )
 1142 {
 1143   uint spaces = cfg( doc, TidyIndentSpaces );
 1144   uint xtra = 2;  /* 1 for the '<', another for the ' ' */
 1145   if ( node->element == NULL )
 1146     return spaces;
 1147 
 1148   if ( !TY_(nodeHasCM)(node, CM_INLINE) ||
 1149        !ShouldIndent(doc, node->parent ? node->parent: node) )
 1150     return xtra + TY_(tmbstrlen)( node->element );
 1151 
 1152   if ( NULL != (node = TY_(FindContainer)(node)) )
 1153     return xtra + TY_(tmbstrlen)( node->element );
 1154   return spaces;
 1155 }
 1156 
 1157 static Bool AttrNoIndentFirst( /*TidyDocImpl* doc,*/ Node* node, AttVal* attr )
 1158 {
 1159   return ( attr==node->attributes );
 1160   
 1161   /*&& 
 1162            ( InsideHead(doc, node) ||
 1163              !TY_(nodeHasCM)(node, CM_INLINE) ) );
 1164              */
 1165 }
 1166 
 1167 static void PPrintAttribute( TidyDocImpl* doc, uint indent,
 1168                              Node *node, AttVal *attr )
 1169 {
 1170     TidyPrintImpl* pprint = &doc->pprint;
 1171     Bool xmlOut    = cfgBool( doc, TidyXmlOut );
 1172     Bool xhtmlOut  = cfgBool( doc, TidyXhtmlOut );
 1173     Bool wrapAttrs = cfgBool( doc, TidyWrapAttVals );
 1174     uint ucAttrs   = cfg( doc, TidyUpperCaseAttrs );
 1175     Bool indAttrs  = cfgBool( doc, TidyIndentAttributes );
 1176     uint xtra      = AttrIndent( doc, node, attr );
 1177     Bool first     = AttrNoIndentFirst( /*doc,*/ node, attr );
 1178     tmbstr name    = attr->attribute;
 1179     Bool wrappable = no;
 1180     tchar c;
 1181 
 1182     /* fix for odd attribute indentation bug triggered by long values */
 1183     if (!indAttrs)
 1184       xtra = 0;
 1185 
 1186     if ( indAttrs )
 1187     {
 1188         if ( TY_(nodeIsElement)(node) && !first )
 1189         {
 1190             indent += xtra;
 1191             PCondFlushLineSmart( doc, indent );
 1192         }
 1193         else
 1194           indAttrs = no;
 1195     }
 1196 
 1197     CheckWrapIndent( doc, indent );
 1198 
 1199     if ( !xmlOut && !xhtmlOut && attr->dict )
 1200     {
 1201         if ( TY_(IsScript)(doc, name) )
 1202             wrappable = cfgBool( doc, TidyWrapScriptlets );
 1203         else if (!(attrIsCONTENT(attr) || attrIsVALUE(attr) || attrIsALT(attr) || attrIsTITLE(attr)) && wrapAttrs )
 1204             wrappable = yes;
 1205     }
 1206 
 1207     if ( !first && !SetWrap(doc, indent) )
 1208     {
 1209         TY_(PFlushLine)( doc, indent+xtra );  /* Put it on next line */
 1210     }
 1211     else if ( pprint->linelen > 0 )
 1212     {
 1213         AddChar( pprint, ' ' );
 1214     }
 1215 
 1216     /* Attribute name */
 1217     while (*name)
 1218     {
 1219         c = (unsigned char)*name;
 1220 
 1221         if (c > 0x7F)
 1222             name += TY_(GetUTF8)(name, &c);
 1223         else if (ucAttrs == TidyUppercaseYes)
 1224             c = TY_(ToUpper)(c);
 1225 
 1226         AddChar(pprint, c);
 1227         ++name;
 1228     }
 1229 
 1230     CheckWrapIndent( doc, indent );
 1231  
 1232     if ( attr->value == NULL )
 1233     {
 1234         Bool isB = TY_(IsBoolAttribute)(attr);
 1235         Bool scriptAttr = TY_(attrIsEvent)(attr);
 1236 
 1237         if ( xmlOut )
 1238             PPrintAttrValue( doc, indent, isB ? attr->attribute : NULLSTR,
 1239                              attr->delim, no, scriptAttr );
 1240 
 1241         else if ( !isB && !TY_(IsNewNode)(node) )
 1242             PPrintAttrValue( doc, indent, "", attr->delim, yes, scriptAttr );
 1243 
 1244         else 
 1245             SetWrap( doc, indent );
 1246     }
 1247     else
 1248         PPrintAttrValue( doc, indent, attr->value, attr->delim, wrappable, no );
 1249 }
 1250 
 1251 static void PPrintAttrs( TidyDocImpl* doc, uint indent, Node *node )
 1252 {
 1253     TidyPrintImpl* pprint = &doc->pprint;
 1254     AttVal* av;
 1255 
 1256     /* add xml:space attribute to pre and other elements */
 1257     if ( cfgBool(doc, TidyXmlOut) && cfgBool(doc, TidyXmlSpace) &&
 1258          !TY_(GetAttrByName)(node, "xml:space") &&
 1259          TY_(XMLPreserveWhiteSpace)(doc, node) )
 1260     {
 1261         TY_(AddAttribute)( doc, node, "xml:space", "preserve" );
 1262     }
 1263 
 1264     for ( av = node->attributes; av; av = av->next )
 1265     {
 1266         if ( av->attribute != NULL )
 1267         {
 1268             PPrintAttribute( doc, indent, node, av );
 1269         }
 1270         else if ( av->asp != NULL )
 1271         {
 1272             AddChar( pprint, ' ' );
 1273             PPrintAsp( doc, indent, av->asp );
 1274         }
 1275         else if ( av->php != NULL )
 1276         {
 1277             AddChar( pprint, ' ' );
 1278             PPrintPhp( doc, indent, av->php );
 1279         }
 1280     }
 1281 }
 1282 
 1283 Bool TY_(TextNodeEndWithSpace)( Lexer *lexer, Node *node )
 1284 {
 1285     if (TY_(nodeIsText)(node) && node->end > node->start)
 1286     {
 1287         uint i, c = '\0'; /* initialised to avoid warnings */
 1288         for (i = node->start; i < node->end; ++i)
 1289         {
 1290             c = (byte) lexer->lexbuf[i];
 1291             if ( c > 0x7F )
 1292                 i += TY_(GetUTF8)( lexer->lexbuf + i, &c );
 1293         }
 1294 
 1295         if ( c == ' ' || c == '\n' )
 1296             return yes;
 1297     }
 1298     return no;
 1299 }
 1300 
 1301 /*
 1302  Line can be wrapped immediately after inline start tag provided
 1303  if follows a text node ending in a space, or it follows a <br>,
 1304  or its parent is an inline element that that rule applies to.
 1305  This behaviour was reverse engineered from Netscape 3.0.
 1306 
 1307  Line wrapping can occur if an element is not empty and before a block
 1308  level. For instance:
 1309  <p><span>
 1310  x</span>y</p>
 1311  will display properly. Whereas
 1312  <p><img />
 1313  x</p> won't.
 1314 */
 1315 static Bool AfterSpaceImp(Lexer *lexer, Node *node, Bool isEmpty)
 1316 {
 1317     Node *prev;
 1318 
 1319     if ( !TY_(nodeCMIsInline)(node) )
 1320         return yes;
 1321 
 1322     prev = node->prev;
 1323     if (prev)
 1324     {
 1325         if (TY_(nodeIsText)(prev))
 1326             return TY_(TextNodeEndWithSpace)( lexer, prev );
 1327         else if (nodeIsBR(prev))
 1328             return yes;
 1329 
 1330         return no;
 1331     }
 1332 
 1333     if ( isEmpty && !TY_(nodeCMIsInline)(node->parent) )
 1334         return no;
 1335 
 1336     return AfterSpaceImp(lexer, node->parent, isEmpty);
 1337 }
 1338 
 1339 static Bool AfterSpace(Lexer *lexer, Node *node)
 1340 {
 1341     return AfterSpaceImp(lexer, node, TY_(nodeCMIsEmpty)(node));
 1342 }
 1343 
 1344 static void PPrintEndTag( TidyDocImpl* doc, uint ARG_UNUSED(mode),
 1345                           uint ARG_UNUSED(indent), Node *node );
 1346 
 1347 /*\
 1348  *  See Issue #162 - void elements also get a closing tag, like img, br, ...
 1349  *
 1350  *  from : http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
 1351  *  A complete list of the void elements in HTML:
 1352  *  area, base, br, col, command, embed, hr, img, input, keygen, link, meta, param, source, track, wbr
 1353  *
 1354  *  This could be sped up by NOT using the macro nodeIsXXXX, since this repeatedly checks the node,
 1355  *  and then the node->tag, which here are checked at the beginning...
 1356  *
 1357  *  Some have already been done... at least where no macro yet exists.
 1358  *
 1359  *  And maybe a switch(id) case would be faster.
 1360 \*/
 1361 
 1362 static Bool TY_(isVoidElement)( Node *node )
 1363 {
 1364     TidyTagId id;
 1365     if ( !node )
 1366         return no;
 1367     if ( !node->tag )
 1368         return no;
 1369     id = node->tag->id;
 1370     if (nodeIsAREA(node))
 1371         return yes;
 1372     if (nodeIsBASE(node))
 1373         return yes;
 1374     if (nodeIsBR(node))
 1375         return yes;
 1376     if (nodeIsCOL(node))
 1377         return yes;
 1378     /* if (nodeIsCOMMAND(node)) */
 1379     if (id == TidyTag_COMMAND)
 1380         return yes;
 1381     if (nodeIsEMBED(node))
 1382         return yes;
 1383     if (nodeIsHR(node))
 1384         return yes;
 1385     if (nodeIsIMG(node))
 1386         return yes;
 1387     if (nodeIsINPUT(node))
 1388         return yes;
 1389     /* if (nodeIsKEYGEN(node)) */
 1390     if (id == TidyTag_KEYGEN )
 1391         return yes;
 1392     if (nodeIsLINK(node))
 1393         return yes;
 1394     if (nodeIsMETA(node))
 1395         return yes;
 1396     if (nodeIsPARAM(node))
 1397         return yes;
 1398     /* if (nodeIsSOURCE(node)) */
 1399     if (id == TidyTag_SOURCE )
 1400         return yes;
 1401     /* if (nodeIsTRACK(node)) */
 1402     if (id == TidyTag_TRACK )
 1403         return yes;
 1404     if (nodeIsWBR(node))
 1405         return yes;
 1406 
 1407     return no;
 1408 }
 1409 
 1410 static void PPrintTag( TidyDocImpl* doc,
 1411                        uint mode, uint indent, Node *node )
 1412 {
 1413     TidyPrintImpl* pprint = &doc->pprint;
 1414     Bool uc = cfgBool( doc, TidyUpperCaseTags );
 1415     Bool xhtmlOut = cfgBool( doc, TidyXhtmlOut );
 1416     Bool xmlOut = cfgBool( doc, TidyXmlOut );
 1417     tchar c;
 1418     tmbstr s = node->element;
 1419 
 1420     AddChar( pprint, '<' );
 1421 
 1422     if ( node->type == EndTag )
 1423         AddChar( pprint, '/' );
 1424 
 1425     if (s)
 1426     {
 1427         while (*s)
 1428         {
 1429             c = (unsigned char)*s;
 1430 
 1431             if (c > 0x7F)
 1432                 s += TY_(GetUTF8)(s, &c);
 1433             else if (uc)
 1434                 c = TY_(ToUpper)(c);
 1435 
 1436             AddChar(pprint, c);
 1437             ++s;
 1438         }
 1439     }
 1440 
 1441     PPrintAttrs( doc, indent, node );
 1442 
 1443     if ( (xmlOut || xhtmlOut) &&
 1444          (node->type == StartEndTag || TY_(nodeCMIsEmpty)(node)) )
 1445     {
 1446         AddChar( pprint, ' ' );   /* Space is NS compatibility hack <br /> */
 1447         AddChar( pprint, '/' );   /* Required end tag marker */
 1448     }
 1449 
 1450     AddChar( pprint, '>' );
 1451 
 1452     /*\
 1453      *  Appears this was added for Issue #111, #112, #113, but will now add an end tag
 1454      *  for elements like <img ...> which do NOT have an EndTag, even in html5
 1455      *  See Issue #162 - void elements also get a closing tag, like img, br, ...
 1456      *  A complete list of the void elements in HTML:
 1457      *  area, base, br, col, command, embed, hr, img, input, keygen, link, meta, param, source, track, wbr
 1458     \*/
 1459     if ((node->type == StartEndTag && TY_(HTMLVersion)(doc) == HT50) && !TY_(isVoidElement)(node) )
 1460     {
 1461         PPrintEndTag( doc, mode, indent, node );
 1462     }
 1463 
 1464     if ( (node->type != StartEndTag || xhtmlOut || (node->type == StartEndTag && TY_(HTMLVersion)(doc) == HT50)) && !(mode & PREFORMATTED) )
 1465     {
 1466         uint wraplen = cfg( doc, TidyWrapLen );
 1467         CheckWrapIndent( doc, indent );
 1468 
 1469         if ( indent + pprint->linelen < wraplen )
 1470         {
 1471             /* wrap after start tag if is <br/> or if it's not inline.
 1472                Technically, it would be safe to call only AfterSpace.
 1473                However, it would disrupt the existing algorithm. So let's
 1474                leave as is. Note that AfterSpace returns true for non inline
 1475                elements but can still be false for some <br>. So it has to
 1476                stay as well. */
 1477             if (!(mode & NOWRAP)
 1478                 && (!TY_(nodeCMIsInline)(node) || nodeIsBR(node))
 1479                 && AfterSpace(doc->lexer, node))
 1480             {
 1481                 pprint->wraphere = pprint->linelen;
 1482             }
 1483         }
 1484         /* flush the current buffer only if it is known to be safe,
 1485            i.e. it will not introduce some spurious white spaces.
 1486            See bug #996484 */
 1487         else if ( mode & NOWRAP ||
 1488                   nodeIsBR(node) || AfterSpace(doc->lexer, node))
 1489             PCondFlushLineSmart( doc, indent );
 1490     }
 1491 }
 1492 
 1493 static void PPrintEndTag( TidyDocImpl* doc, uint ARG_UNUSED(mode),
 1494                           uint ARG_UNUSED(indent), Node *node )
 1495 {
 1496     TidyPrintImpl* pprint = &doc->pprint;
 1497     Bool uc = cfgBool( doc, TidyUpperCaseTags );
 1498     tmbstr s = node->element;
 1499     tchar c;
 1500 
 1501     AddString( pprint, "</" );
 1502 
 1503     if (s)
 1504     {
 1505         while (*s)
 1506         {
 1507              c = (unsigned char)*s;
 1508 
 1509              if (c > 0x7F)
 1510                  s += TY_(GetUTF8)(s, &c);
 1511              else if (uc)
 1512                  c = TY_(ToUpper)(c);
 1513 
 1514              AddChar(pprint, c);
 1515              ++s;
 1516         }
 1517     }
 1518 
 1519     AddChar( pprint, '>' );
 1520 }
 1521 
 1522 static void PPrintComment( TidyDocImpl* doc, uint indent, Node* node )
 1523 {
 1524     TidyPrintImpl* pprint = &doc->pprint;
 1525 
 1526     SetWrap( doc, indent );
 1527     AddString( pprint, "<!--" );
 1528 
 1529     PPrintText(doc, COMMENT, 0, node);
 1530 
 1531     AddString(pprint, "--");
 1532     AddChar( pprint, '>' );
 1533     if ( node->linebreak && node->next )
 1534         TY_(PFlushLineSmart)( doc, indent );
 1535 }
 1536 
 1537 static void PPrintDocType( TidyDocImpl* doc, uint indent, Node *node )
 1538 {
 1539     TidyPrintImpl* pprint = &doc->pprint;
 1540     uint wraplen = cfg( doc, TidyWrapLen );
 1541     uint spaces = cfg( doc, TidyIndentSpaces );
 1542     AttVal* fpi = TY_(GetAttrByName)(node, "PUBLIC");
 1543     AttVal* sys = TY_(GetAttrByName)(node, "SYSTEM");
 1544 
 1545     /* todo: handle non-ASCII characters in FPI / SI / node->element */
 1546 
 1547     SetWrap( doc, indent );
 1548     PCondFlushLineSmart( doc, indent );
 1549 
 1550     AddString( pprint, "<!DOCTYPE " );
 1551     SetWrap( doc, indent );
 1552     if (node->element)
 1553     {
 1554         AddString(pprint, node->element);
 1555     }
 1556 
 1557     if (fpi && fpi->value)
 1558     {
 1559         AddString(pprint, " PUBLIC ");
 1560         AddChar(pprint, fpi->delim);
 1561         AddString(pprint, fpi->value);
 1562         AddChar(pprint, fpi->delim);
 1563     }
 1564 
 1565     if (fpi && fpi->value && sys && sys->value)
 1566     {
 1567         uint i = pprint->linelen - (TY_(tmbstrlen)(sys->value) + 2) - 1;
 1568         if (!(i>0&&TY_(tmbstrlen)(sys->value)+2+i<wraplen&&i<=(spaces?spaces:2)*2))
 1569             i = 0;
 1570 
 1571         PCondFlushLineSmart(doc, i);
 1572         if (pprint->linelen)
 1573             AddChar(pprint, ' ');
 1574     }
 1575     else if (sys && sys->value)
 1576     {
 1577         AddString(pprint, " SYSTEM ");
 1578     }
 1579 
 1580     if (sys && sys->value)
 1581     {
 1582         AddChar(pprint, sys->delim);
 1583         AddString(pprint, sys->value);
 1584         AddChar(pprint, sys->delim);
 1585     }
 1586 
 1587     if (node->content)
 1588     {
 1589         PCondFlushLineSmart(doc, indent);
 1590         AddChar(pprint, '[');
 1591         PPrintText(doc, CDATA, 0, node->content);
 1592         AddChar(pprint, ']');
 1593     }
 1594 
 1595     SetWrap( doc, 0 );
 1596     AddChar( pprint, '>' );
 1597     PCondFlushLineSmart( doc, indent );
 1598 }
 1599 
 1600 static void PPrintPI( TidyDocImpl* doc, uint indent, Node *node )
 1601 {
 1602     TidyPrintImpl* pprint = &doc->pprint;
 1603     tchar c;
 1604     tmbstr s;
 1605 
 1606     SetWrap( doc, indent );
 1607     AddString( pprint, "<?" );
 1608 
 1609     s = node->element;
 1610 
 1611     while (s && *s)
 1612     {
 1613         c = (unsigned char)*s;
 1614         if (c > 0x7F)
 1615             s += TY_(GetUTF8)(s, &c);
 1616         AddChar(pprint, c);
 1617         ++s;
 1618     }
 1619 
 1620     /* set CDATA to pass < and > unescaped */
 1621     PPrintText( doc, CDATA, indent, node );
 1622 
 1623     if (cfgBool(doc, TidyXmlOut) ||
 1624         cfgBool(doc, TidyXhtmlOut) || node->closed)
 1625         AddChar( pprint, '?' );
 1626 
 1627     AddChar( pprint, '>' );
 1628     PCondFlushLine( doc, indent );
 1629 }
 1630 
 1631 static void PPrintXmlDecl( TidyDocImpl* doc, uint indent, Node *node )
 1632 {
 1633     AttVal* att;
 1634     uint saveWrap;
 1635     TidyPrintImpl* pprint = &doc->pprint;
 1636     Bool ucAttrs;
 1637     SetWrap( doc, indent );
 1638     saveWrap = WrapOff( doc );
 1639 
 1640     /* no case translation for XML declaration pseudo attributes */
 1641     ucAttrs = cfg(doc, TidyUpperCaseAttrs);
 1642     TY_(SetOptionInt)(doc, TidyUpperCaseAttrs, no);
 1643 
 1644     AddString( pprint, "<?xml" );
 1645 
 1646     /* Force order of XML declaration attributes */
 1647     /* PPrintAttrs( doc, indent, node ); */
 1648     if ( NULL != (att = TY_(AttrGetById)(node, TidyAttr_VERSION)) )
 1649       PPrintAttribute( doc, indent, node, att );
 1650     if ( NULL != (att = TY_(AttrGetById)(node, TidyAttr_ENCODING)) )
 1651       PPrintAttribute( doc, indent, node, att );
 1652     if ( NULL != (att = TY_(GetAttrByName)(node, "standalone")) )
 1653       PPrintAttribute( doc, indent, node, att );
 1654 
 1655     /* restore old config value */
 1656     TY_(SetOptionInt)(doc, TidyUpperCaseAttrs, ucAttrs);
 1657 
 1658     if ( node->end <= 0 || doc->lexer->lexbuf[node->end - 1] != '?' )
 1659         AddChar( pprint, '?' );
 1660     AddChar( pprint, '>' );
 1661     WrapOn( doc, saveWrap );
 1662     TY_(PFlushLineSmart)( doc, indent );
 1663 }
 1664 
 1665 /* note ASP and JSTE share <% ... %> syntax */
 1666 static void PPrintAsp( TidyDocImpl* doc, uint indent, Node *node )
 1667 {
 1668     TidyPrintImpl* pprint = &doc->pprint;
 1669     Bool wrapAsp  = cfgBool( doc, TidyWrapAsp );
 1670     Bool wrapJste = cfgBool( doc, TidyWrapJste );
 1671     uint saveWrap = WrapOffCond( doc, !wrapAsp || !wrapJste );
 1672 
 1673     AddString( pprint, "<%" );
 1674     PPrintText( doc, (wrapAsp ? CDATA : COMMENT), indent, node );
 1675     AddString( pprint, "%>" );
 1676 
 1677     /* PCondFlushLine( doc, indent ); */
 1678     WrapOn( doc, saveWrap );
 1679 }
 1680 
 1681 /* JSTE also supports <# ... #> syntax */
 1682 static void PPrintJste( TidyDocImpl* doc, uint indent, Node *node )
 1683 {
 1684     TidyPrintImpl* pprint = &doc->pprint;
 1685     Bool wrapAsp = cfgBool( doc, TidyWrapAsp );
 1686     uint saveWrap = WrapOffCond( doc, !wrapAsp  );
 1687 
 1688     AddString( pprint, "<#" );
 1689     PPrintText( doc, (cfgBool(doc, TidyWrapJste) ? CDATA : COMMENT),
 1690                 indent, node );
 1691     AddString( pprint, "#>" );
 1692 
 1693     /* PCondFlushLine( doc, indent ); */
 1694     WrapOn( doc, saveWrap );
 1695 }
 1696 
 1697 /* PHP is based on XML processing instructions */
 1698 static void PPrintPhp( TidyDocImpl* doc, uint indent, Node *node )
 1699 {
 1700     TidyPrintImpl* pprint = &doc->pprint;
 1701     Bool wrapPhp = cfgBool( doc, TidyWrapPhp );
 1702     /* uint saveWrap = WrapOffCond( doc, !wrapPhp  ); */
 1703 
 1704     AddString( pprint, "<?" );
 1705     PPrintText( doc, CDATA, indent, node );
 1706     AddString( pprint, "?>" );
 1707 
 1708     /* Issue #437 - add a new line if 'wrap-php' is on */
 1709     if (wrapPhp)
 1710         PCondFlushLine( doc, indent ); 
 1711        
 1712     /* WrapOn( doc, saveWrap ); */
 1713 }
 1714 
 1715 static void PPrintCDATA( TidyDocImpl* doc, uint indent, Node *node )
 1716 {
 1717     uint saveWrap;
 1718     TidyPrintImpl* pprint = &doc->pprint;
 1719     Bool indentCData = cfgBool( doc, TidyIndentCdata );
 1720     if ( !indentCData )
 1721         indent = 0;
 1722 
 1723     PCondFlushLineSmart( doc, indent );
 1724     saveWrap = WrapOff( doc );        /* disable wrapping */
 1725 
 1726     AddString( pprint, "<![CDATA[" );
 1727     PPrintText( doc, COMMENT, indent, node );
 1728     AddString( pprint, "]]>" );
 1729 
 1730     PCondFlushLineSmart( doc, indent );
 1731     WrapOn( doc, saveWrap );          /* restore wrapping */
 1732 }
 1733 
 1734 static void PPrintSection( TidyDocImpl* doc, uint indent, Node *node )
 1735 {
 1736     TidyPrintImpl* pprint = &doc->pprint;
 1737     Bool wrapSect = cfgBool( doc, TidyWrapSection );
 1738     uint saveWrap = WrapOffCond( doc, !wrapSect  );
 1739 
 1740     AddString( pprint, "<![" );
 1741     PPrintText( doc, (wrapSect ? CDATA : COMMENT),
 1742                 indent, node );
 1743     AddString( pprint, "]>" );
 1744 
 1745     /* PCondFlushLine( doc, indent ); */
 1746     WrapOn( doc, saveWrap );
 1747 }
 1748 
 1749 
 1750 static ctmbstr CDATA_START           = "<![CDATA[";
 1751 static ctmbstr CDATA_END             = "]]>";
 1752 static ctmbstr JS_COMMENT_START      = "//";
 1753 static ctmbstr JS_COMMENT_END        = "";
 1754 static ctmbstr VB_COMMENT_START      = "\'";
 1755 static ctmbstr VB_COMMENT_END        = "";
 1756 static ctmbstr CSS_COMMENT_START     = "/*";
 1757 static ctmbstr CSS_COMMENT_END       = "*/";
 1758 static ctmbstr DEFAULT_COMMENT_START = "";
 1759 static ctmbstr DEFAULT_COMMENT_END   = "";
 1760 
 1761 static Bool InsideHead( TidyDocImpl* doc, Node *node )
 1762 {
 1763   if ( nodeIsHEAD(node) )
 1764     return yes;
 1765 
 1766   if ( node->parent != NULL )
 1767     return InsideHead( doc, node->parent );
 1768 
 1769   return no;
 1770 }
 1771 
 1772 /* Is text node and already ends w/ a newline?
 1773  
 1774    Used to pretty print CDATA/PRE text content.
 1775    If it already ends on a newline, it is not
 1776    necessary to print another before printing end tag.
 1777 */
 1778 static int TextEndsWithNewline(Lexer *lexer, Node *node, uint mode )
 1779 {
 1780     if ( (mode & (CDATA|COMMENT)) && TY_(nodeIsText)(node) && node->end > node->start )
 1781     {
 1782         uint ch, ix = node->end - 1;
 1783         /*\
 1784          *  Skip non-newline whitespace. 
 1785          *  Issue #379 - Only if ix is GT start can it be decremented!
 1786         \*/
 1787         while ( ix > node->start && (ch = (lexer->lexbuf[ix] & 0xff))
 1788                  && ( ch == ' ' || ch == '\t' || ch == '\r' ) )
 1789             --ix;
 1790 
 1791         if ( lexer->lexbuf[ ix ] == '\n' )
 1792           return node->end - ix - 1; /* #543262 tidy eats all memory */
 1793     }
 1794     return -1;
 1795 }
 1796 
 1797 /*\
 1798  * Issue #133 - creeping indent - a very OLD bug - 2nd tidy run increases the indent!
 1799  * If the node is text, then remove any white space equal to the indent,
 1800  * but this also applies to the AspTag, which is text like...
 1801  * And may apply to other text like nodes as well.
 1802  *
 1803  * Here the total white space is returned, and then a sister service, IncrWS() 
 1804  * will advance the start of the lexer output by the amount of the indent.
 1805 \*/
 1806 static Bool TY_(nodeIsTextLike)( Node *node )
 1807 {
 1808     if ( TY_(nodeIsText)(node) )
 1809         return yes;
 1810     if ( node->type == AspTag )
 1811         return yes;
 1812     if (node->type == PhpTag)
 1813         return yes; /* Issue #392 */
 1814     /* add other text like nodes... */
 1815     return no;
 1816 }
 1817 
 1818 static int TextStartsWithWhitespace( Lexer *lexer, Node *node, uint start, uint mode )
 1819 {
 1820     assert( node != NULL );
 1821     if ( (mode & (CDATA|COMMENT)) && TY_(nodeIsTextLike)(node) && node->end > node->start && start >= node->start )
 1822     {
 1823         uint ch, ix = start;
 1824         /* Skip whitespace. */
 1825         while ( ix < node->end && (ch = (lexer->lexbuf[ix] & 0xff))
 1826                 && ( ch==' ' || ch=='\t' || ch=='\r' ) )
 1827             ++ix;
 1828 
 1829         if ( ix > start )
 1830           return ix - start;
 1831     }
 1832     return -1;
 1833 }
 1834 
 1835 static Bool HasCDATA( Lexer* lexer, Node* node )
 1836 {
 1837     /* Scan forward through the textarray. Since the characters we're
 1838     ** looking for are < 0x7f, we don't have to do any UTF-8 decoding.
 1839     */
 1840     ctmbstr start = lexer->lexbuf + node->start;
 1841     int len = node->end - node->start + 1;
 1842 
 1843     if ( node->type != TextNode )
 1844         return no;
 1845 
 1846     return ( NULL != TY_(tmbsubstrn)( start, len, CDATA_START ));
 1847 }
 1848 
 1849 
 1850 static
 1851 void PPrintScriptStyle( TidyDocImpl* doc, uint mode, uint indent, Node *node )
 1852 {
 1853     TidyPrintImpl* pprint = &doc->pprint;
 1854     Node*   content;
 1855     ctmbstr commentStart = DEFAULT_COMMENT_START;
 1856     ctmbstr commentEnd = DEFAULT_COMMENT_END;
 1857     Bool    hasCData = no;
 1858     int     contentIndent = -1;
 1859     Bool    xhtmlOut = cfgBool( doc, TidyXhtmlOut );
 1860 
 1861     if ( InsideHead(doc, node) )
 1862       TY_(PFlushLineSmart)( doc, indent );
 1863 
 1864     PCondFlushLineSmart( doc, indent );  /* Issue #56 - long oustanding bug - flush any existing closing tag */
 1865 
 1866     PPrintTag( doc, mode, indent, node );
 1867 
 1868     /* SCRIPT may have no content such as when loading code via its SRC attribute.
 1869        In this case we don't want to flush the line, preferring to keep the required
 1870        closing SCRIPT tag on the same line. */
 1871     if ( node->content != NULL )
 1872         TY_(PFlushLineSmart)(doc, indent);
 1873 
 1874     if ( xhtmlOut && node->content != NULL )
 1875     {
 1876         AttVal* type = attrGetTYPE(node);
 1877 
 1878         if (AttrValueIs(type, "text/javascript"))
 1879         {
 1880             commentStart = JS_COMMENT_START;
 1881             commentEnd = JS_COMMENT_END;
 1882         }
 1883         else if (AttrValueIs(type, "text/css"))
 1884         {
 1885             commentStart = CSS_COMMENT_START;
 1886             commentEnd = CSS_COMMENT_END;
 1887         }
 1888         else if (AttrValueIs(type, "text/vbscript"))
 1889         {
 1890             commentStart = VB_COMMENT_START;
 1891             commentEnd = VB_COMMENT_END;
 1892         }
 1893 
 1894         hasCData = HasCDATA(doc->lexer, node->content);
 1895 
 1896         if (!hasCData)
 1897         {
 1898             uint saveWrap = WrapOff( doc );
 1899 
 1900             AddString( pprint, commentStart );
 1901             AddString( pprint, CDATA_START );
 1902             AddString( pprint, commentEnd );
 1903             PCondFlushLineSmart( doc, indent );
 1904 
 1905             WrapOn( doc, saveWrap );
 1906         }
 1907     }
 1908 
 1909     for ( content = node->content;
 1910           content != NULL;
 1911           content = content->next )
 1912     {
 1913         /*
 1914           This is a bit odd, with the current code there can only
 1915           be one child and the only caller of this function defines
 1916           all these modes already...
 1917         */
 1918         TY_(PPrintTree)( doc, (mode | PREFORMATTED | NOWRAP | CDATA), 
 1919                          indent, content );
 1920 
 1921         if ( content == node->last )
 1922             contentIndent = TextEndsWithNewline( doc->lexer, content, CDATA );
 1923     }
 1924 
 1925     /* Only flush the line if these was content present so that the closing
 1926        SCRIPT tag will stay on the same line. */
 1927     if ( contentIndent < 0 && node->content != NULL )
 1928     {
 1929         PCondFlushLineSmart( doc, indent );
 1930         contentIndent = 0;
 1931     }
 1932 
 1933     if ( xhtmlOut && node->content != NULL )
 1934     {
 1935         if ( ! hasCData )
 1936         {
 1937             uint saveWrap = WrapOff( doc );
 1938 
 1939             AddString( pprint, commentStart );
 1940             AddString( pprint, CDATA_END );
 1941             AddString( pprint, commentEnd );
 1942 
 1943             WrapOn( doc, saveWrap );
 1944             PCondFlushLineSmart( doc, indent );
 1945         }
 1946     }
 1947 
 1948     if ( node->content && pprint->indent[ 0 ].spaces != (int)indent )
 1949     {
 1950 #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_INDENT)
 1951         SPRTF("%s Indent from %d to %d\n", __FUNCTION__, pprint->indent[ 0 ].spaces, indent );
 1952 #endif  
 1953         pprint->indent[ 0 ].spaces = indent;
 1954     }
 1955     PPrintEndTag( doc, mode, indent, node );
 1956     if ( cfgAutoBool(doc, TidyIndentContent) == TidyNoState
 1957          && node->next != NULL &&
 1958          !( TY_(nodeHasCM)(node, CM_INLINE) || TY_(nodeIsText)(node) ) )
 1959         TY_(PFlushLineSmart)( doc, indent );
 1960 }
 1961 
 1962 
 1963 
 1964 static Bool ShouldIndent( TidyDocImpl* doc, Node *node )
 1965 {
 1966     TidyTriState indentContent = cfgAutoBool( doc, TidyIndentContent );
 1967     if ( indentContent == TidyNoState )
 1968         return no;
 1969 
 1970     if ( nodeIsTEXTAREA(node) )
 1971         return no;
 1972 
 1973     if ( indentContent == TidyAutoState )
 1974     {
 1975         if ( node->content && TY_(nodeHasCM)(node, CM_NO_INDENT) )
 1976         {
 1977             for ( node = node->content; node; node = node->next )
 1978                 if ( TY_(nodeHasCM)(node, CM_BLOCK) )
 1979                     return yes;
 1980             return no;
 1981         }
 1982 
 1983         if ( TY_(nodeHasCM)(node, CM_HEADING) )
 1984             return no;
 1985 
 1986         if ( nodeIsHTML(node) )
 1987             return no;
 1988 
 1989         if ( nodeIsP(node) )
 1990             return no;
 1991 
 1992         if ( nodeIsTITLE(node) )
 1993             return no;
 1994 
 1995         /* http://tidy.sf.net/issue/1610888
 1996            Indenting <div><img /></div> produces spurious lines with IE 6.x */
 1997         if ( nodeIsDIV(node) && node->last && nodeIsIMG(node->last) )
 1998             return no;
 1999     }
 2000 
 2001     if ( TY_(nodeHasCM)(node, CM_FIELD | CM_OBJECT) )
 2002         return yes;
 2003 
 2004     if ( nodeIsMAP(node) )
 2005         return yes;
 2006 
 2007     return ( !TY_(nodeHasCM)( node, CM_INLINE ) && node->content );
 2008 }
 2009 
 2010 /*
 2011  Feature request #434940 - fix by Dave Raggett/Ignacio Vazquez-Abrams 21 Jun 01
 2012  print just the content of the body element.
 2013  useful when you want to reuse material from
 2014  other documents.
 2015 
 2016  -- Sebastiano Vigna <vigna@dsi.unimi.it>
 2017 */
 2018 void TY_(PrintBody)( TidyDocImpl* doc )
 2019 {
 2020     Node *node = TY_(FindBody)( doc );
 2021 
 2022     if ( node )
 2023     {
 2024         for ( node = node->content; node != NULL; node = node->next )
 2025             TY_(PPrintTree)( doc, NORMAL, 0, node );
 2026     }
 2027 }
 2028 
 2029 /* #130 MathML attr and entity fix! 
 2030    Support MathML namepsace */
 2031 static void PPrintMathML( TidyDocImpl* doc, uint indent, Node *node )
 2032 {
 2033     Node *content;
 2034     uint mode = OtherNamespace;
 2035 
 2036     PPrintTag( doc, mode, indent, node );
 2037 
 2038     for ( content = node->content; content; content = content->next )
 2039            TY_(PPrintTree)( doc, mode, indent, content );
 2040 
 2041     PPrintEndTag( doc, mode, indent, node );
 2042 }
 2043 
 2044 void TY_(PPrintTree)( TidyDocImpl* doc, uint mode, uint indent, Node *node )
 2045 {
 2046     Node *content, *last;
 2047     uint spaces = cfg( doc, TidyIndentSpaces );
 2048     Bool xhtml = cfgBool( doc, TidyXhtmlOut );
 2049 
 2050     if ( node == NULL )
 2051         return;
 2052 
 2053     if (doc->progressCallback)
 2054     {
 2055         doc->progressCallback( tidyImplToDoc(doc), node->line, node->column, doc->pprint.line + 1 );
 2056     }
 2057 
 2058 #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_PPRINT)
 2059     dbg_show_node( doc, node, 4, GetSpaces( &doc->pprint ) );
 2060 #endif
 2061 
 2062     if (node->type == TextNode)
 2063     {
 2064         PPrintText( doc, mode, indent, node );
 2065     }
 2066     else if ( node->type == CommentTag )
 2067     {
 2068         PPrintComment( doc, indent, node );
 2069     }
 2070     else if ( node->type == RootNode )
 2071     {
 2072         for ( content = node->content; content; content = content->next )
 2073            TY_(PPrintTree)( doc, mode, indent, content );
 2074     }
 2075     else if ( node->type == DocTypeTag )
 2076         PPrintDocType( doc, indent, node );
 2077     else if ( node->type == ProcInsTag)
 2078         PPrintPI( doc, indent, node );
 2079     else if ( node->type == XmlDecl)
 2080         PPrintXmlDecl( doc, indent, node );
 2081     else if ( node->type == CDATATag)
 2082         PPrintCDATA( doc, indent, node );
 2083     else if ( node->type == SectionTag)
 2084         PPrintSection( doc, indent, node );
 2085     else if ( node->type == AspTag)
 2086         PPrintAsp( doc, indent, node );
 2087     else if ( node->type == JsteTag)
 2088         PPrintJste( doc, indent, node );
 2089     else if ( node->type == PhpTag)
 2090         PPrintPhp( doc, indent, node );
 2091     else if ( nodeIsMATHML(node) )
 2092         PPrintMathML( doc, indent, node ); /* #130 MathML attr and entity fix! */
 2093     else if ( TY_(nodeCMIsEmpty)(node) ||
 2094               (node->type == StartEndTag && !xhtml) )
 2095     {
 2096         /* Issue #8 - flush to new line? 
 2097            maybe use if ( TY_(nodeHasCM)(node, CM_BLOCK) ) instead
 2098            or remove the CM_INLINE from the tag
 2099          */
 2100         if ( ! TY_(nodeHasCM)(node, CM_INLINE) )
 2101             PCondFlushLineSmart( doc, indent );
 2102 
 2103         if ( nodeIsBR(node) && node->prev &&
 2104              !(nodeIsBR(node->prev) || (mode & PREFORMATTED)) &&
 2105              cfgBool(doc, TidyBreakBeforeBR) )
 2106             TY_(PFlushLineSmart)( doc, indent );
 2107 
 2108         if ( nodeIsHR(node) )
 2109         {
 2110             /* insert extra newline for classic formatting */
 2111             Bool classic = TidyClassicVS; /* #228 - cfgBool( doc, TidyVertSpace ); */
 2112             if (classic && node->parent && node->parent->content != node)
 2113             {
 2114                 TY_(PFlushLineSmart)( doc, indent );
 2115             }
 2116         }
 2117 
 2118         PPrintTag( doc, mode, indent, node );
 2119 
 2120         if (node->next)
 2121         {
 2122           if (nodeIsPARAM(node) || nodeIsAREA(node))
 2123               PCondFlushLineSmart(doc, indent);
 2124           else if ((nodeIsBR(node) && !(mode & PREFORMATTED))
 2125                    || nodeIsHR(node))
 2126               TY_(PFlushLineSmart)(doc, indent);
 2127         }
 2128     }
 2129     else /* some kind of container element */
 2130     {
 2131         if ( node->type == StartEndTag )
 2132             node->type = StartTag;
 2133 
 2134         if ( node->tag && 
 2135              (node->tag->parser == TY_(ParsePre) || nodeIsTEXTAREA(node)) )
 2136         {
 2137             Bool classic  = TidyClassicVS; /* #228 - cfgBool( doc, TidyVertSpace ); */
 2138             uint indprev = indent;
 2139 
 2140             PCondFlushLineSmart( doc, indent ); /* about to add <pre> tag - clear any previous */
 2141 
 2142             /* insert extra newline for classic formatting */
 2143             if (classic && node->parent && node->parent->content != node)
 2144             {
 2145                 TY_(PFlushLineSmart)( doc, indent );
 2146             }
 2147 
 2148             /* Issue #697 - Add NOWRAP to the mode */
 2149             PPrintTag( doc, (mode | NOWRAP), indent, node );   /* add <pre> or <textarea> tag */
 2150 
 2151             indent = 0;
 2152             /* @camoy Fix #158 - remove inserted newlines in pre - TY_(PFlushLineSmart)( doc, indent ); */
 2153 
 2154             for ( content = node->content; content; content = content->next )
 2155             {
 2156                 TY_(PPrintTree)( doc, (mode | PREFORMATTED | NOWRAP),
 2157                                  indent, content );
 2158             }
 2159 
 2160             /* @camoy Fix #158 - remove inserted newlines in pre - PCondFlushLineSmart( doc, indent ); */
 2161             indent = indprev;
 2162             PPrintEndTag( doc, mode, indent, node );
 2163 
 2164             if ( cfgAutoBool(doc, TidyIndentContent) == TidyNoState
 2165                  && node->next != NULL )
 2166                 TY_(PFlushLineSmart)( doc, indent );
 2167         }
 2168         else if ( nodeIsSTYLE(node) || nodeIsSCRIPT(node) )
 2169         {
 2170             PPrintScriptStyle( doc, (mode | PREFORMATTED | NOWRAP | CDATA),
 2171                                indent, node );
 2172         }
 2173         else if ( TY_(nodeCMIsInline)(node) )
 2174         {
 2175             if ( cfgBool(doc, TidyMakeClean) )
 2176             {
 2177                 /* replace <nobr>...</nobr> by &nbsp; or &#160; etc. */
 2178                 if ( nodeIsNOBR(node) )
 2179                 {
 2180                     for ( content = node->content;
 2181                           content != NULL;
 2182                           content = content->next)
 2183                         TY_(PPrintTree)( doc, mode|NOWRAP, indent, content );
 2184                     return;
 2185                 }
 2186             }
 2187 
 2188             /* otherwise a normal inline element */
 2189             PPrintTag( doc, mode, indent, node );
 2190 
 2191             /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
 2192             if ( ShouldIndent(doc, node) )
 2193             {
 2194                 indent += spaces;
 2195                 PCondFlushLineSmart( doc, indent );
 2196 
 2197                 for ( content = node->content;
 2198                       content != NULL;
 2199                       content = content->next )
 2200                     TY_(PPrintTree)( doc, mode, indent, content );
 2201 
 2202                 indent -= spaces;
 2203                 PCondFlushLineSmart( doc, indent );
 2204                 /* PCondFlushLine( doc, indent ); */
 2205             }
 2206             else
 2207             {
 2208                 for ( content = node->content;
 2209                       content != NULL;
 2210                       content = content->next )
 2211                     TY_(PPrintTree)( doc, mode, indent, content );
 2212             }
 2213             PPrintEndTag( doc, mode, indent, node );
 2214         }
 2215         else /* other tags */
 2216         {
 2217             Bool indcont  = ( cfgAutoBool(doc, TidyIndentContent) != TidyNoState );
 2218             /* Issue #582 - Seems this is no longer used
 2219                Bool indsmart = ( cfgAutoBool(doc, TidyIndentContent) == TidyAutoState ); */
 2220             Bool hideend  = cfgBool( doc, TidyOmitOptionalTags );
 2221             Bool classic  = TidyClassicVS; /* #228 - cfgBool( doc, TidyVertSpace ); */
 2222             uint contentIndent = indent;
 2223 
 2224             /* insert extra newline for classic formatting */
 2225             if (classic && node->parent && node->parent->content != node && !nodeIsHTML(node))
 2226             {
 2227                 TY_(PFlushLineSmart)( doc, indent );
 2228             }
 2229 
 2230             if ( ShouldIndent(doc, node) )
 2231                 contentIndent += spaces;
 2232 
 2233             PCondFlushLineSmart( doc, indent );
 2234 
 2235             /*\
 2236              *  Issue #180 - with the above PCondFlushLine, 
 2237              *  this adds an uneccessary additional line!
 2238              *  Maybe only if 'classic' ie --vertical-space yes 
 2239              *  Issue #582 - maybe this is no longer needed!
 2240              *  It adds a 3rd newline if indent: auto...
 2241              *  if ( indsmart && node->prev != NULL && classic)
 2242              *   TY_(PFlushLineSmart)( doc, indent );
 2243             \*/
 2244 
 2245             /* do not omit elements with attributes */
 2246             if ( !hideend || !TY_(nodeHasCM)(node, CM_OMITST) ||
 2247                  node->attributes != NULL )
 2248             {
 2249                 PPrintTag( doc, mode, indent, node );
 2250 
 2251                 if ( ShouldIndent(doc, node) )
 2252                 {
 2253                     /* fix for bug 530791, don't wrap after */
 2254                     /* <li> if first child is text node     */
 2255                     if (!(nodeIsLI(node) && TY_(nodeIsText)(node->content)))
 2256                         PCondFlushLineSmart( doc, contentIndent );
 2257                 }
 2258                 else if ( TY_(nodeHasCM)(node, CM_HTML) || nodeIsNOFRAMES(node) ||
 2259                           (TY_(nodeHasCM)(node, CM_HEAD) && !nodeIsTITLE(node)) )
 2260                     TY_(PFlushLineSmart)( doc, contentIndent );
 2261             }
 2262             else if ( ShouldIndent(doc, node) )
 2263             {
 2264                 /*\
 2265                  * Issue #180 - If the tag was NOT printed due to the -omit option,
 2266                  * then reduce the bumped indent under the same ShouldIndent(doc, node) 
 2267                  * conditions that caused the indent to be bumped.
 2268                 \*/
 2269                 contentIndent -= spaces;
 2270             }
 2271 
 2272             last = NULL;
 2273             for ( content = node->content; content; content = content->next )
 2274             {
 2275                 /* kludge for naked text before block level tag */
 2276                 if ( last && !indcont && TY_(nodeIsText)(last) &&
 2277                      content->tag && !TY_(nodeHasCM)(content, CM_INLINE) )
 2278                 {
 2279                     /* TY_(PFlushLine)(fout, indent); */
 2280                     TY_(PFlushLineSmart)( doc, contentIndent );
 2281                 }
 2282 
 2283                 TY_(PPrintTree)( doc, mode, contentIndent, content );
 2284                 last = content;
 2285             }
 2286 
 2287             /* don't flush line for td and th */
 2288             if ( ShouldIndent(doc, node) ||
 2289                  ( !hideend &&
 2290                    ( TY_(nodeHasCM)(node, CM_HTML) || 
 2291                      nodeIsNOFRAMES(node) ||
 2292                      (TY_(nodeHasCM)(node, CM_HEAD) && !nodeIsTITLE(node))
 2293                    )
 2294                  )
 2295                )
 2296             {
 2297                 PCondFlushLineSmart( doc, indent );
 2298                 if ( !hideend || !TY_(nodeHasCM)(node, CM_OPT) )
 2299                 {
 2300                     PPrintEndTag( doc, mode, indent, node );
 2301                     /* TY_(PFlushLine)( doc, indent ); */
 2302                 }
 2303             }
 2304             else
 2305             {
 2306                 if ( !hideend || !TY_(nodeHasCM)(node, CM_OPT) )
 2307                 {
 2308                     /* newline before endtag for classic formatting */
 2309                     if ( classic && !HasMixedContent(node) )
 2310                         TY_(PFlushLineSmart)( doc, indent );
 2311                     PPrintEndTag( doc, mode, indent, node );
 2312                 }
 2313                 else if (hideend)
 2314                 {
 2315                     /* Issue #390  - must still deal with adjusting indent */
 2316                     TidyPrintImpl* pprint = &doc->pprint;
 2317                     if (pprint->indent[ 0 ].spaces != (int)indent)
 2318                     {
 2319 #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_INDENT)
 2320                         SPRTF("%s Indent from %d to %d\n", __FUNCTION__, pprint->indent[ 0 ].spaces, indent );
 2321 #endif  
 2322                         pprint->indent[ 0 ].spaces = indent;
 2323                     }
 2324                 }
 2325             }
 2326 
 2327             if (!indcont && !hideend && !nodeIsHTML(node) && !classic)
 2328                 TY_(PFlushLineSmart)( doc, indent );
 2329             else if (classic && node->next != NULL && TY_(nodeHasCM)(node, CM_LIST|CM_DEFLIST|CM_TABLE|CM_BLOCK/*|CM_HEADING*/))
 2330                 TY_(PFlushLineSmart)( doc, indent );
 2331         }
 2332     }
 2333 }
 2334 
 2335 void TY_(PPrintXMLTree)( TidyDocImpl* doc, uint mode, uint indent, Node *node )
 2336 {
 2337     Bool xhtmlOut = cfgBool( doc, TidyXhtmlOut );
 2338     if (node == NULL)
 2339         return;
 2340 
 2341     if (doc->progressCallback)
 2342     {
 2343         doc->progressCallback( tidyImplToDoc(doc), node->line, node->column, doc->pprint.line + 1 );
 2344     }
 2345     
 2346     if ( node->type == TextNode)
 2347     {
 2348         PPrintText( doc, mode, indent, node );
 2349     }
 2350     else if ( node->type == CommentTag )
 2351     {
 2352         PCondFlushLineSmart( doc, indent );
 2353         PPrintComment( doc, indent, node);
 2354         /* PCondFlushLine( doc, 0 ); */
 2355     }
 2356     else if ( node->type == RootNode )
 2357     {
 2358         Node *content;
 2359         for ( content = node->content;
 2360               content != NULL;
 2361               content = content->next )
 2362            TY_(PPrintXMLTree)( doc, mode, indent, content );
 2363     }
 2364     else if ( node->type == DocTypeTag )
 2365         PPrintDocType( doc, indent, node );
 2366     else if ( node->type == ProcInsTag )
 2367         PPrintPI( doc, indent, node );
 2368     else if ( node->type == XmlDecl )
 2369         PPrintXmlDecl( doc, indent, node );
 2370     else if ( node->type == CDATATag )
 2371         PPrintCDATA( doc, indent, node );
 2372     else if ( node->type == SectionTag )
 2373         PPrintSection( doc, indent, node );
 2374     else if ( node->type == AspTag )
 2375         PPrintAsp( doc, indent, node );
 2376     else if ( node->type == JsteTag)
 2377         PPrintJste( doc, indent, node );
 2378     else if ( node->type == PhpTag)
 2379         PPrintPhp( doc, indent, node );
 2380     else if ( TY_(nodeHasCM)(node, CM_EMPTY) ||
 2381               (node->type == StartEndTag && !xhtmlOut) )
 2382     {
 2383         PCondFlushLineSmart( doc, indent );
 2384         PPrintTag( doc, mode, indent, node );
 2385         /* TY_(PFlushLine)( doc, indent ); */
 2386     }
 2387     else /* some kind of container element */
 2388     {
 2389         uint spaces = cfg( doc, TidyIndentSpaces );
 2390         Node *content;
 2391         Bool mixed = no;
 2392         uint cindent;
 2393 
 2394         for ( content = node->content; content; content = content->next )
 2395         {
 2396             if ( TY_(nodeIsText)(content) )
 2397             {
 2398                 mixed = yes;
 2399                 break;
 2400             }
 2401         }
 2402 
 2403         PCondFlushLineSmart( doc, indent );
 2404 
 2405         if ( TY_(XMLPreserveWhiteSpace)(doc, node) )
 2406         {
 2407             indent = 0;
 2408             mixed = no;
 2409             cindent = 0;
 2410         }
 2411         else if (mixed)
 2412             cindent = indent;
 2413         else
 2414             cindent = indent + spaces;
 2415 
 2416         PPrintTag( doc, mode, indent, node );
 2417         if ( !mixed && node->content )
 2418             TY_(PFlushLineSmart)( doc, cindent );
 2419  
 2420         for ( content = node->content; content; content = content->next )
 2421             TY_(PPrintXMLTree)( doc, mode, cindent, content );
 2422 
 2423         if ( !mixed && node->content )
 2424             PCondFlushLineSmart( doc, indent );
 2425 
 2426         PPrintEndTag( doc, mode, indent, node );
 2427         /* PCondFlushLine( doc, indent ); */
 2428     }
 2429 }
 2430 
 2431 /*
 2432  * local variables:
 2433  * mode: c
 2434  * indent-tabs-mode: nil
 2435  * c-basic-offset: 4
 2436  * eval: (c-set-offset 'substatement-open 0)
 2437  * end:
 2438  */