Coverage Report

Created: 2025-08-29 07:24

/src/tidy-html5/src/attrs.c
Line
Count
Source (jump to first uncovered line)
1
/* attrs.c -- recognize HTML attributes
2
3
  (c) 1998-2009 (W3C) MIT, ERCIM, Keio University
4
  See tidy.h for the copyright notice.
5
6
*/
7
8
#include "tidy-int.h"
9
#include "attrs.h"
10
#include "message.h"
11
#include "tmbstr.h"
12
#include "utf8.h"
13
14
/*
15
 Bind attribute types to procedures to check values.
16
 You can add new procedures for better validation
17
 and each procedure has access to the node in which
18
 the attribute occurred as well as the attribute name
19
 and its value.
20
21
 By default, attributes are checked without regard
22
 to the element they are found on. You have the choice
23
 of making the procedure test which element is involved
24
 or in writing methods for each element which controls
25
 exactly how the attributes of that element are checked.
26
 This latter approach is best for detecting the absence
27
 of required attributes.
28
*/
29
30
static AttrCheck CheckAction;
31
static AttrCheck CheckScript;
32
static AttrCheck CheckName;
33
static AttrCheck CheckId;
34
static AttrCheck CheckIs;
35
static AttrCheck CheckAlign;
36
static AttrCheck CheckValign;
37
static AttrCheck CheckBool;
38
static AttrCheck CheckLength;
39
static AttrCheck CheckTarget;
40
static AttrCheck CheckFsubmit;
41
static AttrCheck CheckClear;
42
static AttrCheck CheckShape;
43
static AttrCheck CheckNumber;
44
static AttrCheck CheckScope;
45
static AttrCheck CheckColor;
46
static AttrCheck CheckVType;
47
static AttrCheck CheckScroll;
48
static AttrCheck CheckTextDir;
49
static AttrCheck CheckLang;
50
static AttrCheck CheckLoading;
51
static AttrCheck CheckType;
52
static AttrCheck CheckRDFaSafeCURIE;
53
static AttrCheck CheckRDFaTerm;
54
static AttrCheck CheckRDFaPrefix;
55
static AttrCheck CheckDecimal;
56
static AttrCheck CheckSvgAttr;
57
58
#define CH_PCDATA      NULL
59
#define CH_CHARSET     NULL
60
#define CH_TYPE        CheckType
61
#define CH_XTYPE       NULL
62
#define CH_CHARACTER   NULL
63
#define CH_URLS        NULL
64
4.50M
#define CH_URL         TY_(CheckUrl)
65
7.02k
#define CH_SCRIPT      CheckScript
66
#define CH_ALIGN       CheckAlign
67
#define CH_VALIGN      CheckValign
68
#define CH_COLOR       CheckColor
69
#define CH_CLEAR       CheckClear
70
#define CH_BORDER      CheckBool     /* kludge */
71
#define CH_LANG        CheckLang
72
#define CH_LOADING     CheckLoading
73
44.9k
#define CH_BOOL        CheckBool
74
#define CH_COLS        NULL
75
#define CH_NUMBER      CheckNumber
76
#define CH_LENGTH      CheckLength
77
#define CH_COORDS      NULL
78
#define CH_DATE        NULL
79
#define CH_TEXTDIR     CheckTextDir
80
#define CH_IDREFS      NULL
81
#define CH_IDREF       NULL
82
#define CH_IDDEF       CheckId
83
#define CH_ISDEF       CheckIs
84
#define CH_NAME        CheckName
85
#define CH_TFRAME      NULL
86
#define CH_FBORDER     NULL
87
#define CH_MEDIA       NULL
88
#define CH_FSUBMIT     CheckFsubmit
89
#define CH_LINKTYPES   NULL
90
#define CH_TRULES      NULL
91
#define CH_SCOPE       CheckScope
92
#define CH_SHAPE       CheckShape
93
#define CH_SCROLL      CheckScroll
94
#define CH_TARGET      CheckTarget
95
#define CH_VTYPE       CheckVType
96
#define CH_ACTION      CheckAction
97
#define CH_RDFAPREFIX  CheckRDFaPrefix
98
#define CH_RDFASCURIE  CheckRDFaSafeCURIE
99
#define CH_RDFASCURIES CheckRDFaSafeCURIE
100
#define CH_RDFATERM    CheckRDFaTerm
101
#define CH_RDFATERMS   CheckRDFaTerm
102
#define CH_DECIMAL     CheckDecimal
103
#define CH_SVG         CheckSvgAttr
104
105
/*
106
   WARNING: This table /must/ be kept in the EXACT order of the TidyAttrId enum!
107
   When running the DEBUG version, this order is checked, in TY_(InitAttrs)(doc),
108
   and there is an assert() if any difference found.
109
*/
110
static const Attribute attribute_defs [] =
111
{
112
  { TidyAttr_UNKNOWN,                 "unknown!",                NULL         }, 
113
  { TidyAttr_ABBR,                    "abbr",                    CH_PCDATA    }, 
114
  { TidyAttr_ACCEPT,                  "accept",                  CH_XTYPE     }, 
115
  { TidyAttr_ACCEPT_CHARSET,          "accept-charset",          CH_CHARSET   }, 
116
  { TidyAttr_ACCESSKEY,               "accesskey",               CH_CHARACTER }, 
117
  { TidyAttr_ACTION,                  "action",                  CH_ACTION    }, 
118
  { TidyAttr_ADD_DATE,                "add_date",                CH_PCDATA    }, /* A */
119
  { TidyAttr_ALIGN,                   "align",                   CH_ALIGN     }, /* varies by element */
120
  { TidyAttr_ALINK,                   "alink",                   CH_COLOR     }, 
121
  { TidyAttr_ALLOWFULLSCREEN,         "allowfullscreen",         CH_BOOL      },
122
  { TidyAttr_ALT,                     "alt",                     CH_PCDATA    }, /* nowrap */
123
  { TidyAttr_ARCHIVE,                 "archive",                 CH_URLS      }, /* space or comma separated list */
124
  { TidyAttr_AXIS,                    "axis",                    CH_PCDATA    }, 
125
  { TidyAttr_BACKGROUND,              "background",              CH_URL       }, 
126
  { TidyAttr_BGCOLOR,                 "bgcolor",                 CH_COLOR     }, 
127
  { TidyAttr_BGPROPERTIES,            "bgproperties",            CH_PCDATA    }, /* BODY "fixed" fixes background */
128
  { TidyAttr_BORDER,                  "border",                  CH_BORDER    }, /* like LENGTH + "border" */
129
  { TidyAttr_BORDERCOLOR,             "bordercolor",             CH_COLOR     }, /* used on TABLE */
130
  { TidyAttr_BOTTOMMARGIN,            "bottommargin",            CH_NUMBER    }, /* used on BODY */
131
  { TidyAttr_CELLPADDING,             "cellpadding",             CH_LENGTH    }, /* % or pixel values */
132
  { TidyAttr_CELLSPACING,             "cellspacing",             CH_LENGTH    }, 
133
  { TidyAttr_CHAR,                    "char",                    CH_CHARACTER }, 
134
  { TidyAttr_CHAROFF,                 "charoff",                 CH_LENGTH    }, 
135
  { TidyAttr_CHARSET,                 "charset",                 CH_CHARSET   }, 
136
  { TidyAttr_CHECKED,                 "checked",                 CH_BOOL      }, /* i.e. "checked" or absent */
137
  { TidyAttr_CITE,                    "cite",                    CH_URL       }, 
138
  { TidyAttr_CLASS,                   "class",                   CH_PCDATA    }, 
139
  { TidyAttr_CLASSID,                 "classid",                 CH_URL       }, 
140
  { TidyAttr_CLEAR,                   "clear",                   CH_CLEAR     }, /* BR: left, right, all */
141
  { TidyAttr_CODE,                    "code",                    CH_PCDATA    }, /* APPLET */
142
  { TidyAttr_CODEBASE,                "codebase",                CH_URL       }, /* OBJECT */
143
  { TidyAttr_CODETYPE,                "codetype",                CH_XTYPE     }, /* OBJECT */
144
  { TidyAttr_COLOR,                   "color",                   CH_COLOR     }, /* BASEFONT, FONT */
145
  { TidyAttr_COLS,                    "cols",                    CH_COLS      }, /* TABLE & FRAMESET */
146
  { TidyAttr_COLSPAN,                 "colspan",                 CH_NUMBER    }, 
147
  { TidyAttr_COMPACT,                 "compact",                 CH_BOOL      }, /* lists */
148
  { TidyAttr_CONTENT,                 "content",                 CH_PCDATA    }, 
149
  { TidyAttr_COORDS,                  "coords",                  CH_COORDS    }, /* AREA, A */
150
  { TidyAttr_DATA,                    "data",                    CH_URL       }, /* OBJECT */
151
  { TidyAttr_DATAFLD,                 "datafld",                 CH_PCDATA    }, /* used on DIV, IMG */
152
  { TidyAttr_DATAFORMATAS,            "dataformatas",            CH_PCDATA    }, /* used on DIV, IMG */
153
  { TidyAttr_DATAPAGESIZE,            "datapagesize",            CH_NUMBER    }, /* used on DIV, IMG */
154
  { TidyAttr_DATASRC,                 "datasrc",                 CH_URL       }, /* used on TABLE */
155
  { TidyAttr_DATETIME,                "datetime",                CH_DATE      }, /* INS, DEL */
156
  { TidyAttr_DECLARE,                 "declare",                 CH_BOOL      }, /* OBJECT */
157
  { TidyAttr_DEFER,                   "defer",                   CH_BOOL      }, /* SCRIPT */
158
  { TidyAttr_DIR,                     "dir",                     CH_TEXTDIR   }, /* ltr, rtl or auto */
159
  { TidyAttr_DISABLED,                "disabled",                CH_BOOL      }, /* form fields */
160
  { TidyAttr_DOWNLOAD,                "download",                CH_PCDATA    }, /* anchor */
161
  { TidyAttr_ENCODING,                "encoding",                CH_PCDATA    }, /* <?xml?> */
162
  { TidyAttr_ENCTYPE,                 "enctype",                 CH_XTYPE     }, /* FORM */
163
  { TidyAttr_FACE,                    "face",                    CH_PCDATA    }, /* BASEFONT, FONT */
164
  { TidyAttr_FOR,                     "for",                     CH_IDREF     }, /* LABEL */
165
  { TidyAttr_FRAME,                   "frame",                   CH_TFRAME    }, /* TABLE */
166
  { TidyAttr_FRAMEBORDER,             "frameborder",             CH_FBORDER   }, /* 0 or 1 */
167
  { TidyAttr_FRAMESPACING,            "framespacing",            CH_NUMBER    }, 
168
  { TidyAttr_GRIDX,                   "gridx",                   CH_NUMBER    }, /* TABLE Adobe golive*/
169
  { TidyAttr_GRIDY,                   "gridy",                   CH_NUMBER    }, /* TABLE Adobe golive */
170
  { TidyAttr_HEADERS,                 "headers",                 CH_IDREFS    }, /* table cells */
171
  { TidyAttr_HEIGHT,                  "height",                  CH_LENGTH    }, /* pixels only for TH/TD */
172
  { TidyAttr_HREF,                    "href",                    CH_URL       }, /* A, AREA, LINK and BASE */
173
  { TidyAttr_HREFLANG,                "hreflang",                CH_LANG      }, /* A, LINK */
174
  { TidyAttr_HSPACE,                  "hspace",                  CH_NUMBER    }, /* APPLET, IMG, OBJECT */
175
  { TidyAttr_HTTP_EQUIV,              "http-equiv",              CH_PCDATA    }, /* META */
176
  { TidyAttr_ID,                      "id",                      CH_IDDEF     }, 
177
  { TidyAttr_IS,                      "is",                      CH_ISDEF     },
178
  { TidyAttr_ISMAP,                   "ismap",                   CH_BOOL      }, /* IMG */
179
  { TidyAttr_ITEMID,                  "itemid",                  CH_PCDATA    },
180
  { TidyAttr_ITEMPROP,                "itemprop",                CH_PCDATA    },
181
  { TidyAttr_ITEMREF,                 "itemref",                 CH_PCDATA    },
182
  { TidyAttr_ITEMSCOPE,               "itemscope",               CH_BOOL      },
183
  { TidyAttr_ITEMTYPE,                "itemtype",                CH_URL       },
184
  { TidyAttr_LABEL,                   "label",                   CH_PCDATA    }, /* OPT, OPTGROUP */
185
  { TidyAttr_LANG,                    "lang",                    CH_LANG      }, 
186
  { TidyAttr_LANGUAGE,                "language",                CH_PCDATA    }, /* SCRIPT */
187
  { TidyAttr_LAST_MODIFIED,           "last_modified",           CH_PCDATA    }, /* A */
188
  { TidyAttr_LAST_VISIT,              "last_visit",              CH_PCDATA    }, /* A */
189
  { TidyAttr_LEFTMARGIN,              "leftmargin",              CH_NUMBER    }, /* used on BODY */
190
  { TidyAttr_LINK,                    "link",                    CH_COLOR     }, /* BODY */
191
  { TidyAttr_LONGDESC,                "longdesc",                CH_URL       }, /* IMG */
192
  { TidyAttr_LOWSRC,                  "lowsrc",                  CH_URL       }, /* IMG */
193
  { TidyAttr_MARGINHEIGHT,            "marginheight",            CH_NUMBER    }, /* FRAME, IFRAME, BODY */
194
  { TidyAttr_MARGINWIDTH,             "marginwidth",             CH_NUMBER    }, /* ditto */
195
  { TidyAttr_MAXLENGTH,               "maxlength",               CH_NUMBER    }, /* INPUT */
196
  { TidyAttr_MEDIA,                   "media",                   CH_MEDIA     }, /* STYLE, LINK */
197
  { TidyAttr_METHOD,                  "method",                  CH_FSUBMIT   }, /* FORM: get or post */
198
  { TidyAttr_MULTIPLE,                "multiple",                CH_BOOL      }, /* SELECT */
199
  { TidyAttr_NAME,                    "name",                    CH_NAME      }, 
200
  { TidyAttr_NOHREF,                  "nohref",                  CH_BOOL      }, /* AREA */
201
  { TidyAttr_NORESIZE,                "noresize",                CH_BOOL      }, /* FRAME */
202
  { TidyAttr_NOSHADE,                 "noshade",                 CH_BOOL      }, /* HR */
203
  { TidyAttr_NOWRAP,                  "nowrap",                  CH_BOOL      }, /* table cells */
204
  { TidyAttr_OBJECT,                  "object",                  CH_PCDATA    }, /* APPLET */
205
  { TidyAttr_OnAFTERUPDATE,           "onafterupdate",           CH_SCRIPT    }, 
206
  { TidyAttr_OnBEFOREUNLOAD,          "onbeforeunload",          CH_SCRIPT    }, 
207
  { TidyAttr_OnBEFOREUPDATE,          "onbeforeupdate",          CH_SCRIPT    }, 
208
  { TidyAttr_OnBLUR,                  "onblur",                  CH_SCRIPT    }, /* event */
209
  { TidyAttr_OnCHANGE,                "onchange",                CH_SCRIPT    }, /* event */
210
  { TidyAttr_OnCLICK,                 "onclick",                 CH_SCRIPT    }, /* event */
211
  { TidyAttr_OnDATAAVAILABLE,         "ondataavailable",         CH_SCRIPT    }, /* object, applet */
212
  { TidyAttr_OnDATASETCHANGED,        "ondatasetchanged",        CH_SCRIPT    }, /* object, applet */
213
  { TidyAttr_OnDATASETCOMPLETE,       "ondatasetcomplete",       CH_SCRIPT    }, 
214
  { TidyAttr_OnDBLCLICK,              "ondblclick",              CH_SCRIPT    }, /* event */
215
  { TidyAttr_OnERRORUPDATE,           "onerrorupdate",           CH_SCRIPT    }, /* form fields */
216
  { TidyAttr_OnFOCUS,                 "onfocus",                 CH_SCRIPT    }, /* event */
217
  { TidyAttr_OnKEYDOWN,               "onkeydown",               CH_SCRIPT    }, /* event */
218
  { TidyAttr_OnKEYPRESS,              "onkeypress",              CH_SCRIPT    }, /* event */
219
  { TidyAttr_OnKEYUP,                 "onkeyup",                 CH_SCRIPT    }, /* event */
220
  { TidyAttr_OnLOAD,                  "onload",                  CH_SCRIPT    }, /* event */
221
  { TidyAttr_OnMOUSEDOWN,             "onmousedown",             CH_SCRIPT    }, /* event */
222
  { TidyAttr_OnMOUSEMOVE,             "onmousemove",             CH_SCRIPT    }, /* event */
223
  { TidyAttr_OnMOUSEOUT,              "onmouseout",              CH_SCRIPT    }, /* event */
224
  { TidyAttr_OnMOUSEOVER,             "onmouseover",             CH_SCRIPT    }, /* event */
225
  { TidyAttr_OnMOUSEUP,               "onmouseup",               CH_SCRIPT    }, /* event */
226
  { TidyAttr_OnRESET,                 "onreset",                 CH_SCRIPT    }, /* event */
227
  { TidyAttr_OnROWENTER,              "onrowenter",              CH_SCRIPT    }, /* form fields */
228
  { TidyAttr_OnROWEXIT,               "onrowexit",               CH_SCRIPT    }, /* form fields */
229
  { TidyAttr_OnSELECT,                "onselect",                CH_SCRIPT    }, /* event */
230
  { TidyAttr_OnSUBMIT,                "onsubmit",                CH_SCRIPT    }, /* event */
231
  { TidyAttr_OnUNLOAD,                "onunload",                CH_SCRIPT    }, /* event */
232
  { TidyAttr_PROFILE,                 "profile",                 CH_URL       }, /* HEAD */
233
  { TidyAttr_PROMPT,                  "prompt",                  CH_PCDATA    }, /* ISINDEX */
234
  { TidyAttr_RBSPAN,                  "rbspan",                  CH_NUMBER    }, /* ruby markup */
235
  { TidyAttr_READONLY,                "readonly",                CH_BOOL      }, /* form fields */
236
  { TidyAttr_REL,                     "rel",                     CH_LINKTYPES }, 
237
  { TidyAttr_REV,                     "rev",                     CH_LINKTYPES }, 
238
  { TidyAttr_RIGHTMARGIN,             "rightmargin",             CH_NUMBER    }, /* used on BODY */
239
  { TidyAttr_ROLE,                    "role",                    CH_PCDATA    },
240
  { TidyAttr_ROWS,                    "rows",                    CH_NUMBER    }, /* TEXTAREA */
241
  { TidyAttr_ROWSPAN,                 "rowspan",                 CH_NUMBER    }, /* table cells */
242
  { TidyAttr_RULES,                   "rules",                   CH_TRULES    }, /* TABLE */
243
  { TidyAttr_SCHEME,                  "scheme",                  CH_PCDATA    }, /* META */
244
  { TidyAttr_SCOPE,                   "scope",                   CH_SCOPE     }, /* table cells */
245
  { TidyAttr_SCROLLING,               "scrolling",               CH_SCROLL    }, /* yes, no or auto */
246
  { TidyAttr_SELECTED,                "selected",                CH_BOOL      }, /* OPTION */
247
  { TidyAttr_SHAPE,                   "shape",                   CH_SHAPE     }, /* AREA, A */
248
  { TidyAttr_SHOWGRID,                "showgrid",                CH_BOOL      }, /* TABLE Adobe golive */
249
  { TidyAttr_SHOWGRIDX,               "showgridx",               CH_BOOL      }, /* TABLE Adobe golive*/
250
  { TidyAttr_SHOWGRIDY,               "showgridy",               CH_BOOL      }, /* TABLE Adobe golive*/
251
  { TidyAttr_SIZE,                    "size",                    CH_NUMBER    }, /* HR, FONT, BASEFONT, SELECT */
252
  { TidyAttr_SPAN,                    "span",                    CH_NUMBER    }, /* COL, COLGROUP */
253
  { TidyAttr_SRC,                     "src",                     CH_URL       }, /* IMG, FRAME, IFRAME */
254
  { TidyAttr_SRCSET,                  "srcset",                  CH_PCDATA    }, /* IMG (HTML5) */
255
  { TidyAttr_STANDBY,                 "standby",                 CH_PCDATA    }, /* OBJECT */
256
  { TidyAttr_START,                   "start",                   CH_NUMBER    }, /* OL */
257
  { TidyAttr_STYLE,                   "style",                   CH_PCDATA    }, 
258
  { TidyAttr_SUMMARY,                 "summary",                 CH_PCDATA    }, /* TABLE */
259
  { TidyAttr_TABINDEX,                "tabindex",                CH_NUMBER    }, /* fields, OBJECT  and A */
260
  { TidyAttr_TARGET,                  "target",                  CH_TARGET    }, /* names a frame/window */
261
  { TidyAttr_TEXT,                    "text",                    CH_COLOR     }, /* BODY */
262
  { TidyAttr_TITLE,                   "title",                   CH_PCDATA    }, /* text tool tip */
263
  { TidyAttr_TOPMARGIN,               "topmargin",               CH_NUMBER    }, /* used on BODY */
264
  { TidyAttr_TRANSLATE,               "translate",               CH_BOOL      }, /* HTML5 global attribute */
265
  { TidyAttr_TYPE,                    "type",                    CH_TYPE      }, /* also used by SPACER */
266
  { TidyAttr_USEMAP,                  "usemap",                  CH_URL       }, /* things with images */
267
  { TidyAttr_VALIGN,                  "valign",                  CH_VALIGN    }, 
268
  { TidyAttr_VALUE,                   "value",                   CH_PCDATA    }, 
269
  { TidyAttr_VALUETYPE,               "valuetype",               CH_VTYPE     }, /* PARAM: data, ref, object */
270
  { TidyAttr_VERSION,                 "version",                 CH_PCDATA    }, /* HTML <?xml?> */
271
  { TidyAttr_VLINK,                   "vlink",                   CH_COLOR     }, /* BODY */
272
  { TidyAttr_VSPACE,                  "vspace",                  CH_NUMBER    }, /* IMG, OBJECT, APPLET */
273
  { TidyAttr_WIDTH,                   "width",                   CH_LENGTH    }, /* pixels only for TD/TH */
274
  { TidyAttr_WRAP,                    "wrap",                    CH_PCDATA    }, /* textarea */
275
  { TidyAttr_XML_LANG,                "xml:lang",                CH_LANG      }, /* XML language */
276
  { TidyAttr_XML_SPACE,               "xml:space",               CH_PCDATA    }, /* XML white space */
277
278
  /* todo: VERS_ALL is wrong! */
279
  { TidyAttr_XMLNS,                   "xmlns",                   CH_PCDATA    }, /* name space */
280
  { TidyAttr_EVENT,                   "event",                   CH_PCDATA    }, /* reserved for <script> */
281
  { TidyAttr_METHODS,                 "methods",                 CH_PCDATA    }, /* for <a>, never implemented */
282
  { TidyAttr_N,                       "n",                       CH_PCDATA    }, /* for <nextid> */
283
  { TidyAttr_SDAFORM,                 "sdaform",                 CH_PCDATA    }, /* SDATA attribute in HTML 2.0 */
284
  { TidyAttr_SDAPREF,                 "sdapref",                 CH_PCDATA    }, /* SDATA attribute in HTML 2.0 */
285
  { TidyAttr_SDASUFF,                 "sdasuff",                 CH_PCDATA    }, /* SDATA attribute in HTML 2.0 */
286
  { TidyAttr_URN,                     "urn",                     CH_PCDATA    }, /* for <a>, never implemented */
287
288
  /* HTML5 */
289
  { TidyAttr_ASYNC,                   "async",                   CH_BOOL     }, /* <script src="..." async> */
290
  { TidyAttr_AUTOCOMPLETE,            "autocomplete",            CH_PCDATA   },
291
  { TidyAttr_AUTOFOCUS,               "autofocus",               CH_PCDATA   },
292
  { TidyAttr_AUTOPLAY,                "autoplay",                CH_PCDATA   },
293
  { TidyAttr_CHALLENGE,               "challenge",               CH_PCDATA   },
294
  { TidyAttr_CONTENTEDITABLE,         "contenteditable",         CH_PCDATA   },
295
  { TidyAttr_CONTEXTMENU,             "contextmenu",             CH_PCDATA   },
296
  { TidyAttr_CONTROLS,                "controls",                CH_PCDATA   },
297
  { TidyAttr_CROSSORIGIN,             "crossorigin",             CH_PCDATA   },
298
  { TidyAttr_DEFAULT,                 "default",                 CH_PCDATA   },
299
  { TidyAttr_DIRNAME,                 "dirname",                 CH_PCDATA   },
300
  { TidyAttr_DRAGGABLE,               "draggable",               CH_PCDATA   },
301
  { TidyAttr_DROPZONE,                "dropzone",                CH_PCDATA   },
302
  { TidyAttr_FORM,                    "form",                    CH_PCDATA   },
303
  { TidyAttr_FORMACTION,              "formaction",              CH_PCDATA   },
304
  { TidyAttr_FORMENCTYPE,             "formenctype",             CH_PCDATA   },
305
  { TidyAttr_FORMMETHOD,              "formmethod",              CH_PCDATA   },
306
  { TidyAttr_FORMNOVALIDATE,          "formnovalidate",          CH_PCDATA   },
307
  { TidyAttr_FORMTARGET,              "formtarget",              CH_PCDATA   },
308
  { TidyAttr_HIDDEN,                  "hidden",                  CH_PCDATA   },
309
  { TidyAttr_HIGH,                    "high",                    CH_PCDATA   },
310
  { TidyAttr_ICON,                    "icon",                    CH_PCDATA   },
311
  { TidyAttr_KEYTYPE,                 "keytype",                 CH_PCDATA   },
312
  { TidyAttr_KIND,                    "kind",                    CH_PCDATA   },
313
  { TidyAttr_LIST,                    "list",                    CH_PCDATA   },
314
  { TidyAttr_LOOP,                    "loop",                    CH_PCDATA   },
315
  { TidyAttr_LOW,                     "low",                     CH_PCDATA   },
316
  { TidyAttr_MANIFEST,                "manifest",                CH_PCDATA   },
317
  { TidyAttr_MAX,                     "max",                     CH_PCDATA   },
318
  { TidyAttr_MEDIAGROUP,              "mediagroup",              CH_PCDATA   },
319
  { TidyAttr_MIN,                     "min",                     CH_PCDATA   },
320
  { TidyAttr_MUTED,                   "muted",                   CH_BOOL     },
321
  { TidyAttr_NOVALIDATE,              "novalidate",              CH_PCDATA   },
322
  { TidyAttr_OPEN,                    "open",                    CH_BOOL     }, /* Is. #925 PR #932 */
323
  { TidyAttr_OPTIMUM,                 "optimum",                 CH_PCDATA   },
324
  { TidyAttr_OnABORT,                 "onabort",                 CH_PCDATA   },
325
  { TidyAttr_OnAFTERPRINT,            "onafterprint",            CH_PCDATA   },
326
  { TidyAttr_OnBEFOREPRINT,           "onbeforeprint",           CH_PCDATA   },
327
  { TidyAttr_OnCANPLAY,               "oncanplay",               CH_PCDATA   },
328
  { TidyAttr_OnCANPLAYTHROUGH,        "oncanplaythrough",        CH_PCDATA   },
329
  { TidyAttr_OnCONTEXTMENU,           "oncontextmenu",           CH_PCDATA   },
330
  { TidyAttr_OnCUECHANGE,             "oncuechange",             CH_PCDATA   },
331
  { TidyAttr_OnDRAG,                  "ondrag",                  CH_PCDATA   },
332
  { TidyAttr_OnDRAGEND,               "ondragend",               CH_PCDATA   },
333
  { TidyAttr_OnDRAGENTER,             "ondragenter",             CH_PCDATA   },
334
  { TidyAttr_OnDRAGLEAVE,             "ondragleave",             CH_PCDATA   },
335
  { TidyAttr_OnDRAGOVER,              "ondragover",              CH_PCDATA   },
336
  { TidyAttr_OnDRAGSTART,             "ondragstart",             CH_PCDATA   },
337
  { TidyAttr_OnDROP,                  "ondrop",                  CH_PCDATA   },
338
  { TidyAttr_OnDURATIONCHANGE,        "ondurationchange",        CH_PCDATA   },
339
  { TidyAttr_OnEMPTIED,               "onemptied",               CH_PCDATA   },
340
  { TidyAttr_OnENDED,                 "onended",                 CH_PCDATA   },
341
  { TidyAttr_OnERROR,                 "onerror",                 CH_PCDATA   },
342
  { TidyAttr_OnHASHCHANGE,            "onhashchange",            CH_PCDATA   },
343
  { TidyAttr_OnINPUT,                 "oninput",                 CH_PCDATA   },
344
  { TidyAttr_OnINVALID,               "oninvalid",               CH_PCDATA   },
345
  { TidyAttr_OnLOADEDDATA,            "onloadeddata",            CH_PCDATA   },
346
  { TidyAttr_OnLOADEDMETADATA,        "onloadedmetadata",        CH_PCDATA   },
347
  { TidyAttr_OnLOADSTART,             "onloadstart",             CH_PCDATA   },
348
  { TidyAttr_OnMESSAGE,               "onmessage",               CH_PCDATA   },
349
  { TidyAttr_OnMOUSEWHEEL,            "onmousewheel",            CH_PCDATA   },
350
  { TidyAttr_OnOFFLINE,               "onoffline",               CH_PCDATA   },
351
  { TidyAttr_OnONLINE,                "ononline",                CH_PCDATA   },
352
  { TidyAttr_OnPAGEHIDE,              "onpagehide",              CH_PCDATA   },
353
  { TidyAttr_OnPAGESHOW,              "onpageshow",              CH_PCDATA   },
354
  { TidyAttr_OnPAUSE,                 "onpause",                 CH_PCDATA   },
355
  { TidyAttr_OnPLAY,                  "onplay",                  CH_PCDATA   },
356
  { TidyAttr_OnPLAYING,               "onplaying",               CH_PCDATA   },
357
  { TidyAttr_OnPOPSTATE,              "onpopstate",              CH_PCDATA   },
358
  { TidyAttr_OnPROGRESS,              "onprogress",              CH_PCDATA   },
359
  { TidyAttr_OnRATECHANGE,            "onratechange",            CH_PCDATA   },
360
  { TidyAttr_OnREADYSTATECHANGE,      "onreadystatechange",      CH_PCDATA   },
361
  { TidyAttr_OnREDO,                  "onredo",                  CH_PCDATA   },
362
  { TidyAttr_OnRESIZE,                "onresize",                CH_PCDATA   },
363
  { TidyAttr_OnSCROLL,                "onscroll",                CH_PCDATA   },
364
  { TidyAttr_OnSEEKED,                "onseeked",                CH_PCDATA   },
365
  { TidyAttr_OnSEEKING,               "onseeking",               CH_PCDATA   },
366
  { TidyAttr_OnSHOW,                  "onshow",                  CH_PCDATA   },
367
  { TidyAttr_OnSTALLED,               "onstalled",               CH_PCDATA   },
368
  { TidyAttr_OnSTORAGE,               "onstorage",               CH_PCDATA   },
369
  { TidyAttr_OnSUSPEND,               "onsuspend",               CH_PCDATA   },
370
  { TidyAttr_OnTIMEUPDATE,            "ontimeupdate",            CH_PCDATA   },
371
  { TidyAttr_OnUNDO,                  "onundo",                  CH_PCDATA   },
372
  { TidyAttr_OnVOLUMECHANGE,          "onvolumechange",          CH_PCDATA   },
373
  { TidyAttr_OnWAITING,               "onwaiting",               CH_PCDATA   },
374
  { TidyAttr_PATTERN,                 "pattern",                 CH_PCDATA   },
375
  { TidyAttr_PLACEHOLDER,             "placeholder",             CH_PCDATA   },
376
  { TidyAttr_PLAYSINLINE,             "playsinline",             CH_BOOL     },
377
  { TidyAttr_POSTER,                  "poster",                  CH_PCDATA   },
378
  { TidyAttr_PRELOAD,                 "preload",                 CH_PCDATA   },
379
  { TidyAttr_PUBDATE,                 "pubdate",                 CH_PCDATA   },
380
  { TidyAttr_RADIOGROUP,              "radiogroup",              CH_PCDATA   },
381
  { TidyAttr_REQUIRED,                "required",                CH_PCDATA   },
382
  { TidyAttr_REVERSED,                "reversed",                CH_PCDATA   },
383
  { TidyAttr_SANDBOX,                 "sandbox",                 CH_PCDATA   },
384
  { TidyAttr_SCOPED,                  "scoped",                  CH_PCDATA   },
385
  { TidyAttr_SEAMLESS,                "seamless",                CH_PCDATA   },
386
  { TidyAttr_SIZES,                   "sizes",                   CH_PCDATA   },
387
  { TidyAttr_SPELLCHECK,              "spellcheck",              CH_PCDATA   },
388
  { TidyAttr_SRCDOC,                  "srcdoc",                  CH_PCDATA   },
389
  { TidyAttr_SRCLANG,                 "srclang",                 CH_PCDATA   },
390
  { TidyAttr_STEP,                    "step",                    CH_PCDATA   },
391
392
  /* HTML5 Aria Attributes */
393
  { TidyAttr_ARIA_ACTIVEDESCENDANT,   "aria-activedescendant",   CH_PCDATA   },
394
  { TidyAttr_ARIA_ATOMIC,             "aria-atomic",             CH_PCDATA   },
395
  { TidyAttr_ARIA_AUTOCOMPLETE,       "aria-autocomplete",       CH_PCDATA   },
396
  { TidyAttr_ARIA_BUSY,               "aria-busy",               CH_PCDATA   },
397
  { TidyAttr_ARIA_CHECKED,            "aria-checked",            CH_PCDATA   },
398
  { TidyAttr_ARIA_CONTROLS,           "aria-controls",           CH_PCDATA   },
399
  { TidyAttr_ARIA_DESCRIBEDBY,        "aria-describedby",        CH_PCDATA   },
400
  { TidyAttr_ARIA_DISABLED,           "aria-disabled",           CH_PCDATA   },
401
  { TidyAttr_ARIA_DROPEFFECT,         "aria-dropeffect",         CH_PCDATA   },
402
  { TidyAttr_ARIA_EXPANDED,           "aria-expanded",           CH_PCDATA   },
403
  { TidyAttr_ARIA_FLOWTO,             "aria-flowto",             CH_PCDATA   },
404
  { TidyAttr_ARIA_GRABBED,            "aria-grabbed",            CH_PCDATA   },
405
  { TidyAttr_ARIA_HASPOPUP,           "aria-haspopup",           CH_PCDATA   },
406
  { TidyAttr_ARIA_HIDDEN,             "aria-hidden",             CH_PCDATA   },
407
  { TidyAttr_ARIA_INVALID,            "aria-invalid",            CH_PCDATA   },
408
  { TidyAttr_ARIA_LABEL,              "aria-label",              CH_PCDATA   },
409
  { TidyAttr_ARIA_LABELLEDBY,         "aria-labelledby",         CH_PCDATA   },
410
  { TidyAttr_ARIA_LEVEL,              "aria-level",              CH_PCDATA   },
411
  { TidyAttr_ARIA_LIVE,               "aria-live",               CH_PCDATA   },
412
  { TidyAttr_ARIA_MULTILINE,          "aria-multiline",          CH_PCDATA   },
413
  { TidyAttr_ARIA_MULTISELECTABLE,    "aria-multiselectable",    CH_PCDATA   },
414
  { TidyAttr_ARIA_ORIENTATION,        "aria-orientation",        CH_PCDATA   },
415
  { TidyAttr_ARIA_OWNS,               "aria-owns",               CH_PCDATA   },
416
  { TidyAttr_ARIA_POSINSET,           "aria-posinset",           CH_PCDATA   },
417
  { TidyAttr_ARIA_PRESSED,            "aria-pressed",            CH_PCDATA   },
418
  { TidyAttr_ARIA_READONLY,           "aria-readonly",           CH_PCDATA   },
419
  { TidyAttr_ARIA_RELEVANT,           "aria-relevant",           CH_PCDATA   },
420
  { TidyAttr_ARIA_REQUIRED,           "aria-required",           CH_PCDATA   },
421
  { TidyAttr_ARIA_SELECTED,           "aria-selected",           CH_PCDATA   },
422
  { TidyAttr_ARIA_SETSIZE,            "aria-setsize",            CH_PCDATA   },
423
  { TidyAttr_ARIA_SORT,               "aria-sort",               CH_PCDATA   },
424
  { TidyAttr_ARIA_VALUEMAX,           "aria-valuemax",           CH_PCDATA   },
425
  { TidyAttr_ARIA_VALUEMIN,           "aria-valuemin",           CH_PCDATA   },
426
  { TidyAttr_ARIA_VALUENOW,           "aria-valuenow",           CH_PCDATA   },
427
  { TidyAttr_ARIA_VALUETEXT,          "aria-valuetext",          CH_PCDATA   },
428
  { TidyAttr_X,                       "x",                       CH_PCDATA    }, /* for <svg> */
429
  { TidyAttr_Y,                       "y",                       CH_PCDATA    }, /* for <svg> */
430
  { TidyAttr_VIEWBOX,                  "viewbox",                CH_PCDATA    }, /* for <svg> */
431
  { TidyAttr_PRESERVEASPECTRATIO,      "preserveaspectratio",    CH_PCDATA    }, /* for <svg> */
432
  { TidyAttr_ZOOMANDPAN,               "zoomandpan",             CH_PCDATA    }, /* for <svg> */
433
  { TidyAttr_BASEPROFILE,              "baseprofile",            CH_PCDATA    }, /* for <svg> */
434
  { TidyAttr_CONTENTSCRIPTTYPE,        "contentscripttype",      CH_PCDATA    }, /* for <svg> */
435
  { TidyAttr_CONTENTSTYLETYPE,         "contentstyletype",       CH_PCDATA    }, /* for <svg> */
436
  { TidyAttr_DISPLAY,                  "display",                CH_PCDATA   }, /* on MATH tag (html5) */
437
438
  /* RDFa Attributes */
439
  { TidyAttr_ABOUT,                    "about",                  CH_RDFASCURIE },
440
  { TidyAttr_DATATYPE,                 "datatype",               CH_RDFATERM   },
441
  { TidyAttr_INLIST,                   "inlist",                 CH_BOOL       },
442
  { TidyAttr_PREFIX,                   "prefix",                 CH_RDFAPREFIX },
443
  { TidyAttr_PROPERTY,                 "property",               CH_RDFATERMS  },
444
  { TidyAttr_RESOURCE,                 "resource",               CH_RDFASCURIE },
445
  { TidyAttr_TYPEOF,                   "typeof",                 CH_RDFATERMS  },
446
  { TidyAttr_VOCAB,                    "vocab",                  CH_URL        },
447
448
  { TidyAttr_INTEGRITY,                "integrity",              CH_PCDATA   },
449
450
  /* Preload spec: https://www.w3.org/TR/preload/ */
451
  { TidyAttr_AS,                       "as",                     CH_PCDATA   },
452
453
  /* for xmlns:xlink in <svg> */
454
  { TidyAttr_XMLNSXLINK,                "xmlns:xlink",           CH_URL       },
455
  { TidyAttr_SLOT,                      "slot",                  CH_PCDATA    },
456
  { TidyAttr_LOADING,                   "loading",               CH_LOADING   }, /* IMG, IFRAME */
457
458
  /* SVG paint attributes (SVG 1.1) */
459
  { TidyAttr_FILL,                     "fill",                  CH_SVG        },
460
  { TidyAttr_FILLRULE,                 "fill-rule",             CH_SVG        },
461
  { TidyAttr_STROKE,                   "stroke",                CH_SVG        },
462
  { TidyAttr_STROKEDASHARRAY,          "stroke-dasharray",      CH_SVG        },
463
  { TidyAttr_STROKEDASHOFFSET,         "stroke-dashoffset",     CH_SVG        },
464
  { TidyAttr_STROKELINECAP,            "stroke-linecap",        CH_SVG        },
465
  { TidyAttr_STROKELINEJOIN,           "stroke-linejoin",       CH_SVG        },
466
  { TidyAttr_STROKEMITERLIMIT,         "stroke-miterlimit",     CH_SVG        },
467
  { TidyAttr_STROKEWIDTH,              "stroke-width",          CH_SVG        },
468
  { TidyAttr_COLORINTERPOLATION,       "color-interpolation",   CH_SVG        },
469
  { TidyAttr_COLORRENDERING,           "color-rendering",       CH_SVG        },
470
  { TidyAttr_OPACITY,                  "opacity",               CH_SVG        },
471
  { TidyAttr_STROKEOPACITY,            "stroke-opacity",        CH_SVG        },
472
  { TidyAttr_FILLOPACITY,              "fill-opacity",          CH_SVG        },
473
474
  /* this must be the final entry */
475
  { N_TIDY_ATTRIBS,                    NULL,                     NULL         }
476
};
477
478
static uint AttributeVersions(Node* node, AttVal* attval)
479
799k
{
480
799k
    uint i;
481
482
    /* Override or add to items in attrdict.c */
483
799k
    if (attval && attval->attribute) {
484
        /* HTML5 data-* attributes can't be added generically; handle here. */
485
781k
        if (TY_(tmbstrncmp)(attval->attribute, "data-", 5) == 0)
486
998
            return (XH50 | HT50);
487
781k
    }
488
    /* TODO: maybe this should return VERS_PROPRIETARY instead? */
489
798k
    if (!attval || !attval->dict)
490
108k
        return VERS_UNKNOWN;
491
492
689k
    if (!(!node || !node->tag || !node->tag->attrvers))
493
48.3M
        for (i = 0; node->tag->attrvers[i].attribute; ++i)
494
48.2M
            if (node->tag->attrvers[i].attribute == attval->dict->id)
495
561k
                return node->tag->attrvers[i].versions;
496
497
128k
    return VERS_PROPRIETARY;
498
689k
}
499
500
501
/* return the version of the attribute "id" of element "node" */
502
uint TY_(NodeAttributeVersions)( Node* node, TidyAttrId id )
503
46.2k
{
504
46.2k
    uint i;
505
506
46.2k
    if (!node || !node->tag || !node->tag->attrvers)
507
243
        return VERS_UNKNOWN;
508
509
2.23M
    for (i = 0; node->tag->attrvers[i].attribute; ++i)
510
2.23M
        if (node->tag->attrvers[i].attribute == id)
511
46.0k
            return node->tag->attrvers[i].versions;
512
513
0
    return VERS_UNKNOWN;
514
46.0k
}
515
516
/* returns true if the element is a W3C defined element
517
 * but the element/attribute combination is not. We're
518
 * only defining as "proprietary" items that are not in
519
 * the element's AttrVersion structure.
520
 */
