/* attrs.c -- recognize HTML attributes (c) 1998-2000 (W3C) MIT, INRIA, Keio University See tidy.c for the copyright notice. */ #include "platform.h" /* platform independent stuff */ #include "html.h" /* to pull in definition of nodes */ Attribute *attr_href; Attribute *attr_src; Attribute *attr_id; Attribute *attr_name; Attribute *attr_summary; Attribute *attr_alt; Attribute *attr_longdesc; Attribute *attr_usemap; Attribute *attr_ismap; Attribute *attr_language; Attribute *attr_type; Attribute *attr_value; Attribute *attr_content; Attribute *attr_title; Attribute *attr_xmlns; Attribute *attr_datafld; Attribute *attr_width; Attribute *attr_height; AttrCheck CheckUrl; AttrCheck CheckScript; AttrCheck CheckName; AttrCheck CheckId; AttrCheck CheckAlign; AttrCheck CheckValign; AttrCheck CheckBool; extern Bool XmlTags; extern Bool XmlOut; extern char *alt_text; #define HASHSIZE 357 static Attribute *hashtab[HASHSIZE]; /* Bind attribute types to procedures to check values. You can add new procedures for better validation and each procedure has access to the node in which the attribute occurred as well as the attribute name and its value. By default, attributes are checked without regard to the element they are found on. You have the choice of making the procedure test which element is involved or in writing methods for each element which controls exactly how the attributes of that element are checked. This latter approach is best for detecting the absence of required attributes. */ #define TEXT null #define CHARSET null #define TYPE null #define CHARACTER null #define URLS null #define URL CheckUrl #define SCRIPT CheckScript #define ALIGN CheckAlign #define VALIGN CheckValign #define COLOR null #define CLEAR null #define BORDER CheckBool /* kludge */ #define LENGTH null #define CHARSET null #define LANG null #define BOOL CheckBool #define COLS null #define NUMBER null #define LENGTH null #define COORDS null #define DATE null #define TEXTDIR null #define IDREFS null #define IDREF null #define IDDEF CheckId #define NAME CheckName #define TFRAME null #define FBORDER null #define MEDIA null #define FSUBMIT null #define LINKTYPES null #define TRULES null #define SCOPE null #define SHAPE null #define SCROLL null #define TARGET null #define VTYPE null static struct _attrlist { char *name; unsigned versions; AttrCheck *attrchk; } attrlist[] = { {"abbr", VERS_HTML40, TEXT}, {"accept-charset", VERS_HTML40, CHARSET}, {"accept", VERS_ALL, TYPE}, {"accesskey", VERS_HTML40, CHARACTER}, {"action", VERS_ALL, URL}, {"add_date", VERS_NETSCAPE, TEXT}, /* A */ {"align", VERS_ALL, ALIGN}, /* set varies with element */ {"alink", VERS_LOOSE, COLOR}, {"alt", VERS_ALL, TEXT}, {"archive", VERS_HTML40, URLS}, /* space or comma separated list */ {"axis", VERS_HTML40, TEXT}, {"background", VERS_LOOSE, URL}, {"bgcolor", VERS_LOOSE, COLOR}, {"bgproperties", VERS_PROPRIETARY, TEXT}, /* BODY "fixed" fixes background */ {"border", VERS_ALL, BORDER}, /* like LENGTH + "border" */ {"bordercolor", VERS_MICROSOFT, COLOR}, /* used on TABLE */ {"bottommargin", VERS_MICROSOFT, NUMBER}, /* used on BODY */ {"cellpadding", VERS_FROM32, LENGTH}, /* % or pixel values */ {"cellspacing", VERS_FROM32, LENGTH}, {"char", VERS_HTML40, CHARACTER}, {"charoff", VERS_HTML40, LENGTH}, {"charset", VERS_HTML40, CHARSET}, {"checked", VERS_ALL, BOOL}, /* i.e. "checked" or absent */ {"cite", VERS_HTML40, URL}, {"class", VERS_HTML40, TEXT}, {"classid", VERS_HTML40, URL}, {"clear", VERS_LOOSE, CLEAR}, /* BR: left, right, all */ {"code", VERS_LOOSE, TEXT}, /* APPLET */ {"codebase", VERS_HTML40, URL}, /* OBJECT */ {"codetype", VERS_HTML40, TYPE}, /* OBJECT */ {"color", VERS_LOOSE, COLOR}, /* BASEFONT, FONT */ {"cols", VERS_IFRAMES, COLS}, /* TABLE & FRAMESET */ {"colspan", VERS_FROM32, NUMBER}, {"compact", VERS_ALL, BOOL}, /* lists */ {"content", VERS_ALL, TEXT}, /* META */ {"coords", VERS_FROM32, COORDS}, /* AREA, A */ {"data", VERS_HTML40, URL}, /* OBJECT */ {"datafld", VERS_MICROSOFT, TEXT}, /* used on DIV, IMG */ {"dataformatas", VERS_MICROSOFT, TEXT}, /* used on DIV, IMG */ {"datapagesize", VERS_MICROSOFT, NUMBER}, /* used on DIV, IMG */ {"datasrc", VERS_MICROSOFT, URL}, /* used on TABLE */ {"datetime", VERS_HTML40, DATE}, /* INS, DEL */ {"declare", VERS_HTML40, BOOL}, /* OBJECT */ {"defer", VERS_HTML40, BOOL}, /* SCRIPT */ {"dir", VERS_HTML40, TEXTDIR}, /* ltr or rtl */ {"disabled", VERS_HTML40, BOOL}, /* form fields */ {"enctype", VERS_ALL, TYPE}, /* FORM */ {"face", VERS_LOOSE, TEXT}, /* BASEFONT, FONT */ {"for", VERS_HTML40, IDREF}, /* LABEL */ {"frame", VERS_HTML40, TFRAME}, /* TABLE */ {"frameborder", VERS_FRAMES, FBORDER}, /* 0 or 1 */ {"framespacing", VERS_PROPRIETARY, NUMBER}, /* pixel value */ {"gridx", VERS_PROPRIETARY, NUMBER}, /* TABLE Adobe golive*/ {"gridy", VERS_PROPRIETARY, NUMBER}, /* TABLE Adobe golive */ {"headers", VERS_HTML40, IDREFS}, /* table cells */ {"height", VERS_ALL, LENGTH}, /* pixels only for TH/TD */ {"href", VERS_ALL, URL}, /* A, AREA, LINK and BASE */ {"hreflang", VERS_HTML40, LANG}, /* A, LINK */ {"hspace", VERS_ALL, NUMBER}, /* APPLET, IMG, OBJECT */ {"http-equiv", VERS_ALL, TEXT}, /* META */ {"id", VERS_HTML40, IDDEF}, {"ismap", VERS_ALL, BOOL}, /* IMG */ {"label", VERS_HTML40, TEXT}, /* OPT, OPTGROUP */ {"lang", VERS_HTML40, LANG}, {"language", VERS_LOOSE, TEXT}, /* SCRIPT */ {"last_modified", VERS_NETSCAPE, TEXT}, /* A */ {"last_visit", VERS_NETSCAPE, TEXT}, /* A */ {"leftmargin", VERS_MICROSOFT, NUMBER}, /* used on BODY */ {"link", VERS_LOOSE, COLOR}, /* BODY */ {"longdesc", VERS_HTML40, URL}, /* IMG */ {"lowsrc", VERS_PROPRIETARY, URL}, /* IMG */ {"marginheight", VERS_IFRAMES, NUMBER}, /* FRAME, IFRAME, BODY */ {"marginwidth", VERS_IFRAMES, NUMBER}, /* ditto */ {"maxlength", VERS_ALL, NUMBER}, /* INPUT */ {"media", VERS_HTML40, MEDIA}, /* STYLE, LINK */ {"method", VERS_ALL, FSUBMIT}, /* FORM: get or post */ {"multiple", VERS_ALL, BOOL}, /* SELECT */ {"name", VERS_ALL, NAME}, {"nohref", VERS_FROM32, BOOL}, /* AREA */ {"noresize", VERS_FRAMES, BOOL}, /* FRAME */ {"noshade", VERS_LOOSE, BOOL}, /* HR */ {"nowrap", VERS_LOOSE, BOOL}, /* table cells */ {"object", VERS_HTML40_LOOSE, TEXT}, /* APPLET */ {"onblur", VERS_HTML40, SCRIPT}, /* event */ {"onchange", VERS_HTML40, SCRIPT}, /* event */ {"onclick", VERS_HTML40, SCRIPT}, /* event */ {"ondblclick", VERS_HTML40, SCRIPT}, /* event */ {"onkeydown", VERS_HTML40, SCRIPT}, /* event */ {"onkeypress", VERS_HTML40, SCRIPT}, /* event */ {"onkeyup", VERS_HTML40, SCRIPT}, /* event */ {"onload", VERS_HTML40, SCRIPT}, /* event */ {"onmousedown", VERS_HTML40, SCRIPT}, /* event */ {"onmousemove", VERS_HTML40, SCRIPT}, /* event */ {"onmouseout", VERS_HTML40, SCRIPT}, /* event */ {"onmouseover", VERS_HTML40, SCRIPT}, /* event */ {"onmouseup", VERS_HTML40, SCRIPT}, /* event */ {"onsubmit", VERS_HTML40, SCRIPT}, /* event */ {"onreset", VERS_HTML40, SCRIPT}, /* event */ {"onselect", VERS_HTML40, SCRIPT}, /* event */ {"onunload", VERS_HTML40, SCRIPT}, /* event */ {"onafterupdate", VERS_MICROSOFT, SCRIPT}, /* form fields */ {"onbeforeupdate", VERS_MICROSOFT, SCRIPT}, /* form fields */ {"onerrorupdate", VERS_MICROSOFT, SCRIPT}, /* form fields */ {"onrowenter", VERS_MICROSOFT, SCRIPT}, /* form fields */ {"onrowexit", VERS_MICROSOFT, SCRIPT}, /* form fields */ {"onbeforeunload", VERS_MICROSOFT, SCRIPT}, /* form fields */ {"ondatasetchanged", VERS_MICROSOFT, SCRIPT}, /* object, applet */ {"ondataavailable", VERS_MICROSOFT, SCRIPT}, /* object, applet */ {"ondatasetcomplete",VERS_MICROSOFT, SCRIPT}, /* object, applet */ {"profile", VERS_HTML40, URL}, /* HEAD */ {"prompt", VERS_LOOSE, TEXT}, /* ISINDEX */ {"readonly", VERS_HTML40, BOOL}, /* form fields */ {"rel", VERS_ALL, LINKTYPES}, /* A, LINK */ {"rev", VERS_ALL, LINKTYPES}, /* A, LINK */ {"rightmargin", VERS_MICROSOFT, NUMBER}, /* used on BODY */ {"rows", VERS_ALL, NUMBER}, /* TEXTAREA */ {"rowspan", VERS_ALL, NUMBER}, /* table cells */ {"rules", VERS_HTML40, TRULES}, /* TABLE */ {"scheme", VERS_HTML40, TEXT}, /* META */ {"scope", VERS_HTML40, SCOPE}, /* table cells */ {"scrolling", VERS_IFRAMES, SCROLL}, /* yes, no or auto */ {"selected", VERS_ALL, BOOL}, /* OPTION */ {"shape", VERS_FROM32, SHAPE}, /* AREA, A */ {"showgrid", VERS_PROPRIETARY, BOOL}, /* TABLE Adobe golive */ {"showgridx", VERS_PROPRIETARY, BOOL}, /* TABLE Adobe golive*/ {"showgridy", VERS_PROPRIETARY, BOOL}, /* TABLE Adobe golive*/ {"size", VERS_LOOSE, NUMBER}, /* HR, FONT, BASEFONT, SELECT */ {"span", VERS_HTML40, NUMBER}, /* COL, COLGROUP */ {"src", (VERS_ALL|VERS_FRAMES), URL}, /* IMG, FRAME, IFRAME */ {"standby", VERS_HTML40, TEXT}, /* OBJECT */ {"start", VERS_ALL, NUMBER}, /* OL */ {"style", VERS_HTML40, TEXT}, {"summary", VERS_HTML40, TEXT}, /* TABLE */ {"tabindex", VERS_HTML40, NUMBER}, /* fields, OBJECT and A */ {"target", VERS_HTML40, TARGET}, /* names a frame/window */ {"text", VERS_LOOSE, COLOR}, /* BODY */ {"title", VERS_HTML40, TEXT}, /* text tool tip */ {"topmargin", VERS_MICROSOFT, NUMBER}, /* used on BODY */ {"type", VERS_FROM32, TYPE}, /* also used by SPACER */ {"usemap", VERS_ALL, BOOL}, /* things with images */ {"valign", VERS_FROM32, VALIGN}, {"value", VERS_ALL, TEXT}, /* OPTION, PARAM */ {"valuetype", VERS_HTML40, VTYPE}, /* PARAM: data, ref, object */ {"version", VERS_ALL, TEXT}, /* HTML */ {"vlink", VERS_LOOSE, COLOR}, /* BODY */ {"vspace", VERS_LOOSE, NUMBER}, /* IMG, OBJECT, APPLET */ {"width", VERS_ALL, LENGTH}, /* pixels only for TD/TH */ {"wrap", VERS_NETSCAPE, TEXT}, /* textarea */ {"xml:lang", VERS_XML, TEXT}, /* XML language */ {"xmlns", VERS_ALL, TEXT}, /* name space */ /* this must be the final entry */ {null, 0, 0} }; static unsigned hash(char *s) { unsigned hashval; for (hashval = 0; *s != '\0'; s++) hashval = *s + 31*hashval; return hashval % HASHSIZE; } static Attribute *lookup(char *s) { Attribute *np; for (np = hashtab[hash(s)]; np != null; np = np->next) if (wstrcmp(s, np->name) == 0) return np; return null; } static Attribute *install(char *name, uint versions, AttrCheck *attrchk) { Attribute *np; unsigned hashval; if ((np = lookup(name)) == null) { np = (Attribute *)MemAlloc(sizeof(*np)); if (np == null || (np->name = wstrdup(name)) == null) return null; hashval = hash(name); np->next = hashtab[hashval]; hashtab[hashval] = np; } np->versions = versions; np->attrchk = attrchk; np->nowrap = no; np->literal = no; return np; } static void SetNoWrap(Attribute *attr) { attr->nowrap = yes; /* defaults to no */ } /* public method for finding attribute definition by name */ Attribute *FindAttribute(AttVal *attval) { Attribute *np; if (attval->attribute && (np = lookup(attval->attribute))) return np; return null; } AttVal *GetAttrByName(Node *node, char *name) { AttVal *attr; for (attr = node->attributes; attr; attr = attr->next) { if (wstrcmp(attr->attribute, name) == 0) break; } return attr; } void AddAttribute(Node *node, char *name, char *value) { AttVal *av = NewAttribute(); av->delim = '"'; av->attribute = wstrdup(name); av->value = wstrdup(value); av->dict = FindAttribute(av); if (node->attributes == null) node->attributes = av; else /* append to end of attributes */ { AttVal *here = node->attributes; while (here->next) here = here->next; here->next = av; } } Bool IsUrl(char *attrname) { Attribute *np; return (Bool)((np = lookup(attrname)) && np->attrchk == URL); } Bool IsScript(char *attrname) { Attribute *np; return (Bool)((np = lookup(attrname)) && np->attrchk == SCRIPT); } Bool IsLiteralAttribute(char *attrname) { Attribute *np; return (Bool)((np = lookup(attrname)) && np->literal); } /* public method for inititializing attribute dictionary */ void InitAttrs(void) { struct _attrlist *ap; for(ap = attrlist; ap->name != null; ++ap) install(ap->name, ap->versions, ap->attrchk); attr_href = lookup("href"); attr_src = lookup("src"); attr_id = lookup("id"); attr_name = lookup("name"); attr_summary = lookup("summary"); attr_alt = lookup("alt"); attr_longdesc = lookup("longdesc"); attr_usemap = lookup("usemap"); attr_ismap = lookup("ismap"); attr_language = lookup("language"); attr_type = lookup("type"); attr_title = lookup("title"); attr_xmlns = lookup("xmlns"); attr_datafld = lookup("datafld"); attr_value = lookup("value"); attr_content = lookup("content"); attr_width = lookup("width"); attr_height = lookup("height"); SetNoWrap(attr_alt); SetNoWrap(attr_value); SetNoWrap(attr_content); } /* Henry Zrepa reports that some folk are using embed with script attributes where newlines are signficant. These need to be declared and handled specially! */ void DeclareLiteralAttrib(char *name) { Attribute *attrib = lookup(name); if (attrib = null) attrib = install(name, VERS_PROPRIETARY, null); attrib->literal = yes; } void FreeAttrTable(void) { Attribute *dict, *next; int i; for (i = 0; i < HASHSIZE; ++i) { dict = hashtab[i]; while(dict) { next = dict->next; MemFree(dict->name); MemFree(dict); dict = next; } hashtab[i] = null; } } /* the same attribute name can't be used more than once in each element */ static void CheckUniqueAttribute(Lexer *lexer, Node *node, AttVal *attval) { AttVal *attr; int count = 0; for (attr = attval->next; attr; attr = attr->next) { if (attr->asp == null && attr->php == null && wstrcasecmp(attval->attribute, attr->attribute) == 0) ++count; } if (count > 0) ReportAttrError(lexer, node, attval->attribute, REPEATED_ATTRIBUTE); } void CheckUniqueAttributes(Lexer *lexer, Node *node) { AttVal *attval; for (attval = node->attributes; attval != null; attval = attval->next) { if (attval->asp == null && attval->php == null) CheckUniqueAttribute(lexer, node, attval); } } /* ignore unknown attributes for proprietary elements */ Attribute *CheckAttribute(Lexer *lexer, Node *node, AttVal *attval) { Attribute *attribute; if (attval->asp == null && attval->php == null) CheckUniqueAttribute(lexer, node, attval); if ((attribute = attval->dict) != null) { /* title is vers 2.0 for A and LINK otherwise vers 4.0 */ if (attribute == attr_title && (node->tag == tag_a || node->tag == tag_link)) lexer->versions &= VERS_ALL; else if (attribute->versions & VERS_XML) { if (!(XmlTags || XmlOut)) ReportAttrError(lexer, node, attval->attribute, XML_ATTRIBUTE_VALUE); } else lexer->versions &= attribute->versions; if (attribute->attrchk) attribute->attrchk(lexer, node, attval); } else if (!XmlTags && !(node->tag == null) && attval->asp == null && !(node->tag && (node->tag->versions & VERS_PROPRIETARY))) ReportAttrError(lexer, node, attval->attribute, UNKNOWN_ATTRIBUTE); return attribute; } Bool IsBoolAttribute(AttVal *attval) { Attribute *attribute; if ((attribute = attval->dict) != null) { if (attribute->attrchk == CheckBool) return yes; } return no; } /* methods for checking value of a specific attribute */ void CheckUrl(Lexer *lexer, Node *node, AttVal *attval) { char c, *p = attval->value; if (p == null) ReportAttrError(lexer, node, attval->attribute, MISSING_ATTR_VALUE); else if (FixBackslash) { while ((c = *p)) { if (c =='\\') *p = '/'; ++p; } } } void CheckScript(Lexer *lexer, Node *node, AttVal *attval) { } void CheckName(Lexer *lexer, Node *node, AttVal *attval) { } void CheckId(Lexer *lexer, Node *node, AttVal *attval) { } void CheckBool(Lexer *lexer, Node *node, AttVal *attval) { } void CheckAlign(Lexer *lexer, Node *node, AttVal *attval) { char *value; /* IMG, OBJECT, APPLET and EMBED use align for vertical position */ if (node->tag && (node->tag->model & CM_IMG)) { CheckValign(lexer, node, attval); return; } value = attval->value; if (value == null) ReportAttrError(lexer, node, attval->attribute, MISSING_ATTR_VALUE); else if (! (wstrcasecmp(value, "left") == 0 || wstrcasecmp(value, "center") == 0 || wstrcasecmp(value, "right") == 0 || wstrcasecmp(value, "justify") == 0)) ReportAttrError(lexer, node, attval->value, BAD_ATTRIBUTE_VALUE); } void CheckValign(Lexer *lexer, Node *node, AttVal *attval) { char *value; value = attval->value; if (value == null) ReportAttrError(lexer, node, attval->attribute, MISSING_ATTR_VALUE); else if (wstrcasecmp(value, "top") == 0 || wstrcasecmp(value, "middle") == 0 || wstrcasecmp(value, "bottom") == 0 || wstrcasecmp(value, "baseline") == 0) { /* all is fine */ } else if (wstrcasecmp(value, "left") == 0 || wstrcasecmp(value, "right") == 0) { if (!(node->tag && (node->tag->model & CM_IMG))) ReportAttrError(lexer, node, value, BAD_ATTRIBUTE_VALUE); } else if (wstrcasecmp(value, "texttop") == 0 || wstrcasecmp(value, "absmiddle") == 0 || wstrcasecmp(value, "absbottom") == 0 || wstrcasecmp(value, "textbottom") == 0) { lexer->versions &= VERS_PROPRIETARY; ReportAttrError(lexer, node, value, PROPRIETARY_ATTR_VALUE); } else ReportAttrError(lexer, node, value, BAD_ATTRIBUTE_VALUE); } /* default method for checking an element's attributes */ void CheckAttributes(Lexer *lexer, Node *node) { AttVal *attval; for (attval = node->attributes; attval != null; attval = attval->next) CheckAttribute(lexer, node, attval); } /* methods for checking attributes for specific elements */ void CheckHR(Lexer *lexer, Node *node) { if (GetAttrByName(node, "src")) ReportAttrError(lexer, node, "src", PROPRIETARY_ATTR_VALUE); } void CheckIMG(Lexer *lexer, Node *node) { AttVal *attval; Attribute *attribute; Bool HasAlt = no; Bool HasSrc = no; Bool HasUseMap = no; Bool HasIsMap = no; Bool HasDataFld = no; CheckUniqueAttributes(lexer, node); for (attval = node->attributes; attval != null; attval = attval->next) { attribute = CheckAttribute(lexer, node, attval); if (attribute == attr_alt) HasAlt = yes; else if (attribute == attr_src) HasSrc = yes; else if (attribute == attr_usemap) HasUseMap = yes; else if (attribute == attr_ismap) HasIsMap = yes; else if (attribute == attr_datafld) HasDataFld = yes; else if (attribute == attr_width || attribute == attr_height) lexer->versions &= ~VERS_HTML20; } if (!HasAlt) { lexer->badAccess |= MISSING_IMAGE_ALT; ReportAttrError(lexer, node, "alt", MISSING_ATTRIBUTE); if (alt_text) AddAttribute(node, "alt", alt_text); } if (!HasSrc && !HasDataFld) ReportAttrError(lexer, node, "src", MISSING_ATTRIBUTE); if (HasIsMap && !HasUseMap) ReportAttrError(lexer, node, "ismap", MISSING_IMAGEMAP); } void CheckAnchor(Lexer *lexer, Node *node) { CheckUniqueAttributes(lexer, node); FixId(lexer, node); } void CheckMap(Lexer *lexer, Node *node) { CheckUniqueAttributes(lexer, node); FixId(lexer, node); } void CheckTableCell(Lexer *lexer, Node *node) { CheckUniqueAttributes(lexer, node); /* HTML4 strict doesn't allow mixed content for elements with %block; as their content model */ if (GetAttrByName(node, "width") || GetAttrByName(node, "height")) lexer->versions &= ~VERS_HTML40_STRICT; } void CheckCaption(Lexer *lexer, Node *node) { AttVal *attval; char *value = null; CheckUniqueAttributes(lexer, node); for (attval = node->attributes; attval != null; attval = attval->next) { if (wstrcasecmp(attval->attribute, "align") == 0) { value = attval->value; break; } } if (value != null) { if (wstrcasecmp(value, "left") == 0 || wstrcasecmp(value, "right") == 0) lexer->versions &= (VERS_HTML40_LOOSE|VERS_FRAMES); else if (wstrcasecmp(value, "top") == 0 || wstrcasecmp(value, "bottom") == 0) lexer->versions &= VERS_FROM32; else ReportAttrError(lexer, node, value, BAD_ATTRIBUTE_VALUE); } } void CheckHTML(Lexer *lexer, Node *node) { AttVal *attval; Attribute *attribute; CheckUniqueAttributes(lexer, node); for (attval = node->attributes; attval != null; attval = attval->next) { attribute = CheckAttribute(lexer, node, attval); if (attribute == attr_xmlns) lexer->isvoyager = yes; } } void CheckAREA(Lexer *lexer, Node *node) { AttVal *attval; Attribute *attribute; Bool HasAlt = no; Bool HasHref = no; CheckUniqueAttributes(lexer, node); for (attval = node->attributes; attval != null; attval = attval->next) { attribute = CheckAttribute(lexer, node, attval); if (attribute == attr_alt) HasAlt = yes; else if (attribute == attr_href) HasHref = yes; } if (!HasAlt) { lexer->badAccess |= MISSING_LINK_ALT; ReportAttrError(lexer, node, "alt", MISSING_ATTRIBUTE); } if (!HasHref) ReportAttrError(lexer, node, "href", MISSING_ATTRIBUTE); } void CheckTABLE(Lexer *lexer, Node *node) { AttVal *attval; Attribute *attribute; Bool HasSummary = no; CheckUniqueAttributes(lexer, node); for (attval = node->attributes; attval != null; attval = attval->next) { attribute = CheckAttribute(lexer, node, attval); if (attribute == attr_summary) HasSummary = yes; } /* suppress warning for missing summary for HTML 2.0 and HTML 3.2 */ if (!HasSummary && lexer->doctype != VERS_HTML20 && lexer->doctype != VERS_HTML32) { lexer->badAccess |= MISSING_SUMMARY; ReportAttrError(lexer, node, "summary", MISSING_ATTRIBUTE); } /* convert to
*/ if (XmlOut && (attval = GetAttrByName(node, "border"))) { if (attval->value == null) attval->value = wstrdup("1"); } } /* add missing type attribute when appropriate */ void CheckSCRIPT(Lexer *lexer, Node *node) { AttVal *lang, *type; char buf[16]; CheckUniqueAttributes(lexer, node); lang = GetAttrByName(node, "language"); type = GetAttrByName(node, "type"); if (!type) { ReportAttrError(lexer, node, "type", MISSING_ATTRIBUTE); /* check for javascript */ if (lang) { wstrncpy(buf, lang->value, 10); buf[10] = '\0'; if ( (wstrncasecmp(buf, "javascript", 10) == 0) || (wstrncasecmp(buf, "jscript", 7) == 0) ) { AddAttribute(node, "type", "text/javascript"); } } else AddAttribute(node, "type", "text/javascript"); } } /* add missing type attribute when appropriate */ void CheckSTYLE(Lexer *lexer, Node *node) { AttVal *type = GetAttrByName(node, "type"); CheckUniqueAttributes(lexer, node); if (!type) { ReportAttrError(lexer, node, "type", MISSING_ATTRIBUTE); AddAttribute(node, "type", "text/css"); } } /* add missing type attribute when appropriate */ void CheckLINK(Lexer *lexer, Node *node) { AttVal *rel = GetAttrByName(node, "rel"); CheckUniqueAttributes(lexer, node); if (rel && rel->value && wstrcmp(rel->value, "stylesheet") == 0) { AttVal *type = GetAttrByName(node, "type"); if (!type) { ReportAttrError(lexer, node, "type", MISSING_ATTRIBUTE); AddAttribute(node, "type", "text/css"); } } }