tidy-html5/src/message.c
Jim Derry 51e2e0f3bd Following the example of the recent changes in the "reports" aspect of Tidy's
output, classify and organize all of the dialogue type of messages. This paves
the way towards formalizing (and expanding!) the footnotes system with much
greater explanatory text, as well as providing much better fine-grained control
over which types of output that Tidy will produce.

Moved STRING_DOCTYPE_GIVEN, STRING_CONTENT_LOOKS, and STRING_NO_SYSID to the
Report paradigm from the Dialogue paradigm, as these are items that are
traditionally TidyInfo and included in the Report table, rather than any type
of dialogue.

At this point, we are exactly passing all tests.
2017-09-19 13:52:27 -04:00

1489 lines
60 KiB
C
Executable file

/* message.c -- General Message Writing Routines
(c) 1998-2017 (W3C) MIT, ERCIM, Keio University, University of
Toronto, HTACG
See tidy.h for the copyright notice.
*/
#include "message.h"
#include "messageobj.h"
#include "limits.h"
#include "tidy-int.h"
#include "lexer.h"
#include "streamio.h"
#include "tmbstr.h"
#include "utf8.h"
#if !defined(NDEBUG) && defined(_MSC_VER)
#include "sprtf.h"
#endif
#include "version.h"
/*********************************************************************
* Release Information
*********************************************************************/
ctmbstr TY_(ReleaseDate)(void)
{
return TY_(release_date);
}
ctmbstr TY_(tidyLibraryVersion)(void)
{
return TY_(library_version);
}
/*********************************************************************
* General Message Utility Functions
*********************************************************************/
/* Returns the given node's tag as a string. */
static char* TagToString(Node* tag, char* buf, size_t count)
{
*buf = 0;
if (tag)
{
if (TY_(nodeIsElement)(tag))
TY_(tmbsnprintf)(buf, count, "<%s>", tag->element);
else if (tag->type == EndTag)
TY_(tmbsnprintf)(buf, count, "</%s>", tag->element);
else if (tag->type == DocTypeTag)
TY_(tmbsnprintf)(buf, count, "<!DOCTYPE>");
else if (tag->type == TextNode)
TY_(tmbsnprintf)(buf, count, "%s", tidyLocalizedString(STRING_PLAIN_TEXT));
else if (tag->type == XmlDecl)
TY_(tmbsnprintf)(buf, count, "%s", tidyLocalizedString(STRING_XML_DECLARATION));
else if (tag->element)
TY_(tmbsnprintf)(buf, count, "%s", tag->element);
}
return buf + TY_(tmbstrlen)(buf);
}
/* Convert an integer to a string. */
static void NtoS(int n, tmbstr str)
{
tmbchar buf[40];
int i;
for (i = 0;; ++i)
{
buf[i] = (tmbchar)( (n % 10) + '0' );
n = n / 10;
if (n == 0)
break;
}
n = i;
while (i >= 0)
{
str[n-i] = buf[i];
--i;
}
str[n+1] = '\0';
}
/* Get an HTML version string */
static ctmbstr HTMLVersion( TidyDocImpl* doc )
{
uint versionEmitted = doc->lexer->versionEmitted;
uint declared = doc->lexer->doctype;
uint version = versionEmitted == 0 ? declared : versionEmitted;
ctmbstr result = TY_(HTMLVersionNameFromCode)(version, 0);
if (!result)
result = tidyLocalizedString(STRING_HTML_PROPRIETARY);
return result;
}
/*********************************************************************
* Message Writing Functions
* These funtions provide final, formatted output to the output sink.
*********************************************************************/
/* Writes messages to the output sink unless they are suppressed by one of the
** message callback filters, or suppressed by the configuration settings.
** Report messages are messages that are included in the "error table," and
** dialogue messages are any other output that Tidy traditionally emits.
*/
static void messageOut( TidyMessageImpl *message )
{
TidyDocImpl *doc;
Bool go = yes;
if ( !message )
return;
doc = message->tidyDoc;
/* The filter has had a chance to suppress *any* message from output. */
go = message->allowMessage;
/* Update the count of each report type. */
switch ( message->level )
{
case TidyInfo:
doc->infoMessages++;
break;
case TidyWarning:
doc->warnings++;
break;
case TidyConfig:
doc->optionErrors++;
break;
case TidyAccess:
doc->accessErrors++;
break;
case TidyError:
doc->errors++;
break;
case TidyBadDocument:
doc->docErrors++;
break;
case TidyFatal:
/* Ack! */
break;
default:
break;
}
/* Suppress report messages if we've already reached the reporting limit. */
if ( message->level <= TidyFatal )
{
go = go & ( doc->errors < cfg(doc, TidyShowErrors) );
}
// /* Suppress TidyInfo on Reports if applicable. */
// if ( message->level == TidyInfo )
// {
// go = go & (cfgBool(doc, TidyShowInfo) == yes);
// }
/* If suppressing TidyInfo/TidyDialogueInfo on Reports, suppress them. */
if ( message->level == TidyInfo || message->level == TidyDialogueInfo )
{
go = go & (cfgBool(doc, TidyShowInfo) == yes);
}
/* Suppress TidyWarning on Reports if applicable. */
if ( message->level == TidyWarning )
{
go = go & (cfgBool(doc, TidyShowWarnings) == yes);
}
/* If we're TidyQuiet and handling any type of dialogue above summaries,
then suppress. */
if ( message->level > TidyDialogueSummary )
{
go = go & !cfgBool(doc, TidyQuiet);
}
/* Output the message if applicable. */
if ( go )
{
TidyOutputSink *outp = &doc->errout->sink;
ctmbstr cp;
byte b = '\0';
for ( cp = message->messageOutput; *cp; ++cp )
{
b = (*cp & 0xff);
if (b == (byte)'\n')
TY_(WriteChar)( b, doc->errout ); /* for EOL translation */
else
outp->putByte( outp->sinkData, b ); /* #383 - no encoding */
}
/* Always add a trailing newline. Reports require this, and dialogue
messages will be better spaced out without having to fill the
language file with superflous newlines. */
TY_(WriteChar)( '\n', doc->errout );
}
TY_(tidyMessageRelease)(message);
}
/*********************************************************************
* Report Formatting
* In order to provide a single, predictable reporting system, Tidy
* provides an extensible messaging system that provides most of the
* basic requirements for most reports, while permitting simple
* implementation of new reports in a flexible manner. By adding
* additional formatters, new messages can be added easily while
* maintaining Tidy's internal organization.
*********************************************************************/
/* Functions of this type will create new instances of TidyMessage specific to
** the type of report being emitted. Many messages share the same fomatter for
** messages, but new ones can be written as required. Please have a look at
** the existing formatters in order to determine if an existing signature is
** compatible with the report you wish to output before adding a new formatter.
** In particular, if an existing formatter provides most of the local variables
** required to populate your format string, try to use it.
*/
typedef TidyMessageImpl*(messageFormatter)(TidyDocImpl* doc, Node *element, Node *node, uint code, uint level, va_list args);
/* Forward declarations of messageFormatter functions. */
static messageFormatter formatAccessReport;
static messageFormatter formatAttributeReport;
static messageFormatter formatEncodingReport;
static messageFormatter formatStandard;
static messageFormatter formatStandardDynamic;
/* This structure ties together for each report Code the default
** TidyReportLevel, the Formatter to be used to construct the message, and the
** next code to output, if applicable. Assuming an existing formatter can,
** this it makes it simple to output new reports, or to change report level by
** modifying this array.
*/
static struct _dispatchTable {
uint code; /**< The message code. */
TidyReportLevel level; /**< The default TidyReportLevel of the message. */
messageFormatter *handler; /**< The formatter for the report. */
uint next; /**< If multiple codes should be displayed, which is next? */
} dispatchTable[] = {
{ ADDED_MISSING_CHARSET, TidyInfo, formatStandard },
{ ANCHOR_NOT_UNIQUE, TidyWarning, formatAttributeReport },
{ APOS_UNDEFINED, TidyWarning, formatStandard },
{ ATTR_VALUE_NOT_LCASE, TidyWarning, formatAttributeReport },
{ ATTRIBUTE_VALUE_REPLACED, TidyInfo, formatAttributeReport },
{ ATTRIBUTE_IS_NOT_ALLOWED, TidyWarning, formatAttributeReport },
{ BACKSLASH_IN_URI, TidyWarning, formatAttributeReport },
{ BAD_ATTRIBUTE_VALUE_REPLACED, TidyWarning, formatAttributeReport },
{ BAD_ATTRIBUTE_VALUE, TidyWarning, formatAttributeReport },
{ BAD_CDATA_CONTENT, TidyWarning, formatStandard },
{ BAD_SUMMARY_HTML5, TidyWarning, formatStandard },
{ BAD_SURROGATE_LEAD, TidyWarning, formatStandard },
{ BAD_SURROGATE_PAIR, TidyWarning, formatStandard },
{ BAD_SURROGATE_TAIL, TidyWarning, formatStandard },
{ CANT_BE_NESTED, TidyWarning, formatStandard },
{ COERCE_TO_ENDTAG, TidyWarning, formatStandard },
{ CONTENT_AFTER_BODY, TidyWarning, formatStandard },
{ CUSTOM_TAG_DETECTED, TidyInfo, formatStandard },
{ DISCARDING_UNEXPECTED, 0, formatStandardDynamic },
{ DOCTYPE_AFTER_TAGS, TidyWarning, formatStandard },
{ DUPLICATE_FRAMESET, TidyError, formatStandard },
{ ELEMENT_NOT_EMPTY, TidyWarning, formatStandard },
{ ELEMENT_VERS_MISMATCH_ERROR, TidyError, formatStandard },
{ ELEMENT_VERS_MISMATCH_WARN, TidyWarning, formatStandard },
{ ENCODING_MISMATCH, TidyWarning, formatEncodingReport },
{ ESCAPED_ILLEGAL_URI, TidyWarning, formatAttributeReport },
{ FILE_CANT_OPEN, TidyBadDocument, formatStandard },
{ FILE_CANT_OPEN_CFG, TidyBadDocument, formatStandard },
{ FILE_NOT_FILE, TidyBadDocument, formatStandard },
{ FIXED_BACKSLASH, TidyWarning, formatAttributeReport },
{ FOUND_STYLE_IN_BODY, TidyWarning, formatStandard },
{ ID_NAME_MISMATCH, TidyWarning, formatAttributeReport },
{ ILLEGAL_NESTING, TidyWarning, formatStandard },
{ ILLEGAL_URI_CODEPOINT, TidyWarning, formatAttributeReport },
{ ILLEGAL_URI_REFERENCE, TidyWarning, formatAttributeReport },
{ INSERTING_AUTO_ATTRIBUTE, TidyWarning, formatAttributeReport },
{ INSERTING_TAG, TidyWarning, formatStandard },
{ INVALID_ATTRIBUTE, TidyWarning, formatAttributeReport },
{ INVALID_NCR, TidyWarning, formatEncodingReport },
{ INVALID_SGML_CHARS, TidyWarning, formatEncodingReport },
{ INVALID_UTF8, TidyWarning, formatEncodingReport },
{ INVALID_UTF16, TidyWarning, formatEncodingReport },
{ INVALID_XML_ID, TidyWarning, formatAttributeReport },
{ JOINING_ATTRIBUTE, TidyWarning, formatAttributeReport },
{ MALFORMED_COMMENT, TidyWarning, formatStandard },
{ MALFORMED_DOCTYPE, TidyWarning, formatStandard },
{ MISMATCHED_ATTRIBUTE_ERROR, TidyError, formatAttributeReport },
{ MISMATCHED_ATTRIBUTE_WARN, TidyWarning, formatAttributeReport },
{ MISSING_ATTR_VALUE, TidyWarning, formatAttributeReport },
{ MISSING_ATTRIBUTE, TidyWarning, formatStandard },
{ MISSING_DOCTYPE, TidyWarning, formatStandard },
{ MISSING_ENDTAG_BEFORE, TidyWarning, formatStandard },
{ MISSING_ENDTAG_FOR, TidyWarning, formatStandard },
{ MISSING_IMAGEMAP, TidyWarning, formatAttributeReport },
{ MISSING_QUOTEMARK, TidyWarning, formatAttributeReport },
{ MISSING_SEMICOLON_NCR, TidyWarning, formatStandard },
{ MISSING_SEMICOLON, TidyWarning, formatStandard },
{ MISSING_STARTTAG, TidyWarning, formatStandard },
{ MISSING_TITLE_ELEMENT, TidyWarning, formatStandard },
{ MOVED_STYLE_TO_HEAD, TidyWarning, formatStandard },
{ NESTED_EMPHASIS, TidyWarning, formatStandard },
{ NESTED_QUOTATION, TidyWarning, formatStandard },
{ NEWLINE_IN_URI, TidyWarning, formatAttributeReport },
{ NOFRAMES_CONTENT, TidyWarning, formatStandard },
{ NON_MATCHING_ENDTAG, TidyWarning, formatStandard },
{ OBSOLETE_ELEMENT, TidyWarning, formatStandard },
{ PREVIOUS_LOCATION, TidyInfo, formatStandard },
{ PROPRIETARY_ATTR_VALUE, TidyWarning, formatAttributeReport },
{ PROPRIETARY_ATTRIBUTE, TidyWarning, formatAttributeReport },
{ PROPRIETARY_ELEMENT, TidyWarning, formatStandard },
{ REMOVED_HTML5, TidyWarning, formatStandard },
{ REPEATED_ATTRIBUTE, TidyWarning, formatAttributeReport },
{ REPLACING_ELEMENT, TidyWarning, formatStandard },
{ REPLACING_UNEX_ELEMENT, TidyWarning, formatStandard },
{ SPACE_PRECEDING_XMLDECL, TidyWarning, formatStandard },
{ STRING_CONTENT_LOOKS, TidyInfo, formatStandard }, /* reportMarkupVersion() */
{ STRING_DOCTYPE_GIVEN, TidyInfo, formatStandard }, /* reportMarkupVersion() */
{ STRING_MISSING_MALFORMED, TidyConfig, formatStandard },
{ STRING_NO_SYSID, TidyInfo, formatStandard }, /* reportMarkupVersion() */
{ STRING_UNKNOWN_OPTION, TidyConfig, formatStandard },
{ SUSPECTED_MISSING_QUOTE, TidyError, formatStandard },
{ TAG_NOT_ALLOWED_IN, TidyWarning, formatStandard, PREVIOUS_LOCATION },
{ TOO_MANY_ELEMENTS_IN, TidyWarning, formatStandard, PREVIOUS_LOCATION },
{ TOO_MANY_ELEMENTS, TidyWarning, formatStandard },
{ TRIM_EMPTY_ELEMENT, TidyWarning, formatStandard },
{ UNESCAPED_AMPERSAND, TidyWarning, formatStandard },
{ UNEXPECTED_END_OF_FILE_ATTR, TidyWarning, formatAttributeReport },
{ UNEXPECTED_END_OF_FILE, TidyWarning, formatStandard },
{ UNEXPECTED_ENDTAG_IN, TidyError, formatStandard },
{ UNEXPECTED_ENDTAG, TidyWarning, formatStandard },
{ UNEXPECTED_ENDTAG_ERR, TidyError, formatStandard },
{ UNEXPECTED_EQUALSIGN, TidyWarning, formatAttributeReport },
{ UNEXPECTED_GT, TidyWarning, formatAttributeReport },
{ UNEXPECTED_QUOTEMARK, TidyWarning, formatAttributeReport },
{ UNKNOWN_ELEMENT_LOOKS_CUSTOM, TidyError, formatStandard },
{ UNKNOWN_ELEMENT, TidyError, formatStandard },
{ UNKNOWN_ENTITY, TidyWarning, formatStandard },
{ USING_BR_INPLACE_OF, TidyWarning, formatStandard },
{ VENDOR_SPECIFIC_CHARS, TidyWarning, formatEncodingReport },
{ WHITE_IN_URI, TidyWarning, formatAttributeReport },
{ XML_DECLARATION_DETECTED, TidyWarning, formatStandard },
{ XML_ID_SYNTAX, TidyWarning, formatAttributeReport },
{ APPLET_MISSING_ALT, TidyAccess, formatAccessReport },
{ AREA_MISSING_ALT, TidyAccess, formatAccessReport },
{ ASCII_REQUIRES_DESCRIPTION, TidyAccess, formatAccessReport },
{ ASSOCIATE_LABELS_EXPLICITLY, TidyAccess, formatAccessReport },
{ ASSOCIATE_LABELS_EXPLICITLY_FOR, TidyAccess, formatAccessReport },
{ ASSOCIATE_LABELS_EXPLICITLY_ID, TidyAccess, formatAccessReport },
{ AUDIO_MISSING_TEXT_AIFF, TidyAccess, formatAccessReport },
{ AUDIO_MISSING_TEXT_AU, TidyAccess, formatAccessReport },
{ AUDIO_MISSING_TEXT_RA, TidyAccess, formatAccessReport },
{ AUDIO_MISSING_TEXT_RM, TidyAccess, formatAccessReport },
{ AUDIO_MISSING_TEXT_SND, TidyAccess, formatAccessReport },
{ AUDIO_MISSING_TEXT_WAV, TidyAccess, formatAccessReport },
{ COLOR_CONTRAST_ACTIVE_LINK, TidyAccess, formatAccessReport },
{ COLOR_CONTRAST_LINK, TidyAccess, formatAccessReport },
{ COLOR_CONTRAST_TEXT, TidyAccess, formatAccessReport },
{ COLOR_CONTRAST_VISITED_LINK, TidyAccess, formatAccessReport },
{ DATA_TABLE_MISSING_HEADERS, TidyAccess, formatAccessReport },
{ DATA_TABLE_MISSING_HEADERS_COLUMN, TidyAccess, formatAccessReport },
{ DATA_TABLE_MISSING_HEADERS_ROW, TidyAccess, formatAccessReport },
{ DATA_TABLE_REQUIRE_MARKUP_COLUMN_HEADERS, TidyAccess, formatAccessReport },
{ DATA_TABLE_REQUIRE_MARKUP_ROW_HEADERS, TidyAccess, formatAccessReport },
{ DOCTYPE_MISSING, TidyAccess, formatAccessReport },
{ ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_APPLET, TidyAccess, formatAccessReport },
{ ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_EMBED, TidyAccess, formatAccessReport },
{ ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_OBJECT, TidyAccess, formatAccessReport },
{ ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_SCRIPT, TidyAccess, formatAccessReport },
{ FRAME_MISSING_LONGDESC, TidyAccess, formatAccessReport },
{ FRAME_MISSING_NOFRAMES, TidyAccess, formatAccessReport },
{ FRAME_MISSING_TITLE, TidyAccess, formatAccessReport },
{ FRAME_SRC_INVALID, TidyAccess, formatAccessReport },
{ FRAME_TITLE_INVALID_NULL, TidyAccess, formatAccessReport },
{ FRAME_TITLE_INVALID_SPACES, TidyAccess, formatAccessReport },
{ HEADER_USED_FORMAT_TEXT, TidyAccess, formatAccessReport },
{ HEADERS_IMPROPERLY_NESTED, TidyAccess, formatAccessReport },
{ IMAGE_MAP_SERVER_SIDE_REQUIRES_CONVERSION, TidyAccess, formatAccessReport },
{ IMG_ALT_SUSPICIOUS_FILE_SIZE, TidyAccess, formatAccessReport },
{ IMG_ALT_SUSPICIOUS_FILENAME, TidyAccess, formatAccessReport },
{ IMG_ALT_SUSPICIOUS_PLACEHOLDER, TidyAccess, formatAccessReport },
{ IMG_ALT_SUSPICIOUS_TOO_LONG, TidyAccess, formatAccessReport },
{ IMG_BUTTON_MISSING_ALT, TidyAccess, formatAccessReport },
{ IMG_MAP_CLIENT_MISSING_TEXT_LINKS, TidyAccess, formatAccessReport },
{ IMG_MAP_SERVER_REQUIRES_TEXT_LINKS, TidyAccess, formatAccessReport },
{ IMG_MISSING_ALT, TidyAccess, formatAccessReport },
{ IMG_MISSING_DLINK, TidyAccess, formatAccessReport },
{ IMG_MISSING_LONGDESC, TidyAccess, formatAccessReport },
{ IMG_MISSING_LONGDESC_DLINK, TidyAccess, formatAccessReport },
{ INFORMATION_NOT_CONVEYED_APPLET, TidyAccess, formatAccessReport },
{ INFORMATION_NOT_CONVEYED_IMAGE, TidyAccess, formatAccessReport },
{ INFORMATION_NOT_CONVEYED_INPUT, TidyAccess, formatAccessReport },
{ INFORMATION_NOT_CONVEYED_OBJECT, TidyAccess, formatAccessReport },
{ INFORMATION_NOT_CONVEYED_SCRIPT, TidyAccess, formatAccessReport },
{ LANGUAGE_INVALID, TidyAccess, formatAccessReport },
{ LANGUAGE_NOT_IDENTIFIED, TidyAccess, formatAccessReport },
{ LAYOUT_TABLE_INVALID_MARKUP, TidyAccess, formatAccessReport },
{ LAYOUT_TABLES_LINEARIZE_PROPERLY, TidyAccess, formatAccessReport },
{ LINK_TEXT_MISSING, TidyAccess, formatAccessReport },
{ LINK_TEXT_NOT_MEANINGFUL, TidyAccess, formatAccessReport },
{ LINK_TEXT_NOT_MEANINGFUL_CLICK_HERE, TidyAccess, formatAccessReport },
{ LINK_TEXT_TOO_LONG, TidyAccess, formatAccessReport },
{ LIST_USAGE_INVALID_LI, TidyAccess, formatAccessReport },
{ LIST_USAGE_INVALID_OL, TidyAccess, formatAccessReport },
{ LIST_USAGE_INVALID_UL, TidyAccess, formatAccessReport },
{ METADATA_MISSING, TidyAccess, formatAccessReport },
{ METADATA_MISSING_REDIRECT_AUTOREFRESH, TidyAccess, formatAccessReport },
{ MULTIMEDIA_REQUIRES_TEXT, TidyAccess, formatAccessReport },
{ NEW_WINDOWS_REQUIRE_WARNING_BLANK, TidyAccess, formatAccessReport },
{ NEW_WINDOWS_REQUIRE_WARNING_NEW, TidyAccess, formatAccessReport },
{ NOFRAMES_INVALID_CONTENT, TidyAccess, formatAccessReport },
{ NOFRAMES_INVALID_LINK, TidyAccess, formatAccessReport },
{ NOFRAMES_INVALID_NO_VALUE, TidyAccess, formatAccessReport },
{ OBJECT_MISSING_ALT, TidyAccess, formatAccessReport },
{ POTENTIAL_HEADER_BOLD, TidyAccess, formatAccessReport },
{ POTENTIAL_HEADER_ITALICS, TidyAccess, formatAccessReport },
{ POTENTIAL_HEADER_UNDERLINE, TidyAccess, formatAccessReport },
{ PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_APPLET, TidyAccess, formatAccessReport },
{ PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_EMBED, TidyAccess, formatAccessReport },
{ PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_OBJECT, TidyAccess, formatAccessReport },
{ PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_SCRIPT, TidyAccess, formatAccessReport },
{ REMOVE_AUTO_REDIRECT, TidyAccess, formatAccessReport },
{ REMOVE_AUTO_REFRESH, TidyAccess, formatAccessReport },
{ REMOVE_BLINK_MARQUEE, TidyAccess, formatAccessReport },
{ REMOVE_FLICKER_ANIMATED_GIF, TidyAccess, formatAccessReport },
{ REMOVE_FLICKER_APPLET, TidyAccess, formatAccessReport },
{ REMOVE_FLICKER_EMBED, TidyAccess, formatAccessReport },
{ REMOVE_FLICKER_OBJECT, TidyAccess, formatAccessReport },
{ REMOVE_FLICKER_SCRIPT, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_APPLET, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_BASEFONT, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_CENTER, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_DIR, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_FONT, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_ISINDEX, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_MENU, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_S, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_STRIKE, TidyAccess, formatAccessReport },
{ REPLACE_DEPRECATED_HTML_U, TidyAccess, formatAccessReport },
{ SCRIPT_MISSING_NOSCRIPT, TidyAccess, formatAccessReport },
{ SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_CLICK, TidyAccess, formatAccessReport },
{ SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_DOWN, TidyAccess, formatAccessReport },
{ SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_MOVE, TidyAccess, formatAccessReport },
{ SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_OUT, TidyAccess, formatAccessReport },
{ SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_OVER, TidyAccess, formatAccessReport },
{ SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_UP, TidyAccess, formatAccessReport },
{ SKIPOVER_ASCII_ART, TidyAccess, formatAccessReport },
{ STYLE_SHEET_CONTROL_PRESENTATION, TidyAccess, formatAccessReport },
{ STYLESHEETS_REQUIRE_TESTING_LINK, TidyAccess, formatAccessReport },
{ STYLESHEETS_REQUIRE_TESTING_STYLE_ATTR, TidyAccess, formatAccessReport },
{ STYLESHEETS_REQUIRE_TESTING_STYLE_ELEMENT, TidyAccess, formatAccessReport },
{ TABLE_MAY_REQUIRE_HEADER_ABBR, TidyAccess, formatAccessReport },
{ TABLE_MAY_REQUIRE_HEADER_ABBR_NULL, TidyAccess, formatAccessReport },
{ TABLE_MAY_REQUIRE_HEADER_ABBR_SPACES, TidyAccess, formatAccessReport },
{ TABLE_MISSING_CAPTION, TidyAccess, formatAccessReport },
{ TABLE_MISSING_SUMMARY, TidyAccess, formatAccessReport },
{ TABLE_SUMMARY_INVALID_NULL, TidyAccess, formatAccessReport },
{ TABLE_SUMMARY_INVALID_PLACEHOLDER, TidyAccess, formatAccessReport },
{ TABLE_SUMMARY_INVALID_SPACES, TidyAccess, formatAccessReport },
{ TEXT_EQUIVALENTS_REQUIRE_UPDATING_APPLET, TidyAccess, formatAccessReport },
{ TEXT_EQUIVALENTS_REQUIRE_UPDATING_OBJECT, TidyAccess, formatAccessReport },
{ TEXT_EQUIVALENTS_REQUIRE_UPDATING_SCRIPT, TidyAccess, formatAccessReport },
{ 0, 0, NULL }
};
/*********************************************************************
* Message Formatting
* These individual message formatters populate messages with the
* correct, pertinent data.
*********************************************************************/
/* Provides formatting for the Attribute-related reports. This formatter
** should be reserved for messages generated by Tidy's accessibility module,
** even if the signature matches some unrelated report that you wish to
** generate.
*/
TidyMessageImpl *formatAccessReport(TidyDocImpl* doc, Node *element, Node *node, uint code, uint level, va_list args)
{
doc->badAccess |= BA_WAI;
/* Currently *all* cases are handled in the default, but maintain this
structure for possible future cases. */
switch (code)
{
default:
return TY_(tidyMessageCreateWithNode)(doc, node, code, level );
}
return NULL;
}
/* Provides formatting for the Attribute-related reports. This formatter
** provides local variables that are used principally in the formats used for
** the attribute related reports.
*/
TidyMessageImpl *formatAttributeReport(TidyDocImpl* doc, Node *element, Node *node, uint code, uint level, va_list args)
{
AttVal *av = NULL;
char const *name = "NULL";
char const *value = "NULL";
char tagdesc[64];
TagToString(node, tagdesc, sizeof(tagdesc));
if ( ( av = va_arg(args, AttVal*) ) )
{
if (av->attribute)
name = av->attribute;
if (av->value)
value = av->value;
}
switch (code)
{
case BACKSLASH_IN_URI:
case ESCAPED_ILLEGAL_URI:
case FIXED_BACKSLASH:
case ID_NAME_MISMATCH:
case ILLEGAL_URI_CODEPOINT:
case ILLEGAL_URI_REFERENCE:
case INVALID_XML_ID:
case MISSING_IMAGEMAP:
case MISSING_QUOTEMARK:
case NEWLINE_IN_URI:
case UNEXPECTED_EQUALSIGN:
case UNEXPECTED_GT:
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:
case BAD_ATTRIBUTE_VALUE_REPLACED:
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;
}
/* Provides report formatting *and* additional status settings for Tidy's
** encoding reports. Provides the local variables typically used for this type
** of report.
** @todo: These status changes probably SHOULD be made in the calling code;
** however these states are captured to generate future output, which may be
** useful here in the long run.
*/
TidyMessageImpl *formatEncodingReport(TidyDocImpl* doc, Node *element, Node *node, uint code, uint level, va_list args)
{
char buf[ 32 ] = {'\0'};
uint c = va_arg( args, uint );
Bool discarded = va_arg( args, Bool );
ctmbstr action = tidyLocalizedString(discarded ? STRING_DISCARDING : STRING_REPLACING);
switch (code)
{
case INVALID_NCR:
NtoS(c, buf);
doc->badChars |= BC_INVALID_NCR;
break;
case INVALID_SGML_CHARS:
NtoS(c, buf);
doc->badChars |= BC_INVALID_SGML_CHARS;
break;
case INVALID_UTF8:
TY_(tmbsnprintf)(buf, sizeof(buf), "U+%04X", c);
doc->badChars |= BC_INVALID_UTF8;
break;
#if SUPPORT_UTF16_ENCODINGS
case INVALID_UTF16:
TY_(tmbsnprintf)(buf, sizeof(buf), "U+%04X", c);
doc->badChars |= BC_INVALID_UTF16;
break;
#endif
case VENDOR_SPECIFIC_CHARS:
NtoS(c, buf);
doc->badChars |= BC_VENDOR_SPECIFIC_CHARS;
break;
case ENCODING_MISMATCH:
doc->badChars |= BC_ENCODING_MISMATCH;
return TY_(tidyMessageCreateWithLexer)(doc,
code,
level,
TY_(CharEncodingName)(doc->docIn->encoding),
TY_(CharEncodingName)(c));
break;
}
return TY_(tidyMessageCreateWithLexer)(doc, code, level, action, buf );
}
/* Provides general formatting for the majority of Tidy's reports. Because most
** reports use the same basic data derived from the element and node, this
** formatter covers the vast majority of Tidy's report messages. Note that this
** formatter guarantees the values of TidyReportLevel in the dispatchTable[].
** Some of the cases in this formatter start new contexts from the va_list
** when the required data is simple. For complex local variable needs, it may
** be preferred to write a new formatter.
*/
TidyMessageImpl *formatStandard(TidyDocImpl* doc, Node *element, Node *node, uint code, uint level, va_list args)
{
char nodedesc[ 256 ] = {0};
char elemdesc[ 256 ] = {0};
Node* rpt = ( element ? element : node );
TagToString(node, nodedesc, sizeof(nodedesc));
if ( element )
TagToString(element, elemdesc, sizeof(elemdesc));
switch ( code )
{
case CUSTOM_TAG_DETECTED:
{
ctmbstr tagtype;
switch ( cfg( doc, TidyUseCustomTags ) )
{
case TidyCustomBlocklevel:
tagtype = tidyLocalizedString( TIDYCUSTOMBLOCKLEVEL_STRING );
break;
case TidyCustomEmpty:
tagtype = tidyLocalizedString( TIDYCUSTOMEMPTY_STRING );
break;
case TidyCustomInline:
tagtype = tidyLocalizedString( TIDYCUSTOMINLINE_STRING );
break;
case TidyCustomPre:
default:
tagtype = tidyLocalizedString( TIDYCUSTOMPRE_STRING );
break;
}
return TY_(tidyMessageCreateWithNode)(doc, element, code, level, elemdesc, tagtype );
}
case STRING_NO_SYSID:
return TY_(tidyMessageCreate)( doc, code, level );
case FILE_CANT_OPEN:
case FILE_CANT_OPEN_CFG:
case FILE_NOT_FILE:
case STRING_CONTENT_LOOKS:
case STRING_DOCTYPE_GIVEN:
case STRING_MISSING_MALFORMED:
{
ctmbstr str;
if ( (str = va_arg( args, ctmbstr)) )
return TY_(tidyMessageCreate)( doc, code, level, str );
} break;
case APOS_UNDEFINED:
case MISSING_SEMICOLON_NCR:
case MISSING_SEMICOLON:
case UNESCAPED_AMPERSAND:
case UNKNOWN_ENTITY:
{
ctmbstr entityname;
if ( !(entityname = va_arg( args, ctmbstr)) )
{
entityname = "NULL";
}
return TY_(tidyMessageCreateWithLexer)(doc, code, level, entityname);
}
case MISSING_ATTRIBUTE:
{
ctmbstr name;
if ( (name = va_arg( args, ctmbstr)) )
return TY_(tidyMessageCreateWithNode)(doc, node, code, level, nodedesc, name );
} break;
case STRING_UNKNOWN_OPTION:
{
ctmbstr str;
if ( (str = va_arg( args, ctmbstr)) )
return TY_(tidyMessageCreateWithLexer)(doc, code, level, str);
} break;
case BAD_SURROGATE_LEAD:
case BAD_SURROGATE_PAIR:
case BAD_SURROGATE_TAIL:
{
uint c1 = va_arg( args, uint );
uint c2 = va_arg( args, uint );
return TY_(tidyMessageCreateWithLexer)(doc, code, level, c1, c2);
}
case SPACE_PRECEDING_XMLDECL:
/* @TODO: Should this be a TidyInfo "silent" fix? */
return TY_(tidyMessageCreateWithNode)(doc, node, code, level );
case CANT_BE_NESTED:
case NOFRAMES_CONTENT:
case USING_BR_INPLACE_OF:
/* Can we use `rpt` here? No; `element` has a value in every case. */
return TY_(tidyMessageCreateWithNode)(doc, node, code, level, nodedesc );
case ELEMENT_VERS_MISMATCH_ERROR:
case ELEMENT_VERS_MISMATCH_WARN:
return TY_(tidyMessageCreateWithNode)(doc, node, code, level, nodedesc, HTMLVersion(doc) );
case TAG_NOT_ALLOWED_IN:
/* Can we use `rpt` here? No; `element` has a value in every case. */
return TY_(tidyMessageCreateWithNode)(doc, node, code, level, nodedesc, element->element );
case INSERTING_TAG:
case MISSING_STARTTAG:
case TOO_MANY_ELEMENTS:
case UNEXPECTED_ENDTAG:
case UNEXPECTED_ENDTAG_ERR: /* generated by XML docs */
/* Can we use `rpt` here? No; `element` has a value in every case. */
return TY_(tidyMessageCreateWithNode)(doc, node, code, level, node->element );
case UNEXPECTED_ENDTAG_IN:
/* Can we use `rpt` here? No; `element` has a value in every case. */
return TY_(tidyMessageCreateWithNode)(doc, node, code, level, node->element, element->element );
case BAD_CDATA_CONTENT:
case CONTENT_AFTER_BODY:
case DOCTYPE_AFTER_TAGS:
case DUPLICATE_FRAMESET:
case MALFORMED_COMMENT:
case MALFORMED_DOCTYPE:
case MISSING_DOCTYPE:
case MISSING_TITLE_ELEMENT:
case NESTED_QUOTATION:
case SUSPECTED_MISSING_QUOTE:
case XML_DECLARATION_DETECTED:
return TY_(tidyMessageCreateWithNode)(doc, rpt, code, level );
case ELEMENT_NOT_EMPTY:
case FOUND_STYLE_IN_BODY:
case ILLEGAL_NESTING:
case MOVED_STYLE_TO_HEAD:
case TRIM_EMPTY_ELEMENT:
case UNEXPECTED_END_OF_FILE:
return TY_(tidyMessageCreateWithNode)(doc, rpt, code, level, elemdesc );
case OBSOLETE_ELEMENT:
case REPLACING_ELEMENT:
case REPLACING_UNEX_ELEMENT:
return TY_(tidyMessageCreateWithNode)(doc, rpt, code, level, elemdesc, nodedesc );
case ADDED_MISSING_CHARSET:
case BAD_SUMMARY_HTML5:
case NESTED_EMPHASIS:
case PROPRIETARY_ELEMENT:
case REMOVED_HTML5:
case UNKNOWN_ELEMENT:
case UNKNOWN_ELEMENT_LOOKS_CUSTOM:
return TY_(tidyMessageCreateWithNode)(doc, rpt, code, level, nodedesc );
case MISSING_ENDTAG_FOR:
case PREVIOUS_LOCATION:
return TY_(tidyMessageCreateWithNode)(doc, rpt, code, level, element->element );
case MISSING_ENDTAG_BEFORE:
return TY_(tidyMessageCreateWithNode)(doc, rpt, code, level, element->element, nodedesc );
case COERCE_TO_ENDTAG:
case NON_MATCHING_ENDTAG:
case TOO_MANY_ELEMENTS_IN:
return TY_(tidyMessageCreateWithNode)(doc, rpt, code, level, node->element, node->element );
}
return NULL;
}
/* Provides general formatting as formatStandard, except TidyReportLevel is set
** dynamically for these items as it cannot be predicted except at runtime.
*/
TidyMessageImpl *formatStandardDynamic(TidyDocImpl* doc, Node *element, Node *node, uint code, uint level, va_list args)
{
char nodedesc[ 256 ] = {0};
TagToString(node, nodedesc, sizeof(nodedesc));
switch (code)
{
case DISCARDING_UNEXPECTED:
/* Force error if in a bad form, or Issue #166 - repeated <main> element. */
/* Can we use `rpt` here? No; `element` has a value in every case. */
return TY_(tidyMessageCreateWithNode)(doc, node, code, doc->badForm ? TidyError : TidyWarning, nodedesc );
break;
}
return NULL;
}
/*********************************************************************
* High Level Message Writing Functions
* When adding new reports to LibTidy, preference should be given
* to one of the existing, general pupose message writing functions
* above, if possible, otherwise try to use one of these, or as a
* last resort add a new one in this section.
*********************************************************************/
/* This function performs the heavy lifting for TY_(Report)(). Critically we
** can accept the va_list needed for recursion.
*/
static void vReport(TidyDocImpl* doc, Node *element, Node *node, uint code, va_list args)
{
int i = 0;
va_list args_copy;
while ( dispatchTable[i].code != 0 )
{
if ( dispatchTable[i].code == code )
{
TidyMessageImpl *message;
messageFormatter *handler = dispatchTable[i].handler;
TidyReportLevel level = dispatchTable[i].level;
va_copy(args_copy, args);
message = handler( doc, element, node, code, level, args_copy );
va_end(args_copy);
messageOut( message );
if ( dispatchTable[i].next )
{
va_copy(args_copy, args);
vReport(doc, element, node, dispatchTable[i].next, args_copy);
va_end(args_copy);
}
break;
}
i++;
}
}
/* This single Report output function uses the correct formatter with all
** possible, relevant data that can be reported. The only real drawbacks are
** having to pass NULL when some of the values aren't used, and the lack of
** type safety by using the variable arguments. To counter this some convenience
** report output functions exist, too. Any new reports you wish to create must
** be able to use this function signature, although convenience functions should
** be added to abstract the full fuction signature and to preserve type safety.
*/
void TY_(Report)(TidyDocImpl* doc, Node *element, Node *node, uint code, ...)
{
va_list args;
va_start(args, code);
vReport(doc, element, node, code, args);
va_end(args);
}
/*********************************************************************
* Convenience Reporting Functions
* Functions that don't require the full signature of TY_(Report),
* and help protect type safety by avoiding variable arguments in the
* rest of Tidy's code.
*********************************************************************/
#if SUPPORT_ACCESSIBILITY_CHECKS
void TY_(ReportAccessError)( TidyDocImpl* doc, Node* node, uint code )
{
TY_(Report)( doc, NULL, node, code );
}
#endif
void TY_(ReportAttrError)(TidyDocImpl* doc, Node *node, AttVal *av, uint code)
{
TY_(Report)( doc, NULL, node, code, av );
}
void TY_(ReportBadArgument)( TidyDocImpl* doc, ctmbstr option )
{
assert( option != NULL );
TY_(Report)( doc, NULL, NULL, STRING_MISSING_MALFORMED, option );
}
void TY_(ReportEntityError)( TidyDocImpl* doc, uint code, ctmbstr entity, int ARG_UNUSED(c) )
{
/* Note that the report formatter currently doesn't use argument c */
TY_(Report)( doc, NULL, NULL, code, entity, c );
}
void TY_(ReportFileError)( TidyDocImpl* doc, ctmbstr file, uint code )
{
TY_(Report)( doc, NULL, NULL, code, file );
}
void TY_(ReportEncodingError)(TidyDocImpl* doc, uint code, uint c, Bool discarded)
{
TY_(Report)( doc, NULL, NULL, code, c, discarded );
}
void TY_(ReportEncodingWarning)(TidyDocImpl* doc, uint code, uint encoding)
{
/* va_list in formatter expects trailing `no` argument */
TY_(Report)( doc, NULL, NULL, code, encoding, no );
}
void TY_(ReportMissingAttr)( TidyDocImpl* doc, Node* node, ctmbstr name )
{
TY_(Report)( doc, NULL, node, MISSING_ATTRIBUTE, name );
}
void TY_(ReportSurrogateError)(TidyDocImpl* doc, uint code, uint c1, uint c2)
{
TY_(Report)( doc, NULL, NULL, code, c1,c2 );
}
void TY_(ReportUnknownOption)( TidyDocImpl* doc, ctmbstr option )
{
/* lexer is not defined when this is called */
TY_(Report)( doc, NULL, NULL, STRING_UNKNOWN_OPTION, option );
}
/*********************************************************************
* Dialogue Output Functions
* As for issuing reports, Tidy manages all dialogue output from a
* single source in order to manage message categories in a simple
* an consistent manner.
*********************************************************************/
/* This structure ties together for each dialogue Code the default
** TidyReportLevel. This it makes it simple to output new dialogue
** messages, or to change report level by modifying this array.
*/
static struct _dialogueDispatchTable {
uint code; /**< The message code. */
TidyReportLevel level; /**< The default TidyReportLevel of the message. */
} dialogueDispatchTable[] = {
{ STRING_HELLO_ACCESS, TidyDialogueInfo }, /* AccessibilityChecks() */
{ TEXT_GENERAL_INFO, TidyDialogueInfo }, /* tidyGeneralInfo() */
{ TEXT_GENERAL_INFO_PLEA, TidyDialogueInfo }, /* tidyGeneralInfo() */
{ STRING_NEEDS_INTERVENTION, TidyDialogueSummary }, /* tidyDocRunDiagnostics() */
{ STRING_ERROR_COUNT, TidyDialogueSummary }, /* ReportNumWarnings() */
{ STRING_NO_ERRORS, TidyDialogueSummary }, /* ReportNumWarnings() */
{ STRING_NOT_ALL_SHOWN, TidyDialogueSummary }, /* ReportNumWarnings() */
{ TEXT_ACCESS_ADVICE1, TidyDialogueFootnote }, /* errorSummary() */
{ TEXT_ACCESS_ADVICE2, TidyDialogueFootnote },
{ TEXT_BAD_FORM, TidyDialogueFootnote },
{ TEXT_BAD_MAIN, TidyDialogueFootnote },
{ TEXT_HTML_T_ALGORITHM, TidyDialogueFootnote },
{ TEXT_INVALID_URI, TidyDialogueFootnote },
{ TEXT_INVALID_UTF8, TidyDialogueFootnote },
{ TEXT_INVALID_UTF16, TidyDialogueFootnote },
{ TEXT_M_IMAGE_ALT, TidyDialogueFootnote },
{ TEXT_M_IMAGE_MAP, TidyDialogueFootnote },
{ TEXT_M_LINK_ALT, TidyDialogueFootnote },
{ TEXT_M_SUMMARY, TidyDialogueFootnote },
{ TEXT_SGML_CHARS, TidyDialogueFootnote },
{ TEXT_USING_BODY, TidyDialogueFootnote },
{ TEXT_USING_FONT, TidyDialogueFootnote },
{ TEXT_USING_FRAMES, TidyDialogueFootnote },
{ TEXT_USING_LAYER, TidyDialogueFootnote },
{ TEXT_USING_NOBR, TidyDialogueFootnote },
{ TEXT_USING_SPACER, TidyDialogueFootnote },
{ TEXT_VENDOR_CHARS, TidyDialogueFootnote },
};
/* This message formatter for dialogue messages should be capable of formatting
** every message, because they're not all the complex and there aren't that
** many.
*/
TidyMessageImpl *formatDialogue( TidyDocImpl* doc, uint code, TidyReportLevel level, va_list args )
{
switch (code)
{
case TEXT_SGML_CHARS:
case TEXT_VENDOR_CHARS:
{
ctmbstr str = va_arg(args, ctmbstr);
return TY_(tidyMessageCreate)( doc, code, level, str );
}
case STRING_ERROR_COUNT:
case STRING_NOT_ALL_SHOWN:
return TY_(tidyMessageCreate)( doc, code, level,
doc->warnings, tidyLocalizedStringN( STRING_ERROR_COUNT_WARNING, doc->warnings ),
doc->errors, tidyLocalizedStringN( STRING_ERROR_COUNT_ERROR, doc->errors ) );
case STRING_HELLO_ACCESS:
case STRING_NEEDS_INTERVENTION:
case STRING_NO_ERRORS:
case TEXT_ACCESS_ADVICE1:
case TEXT_ACCESS_ADVICE2:
case TEXT_BAD_FORM:
case TEXT_BAD_MAIN:
case TEXT_GENERAL_INFO:
case TEXT_GENERAL_INFO_PLEA:
case TEXT_HTML_T_ALGORITHM:
case TEXT_INVALID_URI:
case TEXT_INVALID_UTF8:
case TEXT_INVALID_UTF16:
case TEXT_M_IMAGE_ALT:
case TEXT_M_IMAGE_MAP:
case TEXT_M_LINK_ALT:
case TEXT_M_SUMMARY:
case TEXT_USING_BODY:
case TEXT_USING_FONT:
case TEXT_USING_FRAMES:
case TEXT_USING_LAYER:
case TEXT_USING_NOBR:
case TEXT_USING_SPACER:
default:
return TY_(tidyMessageCreate)( doc, code, level );
}
return NULL;
}
/* This single Dialogue output function determines the correct message level
** and formats the message with the appropriate formatter, and then outputs it.
** This one dialogue function should be sufficient for every use case.
*/
void TY_(Dialogue)(TidyDocImpl* doc, uint code, ...)
{
int i = 0;
va_list args;
while ( dialogueDispatchTable[i].code != 0 )
{
if ( dialogueDispatchTable[i].code == code )
{
TidyReportLevel level = dialogueDispatchTable[i].level;
va_start(args, code);
TidyMessageImpl *message = formatDialogue( doc, code, level, args );
va_end(args);
messageOut( message );
break;
}
i++;
}
}
/*********************************************************************
* Output Dialogue Information
* In addition to reports that are added to the table, Tidy emits
* various dialogue type information. Most of these are specific to
* exact circumstances, although `TY_(Dialogue)` should be used
* instead of adding a new function, if possible.
*********************************************************************/
/* Outputs the footnotes and other dialogue information after document cleanup
** is complete. LibTidy users might consider capturing these individually in
** the message callback rather than capturing this entire buffer.
** Called by tidyErrorSummary(), in console.
** @todo: This name is a bit misleading and should probably be renamed to
** indicate its focus on printing footnotes.
*/
void TY_(ErrorSummary)( TidyDocImpl* doc )
{
ctmbstr encnam = tidyLocalizedString(STRING_SPECIFIED);
int charenc = cfg( doc, TidyCharEncoding );
if ( charenc == WIN1252 )
encnam = "Windows-1252";
else if ( charenc == MACROMAN )
encnam = "MacRoman";
else if ( charenc == IBM858 )
encnam = "ibm858";
else if ( charenc == LATIN0 )
encnam = "latin0";
/* adjust badAccess to that it is 0 if frames are ok */
if ( doc->badAccess & (BA_USING_FRAMES | BA_USING_NOFRAMES) )
{
if (!((doc->badAccess & BA_USING_FRAMES) && !(doc->badAccess & BA_USING_NOFRAMES)))
{
doc->badAccess &= ~(BA_USING_FRAMES | BA_USING_NOFRAMES);
}
}
if (doc->badChars)
{
if (doc->badChars & BC_VENDOR_SPECIFIC_CHARS)
TY_(Dialogue)( doc, TEXT_VENDOR_CHARS, encnam );
if ((doc->badChars & BC_INVALID_SGML_CHARS) || (doc->badChars & BC_INVALID_NCR))
TY_(Dialogue)( doc, TEXT_SGML_CHARS, encnam );
if (doc->badChars & BC_INVALID_UTF8)
TY_(Dialogue)( doc, TEXT_INVALID_UTF8 );
#if SUPPORT_UTF16_ENCODINGS
if (doc->badChars & BC_INVALID_UTF16)
TY_(Dialogue)( doc, TEXT_INVALID_UTF16 );
#endif
if (doc->badChars & BC_INVALID_URI)
TY_(Dialogue)( doc, TEXT_INVALID_URI );
}
if (doc->badForm)
{
if (doc->badForm & flg_BadForm) /* Issue #166 - changed to BIT flag to support other errors */
TY_(Dialogue)( doc, TEXT_BAD_FORM );
if (doc->badForm & flg_BadMain) /* Issue #166 - repeated <main> element */
TY_(Dialogue)( doc, TEXT_BAD_MAIN );
}
if (doc->badAccess)
{
/* Tidy "classic" accessibility tests */
if ( cfg(doc, TidyAccessibilityCheckLevel) == 0 )
{
if (doc->badAccess & BA_MISSING_SUMMARY)
TY_(Dialogue)( doc, TEXT_M_SUMMARY );
if (doc->badAccess & BA_MISSING_IMAGE_ALT)
TY_(Dialogue)( doc, TEXT_M_IMAGE_ALT );
if (doc->badAccess & BA_MISSING_IMAGE_MAP)
TY_(Dialogue)( doc, TEXT_M_IMAGE_MAP );
if (doc->badAccess & BA_MISSING_LINK_ALT)
TY_(Dialogue)( doc, TEXT_M_LINK_ALT );
if ((doc->badAccess & BA_USING_FRAMES) && !(doc->badAccess & BA_USING_NOFRAMES))
TY_(Dialogue)( doc, TEXT_USING_FRAMES );
}
if ( cfg(doc, TidyAccessibilityCheckLevel) > 0 )
TY_(Dialogue)( doc, TEXT_ACCESS_ADVICE2 );
else
TY_(Dialogue)( doc, TEXT_ACCESS_ADVICE1 );
}
if (doc->badLayout)
{
if (doc->badLayout & USING_LAYER)
TY_(Dialogue)( doc, TEXT_USING_LAYER );
if (doc->badLayout & USING_SPACER)
TY_(Dialogue)( doc, TEXT_USING_SPACER );
if (doc->badLayout & USING_FONT)
TY_(Dialogue)( doc, TEXT_USING_FONT );
if (doc->badLayout & USING_NOBR)
TY_(Dialogue)( doc, TEXT_USING_NOBR );
if (doc->badLayout & USING_BODY)
TY_(Dialogue)( doc, TEXT_USING_BODY );
}
}
/* Outputs document HTML version and version-related information.
** Called by tidyRunDiagnostics(), from console.
** Called by tidyDocReportDoctype(), currently unused.
*/
void TY_(ReportMarkupVersion)( TidyDocImpl* doc )
{
if ( doc->givenDoctype )
TY_(Report)( doc, NULL, NULL, STRING_DOCTYPE_GIVEN, doc->givenDoctype );
if ( ! cfgBool(doc, TidyXmlTags) )
{
Bool isXhtml = doc->lexer->isvoyager;
uint apparentVers = TY_(ApparentVersion)( doc );
ctmbstr vers = TY_(HTMLVersionNameFromCode)( apparentVers, isXhtml );
if ( !vers )
vers = tidyLocalizedString(STRING_HTML_PROPRIETARY);
TY_(Report)( doc, NULL, NULL, STRING_CONTENT_LOOKS, vers );
/* Warn about missing sytem identifier (SI) in emitted doctype */
if ( TY_(WarnMissingSIInEmittedDocType)( doc ) )
TY_(Report)( doc, NULL, NULL, STRING_NO_SYSID );
}
}
/* Reports the number of warnings and errors found in the document.
** Called by tidyRunDiagnostics(), from console.
*/
void TY_(ReportNumWarnings)( TidyDocImpl* doc )
{
if ( doc->warnings > 0 || doc->errors > 0 )
{
if ( doc->errors > cfg(doc, TidyShowErrors) || !cfgBool(doc, TidyShowWarnings) )
{
TY_(Dialogue)( doc, STRING_NOT_ALL_SHOWN );
}
else
{
TY_(Dialogue)( doc, STRING_ERROR_COUNT );
}
}
else
{
TY_(Dialogue)( doc, STRING_NO_ERRORS );
}
TY_(WriteChar)( '\n', doc->errout );
}
/*********************************************************************
* Key Discovery
*********************************************************************/
/*********************************************************************
* LibTidy users may want to to enable their own localization lookup
* lookup methods. Because Tidy's errors codes are enums, the actual
* values can change over time. This table will the LibTidy users
* always have a static value available for use.
*
* For macro documentation, refer to the comments in `tidyenum.h`.
*********************************************************************/
typedef struct tidyStringsKeyItem {
ctmbstr key;
int value;
} tidyStringsKeyItem;
static const tidyStringsKeyItem tidyStringsKeys[] = {
FOREACH_TIDYCONFIGCATEGORY(MAKE_STRUCT)
FOREACH_MSG_MISC(MAKE_STRUCT)
FOREACH_FOOTNOTE_MSG(MAKE_STRUCT)
FOREACH_DIALOG_MSG(MAKE_STRUCT)
FOREACH_REPORT_MSG(MAKE_STRUCT)
{ "TIDYSTRINGS_FIRST", TIDYSTRINGS_FIRST },
#if SUPPORT_ACCESSIBILITY_CHECKS
FOREACH_ACCESS_MSG(MAKE_STRUCT)
#endif
#if SUPPORT_CONSOLE_APP
FOREACH_MSG_CONSOLE(MAKE_STRUCT)
#endif
{ "TIDYSTRINGS_LAST", TIDYSTRINGS_LAST },
{ NULL, 0 },
};
/**
* Given an error code, return the string associated with it.
*/
ctmbstr TY_(tidyErrorCodeAsKey)(uint code)
{
uint i = 0;
while (tidyStringsKeys[i].key) {
if ( tidyStringsKeys[i].value == code )
return tidyStringsKeys[i].key;
i++;
}
return "UNDEFINED";
}
/**
* Given an error code string, return its uint.
*/
uint TY_(tidyErrorCodeFromKey)(ctmbstr code)
{
uint i = 0;
while (tidyStringsKeys[i].key) {
if ( strcmp(tidyStringsKeys[i].key, code) == 0 )
return tidyStringsKeys[i].value;
i++;
}
return UINT_MAX;
}
/**
* Determines the number of error codes used by Tidy.
*/
static const uint tidyErrorCodeListSize()
{
static uint array_size = 0;
if ( array_size == 0 )
{
while ( tidyStringsKeys[array_size].key ) {
array_size++;
}
}
return array_size;
}
/**
* Initializes the TidyIterator to point to the first item
* in Tidy's list of error codes. Individual items must be
* retrieved with getNextErrorCode();
*/
TidyIterator TY_(getErrorCodeList)()
{
return (TidyIterator)(size_t)1;
}
/**
* Returns the next error code.
*/
uint TY_(getNextErrorCode)( TidyIterator* iter )
{
const tidyStringsKeyItem *item = NULL;
size_t itemIndex;
assert( iter != NULL );
itemIndex = (size_t)*iter;
if ( itemIndex > 0 && itemIndex <= tidyErrorCodeListSize() )
{
item = &tidyStringsKeys[itemIndex - 1];
itemIndex++;
}
*iter = (TidyIterator)( itemIndex <= tidyErrorCodeListSize() ? itemIndex : (size_t)0 );
return item->value;
}
/*********************************************************************
* Documentation of configuration options
*
* Although most of the strings now come from the language module,
* generating the documentation by the console application requires a
* series of cross-references that are generated in this messaging
* module.
*********************************************************************/
#if SUPPORT_CONSOLE_APP
/* Cross-references definitions.
* Note that each list must be terminated with `TidyUnknownOption`.
*/
static const TidyOptionId TidyAsciiCharsLinks[] = { TidyMakeClean, TidyUnknownOption };
static const TidyOptionId TidyBlockTagsLinks[] = { TidyEmptyTags, TidyInlineTags, TidyPreTags, TidyUseCustomTags, TidyUnknownOption };
static const TidyOptionId TidyCharEncodingLinks[] = { TidyInCharEncoding, TidyOutCharEncoding, TidyUnknownOption };
static const TidyOptionId TidyDuplicateAttrsLinks[] = { TidyJoinClasses, TidyJoinStyles, TidyUnknownOption };
static const TidyOptionId TidyEmptyTagsLinks[] = { TidyBlockTags, TidyInlineTags, TidyPreTags, TidyUseCustomTags, TidyUnknownOption };
static const TidyOptionId TidyErrFileLinks[] = { TidyOutFile, TidyUnknownOption };
static const TidyOptionId TidyInCharEncodingLinks[] = { TidyCharEncoding, TidyUnknownOption };
static const TidyOptionId TidyIndentContentLinks[] = { TidyIndentSpaces, TidyUnknownOption };
static const TidyOptionId TidyIndentSpacesLinks[] = { TidyIndentContent, TidyUnknownOption };
static const TidyOptionId TidyInlineTagsLinks[] = { TidyBlockTags, TidyEmptyTags, TidyPreTags, TidyUseCustomTags, TidyUnknownOption };
static const TidyOptionId TidyMergeDivsLinks[] = { TidyMakeClean, TidyMergeSpans, TidyUnknownOption };
static const TidyOptionId TidyMergeSpansLinks[] = { TidyMakeClean, TidyMergeDivs, TidyUnknownOption };
static const TidyOptionId TidyNumEntitiesLinks[] = { TidyDoctype, TidyPreserveEntities, TidyUnknownOption };
static const TidyOptionId TidyOutCharEncodingLinks[] = { TidyCharEncoding, TidyUnknownOption };
static const TidyOptionId TidyOutFileLinks[] = { TidyErrFile, TidyUnknownOption };
static const TidyOptionId TidyPreTagsLinks[] = { TidyBlockTags, TidyEmptyTags, TidyInlineTags, TidyUseCustomTags, TidyUnknownOption };
static const TidyOptionId TidyUseCustomTagsLinks[] = { TidyBlockTags, TidyEmptyTags, TidyInlineTags, TidyPreTags, TidyUnknownOption };
static const TidyOptionId TidyWrapAttValsLinks[] = { TidyWrapScriptlets, TidyLiteralAttribs, TidyUnknownOption };
static const TidyOptionId TidyWrapScriptletsLinks[] = { TidyWrapAttVals, TidyUnknownOption };
static const TidyOptionId TidyXmlDeclLinks[] = { TidyCharEncoding, TidyOutCharEncoding, TidyUnknownOption };
/* Cross-reference assignments.
* We can't build a complex array at compile time and we're not counting on
* any type of initialization, so this two-stage building process is required.
*/
static const TidyOptionDoc docs_xrefs[] =
{
{ TidyAsciiChars, TidyAsciiCharsLinks },
{ TidyBlockTags, TidyBlockTagsLinks },
{ TidyCharEncoding, TidyCharEncodingLinks },
{ TidyDuplicateAttrs, TidyDuplicateAttrsLinks },
{ TidyEmptyTags, TidyEmptyTagsLinks },
{ TidyErrFile, TidyErrFileLinks },
{ TidyInCharEncoding, TidyInCharEncodingLinks },
{ TidyIndentContent, TidyIndentContentLinks },
{ TidyIndentSpaces, TidyIndentSpacesLinks },
{ TidyInlineTags, TidyInlineTagsLinks },
{ TidyMergeDivs, TidyMergeDivsLinks },
{ TidyMergeSpans, TidyMergeSpansLinks },
{ TidyNumEntities, TidyNumEntitiesLinks },
{ TidyOutCharEncoding, TidyOutCharEncodingLinks },
{ TidyOutFile, TidyOutFileLinks },
{ TidyPreTags, TidyPreTagsLinks },
{ TidyUseCustomTags, TidyUseCustomTagsLinks },
{ TidyWrapAttVals, TidyWrapAttValsLinks },
{ TidyWrapScriptlets, TidyWrapScriptletsLinks },
{ TidyXmlDecl, TidyXmlDeclLinks },
{ N_TIDY_OPTIONS }
};
/* Cross-reference retrieval. */
const TidyOptionDoc* TY_(OptGetDocDesc)( TidyOptionId optId )
{
uint i = 0;
while( docs_xrefs[i].opt != N_TIDY_OPTIONS )
{
if ( docs_xrefs[i].opt == optId )
return &docs_xrefs[i];
++i;
}
return NULL;
}
#endif /* SUPPORT_CONSOLE_APP */
/*
* local variables:
* mode: c
* indent-tabs-mode: nil
* c-basic-offset: 4
* eval: (c-set-offset 'substatement-open 0)
* end:
*/