521
Bool TY_(AttributeIsProprietary)(Node* node, AttVal* attval)
522
353k
{
523
353k
    if (!node || !attval)
524
0
        return no;
525
526
353k
    if (!node->tag)
527
10.4k
        return no;
528
529
343k
    if (!(node->tag->versions & VERS_ALL))
530
2.86k
        return no;
531
532
340k
    if (AttributeVersions(node, attval) & VERS_ALL)
533
211k
        return no;
534
535
128k
    return yes;
536
340k
}
537
538
/* returns true if the element is a W3C defined element
539
 * but the element/attribute combination is not. We're
540
 * considering it a mismatch if the document version
541
 * does not allow the attribute as called out in its
542
 * AttrVersion structure.
543
 */
544
Bool TY_(AttributeIsMismatched)(Node* node, AttVal* attval, TidyDocImpl* doc)
545
15.7k
{
546
15.7k
    uint doctype;
547
    
548
15.7k
    if (!node || !attval)
549
0
        return no;
550
    
551
15.7k
    if (!node->tag)
552
350
        return no;
553
    
554
15.3k
    if (!(node->tag->versions & VERS_ALL))
555
999
        return no;
556
557
14.3k
    doctype = doc->lexer->versionEmitted == 0 ? doc->lexer->doctype : doc->lexer->versionEmitted;
558
559
14.3k
    if (AttributeVersions(node, attval) & doctype)
560
6.04k
        return no;
561
    
562
8.32k
    return yes;
563
14.3k
}
564
565
566
/* used by CheckColor() */
567
struct _colors
568
{
569
    ctmbstr name;
570
    ctmbstr hex;
571
};
572
573
static const struct _colors colors[] =
574
{
575
    { "black",   "#000000" },
576
    { "green",   "#008000" },
577
    { "silver",  "#C0C0C0" },
578
    { "lime",    "#00FF00" },
579
    { "gray",    "#808080" },
580
    { "olive",   "#808000" },
581
    { "white",   "#FFFFFF" },
582
    { "yellow",  "#FFFF00" },
583
    { "maroon",  "#800000" },
584
    { "navy",    "#000080" },
585
    { "red",     "#FF0000" },
586
    { "blue",    "#0000FF" },
587
    { "purple",  "#800080" },
588
    { "teal",    "#008080" },
589
    { "fuchsia", "#FF00FF" },
590
    { "aqua",    "#00FFFF" },
591
    { NULL,      NULL      }
592
};
593
594
static const struct _colors extended_colors[] =
595
{
596
    { "aliceblue", "#f0f8ff" },
597
    { "antiquewhite", "#faebd7" },
598
    { "aquamarine", "#7fffd4" },
599
    { "azure", "#f0ffff" },
600
    { "beige", "#f5f5dc" },
601
    { "bisque", "#ffe4c4" },
602
    { "blanchedalmond", "#ffebcd" },
603
    { "blueviolet", "#8a2be2" },
604
    { "brown", "#a52a2a" },
605
    { "burlywood", "#deb887" },
606
    { "cadetblue", "#5f9ea0" },
607
    { "chartreuse", "#7fff00" },
608
    { "chocolate", "#d2691e" },
609
    { "coral", "#ff7f50" },
610
    { "cornflowerblue", "#6495ed" },
611
    { "cornsilk", "#fff8dc" },
612
    { "crimson", "#dc143c" },
613
    { "cyan", "#00ffff" },
614
    { "darkblue", "#00008b" },
615
    { "darkcyan", "#008b8b" },
616
    { "darkgoldenrod", "#b8860b" },
617
    { "darkgray", "#a9a9a9" },
618
    { "darkgreen", "#006400" },
619
    { "darkgrey", "#a9a9a9" },
620
    { "darkkhaki", "#bdb76b" },
621
    { "darkmagenta", "#8b008b" },
622
    { "darkolivegreen", "#556b2f" },
623
    { "darkorange", "#ff8c00" },
624
    { "darkorchid", "#9932cc" },
625
    { "darkred", "#8b0000" },
626
    { "darksalmon", "#e9967a" },
627
    { "darkseagreen", "#8fbc8f" },
628
    { "darkslateblue", "#483d8b" },
629
    { "darkslategray", "#2f4f4f" },
630
    { "darkslategrey", "#2f4f4f" },
631
    { "darkturquoise", "#00ced1" },
632
    { "darkviolet", "#9400d3" },
633
    { "deeppink", "#ff1493" },
634
    { "deepskyblue", "#00bfff" },
635
    { "dimgray", "#696969" },
636
    { "dimgrey", "#696969" },
637
    { "dodgerblue", "#1e90ff" },
638
    { "firebrick", "#b22222" },
639
    { "floralwhite", "#fffaf0" },
640
    { "forestgreen", "#228b22" },
641
    { "gainsboro", "#dcdcdc" },
642
    { "ghostwhite", "#f8f8ff" },
643
    { "gold", "#ffd700" },
644
    { "goldenrod", "#daa520" },
645
    { "greenyellow", "#adff2f" },
646
    { "grey", "#808080" },
647
    { "honeydew", "#f0fff0" },
648
    { "hotpink", "#ff69b4" },
649
    { "indianred", "#cd5c5c" },
650
    { "indigo", "#4b0082" },
651
    { "ivory", "#fffff0" },
652
    { "khaki", "#f0e68c" },
653
    { "lavender", "#e6e6fa" },
654
    { "lavenderblush", "#fff0f5" },
655
    { "lawngreen", "#7cfc00" },
656
    { "lemonchiffon", "#fffacd" },
657
    { "lightblue", "#add8e6" },
658
    { "lightcoral", "#f08080" },
659
    { "lightcyan", "#e0ffff" },
660
    { "lightgoldenrodyellow", "#fafad2" },
661
    { "lightgray", "#d3d3d3" },
662
    { "lightgreen", "#90ee90" },
663
    { "lightgrey", "#d3d3d3" },
664
    { "lightpink", "#ffb6c1" },
665
    { "lightsalmon", "#ffa07a" },
666
    { "lightseagreen", "#20b2aa" },
667
    { "lightskyblue", "#87cefa" },
668
    { "lightslategray", "#778899" },
669
    { "lightslategrey", "#778899" },
670
    { "lightsteelblue", "#b0c4de" },
671
    { "lightyellow", "#ffffe0" },
672
    { "limegreen", "#32cd32" },
673
    { "linen", "#faf0e6" },
674
    { "magenta", "#ff00ff" },
675
    { "mediumaquamarine", "#66cdaa" },
676
    { "mediumblue", "#0000cd" },
677
    { "mediumorchid", "#ba55d3" },
678
    { "mediumpurple", "#9370db" },
679
    { "mediumseagreen", "#3cb371" },
680
    { "mediumslateblue", "#7b68ee" },
681
    { "mediumspringgreen", "#00fa9a" },
682
    { "mediumturquoise", "#48d1cc" },
683
    { "mediumvioletred", "#c71585" },
684
    { "midnightblue", "#191970" },
685
    { "mintcream", "#f5fffa" },
686
    { "mistyrose", "#ffe4e1" },
687
    { "moccasin", "#ffe4b5" },
688
    { "navajowhite", "#ffdead" },
689
    { "oldlace", "#fdf5e6" },
690
    { "olivedrab", "#6b8e23" },
691
    { "orange", "#ffa500" },
692
    { "orangered", "#ff4500" },
693
    { "orchid", "#da70d6" },
694
    { "palegoldenrod", "#eee8aa" },
695
    { "palegreen", "#98fb98" },
696
    { "paleturquoise", "#afeeee" },
697
    { "palevioletred", "#db7093" },
698
    { "papayawhip", "#ffefd5" },
699
    { "peachpuff", "#ffdab9" },
700
    { "peru", "#cd853f" },
701
    { "pink", "#ffc0cb" },
702
    { "plum", "#dda0dd" },
703
    { "powderblue", "#b0e0e6" },
704
    { "rebeccapurple", "#663399" },
705
    { "rosybrown", "#bc8f8f" },
706
    { "royalblue", "#4169e1" },
707
    { "saddlebrown", "#8b4513" },
708
    { "salmon", "#fa8072" },
709
    { "sandybrown", "#f4a460" },
710
    { "seagreen", "#2e8b57" },
711
    { "seashell", "#fff5ee" },
712
    { "sienna", "#a0522d" },
713
    { "skyblue", "#87ceeb" },
714
    { "slateblue", "#6a5acd" },
715
    { "slategray", "#708090" },
716
    { "slategrey", "#708090" },
717
    { "snow", "#fffafa" },
718
    { "springgreen", "#00ff7f" },
719
    { "steelblue", "#4682b4" },
720
    { "tan", "#d2b48c" },
721
    { "thistle", "#d8bfd8" },
722
    { "tomato", "#ff6347" },
723
    { "turquoise", "#40e0d0" },
724
    { "violet", "#ee82ee" },
725
    { "wheat", "#f5deb3" },
726
    { "whitesmoke", "#f5f5f5" },
727
    { "yellowgreen", "#9acd32" },
728
    { NULL, NULL }
729
};
730
731
static ctmbstr GetColorCode(ctmbstr name, Bool use_css_colors)
732
10.8k
{
733
10.8k
    uint i;
734
735
176k
    for (i = 0; colors[i].name; ++i)
736
166k
        if (TY_(tmbstrcasecmp)(name, colors[i].name) == 0)
737
962
            return colors[i].hex;
738
739
9.88k
    if (use_css_colors)
740
996k
        for (i = 0; extended_colors[i].name; ++i)
741
989k
            if (TY_(tmbstrcasecmp)(name, extended_colors[i].name) == 0)
742
406
                return extended_colors[i].hex;
743
744
9.48k
    return NULL;
745
9.88k
}
746
747
static ctmbstr GetColorName(ctmbstr code, Bool use_css_colors)
748
609
{
749
609
    uint i;
750
751
8.69k
    for (i = 0; colors[i].name; ++i)
752
8.41k
        if (TY_(tmbstrcasecmp)(code, colors[i].hex) == 0)
753
331
            return colors[i].name;
754
755
278
    if (use_css_colors)
756
32.4k
        for (i = 0; extended_colors[i].name; ++i)
757
32.2k
            if (TY_(tmbstrcasecmp)(code, extended_colors[i].hex) == 0)
758
0
                return extended_colors[i].name;
759
760
278
    return NULL;
761
278
}
762
763
static uint attrsHash(ctmbstr s)
764
5.60M
{
765
5.60M
    uint hashval;
766
767
40.8M
    for (hashval = 0; *s != '\0'; s++)
768
35.2M
        hashval = *s + 31*hashval;
769
770
5.60M
    return hashval % ATTRIBUTE_HASH_SIZE;
771
5.60M
}
772
773
static const Attribute *attrsInstall(TidyDocImpl* doc, TidyAttribImpl * attribs,
774
                                const Attribute* old)
