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