/* clang-format off */ /* pprint.c -- pretty print parse tree (c) 1998-2007 (W3C) MIT, ERCIM, Keio University See tidy.h for the copyright notice. */ #include "third_party/tidy/pprint.h" #include "third_party/tidy/tidy-int.h" #include "third_party/tidy/parser.h" #include "third_party/tidy/entities.h" #include "third_party/tidy/tmbstr.h" #include "third_party/tidy/utf8.h" #include "libc/assert.h" #include "third_party/tidy/sprtf.h" /****************************************************************************//* ** MARK: - Debug Output ** For debug only: ** #define DEBUG_PPRINT ** #define DEBUG_INDENT ***************************************************************************/ #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_PPRINT) extern void dbg_show_node( TidyDocImpl* doc, Node *node, int caller, int indent ); #endif /****************************************************************************//* ** MARK: - Forward Declarations ***************************************************************************/ static void PPrintAsp( TidyDocImpl* doc, uint indent, Node* node ); static void PPrintPhp( TidyDocImpl* doc, uint indent, Node* node ); /****************************************************************************//* ** MARK: - Vertical-Space Tri-State Configuration accessor ** Issue #228 20150715 - macros to access --vertical-space tri state ** configuration parameter. ***************************************************************************/ #define TidyClassicVS ((cfgAutoBool( doc, TidyVertSpace ) == TidyYesState) ? yes : no) #define TidyAddVS ((cfgAutoBool( doc, TidyVertSpace ) == TidyAutoState) ? no : yes ) /****************************************************************************//* ** MARK: - Wrapping Support ***************************************************************************/ /** * This typedef establishes the Unicode categories that individual characters * will belong to. These are used to help ensure that TidyWraplen wraps at * an appropriate place. */ typedef enum { UC00, /**< None */ UCPC, /**< Punctuation, Connector */ UCPD, /**< Punctuation, Dash */ UCPE, /**< Punctuation, Close */ UCPS, /**< Punctuation, Open */ UCPI, /**< Punctuation, Initial quote */ UCPF, /**< Punctuation, Final quote */ UCPO, /**< Punctuation, Other */ UCZS, /**< Separator, Space */ UCZL, /**< Separator, Line */ UCZP /**< Separator, Paragraph */ } UnicodeCategory; /** * From the original code, the following characters are removed: * * U+2011 (non-breaking hyphen) * U+202F (narrow non-break space) * U+2044 (fraction slash) * U+200B (zero width space) * ...... (bidi formatting control characters) * * U+2011 and U+202F are non-breaking, U+2044 is a Sm character, * U+200B is a non-visible space, wrapping after it would make * this space visible, bidi should be done using HTML features * and the characters are neither Px or Zx. * * The following Unicode 3.0 punctuation characters are added: * * U+2048 (question exclamation mark) * U+2049 (exclamation question mark) * U+204A (tironian sign et) * U+204B (reversed pilcrow sign) * U+204C (black leftwards bullet) * U+204D (black rightwards bullet) * U+3030 (wavy dash) * U+30FB (katakana middle dot) * U+FE63 (small hyphen-minus) * U+FE68 (small reverse solidus) * U+FF3F (fullwidth low line) * U+FF5B (fullwidth left curly bracket) * U+FF5D (fullwidth right curly bracket) * * Other additional characters were not included in Unicode 3.0. * The table is based on Unicode 4.0. It must include only those * characters marking a wrapping point, "before" if the general * category is UCPS or UCPI, otherwise "after". */ static struct _unicode4cat { unsigned long code; UnicodeCategory category; } const unicode4cat[] = { { 0x2000, UCZS }, { 0x2001, UCZS }, { 0x2002, UCZS }, { 0x2003, UCZS }, { 0x2004, UCZS }, { 0x2005, UCZS }, { 0x2006, UCZS }, { 0x2008, UCZS }, { 0x2009, UCZS }, { 0x200A, UCZS }, { 0x2010, UCPD }, { 0x2012, UCPD }, { 0x2013, UCPD }, { 0x2014, UCPD }, { 0x2015, UCPD }, { 0x2016, UCPO }, { 0x2017, UCPO }, { 0x2018, UCPI }, { 0x2019, UCPF }, { 0x201A, UCPS }, { 0x201B, UCPI }, { 0x201C, UCPI }, { 0x201D, UCPF }, { 0x201E, UCPS }, { 0x201F, UCPI }, { 0x2020, UCPO }, { 0x2021, UCPO }, { 0x2022, UCPO }, { 0x2023, UCPO }, { 0x2024, UCPO }, { 0x2025, UCPO }, { 0x2026, UCPO }, { 0x2027, UCPO }, { 0x2028, UCZL }, { 0x2029, UCZP }, { 0x2030, UCPO }, { 0x2031, UCPO }, { 0x2032, UCPO }, { 0x2033, UCPO }, { 0x2034, UCPO }, { 0x2035, UCPO }, { 0x2036, UCPO }, { 0x2037, UCPO }, { 0x2038, UCPO }, { 0x2039, UCPI }, { 0x203A, UCPF }, { 0x203B, UCPO }, { 0x203C, UCPO }, { 0x203D, UCPO }, { 0x203E, UCPO }, { 0x203F, UCPC }, { 0x2040, UCPC }, { 0x2041, UCPO }, { 0x2042, UCPO }, { 0x2043, UCPO }, { 0x2045, UCPS }, { 0x2046, UCPE }, { 0x2047, UCPO }, { 0x2048, UCPO }, { 0x2049, UCPO }, { 0x204A, UCPO }, { 0x204B, UCPO }, { 0x204C, UCPO }, { 0x204D, UCPO }, { 0x204E, UCPO }, { 0x204F, UCPO }, { 0x2050, UCPO }, { 0x2051, UCPO }, { 0x2053, UCPO }, { 0x2054, UCPC }, { 0x2057, UCPO }, { 0x205F, UCZS }, { 0x207D, UCPS }, { 0x207E, UCPE }, { 0x208D, UCPS }, { 0x208E, UCPE }, { 0x2329, UCPS }, { 0x232A, UCPE }, { 0x23B4, UCPS }, { 0x23B5, UCPE }, { 0x23B6, UCPO }, { 0x2768, UCPS }, { 0x2769, UCPE }, { 0x276A, UCPS }, { 0x276B, UCPE }, { 0x276C, UCPS }, { 0x276D, UCPE }, { 0x276E, UCPS }, { 0x276F, UCPE }, { 0x2770, UCPS }, { 0x2771, UCPE }, { 0x2772, UCPS }, { 0x2773, UCPE }, { 0x2774, UCPS }, { 0x2775, UCPE }, { 0x27E6, UCPS }, { 0x27E7, UCPE }, { 0x27E8, UCPS }, { 0x27E9, UCPE }, { 0x27EA, UCPS }, { 0x27EB, UCPE }, { 0x2983, UCPS }, { 0x2984, UCPE }, { 0x2985, UCPS }, { 0x2986, UCPE }, { 0x2987, UCPS }, { 0x2988, UCPE }, { 0x2989, UCPS }, { 0x298A, UCPE }, { 0x298B, UCPS }, { 0x298C, UCPE }, { 0x298D, UCPS }, { 0x298E, UCPE }, { 0x298F, UCPS }, { 0x2990, UCPE }, { 0x2991, UCPS }, { 0x2992, UCPE }, { 0x2993, UCPS }, { 0x2994, UCPE }, { 0x2995, UCPS }, { 0x2996, UCPE }, { 0x2997, UCPS }, { 0x2998, UCPE }, { 0x29D8, UCPS }, { 0x29D9, UCPE }, { 0x29DA, UCPS }, { 0x29DB, UCPE }, { 0x29FC, UCPS }, { 0x29FD, UCPE }, { 0x3001, UCPO }, { 0x3002, UCPO }, { 0x3003, UCPO }, { 0x3008, UCPS }, { 0x3009, UCPE }, { 0x300A, UCPS }, { 0x300B, UCPE }, { 0x300C, UCPS }, { 0x300D, UCPE }, { 0x300E, UCPS }, { 0x300F, UCPE }, { 0x3010, UCPS }, { 0x3011, UCPE }, { 0x3014, UCPS }, { 0x3015, UCPE }, { 0x3016, UCPS }, { 0x3017, UCPE }, { 0x3018, UCPS }, { 0x3019, UCPE }, { 0x301A, UCPS }, { 0x301B, UCPE }, { 0x301C, UCPD }, { 0x301D, UCPS }, { 0x301E, UCPE }, { 0x301F, UCPE }, { 0x3030, UCPD }, { 0x303D, UCPO }, { 0x30A0, UCPD }, { 0x30FB, UCPC }, { 0xFD3E, UCPS }, { 0xFD3F, UCPE }, { 0xFE30, UCPO }, { 0xFE31, UCPD }, { 0xFE32, UCPD }, { 0xFE33, UCPC }, { 0xFE34, UCPC }, { 0xFE35, UCPS }, { 0xFE36, UCPE }, { 0xFE37, UCPS }, { 0xFE38, UCPE }, { 0xFE39, UCPS }, { 0xFE3A, UCPE }, { 0xFE3B, UCPS }, { 0xFE3C, UCPE }, { 0xFE3D, UCPS }, { 0xFE3E, UCPE }, { 0xFE3F, UCPS }, { 0xFE40, UCPE }, { 0xFE41, UCPS }, { 0xFE42, UCPE }, { 0xFE43, UCPS }, { 0xFE44, UCPE }, { 0xFE45, UCPO }, { 0xFE46, UCPO }, { 0xFE47, UCPS }, { 0xFE48, UCPE }, { 0xFE49, UCPO }, { 0xFE4A, UCPO }, { 0xFE4B, UCPO }, { 0xFE4C, UCPO }, { 0xFE4D, UCPC }, { 0xFE4E, UCPC }, { 0xFE4F, UCPC }, { 0xFE50, UCPO }, { 0xFE51, UCPO }, { 0xFE52, UCPO }, { 0xFE54, UCPO }, { 0xFE55, UCPO }, { 0xFE56, UCPO }, { 0xFE57, UCPO }, { 0xFE58, UCPD }, { 0xFE59, UCPS }, { 0xFE5A, UCPE }, { 0xFE5B, UCPS }, { 0xFE5C, UCPE }, { 0xFE5D, UCPS }, { 0xFE5E, UCPE }, { 0xFE5F, UCPO }, { 0xFE60, UCPO }, { 0xFE61, UCPO }, { 0xFE63, UCPD }, { 0xFE68, UCPO }, { 0xFE6A, UCPO }, { 0xFE6B, UCPO }, { 0xFF01, UCPO }, { 0xFF02, UCPO }, { 0xFF03, UCPO }, { 0xFF05, UCPO }, { 0xFF06, UCPO }, { 0xFF07, UCPO }, { 0xFF08, UCPS }, { 0xFF09, UCPE }, { 0xFF0A, UCPO }, { 0xFF0C, UCPO }, { 0xFF0D, UCPD }, { 0xFF0E, UCPO }, { 0xFF0F, UCPO }, { 0xFF1A, UCPO }, { 0xFF1B, UCPO }, { 0xFF1F, UCPO }, { 0xFF20, UCPO }, { 0xFF3B, UCPS }, { 0xFF3C, UCPO }, { 0xFF3D, UCPE }, { 0xFF3F, UCPC }, { 0xFF5B, UCPS }, { 0xFF5D, UCPE }, { 0xFF5F, UCPS }, { 0xFF60, UCPE }, { 0xFF61, UCPO }, { 0xFF62, UCPS }, { 0xFF63, UCPE }, { 0xFF64, UCPO }, { 0xFF65, UCPC }, { 0x10100,UCPO }, { 0x10101,UCPO }, { 0x1039F,UCPO }, /* final entry */ { 0x0000, UC00 } }; /** * The values in this enum are used to indicate the wrapping point relative * to a specific character. */ typedef enum { NoWrapPoint, /**< Not a wrapping point. */ WrapBefore, /**< Wrap before this character. */ WrapAfter /**< Wrap after this character. */ } WrapPoint; /** * Given a character, indicate whether a wrap point exists before * the character, after the character, or not at all. * * If long lines of text have no white space as defined in HTML 4 * (U+0009, U+000A, U+000D, U+000C, U+0020) other characters could * be used to determine a wrap point. Since user agents would * normalize the inserted newline character to a space character, * this wrapping behaviour would insert visual whitespace into the * document. * * Characters of the General Category Pi and Ps in the Unicode * character database (opening punctuation and initial quote * characters) mark a wrapping point before the character, other * punctuation characters (Pc, Pd, Pe, Pf, and Po), breakable * space characters (Zs), and paragraph and line separators * (Zl, Zp) mark a wrap point after the character. Using this * function Tidy can for example pretty print * *
....................“...quote...”...
* as *....................\n“...quote...”...
* or *....................“...quote...”\n...
* * if the next normal wrapping point would exceed the user * chosen wrapping column. */ static WrapPoint CharacterWrapPoint(tchar c) { int i; for (i = 0; unicode4cat[i].code && unicode4cat[i].code <= c; ++i) if (unicode4cat[i].code == c) { /* wrapping before opening punctuation and initial quotes */ if (unicode4cat[i].category == UCPS || unicode4cat[i].category == UCPI) return WrapBefore; /* else wrapping after this character */ else return WrapAfter; } /* character has no effect on line wrapping */ return NoWrapPoint; } /** * Given a character in Big5 encoding, indicate whether a wrap point * exists before the character, after the character, or not at all. */ static WrapPoint Big5WrapPoint(tchar c) { if ((c & 0xFF00) == 0xA100) { /* opening brackets have odd codes: break before them */ if ( c > 0xA15C && c < 0xA1AD && (c & 1) == 1 ) return WrapBefore; return WrapAfter; } return NoWrapPoint; } /****************************************************************************//* ** MARK: - Print Buffer Allocation and Deallocation ***************************************************************************/ /** * Initializes an instance of TidyIndent to default */ static void InitIndent( TidyIndent* ind ) { ind->spaces = -1; ind->attrValStart = -1; ind->attrStringStart = -1; } /** * Initializes the tidy document's instance of the pretty print buffer. */ void TY_(InitPrintBuf)( TidyDocImpl* doc ) { TidyClearMemory( &doc->pprint, sizeof(TidyPrintImpl) ); InitIndent( &doc->pprint.indent[0] ); InitIndent( &doc->pprint.indent[1] ); doc->pprint.allocator = doc->allocator; doc->pprint.line = 0; } /** * Frees the tidy document's pretty print buffer. */ void TY_(FreePrintBuf)( TidyDocImpl* doc ) { TidyDocFree( doc, doc->pprint.linebuf ); TY_(InitPrintBuf)( doc ); } /****************************************************************************//* ** MARK: - Buffer Utilities ***************************************************************************/ /** * Expand the size of the pretty print buffer. */ static void expand( TidyPrintImpl* pprint, uint len ) { uint* ip; uint buflen = pprint->lbufsize; if ( buflen == 0 ) buflen = 256; while ( len >= buflen ) buflen *= 2; ip = (uint*) TidyRealloc( pprint->allocator, pprint->linebuf, buflen*sizeof(uint) ); if ( ip ) { TidyClearMemory( ip+pprint->lbufsize, (buflen-pprint->lbufsize)*sizeof(uint) ); pprint->lbufsize = buflen; pprint->linebuf = ip; } } /****************************************************************************//* ** MARK: - Indentation and Wrapping Utilities ***************************************************************************/ /** * Returns the indent level of the current line. */ static uint GetSpaces( TidyPrintImpl* pprint ) { int spaces = pprint->indent[ 0 ].spaces; return ( spaces < 0 ? 0U : (uint) spaces ); } /** * Clears the in-string flag. The pretty printer needs to know * whether the current output position is within an attribute's * string value in order to make word wrapping decisions. */ static int ClearInString( TidyPrintImpl* pprint ) { TidyIndent *ind = pprint->indent + pprint->ixInd; return ind->attrStringStart = -1; } /** * Toggle's the in-string flag. The pretty printer needs to know * whether the current output position is within an attribute's * string value in order to make word wrapping decisions. */ static int ToggleInString( TidyPrintImpl* pprint ) { TidyIndent *ind = pprint->indent + pprint->ixInd; Bool inString = ( ind->attrStringStart >= 0 ); return ind->attrStringStart = ( inString ? -1 : (int) pprint->linelen ); } /** * Returns whether or not the current output position is in an * attribute's string value. This is used to make word wrapping * decisions. */ static Bool IsInString( TidyPrintImpl* pprint ) { TidyIndent *ind = pprint->indent + 0; /* Always 1st */ return ( ind->attrStringStart >= 0 && ind->attrStringStart < (int) pprint->linelen ); } /** * Indicates whether or not the current designated word wrap * position is within an attribute's string. */ static Bool IsWrapInString( TidyPrintImpl* pprint ) { TidyIndent *ind = pprint->indent + 0; /* Always 1st */ int wrap = (int) pprint->wraphere; return ( ind->attrStringStart == 0 || (ind->attrStringStart > 0 && ind->attrStringStart < wrap) ); } /** * Clears the in-attribute flag. The pretty printer needs to know * whether the current output position is within an attribute's * string value in order to make word wrapping decisions. */ static void ClearInAttrVal( TidyPrintImpl* pprint ) { TidyIndent *ind = pprint->indent + pprint->ixInd; ind->attrValStart = -1; } /** * Set the in-attribute flag and returns the attribute start * position. The pretty printer needs to know whether the current * output position is within an attribute's string value in order * to make word wrapping decisions. */ static int SetInAttrVal( TidyPrintImpl* pprint ) { TidyIndent *ind = pprint->indent + pprint->ixInd; return ind->attrValStart = (int) pprint->linelen; } /** * Indicates whether or not the current designated word wrap * position is within an attribute. */ static Bool IsWrapInAttrVal( TidyPrintImpl* pprint ) { TidyIndent *ind = pprint->indent + 0; /* Always 1st */ int wrap = (int) pprint->wraphere; return ( ind->attrValStart == 0 || (ind->attrValStart > 0 && ind->attrValStart < wrap) ); } /** * Carry over the string and attribute state from one to the other. */ static void CarryOver( int* valTo, int* valFrom, uint wrapPoint ) { if ( *valFrom > (int) wrapPoint ) { *valTo = *valFrom - wrapPoint; *valFrom = -1; } } /** * Determines whether the attribute can be and updates the wrap location and * the print buffer's TidyIndent record. */ static Bool SetWrapAttr( TidyDocImpl* doc, uint indent, int attrStart, int strStart ) { TidyPrintImpl* pprint = &doc->pprint; TidyIndent *ind = pprint->indent + 0; Bool wrap = ( indent + pprint->linelen < cfg(doc, TidyWrapLen) ); if ( wrap ) { if ( ind[0].spaces < 0 ) ind[0].spaces = indent; pprint->wraphere = pprint->linelen; } else if ( pprint->ixInd == 0 ) { /* Save indent 1st time we pass the the wrap line */ pprint->indent[ 1 ].spaces = indent; pprint->ixInd = 1; /* Carry over string state */ CarryOver( &ind[1].attrStringStart, &ind[0].attrStringStart, pprint->wraphere ); CarryOver( &ind[1].attrValStart, &ind[0].attrValStart, pprint->wraphere ); } ind += doc->pprint.ixInd; ind->attrValStart = attrStart; ind->attrStringStart = strStart; return wrap; } /** * Indicates whether or not indentation is needed for the current line. */ static Bool WantIndent( TidyDocImpl* doc ) { TidyPrintImpl* pprint = &doc->pprint; Bool wantIt = GetSpaces(pprint) > 0; if ( wantIt ) { Bool indentAttrs = cfgBool( doc, TidyIndentAttributes ); wantIt = ( ( !IsWrapInAttrVal(pprint) || indentAttrs ) && !IsWrapInString(pprint) ); } return wantIt; } /** * Turns off wrapping from the current print point onward. It also * returns the current wrapping setting (prior to turning it off) so * that the current state can be restored later. */ static uint WrapOff( TidyDocImpl* doc ) { uint saveWrap = cfg( doc, TidyWrapLen ); TY_(SetOptionInt)( doc, TidyWrapLen, 0xFFFFFFFF ); /* very large number */ return saveWrap; } /** * Turns wrapping on from the current point forward, wrapping at the column * specified by `saveWrap`. */ static void WrapOn( TidyDocImpl* doc, uint saveWrap ) { TY_(SetOptionInt)( doc, TidyWrapLen, saveWrap ); } /** * Conditionally turns off word wrapping, returning the pre-existing value. */ static uint WrapOffCond( TidyDocImpl* doc, Bool onoff ) { if ( onoff ) return WrapOff( doc ); return cfg( doc, TidyWrapLen ); } /** * Saves current output point as the wrap point, but only if indentation * would NOT overflow the current line. Otherwise keep previous wrap point. */ static Bool SetWrap( TidyDocImpl* doc, uint indent ) { TidyPrintImpl* pprint = &doc->pprint; Bool wrap = ( indent + pprint->linelen < cfg(doc, TidyWrapLen) ); if ( wrap ) { if ( pprint->indent[0].spaces < 0 ) pprint->indent[0].spaces = indent; pprint->wraphere = pprint->linelen; } else if ( pprint->ixInd == 0 ) { /* Save indent 1st time we pass the the wrap line */ pprint->indent[ 1 ].spaces = indent; pprint->ixInd = 1; } return wrap; } /** * Reset indent state after flushing a new line. */ static void ResetLine( TidyPrintImpl* pprint ) { TidyIndent* ind = pprint->indent + 0; if ( pprint->ixInd > 0 ) { ind[0] = ind[1]; InitIndent( &ind[1] ); } if ( pprint->wraphere > 0 ) { int wrap = (int) pprint->wraphere; if ( ind[0].attrStringStart > wrap ) ind[0].attrStringStart -= wrap; if ( ind[0].attrValStart > wrap ) ind[0].attrValStart -= wrap; } else { if ( ind[0].attrStringStart > 0 ) ind[0].attrStringStart = 0; if ( ind[0].attrValStart > 0 ) ind[0].attrValStart = 0; } pprint->wraphere = pprint->ixInd = 0; } /** * Shift text after wrap point to beginning of next line. */ static void ResetLineAfterWrap( TidyPrintImpl* pprint ) { if ( pprint->linelen > pprint->wraphere ) { uint *p = pprint->linebuf; uint *q = p + pprint->wraphere; uint *end = p + pprint->linelen; if ( ! IsWrapInAttrVal(pprint) ) { while ( q < end && *q == ' ' ) ++q, ++pprint->wraphere; } while ( q < end ) *p++ = *q++; pprint->linelen -= pprint->wraphere; } else { pprint->linelen = 0; } ResetLine( pprint ); } /** * Write the 'indent' char to output. * Issue #335 - The GetSpaces() returns the number of spaces to be * used for the indent. This is fine if outputting spaces. * However, if outputting 'tab' chars, then the number of tabs * output should equivalent to spaces divided by 'tab-size' */ static void WriteIndentChar(TidyDocImpl* doc ) { TidyPrintImpl* pprint = &doc->pprint; uint i; uint spaces = GetSpaces(pprint); uint tabsize = cfg(doc, TidyTabSize); if (spaces && (doc->indent_char == '\t') && tabsize) { spaces /= tabsize; /* set number of tabs to output */ if (spaces == 0) /* with a minimum of one */ spaces = 1; } for (i = 0; i < spaces; i++) TY_(WriteChar)(doc->indent_char, doc->docOut); /* 20150515 - Issue #108 */ } /** * Writes the current line up to the previously saved wrap point, and * shifts unwritten text in output buffer to the beginning of next line. */ static void WrapLine( TidyDocImpl* doc ) { TidyPrintImpl* pprint = &doc->pprint; uint i; if ( pprint->wraphere == 0 ) return; if ( WantIndent(doc) ) WriteIndentChar(doc); for ( i = 0; i < pprint->wraphere; ++i ) TY_(WriteChar)( pprint->linebuf[i], doc->docOut ); if ( IsWrapInString(pprint) ) TY_(WriteChar)( '\\', doc->docOut ); TY_(WriteChar)( '\n', doc->docOut ); pprint->line++; ResetLineAfterWrap( pprint ); } /** * Checks current output line length along with current indent. * If combined they overflow output line length, go ahead * and flush output up to the current wrap point. */ static Bool CheckWrapLine( TidyDocImpl* doc ) { TidyPrintImpl* pprint = &doc->pprint; if ( GetSpaces(pprint) + pprint->linelen >= cfg(doc, TidyWrapLen) ) { WrapLine( doc ); return yes; } return no; } /** * If the line, when idented, is ready to wrap, then wrap it. The return * value indicates whether or not the line was wrapped. */ static Bool CheckWrapIndent( TidyDocImpl* doc, uint indent ) { TidyPrintImpl* pprint = &doc->pprint; if ( GetSpaces(pprint) + pprint->linelen >= cfg(doc, TidyWrapLen) ) { WrapLine( doc ); if ( pprint->indent[ 0 ].spaces < 0 ) { #if defined(ENABLE_DEBUG_LOG) && defined(DEBUG_INDENT) SPRTF("%s Indent from %d to %d\n", __FUNCTION__, pprint->indent[ 0 ].spaces, indent ); #endif pprint->indent[ 0 ].spaces = indent; } return yes; } return no; } /** * Wraps an attribute value appropriately. */ static void WrapAttrVal( TidyDocImpl* doc ) { TidyPrintImpl* pprint = &doc->pprint; uint i; if ( WantIndent(doc) ) WriteIndentChar(doc); for ( i = 0; i < pprint->wraphere; ++i ) TY_(WriteChar)( pprint->linebuf[i], doc->docOut ); if ( IsWrapInString(pprint) ) TY_(WriteChar)( '\\', doc->docOut ); else TY_(WriteChar)( ' ', doc->docOut ); TY_(WriteChar)( '\n', doc->docOut ); pprint->line++; ResetLineAfterWrap( pprint ); } /****************************************************************************//* ** MARK: - Node Utilities ***************************************************************************/ /** * Indicates whether or not the given node has text content after it. */ static Bool HasMixedContent (Node *element) { Node * node; if (!element) return no; for (node = element->content; node; node = node->next) if ( TY_(nodeIsText)(node) ) return yes; return no; } /****************************************************************************//* ** MARK: - String Utilities ***************************************************************************/ /** * Adds a character to the print buffer at position `string_index`. */ static void AddC( TidyPrintImpl* pprint, uint c, uint string_index) { if ( string_index + 1 >= pprint->lbufsize ) expand( pprint, string_index + 1 ); pprint->linebuf[string_index] = c; } /** * Adds a character to the end of the print buffer. */ static uint AddChar( TidyPrintImpl* pprint, uint c ) { AddC( pprint, c, pprint->linelen ); return ++pprint->linelen; } /** * Adds an ASCII string to the print buffer at the given position. */ static uint AddAsciiString( TidyPrintImpl* pprint, ctmbstr str, uint string_index ) { uint ix, len = TY_(tmbstrlen)( str ); if ( string_index + len >= pprint->lbufsize ) expand( pprint, string_index + len ); for ( ix=0; ix* xy
* will display properly. Whereas ** x
won't. */ static Bool AfterSpace(Lexer *lexer, Node *node) { return AfterSpaceImp(lexer, node, TY_(nodeCMIsEmpty)(node)); } /** * Pretty prints a node's end tag. */ static void PPrintEndTag( TidyDocImpl* doc, uint ARG_UNUSED(mode), uint ARG_UNUSED(indent), Node *node ) { TidyPrintImpl* pprint = &doc->pprint; Bool uc = cfgBool( doc, TidyUpperCaseTags ); tmbstr s = node->element; tchar c; AddString( pprint, "" ); if (s) { while (*s) { c = (unsigned char)*s; if (c > 0x7F) s += TY_(GetUTF8)(s, &c); else if (uc) c = TY_(ToUpper)(c); AddChar(pprint, c); ++s; } } AddChar( pprint, '>' ); } /** * Prints the tag for the given node. */ static void PPrintTag( TidyDocImpl* doc, uint mode, uint indent, Node *node ) { TidyPrintImpl* pprint = &doc->pprint; Bool uc = cfgBool( doc, TidyUpperCaseTags ); Bool xhtmlOut = cfgBool( doc, TidyXhtmlOut ); Bool xmlOut = cfgBool( doc, TidyXmlOut ); tchar c; tmbstr s = node->element; AddChar( pprint, '<' ); if ( node->type == EndTag ) AddChar( pprint, '/' ); if (s) { while (*s) { c = (unsigned char)*s; if (c > 0x7F) s += TY_(GetUTF8)(s, &c); else if (uc) c = TY_(ToUpper)(c); AddChar(pprint, c); ++s; } } PPrintAttrs( doc, indent, node ); if ( (xmlOut || xhtmlOut) && (node->type == StartEndTag || TY_(nodeCMIsEmpty)(node)) ) { AddChar( pprint, ' ' ); /* Space is NS compatibility hack