775
66.1k
{
776
66.1k
    AttrHash *np;
777
66.1k
    uint hashval;
778
779
66.1k
    if (old)
780
66.1k
    {
781
66.1k
        np = (AttrHash *)TidyDocAlloc(doc, sizeof(*np));
782
66.1k
        np->attr = old;
783
784
66.1k
        hashval = attrsHash(old->name);
785
66.1k
        np->next = attribs->hashtab[hashval];
786
66.1k
        attribs->hashtab[hashval] = np;
787
66.1k
    }
788
789
66.1k
    return old;
790
66.1k
}
791
792
static void attrsRemoveFromHash( TidyDocImpl* doc, TidyAttribImpl *attribs,
793
                            ctmbstr s )
794
0
{
795
0
    uint h = attrsHash(s);
796
0
    AttrHash *p, *prev = NULL;
797
0
    for (p = attribs->hashtab[h]; p && p->attr; p = p->next)
798
0
    {
799
0
        if (TY_(tmbstrcmp)(s, p->attr->name) == 0)
800
0
        {
801
0
            AttrHash* next = p->next;
802
0
            if ( prev )
803
0
                prev->next = next; 
804
0
            else
805
0
                attribs->hashtab[h] = next;
806
0
            TidyDocFree(doc, p);
807
0
            return;
808
0
        }
809
0
        prev = p;
810
0
    }
811
0
}
812
813
static void attrsEmptyHash( TidyDocImpl* doc, TidyAttribImpl * attribs )
814
34.6k
{
815
34.6k
    AttrHash *dict, *next;
816
34.6k
    uint i;
817
818
6.20M
    for (i = 0; i < ATTRIBUTE_HASH_SIZE; ++i)
819
6.16M
    {
820
6.16M
        dict = attribs->hashtab[i];
821
822
6.23M
        while(dict)
823
66.1k
        {
824
66.1k
            next = dict->next;
825
66.1k
            TidyDocFree(doc, dict);
826
66.1k
            dict = next;
827
66.1k
        }
828
829
6.16M
        attribs->hashtab[i] = NULL;
830
6.16M
    }
831
34.6k
}
832
833
static const Attribute* attrsLookup(TidyDocImpl* doc,
834
                               TidyAttribImpl* ARG_UNUSED(attribs),
835
                               ctmbstr atnam)
