1 /* attrs.c -- recognize HTML attributes
2 
3   (c) 1998-2006 (W3C) MIT, ERCIM, Keio University
4   See tidy.h for the copyright notice.
5 
6   CVS Info :
7 
8     $Author: arnaud02 $
9     $Date: 2006/02/10 17:20:26 $
10     $Revision: 1.121 $
11 
12 */
13 
14 #include "tidy-int.h"
15 #include "attrs.h"
16 #include "message.h"
17 #include "tmbstr.h"
18 #include "utf8.h"
19 
20 static AttrCheck CheckAction;
21 
22 #define CH_PCDATA      NULL
23 #define CH_CHARSET     NULL
24 #define CH_TYPE        CheckType
25 #define CH_XTYPE       NULL
26 #define CH_CHARACTER   NULL
27 #define CH_URLS        NULL
28 #define CH_URL         CheckUrl
29 #define CH_SCRIPT      CheckScript
30 #define CH_ALIGN       CheckAlign
31 #define CH_VALIGN      CheckValign
32 #define CH_COLOR       CheckColor
33 #define CH_CLEAR       CheckClear
34 #define CH_BORDER      CheckBool     /* kludge */
35 #define CH_LANG        CheckLang
36 #define CH_BOOL        CheckBool
37 #define CH_COLS        NULL
38 #define CH_NUMBER      CheckNumber
39 #define CH_LENGTH      CheckLength
40 #define CH_COORDS      NULL
41 #define CH_DATE        NULL
42 #define CH_TEXTDIR     CheckTextDir
43 #define CH_IDREFS      NULL
44 #define CH_IDREF       NULL
45 #define CH_IDDEF       CheckId
46 #define CH_NAME        CheckName
47 #define CH_TFRAME      NULL
48 #define CH_FBORDER     NULL
49 #define CH_MEDIA       NULL
50 #define CH_FSUBMIT     CheckFsubmit
51 #define CH_LINKTYPES   NULL
52 #define CH_TRULES      NULL
53 #define CH_SCOPE       CheckScope
54 #define CH_SHAPE       CheckShape
55 #define CH_SCROLL      CheckScroll
56 #define CH_TARGET      CheckTarget
57 #define CH_VTYPE       CheckVType
58 #define CH_ACTION      CheckAction
59 
60 static const Attribute attribute_defs [] =
61 {
62   { TidyAttr_UNKNOWN,           "unknown!",          VERS_PROPRIETARY,  NULL         },
63   { TidyAttr_ABBR,              "abbr",              VERS_HTML40,       CH_PCDATA    },
64   { TidyAttr_ACCEPT,            "accept",            VERS_ALL,          CH_XTYPE     },
65   { TidyAttr_ACCEPT_CHARSET,    "accept-charset",    VERS_HTML40,       CH_CHARSET   },
66   { TidyAttr_ACCESSKEY,         "accesskey",         VERS_HTML40,       CH_CHARACTER },
67   { TidyAttr_ACTION,            "action",            VERS_ALL,          CH_ACTION    },
68   { TidyAttr_ADD_DATE,          "add_date",          VERS_NETSCAPE,     CH_PCDATA    }, /* A */
69   { TidyAttr_ALIGN,             "align",             VERS_ALL,          CH_ALIGN     }, /* varies by element */
70   { TidyAttr_ALINK,             "alink",             VERS_LOOSE,        CH_COLOR     },
71   { TidyAttr_ALT,               "alt",               VERS_ALL,          CH_PCDATA    }, /* nowrap */
72   { TidyAttr_ARCHIVE,           "archive",           VERS_HTML40,       CH_URLS      }, /* space or comma separated list */
73   { TidyAttr_AXIS,              "axis",              VERS_HTML40,       CH_PCDATA    },
74   { TidyAttr_BACKGROUND,        "background",        VERS_LOOSE,        CH_URL       },
75   { TidyAttr_BGCOLOR,           "bgcolor",           VERS_LOOSE,        CH_COLOR     },
76   { TidyAttr_BGPROPERTIES,      "bgproperties",      VERS_PROPRIETARY,  CH_PCDATA    }, /* BODY "fixed" fixes background */
77   { TidyAttr_BORDER,            "border",            VERS_ALL,          CH_BORDER    }, /* like LENGTH + "border" */
78   { TidyAttr_BORDERCOLOR,       "bordercolor",       VERS_MICROSOFT,    CH_COLOR     }, /* used on TABLE */
79   { TidyAttr_BOTTOMMARGIN,      "bottommargin",      VERS_MICROSOFT,    CH_NUMBER    }, /* used on BODY */
80   { TidyAttr_CELLPADDING,       "cellpadding",       VERS_FROM32,       CH_LENGTH    }, /* % or pixel values */
81   { TidyAttr_CELLSPACING,       "cellspacing",       VERS_FROM32,       CH_LENGTH    },
82   { TidyAttr_CHAR,              "char",              VERS_HTML40,       CH_CHARACTER },
83   { TidyAttr_CHAROFF,           "charoff",           VERS_HTML40,       CH_LENGTH    },
84   { TidyAttr_CHARSET,           "charset",           VERS_HTML40,       CH_CHARSET   },
85   { TidyAttr_CHECKED,           "checked",           VERS_ALL,          CH_BOOL      }, /* i.e. "checked" or absent */
86   { TidyAttr_CITE,              "cite",              VERS_HTML40,       CH_URL       },
87   { TidyAttr_CLASS,             "class",             VERS_HTML40,       CH_PCDATA    },
88   { TidyAttr_CLASSID,           "classid",           VERS_HTML40,       CH_URL       },
89   { TidyAttr_CLEAR,             "clear",             VERS_LOOSE,        CH_CLEAR     }, /* BR: left, right, all */
90   { TidyAttr_CODE,              "code",              VERS_LOOSE,        CH_PCDATA    }, /* APPLET */
91   { TidyAttr_CODEBASE,          "codebase",          VERS_HTML40,       CH_URL       }, /* OBJECT */
92   { TidyAttr_CODETYPE,          "codetype",          VERS_HTML40,       CH_XTYPE     }, /* OBJECT */
93   { TidyAttr_COLOR,             "color",             VERS_LOOSE,        CH_COLOR     }, /* BASEFONT, FONT */
94   { TidyAttr_COLS,              "cols",              VERS_IFRAME,       CH_COLS      }, /* TABLE & FRAMESET */
95   { TidyAttr_COLSPAN,           "colspan",           VERS_FROM32,       CH_NUMBER    },
96   { TidyAttr_COMPACT,           "compact",           VERS_ALL,          CH_BOOL      }, /* lists */
97   { TidyAttr_CONTENT,           "content",           VERS_ALL,          CH_PCDATA    },
98   { TidyAttr_COORDS,            "coords",            VERS_FROM32,       CH_COORDS    }, /* AREA, A */
99   { TidyAttr_DATA,              "data",              VERS_HTML40,       CH_URL       }, /* OBJECT */
100   { TidyAttr_DATAFLD,           "datafld",           VERS_MICROSOFT,    CH_PCDATA    }, /* used on DIV, IMG */
101   { TidyAttr_DATAFORMATAS,      "dataformatas",      VERS_MICROSOFT,    CH_PCDATA    }, /* used on DIV, IMG */
102   { TidyAttr_DATAPAGESIZE,      "datapagesize",      VERS_MICROSOFT,    CH_NUMBER    }, /* used on DIV, IMG */
103   { TidyAttr_DATASRC,           "datasrc",           VERS_MICROSOFT,    CH_URL       }, /* used on TABLE */
104   { TidyAttr_DATETIME,          "datetime",          VERS_HTML40,       CH_DATE      }, /* INS, DEL */
105   { TidyAttr_DECLARE,           "declare",           VERS_HTML40,       CH_BOOL      }, /* OBJECT */
106   { TidyAttr_DEFER,             "defer",             VERS_HTML40,       CH_BOOL      }, /* SCRIPT */
107   { TidyAttr_DIR,               "dir",               VERS_HTML40,       CH_TEXTDIR   }, /* ltr or rtl */
108   { TidyAttr_DISABLED,          "disabled",          VERS_HTML40,       CH_BOOL      }, /* form fields */
109   { TidyAttr_ENCODING,          "encoding",          VERS_XML,          CH_PCDATA    }, /* <?xml?> */
110   { TidyAttr_ENCTYPE,           "enctype",           VERS_ALL,          CH_XTYPE     }, /* FORM */
111   { TidyAttr_FACE,              "face",              VERS_LOOSE,        CH_PCDATA    }, /* BASEFONT, FONT */
112   { TidyAttr_FOR,               "for",               VERS_HTML40,       CH_IDREF     }, /* LABEL */
113   { TidyAttr_FRAME,             "frame",             VERS_HTML40,       CH_TFRAME    }, /* TABLE */
114   { TidyAttr_FRAMEBORDER,       "frameborder",       VERS_FRAMESET,     CH_FBORDER   }, /* 0 or 1 */
115   { TidyAttr_FRAMESPACING,      "framespacing",      VERS_PROPRIETARY,  CH_NUMBER    },
116   { TidyAttr_GRIDX,             "gridx",             VERS_PROPRIETARY,  CH_NUMBER    }, /* TABLE Adobe golive*/
117   { TidyAttr_GRIDY,             "gridy",             VERS_PROPRIETARY,  CH_NUMBER    }, /* TABLE Adobe golive */
118   { TidyAttr_HEADERS,           "headers",           VERS_HTML40,       CH_IDREFS    }, /* table cells */
119   { TidyAttr_HEIGHT,            "height",            VERS_ALL,          CH_LENGTH    }, /* pixels only for TH/TD */
120   { TidyAttr_HREF,              "href",              VERS_ALL,          CH_URL       }, /* A, AREA, LINK and BASE */
121   { TidyAttr_HREFLANG,          "hreflang",          VERS_HTML40,       CH_LANG      }, /* A, LINK */
122   { TidyAttr_HSPACE,            "hspace",            VERS_ALL,          CH_NUMBER    }, /* APPLET, IMG, OBJECT */
123   { TidyAttr_HTTP_EQUIV,        "http-equiv",        VERS_ALL,          CH_PCDATA    }, /* META */
124   { TidyAttr_ID,                "id",                VERS_HTML40,       CH_IDDEF     },
125   { TidyAttr_ISMAP,             "ismap",             VERS_ALL,          CH_BOOL      }, /* IMG */
126   { TidyAttr_LABEL,             "label",             VERS_HTML40,       CH_PCDATA    }, /* OPT, OPTGROUP */
127   { TidyAttr_LANG,              "lang",              VERS_HTML40,       CH_LANG      },
128   { TidyAttr_LANGUAGE,          "language",          VERS_LOOSE,        CH_PCDATA    }, /* SCRIPT */
129   { TidyAttr_LAST_MODIFIED,     "last_modified",     VERS_NETSCAPE,     CH_PCDATA    }, /* A */
130   { TidyAttr_LAST_VISIT,        "last_visit",        VERS_NETSCAPE,     CH_PCDATA    }, /* A */
131   { TidyAttr_LEFTMARGIN,        "leftmargin",        VERS_MICROSOFT,    CH_NUMBER    }, /* used on BODY */
132   { TidyAttr_LINK,              "link",              VERS_LOOSE,        CH_COLOR     }, /* BODY */
133   { TidyAttr_LONGDESC,          "longdesc",          VERS_HTML40,       CH_URL       }, /* IMG */
134   { TidyAttr_LOWSRC,            "lowsrc",            VERS_PROPRIETARY,  CH_URL       }, /* IMG */
135   { TidyAttr_MARGINHEIGHT,      "marginheight",      VERS_IFRAME,       CH_NUMBER    }, /* FRAME, IFRAME, BODY */
136   { TidyAttr_MARGINWIDTH,       "marginwidth",       VERS_IFRAME,       CH_NUMBER    }, /* ditto */
137   { TidyAttr_MAXLENGTH,         "maxlength",         VERS_ALL,          CH_NUMBER    }, /* INPUT */
138   { TidyAttr_MEDIA,             "media",             VERS_HTML40,       CH_MEDIA     }, /* STYLE, LINK */
139   { TidyAttr_METHOD,            "method",            VERS_ALL,          CH_FSUBMIT   }, /* FORM: get or post */
140   { TidyAttr_MULTIPLE,          "multiple",          VERS_ALL,          CH_BOOL      }, /* SELECT */
141   { TidyAttr_NAME,              "name",              VERS_ALL,          CH_NAME      },
142   { TidyAttr_NOHREF,            "nohref",            VERS_FROM32,       CH_BOOL      }, /* AREA */
143   { TidyAttr_NORESIZE,          "noresize",          VERS_FRAMESET,     CH_BOOL      }, /* FRAME */
144   { TidyAttr_NOSHADE,           "noshade",           VERS_LOOSE,        CH_BOOL      }, /* HR */
145   { TidyAttr_NOWRAP,            "nowrap",            VERS_LOOSE,        CH_BOOL      }, /* table cells */
146   { TidyAttr_OBJECT,            "object",            VERS_HTML40_LOOSE, CH_PCDATA    }, /* APPLET */
147   { TidyAttr_OnAFTERUPDATE,     "onafterupdate",     VERS_MICROSOFT,    CH_SCRIPT    },
148   { TidyAttr_OnBEFOREUNLOAD,    "onbeforeunload",    VERS_MICROSOFT,    CH_SCRIPT    },
149   { TidyAttr_OnBEFOREUPDATE,    "onbeforeupdate",    VERS_MICROSOFT,    CH_SCRIPT    },
150   { TidyAttr_OnBLUR,            "onblur",            VERS_EVENTS,       CH_SCRIPT    }, /* event */
151   { TidyAttr_OnCHANGE,          "onchange",          VERS_EVENTS,       CH_SCRIPT    }, /* event */
152   { TidyAttr_OnCLICK,           "onclick",           VERS_EVENTS,       CH_SCRIPT    }, /* event */
153   { TidyAttr_OnDATAAVAILABLE,   "ondataavailable",   VERS_MICROSOFT,    CH_SCRIPT    }, /* object, applet */
154   { TidyAttr_OnDATASETCHANGED,  "ondatasetchanged",  VERS_MICROSOFT,    CH_SCRIPT    }, /* object, applet */
155   { TidyAttr_OnDATASETCOMPLETE, "ondatasetcomplete", VERS_MICROSOFT,    CH_SCRIPT    },
156   { TidyAttr_OnDBLCLICK,        "ondblclick",        VERS_EVENTS,       CH_SCRIPT    }, /* event */
157   { TidyAttr_OnERRORUPDATE,     "onerrorupdate",     VERS_MICROSOFT,    CH_SCRIPT    }, /* form fields */
158   { TidyAttr_OnFOCUS,           "onfocus",           VERS_EVENTS,       CH_SCRIPT    }, /* event */
159   { TidyAttr_OnKEYDOWN,         "onkeydown",         VERS_EVENTS,       CH_SCRIPT    }, /* event */
160   { TidyAttr_OnKEYPRESS,        "onkeypress",        VERS_EVENTS,       CH_SCRIPT    }, /* event */
161   { TidyAttr_OnKEYUP,           "onkeyup",           VERS_EVENTS,       CH_SCRIPT    }, /* event */
162   { TidyAttr_OnLOAD,            "onload",            VERS_EVENTS,       CH_SCRIPT    }, /* event */
163   { TidyAttr_OnMOUSEDOWN,       "onmousedown",       VERS_EVENTS,       CH_SCRIPT    }, /* event */
164   { TidyAttr_OnMOUSEMOVE,       "onmousemove",       VERS_EVENTS,       CH_SCRIPT    }, /* event */
165   { TidyAttr_OnMOUSEOUT,        "onmouseout",        VERS_EVENTS,       CH_SCRIPT    }, /* event */
166   { TidyAttr_OnMOUSEOVER,       "onmouseover",       VERS_EVENTS,       CH_SCRIPT    }, /* event */
167   { TidyAttr_OnMOUSEUP,         "onmouseup",         VERS_EVENTS,       CH_SCRIPT    }, /* event */
168   { TidyAttr_OnRESET,           "onreset",           VERS_EVENTS,       CH_SCRIPT    }, /* event */
169   { TidyAttr_OnROWENTER,        "onrowenter",        VERS_MICROSOFT,    CH_SCRIPT    }, /* form fields */
170   { TidyAttr_OnROWEXIT,         "onrowexit",         VERS_MICROSOFT,    CH_SCRIPT    }, /* form fields */
171   { TidyAttr_OnSELECT,          "onselect",          VERS_EVENTS,       CH_SCRIPT    }, /* event */
172   { TidyAttr_OnSUBMIT,          "onsubmit",          VERS_EVENTS,       CH_SCRIPT    }, /* event */
173   { TidyAttr_OnUNLOAD,          "onunload",          VERS_EVENTS,       CH_SCRIPT    }, /* event */
174   { TidyAttr_PROFILE,           "profile",           VERS_HTML40,       CH_URL       }, /* HEAD */
175   { TidyAttr_PROMPT,            "prompt",            VERS_LOOSE,        CH_PCDATA    }, /* ISINDEX */
176   { TidyAttr_RBSPAN,            "rbspan",            VERS_XHTML11,      CH_NUMBER    }, /* ruby markup */
177   { TidyAttr_READONLY,          "readonly",          VERS_HTML40,       CH_BOOL      }, /* form fields */
178   { TidyAttr_REL,               "rel",               VERS_ALL,          CH_LINKTYPES },
179   { TidyAttr_REV,               "rev",               VERS_ALL,          CH_LINKTYPES },
180   { TidyAttr_RIGHTMARGIN,       "rightmargin",       VERS_MICROSOFT,    CH_NUMBER    }, /* used on BODY */
181   { TidyAttr_ROWS,              "rows",              VERS_ALL,          CH_NUMBER    }, /* TEXTAREA */
182   { TidyAttr_ROWSPAN,           "rowspan",           VERS_ALL,          CH_NUMBER    }, /* table cells */
183   { TidyAttr_RULES,             "rules",             VERS_HTML40,       CH_TRULES    }, /* TABLE */
184   { TidyAttr_SCHEME,            "scheme",            VERS_HTML40,       CH_PCDATA    }, /* META */
185   { TidyAttr_SCOPE,             "scope",             VERS_HTML40,       CH_SCOPE     }, /* table cells */
186   { TidyAttr_SCROLLING,         "scrolling",         VERS_IFRAME,       CH_SCROLL    }, /* yes, no or auto */
187   { TidyAttr_SELECTED,          "selected",          VERS_ALL,          CH_BOOL      }, /* OPTION */
188   { TidyAttr_SHAPE,             "shape",             VERS_FROM32,       CH_SHAPE     }, /* AREA, A */
189   { TidyAttr_SHOWGRID,          "showgrid",          VERS_PROPRIETARY,  CH_BOOL      }, /* TABLE Adobe golive */
190   { TidyAttr_SHOWGRIDX,         "showgridx",         VERS_PROPRIETARY,  CH_BOOL      }, /* TABLE Adobe golive*/
191   { TidyAttr_SHOWGRIDY,         "showgridy",         VERS_PROPRIETARY,  CH_BOOL      }, /* TABLE Adobe golive*/
192   { TidyAttr_SIZE,              "size",              VERS_LOOSE,        CH_NUMBER    }, /* HR, FONT, BASEFONT, SELECT */
193   { TidyAttr_SPAN,              "span",              VERS_HTML40,       CH_NUMBER    }, /* COL, COLGROUP */
194   { TidyAttr_SRC,               "src",               VERS_ALL,          CH_URL       }, /* IMG, FRAME, IFRAME */
195   { TidyAttr_STANDBY,           "standby",           VERS_HTML40,       CH_PCDATA    }, /* OBJECT */
196   { TidyAttr_START,             "start",             VERS_ALL,          CH_NUMBER    }, /* OL */
197   { TidyAttr_STYLE,             "style",             VERS_HTML40,       CH_PCDATA    },
198   { TidyAttr_SUMMARY,           "summary",           VERS_HTML40,       CH_PCDATA    }, /* TABLE */
199   { TidyAttr_TABINDEX,          "tabindex",          VERS_HTML40,       CH_NUMBER    }, /* fields, OBJECT  and A */
200   { TidyAttr_TARGET,            "target",            VERS_HTML40,       CH_TARGET    }, /* names a frame/window */
201   { TidyAttr_TEXT,              "text",              VERS_LOOSE,        CH_COLOR     }, /* BODY */
202   { TidyAttr_TITLE,             "title",             VERS_HTML40,       CH_PCDATA    }, /* text tool tip */
203   { TidyAttr_TOPMARGIN,         "topmargin",         VERS_MICROSOFT,    CH_NUMBER    }, /* used on BODY */
204   { TidyAttr_TYPE,              "type",              VERS_FROM32,       CH_TYPE      }, /* also used by SPACER */
205   { TidyAttr_USEMAP,            "usemap",            VERS_ALL,          CH_URL       }, /* things with images */
206   { TidyAttr_VALIGN,            "valign",            VERS_FROM32,       CH_VALIGN    },
207   { TidyAttr_VALUE,             "value",             VERS_ALL,          CH_PCDATA    },
208   { TidyAttr_VALUETYPE,         "valuetype",         VERS_HTML40,       CH_VTYPE     }, /* PARAM: data, ref, object */
209   { TidyAttr_VERSION,           "version",           VERS_ALL|VERS_XML, CH_PCDATA    }, /* HTML <?xml?> */
210   { TidyAttr_VLINK,             "vlink",             VERS_LOOSE,        CH_COLOR     }, /* BODY */
211   { TidyAttr_VSPACE,            "vspace",            VERS_LOOSE,        CH_NUMBER    }, /* IMG, OBJECT, APPLET */
212   { TidyAttr_WIDTH,             "width",             VERS_ALL,          CH_LENGTH    }, /* pixels only for TD/TH */
213   { TidyAttr_WRAP,              "wrap",              VERS_NETSCAPE,     CH_PCDATA    }, /* textarea */
214   { TidyAttr_XML_LANG,          "xml:lang",          VERS_XML,          CH_LANG      }, /* XML language */
215   { TidyAttr_XML_SPACE,         "xml:space",         VERS_XML,          CH_PCDATA    }, /* XML white space */
216 
217   /* todo: VERS_ALL is wrong! */
218   { TidyAttr_XMLNS,             "xmlns",             VERS_ALL,          CH_PCDATA    }, /* name space */
219   { TidyAttr_EVENT,             "event",             VERS_HTML40,       CH_PCDATA    }, /* reserved for <script> */
220   { TidyAttr_METHODS,           "methods",           VERS_HTML20,       CH_PCDATA    }, /* for <a>, never implemented */
221   { TidyAttr_N,                 "n",                 VERS_HTML20,       CH_PCDATA    }, /* for <nextid> */
222   { TidyAttr_SDAFORM,           "sdaform",           VERS_HTML20,       CH_PCDATA    }, /* SDATA attribute in HTML 2.0 */
223   { TidyAttr_SDAPREF,           "sdapref",           VERS_HTML20,       CH_PCDATA    }, /* SDATA attribute in HTML 2.0 */
224   { TidyAttr_SDASUFF,           "sdasuff",           VERS_HTML20,       CH_PCDATA    }, /* SDATA attribute in HTML 2.0 */
225   { TidyAttr_URN,               "urn",               VERS_HTML20,       CH_PCDATA    }, /* for <a>, never implemented */
226 
227   /* this must be the final entry */
228   { N_TIDY_ATTRIBS,             NULL,                VERS_UNKNOWN,      NULL         }
229 };
230 
AttributeVersions(Node * node,AttVal * attval)231 static uint AttributeVersions(Node* node, AttVal* attval)
232 {
233     uint i;
234 
235     if (!attval || !attval->dict)
236         return VERS_UNKNOWN;
237 
238     if (!node || !node->tag || !node->tag->attrvers)
239         return attval->dict->versions;
240 
241     for (i = 0; node->tag->attrvers[i].attribute; ++i)
242         if (node->tag->attrvers[i].attribute == attval->dict->id)
243             return node->tag->attrvers[i].versions;
244 
245     return attval->dict->versions & VERS_ALL
246              ? VERS_UNKNOWN
247              : attval->dict->versions;
248 
249 }
250 
251 
252 /* return the version of the attribute "id" of element "node" */
NodeAttributeVersions(Node * node,TidyAttrId id)253 uint NodeAttributeVersions( Node* node, TidyAttrId id )
254 {
255     uint i;
256 
257     if (!node || !node->tag || !node->tag->attrvers)
258         return VERS_UNKNOWN;
259 
260     for (i = 0; node->tag->attrvers[i].attribute; ++i)
261         if (node->tag->attrvers[i].attribute == id)
262             return node->tag->attrvers[i].versions;
263 
264     return VERS_UNKNOWN;
265 }
266 
267 /* returns true if the element is a W3C defined element */
268 /* but the element/attribute combination is not         */
AttributeIsProprietary(Node * node,AttVal * attval)269 static Bool AttributeIsProprietary(Node* node, AttVal* attval)
270 {
271     if (!node || !attval)
272         return no;
273 
274     if (!node->tag)
275         return no;
276 
277     if (!(node->tag->versions & VERS_ALL))
278         return no;
279 
280     if (AttributeVersions(node, attval) & VERS_ALL)
281         return no;
282 
283     return yes;
284 }
285 
286 /* used by CheckColor() */
287 struct _colors
288 {
289     ctmbstr name;
290     ctmbstr hex;
291 };
292 
293 static const struct _colors colors[] =
294 {
295     { "black",   "#000000" },
296     { "green",   "#008000" },
297     { "silver",  "#C0C0C0" },
298     { "lime",    "#00FF00" },
299     { "gray",    "#808080" },
300     { "olive",   "#808000" },
301     { "white",   "#FFFFFF" },
302     { "yellow",  "#FFFF00" },
303     { "maroon",  "#800000" },
304     { "navy",    "#000080" },
305     { "red",     "#FF0000" },
306     { "blue",    "#0000FF" },
307     { "purple",  "#800080" },
308     { "teal",    "#008080" },
309     { "fuchsia", "#FF00FF" },
310     { "aqua",    "#00FFFF" },
311     { NULL,      NULL      }
312 };
313 
GetColorCode(ctmbstr name)314 static ctmbstr GetColorCode(ctmbstr name)
315 {
316     uint i;
317 
318     for (i = 0; colors[i].name; ++i)
319         if (tmbstrcasecmp(name, colors[i].name) == 0)
320             return colors[i].hex;
321 
322     return NULL;
323 }
324 
GetColorName(ctmbstr code)325 static ctmbstr GetColorName(ctmbstr code)
326 {
327     uint i;
328 
329     for (i = 0; colors[i].name; ++i)
330         if (tmbstrcasecmp(code, colors[i].hex) == 0)
331             return colors[i].name;
332 
333     return NULL;
334 }
335 
336 #if 0
337 static const struct _colors fancy_colors[] =
338 {
339     { "darkgreen",            "#006400" },
340     { "antiquewhite",         "#FAEBD7" },
341     { "aqua",                 "#00FFFF" },
342     { "aquamarine",           "#7FFFD4" },
343     { "azure",                "#F0FFFF" },
344     { "beige",                "#F5F5DC" },
345     { "bisque",               "#FFE4C4" },
346     { "black",                "#000000" },
347     { "blanchedalmond",       "#FFEBCD" },
348     { "blue",                 "#0000FF" },
349     { "blueviolet",           "#8A2BE2" },
350     { "brown",                "#A52A2A" },
351     { "burlywood",            "#DEB887" },
352     { "cadetblue",            "#5F9EA0" },
353     { "chartreuse",           "#7FFF00" },
354     { "chocolate",            "#D2691E" },
355     { "coral",                "#FF7F50" },
356     { "cornflowerblue",       "#6495ED" },
357     { "cornsilk",             "#FFF8DC" },
358     { "crimson",              "#DC143C" },
359     { "cyan",                 "#00FFFF" },
360     { "darkblue",             "#00008B" },
361     { "darkcyan",             "#008B8B" },
362     { "darkgoldenrod",        "#B8860B" },
363     { "darkgray",             "#A9A9A9" },
364     { "darkgreen",            "#006400" },
365     { "darkkhaki",            "#BDB76B" },
366     { "darkmagenta",          "#8B008B" },
367     { "darkolivegreen",       "#556B2F" },
368     { "darkorange",           "#FF8C00" },
369     { "darkorchid",           "#9932CC" },
370     { "darkred",              "#8B0000" },
371     { "darksalmon",           "#E9967A" },
372     { "darkseagreen",         "#8FBC8F" },
373     { "darkslateblue",        "#483D8B" },
374     { "darkslategray",        "#2F4F4F" },
375     { "darkturquoise",        "#00CED1" },
376     { "darkviolet",           "#9400D3" },
377     { "deeppink",             "#FF1493" },
378     { "deepskyblue",          "#00BFFF" },
379     { "dimgray",              "#696969" },
380     { "dodgerblue",           "#1E90FF" },
381     { "firebrick",            "#B22222" },
382     { "floralwhite",          "#FFFAF0" },
383     { "forestgreen",          "#228B22" },
384     { "fuchsia",              "#FF00FF" },
385     { "gainsboro",            "#DCDCDC" },
386     { "ghostwhite",           "#F8F8FF" },
387     { "gold",                 "#FFD700" },
388     { "goldenrod",            "#DAA520" },
389     { "gray",                 "#808080" },
390     { "green",                "#008000" },
391     { "greenyellow",          "#ADFF2F" },
392     { "honeydew",             "#F0FFF0" },
393     { "hotpink",              "#FF69B4" },
394     { "indianred",            "#CD5C5C" },
395     { "indigo",               "#4B0082" },
396     { "ivory",                "#FFFFF0" },
397     { "khaki",                "#F0E68C" },
398     { "lavender",             "#E6E6FA" },
399     { "lavenderblush",        "#FFF0F5" },
400     { "lawngreen",            "#7CFC00" },
401     { "lemonchiffon",         "#FFFACD" },
402     { "lightblue",            "#ADD8E6" },
403     { "lightcoral",           "#F08080" },
404     { "lightcyan",            "#E0FFFF" },
405     { "lightgoldenrodyellow", "#FAFAD2" },
406     { "lightgreen",           "#90EE90" },
407     { "lightgrey",            "#D3D3D3" },
408     { "lightpink",            "#FFB6C1" },
409     { "lightsalmon",          "#FFA07A" },
410     { "lightseagreen",        "#20B2AA" },
411     { "lightskyblue",         "#87CEFA" },
412     { "lightslategray",       "#778899" },
413     { "lightsteelblue",       "#B0C4DE" },
414     { "lightyellow",          "#FFFFE0" },
415     { "lime",                 "#00FF00" },
416     { "limegreen",            "#32CD32" },
417     { "linen",                "#FAF0E6" },
418     { "magenta",              "#FF00FF" },
419     { "maroon",               "#800000" },
420     { "mediumaquamarine",     "#66CDAA" },
421     { "mediumblue",           "#0000CD" },
422     { "mediumorchid",         "#BA55D3" },
423     { "mediumpurple",         "#9370DB" },
424     { "mediumseagreen",       "#3CB371" },
425     { "mediumslateblue",      "#7B68EE" },
426     { "mediumspringgreen",    "#00FA9A" },
427     { "mediumturquoise",      "#48D1CC" },
428     { "mediumvioletred",      "#C71585" },
429     { "midnightblue",         "#191970" },
430     { "mintcream",            "#F5FFFA" },
431     { "mistyrose",            "#FFE4E1" },
432     { "moccasin",             "#FFE4B5" },
433     { "navajowhite",          "#FFDEAD" },
434     { "navy",                 "#000080" },
435     { "oldlace",              "#FDF5E6" },
436     { "olive",                "#808000" },
437     { "olivedrab",            "#6B8E23" },
438     { "orange",               "#FFA500" },
439     { "orangered",            "#FF4500" },
440     { "orchid",               "#DA70D6" },
441     { "palegoldenrod",        "#EEE8AA" },
442     { "palegreen",            "#98FB98" },
443     { "paleturquoise",        "#AFEEEE" },
444     { "palevioletred",        "#DB7093" },
445     { "papayawhip",           "#FFEFD5" },
446     { "peachpuff",            "#FFDAB9" },
447     { "peru",                 "#CD853F" },
448     { "pink",                 "#FFC0CB" },
449     { "plum",                 "#DDA0DD" },
450     { "powderblue",           "#B0E0E6" },
451     { "purple",               "#800080" },
452     { "red",                  "#FF0000" },
453     { "rosybrown",            "#BC8F8F" },
454     { "royalblue",            "#4169E1" },
455     { "saddlebrown",          "#8B4513" },
456     { "salmon",               "#FA8072" },
457     { "sandybrown",           "#F4A460" },
458     { "seagreen",             "#2E8B57" },
459     { "seashell",             "#FFF5EE" },
460     { "sienna",               "#A0522D" },
461     { "silver",               "#C0C0C0" },
462     { "skyblue",              "#87CEEB" },
463     { "slateblue",            "#6A5ACD" },
464     { "slategray",            "#708090" },
465     { "snow",                 "#FFFAFA" },
466     { "springgreen",          "#00FF7F" },
467     { "steelblue",            "#4682B4" },
468     { "tan",                  "#D2B48C" },
469     { "teal",                 "#008080" },
470     { "thistle",              "#D8BFD8" },
471     { "tomato",               "#FF6347" },
472     { "turquoise",            "#40E0D0" },
473     { "violet",               "#EE82EE" },
474     { "wheat",                "#F5DEB3" },
475     { "white",                "#FFFFFF" },
476     { "whitesmoke",           "#F5F5F5" },
477     { "yellow",               "#FFFF00" },
478     { "yellowgreen",          "#9ACD32" },
479     { NULL,                   NULL      }
480 };
481 #endif
482 
483 #if ATTRIBUTE_HASH_LOOKUP
hash(ctmbstr s)484 static uint hash(ctmbstr s)
485 {
486     uint hashval;
487 
488     for (hashval = 0; *s != '\0'; s++)
489         hashval = *s + 31*hashval;
490 
491     return hashval % ATTRIBUTE_HASH_SIZE;
492 }
493 
install(TidyAttribImpl * attribs,const Attribute * old)494 static const Attribute *install(TidyAttribImpl * attribs, const Attribute* old)
495 {
496     AttrHash *np;
497     uint hashval;
498 
499     if (old)
500     {
501         np = (AttrHash *)MemAlloc(sizeof(*np));
502         np->attr = old;
503 
504         hashval = hash(old->name);
505         np->next = attribs->hashtab[hashval];
506         attribs->hashtab[hashval] = np;
507     }
508 
509     return old;
510 }
511 
removeFromHash(TidyAttribImpl * attribs,ctmbstr s)512 static void removeFromHash( TidyAttribImpl * attribs, ctmbstr s )
513 {
514     uint h = hash(s);
515     AttrHash *p, *prev = NULL;
516     for (p = attribs->hashtab[h]; p && p->attr; p = p->next)
517     {
518         if (tmbstrcmp(s, p->attr->name) == 0)
519         {
520             AttrHash* next = p->next;
521             if ( prev )
522                 prev->next = next;
523             else
524                 attribs->hashtab[h] = next;
525             MemFree(p);
526             return;
527         }
528         prev = p;
529     }
530 }
531 
emptyHash(TidyAttribImpl * attribs)532 static void emptyHash( TidyAttribImpl * attribs )
533 {
534     AttrHash *dict, *next;
535     uint i;
536 
537     for (i = 0; i < ATTRIBUTE_HASH_SIZE; ++i)
538     {
539         dict = attribs->hashtab[i];
540 
541         while(dict)
542         {
543             next = dict->next;
544             MemFree(dict);
545             dict = next;
546         }
547 
548         attribs->hashtab[i] = NULL;
549     }
550 }
551 #endif
552 
lookup(TidyAttribImpl * ARG_UNUSED (attribs),ctmbstr atnam)553 static const Attribute* lookup(TidyAttribImpl* ARG_UNUSED(attribs),
554                                ctmbstr atnam)
555 {
556     const Attribute *np;
557 #if ATTRIBUTE_HASH_LOOKUP
558     const AttrHash *p;
559 #endif
560 
561     if (!atnam)
562         return NULL;
563 
564 #if ATTRIBUTE_HASH_LOOKUP
565     for (p = attribs->hashtab[hash(atnam)]; p && p->attr; p = p->next)
566         if (tmbstrcmp(atnam, p->attr->name) == 0)
567             return p->attr;
568 
569     for (np = attribute_defs; np && np->name; ++np)
570         if (tmbstrcmp(atnam, np->name) == 0)
571             return install(attribs, np);
572 #else
573     for (np = attribute_defs; np && np->name; ++np)
574         if (tmbstrcmp(atnam, np->name) == 0)
575             return np;
576 #endif
577 
578     return NULL;
579 }
580 
581 
582 /* Locate attributes by type */
AttrGetById(Node * node,TidyAttrId id)583 AttVal* AttrGetById( Node* node, TidyAttrId id )
584 {
585    AttVal* av;
586    for ( av = node->attributes; av; av = av->next )
587    {
588      if ( AttrIsId(av, id) )
589          return av;
590    }
591    return NULL;
592 }
593 
594 /* public method for finding attribute definition by name */
FindAttribute(TidyDocImpl * doc,AttVal * attval)595 const Attribute* FindAttribute( TidyDocImpl* doc, AttVal *attval )
596 {
597     if ( attval )
598        return lookup( &doc->attribs, attval->attribute );
599     return NULL;
600 }
601 
GetAttrByName(Node * node,ctmbstr name)602 AttVal* GetAttrByName( Node *node, ctmbstr name )
603 {
604     AttVal *attr;
605     for (attr = node->attributes; attr != NULL; attr = attr->next)
606     {
607         if (attr->attribute && tmbstrcmp(attr->attribute, name) == 0)
608             break;
609     }
610     return attr;
611 }
612 
AddAttribute(TidyDocImpl * doc,Node * node,ctmbstr name,ctmbstr value)613 AttVal* AddAttribute( TidyDocImpl* doc,
614                       Node *node, ctmbstr name, ctmbstr value )
615 {
616     AttVal *av = NewAttribute();
617     av->delim = '"';
618     av->attribute = tmbstrdup(name);
619 
620     if (value)
621         av->value = tmbstrdup(value);
622     else
623         av->value = NULL;
624 
625     av->dict = lookup(&doc->attribs, name);
626 
627     InsertAttributeAtEnd(node, av);
628     return av;
629 }
630 
RepairAttrValue(TidyDocImpl * doc,Node * node,ctmbstr name,ctmbstr value)631 AttVal* RepairAttrValue(TidyDocImpl* doc, Node* node, ctmbstr name, ctmbstr value)
632 {
633     AttVal* old = GetAttrByName(node, name);
634 
635     if (old)
636     {
637         if (old->value)
638             MemFree(old->value);
639         if (value)
640             old->value = tmbstrdup(value);
641         else
642             old->value = NULL;
643 
644         return old;
645     }
646     else
647         return AddAttribute(doc, node, name, value);
648 }
649 
CheckAttrType(TidyDocImpl * doc,ctmbstr attrname,AttrCheck type)650 static Bool CheckAttrType( TidyDocImpl* doc,
651                            ctmbstr attrname, AttrCheck type )
652 {
653     const Attribute* np = lookup( &doc->attribs, attrname );
654     return (Bool)( np && np->attrchk == type );
655 }
656 
IsUrl(TidyDocImpl * doc,ctmbstr attrname)657 Bool IsUrl( TidyDocImpl* doc, ctmbstr attrname )
658 {
659     return CheckAttrType( doc, attrname, CH_URL );
660 }
661 
IsBool(TidyDocImpl * doc,ctmbstr attrname)662 Bool IsBool( TidyDocImpl* doc, ctmbstr attrname )
663 {
664     return CheckAttrType( doc, attrname, CH_BOOL );
665 }
666 
IsScript(TidyDocImpl * doc,ctmbstr attrname)667 Bool IsScript( TidyDocImpl* doc, ctmbstr attrname )
668 {
669     return CheckAttrType( doc, attrname, CH_SCRIPT );
670 }
671 
672 /* may id or name serve as anchor? */
IsAnchorElement(TidyDocImpl * ARG_UNUSED (doc),Node * node)673 Bool IsAnchorElement( TidyDocImpl* ARG_UNUSED(doc), Node* node)
674 {
675     TidyTagId tid = TagId( node );
676     if ( tid == TidyTag_A      ||
677          tid == TidyTag_APPLET ||
678          tid == TidyTag_FORM   ||
679          tid == TidyTag_FRAME  ||
680          tid == TidyTag_IFRAME ||
681          tid == TidyTag_IMG    ||
682          tid == TidyTag_MAP )
683         return yes;
684 
685     return no;
686 }
687 
688 /*
689   In CSS1, selectors can contain only the characters A-Z, 0-9,
690   and Unicode characters 161-255, plus dash (-); they cannot start
691   with a dash or a digit; they can also contain escaped characters
692   and any Unicode character as a numeric code (see next item).
693 
694   The backslash followed by at most four hexadecimal digits
695   (0..9A..F) stands for the Unicode character with that number.
696 
697   Any character except a hexadecimal digit can be escaped to remove
698   its special meaning, by putting a backslash in front.
699 
700   #508936 - CSS class naming for -clean option
701 */
IsCSS1Selector(ctmbstr buf)702 Bool IsCSS1Selector( ctmbstr buf )
703 {
704     Bool valid = yes;
705     int esclen = 0;
706     byte c;
707     int pos;
708 
709     for ( pos=0; valid && (c = *buf++); ++pos )
710     {
711         if ( c == '\\' )
712         {
713             esclen = 1;  /* ab\555\444 is 4 chars {'a', 'b', \555, \444} */
714         }
715         else if ( isdigit( c ) )
716         {
717             /* Digit not 1st, unless escaped (Max length "\112F") */
718             if ( esclen > 0 )
719                 valid = ( ++esclen < 6 );
720             if ( valid )
721                 valid = ( pos>0 || esclen>0 );
722         }
723         else
724         {
725             valid = (
726                 esclen > 0                       /* Escaped? Anything goes. */
727                 || ( pos>0 && c == '-' )         /* Dash cannot be 1st char */
728                 || isalpha(c)                    /* a-z, A-Z anywhere */
729                 || ( c >= 161 )                  /* Unicode 161-255 anywhere */
730             );
731             esclen = 0;
732         }
733     }
734     return valid;
735 }
736 
737 /* free single anchor */
FreeAnchor(Anchor * a)738 static void FreeAnchor(Anchor *a)
739 {
740     if ( a )
741         MemFree( a->name );
742     MemFree( a );
743 }
744 
745 /* removes anchor for specific node */
RemoveAnchorByNode(TidyDocImpl * doc,Node * node)746 void RemoveAnchorByNode( TidyDocImpl* doc, Node *node )
747 {
748     TidyAttribImpl* attribs = &doc->attribs;
749     Anchor *delme = NULL, *curr, *prev = NULL;
750 
751     for ( curr=attribs->anchor_list; curr!=NULL; curr=curr->next )
752     {
753         if ( curr->node == node )
754         {
755             if ( prev )
756                 prev->next = curr->next;
757             else
758                 attribs->anchor_list = curr->next;
759             delme = curr;
760             break;
761         }
762         prev = curr;
763     }
764     FreeAnchor( delme );
765 }
766 
767 /* initialize new anchor */
NewAnchor(ctmbstr name,Node * node)768 static Anchor* NewAnchor( ctmbstr name, Node* node )
769 {
770     Anchor *a = (Anchor*) MemAlloc( sizeof(Anchor) );
771 
772     a->name = tmbstrdup( name );
773     a->name = tmbstrtolower(a->name);
774     a->node = node;
775     a->next = NULL;
776 
777     return a;
778 }
779 
780 /* add new anchor to namespace */
AddAnchor(TidyDocImpl * doc,ctmbstr name,Node * node)781 Anchor* AddAnchor( TidyDocImpl* doc, ctmbstr name, Node *node )
782 {
783     TidyAttribImpl* attribs = &doc->attribs;
784     Anchor *a = NewAnchor( name, node );
785 
786     if ( attribs->anchor_list == NULL)
787          attribs->anchor_list = a;
788     else
789     {
790         Anchor *here =  attribs->anchor_list;
791         while (here->next)
792             here = here->next;
793         here->next = a;
794     }
795 
796     return attribs->anchor_list;
797 }
798 
799 /* return node associated with anchor */
GetNodeByAnchor(TidyDocImpl * doc,ctmbstr name)800 Node* GetNodeByAnchor( TidyDocImpl* doc, ctmbstr name )
801 {
802     TidyAttribImpl* attribs = &doc->attribs;
803     Anchor *found;
804     tmbstr lname = tmbstrdup(name);
805     lname = tmbstrtolower(lname);
806 
807     for ( found = attribs->anchor_list; found != NULL; found = found->next )
808     {
809         if ( tmbstrcmp(found->name, lname) == 0 )
810             break;
811     }
812 
813     MemFree(lname);
814     if ( found )
815         return found->node;
816     return NULL;
817 }
818 
819 /* free all anchors */
FreeAnchors(TidyDocImpl * doc)820 void FreeAnchors( TidyDocImpl* doc )
821 {
822     TidyAttribImpl* attribs = &doc->attribs;
823     Anchor* a;
824     while (NULL != (a = attribs->anchor_list) )
825     {
826         attribs->anchor_list = a->next;
827         FreeAnchor(a);
828     }
829 }
830 
831 /* public method for inititializing attribute dictionary */
InitAttrs(TidyDocImpl * doc)832 void InitAttrs( TidyDocImpl* doc )
833 {
834     ClearMemory( &doc->attribs, sizeof(TidyAttribImpl) );
835 #ifdef _DEBUG
836     {
837       /* Attribute ID is index position in Attribute type lookup table */
838       uint ix;
839       for ( ix=0; ix < N_TIDY_ATTRIBS; ++ix )
840       {
841         const Attribute* dict = &attribute_defs[ ix ];
842         assert( (uint) dict->id == ix );
843       }
844     }
845 #endif
846 }
847 
848 /* free all declared attributes */
FreeDeclaredAttributes(TidyDocImpl * doc)849 static void FreeDeclaredAttributes( TidyDocImpl* doc )
850 {
851     TidyAttribImpl* attribs = &doc->attribs;
852     Attribute* dict;
853     while ( NULL != (dict = attribs->declared_attr_list) )
854     {
855         attribs->declared_attr_list = dict->next;
856 #if ATTRIBUTE_HASH_LOOKUP
857         removeFromHash( &doc->attribs, dict->name );
858 #endif
859         MemFree( dict->name );
860         MemFree( dict );
861     }
862 }
863 
FreeAttrTable(TidyDocImpl * doc)864 void FreeAttrTable( TidyDocImpl* doc )
865 {
866 #if ATTRIBUTE_HASH_LOOKUP
867     emptyHash( &doc->attribs );
868 #endif
869     FreeAnchors( doc );
870     FreeDeclaredAttributes( doc );
871 }
872 
AppendToClassAttr(AttVal * classattr,ctmbstr classname)873 void AppendToClassAttr( AttVal *classattr, ctmbstr classname )
874 {
875     uint len = tmbstrlen(classattr->value) +
876         tmbstrlen(classname) + 2;
877     tmbstr s = (tmbstr) MemAlloc( len );
878     s[0] = '\0';
879     if (classattr->value)
880     {
881         tmbstrcpy( s, classattr->value );
882         tmbstrcat( s, " " );
883     }
884     tmbstrcat( s, classname );
885     if (classattr->value)
886         MemFree( classattr->value );
887     classattr->value = s;
888 }
889 
890 /* concatenate styles */
AppendToStyleAttr(AttVal * styleattr,ctmbstr styleprop)891 static void AppendToStyleAttr( AttVal *styleattr, ctmbstr styleprop )
892 {
893     /*
894     this doesn't handle CSS comments and
895     leading/trailing white-space very well
896     see http://www.w3.org/TR/css-style-attr
897     */
898     uint end = tmbstrlen(styleattr->value);
899 
900     if (end >0 && styleattr->value[end - 1] == ';')
901     {
902         /* attribute ends with declaration seperator */
903 
904         styleattr->value = (tmbstr) MemRealloc(styleattr->value,
905             end + tmbstrlen(styleprop) + 2);
906 
907         tmbstrcat(styleattr->value, " ");
908         tmbstrcat(styleattr->value, styleprop);
909     }
910     else if (end >0 && styleattr->value[end - 1] == '}')
911     {
912         /* attribute ends with rule set */
913 
914         styleattr->value = (tmbstr) MemRealloc(styleattr->value,
915             end + tmbstrlen(styleprop) + 6);
916 
917         tmbstrcat(styleattr->value, " { ");
918         tmbstrcat(styleattr->value, styleprop);
919         tmbstrcat(styleattr->value, " }");
920     }
921     else
922     {
923         /* attribute ends with property value */
924 
925         styleattr->value = (tmbstr) MemRealloc(styleattr->value,
926             end + tmbstrlen(styleprop) + 3);
927 
928         if (end > 0)
929             tmbstrcat(styleattr->value, "; ");
930         tmbstrcat(styleattr->value, styleprop);
931     }
932 }
933 
934 /*
935  the same attribute name can't be used
936  more than once in each element
937 */
RepairDuplicateAttributes(TidyDocImpl * doc,Node * node)938 void RepairDuplicateAttributes( TidyDocImpl* doc, Node *node)
939 {
940     AttVal *first;
941 
942     for (first = node->attributes; first != NULL;)
943     {
944         AttVal *second;
945         Bool firstRedefined = no;
946 
947         if (!(first->asp == NULL && first->php == NULL))
948         {
949             first = first->next;
950             continue;
951         }
952 
953         for (second = first->next; second != NULL;)
954         {
955             AttVal *temp;
956 
957             if (!(second->asp == NULL && second->php == NULL &&
958                 AttrsHaveSameId(first, second)))
959             {
960                 second = second->next;
961                 continue;
962             }
963 
964             /* first and second attribute have same local name */
965             /* now determine what to do with this duplicate... */
966 
967             if (attrIsCLASS(first) && cfgBool(doc, TidyJoinClasses)
968                 && AttrHasValue(first) && AttrHasValue(second))
969             {
970                 /* concatenate classes */
971 
972                 AppendToClassAttr(first, second->value);
973 
974                 temp = second->next;
975                 ReportAttrError( doc, node, second, JOINING_ATTRIBUTE);
976                 RemoveAttribute( doc, node, second );
977                 second = temp;
978             }
979             else if (attrIsSTYLE(first) && cfgBool(doc, TidyJoinStyles)
980                      && AttrHasValue(first) && AttrHasValue(second))
981             {
982                 AppendToStyleAttr( first, second->value );
983 
984                 temp = second->next;
985                 ReportAttrError( doc, node, second, JOINING_ATTRIBUTE);
986                 RemoveAttribute( doc, node, second );
987                 second = temp;
988             }
989             else if ( cfg(doc, TidyDuplicateAttrs) == TidyKeepLast )
990             {
991                 temp = first->next;
992                 ReportAttrError( doc, node, first, REPEATED_ATTRIBUTE);
993                 RemoveAttribute( doc, node, first );
994                 firstRedefined = yes;
995                 first = temp;
996                 second = second->next;
997             }
998             else /* TidyDuplicateAttrs == TidyKeepFirst */
999             {
1000                 temp = second->next;
1001                 ReportAttrError( doc, node, second, REPEATED_ATTRIBUTE);
1002                 RemoveAttribute( doc, node, second );
1003                 second = temp;
1004             }
1005         }
1006         if (!firstRedefined)
1007             first = first->next;
1008     }
1009 }
1010 
1011 /* ignore unknown attributes for proprietary elements */
CheckAttribute(TidyDocImpl * doc,Node * node,AttVal * attval)1012 const Attribute* CheckAttribute( TidyDocImpl* doc, Node *node, AttVal *attval )
1013 {
1014     const Attribute* attribute = attval->dict;
1015 
1016     if ( attribute != NULL )
1017     {
1018         if (attribute->versions & VERS_XML)
1019         {
1020             doc->lexer->isvoyager = yes;
1021             if (!cfgBool(doc, TidyHtmlOut))
1022             {
1023                 SetOptionBool(doc, TidyXhtmlOut, yes);
1024                 SetOptionBool(doc, TidyXmlOut, yes);
1025             }
1026         }
1027 
1028         ConstrainVersion(doc, AttributeVersions(node, attval));
1029 
1030         if (attribute->attrchk)
1031             attribute->attrchk( doc, node, attval );
1032     }
1033 
1034     if (AttributeIsProprietary(node, attval))
1035     {
1036         ReportAttrError(doc, node, attval, PROPRIETARY_ATTRIBUTE);
1037 
1038         if (cfgBool(doc, TidyDropPropAttrs))
1039             RemoveAttribute( doc, node, attval );
1040     }
1041 
1042     return attribute;
1043 }
1044 
IsBoolAttribute(AttVal * attval)1045 Bool IsBoolAttribute(AttVal *attval)
1046 {
1047     const Attribute *attribute = ( attval ? attval->dict : NULL );
1048     if ( attribute && attribute->attrchk == CH_BOOL )
1049         return yes;
1050     return no;
1051 }
1052 
attrIsEvent(AttVal * attval)1053 Bool attrIsEvent( AttVal* attval )
1054 {
1055     TidyAttrId atid = AttrId( attval );
1056 
1057     return (atid == TidyAttr_OnAFTERUPDATE     ||
1058             atid == TidyAttr_OnBEFOREUNLOAD    ||
1059             atid == TidyAttr_OnBEFOREUPDATE    ||
1060             atid == TidyAttr_OnBLUR            ||
1061             atid == TidyAttr_OnCHANGE          ||
1062             atid == TidyAttr_OnCLICK           ||
1063             atid == TidyAttr_OnDATAAVAILABLE   ||
1064             atid == TidyAttr_OnDATASETCHANGED  ||
1065             atid == TidyAttr_OnDATASETCOMPLETE ||
1066             atid == TidyAttr_OnDBLCLICK        ||
1067             atid == TidyAttr_OnERRORUPDATE     ||
1068             atid == TidyAttr_OnFOCUS           ||
1069             atid == TidyAttr_OnKEYDOWN         ||
1070             atid == TidyAttr_OnKEYPRESS        ||
1071             atid == TidyAttr_OnKEYUP           ||
1072             atid == TidyAttr_OnLOAD            ||
1073             atid == TidyAttr_OnMOUSEDOWN       ||
1074             atid == TidyAttr_OnMOUSEMOVE       ||
1075             atid == TidyAttr_OnMOUSEOUT        ||
1076             atid == TidyAttr_OnMOUSEOVER       ||
1077             atid == TidyAttr_OnMOUSEUP         ||
1078             atid == TidyAttr_OnRESET           ||
1079             atid == TidyAttr_OnROWENTER        ||
1080             atid == TidyAttr_OnROWEXIT         ||
1081             atid == TidyAttr_OnSELECT          ||
1082             atid == TidyAttr_OnSUBMIT          ||
1083             atid == TidyAttr_OnUNLOAD);
1084 }
1085 
CheckLowerCaseAttrValue(TidyDocImpl * doc,Node * node,AttVal * attval)1086 static void CheckLowerCaseAttrValue( TidyDocImpl* doc, Node *node, AttVal *attval)
1087 {
1088     tmbstr p;
1089     Bool hasUpper = no;
1090 
1091     if (!AttrHasValue(attval))
1092         return;
1093 
1094     p = attval->value;
1095 
1096     while (*p)
1097     {
1098         if (IsUpper(*p)) /* #501230 - fix by Terry Teague - 09 Jan 02 */
1099         {
1100             hasUpper = yes;
1101             break;
1102         }
1103         p++;
1104     }
1105 
1106     if (hasUpper)
1107     {
1108         Lexer* lexer = doc->lexer;
1109         if (lexer->isvoyager)
1110             ReportAttrError( doc, node, attval, ATTR_VALUE_NOT_LCASE);
1111 
1112         if ( lexer->isvoyager || cfgBool(doc, TidyLowerLiterals) )
1113             attval->value = tmbstrtolower(attval->value);
1114     }
1115 }
1116 
1117 /* methods for checking value of a specific attribute */
1118 
CheckUrl(TidyDocImpl * doc,Node * node,AttVal * attval)1119 void CheckUrl( TidyDocImpl* doc, Node *node, AttVal *attval)
1120 {
1121     tmbchar c;
1122     tmbstr dest, p;
1123     uint escape_count = 0, backslash_count = 0;
1124     uint i, pos = 0;
1125     uint len;
1126 
1127     if (!AttrHasValue(attval))
1128     {
1129         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1130         return;
1131     }
1132 
1133     p = attval->value;
1134 
1135     for (i = 0; 0 != (c = p[i]); ++i)
1136     {
1137         if (c == '\\')
1138         {
1139             ++backslash_count;
1140             if ( cfgBool(doc, TidyFixBackslash) )
1141                 p[i] = '/';
1142         }
1143         else if ((c > 0x7e) || (c <= 0x20) || (strchr("<>", c)))
1144             ++escape_count;
1145     }
1146 
1147     if ( cfgBool(doc, TidyFixUri) && escape_count )
1148     {
1149         len = tmbstrlen(p) + escape_count * 2 + 1;
1150         dest = (tmbstr) MemAlloc(len);
1151 
1152         for (i = 0; 0 != (c = p[i]); ++i)
1153         {
1154             if ((c > 0x7e) || (c <= 0x20) || (strchr("<>", c)))
1155                 pos += sprintf( dest + pos, "%%%02X", (byte)c );
1156             else
1157                 dest[pos++] = c;
1158         }
1159         dest[pos] = 0;
1160 
1161         MemFree(attval->value);
1162         attval->value = dest;
1163     }
1164     if ( backslash_count )
1165     {
1166         if ( cfgBool(doc, TidyFixBackslash) )
1167             ReportAttrError( doc, node, attval, FIXED_BACKSLASH );
1168         else
1169             ReportAttrError( doc, node, attval, BACKSLASH_IN_URI );
1170     }
1171     if ( escape_count )
1172     {
1173         if ( cfgBool(doc, TidyFixUri) )
1174             ReportAttrError( doc, node, attval, ESCAPED_ILLEGAL_URI);
1175         else
1176             ReportAttrError( doc, node, attval, ILLEGAL_URI_REFERENCE);
1177 
1178         doc->badChars |= BC_INVALID_URI;
1179     }
1180 }
1181 
1182 /* RFC 2396, section 4.2 states:
1183      "[...] in the case of HTML's FORM element, [...] an
1184      empty URI reference represents the base URI of the
1185      current document and should be replaced by that URI
1186      when transformed into a request."
1187 */
CheckAction(TidyDocImpl * doc,Node * node,AttVal * attval)1188 void CheckAction( TidyDocImpl* doc, Node *node, AttVal *attval)
1189 {
1190     if (AttrHasValue(attval))
1191         CheckUrl( doc, node, attval );
1192 }
1193 
CheckScript(TidyDocImpl * ARG_UNUSED (doc),Node * ARG_UNUSED (node),AttVal * ARG_UNUSED (attval))1194 void CheckScript( TidyDocImpl* ARG_UNUSED(doc), Node* ARG_UNUSED(node),
1195                   AttVal* ARG_UNUSED(attval))
1196 {
1197 }
1198 
IsValidHTMLID(ctmbstr id)1199 Bool IsValidHTMLID(ctmbstr id)
1200 {
1201     ctmbstr s = id;
1202 
1203     if (!s)
1204         return no;
1205 
1206     if (!IsLetter(*s++))
1207         return no;
1208 
1209     while (*s)
1210         if (!IsNamechar(*s++))
1211             return no;
1212 
1213     return yes;
1214 
1215 }
1216 
IsValidXMLID(ctmbstr id)1217 Bool IsValidXMLID(ctmbstr id)
1218 {
1219     ctmbstr s = id;
1220     tchar c;
1221 
1222     if (!s)
1223         return no;
1224 
1225     c = *s++;
1226     if (c > 0x7F)
1227         s += GetUTF8(s, &c);
1228 
1229     if (!(IsXMLLetter(c) || c == '_' || c == ':'))
1230         return no;
1231 
1232     while (*s)
1233     {
1234         c = (unsigned char)*s;
1235 
1236         if (c > 0x7F)
1237             s += GetUTF8(s, &c);
1238 
1239         ++s;
1240 
1241         if (!IsXMLNamechar(c))
1242             return no;
1243     }
1244 
1245     return yes;
1246 }
1247 
IsValidNMTOKEN(ctmbstr name)1248 static Bool IsValidNMTOKEN(ctmbstr name)
1249 {
1250     ctmbstr s = name;
1251     tchar c;
1252 
1253     if (!s)
1254         return no;
1255 
1256     while (*s)
1257     {
1258         c = (unsigned char)*s;
1259 
1260         if (c > 0x7F)
1261             s += GetUTF8(s, &c);
1262 
1263         ++s;
1264 
1265         if (!IsXMLNamechar(c))
1266             return no;
1267     }
1268 
1269     return yes;
1270 }
1271 
AttrValueIsAmong(AttVal * attval,ctmbstr const list[])1272 static Bool AttrValueIsAmong(AttVal *attval, ctmbstr const list[])
1273 {
1274     const ctmbstr *v;
1275     for (v = list; *v; ++v)
1276         if (AttrValueIs(attval, *v))
1277             return yes;
1278     return no;
1279 }
1280 
CheckAttrValidity(TidyDocImpl * doc,Node * node,AttVal * attval,ctmbstr const list[])1281 static void CheckAttrValidity( TidyDocImpl* doc, Node *node, AttVal *attval,
1282                                ctmbstr const list[])
1283 {
1284     if (!AttrHasValue(attval))
1285     {
1286         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1287         return;
1288     }
1289 
1290     CheckLowerCaseAttrValue( doc, node, attval );
1291 
1292     if (!AttrValueIsAmong(attval, list))
1293         ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1294 }
1295 
CheckName(TidyDocImpl * doc,Node * node,AttVal * attval)1296 void CheckName( TidyDocImpl* doc, Node *node, AttVal *attval)
1297 {
1298     Node *old;
1299 
1300     if (!AttrHasValue(attval))
1301     {
1302         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1303         return;
1304     }
1305 
1306     if ( IsAnchorElement(doc, node) )
1307     {
1308         if (cfgBool(doc, TidyXmlOut) && !IsValidNMTOKEN(attval->value))
1309             ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1310 
1311         if ((old = GetNodeByAnchor(doc, attval->value)) &&  old != node)
1312         {
1313             ReportAttrError( doc, node, attval, ANCHOR_NOT_UNIQUE);
1314         }
1315         else
1316             AddAnchor( doc, attval->value, node );
1317     }
1318 }
1319 
CheckId(TidyDocImpl * doc,Node * node,AttVal * attval)1320 void CheckId( TidyDocImpl* doc, Node *node, AttVal *attval )
1321 {
1322     Lexer* lexer = doc->lexer;
1323     Node *old;
1324 
1325     if (!AttrHasValue(attval))
1326     {
1327         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1328         return;
1329     }
1330 
1331     if (!IsValidHTMLID(attval->value))
1332     {
1333         if (lexer->isvoyager && IsValidXMLID(attval->value))
1334             ReportAttrError( doc, node, attval, XML_ID_SYNTAX);
1335         else
1336             ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1337     }
1338 
1339     if ((old = GetNodeByAnchor(doc, attval->value)) &&  old != node)
1340     {
1341         ReportAttrError( doc, node, attval, ANCHOR_NOT_UNIQUE);
1342     }
1343     else
1344         AddAnchor( doc, attval->value, node );
1345 }
1346 
CheckBool(TidyDocImpl * doc,Node * node,AttVal * attval)1347 void CheckBool( TidyDocImpl* doc, Node *node, AttVal *attval)
1348 {
1349     if (!AttrHasValue(attval))
1350         return;
1351 
1352     CheckLowerCaseAttrValue( doc, node, attval );
1353 }
1354 
CheckAlign(TidyDocImpl * doc,Node * node,AttVal * attval)1355 void CheckAlign( TidyDocImpl* doc, Node *node, AttVal *attval)
1356 {
1357     ctmbstr const values[] = {"left", "right", "center", "justify", NULL};
1358 
1359     /* IMG, OBJECT, APPLET and EMBED use align for vertical position */
1360     if (node->tag && (node->tag->model & CM_IMG))
1361     {
1362         CheckValign( doc, node, attval );
1363         return;
1364     }
1365 
1366     if (!AttrHasValue(attval))
1367     {
1368         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1369         return;
1370     }
1371 
1372     CheckLowerCaseAttrValue( doc, node, attval);
1373 
1374     /* currently CheckCaption(...) takes care of the remaining cases */
1375     if (nodeIsCAPTION(node))
1376         return;
1377 
1378     if (!AttrValueIsAmong(attval, values))
1379     {
1380         /* align="char" is allowed for elements with CM_TABLE|CM_ROW
1381            except CAPTION which is excluded above, */
1382         if( !(AttrValueIs(attval, "char")
1383               && nodeHasCM(node, CM_TABLE|CM_ROW)) )
1384              ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1385     }
1386 }
1387 
CheckValign(TidyDocImpl * doc,Node * node,AttVal * attval)1388 void CheckValign( TidyDocImpl* doc, Node *node, AttVal *attval)
1389 {
1390     ctmbstr const values[] = {"top", "middle", "bottom", "baseline", NULL};
1391     ctmbstr const values2[] = {"left", "right", NULL};
1392     ctmbstr const valuesp[] = {"texttop", "absmiddle", "absbottom",
1393                                "textbottom", NULL};
1394 
1395     if (!AttrHasValue(attval))
1396     {
1397         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1398         return;
1399     }
1400 
1401     CheckLowerCaseAttrValue( doc, node, attval );
1402 
1403     if (AttrValueIsAmong(attval, values))
1404     {
1405             /* all is fine */
1406     }
1407     else if (AttrValueIsAmong(attval, values2))
1408     {
1409         if (!(node->tag && (node->tag->model & CM_IMG)))
1410             ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1411     }
1412     else if (AttrValueIsAmong(attval, valuesp))
1413     {
1414         ConstrainVersion( doc, VERS_PROPRIETARY );
1415         ReportAttrError( doc, node, attval, PROPRIETARY_ATTR_VALUE);
1416     }
1417     else
1418         ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1419 }
1420 
CheckLength(TidyDocImpl * doc,Node * node,AttVal * attval)1421 void CheckLength( TidyDocImpl* doc, Node *node, AttVal *attval)
1422 {
1423     tmbstr p;
1424 
1425     if (!AttrHasValue(attval))
1426     {
1427         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1428         return;
1429     }
1430 
1431     /* don't check for <col width=...> and <colgroup width=...> */
1432     if (attrIsWIDTH(attval) && (nodeIsCOL(node) || nodeIsCOLGROUP(node)))
1433         return;
1434 
1435     p = attval->value;
1436 
1437     if (!IsDigit(*p++))
1438     {
1439         ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1440     }
1441     else
1442     {
1443         while (*p)
1444         {
1445             if (!IsDigit(*p) && *p != '%')
1446             {
1447                 ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1448                 break;
1449             }
1450             ++p;
1451         }
1452     }
1453 }
1454 
CheckTarget(TidyDocImpl * doc,Node * node,AttVal * attval)1455 void CheckTarget( TidyDocImpl* doc, Node *node, AttVal *attval)
1456 {
1457     ctmbstr const values[] = {"_blank", "_self", "_parent", "_top", NULL};
1458 
1459     if (!AttrHasValue(attval))
1460     {
1461         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1462         return;
1463     }
1464 
1465     /* target names must begin with A-Za-z ... */
1466     if (IsLetter(attval->value[0]))
1467         return;
1468 
1469     /* or be one of the allowed list */
1470     if (!AttrValueIsAmong(attval, values))
1471         ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1472 }
1473 
CheckFsubmit(TidyDocImpl * doc,Node * node,AttVal * attval)1474 void CheckFsubmit( TidyDocImpl* doc, Node *node, AttVal *attval)
1475 {
1476     ctmbstr const values[] = {"get", "post", NULL};
1477     CheckAttrValidity( doc, node, attval, values );
1478 }
1479 
CheckClear(TidyDocImpl * doc,Node * node,AttVal * attval)1480 void CheckClear( TidyDocImpl* doc, Node *node, AttVal *attval)
1481 {
1482     ctmbstr const values[] = {"none", "left", "right", "all", NULL};
1483 
1484     if (!AttrHasValue(attval))
1485     {
1486         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1487         if (attval->value == NULL)
1488             attval->value = tmbstrdup( "none" );
1489         return;
1490     }
1491 
1492     CheckLowerCaseAttrValue( doc, node, attval );
1493 
1494     if (!AttrValueIsAmong(attval, values))
1495         ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1496 }
1497 
CheckShape(TidyDocImpl * doc,Node * node,AttVal * attval)1498 void CheckShape( TidyDocImpl* doc, Node *node, AttVal *attval)
1499 {
1500     ctmbstr const values[] = {"rect", "default", "circle", "poly", NULL};
1501     CheckAttrValidity( doc, node, attval, values );
1502 }
1503 
CheckScope(TidyDocImpl * doc,Node * node,AttVal * attval)1504 void CheckScope( TidyDocImpl* doc, Node *node, AttVal *attval)
1505 {
1506     ctmbstr const values[] = {"row", "rowgroup", "col", "colgroup", NULL};
1507     CheckAttrValidity( doc, node, attval, values );
1508 }
1509 
CheckNumber(TidyDocImpl * doc,Node * node,AttVal * attval)1510 void CheckNumber( TidyDocImpl* doc, Node *node, AttVal *attval)
1511 {
1512     tmbstr p;
1513 
1514     if (!AttrHasValue(attval))
1515     {
1516         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1517         return;
1518     }
1519 
1520     /* don't check <frameset cols=... rows=...> */
1521     if ( nodeIsFRAMESET(node) &&
1522         (attrIsCOLS(attval) || attrIsROWS(attval)))
1523      return;
1524 
1525     p  = attval->value;
1526 
1527     /* font size may be preceded by + or - */
1528     if ( nodeIsFONT(node) && (*p == '+' || *p == '-') )
1529         ++p;
1530 
1531     while (*p)
1532     {
1533         if (!IsDigit(*p))
1534         {
1535             ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1536             break;
1537         }
1538         ++p;
1539     }
1540 }
1541 
1542 /* check hexadecimal color value */
IsValidColorCode(ctmbstr color)1543 static Bool IsValidColorCode(ctmbstr color)
1544 {
1545     uint i;
1546 
1547     if (tmbstrlen(color) != 6)
1548         return no;
1549 
1550     /* check if valid hex digits and letters */
1551     for (i = 0; i < 6; i++)
1552         if (!IsDigit(color[i]) && !strchr("abcdef", ToLower(color[i])))
1553             return no;
1554 
1555     return yes;
1556 }
1557 
1558 /* check color syntax and beautify value by option */
CheckColor(TidyDocImpl * doc,Node * node,AttVal * attval)1559 void CheckColor( TidyDocImpl* doc, Node *node, AttVal *attval)
1560 {
1561     Bool valid = no;
1562     tmbstr given;
1563 
1564     if (!AttrHasValue(attval))
1565     {
1566         ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1567         return;
1568     }
1569 
1570     given = attval->value;
1571 
1572     /* 727851 - add hash to hash-less color values */
1573     if (given[0] != '#' && (valid = IsValidColorCode(given)))
1574     {
1575         tmbstr cp, s;
1576 
1577         cp = s = (tmbstr) MemAlloc(2 + tmbstrlen (given));
1578         *cp++ = '#';
1579         while ('\0' != (*cp++ = *given++))
1580             continue;
1581 
1582         ReportAttrError(doc, node, attval, BAD_ATTRIBUTE_VALUE_REPLACED);
1583 
1584         MemFree(attval->value);
1585         given = attval->value = s;
1586     }
1587 
1588     if (!valid && given[0] == '#')
1589         valid = IsValidColorCode(given + 1);
1590 
1591     if (valid && given[0] == '#' && cfgBool(doc, TidyReplaceColor))
1592     {
1593         ctmbstr newName = GetColorName(given);
1594 
1595         if (newName)
1596         {
1597             MemFree(attval->value);
1598             given = attval->value = tmbstrdup(newName);
1599         }
1600     }
1601 
1602     /* if it is not a valid color code, it is a color name */
1603     if (!valid)
1604         valid = GetColorCode(given) != NULL;
1605 
1606     if (valid && given[0] == '#')
1607         attval->value = tmbstrtoupper(attval->value);
1608     else if (valid)
1609         attval->value = tmbstrtolower(attval->value);
1610 
1611     if (!valid)
1612         ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1613 }
1614 
1615 /* check valuetype attribute for element param */
CheckVType(TidyDocImpl * doc,Node * node,AttVal * attval)1616 void CheckVType( TidyDocImpl* doc, Node *node, AttVal *attval)
1617 {
1618     ctmbstr const values[] = {"data", "object", "ref", NULL};
1619     CheckAttrValidity( doc, node, attval, values );
1620 }
1621 
1622 /* checks scrolling attribute */
CheckScroll(TidyDocImpl * doc,Node * node,AttVal * attval)1623 void CheckScroll( TidyDocImpl* doc, Node *node, AttVal *attval)
1624 {
1625     ctmbstr const values[] = {"no", "auto", "yes", NULL};
1626     CheckAttrValidity( doc, node, attval, values );
1627 }
1628 
1629 /* checks dir attribute */
CheckTextDir(TidyDocImpl * doc,Node * node,AttVal * attval)1630 void CheckTextDir( TidyDocImpl* doc, Node *node, AttVal *attval)
1631 {
1632     ctmbstr const values[] = {"rtl", "ltr", NULL};
1633     CheckAttrValidity( doc, node, attval, values );
1634 }
1635 
1636 /* checks lang and xml:lang attributes */
CheckLang(TidyDocImpl * doc,Node * node,AttVal * attval)1637 void CheckLang( TidyDocImpl* doc, Node *node, AttVal *attval)
1638 {
1639     /* empty xml:lang is allowed through XML 1.0 SE errata */
1640     if (!AttrHasValue(attval) && !attrIsXML_LANG(attval))
1641     {
1642         if ( cfg(doc, TidyAccessibilityCheckLevel) == 0 )
1643         {
1644             ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE );
1645         }
1646         return;
1647     }
1648 }
1649 
1650 /* checks type attribute */
CheckType(TidyDocImpl * doc,Node * node,AttVal * attval)1651 void CheckType( TidyDocImpl* doc, Node *node, AttVal *attval)
1652 {
1653     ctmbstr const valuesINPUT[] = {"text", "password", "checkbox", "radio",
1654                                    "submit", "reset", "file", "hidden",
1655                                    "image", "button", NULL};
1656     ctmbstr const valuesBUTTON[] = {"button", "submit", "reset", NULL};
1657     ctmbstr const valuesUL[] = {"disc", "square", "circle", NULL};
1658     ctmbstr const valuesOL[] = {"1", "a", "i", NULL};
1659 
1660     if (nodeIsINPUT(node))
1661         CheckAttrValidity( doc, node, attval, valuesINPUT );
1662     else if (nodeIsBUTTON(node))
1663         CheckAttrValidity( doc, node, attval, valuesBUTTON );
1664     else if (nodeIsUL(node))
1665         CheckAttrValidity( doc, node, attval, valuesUL );
1666     else if (nodeIsOL(node))
1667     {
1668         if (!AttrHasValue(attval))
1669         {
1670             ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1671             return;
1672         }
1673         if (!AttrValueIsAmong(attval, valuesOL))
1674             ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1675     }
1676     else if (nodeIsLI(node))
1677     {
1678         if (!AttrHasValue(attval))
1679         {
1680             ReportAttrError( doc, node, attval, MISSING_ATTR_VALUE);
1681             return;
1682         }
1683         if (AttrValueIsAmong(attval, valuesUL))
1684             CheckLowerCaseAttrValue( doc, node, attval );
1685         else if (!AttrValueIsAmong(attval, valuesOL))
1686             ReportAttrError( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1687     }
1688     return;
1689 }
1690 
1691 /*
1692  * local variables:
1693  * mode: c
1694  * indent-tabs-mode: nil
1695  * c-basic-offset: 4
1696  * eval: (c-set-offset 'substatement-open 0)
1697  * end:
1698  */
1699