From c579d5b62c9aa312f0ec939f9275a1f612d37a66 Mon Sep 17 00:00:00 2001 From: Jim Derry Date: Fri, 22 Sep 2017 19:01:31 -0400 Subject: [PATCH 1/6] Address #412 Add a TidyInfo message each time an unquoted attribute is found. However, refer to #412 for discussion before merging this. --- include/tidyenum.h | 1 + src/language_en.h | 1 + src/lexer.c | 13 +++++++------ src/message.c | 15 ++++++--------- 4 files changed, 15 insertions(+), 15 deletions(-) mode change 100644 => 100755 include/tidyenum.h mode change 100644 => 100755 src/language_en.h mode change 100644 => 100755 src/lexer.c diff --git a/include/tidyenum.h b/include/tidyenum.h old mode 100644 new mode 100755 index 4bbae26..2a261ed --- a/include/tidyenum.h +++ b/include/tidyenum.h @@ -220,6 +220,7 @@ extern "C" { FN(MISSING_ENDTAG_FOR) \ FN(MISSING_IMAGEMAP) \ FN(MISSING_QUOTEMARK) \ + FN(MISSING_QUOTEMARK_OPEN) \ FN(MISSING_SEMICOLON_NCR) \ FN(MISSING_SEMICOLON) \ FN(MISSING_STARTTAG) \ diff --git a/src/language_en.h b/src/language_en.h old mode 100644 new mode 100755 index addf8d1..a41e220 --- a/src/language_en.h +++ b/src/language_en.h @@ -1877,6 +1877,7 @@ static languageDefinition language_en = { whichPluralForm_en, { { MISSING_ENDTAG_FOR, 0, "missing " }, { MISSING_IMAGEMAP, 0, "%s should use client-side image map" }, { MISSING_QUOTEMARK, 0, "%s attribute with missing trailing quote mark" }, + { MISSING_QUOTEMARK_OPEN, 0, "value for attribute \"%s\" missing quote marks" }, { MISSING_SEMICOLON_NCR, 0, "numeric character reference \"%s\" doesn't end in ';'" }, { MISSING_SEMICOLON, 0, "entity \"%s\" doesn't end in ';'" }, { MISSING_STARTTAG, 0, "missing <%s>" }, diff --git a/src/lexer.c b/src/lexer.c old mode 100644 new mode 100755 index a23ed8e..b0fda1d --- a/src/lexer.c +++ b/src/lexer.c @@ -3621,8 +3621,9 @@ static Node *ParsePhp( TidyDocImpl* doc ) } /* consumes the '>' terminating start tags */ +/* @TODO: float the errors back to the calling method */ static tmbstr ParseAttribute( TidyDocImpl* doc, Bool *isempty, - Node **asp, Node **php) + Node **asp, Node **php ) { Lexer* lexer = doc->lexer; int start, len = 0; @@ -3984,8 +3985,6 @@ static tmbstr ParseValue( TidyDocImpl* doc, ctmbstr name, { uint q = c; - TY_(ReportAttrError)( doc, lexer->token, NULL, UNEXPECTED_QUOTEMARK ); - /* handle and ... */ /* this doesn't handle which browsers treat as */ /* 'foo"/' nor which browser treat as 'foo"' */ @@ -4156,7 +4155,7 @@ static tmbstr ParseValue( TidyDocImpl* doc, ctmbstr name, value = NULL; /* note delimiter if given */ - *pdelim = (delim ? delim : '"'); + *pdelim = delim; return value; } @@ -4273,11 +4272,13 @@ static AttVal* ParseAttrs( TidyDocImpl* doc, Bool *isempty ) (cfgBool(doc, TidyXmlTags) && IsValidXMLAttrName(attribute)))) { av = TY_(NewAttribute)(doc); - av->delim = delim; + av->delim = delim ? delim : '"'; av->attribute = attribute; av->value = value; av->dict = TY_(FindAttribute)( doc, av ); - AddAttrToList( &list, av ); + AddAttrToList( &list, av ); + if ( !delim && value ) + TY_(ReportAttrError)( doc, lexer->token, av, MISSING_QUOTEMARK_OPEN); } else { diff --git a/src/message.c b/src/message.c index f9149b4..e64a412 100755 --- a/src/message.c +++ b/src/message.c @@ -309,6 +309,7 @@ static struct _dispatchTable { { MISSING_ENDTAG_FOR, TidyWarning, formatStandard }, { MISSING_IMAGEMAP, TidyWarning, formatAttributeReport }, { MISSING_QUOTEMARK, TidyWarning, formatAttributeReport }, + { MISSING_QUOTEMARK_OPEN, TidyInfo, formatAttributeReport }, { MISSING_SEMICOLON_NCR, TidyWarning, formatStandard }, { MISSING_SEMICOLON, TidyWarning, formatStandard }, { MISSING_STARTTAG, TidyWarning, formatStandard }, @@ -531,7 +532,10 @@ TidyMessageImpl *formatAttributeReport(TidyDocImpl* doc, Node *element, Node *no switch (code) { - case BACKSLASH_IN_URI: + case MISSING_QUOTEMARK_OPEN: + return TY_(tidyMessageCreateWithNode)(doc, node, code, level, name ); + + case BACKSLASH_IN_URI: case ESCAPED_ILLEGAL_URI: case FIXED_BACKSLASH: case ID_NAME_MISMATCH: @@ -546,14 +550,12 @@ TidyMessageImpl *formatAttributeReport(TidyDocImpl* doc, Node *element, Node *no case UNEXPECTED_QUOTEMARK: case WHITE_IN_URI: return TY_(tidyMessageCreateWithNode)(doc, node, code, level, tagdesc ); - break; case ATTRIBUTE_IS_NOT_ALLOWED: case JOINING_ATTRIBUTE: case MISSING_ATTR_VALUE: case PROPRIETARY_ATTRIBUTE: return TY_(tidyMessageCreateWithNode)(doc, node, code, level, tagdesc, name ); - break; case ATTRIBUTE_VALUE_REPLACED: case BAD_ATTRIBUTE_VALUE: @@ -561,30 +563,25 @@ TidyMessageImpl *formatAttributeReport(TidyDocImpl* doc, Node *element, Node *no case INSERTING_AUTO_ATTRIBUTE: case INVALID_ATTRIBUTE: return TY_(tidyMessageCreateWithNode)(doc, node, code, level, tagdesc, name, value ); - break; case MISMATCHED_ATTRIBUTE_ERROR: case MISMATCHED_ATTRIBUTE_WARN: return TY_(tidyMessageCreateWithNode)(doc, node, code, level, tagdesc, name, HTMLVersion(doc)); - break; case ANCHOR_NOT_UNIQUE: case ATTR_VALUE_NOT_LCASE: case PROPRIETARY_ATTR_VALUE: case XML_ID_SYNTAX: return TY_(tidyMessageCreateWithNode)(doc, node, code, level, tagdesc, value ); - break; case REPEATED_ATTRIBUTE: return TY_(tidyMessageCreateWithNode)(doc, node, code, level, tagdesc, value, name ); - break; case UNEXPECTED_END_OF_FILE_ATTR: /* on end of file adjust reported position to end of input */ doc->lexer->lines = doc->docIn->curline; doc->lexer->columns = doc->docIn->curcol; return TY_(tidyMessageCreateWithLexer)(doc, code, level, tagdesc ); - break; } return NULL; @@ -1095,8 +1092,8 @@ void TY_(Dialogue)(TidyDocImpl* doc, uint code, ...) { if ( dialogueDispatchTable[i].code == code ) { - TidyMessageImpl *message; TidyReportLevel level = dialogueDispatchTable[i].level; + TidyMessageImpl *message; va_start(args, code); message = formatDialogue( doc, code, level, args ); va_end(args); From 64fb5640cbf5e1487d3f8bddfd61106d69651ee6 Mon Sep 17 00:00:00 2001 From: Jim Derry Date: Fri, 22 Sep 2017 19:27:47 -0400 Subject: [PATCH 2/6] MSVC snuck in some tab characters... --- src/lexer.c | 4 ++-- src/message.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lexer.c b/src/lexer.c index b0fda1d..4ff4388 100755 --- a/src/lexer.c +++ b/src/lexer.c @@ -4277,8 +4277,8 @@ static AttVal* ParseAttrs( TidyDocImpl* doc, Bool *isempty ) av->value = value; av->dict = TY_(FindAttribute)( doc, av ); AddAttrToList( &list, av ); - if ( !delim && value ) - TY_(ReportAttrError)( doc, lexer->token, av, MISSING_QUOTEMARK_OPEN); + if ( !delim && value ) + TY_(ReportAttrError)( doc, lexer->token, av, MISSING_QUOTEMARK_OPEN); } else { diff --git a/src/message.c b/src/message.c index e64a412..7debb33 100755 --- a/src/message.c +++ b/src/message.c @@ -535,7 +535,7 @@ TidyMessageImpl *formatAttributeReport(TidyDocImpl* doc, Node *element, Node *no case MISSING_QUOTEMARK_OPEN: return TY_(tidyMessageCreateWithNode)(doc, node, code, level, name ); - case BACKSLASH_IN_URI: + case BACKSLASH_IN_URI: case ESCAPED_ILLEGAL_URI: case FIXED_BACKSLASH: case ID_NAME_MISMATCH: @@ -1092,8 +1092,8 @@ void TY_(Dialogue)(TidyDocImpl* doc, uint code, ...) { if ( dialogueDispatchTable[i].code == code ) { + TidyMessageImpl *message; TidyReportLevel level = dialogueDispatchTable[i].level; - TidyMessageImpl *message; va_start(args, code); message = formatDialogue( doc, code, level, args ); va_end(args); From 23c28e5b82eb7466d3e8dab3b04ec661cc78bcca Mon Sep 17 00:00:00 2001 From: Jim Derry Date: Fri, 22 Sep 2017 22:14:59 -0400 Subject: [PATCH 3/6] Initial commit of #434; WIP --- include/tidyenum.h | 1 + src/attrs.c | 118 ++++++++++++++++++++++++++++++++++++++++++--- src/attrs.h | 12 +++++ src/config.c | 88 ++++++++++++++++++++++++++++++++- src/language_en.h | 16 ++++++ src/tidylib.c | 3 +- 6 files changed, 229 insertions(+), 9 deletions(-) diff --git a/include/tidyenum.h b/include/tidyenum.h index 2a261ed..6f4ff19 100755 --- a/include/tidyenum.h +++ b/include/tidyenum.h @@ -608,6 +608,7 @@ typedef enum TidyPPrintTabs, /**< Indent using tabs istead of spaces */ TidyPreserveEntities, /**< Preserve entities */ TidyPreTags, /**< Declared pre tags */ + TidyPriorityAttributes, /**< Attributes to place first in an element */ #if SUPPORT_ASIAN_ENCODINGS TidyPunctWrap, /**< consider punctuation and breaking spaces for wrapping */ #else diff --git a/src/attrs.c b/src/attrs.c index a4cb379..0286f55 100644 --- a/src/attrs.c +++ b/src/attrs.c @@ -929,6 +929,42 @@ AttVal* TY_(RepairAttrValue)(TidyDocImpl* doc, Node* node, ctmbstr name, ctmbstr return TY_(AddAttribute)(doc, node, name, value); } + +void TY_(DefinePriorityAttribute)(TidyDocImpl* doc, ctmbstr name) +{ + enum { capacity = 10 }; + PriorityAttribs *priorities = &(doc->attribs.priorityAttribs); + + /* @TODO: Don't forget to free this stuff */ + if ( !priorities->list ) + { + priorities->list = malloc( sizeof(ctmbstr) * capacity ); + priorities->list[0] = NULL; + priorities->capacity = capacity; + priorities->count = 0; + } + + if ( priorities->count >= priorities->capacity ) + { + priorities->capacity = priorities->capacity * 2; + priorities->list = realloc( priorities->list, sizeof(tmbstr) * priorities->capacity + 1 ); + } + + priorities->list[priorities->count] = TY_(tmbstrdup)( doc->allocator, name); + priorities->count++; + priorities->list[priorities->count] = NULL; + + uint i = 0; + while ( priorities->list[i] != NULL ) + { + printf("Array contains %s.\n", priorities->list[i]); + i++; + } + + printf("Adding %s to list.\n", name); +} + + static Bool CheckAttrType( TidyDocImpl* doc, ctmbstr attrname, AttrCheck type ) { @@ -2219,25 +2255,85 @@ void TY_(SortAttributes)(Node* node, TidyAttrSortStrategy strat) * SOFTWARE. */ -typedef int(*ptAttValComparator)(AttVal *one, AttVal *two); +typedef int(*ptAttValComparator)(AttVal *one, AttVal *two, ctmbstr *list); -/* Comparison function for TidySortAttrAlpha */ +/* Returns the index of the item in the array, or -1 if not in the array */ static -int AlphaComparator(AttVal *one, AttVal *two) +int indexof( ctmbstr item, ctmbstr *list ) { + uint i = 0; + while ( list[i] != NULL ) { + if ( TY_(tmbstrcasecmp)(item, list[i]) == 0 ) + return i; + i++; + } + + return -1; +} + +/* Comparison function for TidySortAttrAlpha. Will also consider items in + the passed in list as higher-priority, and will group them first. + */ +static +int AlphaComparator(AttVal *one, AttVal *two, ctmbstr *list) +{ + int oneIndex = indexof( one->attribute, list ); + int twoIndex = indexof( two->attribute, list ); + + /* If both on the list, the lower index has priority. */ + if ( oneIndex >= 0 && twoIndex >= 0 ) + return oneIndex < twoIndex ? -1 : 1; + + /* If A on the list but B not on the list, then A has priority. */ + if ( oneIndex >= 0 && twoIndex == -1 ) + return -1; + + /* If A not on the list but B is on the list, then B has priority. */ + if ( oneIndex == -1 && twoIndex >= 0 ) + return 1; + + /* Otherwise nothing is on the list, so just compare strings. */ return TY_(tmbstrcmp)(one->attribute, two->attribute); } +/* Comparison function for prioritizing list items. It doesn't otherwise + sort. + */ +static +int PriorityComparator(AttVal *one, AttVal *two, ctmbstr *list) +{ + int oneIndex = indexof( one->attribute, list ); + int twoIndex = indexof( two->attribute, list ); + + /* If both on the list, the lower index has priority. */ + if ( oneIndex >= 0 && twoIndex >= 0 ) + return oneIndex < twoIndex ? -1 : 1; + + /* If A on the list but B not on the list, then A has priority. */ + if ( oneIndex >= 0 && twoIndex == -1 ) + return -1; + + /* If A not on the list but B is on the list, then B has priority. */ + if ( oneIndex == -1 && twoIndex >= 0 ) + return 1; + + /* Otherwise nothing is on the list, so just mark them as the same. */ + return 0; +} + + /* The "factory method" that returns a pointer to the comparator function */ static -ptAttValComparator GetAttValComparator(TidyAttrSortStrategy strat) +ptAttValComparator GetAttValComparator(TidyAttrSortStrategy strat, ctmbstr *list) { switch (strat) { case TidySortAttrAlpha: return AlphaComparator; case TidySortAttrNone: + if ( list[0] ) + return PriorityComparator; break; } return 0; @@ -2247,7 +2343,13 @@ ptAttValComparator GetAttValComparator(TidyAttrSortStrategy strat) static AttVal *SortAttVal( AttVal *list, TidyAttrSortStrategy strat) { - ptAttValComparator ptComparator = GetAttValComparator(strat); + /* Get the list from the pass-in tidyDoc, which is a to-do. + We'll use this static list temporarily. + */ +// ctmbstr doc[] = { NULL }; + ctmbstr temp_list[] = { "id", "name", "class", NULL }; /* temp until option */ + + ptAttValComparator ptComparator = GetAttValComparator(strat, temp_list); AttVal *p, *q, *e, *tail; int insize, nmerges, psize, qsize, i; @@ -2258,6 +2360,10 @@ AttVal *SortAttVal( AttVal *list, TidyAttrSortStrategy strat) if (!list) return NULL; + /* If no comparator, return the list as is */ + if (ptComparator == 0) + return list; + insize = 1; while (1) { @@ -2291,7 +2397,7 @@ AttVal *SortAttVal( AttVal *list, TidyAttrSortStrategy strat) } else if (qsize == 0 || !q) { /* q is empty; e must come from p. */ e = p; p = p->next; psize--; - } else if (ptComparator(p,q) <= 0) { + } else if (ptComparator(p,q, temp_list) <= 0) { /* First element of p is lower (or same); * e must come from p. */ e = p; p = p->next; psize--; diff --git a/src/attrs.h b/src/attrs.h index 0192efc..36f3a63 100644 --- a/src/attrs.h +++ b/src/attrs.h @@ -60,6 +60,12 @@ enum ANCHOR_HASH_SIZE=1021u }; +typedef struct _priorityAttribs { + ctmbstr* list; + uint count; + uint capacity; +} PriorityAttribs; + struct _TidyAttribImpl { /* anchor/node lookup */ @@ -68,6 +74,9 @@ struct _TidyAttribImpl /* Declared literal attributes */ Attribute* declared_attr_list; + /* Prioritized list of attributes to write */ + PriorityAttribs priorityAttribs; + #if ATTRIBUTE_HASH_LOOKUP AttrHash* hashtab[ATTRIBUTE_HASH_SIZE]; #endif @@ -93,6 +102,9 @@ AttVal* TY_(AddAttribute)( TidyDocImpl* doc, AttVal* TY_(RepairAttrValue)(TidyDocImpl* doc, Node* node, ctmbstr name, ctmbstr value); +/* Add an item to the list of priority attributes to write first. */ +void TY_(DefinePriorityAttribute)(TidyDocImpl* doc, ctmbstr name); + Bool TY_(IsUrl)( TidyDocImpl* doc, ctmbstr attrname ); /* Bool IsBool( TidyDocImpl* doc, ctmbstr attrname ); */ diff --git a/src/config.c b/src/config.c index 64003ea..736473b 100644 --- a/src/config.c +++ b/src/config.c @@ -172,6 +172,9 @@ static PickListItems attributeCasePicks = { static void AdjustConfig( TidyDocImpl* doc ); +/* a space or comma separated list of attribute names */ +static ParseProperty ParseAttribNames; + /* parser for integer values */ static ParseProperty ParseInt; @@ -277,6 +280,7 @@ static const TidyOptionImpl option_defs[] = { TidyPPrintTabs, PP, "indent-with-tabs", BL, no, ParseTabs, &boolPicks }, /* 20150515 - Issue #108 */ { TidyPreserveEntities, MU, "preserve-entities", BL, no, ParsePickList, &boolPicks }, { TidyPreTags, MU, "new-pre-tags", ST, 0, ParseTagNames, NULL }, + { TidyPriorityAttributes, MU, "priority-attributes", ST, 0, ParseAttribNames, NULL }, #if SUPPORT_ASIAN_ENCODINGS { TidyPunctWrap, PP, "punctuation-wrap", BL, no, ParsePickList, &boolPicks }, #endif @@ -1087,6 +1091,88 @@ void AdjustConfig( TidyDocImpl* doc ) } } + +/* Coordinates Config update and Attributes data */ +void TY_(DeclarePriorityAttrib)( TidyDocImpl* doc, TidyOptionId optId, ctmbstr name ) +{ + ctmbstr prvval = cfgStr( doc, optId ); + tmbstr catval = NULL; + ctmbstr theval = name; + if ( prvval ) + { + uint len = TY_(tmbstrlen)(name) + TY_(tmbstrlen)(prvval) + 3; + catval = TY_(tmbstrndup)( doc->allocator, prvval, len ); + TY_(tmbstrcat)( catval, ", " ); + TY_(tmbstrcat)( catval, name ); + theval = catval; + } + + TY_(DefinePriorityAttribute)( doc, name ); + SetOptionValue( doc, optId, theval ); + if ( catval ) + TidyDocFree( doc, catval ); + } + +/* a space or comma separated list of attribute names */ +Bool ParseAttribNames( TidyDocImpl* doc, const TidyOptionImpl* option ) +{ + TidyConfigImpl* cfg = &doc->config; + tmbchar buf[1024]; + uint i = 0, nAttribs = 0; + uint c = SkipWhite( cfg ); + + + SetOptionValue( doc, option->id, NULL ); + + do + { + if (c == ' ' || c == '\t' || c == ',') + { + c = AdvanceChar( cfg ); + continue; + } + + if ( c == '\r' || c == '\n' ) + { + uint c2 = AdvanceChar( cfg ); + if ( c == '\r' && c2 == '\n' ) + c = AdvanceChar( cfg ); + else + c = c2; + + if ( !TY_(IsWhite)(c) ) + { + buf[i] = 0; + TY_(UngetChar)( c, cfg->cfgIn ); + TY_(UngetChar)( '\n', cfg->cfgIn ); + break; + } + } + + while ( i < sizeof(buf)-2 && c != EndOfStream && !TY_(IsWhite)(c) && c != ',' ) + { + buf[i++] = (tmbchar) c; + c = AdvanceChar( cfg ); + } + + buf[i] = '\0'; + if (i == 0) /* Skip empty attribute definition. Possible when */ + continue; /* there is a trailing space on the line. */ + + /* add attribute to array */ + TY_(DeclarePriorityAttrib)( doc, option->id, buf ); + i = 0; + ++nAttribs; + } + while ( c != EndOfStream ); + + if ( i > 0 ) + TY_(DeclarePriorityAttrib)( doc, option->id, buf ); + + return ( nAttribs > 0 ); +} + + /* unsigned integers */ Bool ParseInt( TidyDocImpl* doc, const TidyOptionImpl* entry ) { @@ -1351,9 +1437,9 @@ Bool ParseTagNames( TidyDocImpl* doc, const TidyOptionImpl* option ) return ( nTags > 0 ); } + /* a string including whitespace */ /* munges whitespace sequences */ - Bool ParseString( TidyDocImpl* doc, const TidyOptionImpl* option ) { TidyConfigImpl* cfg = &doc->config; diff --git a/src/language_en.h b/src/language_en.h index a41e220..82a9d99 100755 --- a/src/language_en.h +++ b/src/language_en.h @@ -1005,6 +1005,22 @@ static languageDefinition language_en = { whichPluralForm_en, { "
" "This option is ignored in XML mode. " }, + {/* Important notes for translators: + - Use only , , , , and +
. + - Entities, tags, attributes, etc., should be enclosed in . + - Option values should be enclosed in . + - It's very important that
be self-closing! + - The strings "Tidy" and "HTML Tidy" are the program name and must not + be translated. */ + TidyPriorityAttributes, 0, + "This option allows prioritizing the writing of attributes in tidied " + "documents, allowing them to written before the other attributes of an " + "element. For example, you might specify that id and " + "name are written before every other attribute. " + "
" + "This option takes a space or comma separated list of attribute names. " + }, #if SUPPORT_ASIAN_ENCODINGS {/* Important notes for translators: - Use only , , , , and diff --git a/src/tidylib.c b/src/tidylib.c index 162fe5d..96e2afb 100755 --- a/src/tidylib.c +++ b/src/tidylib.c @@ -2215,8 +2215,7 @@ int tidyDocSaveStream( TidyDocImpl* doc, StreamOut* out ) else TY_(ReplacePreformattedSpaces)(doc, &doc->root); - if ( sortAttrStrat != TidySortAttrNone ) - TY_(SortAttributes)(&doc->root, sortAttrStrat); + TY_(SortAttributes)(&doc->root, sortAttrStrat); if ( showMarkup && (doc->errors == 0 || forceOutput) ) { From 70681131d61c6246302f1ff24d12c2d95081918a Mon Sep 17 00:00:00 2001 From: Jim Derry Date: Sat, 23 Sep 2017 07:20:31 -0400 Subject: [PATCH 4/6] New config option now drives the list; memory deallocated. --- src/attrs.c | 69 +++++++++++++++++++++++++++++---------------------- src/attrs.h | 7 ++++-- src/tidylib.c | 3 ++- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/attrs.c b/src/attrs.c index 0286f55..adb2695 100644 --- a/src/attrs.c +++ b/src/attrs.c @@ -930,15 +930,31 @@ AttVal* TY_(RepairAttrValue)(TidyDocImpl* doc, Node* node, ctmbstr name, ctmbstr } +void TY_(FreeAttrPriorityList)( TidyDocImpl* doc ) +{ + PriorityAttribs *priorities = &(doc->attribs.priorityAttribs); + + if ( priorities->list ) + { + uint i = 0; + while ( priorities->list[i] != NULL ) + { + TidyFree( doc->allocator, priorities->list[i] ); + i++; + } + + TidyFree( doc->allocator, priorities->list ); + } +} + void TY_(DefinePriorityAttribute)(TidyDocImpl* doc, ctmbstr name) { enum { capacity = 10 }; PriorityAttribs *priorities = &(doc->attribs.priorityAttribs); - /* @TODO: Don't forget to free this stuff */ if ( !priorities->list ) { - priorities->list = malloc( sizeof(ctmbstr) * capacity ); + priorities->list = TidyAlloc(doc->allocator, sizeof(ctmbstr) * capacity ); priorities->list[0] = NULL; priorities->capacity = capacity; priorities->count = 0; @@ -953,15 +969,6 @@ void TY_(DefinePriorityAttribute)(TidyDocImpl* doc, ctmbstr name) priorities->list[priorities->count] = TY_(tmbstrdup)( doc->allocator, name); priorities->count++; priorities->list[priorities->count] = NULL; - - uint i = 0; - while ( priorities->list[i] != NULL ) - { - printf("Array contains %s.\n", priorities->list[i]); - i++; - } - - printf("Adding %s to list.\n", name); } @@ -2210,15 +2217,15 @@ void CheckType( TidyDocImpl* doc, Node *node, AttVal *attval) } static -AttVal *SortAttVal( AttVal* list, TidyAttrSortStrategy strat ); +AttVal *SortAttVal( TidyDocImpl* doc, AttVal* list, TidyAttrSortStrategy strat ); -void TY_(SortAttributes)(Node* node, TidyAttrSortStrategy strat) +void TY_(SortAttributes)(TidyDocImpl* doc, Node* node, TidyAttrSortStrategy strat) { while (node) { - node->attributes = SortAttVal( node->attributes, strat ); + node->attributes = SortAttVal( doc, node->attributes, strat ); if (node->content) - TY_(SortAttributes)(node->content, strat); + TY_(SortAttributes)(doc, node->content, strat); node = node->next; } } @@ -2261,11 +2268,14 @@ typedef int(*ptAttValComparator)(AttVal *one, AttVal *two, ctmbstr *list); static int indexof( ctmbstr item, ctmbstr *list ) { - uint i = 0; - while ( list[i] != NULL ) { - if ( TY_(tmbstrcasecmp)(item, list[i]) == 0 ) - return i; - i++; + if ( list ) + { + uint i = 0; + while ( list[i] != NULL ) { + if ( TY_(tmbstrcasecmp)(item, list[i]) == 0 ) + return i; + i++; + } } return -1; @@ -2332,7 +2342,7 @@ ptAttValComparator GetAttValComparator(TidyAttrSortStrategy strat, ctmbstr *list case TidySortAttrAlpha: return AlphaComparator; case TidySortAttrNone: - if ( list[0] ) + if ( list && list[0] ) return PriorityComparator; break; } @@ -2341,15 +2351,14 @@ ptAttValComparator GetAttValComparator(TidyAttrSortStrategy strat, ctmbstr *list /* The sort routine */ static -AttVal *SortAttVal( AttVal *list, TidyAttrSortStrategy strat) +AttVal *SortAttVal( TidyDocImpl* doc, AttVal *list, TidyAttrSortStrategy strat) { - /* Get the list from the pass-in tidyDoc, which is a to-do. - We'll use this static list temporarily. - */ -// ctmbstr doc[] = { NULL }; - ctmbstr temp_list[] = { "id", "name", "class", NULL }; /* temp until option */ + /* Get the list from the passed-in tidyDoc. */ +// ctmbstr* priorityList = (ctmbstr*)doc->attribs.priorityAttribs.list; +// ctmbstr priorityList[] = { "id", NULL }; + ctmbstr* priorityList = (ctmbstr*)doc->attribs.priorityAttribs.list; - ptAttValComparator ptComparator = GetAttValComparator(strat, temp_list); + ptAttValComparator ptComparator = GetAttValComparator(strat, priorityList); AttVal *p, *q, *e, *tail; int insize, nmerges, psize, qsize, i; @@ -2360,7 +2369,7 @@ AttVal *SortAttVal( AttVal *list, TidyAttrSortStrategy strat) if (!list) return NULL; - /* If no comparator, return the list as is */ + /* If no comparator, return the list as-is */ if (ptComparator == 0) return list; @@ -2397,7 +2406,7 @@ AttVal *SortAttVal( AttVal *list, TidyAttrSortStrategy strat) } else if (qsize == 0 || !q) { /* q is empty; e must come from p. */ e = p; p = p->next; psize--; - } else if (ptComparator(p,q, temp_list) <= 0) { + } else if (ptComparator(p,q, priorityList) <= 0) { /* First element of p is lower (or same); * e must come from p. */ e = p; p = p->next; psize--; diff --git a/src/attrs.h b/src/attrs.h index 36f3a63..22b4041 100644 --- a/src/attrs.h +++ b/src/attrs.h @@ -60,8 +60,9 @@ enum ANCHOR_HASH_SIZE=1021u }; +/* Keeps a list of attributes that are sorted ahead of the others. */ typedef struct _priorityAttribs { - ctmbstr* list; + tmbstr* list; uint count; uint capacity; } PriorityAttribs; @@ -144,13 +145,15 @@ void TY_(FreeAnchors)( TidyDocImpl* doc ); void TY_(InitAttrs)( TidyDocImpl* doc ); void TY_(FreeAttrTable)( TidyDocImpl* doc ); +void TY_(FreeAttrPriorityList)( TidyDocImpl* doc ); + void TY_(AppendToClassAttr)( TidyDocImpl* doc, AttVal *classattr, ctmbstr classname ); /* the same attribute name can't be used more than once in each element */ void TY_(RepairDuplicateAttributes)( TidyDocImpl* doc, Node* node, Bool isXml ); -void TY_(SortAttributes)(Node* node, TidyAttrSortStrategy strat); +void TY_(SortAttributes)(TidyDocImpl* doc, Node* node, TidyAttrSortStrategy strat); Bool TY_(IsBoolAttribute)( AttVal* attval ); Bool TY_(attrIsEvent)( AttVal* attval ); diff --git a/src/tidylib.c b/src/tidylib.c index 96e2afb..c6104eb 100755 --- a/src/tidylib.c +++ b/src/tidylib.c @@ -181,6 +181,7 @@ void tidyDocRelease( TidyDocImpl* doc ) TY_(FreeConfig)( doc ); TY_(FreeAttrTable)( doc ); + TY_(FreeAttrPriorityList)( doc ); TY_(FreeTags)( doc ); /*\ * Issue #186 - Now FreeNode depend on the doctype, so the lexer is needed @@ -2215,7 +2216,7 @@ int tidyDocSaveStream( TidyDocImpl* doc, StreamOut* out ) else TY_(ReplacePreformattedSpaces)(doc, &doc->root); - TY_(SortAttributes)(&doc->root, sortAttrStrat); + TY_(SortAttributes)(doc, &doc->root, sortAttrStrat); if ( showMarkup && (doc->errors == 0 || forceOutput) ) { From 64a4302e6cd98d019726b76e96eee616f80c12dc Mon Sep 17 00:00:00 2001 From: Jim Derry Date: Sat, 23 Sep 2017 07:30:17 -0400 Subject: [PATCH 5/6] Migrated the attribute list parser to a general parser for future re-used by other potential list-based options. --- src/config.c | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/config.c b/src/config.c index 736473b..8de74c6 100644 --- a/src/config.c +++ b/src/config.c @@ -172,12 +172,12 @@ static PickListItems attributeCasePicks = { static void AdjustConfig( TidyDocImpl* doc ); -/* a space or comma separated list of attribute names */ -static ParseProperty ParseAttribNames; - /* parser for integer values */ static ParseProperty ParseInt; +/* a space or comma separated list */ +static ParseProperty ParseList; + /* a string excluding whitespace */ static ParseProperty ParseName; @@ -280,7 +280,7 @@ static const TidyOptionImpl option_defs[] = { TidyPPrintTabs, PP, "indent-with-tabs", BL, no, ParseTabs, &boolPicks }, /* 20150515 - Issue #108 */ { TidyPreserveEntities, MU, "preserve-entities", BL, no, ParsePickList, &boolPicks }, { TidyPreTags, MU, "new-pre-tags", ST, 0, ParseTagNames, NULL }, - { TidyPriorityAttributes, MU, "priority-attributes", ST, 0, ParseAttribNames, NULL }, + { TidyPriorityAttributes, MU, "priority-attributes", ST, 0, ParseList, NULL }, #if SUPPORT_ASIAN_ENCODINGS { TidyPunctWrap, PP, "punctuation-wrap", BL, no, ParsePickList, &boolPicks }, #endif @@ -1092,7 +1092,9 @@ void AdjustConfig( TidyDocImpl* doc ) } -/* Coordinates Config update and Attributes data */ +/* Coordinates Config update and Attributes data for priority attributes, as + a service to ParseList(). + */ void TY_(DeclarePriorityAttrib)( TidyDocImpl* doc, TidyOptionId optId, ctmbstr name ) { ctmbstr prvval = cfgStr( doc, optId ); @@ -1114,14 +1116,13 @@ void TY_(DeclarePriorityAttrib)( TidyDocImpl* doc, TidyOptionId optId, ctmbstr n } /* a space or comma separated list of attribute names */ -Bool ParseAttribNames( TidyDocImpl* doc, const TidyOptionImpl* option ) +Bool ParseList( TidyDocImpl* doc, const TidyOptionImpl* option ) { TidyConfigImpl* cfg = &doc->config; tmbchar buf[1024]; - uint i = 0, nAttribs = 0; + uint i = 0, nItems = 0; uint c = SkipWhite( cfg ); - SetOptionValue( doc, option->id, NULL ); do @@ -1160,16 +1161,33 @@ Bool ParseAttribNames( TidyDocImpl* doc, const TidyOptionImpl* option ) continue; /* there is a trailing space on the line. */ /* add attribute to array */ - TY_(DeclarePriorityAttrib)( doc, option->id, buf ); + switch ( option->id ) + { + case TidyPriorityAttributes: + TY_(DeclarePriorityAttrib)( doc, option->id, buf ); + break; + + default: + break; + } + i = 0; - ++nAttribs; + ++nItems; } while ( c != EndOfStream ); if ( i > 0 ) - TY_(DeclarePriorityAttrib)( doc, option->id, buf ); + switch ( option->id ) + { + case TidyPriorityAttributes: + TY_(DeclarePriorityAttrib)( doc, option->id, buf ); + break; - return ( nAttribs > 0 ); + default: + break; + } + + return ( nItems > 0 ); } From 20a1b7bb69a76581ee35a31384c5834c828c1ca7 Mon Sep 17 00:00:00 2001 From: Jim Derry Date: Sat, 23 Sep 2017 07:53:57 -0400 Subject: [PATCH 6/6] Updated documentation relating to new option. --- console/tidy.c | 6 ++++++ include/tidy.h | 2 +- localize/translations/tidy.pot | 29 ++++++++++++++++++++++++++++- src/language_en.h | 4 ++++ src/message.c | 2 ++ 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/console/tidy.c b/console/tidy.c index 0d6179b..d340c86 100755 --- a/console/tidy.c +++ b/console/tidy.c @@ -576,6 +576,12 @@ static void GetOption(TidyDoc tdoc, /**< The tidy document. */ d->vals = "tagX, tagY, ..."; d->def = NULL; break; + + case TidyPriorityAttributes: + d->type = "Attributes Names"; + d->vals = "attributeX, attributeY, ..."; + d->def = NULL; + break; case TidyCharEncoding: case TidyInCharEncoding: diff --git a/include/tidy.h b/include/tidy.h index 29ccc09..3b5762a 100644 --- a/include/tidy.h +++ b/include/tidy.h @@ -925,7 +925,7 @@ TIDY_EXPORT TidyIterator TIDY_CALL tidyOptGetDocLinksList(TidyDoc tdoc, /**< T /** Given a valid TidyIterator initiated with tidyOptGetDocLinksList(), returns ** a TidyOption instance. - ** @result Returns in instane of TidyOption. + ** @result Returns in instance of TidyOption. */ TIDY_EXPORT TidyOption TIDY_CALL tidyOptGetNextDocLinks(TidyDoc tdoc, /**< The tidy document to query. */ TidyIterator* pos /**< The TidyIterator (initiated with tidyOptGetDocLinksList()) token. */ diff --git a/localize/translations/tidy.pot b/localize/translations/tidy.pot index 27a3879..3a5c734 100644 --- a/localize/translations/tidy.pot +++ b/localize/translations/tidy.pot @@ -5,7 +5,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: HTML Tidy poconvert.rb\n" "Project-Id-Version: \n" -"POT-Creation-Date: 2017-09-19 14:00:03\n" +"POT-Creation-Date: 2017-09-23 07:53:22\n" "Last-Translator: jderry\n" "Language-Team: \n" @@ -1055,6 +1055,24 @@ msgid "" "This option is ignored in XML mode. " msgstr "" +#. Important notes for translators: +#. - Use only , , , , and +#.
. +#. - Entities, tags, attributes, etc., should be enclosed in . +#. - Option values should be enclosed in . +#. - It's very important that
be self-closing! +#. - The strings "Tidy" and "HTML Tidy" are the program name and must not +#. be translated. +msgctxt "TidyPriorityAttributes" +msgid "" +"This option allows prioritizing the writing of attributes in tidied " +"documents, allowing them to written before the other attributes of an " +"element. For example, you might specify that id and " +"name are written before every other attribute. " +"
" +"This option takes a space or comma separated list of attribute names. " +msgstr "" + #. Important notes for translators: #. - Use only , , , , and #.
. @@ -1226,6 +1244,10 @@ msgid "" "This option specifies that Tidy should sort attributes within an element " "using the specified sort algorithm. If set to alpha, the " "algorithm is an ascending alphabetic sort. " +"
" +"When used while sorting with priority-attributes, any " +"attribute sorting will take place after the priority attributes have " +"been output. " msgstr "" #. Important notes for translators: @@ -2329,6 +2351,11 @@ msgctxt "MISSING_QUOTEMARK" msgid "%s attribute with missing trailing quote mark" msgstr "" +#, c-format +msgctxt "MISSING_QUOTEMARK_OPEN" +msgid "value for attribute \"%s\" missing quote marks" +msgstr "" + #, c-format msgctxt "MISSING_SEMICOLON_NCR" msgid "numeric character reference \"%s\" doesn't end in ';'" diff --git a/src/language_en.h b/src/language_en.h index 82a9d99..18db40c 100755 --- a/src/language_en.h +++ b/src/language_en.h @@ -1172,6 +1172,10 @@ static languageDefinition language_en = { whichPluralForm_en, { "This option specifies that Tidy should sort attributes within an element " "using the specified sort algorithm. If set to alpha, the " "algorithm is an ascending alphabetic sort. " + "
" + "When used while sorting with priority-attributes, any " + "attribute sorting will take place after the priority attributes have " + "been output. " }, {/* Important notes for translators: - Use only , , , , and diff --git a/src/message.c b/src/message.c index 7debb33..409f571 100755 --- a/src/message.c +++ b/src/message.c @@ -1424,6 +1424,7 @@ static const TidyOptionId TidyNumEntitiesLinks[] = { TidyDoctype, TidyPreser static const TidyOptionId TidyOutCharEncodingLinks[] = { TidyCharEncoding, TidyUnknownOption }; static const TidyOptionId TidyOutFileLinks[] = { TidyErrFile, TidyUnknownOption }; static const TidyOptionId TidyPreTagsLinks[] = { TidyBlockTags, TidyEmptyTags, TidyInlineTags, TidyUseCustomTags, TidyUnknownOption }; +static const TidyOptionId TidySortAttributesLinks[] = { TidyPriorityAttributes, TidyUnknownOption }; static const TidyOptionId TidyUseCustomTagsLinks[] = { TidyBlockTags, TidyEmptyTags, TidyInlineTags, TidyPreTags, TidyUnknownOption }; static const TidyOptionId TidyWrapAttValsLinks[] = { TidyWrapScriptlets, TidyLiteralAttribs, TidyUnknownOption }; static const TidyOptionId TidyWrapScriptletsLinks[] = { TidyWrapAttVals, TidyUnknownOption }; @@ -1451,6 +1452,7 @@ static const TidyOptionDoc docs_xrefs[] = { TidyOutCharEncoding, TidyOutCharEncodingLinks }, { TidyOutFile, TidyOutFileLinks }, { TidyPreTags, TidyPreTagsLinks }, + { TidySortAttributes, TidySortAttributesLinks }, { TidyUseCustomTags, TidyUseCustomTagsLinks }, { TidyWrapAttVals, TidyWrapAttValsLinks }, { TidyWrapScriptlets, TidyWrapScriptletsLinks },