836
5.55M
{
837
5.55M
    const Attribute *np;
838
5.55M
    const AttrHash *p;
839
840
5.55M
    if (!atnam)
841
18.7k
        return NULL;
842
843
5.54M
    for (p = attribs->hashtab[attrsHash(atnam)]; p && p->attr; p = p->next)
844
4.99M
        if (TY_(tmbstrcasecmp)(atnam, p->attr->name) == 0)
845
4.98M
            return p->attr;
846
847
174M
    for (np = attribute_defs; np && np->name; ++np)
848
173M
        if (TY_(tmbstrcasecmp)(atnam, np->name) == 0)
849
66.1k
            return attrsInstall(doc, attribs, np);
850
851
482k
    return NULL;
852
549k
}
853
854
855
/* Locate attributes by type */
856
AttVal* TY_(AttrGetById)( Node* node, TidyAttrId id )
857
8.51M
{
858
8.51M
   AttVal* av;
859
9.74M
   for ( av = node->attributes; av; av = av->next )
860
1.35M
   {
861
1.35M
     if ( AttrIsId(av, id) )
862
128k
         return av;
863
1.35M
   }
864
8.38M
   return NULL;
865
8.51M
}
866
867
/* public method for finding attribute definition by name */
868
const Attribute* TY_(FindAttribute)( TidyDocImpl* doc, AttVal *attval )
869
896k
{
870
896k
    if ( attval )
871
896k
       return attrsLookup( doc, &doc->attribs, attval->attribute );
872
0
    return NULL;
873
896k
}
874
875
AttVal* TY_(GetAttrByName)( Node *node, ctmbstr name )
876
426k
{
877
426k
    AttVal *attr;
878
499k
    for (attr = node->attributes; attr != NULL; attr = attr->next)
879
101k
    {
880
101k
        if (attr->attribute && TY_(tmbstrcmp)(attr->attribute, name) == 0)
881
28.6k
            break;
882
101k
    }
883
426k
    return attr;
884
426k
}
885
886
void TY_(DropAttrByName)( TidyDocImpl* doc, Node *node, ctmbstr name )
887
0
{
888
0
    AttVal *attr, *prev = NULL, *next;
889
890
0
    for (attr = node->attributes; attr != NULL; prev = attr, attr = next)
891
0
    {
892
0
        next = attr->next;
893
894
0
        if (attr->attribute && TY_(tmbstrcmp)(attr->attribute, name) == 0)
895
0
        {
896
0
            if (prev)
897
0
                 prev->next = next;
898
0
            else
899
0
                 node->attributes = next;
900
901
0
            TY_(FreeAttribute)( doc, attr ); 
902
0
            break;
903
0
        }
904
0
    }
905
0
}
906
907
AttVal* TY_(AddAttribute)( TidyDocImpl* doc,
908
                           Node *node, ctmbstr name, ctmbstr value )
909
144k
{
910
144k
    AttVal *av = TY_(NewAttribute)(doc);
911
144k
    av->delim = '"';
912
144k
    av->attribute = TY_(tmbstrdup)(doc->allocator, name);
913
914
144k
    if (value)
915
119k
        av->value = TY_(tmbstrdup)(doc->allocator, value);
916
24.4k
    else
917
24.4k
        av->value = NULL;
918
919
144k
    av->dict = attrsLookup(doc, &doc->attribs, name);
920
921
144k
    TY_(InsertAttributeAtEnd)(node, av);
922
144k
    return av;
923
144k
}
924
925
AttVal* TY_(RepairAttrValue)(TidyDocImpl* doc, Node* node, ctmbstr name, ctmbstr value)
926
57.1k
{
927
57.1k
    AttVal* old = TY_(GetAttrByName)(node, name);
928
929
57.1k
    if (old)
930
36
    {
931
36
        if (old->value)
932
36
            TidyDocFree(doc, old->value);
933
36
        if (value)
934
36
            old->value = TY_(tmbstrdup)(doc->allocator, value);
935
0
        else
936
0
            old->value = NULL;
937
938
36
        return old;
939
36
    }
940
57.1k
    else
941
57.1k
        return TY_(AddAttribute)(doc, node, name, value);
942
57.1k
}
943
944
945
void TY_(FreeAttrPriorityList)( TidyDocImpl* doc )
946
34.6k
{
947
34.6k
    PriorityAttribs *priorities = &(doc->attribs.priorityAttribs);
948
949
34.6k
    if ( priorities->list )
950
0
    {
951
0
        uint i = 0;
952
0
        while ( priorities->list[i] != NULL )
953
0
        {
954
0
            TidyFree( doc->allocator, priorities->list[i] );
955
0
            i++;
956
0
        }
957
958
0
        TidyFree( doc->allocator, priorities->list );
959
0
    }
960
34.6k
}
961
962
963
void TY_(DefinePriorityAttribute)(TidyDocImpl* doc, ctmbstr name)
964
0
{
965
0
    enum { capacity = 10 };
966
0
    PriorityAttribs *priorities = &(doc->attribs.priorityAttribs);
967
968
0
    if ( !priorities->list )
969
0
    {
970
0
        priorities->list = TidyAlloc(doc->allocator, sizeof(ctmbstr) * capacity );
971
0
        priorities->list[0] = NULL;
972
0
        priorities->capacity = capacity;
973
0
        priorities->count = 0;
974
0
    }
975
976
0
    if ( priorities->count >= priorities->capacity )
977
0
    {
978
0
        priorities->capacity = priorities->capacity * 2;
979
0
        priorities->list = TidyRealloc(doc->allocator, priorities->list, sizeof(tmbstr) * priorities->capacity + 1 );
980
0
    }
981
982
0
    priorities->list[priorities->count] = TY_(tmbstrdup)( doc->allocator, name);
983
0
    priorities->count++;
984
0
    priorities->list[priorities->count] = NULL;
985
0
}
986
987
988
TidyIterator TY_(getPriorityAttrList)( TidyDocImpl* doc )
989
0
{
990
0
    PriorityAttribs *priorities = &(doc->attribs.priorityAttribs);
991
0
    size_t result = priorities->count > 0 ? 1 : 0;
992
993
0
    return (TidyIterator) result;
994
0
}
995
996
997
ctmbstr  TY_(getNextPriorityAttr)( TidyDocImpl* doc, TidyIterator* iter )
998
0
{
999
0
    PriorityAttribs *priorities = &(doc->attribs.priorityAttribs);
1000
0
    size_t index;
1001
0
    ctmbstr result = NULL;
1002
0
    assert( iter != NULL );
1003
0
    index = (size_t)*iter;
1004
1005
0
    if ( index > 0 && index <= priorities->count )
1006
0
    {
1007
0
        result = priorities->list[index-1];
1008
0
        index++;
1009
0
    }
1010
0
    *iter = (TidyIterator) ( index <= priorities->count ? index : (size_t)0 );
1011
1012
0
    return result;
1013
0
}
1014
1015
1016
static Bool CheckAttrType( TidyDocImpl* doc,
1017
                           ctmbstr attrname, AttrCheck type )
1018
4.51M
{
1019
4.51M
    const Attribute* np = attrsLookup( doc, &doc->attribs, attrname );
1020
4.51M
    return (Bool)( np && np->attrchk == type );
1021
4.51M
}
1022
1023
Bool TY_(IsUrl)( TidyDocImpl* doc, ctmbstr attrname )
1024
4.50M
{
1025
4.50M
    return CheckAttrType( doc, attrname, CH_URL );
1026
4.50M
}
1027
1028
/*
1029
Bool IsBool( TidyDocImpl* doc, ctmbstr attrname )
1030
{
1031
    return CheckAttrType( doc, attrname, CH_BOOL );
1032
}
1033
*/
1034
1035
Bool TY_(IsScript)( TidyDocImpl* doc, ctmbstr attrname )
1036
7.02k
{
1037
7.02k
    return CheckAttrType( doc, attrname, CH_SCRIPT );
1038
7.02k
}
1039
1040
/* may id or name serve as anchor? */
1041
Bool TY_(IsAnchorElement)( TidyDocImpl* ARG_UNUSED(doc), Node* node)
1042
3.38M
{
1043
3.38M
    TidyTagId tid = TagId( node );
1044
3.38M
    if ( tid == TidyTag_A      ||
1045
3.38M
         tid == TidyTag_APPLET ||
1046
3.38M
         tid == TidyTag_FORM   ||
1047
3.38M
         tid == TidyTag_FRAME  ||
1048
3.38M
         tid == TidyTag_IFRAME ||
1049
3.38M
         tid == TidyTag_IMG    ||
1050
3.38M
         tid == TidyTag_MAP )
1051
306k
        return yes;
1052
1053
3.07M
    return no;
1054
3.38M
}
1055
1056
/*
1057
  In CSS1, selectors can contain only the characters A-Z, 0-9,
1058
  and Unicode characters 161-255, plus dash (-); they cannot start
1059
  with a dash or a digit; they can also contain escaped characters
1060
  and any Unicode character as a numeric code (see next item).
1061
1062
  The backslash followed by at most four hexadecimal digits
1063
  (0..9A..F) stands for the Unicode character with that number.
1064
1065
  Any character except a hexadecimal digit can be escaped to remove
1066
  its special meaning, by putting a backslash in front.
1067
1068
  #508936 - CSS class naming for -clean option
1069
*/
1070
Bool TY_(IsCSS1Selector)( ctmbstr buf )
1071
0
{
1072
0
    Bool valid = yes;
1073
0
    int esclen = 0;
1074
0
    byte c;
1075
0
    int pos;
1076
1077
0
    for ( pos=0; valid && (c = *buf++); ++pos )
1078
0
    {
1079
0
        if ( c == '\\' )
1080
0
        {
1081
0
            esclen = 1;  /* ab\555\444 is 4 chars {'a', 'b', \555, \444} */
1082
0
        }
1083
0
        else if ( isdigit( c ) )
1084
0
        {
1085
            /* Digit not 1st, unless escaped (Max length "\112F") */
1086
0
            if ( esclen > 0 )
1087
0
                valid = ( ++esclen < 6 );
1088
0
            if ( valid )
1089
0
                valid = ( pos>0 || esclen>0 );
1090
0
        }
1091
0
        else
1092
0
        {
1093
0
            valid = (
1094
0
                esclen > 0                       /* Escaped? Anything goes. */
1095
0
                || ( pos>0 && c == '-' )         /* Dash cannot be 1st char */
1096
                || isalpha(c)                    /* a-z, A-Z anywhere */
1097
0
                || ( c >= 161 )                  /* Unicode 161-255 anywhere */
1098
0
            );
1099
0
            esclen = 0;
1100
0
        }
1101
0
    }
1102
0
    return valid;
1103
0
}
1104
1105
/* free single anchor */
1106
static void FreeAnchor(TidyDocImpl* doc, Anchor *a)
1107
129k
{
1108
129k
    if ( a )
1109
129k
        TidyDocFree( doc, a->name );
1110
129k
    TidyDocFree( doc, a );
1111
129k
}
1112
1113
static uint anchorNameHash(ctmbstr s)
1114
265k
{
1115
265k
    uint hashval = 0;
1116
    /* Issue #149 - an inferred name can be null. avoid crash */
1117
265k
    if (s) 
1118
261k
    {
1119
7.84M
        for ( ; *s != '\0'; s++) {
1120
7.58M
            tmbchar c = TY_(ToLower)( *s );
1121
7.58M
            hashval = c + 31*hashval;
1122
7.58M
        }
1123
261k
    }
1124
265k
    return hashval % ANCHOR_HASH_SIZE;
1125
265k
}
1126
1127
/*\
1128
 *  New Service for HTML5
1129
 *  Issue #185 - Treat elements ids as case-sensitive
1130
 *  if in HTML5 modes, make hash of value AS IS!
1131
\*/
1132
static uint anchorNameHash5(ctmbstr s)
1133
578
{
1134
578
    uint hashval = 0;
1135
    /* Issue #149 - an inferred name can be null. avoid crash */
1136
578
    if (s) 
1137
574
    {
1138
3.72k
        for ( ; *s != '\0'; s++) {
1139
3.14k
            tmbchar c = *s;
1140
3.14k
            hashval = c + 31*hashval;
1141
3.14k
        }
1142
574
    }
1143
578
    return hashval % ANCHOR_HASH_SIZE;
1144
578
}
1145
1146
1147
/*\ 
1148
 *  removes anchor for specific node 
1149
 *  Issue #185 - Treat elements ids as case-sensitive
1150
 *  if in HTML5 modes, make hash of value AS IS!
1151
\*/
1152
void TY_(RemoveAnchorByNode)( TidyDocImpl* doc, ctmbstr name, Node *node )
1153
101k
{
1154
101k
    TidyAttribImpl* attribs = &doc->attribs;
1155
101k
    Anchor *delme = NULL, *curr, *prev = NULL;
1156
101k
    uint h;
1157
101k
    if (TY_(HTMLVersion)(doc) == HT50)
1158
78
        h = anchorNameHash5(name);
1159
101k
    else
1160
101k
        h = anchorNameHash(name);
1161
1162
353k
    for ( curr=attribs->anchor_hash[h]; curr!=NULL; curr=curr->next )
1163
258k
    {
1164
258k
        if ( curr->node == node )
1165
6.73k
        {
1166
6.73k
            if ( prev )
1167
1.17k
                prev->next = curr->next;
1168
5.55k
            else
1169
5.55k
                attribs->anchor_hash[h] = curr->next;
1170
6.73k
            delme = curr;
1171
6.73k
            break;
1172
6.73k
        }
1173
251k
        prev = curr;
1174
251k
    }
1175
101k
    FreeAnchor( doc, delme );
1176
101k
}
1177
1178
/* initialize new anchor 
1179
   Is. #726 & #185 - HTML5 is case-sensitive
1180
*/
1181
static Anchor* NewAnchor( TidyDocImpl* doc, ctmbstr name, Node* node )
1182
34.6k
{
1183
34.6k
    Anchor *a = (Anchor*) TidyDocAlloc( doc, sizeof(Anchor) );
1184
1185
34.6k
    a->name = TY_(tmbstrdup)( doc->allocator, name );
1186
34.6k
    if (!TY_(IsHTML5Mode)(doc)) /* Is. #726 - if NOT HTML5, to lowercase */
1187
2.48k
        a->name = TY_(tmbstrtolower)(a->name);
1188
34.6k
    a->node = node;
1189
34.6k
    a->next = NULL;
1190
1191
34.6k
    return a;
1192
34.6k
}
1193
1194
/*\
1195
 *  add new anchor to namespace 
1196
 *  Issue #185 - Treat elements ids as case-sensitive
1197
 *  if in HTML5 modes, make hash of value AS IS!
1198
\*/
1199
static Anchor* AddAnchor( TidyDocImpl* doc, ctmbstr name, Node *node )
1200
34.6k
{
1201
34.6k
    TidyAttribImpl* attribs = &doc->attribs;
1202
34.6k
    Anchor *a = NewAnchor( doc, name, node );
1203
34.6k
    uint h;
1204
34.6k
    if (TY_(HTMLVersion)(doc) == HT50)
1205
99
        h = anchorNameHash5(name);
1206
34.5k
    else
1207
34.5k
        h = anchorNameHash(name);
1208
1209
34.6k
    if ( attribs->anchor_hash[h] == NULL)
1210
5.60k
         attribs->anchor_hash[h] = a;
1211
29.0k
    else
1212
29.0k
    {
1213
29.0k
        Anchor *here =  attribs->anchor_hash[h];
1214
1.47M
        while (here->next)
1215
1.44M
            here = here->next;
1216
29.0k
        here->next = a;
1217
29.0k
    }
1218
1219
34.6k
    return attribs->anchor_hash[h];
1220
34.6k
}
1221
1222
/*\
1223
 *  return node associated with anchor 
1224
 *  Issue #185 - Treat elements ids as case-sensitive
1225
 *  if in HTML5 modes, make hash of value AS IS!
1226
\*/
1227
static Node* GetNodeByAnchor( TidyDocImpl* doc, ctmbstr name )
1228
129k
{
1229
129k
    TidyAttribImpl* attribs = &doc->attribs;
1230
129k
    Anchor *found;
1231
129k
    uint h;
1232
129k
    tmbstr lname = TY_(tmbstrdup)(doc->allocator, name);
1233
129k
    if (TY_(HTMLVersion)(doc) == HT50) {
1234
401
        h = anchorNameHash5(name);
1235
401
    }
1236
128k
    else
1237
128k
    {
1238
128k
        h = anchorNameHash(name);
1239
128k
        lname = TY_(tmbstrtolower)(lname);
1240
128k
    }
1241
1242
1.60M
    for ( found = attribs->anchor_hash[h]; found != NULL; found = found->next )
1243
1.57M
    {
1244
1.57M
        if ( TY_(tmbstrcmp)(found->name, lname) == 0 )
1245
94.7k
            break;
1246
1.57M
    }
1247
    
1248
129k
    TidyDocFree(doc, lname);
1249
129k
    if ( found )
1250
94.7k
        return found->node;
1251
34.6k
    return NULL;
1252
129k
}
1253
1254
/* free all anchors */
1255
void TY_(FreeAnchors)( TidyDocImpl* doc )
1256
69.3k
{
1257
69.3k
    TidyAttribImpl* attribs = &doc->attribs;
1258
69.3k
    Anchor* a;
1259
69.3k
    uint h;
1260
70.8M
    for (h = 0; h < ANCHOR_HASH_SIZE; h++) {
1261
70.8M
        while (NULL != (a = attribs->anchor_hash[h]) )
1262
27.9k
        {
1263
27.9k
            attribs->anchor_hash[h] = a->next;
1264
27.9k
            FreeAnchor(doc, a);
1265
27.9k
        }
1266
70.7M
    }
1267
69.3k
}
1268
1269
/* public method for inititializing attribute dictionary */
1270
void TY_(InitAttrs)( TidyDocImpl* doc )
1271
34.6k
{
1272
34.6k
    TidyClearMemory( &doc->attribs, sizeof(TidyAttribImpl) );
1273
#ifdef _DEBUG
1274
    {
1275
      /* Attribute ID is index position in Attribute type lookup table */
1276
      uint ix;
1277
      for ( ix=0; ix < N_TIDY_ATTRIBS; ++ix )
1278
      {
1279
        const Attribute* dict = &attribute_defs[ ix ];
1280
        assert( (uint) dict->id == ix );
1281
      }
1282
    }
1283
#endif
1284
34.6k
}
1285
1286
/* free all declared attributes */
1287
static void FreeDeclaredAttributes( TidyDocImpl* doc )
1288
34.6k
{
1289
34.6k
    TidyAttribImpl* attribs = &doc->attribs;
1290
34.6k
    Attribute* dict;
1291
34.6k
    while ( NULL != (dict = attribs->declared_attr_list) )
1292
0
    {
1293
0
        attribs->declared_attr_list = dict->next;
1294
0
        attrsRemoveFromHash( doc, &doc->attribs, dict->name );
1295
0
        TidyDocFree( doc, dict->name );
1296
0
        TidyDocFree( doc, dict );
1297
0
    }
1298
34.6k
}
1299
1300
void TY_(FreeAttrTable)( TidyDocImpl* doc )
1301
34.6k
{
1302
34.6k
    attrsEmptyHash( doc, &doc->attribs );
1303
34.6k
    TY_(FreeAnchors)( doc );
1304
34.6k
    FreeDeclaredAttributes( doc );
1305
34.6k
}
1306
1307
void TY_(AppendToClassAttr)( TidyDocImpl* doc, AttVal *classattr, ctmbstr classname )
1308
32
{
1309
32
    uint len = TY_(tmbstrlen)(classattr->value) +
1310
32
        TY_(tmbstrlen)(classname) + 2;
1311
32
    tmbstr s = (tmbstr) TidyDocAlloc( doc, len );
1312
32
    s[0] = '\0';
1313
32
    if (classattr->value)
1314
32
    {
1315
32
        TY_(tmbstrcpy)( s, classattr->value );
1316
32
        TY_(tmbstrcat)( s, " " );
1317
32
    }
1318
32
    TY_(tmbstrcat)( s, classname );
1319
32
    if (classattr->value)
1320
32
        TidyDocFree( doc, classattr->value );
1321
32
    classattr->value = s;
1322
32
}
1323
1324
/* concatenate styles */
1325
static void AppendToStyleAttr( TidyDocImpl* doc, AttVal *styleattr, ctmbstr styleprop )
1326
3.81k
{
1327
    /*
1328
    this doesn't handle CSS comments and
1329
    leading/trailing white-space very well
1330
    see https://www.w3.org/TR/css-style-attr/
1331
    */
1332
3.81k
    uint end = TY_(tmbstrlen)(styleattr->value);
1333
1334
3.81k
    if (end >0 && styleattr->value[end - 1] == ';')
1335
2.18k
    {
1336
        /* attribute ends with declaration separator */
1337
1338
2.18k
        styleattr->value = (tmbstr) TidyDocRealloc(doc, styleattr->value,
1339
2.18k
            end + TY_(tmbstrlen)(styleprop) + 2);
1340
1341
2.18k
        TY_(tmbstrcat)(styleattr->value, " ");
1342
2.18k
        TY_(tmbstrcat)(styleattr->value, styleprop);
1343
2.18k
    }
1344
1.63k
    else if (end >0 && styleattr->value[end - 1] == '}')
1345
418
    {
1346
        /* attribute ends with rule set */
1347
1348
418
        styleattr->value = (tmbstr) TidyDocRealloc(doc, styleattr->value,
1349
418
            end + TY_(tmbstrlen)(styleprop) + 6);
1350
1351
418
        TY_(tmbstrcat)(styleattr->value, " { ");
1352
418
        TY_(tmbstrcat)(styleattr->value, styleprop);
1353
418
        TY_(tmbstrcat)(styleattr->value, " }");
1354
418
    }
1355
1.21k
    else
1356
1.21k
    {
1357
        /* attribute ends with property value */
1358
1359
1.21k
        styleattr->value = (tmbstr) TidyDocRealloc(doc, styleattr->value,
1360
1.21k
            end + TY_(tmbstrlen)(styleprop) + 3);
1361
1362
1.21k
        if (end > 0)
1363
681
            TY_(tmbstrcat)(styleattr->value, "; ");
1364
1.21k
        TY_(tmbstrcat)(styleattr->value, styleprop);
1365
1.21k
    }
1366
3.81k
}
1367
1368
/*
1369
 the same attribute name can't be used
1370
 more than once in each element
1371
*/
1372
static Bool AttrsHaveSameName( AttVal* av1, AttVal* av2)
1373
26.1M
{
1374
26.1M
    TidyAttrId id1, id2;
1375
1376
26.1M
    id1 = AttrId(av1);
1377
26.1M
    id2 = AttrId(av2);
1378
26.1M
    if (id1 != TidyAttr_UNKNOWN && id2 != TidyAttr_UNKNOWN)
1379
31.8k
        return AttrsHaveSameId(av1, av2);
1380
26.1M
    if (id1 != TidyAttr_UNKNOWN || id2 != TidyAttr_UNKNOWN)
1381
104k
        return no;
1382
26.0M
    if (av1->attribute && av2->attribute)
1383
26.0M
        return TY_(tmbstrcmp)(av1->attribute, av2->attribute) == 0;
1384
984
     return no;
1385
26.0M
}
1386
1387
void TY_(RepairDuplicateAttributes)( TidyDocImpl* doc, Node *node, Bool isXml )
1388
2.56M
{
1389
2.56M
    AttVal *first;
1390
1391
2.87M
    for (first = node->attributes; first != NULL;)
1392
306k
    {
1393
306k
        AttVal *second;
1394
306k
        Bool firstRedefined = no;
1395
1396
306k
        if (!(first->asp == NULL && first->php == NULL))
1397
11.5k
        {
1398
11.5k
            first = first->next;
1399
11.5k
            continue;
1400
11.5k
        }
1401
1402
26.4M
        for (second = first->next; second != NULL;)
1403
26.1M
        {
1404
26.1M
            AttVal *temp;
1405
1406
26.1M
            if (!(second->asp == NULL && second->php == NULL
1407
26.1M
                  && AttrsHaveSameName(first, second)))
1408
26.1M
            {
1409
26.1M
                second = second->next;
1410
26.1M
                continue;
1411
26.1M
            }
1412
1413
            /* first and second attribute have same local name */
1414
            /* now determine what to do with this duplicate... */
1415
1416
22.1k
            if (!isXml
1417
22.1k
                && attrIsCLASS(first) && cfgBool(doc, TidyJoinClasses)
1418
22.1k
                && AttrHasValue(first) && AttrHasValue(second))
1419
32
            {
1420
                /* concatenate classes */
1421
1422
32
                TY_(AppendToClassAttr)(doc, first, second->value);
1423
1424
32
                temp = second->next;
1425
32
                TY_(ReportAttrError)( doc, node, second, JOINING_ATTRIBUTE);
1426
32
                TY_(RemoveAttribute)( doc, node, second );
1427
32
                second = temp;
1428
32
            }
1429
22.1k
            else if (!isXml
1430
22.1k
                     && attrIsSTYLE(first) && cfgBool(doc, TidyJoinStyles)
1431
22.1k
                     && AttrHasValue(first) && AttrHasValue(second))
1432
3.81k
            {
1433
3.81k
                AppendToStyleAttr( doc, first, second->value );
1434
1435
3.81k
                temp = second->next;
1436
3.81k
                TY_(ReportAttrError)( doc, node, second, JOINING_ATTRIBUTE);
1437
3.81k
                TY_(RemoveAttribute)( doc, node, second );
1438
3.81k
                second = temp;
1439
3.81k
            }
1440
18.3k
            else if ( cfg(doc, TidyDuplicateAttrs) == TidyKeepLast )
1441
18.3k
            {
1442
18.3k
                temp = first->next;
1443
18.3k
                TY_(ReportAttrError)( doc, node, first, REPEATED_ATTRIBUTE);
1444
18.3k
                TY_(RemoveAttribute)( doc, node, first );
1445
18.3k
                firstRedefined = yes;
1446
18.3k
                first = temp;
1447
18.3k
                second = second->next;
1448
18.3k
            }
1449
0
            else /* TidyDuplicateAttrs == TidyKeepFirst */
1450
0
            {
1451
0
                temp = second->next;
1452
0
                TY_(ReportAttrError)( doc, node, second, REPEATED_ATTRIBUTE);
1453
0
                TY_(RemoveAttribute)( doc, node, second );
1454
0
                second = temp;
1455
0
            }
1456
22.1k
        }
1457
295k
        if (!firstRedefined)
1458
288k
            first = first->next;
1459
295k
    }
1460
2.56M
}
1461
1462
/* ignore unknown attributes for proprietary elements */
1463
const Attribute* TY_(CheckAttribute)( TidyDocImpl* doc, Node *node, AttVal *attval )
1464
696k
{
1465
696k
    const Attribute* attribute = attval->dict;
1466
1467
696k
    if ( attribute != NULL )
1468
444k
    {
1469
444k
        if (attrIsXML_LANG(attval) || attrIsXML_SPACE(attval))
1470
47.2k
        {
1471
47.2k
            doc->lexer->isvoyager = yes;
1472
47.2k
            if (!cfgBool(doc, TidyHtmlOut))
1473
47.2k
            {
1474
47.2k
                TY_(SetOptionBool)(doc, TidyXhtmlOut, yes);
1475
47.2k
                TY_(SetOptionBool)(doc, TidyXmlOut, yes);
1476
47.2k
            }
1477
47.2k
        }
1478
1479
444k
        TY_(ConstrainVersion)(doc, AttributeVersions(node, attval));
1480
        
1481
444k
        if (attribute->attrchk)
1482
366k
            attribute->attrchk( doc, node, attval );
1483
444k
    }
1484
1485
696k
    return attribute;
1486
696k
}
1487
1488
Bool TY_(IsBoolAttribute)(AttVal *attval)
1489
132k
{
1490
132k
    const Attribute *attribute = ( attval ? attval->dict : NULL );
1491
132k
    if ( attribute && attribute->attrchk == CH_BOOL )
1492
753
        return yes;
1493
132k
    return no;
1494
132k
}
1495
1496
Bool TY_(attrIsEvent)( AttVal* attval )
1497
132k
{
1498
132k
    TidyAttrId atid = AttrId( attval );
1499
1500
132k
    return (atid == TidyAttr_OnAFTERUPDATE     ||
1501
132k
            atid == TidyAttr_OnBEFOREUNLOAD    ||
1502
132k
            atid == TidyAttr_OnBEFOREUPDATE    ||
1503
132k
            atid == TidyAttr_OnBLUR            ||
1504
132k
            atid == TidyAttr_OnCHANGE          ||
1505
132k
            atid == TidyAttr_OnCLICK           ||
1506
132k
            atid == TidyAttr_OnDATAAVAILABLE   ||
1507
132k
            atid == TidyAttr_OnDATASETCHANGED  ||
1508
132k
            atid == TidyAttr_OnDATASETCOMPLETE ||
1509
132k
            atid == TidyAttr_OnDBLCLICK        ||
1510
132k
            atid == TidyAttr_OnERRORUPDATE     ||
1511
132k
            atid == TidyAttr_OnFOCUS           ||
1512
132k
            atid == TidyAttr_OnKEYDOWN         ||
1513
132k
            atid == TidyAttr_OnKEYPRESS        ||
1514
132k
            atid == TidyAttr_OnKEYUP           ||
1515
132k
            atid == TidyAttr_OnLOAD            ||
1516
132k
            atid == TidyAttr_OnMOUSEDOWN       ||
1517
132k
            atid == TidyAttr_OnMOUSEMOVE       ||
1518
132k
            atid == TidyAttr_OnMOUSEOUT        ||
1519
132k
            atid == TidyAttr_OnMOUSEOVER       ||
1520
132k
            atid == TidyAttr_OnMOUSEUP         ||
1521
132k
            atid == TidyAttr_OnRESET           ||
1522
132k
            atid == TidyAttr_OnROWENTER        ||
1523
132k
            atid == TidyAttr_OnROWEXIT         ||
1524
132k
            atid == TidyAttr_OnSELECT          ||
1525
132k
            atid == TidyAttr_OnSUBMIT          ||
1526
132k
            atid == TidyAttr_OnUNLOAD);
1527
132k
}
1528
1529
static void CheckLowerCaseAttrValue( TidyDocImpl* doc, Node *node, AttVal *attval)
1530
34.6k
{
1531
34.6k
    tmbstr p;
1532
34.6k
    Bool hasUpper = no;
1533
    
1534
34.6k
    if (!AttrHasValue(attval))
1535
0
        return;
1536
1537
34.6k
    p = attval->value;
1538
    
1539
133k
    while (*p)
1540
121k
    {
1541
121k
        if (TY_(IsUpper)(*p)) /* #501230 - fix by Terry Teague - 09 Jan 02 */
1542
22.6k
        {
1543
22.6k
            hasUpper = yes;
1544
22.6k
            break;
1545
22.6k
        }
1546
98.8k
        p++;
1547
98.8k
    }
1548
1549
34.6k
    if (hasUpper)
1550
22.6k
    {
1551
22.6k
        Lexer* lexer = doc->lexer;
1552
22.6k
        if (lexer->isvoyager)
1553
4.45k
            TY_(ReportAttrError)( doc, node, attval, ATTR_VALUE_NOT_LCASE);
1554
  
1555
22.6k
        if ( lexer->isvoyager || cfgBool(doc, TidyLowerLiterals) )
1556
20.1k
            attval->value = TY_(tmbstrtolower)(attval->value);
1557
22.6k
    }
1558
34.6k
}
1559
1560
/* Issue #588 - use simple macros only!
1561
   Seems 'isalnum(c)' is undefined and can
1562
   cause an assert or a SIGSEGV in some libraries
1563
   if 'c' is not EOF, or in the range 0 to 0xff,
1564
   so avoid using it.
1565
*/
1566
100M
#define ISUPPER(a) ((a >= 'A') && (a <= 'Z'))
1567
100M
#define ISLOWER(a) ((a >= 'a') && (a <= 'z'))
1568
49.8M
#define ISNUMERIC(a) ((a >= '0') && (a <= '9'))
1569
100M
#define ISALNUM(a) (ISUPPER(a) || ISLOWER(a) || ISNUMERIC(a))
1570
1571
static Bool IsURLCodePoint( ctmbstr p, uint *increment )
1572
50.3M
{
1573
50.3M
    uint c;
1574
50.3M
    *increment = TY_(GetUTF8)( p, &c ) + 1;
1575
1576
50.3M
    return ISALNUM( c ) ||
1577
50.3M
        c == '%' ||    /* not a valid codepoint, but an escape sequence */
1578
50.3M
        c == '#' ||    /* not a valid codepoint, but a delimiter */
1579
50.3M
        c == '!' ||
1580
50.3M
        c == '$' ||
1581
50.3M
        c == '&' ||
1582
50.3M
        c == '\'' ||
1583
50.3M
        c == '(' ||
1584
50.3M
        c == ')' ||
1585
50.3M
        c == '*' ||
1586
50.3M
        c == '+' ||
1587
50.3M
        c == ',' ||
1588
50.3M
        c == '-' ||
1589
50.3M
        c == '.' ||
1590
50.3M
        c == '/' ||
1591
50.3M
        c == ':' ||
1592
50.3M
        c == ';' ||
1593
50.3M
        c == '=' ||
1594
50.3M
        c == '?' ||
1595
50.3M
        c == '@' ||
1596
50.3M
        c == '_' ||
1597
50.3M
        c == '~' ||
1598
50.3M
        (c >= 0x00A0 && c <= 0xD7FF) ||
1599
50.3M
        (c >= 0xE000 && c <= 0xFDCF) ||
1600
50.3M
        (c >= 0xFDF0 && c <= 0xFFEF) ||
1601
50.3M
        (c >= 0x10000 && c <= 0x1FFFD) ||
1602
50.3M
        (c >= 0x20000 && c <= 0x2FFFD) ||
1603
50.3M
        (c >= 0x30000 && c <= 0x3FFFD) ||
1604
50.3M
        (c >= 0x40000 && c <= 0x4FFFD) ||
1605
50.3M
        (c >= 0x50000 && c <= 0x5FFFD) ||
1606
50.3M
        (c >= 0x60000 && c <= 0x6FFFD) ||
1607
50.3M
        (c >= 0x70000 && c <= 0x7FFFD) ||
1608
50.3M
        (c >= 0x80000 && c <= 0x8FFFD) ||
1609
50.3M
        (c >= 0x90000 && c <= 0x9FFFD) ||
1610
50.3M
        (c >= 0xA0000 && c <= 0xAFFFD) ||
1611
50.3M
        (c >= 0xB0000 && c <= 0xBFFFD) ||
1612
50.3M
        (c >= 0xC0000 && c <= 0xCFFFD) ||
1613
50.3M
        (c >= 0xD0000 && c <= 0xDFFFD) ||
1614
50.3M
        (c >= 0xE0000 && c <= 0xEFFFD) ||
1615
50.3M
        (c >= 0xF0000 && c <= 0xFFFFD) ||
1616
50.3M
        (c >= 0x100000 && c <= 0x10FFFD);
1617
50.3M
}
1618
1619
void TY_(CheckUrl)( TidyDocImpl* doc, Node *node, AttVal *attval)
1620
33.4k
{
1621
33.4k
    tmbchar c;
1622
33.4k
    tmbstr dest, p;
1623
33.4k
    uint escape_count = 0, backslash_count = 0, bad_codepoint_count = 0;
1624
33.4k
    uint i, pos = 0;
1625
33.4k
    uint len;
1626
33.4k
    uint increment;
1627
33.4k
    Bool isJavascript = no;
1628
1629
33.4k
    if (!AttrHasValue(attval))
1630
6.90k
    {
1631
6.90k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
1632
6.90k
        return;
1633
6.90k
    }
1634
1635
26.5k
    p = attval->value;
1636
1637
26.5k
    isJavascript =
1638
26.5k
        TY_(tmbstrncmp)(p,"javascript:",sizeof("javascript:")-1)==0;
1639
1640
52.2M
    for (i = 0; '\0' != (c = p[i]); ++i)
1641
52.1M
    {
1642
52.1M
        if (c == '\\')
1643
28.9k
        {
1644
28.9k
            ++backslash_count;
1645
28.9k
            if ( cfgBool(doc, TidyFixBackslash) && !isJavascript)
1646
20.2k
                p[i] = '/';
1647
28.9k
        }
1648
52.1M
        else if ((c > 0x7e) || (c <= 0x20) || (strchr("<>", c)))
1649
51.3M
            ++escape_count;
1650
52.1M
    }
1651
1652
50.4M
    while ( *p != 0 )
1653
50.3M
    {
1654
50.3M
        if ( !IsURLCodePoint( p, &increment ) )
1655
49.5M
            ++bad_codepoint_count;
1656
50.3M
         p = p + increment;
1657
50.3M
    }
1658
26.5k
    p = attval->value;
1659
1660
26.5k
    if ( cfgBool(doc, TidyFixUri) && escape_count )
1661
22.7k
    {
1662
22.7k
        Bool hadnonspace = no;
1663
22.7k
        len = TY_(tmbstrlen)(p) + escape_count * 2 + 1;
1664
22.7k
        dest = (tmbstr) TidyDocAlloc(doc, len);
1665
 
1666
52.1M
        for (i = 0; 0 != (c = p[i]); ++i)
1667
52.1M
        {
1668
52.1M
            if ((c > 0x7e) || (c <= 0x20) || (strchr("<>", c)))
1669
51.3M
            {
1670
51.3M
                if (c == 0x20)
1671
48.3M
                {
1672
                    /* #345 - special case for leading spaces - discard */
1673
48.3M
                    if (hadnonspace)
1674
48.3M
                        pos += sprintf( dest + pos, "%%%02X", (byte)c );
1675
48.3M
                }
1676
2.97M
                else
1677
2.97M
                {
1678
2.97M
                    pos += sprintf( dest + pos, "%%%02X", (byte)c );
1679
2.97M
                    hadnonspace = yes;
1680
2.97M
                }
1681
51.3M
            }
1682
819k
            else
1683
819k
            {
1684
819k
                hadnonspace = yes;
1685
819k
                dest[pos++] = c;
1686
819k
            }
1687
52.1M
        }
1688
22.7k
        dest[pos] = 0;
1689
1690
22.7k
        TidyDocFree(doc, attval->value);
1691
22.7k
        attval->value = dest;
1692
22.7k
    }
1693
26.5k
    if ( backslash_count )
1694
5.25k
    {
1695
5.25k
        if ( cfgBool(doc, TidyFixBackslash) && !isJavascript )
1696
2.30k
            TY_(ReportAttrError)( doc, node, attval, FIXED_BACKSLASH );
1697
2.95k
        else
1698
2.95k
            TY_(ReportAttrError)( doc, node, attval, BACKSLASH_IN_URI );
1699
5.25k
    }
1700
26.5k
    if ( escape_count )
1701
22.8k
    {
1702
22.8k
        if ( cfgBool(doc, TidyFixUri) )
1703
22.7k
            TY_(ReportAttrError)( doc, node, attval, ESCAPED_ILLEGAL_URI);
1704
154
        else if ( !(TY_(HTMLVersion)(doc) & VERS_HTML5) )
1705
2
            TY_(ReportAttrError)( doc, node, attval, ILLEGAL_URI_REFERENCE);
1706
1707
22.8k
        doc->badChars |= BC_INVALID_URI;
1708
22.8k
    }
1709
26.5k
    if ( bad_codepoint_count )
1710
21.3k
    {
1711
21.3k
        TY_(ReportAttrError)( doc, node, attval, ILLEGAL_URI_CODEPOINT );
1712
21.3k
    }
1713
26.5k
}
1714
1715
/* RFC 2396, section 4.2 states:
1716
     "[...] in the case of HTML's FORM element, [...] an
1717
     empty URI reference represents the base URI of the
1718
     current document and should be replaced by that URI
1719
     when transformed into a request."
1720
*/
1721
void CheckAction( TidyDocImpl* doc, Node *node, AttVal *attval)
1722
1.13k
{
1723
1.13k
    if (AttrHasValue(attval))
1724
602
        TY_(CheckUrl)( doc, node, attval );
1725
1.13k
}
1726
1727
void CheckScript( TidyDocImpl* ARG_UNUSED(doc), Node* ARG_UNUSED(node),
1728
                  AttVal* ARG_UNUSED(attval))
1729
6.82k
{
1730
6.82k
}
1731
1732
Bool TY_(IsValidHTMLID)(ctmbstr id)
1733
107k
{
1734
107k
    ctmbstr s = id;
1735
1736
107k
    if (!s)
1737
290
        return no;
1738
1739
2.68M
    while (*s)
1740
2.63M
        if (TY_(IsHTMLSpace)(*s++))
1741
53.0k
            return no;
1742
1743
53.9k
    return yes;
1744
1745
107k
}
1746
1747
Bool TY_(IsValidXMLID)(ctmbstr id)
1748
176k
{
1749
176k
    ctmbstr s = id;
1750
176k
    tchar c;
1751
1752
176k
    if (!s)
1753
0
        return no;
1754
1755
176k
    c = *s++;
1756
176k
    if (c > 0x7F)
1757
21.1k
        s += TY_(GetUTF8)(s, &c);
1758
1759
176k
    if (!(TY_(IsXMLLetter)(c) || c == '_' || c == ':'))
1760
45.7k
        return no;
1761
1762
4.25M
    while (*s)
1763
4.17M
    {
1764
4.17M
        c = (unsigned char)*s;
1765
1766
4.17M
        if (c > 0x7F)
1767
283k
            s += TY_(GetUTF8)(s, &c);
1768
1769
4.17M
        ++s;
1770
1771
4.17M
        if (!TY_(IsXMLNamechar)(c))
1772
56.5k
            return no;
1773
4.17M
    }
1774
1775
73.9k
    return yes;
1776
130k
}
1777
1778
static Bool IsValidNMTOKEN(ctmbstr name)
1779
56.6k
{
1780
56.6k
    ctmbstr s = name;
1781
56.6k
    tchar c;
1782
1783
56.6k
    if (!s)
1784
0
        return no;
1785
1786
493k
    while (*s)
1787
477k
    {
1788
477k
        c = (unsigned char)*s;
1789
1790
477k
        if (c > 0x7F)
1791
388k
            s += TY_(GetUTF8)(s, &c);
1792
1793
477k
        ++s;
1794
1795
477k
        if (!TY_(IsXMLNamechar)(c))
1796
40.9k
            return no;
1797
477k
    }
1798
1799
15.7k
    return yes;
1800
56.6k
}
1801
1802
static Bool AttrValueIsAmong(AttVal *attval, ctmbstr const list[])
1803
49.6k
{
1804
49.6k
    const ctmbstr *v;   
1805
187k
    for (v = list; *v; ++v)
1806
149k
        if (AttrValueIs(attval, *v))
1807
11.5k
            return yes;
1808
38.0k
    return no;
1809
49.6k
}
1810
1811
static void CheckAttrValidity( TidyDocImpl* doc, Node *node, AttVal *attval,
1812
                               ctmbstr const list[])
1813
13.0k
{
1814
13.0k
    if (!AttrHasValue(attval))
1815
8.09k
    {
1816
8.09k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
1817
8.09k
        return;
1818
8.09k
    }
1819
1820
4.95k
    CheckLowerCaseAttrValue( doc, node, attval );
1821
1822
4.95k
    if (!AttrValueIsAmong(attval, list))
1823
4.18k
        TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1824
4.95k
}
1825
1826
void CheckName( TidyDocImpl* doc, Node *node, AttVal *attval)
1827
62.4k
{
1828
62.4k
    Node *old;
1829
1830
62.4k
    if (!AttrHasValue(attval))
1831
2.42k
    {
1832
2.42k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
1833
2.42k
        return;
1834
2.42k
    }
1835
1836
60.0k
    if ( TY_(IsAnchorElement)(doc, node) )
1837
56.6k
    {
1838
56.6k
        if (cfgBool(doc, TidyXmlOut) && !IsValidNMTOKEN(attval->value))
1839
40.9k
            TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1840
1841
56.6k
        if ((old = GetNodeByAnchor(doc, attval->value)) &&  old != node)
1842
51.7k
        {
1843
51.7k
            if (node->implicit) /* Is #709 - improve warning text */
1844
41.3k
                TY_(ReportAttrError)(doc, node, attval, ANCHOR_DUPLICATED);
1845
10.4k
            else
1846
10.4k
                TY_(ReportAttrError)( doc, node, attval, ANCHOR_NOT_UNIQUE);
1847
51.7k
        }
1848
4.86k
        else
1849
4.86k
            AddAnchor( doc, attval->value, node );
1850
56.6k
    }
1851
60.0k
}
1852
1853
void CheckId( TidyDocImpl* doc, Node *node, AttVal *attval )
1854
91.5k
{
1855
91.5k
    Lexer* lexer = doc->lexer;
1856
91.5k
    Node *old;
1857
1858
91.5k
    if (!AttrHasValue(attval))
1859
18.8k
    {
1860
18.8k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
1861
18.8k
        return;
1862
18.8k
    }
1863
1864
72.6k
    if (!TY_(IsValidHTMLID)(attval->value))
1865
52.9k
    {
1866
52.9k
        if (lexer->isvoyager && TY_(IsValidXMLID)(attval->value))
1867
0
            TY_(ReportAttrError)( doc, node, attval, XML_ID_SYNTAX);
1868
52.9k
        else
1869
52.9k
            TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1870
52.9k
    }
1871
1872
72.6k
    if ((old = GetNodeByAnchor(doc, attval->value)) &&  old != node)
1873
42.9k
    {
1874
42.9k
        if (node->implicit) /* Is #709 - improve warning text */
1875
39.0k
            TY_(ReportAttrError)(doc, node, attval, ANCHOR_DUPLICATED);
1876
3.82k
        else
1877
3.82k
            TY_(ReportAttrError)( doc, node, attval, ANCHOR_NOT_UNIQUE);
1878
42.9k
    }
1879
29.7k
    else
1880
29.7k
        AddAnchor( doc, attval->value, node );
1881
72.6k
}
1882
1883
void CheckIs( TidyDocImpl* doc, Node *node, AttVal *attval )
1884
2.82k
{
1885
2.82k
    const char *ptr;
1886
2.82k
    Bool go = yes;
1887
1888
    /* `is` MUST NOT be in an autonomous custom tag */
1889
2.82k
    ptr = strchr(node->element, '-');
1890
2.82k
    if ( ( ptr && (ptr - node->element > 0) ) )
1891
0
    {
1892
0
        TY_(ReportAttrError)( doc, node, attval, ATTRIBUTE_IS_NOT_ALLOWED);
1893
0
    }
1894
1895
    /* Even if we fail the above test, we'll continue to emit reports because
1896
       the user should *also* know that his attribute values are wrong, even
1897
       if they shouldn't be in custom tags anyway. */
1898
1899
    /* `is` MUST have a value */
1900
2.82k
    if (!AttrHasValue(attval))
1901
1.37k
    {
1902
1.37k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
1903
1.37k
        return;
1904
1.37k
    }
1905
1906
    /* `is` MUST contain a hyphen and no space. */
1907
1.44k
    ptr = strchr(attval->value, '-');
1908
1.44k
    go = ( ptr && (ptr - attval->value > 0) );
1909
1.44k
    ptr = strchr(attval->value, ' ');
1910
1.44k
    go = go & (ptr == NULL);
1911
1.44k
    if ( !go )
1912
508
    {
1913
508
        TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1914
508
    }
1915
1.44k
}
1916
1917
void CheckBool( TidyDocImpl* doc, Node *node, AttVal *attval)
1918
9.40k
{
1919
9.40k
    if (!AttrHasValue(attval))
1920
5.63k
        return;
1921
1922
3.76k
    CheckLowerCaseAttrValue( doc, node, attval );
1923
3.76k
}
1924
1925
void CheckAlign( TidyDocImpl* doc, Node *node, AttVal *attval)
1926
17.1k
{
1927
17.1k
    ctmbstr const values[] = {"left", "right", "center", "justify", NULL};
1928
1929
    /* IMG, OBJECT, APPLET and EMBED use align for vertical position */
1930
17.1k
    if (node->tag && (node->tag->model & CM_IMG))
1931
3.65k
    {
1932
3.65k
        CheckValign( doc, node, attval );
1933
3.65k
        return;
1934
3.65k
    }
1935
1936
13.5k
    if (!AttrHasValue(attval))
1937
2.46k
    {
1938
2.46k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
1939
2.46k
        return;
1940
2.46k
    }
1941
1942
11.0k
    CheckLowerCaseAttrValue( doc, node, attval);
1943
1944
    /* currently CheckCaption(...) takes care of the remaining cases */
1945
11.0k
    if (nodeIsCAPTION(node))
1946
3.07k
        return;
1947
1948
7.99k
    if (!AttrValueIsAmong(attval, values))
1949
6.91k
    {
1950
        /* align="char" is allowed for elements with CM_TABLE|CM_ROW
1951
           except CAPTION which is excluded above, */
1952
6.91k
        if( !(AttrValueIs(attval, "char")
1953
6.91k
              && TY_(nodeHasCM)(node, CM_TABLE|CM_ROW)) )
1954
6.41k
             TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1955
6.91k
    }
1956
7.99k
}
1957
1958
void CheckValign( TidyDocImpl* doc, Node *node, AttVal *attval)
1959
8.38k
{
1960
8.38k
    ctmbstr const values[] = {"top", "middle", "bottom", "baseline", NULL};
1961
8.38k
    ctmbstr const values2[] = {"left", "right", NULL};
1962
8.38k
    ctmbstr const valuesp[] = {"texttop", "absmiddle", "absbottom",
1963
8.38k
                               "textbottom", NULL};
1964
1965
8.38k
    if (!AttrHasValue(attval))
1966
1.95k
    {
1967
1.95k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
1968
1.95k
        return;
1969
1.95k
    }
1970
1971
6.42k
    CheckLowerCaseAttrValue( doc, node, attval );
1972
1973
6.42k
    if (AttrValueIsAmong(attval, values))
1974
423
    {
1975
            /* all is fine */
1976
423
    }
1977
6.00k
    else if (AttrValueIsAmong(attval, values2))
1978
1.22k
    {
1979
1.22k
        if (!(node->tag && (node->tag->model & CM_IMG)))
1980
871
            TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1981
1.22k
    }
1982
4.77k
    else if (AttrValueIsAmong(attval, valuesp))
1983
1.95k
    {
1984
1.95k
        TY_(ConstrainVersion)( doc, VERS_PROPRIETARY );
1985
1.95k
        TY_(ReportAttrError)( doc, node, attval, PROPRIETARY_ATTR_VALUE);
1986
1.95k
    }
1987
2.81k
    else
1988
2.81k
        TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
1989
6.42k
}
1990
1991
void CheckLength( TidyDocImpl* doc, Node *node, AttVal *attval)
1992
8.09k
{
1993
8.09k
    tmbstr p;
1994
    
1995
8.09k
    if (!AttrHasValue(attval))
1996
1.07k
    {
1997
1.07k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
1998
1.07k
        return;
1999
1.07k
    }
2000
2001
    /* don't check for <col width=...> and <colgroup width=...> */
2002
7.01k
    if (attrIsWIDTH(attval) && (nodeIsCOL(node) || nodeIsCOLGROUP(node)))
2003
2.17k
        return;
2004
2005
4.84k
    p = attval->value;
2006
    
2007
4.84k
    if (!TY_(IsDigit)(*p++))
2008
1.78k
    {
2009
1.78k
        TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2010
1.78k
    }
2011
3.06k
    else
2012
3.06k
    {
2013
3.06k
        Bool percentFound = no;
2014
16.2k
        while (*p)
2015
14.4k
        {
2016
14.4k
            if (!percentFound && *p == '%')
2017
586
            {
2018
586
                percentFound = yes;
2019
586
            }
2020
13.8k
            else if (percentFound || !TY_(IsDigit)(*p))
2021
1.26k
            {
2022
1.26k
                TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2023
1.26k
                break;
2024
1.26k
            }
2025
2026
13.2k
            ++p;
2027
13.2k
        }
2028
3.06k
    }
2029
4.84k
}
2030
2031
void CheckTarget( TidyDocImpl* doc, Node *node, AttVal *attval)
2032
3.28k
{
2033
3.28k
    ctmbstr const values[] = {"_blank", "_self", "_parent", "_top", NULL};
2034
2035
3.28k
    if (!AttrHasValue(attval))
2036
462
    {
2037
462
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
2038
462
        return;
2039
462
    }
2040
2041
    /* target names must begin with A-Za-z ... */
2042
2.82k
    if (TY_(IsLetter)(attval->value[0]))
2043
731
        return;
2044
2045
    /* or be one of the allowed list */
2046
2.09k
    if (!AttrValueIsAmong(attval, values))
2047
1.19k
        TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2048
2.09k
}
2049
2050
void CheckFsubmit( TidyDocImpl* doc, Node *node, AttVal *attval)
2051
445
{
2052
445
    ctmbstr const values[] = {"get", "post", NULL};
2053
445
    CheckAttrValidity( doc, node, attval, values );
2054
445
}
2055
2056
void CheckClear( TidyDocImpl* doc, Node *node, AttVal *attval)
2057
5.51k
{
2058
5.51k
    ctmbstr const values[] = {"none", "left", "right", "all", NULL};
2059
2060
5.51k
    if (!AttrHasValue(attval))
2061
472
    {
2062
472
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
2063
472
        if (attval->value == NULL)
2064
472
            attval->value = TY_(tmbstrdup)( doc->allocator, "none" );
2065
472
        return;
2066
472
    }
2067
2068
5.04k
    CheckLowerCaseAttrValue( doc, node, attval );
2069
        
2070
5.04k
    if (!AttrValueIsAmong(attval, values))
2071
4.27k
        TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2072
5.04k
}
2073
2074
void CheckShape( TidyDocImpl* doc, Node *node, AttVal *attval)
2075
1.18k
{
2076
1.18k
    ctmbstr const values[] = {"rect", "default", "circle", "poly", NULL};
2077
1.18k
    CheckAttrValidity( doc, node, attval, values );
2078
1.18k
}
2079
2080
void CheckScope( TidyDocImpl* doc, Node *node, AttVal *attval)
2081
930
{
2082
930
    ctmbstr const values[] = {"row", "rowgroup", "col", "colgroup", NULL};
2083
930
    CheckAttrValidity( doc, node, attval, values );
2084
930
}
2085
2086
void CheckNumber( TidyDocImpl* doc, Node *node, AttVal *attval)
2087
15.8k
{
2088
15.8k
    tmbstr p;
2089
    
2090
15.8k
    if (!AttrHasValue(attval))
2091
584
    {
2092
584
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
2093
584
        return;
2094
584
    }
2095
2096
    /* don't check <frameset cols=... rows=...> */
2097
15.2k
    if ( nodeIsFRAMESET(node) &&
2098
15.2k
        (attrIsCOLS(attval) || attrIsROWS(attval)))
2099
707
     return;
2100
2101
14.5k
    p  = attval->value;
2102
    
2103
    /* font size may be preceded by + or - */
2104
14.5k
    if ( nodeIsFONT(node) && (*p == '+' || *p == '-') )
2105
2.28k
        ++p;
2106
    /* tabindex may be preceded by - */
2107
14.5k
    if (attval->attribute && (strcmp(attval->attribute,"tabindex") == 0) && (*p == '-'))
2108
499
        ++p;
2109
2110
19.1k
    while (*p)
2111
16.4k
    {
2112
16.4k
        if (!TY_(IsDigit)(*p))
2113
11.8k
        {
2114
11.8k
            TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2115
11.8k
            break;
2116
11.8k
        }
2117
4.60k
        ++p;
2118
4.60k
    }
2119
14.5k
}
2120
2121
/* check hexadecimal color value */
2122
static Bool IsValidColorCode(ctmbstr color)
2123
13.7k
{
2124
13.7k
    uint i;
2125
2126
13.7k
    if (TY_(tmbstrlen)(color) != 6)
2127
7.18k
        return no;
2128
2129
    /* check if valid hex digits and letters */
2130
32.8k
    for (i = 0; i < 6; i++)
2131
29.8k
        if (!TY_(IsDigit)(color[i]) && !strchr("abcdef", TY_(ToLower)(color[i])))
2132
3.66k
            return no;
2133
2134
2.93k
    return yes;
2135
6.60k
}
2136
2137
/* check color syntax and beautify value by option */
2138
void CheckColor( TidyDocImpl* doc, Node *node, AttVal *attval)
2139
14.9k
{
2140
14.9k
    Bool valid = no;
2141
14.9k
    tmbstr given;
2142
2143
14.9k
    if (!AttrHasValue(attval))
2144
1.12k
    {
2145
1.12k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
2146
1.12k
        return;
2147
1.12k
    }
2148
2149
13.7k
    given = attval->value;
2150
2151
    /* 727851 - add hash to hash-less color values */
2152
13.7k
    if (given[0] != '#' && (valid = IsValidColorCode(given)))
2153
1.82k
    {
2154
1.82k
        tmbstr cp, s;
2155
2156
1.82k
        cp = s = (tmbstr) TidyDocAlloc(doc, 2 + TY_(tmbstrlen)(given));
2157
1.82k
        *cp++ = '#';
2158
12.7k
        while ('\0' != (*cp++ = *given++))
2159
10.9k
            continue;
2160
2161
1.82k
        TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE_REPLACED);
2162
2163
1.82k
        TidyDocFree(doc, attval->value);
2164
1.82k
        given = attval->value = s;
2165
1.82k
    }
2166
2167
13.7k
    if (!valid && given[0] == '#')
2168
3.05k
        valid = IsValidColorCode(given + 1);
2169
2170
13.7k
    if (valid && given[0] == '#' && cfgBool(doc, TidyReplaceColor))
2171
609
    {
2172
609
        ctmbstr newName = GetColorName(given, TY_(IsHTML5Mode)(doc));
2173
2174
609
        if (newName)
2175
331
        {
2176
331
            TidyDocFree(doc, attval->value);
2177
331
            given = attval->value = TY_(tmbstrdup)(doc->allocator, newName);
2178
331
        }
2179
609
    }
2180
2181
    /* if it is not a valid color code, it is a color name */
2182
13.7k
    if (!valid)
2183
10.8k
        valid = GetColorCode(given, TY_(IsHTML5Mode)(doc)) != NULL;
2184
2185
13.7k
    if (valid && given[0] == '#')
2186
2.60k
        attval->value = TY_(tmbstrtoupper)(attval->value);
2187
11.1k
    else if (valid)
2188
1.69k
        attval->value = TY_(tmbstrtolower)(attval->value);
2189
2190
13.7k
    if (!valid)
2191
9.48k
        TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2192
13.7k
}
2193
2194
/* check valuetype attribute for element param */
2195
void CheckVType( TidyDocImpl* doc, Node *node, AttVal *attval)
2196
566
{
2197
566
    ctmbstr const values[] = {"data", "object", "ref", NULL};
2198
566
    CheckAttrValidity( doc, node, attval, values );
2199
566
}
2200
2201
/* checks scrolling attribute */
2202
void CheckScroll( TidyDocImpl* doc, Node *node, AttVal *attval)
2203
1.14k
{
2204
1.14k
    ctmbstr const values[] = {"no", "auto", "yes", NULL};
2205
1.14k
    CheckAttrValidity( doc, node, attval, values );
2206
1.14k
}
2207
2208
/* checks dir attribute */
2209
void CheckTextDir( TidyDocImpl* doc, Node *node, AttVal *attval)
2210
3.32k
{
2211
3.32k
    ctmbstr const values4[] = { "rtl", "ltr", NULL };
2212
    /* PR #712 - add 'auto' for HTML5 - @doronbehar */
2213
3.32k
    ctmbstr const values5[] = { "rtl", "ltr", "auto", NULL };
2214
3.32k
    CheckAttrValidity(doc, node, attval,
2215
3.32k
        (TY_(IsHTML5Mode)(doc) ? values5 : values4));
2216
3.32k
}
2217
2218
/* checks lang and xml:lang attributes */
2219
void CheckLang( TidyDocImpl* doc, Node *node, AttVal *attval)
2220
51.9k
{
2221
    /* empty xml:lang is allowed through XML 1.0 SE errata */
2222
51.9k
    if (!AttrHasValue(attval) && !attrIsXML_LANG(attval))
2223
3.76k
    {
2224
3.76k
        if ( cfg(doc, TidyAccessibilityCheckLevel) == 0 )
2225
3.76k
        {
2226
3.76k
            TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE );
2227
3.76k
        }
2228
3.76k
        return;
2229
3.76k
    }
2230
51.9k
}
2231
2232
/* checks loading attribute */
2233
void CheckLoading( TidyDocImpl* doc, Node *node, AttVal *attval)
2234
422
{
2235
422
    ctmbstr const values[] = {"lazy", "eager", NULL};
2236
422
    CheckAttrValidity( doc, node, attval, values );
2237
422
}
2238
2239
/* checks type attribute */
2240
void CheckType( TidyDocImpl* doc, Node *node, AttVal *attval)
2241
13.3k
{
2242
13.3k
    ctmbstr const valuesINPUT[] = {
2243
13.3k
        "text", "password", "checkbox", "radio", "submit", "reset", "file",
2244
13.3k
        "hidden", "image", "button", "color", "date", "datetime",
2245
13.3k
        "datetime-local", "email", "month", "number", "range", "search",
2246
13.3k
        "tel", "time", "url", "week", NULL};
2247
13.3k
    ctmbstr const valuesBUTTON[] = {"button", "submit", "reset", NULL};
2248
13.3k
    ctmbstr const valuesUL[] = {"disc", "square", "circle", NULL};
2249
13.3k
    ctmbstr const valuesOL[] = {"1", "a", "i", NULL};
2250
2251
13.3k
    if (nodeIsINPUT(node))
2252
388
        CheckAttrValidity( doc, node, attval, valuesINPUT );
2253
12.9k
    else if (nodeIsBUTTON(node))
2254
952
        CheckAttrValidity( doc, node, attval, valuesBUTTON );
2255
11.9k
    else if (nodeIsUL(node))
2256
3.69k
        CheckAttrValidity( doc, node, attval, valuesUL );
2257
8.27k
    else if (nodeIsOL(node))
2258
2.65k
    {
2259
2.65k
        if (!AttrHasValue(attval))
2260
1.24k
        {
2261
1.24k
            TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
2262
1.24k
            return;
2263
1.24k
        }
2264
1.40k
        if (!AttrValueIsAmong(attval, valuesOL))
2265
878
            TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2266
1.40k
    }
2267
5.62k
    else if (nodeIsLI(node))
2268
2.45k
    {
2269
2.45k
        if (!AttrHasValue(attval))
2270
408
        {
2271
408
            TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
2272
408
            return;
2273
408
        }
2274
2.04k
        if (AttrValueIsAmong(attval, valuesUL))
2275
602
            CheckLowerCaseAttrValue( doc, node, attval );
2276
1.44k
        else if (!AttrValueIsAmong(attval, valuesOL))
2277
940
            TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2278
2.04k
    }
2279
11.6k
    return;
2280
13.3k
}
2281
2282
static void CheckDecimal( TidyDocImpl* doc, Node *node, AttVal *attval)
2283
4.11k
{
2284
4.11k
    tmbstr p;
2285
4.11k
    Bool hasPoint = no;
2286
2287
4.11k
    p  = attval->value;
2288
2289
    /* Allow leading sign */
2290
4.11k
    if (*p == '+' || *p == '-')
2291
1.79k
        ++p;
2292
2293
47.4k
    while (*p)
2294
46.3k
    {
2295
        /* Allow a single decimal point */
2296
46.3k
        if (*p == '.')
2297
1.56k
        {
2298
1.56k
            if (!hasPoint)
2299
1.56k
                hasPoint = yes;
2300
0
            else
2301
0
                TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2302
1.56k
                break;
2303
1.56k
        }
2304
        
2305
44.8k
        if (!TY_(IsDigit)(*p))
2306
1.51k
        {
2307
1.51k
            TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2308
1.51k
            break;
2309
1.51k
        }
2310
43.2k
        ++p;
2311
43.2k
    }
2312
4.11k
}
2313
2314
static Bool IsSvgPaintAttr(AttVal *attval)
2315
15.8k
{
2316
15.8k
    return attrIsCOLOR(attval)
2317
15.8k
        || attrIsSVG_FILL(attval)
2318
15.8k
        || attrIsSVG_FILLRULE(attval)
2319
15.8k
        || attrIsSVG_STROKE(attval)
2320
15.8k
        || attrIsSVG_STROKEDASHARRAY(attval)
2321
15.8k
        || attrIsSVG_STROKEDASHOFFSET(attval)
2322
15.8k
        || attrIsSVG_STROKELINECAP(attval)
2323
15.8k
        || attrIsSVG_STROKELINEJOIN(attval)
2324
15.8k
        || attrIsSVG_STROKEMITERLIMIT(attval)
2325
15.8k
        || attrIsSVG_STROKEWIDTH(attval)
2326
15.8k
        || attrIsSVG_COLORINTERPOLATION(attval)
2327
15.8k
        || attrIsSVG_COLORRENDERING(attval)
2328
15.8k
        || attrIsSVG_OPACITY(attval)
2329
15.8k
        || attrIsSVG_STROKEOPACITY(attval)
2330
15.8k
        || attrIsSVG_FILLOPACITY(attval);
2331
15.8k
}
2332
2333
/* Check SVG attributes */
2334
static void CheckSvgAttr( TidyDocImpl* doc, Node *node, AttVal *attval)
2335
16.2k
{
2336
16.2k
    if (!nodeIsSVG(node))
2337
375
    {
2338
375
        TY_(ReportAttrError)(doc, node, attval, ATTRIBUTE_IS_NOT_ALLOWED);
2339
375
        return;
2340
375
    }
2341
2342
    /* Issue #903 - check SVG paint attributes */
2343
15.8k
    if (IsSvgPaintAttr(attval))
2344
15.8k
    {
2345
        /* all valid paint attributes have values */
2346
15.8k
        if (!AttrHasValue(attval))
2347
3.15k
        {
2348
3.15k
            TY_(ReportAttrError)(doc, node, attval, MISSING_ATTR_VALUE);
2349
3.15k
            return;
2350
3.15k
        }
2351
        /* all paint attributes support an 'inherit' value,
2352
        per https://dev.w3.org/SVG/profiles/1.1F2/publish/painting.html#SpecifyingPaint */
2353
12.7k
        if (AttrValueIs(attval, "inherit"))
2354
0
        {
2355
0
            return;
2356
0
        }
2357
2358
        /* check paint datatypes
2359
        see https://dev.w3.org/SVG/profiles/1.1F2/publish/painting.html#SpecifyingPaint
2360
        */
2361
12.7k
        if (attrIsSVG_FILL(attval) || attrIsSVG_STROKE(attval))
2362
1.74k
        {
2363
            /* TODO: support funciri */
2364
1.74k
            static ctmbstr const values[] = {
2365
1.74k
                "none", "currentColor", NULL};
2366
2367
1.74k
            if (AttrValueIsAmong(attval, values))
2368
206
                CheckLowerCaseAttrValue(doc, node, attval);
2369
1.54k
            else
2370
1.54k
                CheckColor(doc, node, attval);
2371
1.74k
        } 
2372
10.9k
        else if (attrIsSVG_FILLRULE(attval))
2373
387
        {
2374
387
            static ctmbstr const values[] = {"nonzero", "evenodd", NULL};
2375
2376
387
            if (AttrValueIsAmong(attval, values))
2377
211
                CheckLowerCaseAttrValue(doc, node, attval);
2378
176
            else
2379
176
                TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE);
2380
387
        }
2381
10.5k
        else if (attrIsSVG_STROKEDASHARRAY(attval))
2382
2.01k
        {
2383
2.01k
            static ctmbstr const values[] = {"none", NULL};
2384
2385
2.01k
            if (AttrValueIsAmong(attval, values))
2386
734
                CheckLowerCaseAttrValue(doc, node, attval);
2387
1.27k
            else
2388
1.27k
            {
2389
                /* TODO: process dash arrays */
2390
1.27k
            }
2391
2.01k
        }
2392
8.56k
        else if (attrIsSVG_STROKEDASHOFFSET(attval))
2393
222
        {
2394
222
            CheckLength(doc, node, attval);
2395
222
        }
2396
8.33k
        else if (attrIsSVG_STROKELINECAP(attval))
2397
677
        {
2398
677
            static ctmbstr const values[] = {"butt", "round", "square", NULL};
2399
2400
677
            if (AttrValueIsAmong(attval, values))
2401
166
                CheckLowerCaseAttrValue(doc, node, attval);
2402
511
            else
2403
511
                TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE);
2404
677
        }
2405
7.66k
        else if (attrIsSVG_STROKELINEJOIN(attval))
2406
2.62k
        {
2407
2.62k
            static ctmbstr const values[] = {"miter", "round", "bevel", NULL};
2408
2409
2.62k
            if (AttrValueIsAmong(attval, values))
2410
1.48k
                CheckLowerCaseAttrValue(doc, node, attval);
2411
1.13k
            else
2412
1.13k
                TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE);
2413
2.62k
        }
2414
5.03k
        else if (attrIsSVG_STROKEMITERLIMIT(attval))
2415
453
        {
2416
453
            CheckNumber(doc, node, attval);
2417
453
        }
2418
4.58k
        else if (attrIsSVG_STROKEWIDTH(attval))
2419
468
        {
2420
468
            CheckLength(doc, node, attval);
2421
468
        }
2422
4.11k
        else if (attrIsSVG_COLORINTERPOLATION(attval))
2423
0
        {
2424
0
            static ctmbstr const values[] = {"auto", "sRGB", "linearRGB", NULL};
2425
2426
0
            if (AttrValueIsAmong(attval, values))
2427
0
                CheckLowerCaseAttrValue(doc, node, attval);
2428
0
            else
2429
0
                TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE);
2430
0
        }
2431
4.11k
        else if (attrIsSVG_COLORRENDERING(attval))
2432
0
        {
2433
0
            static ctmbstr const values[] = {
2434
0
                "auto", "optimizeSpeed", "optimizeQuality", NULL};
2435
2436
0
            if (AttrValueIsAmong(attval, values))
2437
0
                CheckLowerCaseAttrValue(doc, node, attval);
2438
0
            else
2439
0
                TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE);
2440
0
        }
2441
4.11k
        else if(attrIsSVG_OPACITY(attval))
2442
2.76k
        {
2443
2.76k
            CheckDecimal(doc, node, attval);
2444
2.76k
        }
2445
1.35k
        else if(attrIsSVG_STROKEOPACITY(attval))
2446
815
        {
2447
815
            CheckDecimal(doc, node, attval);
2448
815
        }
2449
540
        else if(attrIsSVG_FILLOPACITY(attval))
2450
540
        {
2451
540
            CheckDecimal(doc, node, attval);
2452
540
        }
2453
12.7k
    }
2454
15.8k
}
2455
2456
static
2457
AttVal *SortAttVal( TidyDocImpl* doc, AttVal* list, TidyAttrSortStrategy strat );
2458
2459
void TY_(SortAttributes)(TidyDocImpl* doc, Node* node, TidyAttrSortStrategy strat)
2460
1.86M
{
2461
5.08M
    while (node)
2462
3.21M
    {
2463
3.21M
        node->attributes = SortAttVal( doc, node->attributes, strat );
2464
3.21M
        if (node->content)
2465
1.85M
            TY_(SortAttributes)(doc, node->content, strat);
2466
3.21M
        node = node->next;
2467
3.21M
    }
2468
1.86M
}
2469
2470
/**
2471
* Attribute sorting contributed by Adrian Wilkins, 2007
2472
* 
2473
* Portions copyright Simon Tatham 2001.
2474
*
2475
* Merge sort algorithm adapted from listsort.c linked from 
2476
* https://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
2477
* 
2478
* Original copyright notice proceeds below.
2479
* 
2480
* Permission is hereby granted, free of charge, to any person
2481
* obtaining a copy of this software and associated documentation
2482
* files (the "Software"), to deal in the Software without
2483
* restriction, including without limitation the rights to use,
2484
* copy, modify, merge, publish, distribute, sublicense, and/or
2485
* sell copies of the Software, and to permit persons to whom the
2486
* Software is furnished to do so, subject to the following
2487
* conditions:
2488
* 
2489
* The above copyright notice and this permission notice shall be
2490
* included in all copies or substantial portions of the Software.
2491
* 
2492
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2493
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
2494
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2495
* NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
2496
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
2497
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2498
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2499
* SOFTWARE.
2500
*/
2501
2502
typedef int(*ptAttValComparator)(AttVal *one, AttVal *two, ctmbstr *list);
2503
2504
/* Returns the index of the item in the array, or -1 if not in the array */
2505
static
2506
int indexof( ctmbstr item, ctmbstr *list )
2507
0
{
2508
0
    if ( list )
2509
0
    {
2510
0
        uint i = 0;
2511
0
        while ( list[i] != NULL ) {
2512
0
            if ( TY_(tmbstrcasecmp)(item, list[i]) == 0 )
2513
0
                return i;
2514
0
            i++;
2515
0
        }
2516
0
    }
2517
2518
0
    return -1;
2519
0
}
2520
2521
/* Comparison function for TidySortAttrAlpha. Will also consider items in
2522
   the passed in list as higher-priority, and will group them first.
2523
 */
2524
static
2525
int AlphaComparator(AttVal *one, AttVal *two, ctmbstr *list)
2526
0
{
2527
0
    int oneIndex = indexof( one->attribute, list );
2528
0
    int twoIndex = indexof( two->attribute, list );
2529
2530
    /* If both on the list, the lower index has priority. */
2531
0
    if ( oneIndex >= 0 && twoIndex >= 0 )
2532
0
        return oneIndex < twoIndex ? -1 : 1;
2533
2534
    /* If A on the list but B not on the list, then A has priority. */
2535
0
    if ( oneIndex >= 0 && twoIndex == -1 )
2536
0
        return -1;
2537
2538
    /* If A not on the list but B is on the list, then B has priority. */
2539
0
    if ( oneIndex == -1 && twoIndex >= 0 )
2540
0
        return 1;
2541
2542
    /* Otherwise nothing is on the list, so just compare strings. */
2543
0
    return TY_(tmbstrcmp)(one->attribute, two->attribute);
2544
0
}
2545
2546
2547
/* Comparison function for prioritizing list items. It doesn't otherwise
2548
   sort.
2549
 */
2550
static
2551
int PriorityComparator(AttVal *one, AttVal *two, ctmbstr *list)
2552
0
{
2553
0
    int oneIndex = indexof( one->attribute, list );
2554
0
    int twoIndex = indexof( two->attribute, list );
2555
2556
    /* If both on the list, the lower index has priority. */
2557
0
    if ( oneIndex >= 0 && twoIndex >= 0 )
2558
0
        return oneIndex < twoIndex ? -1 : 1;
2559
2560
    /* If A on the list but B not on the list, then A has priority. */
2561
0
    if ( oneIndex >= 0 && twoIndex == -1 )
2562
0
        return -1;
2563
2564
    /* If A not on the list but B is on the list, then B has priority. */
2565
0
    if ( oneIndex == -1 && twoIndex >= 0 )
2566
0
        return 1;
2567
2568
    /* Otherwise nothing is on the list, so just mark them as the same. */
2569
0
    return 0;
2570
0
}
2571
2572
2573
/* The "factory method" that returns a pointer to the comparator function */
2574
static
2575
ptAttValComparator GetAttValComparator(TidyAttrSortStrategy strat, ctmbstr *list)
2576
3.21M
{
2577
3.21M
    switch (strat)
2578
3.21M
    {
2579
0
    case TidySortAttrAlpha:
2580
0
        return AlphaComparator;
2581
3.21M
    case TidySortAttrNone:
2582
3.21M
        if ( list && list[0] )
2583
0
            return PriorityComparator;
2584
3.21M
        break;
2585
3.21M
    }
2586
3.21M
    return 0;
2587
3.21M
}
2588
2589
/* The sort routine */
2590
static
2591
AttVal *SortAttVal( TidyDocImpl* doc, AttVal *list, TidyAttrSortStrategy strat)
2592
3.21M
{
2593
    /* Get the list from the passed-in tidyDoc. */
2594
3.21M
    ctmbstr* priorityList = (ctmbstr*)doc->attribs.priorityAttribs.list;
2595
2596
3.21M
    ptAttValComparator ptComparator = GetAttValComparator(strat, priorityList);
2597
3.21M
    AttVal *p, *q, *e, *tail;
2598
3.21M
    int insize, nmerges, psize, qsize, i;
2599
2600
    /*
2601
    * Silly special case: if `list' was passed in as NULL, return
2602
    * NULL immediately.
2603
    */
2604
3.21M
    if (!list)
2605
2.96M
        return NULL;
2606
2607
    /* If no comparator, return the list as-is */
2608
251k
    if (ptComparator == 0)
2609
251k
        return list;
2610
2611
0
    insize = 1;
2612
2613
0
    while (1) {
2614
0
        p = list;
2615
0
        list = NULL;
2616
0
        tail = NULL;
2617
2618
0
        nmerges = 0;  /* count number of merges we do in this pass */
2619
2620
0
        while (p) {
2621
0
            nmerges++;  /* there exists a merge to be done */
2622
            /* step `insize' places along from p */
2623
0
            q = p;
2624
0
            psize = 0;
2625
0
            for (i = 0; i < insize; i++) {
2626
0
                psize++;
2627
0
                q = q->next;
2628
0
                if(!q) break;
2629
0
            }
2630
2631
            /* if q hasn't fallen off end, we have two lists to merge */
2632
0
            qsize = insize;
2633
2634
            /* now we have two lists; merge them */
2635
0
            while (psize > 0 || (qsize > 0 && q)) {
2636
2637
                /* decide whether next element of merge comes from p or q */
2638
0
                if (psize == 0) {
2639
                    /* p is empty; e must come from q. */
2640
0
                    e = q; q = q->next; qsize--;
2641
0
                } else if (qsize == 0 || !q) {
2642
                    /* q is empty; e must come from p. */
2643
0
                    e = p; p = p->next; psize--;
2644
0
                } else if (ptComparator(p,q, priorityList) <= 0) {
2645
                    /* First element of p is lower (or same);
2646
                    * e must come from p. */
2647
0
                    e = p; p = p->next; psize--;
2648
0
                } else {
2649
                    /* First element of q is lower; e must come from q. */
2650
0
                    e = q; q = q->next; qsize--;
2651
0
                }
2652
2653
                /* add the next element to the merged list */
2654
0
                if (tail) {
2655
0
                    tail->next = e;
2656
0
                } else {
2657
0
                    list = e;
2658
0
                }
2659
2660
0
                tail = e;
2661
0
            }
2662
2663
            /* now p has stepped `insize' places along, and q has too */
2664
0
            p = q;
2665
0
        }
2666
2667
0
        tail->next = NULL;
2668
2669
        /* If we have done only one merge, we're finished. */
2670
0
        if (nmerges <= 1)   /* allow for nmerges==0, the empty list case */
2671
0
            return list;
2672
2673
        /* Otherwise repeat, merging lists twice the size */
2674
0
        insize *= 2;
2675
0
    }
2676
0
}
2677
2678
/* RDFA support checkers
2679
 *
2680
 */
2681
2682
/* CheckRDFAPrefix - ensure the prefix attribute value is
2683
 * correct
2684
 *
2685
 * @prefix takes prefix value pairs in the form:
2686
 * 
2687
 *      NCName ':' ' '+ AnyURI
2688
 */
2689
2690
void CheckRDFaPrefix ( TidyDocImpl* doc, Node *node, AttVal *attval)
2691
3.39k
{
2692
3.39k
    if (!AttrHasValue(attval))
2693
415
    {
2694
415
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
2695
415
        return;
2696
415
    }
2697
2698
    /* Copy the attribute value so we can split it */
2699
2.98k
    if (attval->value) {
2700
2.98k
        tmbstr t, tPtr ;
2701
2702
2.98k
        uint prefixCount = 0;
2703
        /* isPrefix toggles - start at 1 and change to 0 as we
2704
         * iterate over the components of the value */
2705
2.98k
        uint isPrefix = 1;
2706
2707
        /* Copy it over */
2708
2709
2.98k
        uint len = TY_(tmbstrlen)(attval->value);
2710
2.98k
        tmbstr s = (tmbstr) TidyDocAlloc( doc, len + 1 );
2711
2.98k
        s[0] = '\0';
2712
2.98k
        TY_(tmbstrcpy)( s, attval->value );
2713
2714
        /* iterate over value */
2715
2.98k
        tPtr = s;
2716
2717
82.8k
        while ( ( t = strtok(tPtr, " ") ) != NULL ) {
2718
79.8k
            tPtr = NULL;
2719
79.8k
            if (isPrefix) {
2720
                /* this piece should be a prefix */
2721
                /* prefix rules are that it can have any
2722
                 * character except a colon - that one must be
2723
                 * at the end */
2724
40.6k
                tmbstr i = strchr(t, ':') ;
2725
40.6k
                if (i == NULL) {
2726
                    /* no colon - bad! */
2727
39.0k
                    TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2728
39.0k
                } else if (i != ( t + TY_(tmbstrlen)(t) - 1) ) {
2729
                    /* not at the end - also bad */
2730
622
                    TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE);
2731
622
                }
2732
40.6k
            } else {
2733
                /* this piece should be a URL */
2734
39.1k
                prefixCount ++;
2735
39.1k
            }
2736
79.8k
            isPrefix = !isPrefix;
2737
79.8k
        }
2738
2.98k
        TidyDocFree( doc, s ) ;
2739
2.98k
    }
2740
2.98k
}
2741
2742
/* CheckRDFaTerm - are terms valid
2743
 *
2744
 */
2745
2746
void CheckRDFaTerm ( TidyDocImpl* doc, Node *node, AttVal *attval)
2747
1.74k
{
2748
1.74k
    if (!AttrHasValue(attval))
2749
1.01k
    {
2750
1.01k
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
2751
1.01k
        return;
2752
1.01k
    }
2753
2754
1.74k
}
2755
2756
/* CheckRDFaSafeCURIE - is a CURIE legal
2757
 *
2758
 */
2759
2760
void CheckRDFaSafeCURIE ( TidyDocImpl* doc, Node *node, AttVal *attval)
2761
4.17k
{
2762
4.17k
    if (!AttrHasValue(attval))
2763
415
    {
2764
415
        TY_(ReportAttrError)( doc, node, attval, MISSING_ATTR_VALUE);
2765
415
        return;
2766
415
    }
2767
2768
4.17k
}
2769
2770
/*
2771
 * local variables:
2772
 * mode: c
2773
 * indent-tabs-mode: nil
2774
 * c-basic-offset: 4
2775
 * eval: (c-set-offset 'substatement-open 0)
2776
 * end:
2777
 */