/src/tidy-html5/src/config.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* config.c |
2 | | * Read configuration files and manage configuration properties. |
3 | | * |
4 | | * Copyright (c) 1998-2017 World Wide Web Consortium (Massachusetts |
5 | | * Institute of Technology, European Research Consortium for Informatics |
6 | | * and Mathematics, Keio University) and HTACG. |
7 | | * |
8 | | * See tidy.h for the copyright notice. |
9 | | */ |
10 | | |
11 | | #include "config.h" |
12 | | #include "tidy-int.h" |
13 | | #include "message.h" |
14 | | #include "tmbstr.h" |
15 | | #include "tags.h" |
16 | | |
17 | | #ifdef WINDOWS_OS |
18 | | # include <io.h> |
19 | | #else |
20 | | # ifdef DMALLOC |
21 | | /* macro for valloc() in dmalloc.h may conflict with declaration for valloc() |
22 | | in unistd.h - we don't need (debugging for) valloc() here. dmalloc.h should |
23 | | come last but it doesn't.*/ |
24 | | # ifdef valloc |
25 | | # undef valloc |
26 | | # endif |
27 | | # endif |
28 | | # include <unistd.h> |
29 | | #endif |
30 | | |
31 | | |
32 | | /***************************************************************************** |
33 | | ** Picklist Configuration |
34 | | ** |
35 | | ** Arrange so index can be cast to enum. Note that the value field in the |
36 | | ** following structures is not currently used in code; they're present for |
37 | | ** documentation purposes currently. The arrays must be populated in enum |
38 | | ** order. |
39 | | ******************************************************************************/ |
40 | | |
41 | | static PickListItems boolPicks = { |
42 | | { "no", TidyNoState, { "0", "n", "f", "no", "false", NULL } }, |
43 | | { "yes", TidyYesState, { "1", "y", "t", "yes", "true", NULL } }, |
44 | | { NULL } |
45 | | }; |
46 | | |
47 | | static PickListItems autoBoolPicks = { |
48 | | { "no", TidyNoState, { "0", "n", "f", "no", "false", NULL } }, |
49 | | { "yes", TidyYesState, { "1", "y", "t", "yes", "true", NULL } }, |
50 | | { "auto", TidyAutoState, { "auto", NULL } }, |
51 | | { NULL } |
52 | | }; |
53 | | |
54 | | static PickListItems repeatAttrPicks = { |
55 | | { "keep-first", TidyNoState, { "keep-first", NULL } }, |
56 | | { "keep-last", TidyYesState, { "keep-last", NULL } }, |
57 | | { NULL } |
58 | | }; |
59 | | |
60 | | static PickListItems accessPicks = { |
61 | | { "0 (Tidy Classic)", 0, { "0", "0 (Tidy Classic)", NULL } }, |
62 | | { "1 (Priority 1 Checks)", 1, { "1", "1 (Priority 1 Checks)", NULL } }, |
63 | | { "2 (Priority 2 Checks)", 2, { "2", "2 (Priority 2 Checks)", NULL } }, |
64 | | { "3 (Priority 3 Checks)", 3, { "3", "3 (Priority 3 Checks)", NULL } }, |
65 | | { NULL } |
66 | | }; |
67 | | |
68 | | static PickListItems charEncPicks = { |
69 | | { "raw", TidyEncRaw, { "raw", NULL } }, |
70 | | { "ascii", TidyEncAscii, { "ascii", NULL } }, |
71 | | { "latin0", TidyEncLatin0, { "latin0", NULL } }, |
72 | | { "latin1", TidyEncLatin1, { "latin1", NULL } }, |
73 | | { "utf8", TidyEncUtf8, { "utf8", NULL } }, |
74 | | #ifndef NO_NATIVE_ISO2022_SUPPORT |
75 | | { "iso2022", TidyEncIso2022, { "iso2022", NULL } }, |
76 | | #endif |
77 | | { "mac", TidyEncMac, { "mac", NULL } }, |
78 | | { "win1252", TidyEncWin1252, { "win1252", NULL } }, |
79 | | { "ibm858", TidyEncIbm858, { "ibm858", NULL } }, |
80 | | { "utf16le", TidyEncUtf16le, { "utf16le", NULL } }, |
81 | | { "utf16be", TidyEncUtf16be, { "utf16be", NULL } }, |
82 | | { "utf16", TidyEncUtf16, { "utf16", NULL } }, |
83 | | { "big5", TidyEncBig5, { "big5", NULL } }, |
84 | | { "shiftjis", TidyEncShiftjis, { "shiftjis", NULL } }, |
85 | | { NULL } |
86 | | }; |
87 | | |
88 | | static PickListItems newlinePicks = { |
89 | | { "LF", TidyLF, { "lf", NULL } }, |
90 | | { "CRLF", TidyCRLF, { "crlf", NULL } }, |
91 | | { "CR", TidyCR, { "cr", NULL } }, |
92 | | { NULL } |
93 | | }; |
94 | | |
95 | | static PickListItems doctypePicks = { |
96 | | { "html5", TidyDoctypeHtml5, { "html5", NULL } }, |
97 | | { "omit", TidyDoctypeOmit, { "omit", NULL } }, |
98 | | { "auto", TidyDoctypeAuto, { "auto", NULL } }, |
99 | | { "strict", TidyDoctypeStrict, { "strict", NULL } }, |
100 | | { "transitional", TidyDoctypeLoose, { "loose", "transitional", NULL } }, |
101 | | { "user", TidyDoctypeUser, { "user", NULL } }, |
102 | | { NULL } |
103 | | }; |
104 | | |
105 | | static PickListItems sorterPicks = { |
106 | | { "none", TidySortAttrNone, { "none", NULL } }, |
107 | | { "alpha", TidySortAttrAlpha, { "alpha", NULL } }, |
108 | | { NULL } |
109 | | }; |
110 | | |
111 | | static PickListItems customTagsPicks = { |
112 | | {"no", TidyCustomNo, { "no", "n", NULL } }, |
113 | | {"blocklevel", TidyCustomBlocklevel, { "blocklevel", NULL } }, |
114 | | {"empty", TidyCustomEmpty, { "empty", NULL } }, |
115 | | {"inline", TidyCustomInline, { "inline", "y", "yes", NULL } }, |
116 | | {"pre", TidyCustomPre, { "pre", NULL } }, |
117 | | { NULL } |
118 | | }; |
119 | | |
120 | | static PickListItems attributeCasePicks = { |
121 | | { "no", TidyUppercaseNo, { "0", "n", "f", "no", "false", NULL } }, |
122 | | { "yes", TidyUppercaseYes, { "1", "y", "t", "yes", "true", NULL } }, |
123 | | { "preserve", TidyUppercasePreserve, { "preserve", NULL } }, |
124 | | { NULL } |
125 | | }; |
126 | | |
127 | | |
128 | | /***************************************************************************** |
129 | | ** Option Configuration |
130 | | ******************************************************************************/ |
131 | | |
132 | | #define DG TidyDiagnostics |
133 | | #define DD TidyDisplay |
134 | | #define DT TidyDocumentIO |
135 | | #define CE TidyEncoding |
136 | | #define IO TidyFileIO |
137 | | #define MC TidyMarkupCleanup |
138 | | #define ME TidyMarkupEntities |
139 | | #define MR TidyMarkupRepair |
140 | | #define MT TidyMarkupTeach |
141 | | #define MX TidyMarkupXForm |
142 | | #define PP TidyPrettyPrint |
143 | | #define IR TidyInternalCategory |
144 | | |
145 | | #define IN TidyInteger |
146 | | #define BL TidyBoolean |
147 | | #define ST TidyString |
148 | | |
149 | | #define XX (TidyConfigCategory)-1 |
150 | | #define XY (TidyOptionType)-1 |
151 | | |
152 | | #define DLF DEFAULT_NL_CONFIG |
153 | | |
154 | | /* forward declarations */ |
155 | | static ParseProperty ParseInt; |
156 | | static ParseProperty ParseList; |
157 | | static ParseProperty ParseName; |
158 | | static ParseProperty ParseCSS1Selector; |
159 | | static ParseProperty ParseString; |
160 | | static ParseProperty ParseCharEnc; |
161 | | static ParseProperty ParseDocType; |
162 | | static ParseProperty ParseTabs; |
163 | | static ParseProperty ParsePickList; |
164 | | |
165 | | /*****************************************************************/ |
166 | | /* Ensure struct order is same order as tidyenum.h:TidyOptionId! */ |
167 | | /*****************************************************************/ |
168 | | static const TidyOptionImpl option_defs[] = |
169 | | { |
170 | | { TidyUnknownOption, IR, "unknown!", IN, 0, NULL, NULL }, |
171 | | { TidyAccessibilityCheckLevel, DG, "accessibility-check", IN, 0, ParsePickList, &accessPicks }, |
172 | | { TidyAltText, MR, "alt-text", ST, 0, ParseString, NULL }, |
173 | | { TidyAnchorAsName, MR, "anchor-as-name", BL, yes, ParsePickList, &boolPicks }, |
174 | | { TidyAsciiChars, ME, "ascii-chars", BL, no, ParsePickList, &boolPicks }, |
175 | | { TidyBlockTags, MT, "new-blocklevel-tags", ST, 0, ParseList, NULL }, |
176 | | { TidyBodyOnly, DD, "show-body-only", IN, no, ParsePickList, &autoBoolPicks }, |
177 | | { TidyBreakBeforeBR, PP, "break-before-br", BL, no, ParsePickList, &boolPicks }, |
178 | | { TidyCharEncoding, CE, "char-encoding", IN, UTF8, ParseCharEnc, &charEncPicks }, |
179 | | { TidyCoerceEndTags, MR, "coerce-endtags", BL, yes, ParsePickList, &boolPicks }, |
180 | | { TidyCSSPrefix, MR, "css-prefix", ST, 0, ParseCSS1Selector, NULL, "c" }, |
181 | | { TidyCustomTags, IR, "new-custom-tags", ST, 0, ParseList, NULL }, /* 20170309 - Issue #119 */ |
182 | | { TidyDecorateInferredUL, MX, "decorate-inferred-ul", BL, no, ParsePickList, &boolPicks }, |
183 | | { TidyDoctype, DT, "doctype", ST, TidyDoctypeAuto, ParseDocType, &doctypePicks }, |
184 | | #ifndef DOXYGEN_SHOULD_SKIP_THIS |
185 | | { TidyDoctypeMode, IR, "doctype-mode", IN, TidyDoctypeAuto, NULL, &doctypePicks }, |
186 | | #endif |
187 | | { TidyDropEmptyElems, MC, "drop-empty-elements", BL, yes, ParsePickList, &boolPicks }, |
188 | | { TidyDropEmptyParas, MC, "drop-empty-paras", BL, yes, ParsePickList, &boolPicks }, |
189 | | { TidyDropPropAttrs, MC, "drop-proprietary-attributes", BL, no, ParsePickList, &boolPicks }, |
190 | | { TidyDuplicateAttrs, MR, "repeated-attributes", IN, TidyKeepLast, ParsePickList, &repeatAttrPicks }, |
191 | | { TidyEmacs, DD, "gnu-emacs", BL, no, ParsePickList, &boolPicks }, |
192 | | #ifndef DOXYGEN_SHOULD_SKIP_THIS |
193 | | { TidyEmacsFile, IR, "gnu-emacs-file", ST, 0, ParseString, NULL }, |
194 | | #endif |
195 | | { TidyEmptyTags, MT, "new-empty-tags", ST, 0, ParseList, NULL }, |
196 | | { TidyEncloseBlockText, MR, "enclose-block-text", BL, no, ParsePickList, &boolPicks }, |
197 | | { TidyEncloseBodyText, MR, "enclose-text", BL, no, ParsePickList, &boolPicks }, |
198 | | { TidyErrFile, IO, "error-file", ST, 0, ParseString, NULL }, |
199 | | { TidyEscapeCdata, MX, "escape-cdata", BL, no, ParsePickList, &boolPicks }, |
200 | | { TidyEscapeScripts, MR, "escape-scripts", BL, yes, ParsePickList, &boolPicks }, /* 20160227 - Issue #348 */ |
201 | | { TidyFixBackslash, MR, "fix-backslash", BL, yes, ParsePickList, &boolPicks }, |
202 | | { TidyFixComments, MR, "fix-bad-comments", IN, TidyAutoState, ParsePickList, &autoBoolPicks }, |
203 | | { TidyFixUri, MR, "fix-uri", BL, yes, ParsePickList, &boolPicks }, |
204 | | { TidyForceOutput, DG, "force-output", BL, no, ParsePickList, &boolPicks }, |
205 | | { TidyGDocClean, MC, "gdoc", BL, no, ParsePickList, &boolPicks }, |
206 | | { TidyHideComments, MX, "hide-comments", BL, no, ParsePickList, &boolPicks }, |
207 | | { TidyHtmlOut, DT, "output-html", BL, no, ParsePickList, &boolPicks }, |
208 | | { TidyInCharEncoding, CE, "input-encoding", IN, UTF8, ParseCharEnc, &charEncPicks }, |
209 | | { TidyIndentAttributes, PP, "indent-attributes", BL, no, ParsePickList, &boolPicks }, |
210 | | { TidyIndentCdata, PP, "indent-cdata", BL, no, ParsePickList, &boolPicks }, |
211 | | { TidyIndentContent, PP, "indent", IN, TidyNoState, ParsePickList, &autoBoolPicks }, |
212 | | { TidyIndentSpaces, PP, "indent-spaces", IN, 2, ParseInt, NULL }, |
213 | | { TidyInlineTags, MT, "new-inline-tags", ST, 0, ParseList, NULL }, |
214 | | { TidyJoinClasses, MX, "join-classes", BL, no, ParsePickList, &boolPicks }, |
215 | | { TidyJoinStyles, MX, "join-styles", BL, yes, ParsePickList, &boolPicks }, |
216 | | { TidyKeepFileTimes, IO, "keep-time", BL, no, ParsePickList, &boolPicks }, |
217 | | { TidyKeepTabs, PP, "keep-tabs", BL, no, ParsePickList, &boolPicks }, /* 20171103 - Issue #403 */ |
218 | | { TidyLiteralAttribs, MR, "literal-attributes", BL, no, ParsePickList, &boolPicks }, |
219 | | { TidyLogicalEmphasis, MC, "logical-emphasis", BL, no, ParsePickList, &boolPicks }, |
220 | | { TidyLowerLiterals, MR, "lower-literals", BL, yes, ParsePickList, &boolPicks }, |
221 | | { TidyMakeBare, MC, "bare", BL, no, ParsePickList, &boolPicks }, |
222 | | { TidyMakeClean, MC, "clean", BL, no, ParsePickList, &boolPicks }, |
223 | | { TidyMark, PP, "tidy-mark", BL, yes, ParsePickList, &boolPicks }, |
224 | | { TidyMergeDivs, MC, "merge-divs", IN, TidyAutoState, ParsePickList, &autoBoolPicks }, |
225 | | { TidyMergeEmphasis, MX, "merge-emphasis", BL, yes, ParsePickList, &boolPicks }, |
226 | | { TidyMergeSpans, MC, "merge-spans", IN, TidyAutoState, ParsePickList, &autoBoolPicks }, |
227 | | { TidyMetaCharset, DT, "add-meta-charset", BL, no, ParsePickList, &boolPicks }, /* 20161004 - Issue #456 */ |
228 | | { TidyMuteReports, DD, "mute", ST, 0, ParseList, NULL }, |
229 | | { TidyMuteShow, DD, "mute-id", BL, no, ParsePickList, &boolPicks }, |
230 | | { TidyNCR, ME, "ncr", BL, yes, ParsePickList, &boolPicks }, |
231 | | { TidyNewline, CE, "newline", IN, DLF, ParsePickList, &newlinePicks }, |
232 | | { TidyNumEntities, ME, "numeric-entities", BL, no, ParsePickList, &boolPicks }, |
233 | | { TidyOmitOptionalTags, PP, "omit-optional-tags", BL, no, ParsePickList, &boolPicks }, |
234 | | { TidyOutCharEncoding, CE, "output-encoding", IN, UTF8, ParseCharEnc, &charEncPicks }, |
235 | | { TidyOutFile, IO, "output-file", ST, 0, ParseString, NULL }, |
236 | | { TidyOutputBOM, CE, "output-bom", IN, TidyAutoState, ParsePickList, &autoBoolPicks }, |
237 | | { TidyPPrintTabs, PP, "indent-with-tabs", BL, no, ParseTabs, &boolPicks }, /* 20150515 - Issue #108 */ |
238 | | { TidyPreserveEntities, ME, "preserve-entities", BL, no, ParsePickList, &boolPicks }, |
239 | | { TidyPreTags, MT, "new-pre-tags", ST, 0, ParseList, NULL }, |
240 | | { TidyPriorityAttributes, PP, "priority-attributes", ST, 0, ParseList, NULL }, |
241 | | { TidyPunctWrap, PP, "punctuation-wrap", BL, no, ParsePickList, &boolPicks }, |
242 | | { TidyQuiet, DD, "quiet", BL, no, ParsePickList, &boolPicks }, |
243 | | { TidyQuoteAmpersand, ME, "quote-ampersand", BL, yes, ParsePickList, &boolPicks }, |
244 | | { TidyQuoteMarks, ME, "quote-marks", BL, no, ParsePickList, &boolPicks }, |
245 | | { TidyQuoteNbsp, ME, "quote-nbsp", BL, yes, ParsePickList, &boolPicks }, |
246 | | { TidyReplaceColor, MX, "replace-color", BL, no, ParsePickList, &boolPicks }, |
247 | | { TidyShowErrors, DD, "show-errors", IN, 6, ParseInt, NULL }, |
248 | | { TidyShowFilename, DD, "show-filename", BL, no, ParsePickList, &boolPicks }, |
249 | | { TidyShowInfo, DD, "show-info", BL, yes, ParsePickList, &boolPicks }, |
250 | | { TidyShowMarkup, DD, "markup", BL, yes, ParsePickList, &boolPicks }, |
251 | | { TidyShowMetaChange, DG, "show-meta-change", BL, no, ParsePickList, &boolPicks }, /* 20170609 - Issue #456 */ |
252 | | { TidyShowWarnings, DD, "show-warnings", BL, yes, ParsePickList, &boolPicks }, |
253 | | { TidySkipNested, MR, "skip-nested", BL, yes, ParsePickList, &boolPicks }, /* 1642186 - Issue #65 */ |
254 | | { TidySortAttributes, PP, "sort-attributes", IN, TidySortAttrNone,ParsePickList, &sorterPicks }, |
255 | | { TidyStrictTagsAttr, MR, "strict-tags-attributes", BL, no, ParsePickList, &boolPicks }, /* 20160209 - Issue #350 */ |
256 | | { TidyStyleTags, MR, "fix-style-tags", BL, yes, ParsePickList, &boolPicks }, |
257 | | { TidyTabSize, PP, "tab-size", IN, 8, ParseInt, NULL }, |
258 | | { TidyUpperCaseAttrs, MR, "uppercase-attributes", IN, TidyUppercaseNo, ParsePickList, &attributeCasePicks }, |
259 | | { TidyUpperCaseTags, MR, "uppercase-tags", BL, no, ParsePickList, &boolPicks }, |
260 | | { TidyUseCustomTags, MR, "custom-tags", IN, TidyCustomNo, ParsePickList, &customTagsPicks }, /* 20170309 - Issue #119 */ |
261 | | { TidyVertSpace, PP, "vertical-space", IN, no, ParsePickList, &autoBoolPicks }, /* #228 - tri option */ |
262 | | { TidyWarnPropAttrs, DG, "warn-proprietary-attributes", BL, yes, ParsePickList, &boolPicks }, |
263 | | { TidyWord2000, MC, "word-2000", BL, no, ParsePickList, &boolPicks }, |
264 | | { TidyWrapAsp, PP, "wrap-asp", BL, yes, ParsePickList, &boolPicks }, |
265 | | { TidyWrapAttVals, PP, "wrap-attributes", BL, no, ParsePickList, &boolPicks }, |
266 | | { TidyWrapJste, PP, "wrap-jste", BL, yes, ParsePickList, &boolPicks }, |
267 | | { TidyWrapLen, PP, "wrap", IN, 68, ParseInt, NULL }, |
268 | | { TidyWrapPhp, PP, "wrap-php", BL, no, ParsePickList, &boolPicks }, |
269 | | { TidyWrapScriptlets, PP, "wrap-script-literals", BL, no, ParsePickList, &boolPicks }, |
270 | | { TidyWrapSection, PP, "wrap-sections", BL, yes, ParsePickList, &boolPicks }, |
271 | | { TidyWriteBack, IO, "write-back", BL, no, ParsePickList, &boolPicks }, |
272 | | { TidyXhtmlOut, DT, "output-xhtml", BL, no, ParsePickList, &boolPicks }, |
273 | | { TidyXmlDecl, DT, "add-xml-decl", BL, no, ParsePickList, &boolPicks }, |
274 | | { TidyXmlOut, DT, "output-xml", BL, no, ParsePickList, &boolPicks }, |
275 | | { TidyXmlPIs, MR, "assume-xml-procins", BL, no, ParsePickList, &boolPicks }, |
276 | | { TidyXmlSpace, DT, "add-xml-space", BL, no, ParsePickList, &boolPicks }, |
277 | | { TidyXmlTags, DT, "input-xml", BL, no, ParsePickList, &boolPicks }, |
278 | | { N_TIDY_OPTIONS, XX, NULL, XY, 0, NULL, NULL } |
279 | | }; |
280 | | |
281 | | |
282 | | /***************************************************************************** |
283 | | ** Deleted Options Configuration |
284 | | ** |
285 | | ** Keep track of options that have been removed from Tidy, so that we can |
286 | | ** suggests a replacement. When a deleted option is used, client programs |
287 | | ** will have the opportunity to consume the option first via the callback, |
288 | | ** and if not handled by the callback, will be handled by Tidy, generally |
289 | | ** by setting an alternate or new option, in `subDeprecatedOption()`. |
290 | | ******************************************************************************/ |
291 | | |
292 | | static const struct { |
293 | | ctmbstr name; /**< name of the deprecated option */ |
294 | | TidyOptionId replacementId; /**< Id of the replacement option, or 0 if none. */ |
295 | | } deprecatedOptions[] = { |
296 | | /* { "show-body-only", TidyBodyOnly }, */ |
297 | | { NULL } |
298 | | }; |
299 | | |
300 | | |
301 | | /***************************************************************************** |
302 | | ** Supporting Functions |
303 | | ******************************************************************************/ |
304 | | |
305 | | |
306 | | /* forward declarations */ |
307 | | static Bool GetPickListValue( ctmbstr value, PickListItems* pickList, uint *result ); |
308 | | |
309 | | |
310 | | void TY_(InitConfig)( TidyDocImpl* doc ) |
311 | 69 | { |
312 | 69 | TidyClearMemory( &doc->config, sizeof(TidyConfigImpl) ); |
313 | 69 | TY_(ResetConfigToDefault)( doc ); |
314 | 69 | } |
315 | | |
316 | | |
317 | | void TY_(FreeConfig)( TidyDocImpl* doc ) |
318 | 69 | { |
319 | 69 | doc->pConfigChangeCallback = NULL; |
320 | 69 | TY_(ResetConfigToDefault)( doc ); |
321 | 69 | TY_(TakeConfigSnapshot)( doc ); |
322 | 69 | } |
323 | | |
324 | | |
325 | | /* Should only be called by options set by name |
326 | | ** thus, it is cheaper to do a few scans than set |
327 | | ** up every option in a hash table. |
328 | | */ |
329 | | const TidyOptionImpl* TY_(lookupOption)( ctmbstr s ) |
330 | 0 | { |
331 | 0 | const TidyOptionImpl* np = option_defs; |
332 | 0 | for ( /**/; np < option_defs + N_TIDY_OPTIONS; ++np ) |
333 | 0 | { |
334 | 0 | if ( TY_(tmbstrcasecmp)(s, np->name) == 0 ) |
335 | 0 | return np; |
336 | 0 | } |
337 | 0 | return NULL; |
338 | 0 | } |
339 | | |
340 | | |
341 | | const TidyOptionImpl* TY_(getOption)( TidyOptionId optId ) |
342 | 0 | { |
343 | 0 | if ( optId < N_TIDY_OPTIONS ) |
344 | 0 | return option_defs + optId; |
345 | 0 | return NULL; |
346 | 0 | } |
347 | | |
348 | | const Bool TY_(getOptionIsList)( TidyOptionId optId ) |
349 | 0 | { |
350 | 0 | const TidyOptionImpl* option = TY_(getOption)( optId ); |
351 | 0 | return option->parser == ParseList; |
352 | 0 | } |
353 | | |
354 | | static Bool OptionChangedValuesDiffer( ctmbstr a, ctmbstr b ) |
355 | 0 | { |
356 | 0 | if ( a != b ) |
357 | 0 | { |
358 | 0 | if ( a == NULL || b == NULL ) /* can't both be null at this point. */ |
359 | 0 | return yes; |
360 | 0 | else |
361 | 0 | return TY_(tmbstrcmp)( a, b ) != 0; |
362 | 0 | } |
363 | | |
364 | 0 | return no; |
365 | 0 | } |
366 | | |
367 | | static void PerformOptionChangedCallback( TidyDocImpl* doc, const TidyOptionImpl* option ) |
368 | 0 | { |
369 | 0 | if ( doc->pConfigChangeCallback ) |
370 | 0 | { |
371 | 0 | TidyDoc tdoc = tidyImplToDoc( doc ); |
372 | 0 | TidyOption opt = tidyImplToOption( option ); |
373 | 0 | doc->pConfigChangeCallback( tdoc, opt ); |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | | static void FreeOptionValue( TidyDocImpl* doc, const TidyOptionImpl* option, TidyOptionValue* value ) |
378 | 28.7k | { |
379 | 28.7k | if ( option->type == TidyString && value->p && value->p != option->pdflt ) |
380 | 28.7k | TidyDocFree( doc, value->p ); |
381 | 28.7k | } |
382 | | |
383 | | |
384 | | static void CopyOptionValue( TidyDocImpl* doc, const TidyOptionImpl* option, |
385 | | TidyOptionValue* oldval, const TidyOptionValue* newval ) |
386 | 28.7k | { |
387 | 28.7k | Bool fire_callback = no; |
388 | 28.7k | assert( oldval != NULL ); |
389 | | |
390 | | /* Compare the old and new values. */ |
391 | 28.7k | if ( doc->pConfigChangeCallback ) |
392 | 0 | { |
393 | 0 | if ( option->type == TidyString ) |
394 | 0 | fire_callback = OptionChangedValuesDiffer( oldval->p, newval->p ); |
395 | 0 | else |
396 | 0 | fire_callback = oldval->v != newval->v; |
397 | 0 | } |
398 | | |
399 | 28.7k | FreeOptionValue( doc, option, oldval ); |
400 | | |
401 | 28.7k | if ( option->type == TidyString ) |
402 | 3.58k | { |
403 | 3.58k | if ( newval->p && newval->p != option->pdflt ) |
404 | 0 | oldval->p = TY_(tmbstrdup)( doc->allocator, newval->p ); |
405 | 3.58k | else |
406 | 3.58k | oldval->p = newval->p; |
407 | 3.58k | } |
408 | 25.1k | else |
409 | 25.1k | oldval->v = newval->v; |
410 | | |
411 | 28.7k | if ( fire_callback ) |
412 | 0 | PerformOptionChangedCallback( doc, option ); |
413 | 28.7k | } |
414 | | |
415 | | |
416 | | static Bool SetOptionValue( TidyDocImpl* doc, TidyOptionId optId, ctmbstr val ) |
417 | 0 | { |
418 | 0 | const TidyOptionImpl* option = &option_defs[ optId ]; |
419 | 0 | Bool fire_callback = no; |
420 | 0 | Bool status = ( optId < N_TIDY_OPTIONS ); |
421 | |
|
422 | 0 | if ( status ) |
423 | 0 | { |
424 | 0 | assert( option->id == optId && option->type == TidyString ); |
425 | | |
426 | | /* Compare the old and new values. */ |
427 | 0 | if ( doc->pConfigChangeCallback ) |
428 | 0 | { |
429 | 0 | TidyOptionValue* oldval = &(doc->config.value[ optId ]); |
430 | 0 | fire_callback = OptionChangedValuesDiffer( oldval->p, val ); |
431 | 0 | } |
432 | |
|
433 | 0 | FreeOptionValue( doc, option, &doc->config.value[ optId ] ); |
434 | 0 | if ( TY_(tmbstrlen)(val)) /* Issue #218 - ONLY if it has LENGTH! */ |
435 | 0 | doc->config.value[ optId ].p = TY_(tmbstrdup)( doc->allocator, val ); |
436 | 0 | else |
437 | 0 | doc->config.value[ optId ].p = 0; /* should already be zero, but to be sure... */ |
438 | 0 | } |
439 | | |
440 | 0 | if ( fire_callback ) |
441 | 0 | PerformOptionChangedCallback( doc, option ); |
442 | |
|
443 | 0 | return status; |
444 | 0 | } |
445 | | |
446 | | |
447 | | ctmbstr TY_(GetPickListLabelForPick)( TidyOptionId optId, uint pick ) |
448 | 0 | { |
449 | 0 | const TidyOptionImpl* option = TY_(getOption)( optId ); |
450 | |
|
451 | 0 | if ( option && option->pickList ) |
452 | 0 | { |
453 | 0 | uint ix = 0; |
454 | 0 | const PickListItem *item = NULL; |
455 | | |
456 | | /* Loop through the picklist until index matches the value. */ |
457 | 0 | while ( (item = &(*option->pickList)[ ix ]) && item->label && ix<pick ) |
458 | 0 | { |
459 | 0 | ++ix; |
460 | 0 | } |
461 | 0 | if ( ix==pick && item->label ) |
462 | 0 | return item->label; |
463 | 0 | } |
464 | | |
465 | 0 | return NULL; |
466 | 0 | } |
467 | | |
468 | | |
469 | | static void SetOptionInteger( TidyDocImpl* doc, TidyOptionId optId, ulong val ) |
470 | 1.67k | { |
471 | 1.67k | const TidyOptionImpl* option = &option_defs[ optId ]; |
472 | 1.67k | ulong* optVal = &(doc->config.value[ optId ].v); |
473 | 1.67k | Bool fire_callback = doc->pConfigChangeCallback && *optVal != val; |
474 | | |
475 | 1.67k | *optVal = val; |
476 | | |
477 | 1.67k | if ( fire_callback ) |
478 | 0 | PerformOptionChangedCallback( doc, option ); |
479 | 1.67k | } |
480 | | |
481 | | Bool TY_(SetOptionInt)( TidyDocImpl* doc, TidyOptionId optId, ulong val ) |
482 | 776 | { |
483 | 776 | Bool status = ( optId < N_TIDY_OPTIONS ); |
484 | 776 | if ( status ) |
485 | 776 | { |
486 | 776 | assert( option_defs[ optId ].type == TidyInteger ); |
487 | 776 | SetOptionInteger( doc, optId, val ); |
488 | 776 | } |
489 | 776 | return status; |
490 | 776 | } |
491 | | |
492 | | |
493 | | Bool TY_(SetOptionBool)( TidyDocImpl* doc, TidyOptionId optId, Bool val ) |
494 | 897 | { |
495 | 897 | Bool status = ( optId < N_TIDY_OPTIONS ); |
496 | 897 | if ( status ) |
497 | 897 | { |
498 | 897 | assert( option_defs[ optId ].type == TidyBoolean ); |
499 | 897 | SetOptionInteger( doc, optId, (ulong)val ); |
500 | 897 | } |
501 | 897 | return status; |
502 | 897 | } |
503 | | |
504 | | |
505 | | static void GetOptionDefault( const TidyOptionImpl* option, |
506 | | TidyOptionValue* dflt ) |
507 | 14.3k | { |
508 | 14.3k | if ( option->type == TidyString ) |
509 | 1.79k | dflt->p = (char*)option->pdflt; |
510 | 12.5k | else |
511 | 12.5k | dflt->v = option->dflt; |
512 | 14.3k | } |
513 | | |
514 | | |
515 | | static Bool OptionValueEqDefault( const TidyOptionImpl* option, |
516 | | const TidyOptionValue* val ) |
517 | 0 | { |
518 | 0 | return ( option->type == TidyString ) ? |
519 | 0 | val->p == option->pdflt : |
520 | 0 | val->v == option->dflt; |
521 | 0 | } |
522 | | |
523 | | |
524 | | Bool TY_(ResetOptionToDefault)( TidyDocImpl* doc, TidyOptionId optId ) |
525 | 0 | { |
526 | 0 | Bool status = ( optId > 0 && optId < N_TIDY_OPTIONS ); |
527 | 0 | if ( status ) |
528 | 0 | { |
529 | 0 | TidyOptionValue dflt; |
530 | 0 | const TidyOptionImpl* option = option_defs + optId; |
531 | 0 | TidyOptionValue* value = &doc->config.value[ optId ]; |
532 | 0 | assert( optId == option->id ); |
533 | 0 | GetOptionDefault( option, &dflt ); |
534 | 0 | CopyOptionValue( doc, option, value, &dflt ); |
535 | 0 | } |
536 | 0 | return status; |
537 | 0 | } |
538 | | |
539 | | |
540 | | static void ReparseTagType( TidyDocImpl* doc, TidyOptionId optId ) |
541 | 0 | { |
542 | 0 | ctmbstr tagdecl = cfgStr( doc, optId ); |
543 | 0 | tmbstr dupdecl = TY_(tmbstrdup)( doc->allocator, tagdecl ); |
544 | 0 | TY_(ParseConfigValue)( doc, optId, dupdecl ); |
545 | 0 | TidyDocFree( doc, dupdecl ); |
546 | 0 | } |
547 | | |
548 | | |
549 | | static Bool OptionValueIdentical( const TidyOptionImpl* option, |
550 | | const TidyOptionValue* val1, |
551 | | const TidyOptionValue* val2 ) |
552 | 0 | { |
553 | 0 | if ( option->type == TidyString ) |
554 | 0 | { |
555 | 0 | if ( val1->p == val2->p ) |
556 | 0 | return yes; |
557 | 0 | if ( !val1->p || !val2->p ) |
558 | 0 | return no; |
559 | 0 | return TY_(tmbstrcmp)( val1->p, val2->p ) == 0; |
560 | 0 | } |
561 | 0 | else |
562 | 0 | return val1->v == val2->v; |
563 | 0 | } |
564 | | |
565 | | |
566 | | static Bool NeedReparseTagDecls( TidyDocImpl* doc, |
567 | | const TidyOptionValue* current, |
568 | | const TidyOptionValue* new, |
569 | | uint *changedUserTags ) |
570 | 0 | { |
571 | 0 | Bool ret = no; |
572 | 0 | uint ixVal; |
573 | 0 | const TidyOptionImpl* option = option_defs; |
574 | 0 | *changedUserTags = tagtype_null; |
575 | |
|
576 | 0 | for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal ) |
577 | 0 | { |
578 | 0 | assert( ixVal == (uint) option->id ); |
579 | 0 | switch (option->id) |
580 | 0 | { |
581 | 0 | #define TEST_USERTAGS(USERTAGOPTION,USERTAGTYPE) \ |
582 | 0 | case USERTAGOPTION: \ |
583 | 0 | if (!OptionValueIdentical(option,¤t[ixVal],&new[ixVal])) \ |
584 | 0 | { \ |
585 | 0 | *changedUserTags |= USERTAGTYPE; \ |
586 | 0 | ret = yes; \ |
587 | 0 | } \ |
588 | 0 | break |
589 | 0 | TEST_USERTAGS(TidyInlineTags,tagtype_inline); |
590 | 0 | TEST_USERTAGS(TidyBlockTags,tagtype_block); |
591 | 0 | TEST_USERTAGS(TidyEmptyTags,tagtype_empty); |
592 | 0 | TEST_USERTAGS(TidyPreTags,tagtype_pre); |
593 | 0 | default: |
594 | 0 | break; |
595 | 0 | } |
596 | 0 | } |
597 | 0 | return ret; |
598 | 0 | } |
599 | | |
600 | | |
601 | | static void ReparseTagDecls( TidyDocImpl* doc, uint changedUserTags ) |
602 | 0 | { |
603 | 0 | #define REPARSE_USERTAGS(USERTAGOPTION,USERTAGTYPE) \ |
604 | 0 | if ( changedUserTags & USERTAGTYPE ) \ |
605 | 0 | { \ |
606 | 0 | TY_(FreeDeclaredTags)( doc, USERTAGTYPE ); \ |
607 | 0 | ReparseTagType( doc, USERTAGOPTION ); \ |
608 | 0 | } |
609 | 0 | REPARSE_USERTAGS(TidyInlineTags,tagtype_inline); |
610 | 0 | REPARSE_USERTAGS(TidyBlockTags,tagtype_block); |
611 | 0 | REPARSE_USERTAGS(TidyEmptyTags,tagtype_empty); |
612 | 0 | REPARSE_USERTAGS(TidyPreTags,tagtype_pre); |
613 | 0 | } |
614 | | |
615 | | |
616 | | /* Returns the option id of the replacement Tidy option for optName. Because |
617 | | ** an option might not have a replacement (0, TidyUnknownOption), a return |
618 | | ** value of N_TIDY_OPTIONS indicates an error, i.e., that the option isn't |
619 | | ** in the deprecated list. |
620 | | */ |
621 | | static TidyOptionId getOptionReplacement( ctmbstr optName ) |
622 | 0 | { |
623 | 0 | uint i = 0; |
624 | 0 | ctmbstr testName; |
625 | 0 | while ( (testName = deprecatedOptions[i].name) ) |
626 | 0 | { |
627 | 0 | if ( TY_(tmbstrcasecmp)( optName, testName ) == 0 ) |
628 | 0 | return deprecatedOptions[i].replacementId; |
629 | | |
630 | 0 | i++; |
631 | 0 | } |
632 | 0 | return N_TIDY_OPTIONS; |
633 | 0 | } |
634 | | |
635 | | |
636 | | /* Indicates whether or not optName is deprecated */ |
637 | | static Bool isOptionDeprecated( ctmbstr optName ) |
638 | 0 | { |
639 | 0 | return getOptionReplacement( optName ) != N_TIDY_OPTIONS; |
640 | 0 | } |
641 | | |
642 | | |
643 | | /* Substitute the new option for the deprecated one. */ |
644 | | static Bool subDeprecatedOption( TidyDocImpl* doc, ctmbstr oldName, ctmbstr oldValue) |
645 | 0 | { |
646 | 0 | TidyOptionId newOptId = getOptionReplacement( oldName ); |
647 | 0 | ctmbstr newName = TY_(getOption)( newOptId )->name; |
648 | 0 | TidyDoc tdoc = tidyImplToDoc( doc ); |
649 | |
|
650 | 0 | assert( isOptionDeprecated(oldName)); |
651 | | |
652 | 0 | if ( newOptId == TidyUnknownOption ) |
653 | 0 | { |
654 | 0 | TY_(Report)( doc, NULL, NULL, OPTION_REMOVED, oldName ); |
655 | 0 | return no; |
656 | 0 | } |
657 | | |
658 | | /********************/ |
659 | | /* `show-body-only` */ |
660 | | /********************/ |
661 | 0 | if ( TY_(tmbstrcasecmp)( oldName, "show-body-only" ) == 0 ) |
662 | 0 | { |
663 | 0 | uint value; |
664 | | |
665 | | /* `show-body-only` used to use the autoBoolPicks */ |
666 | 0 | if ( GetPickListValue( oldValue, &autoBoolPicks, &value ) ) |
667 | 0 | { |
668 | 0 | if ( value == TidyNoState ) |
669 | 0 | { |
670 | 0 | TY_(SetOptionInt)( doc, newOptId, value ); |
671 | 0 | TY_(Report)( doc, NULL, NULL, OPTION_REMOVED_UNAPPLIED, oldName, newName ); |
672 | 0 | } |
673 | 0 | else |
674 | 0 | { |
675 | 0 | ctmbstr val; |
676 | 0 | TY_(SetOptionInt)( doc, newOptId, value ); |
677 | 0 | val = tidyOptGetCurrPick( tdoc, newOptId ); |
678 | 0 | TY_(Report)( doc, NULL, NULL, OPTION_REMOVED_APPLIED, oldName, newName, val ); |
679 | 0 | } |
680 | 0 | } |
681 | 0 | else |
682 | 0 | { |
683 | 0 | TY_(ReportBadArgument)(doc, oldName); |
684 | 0 | } |
685 | 0 | return yes; |
686 | 0 | } |
687 | | |
688 | 0 | return no; |
689 | 0 | } |
690 | | |
691 | | |
692 | | void TY_(ResetConfigToDefault)( TidyDocImpl* doc ) |
693 | 138 | { |
694 | 138 | uint ixVal; |
695 | 138 | const TidyOptionImpl* option = option_defs; |
696 | 138 | TidyOptionValue* value = &doc->config.value[ 0 ]; |
697 | 14.4k | for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal ) |
698 | 14.3k | { |
699 | 14.3k | TidyOptionValue dflt; |
700 | 14.3k | assert( ixVal == (uint) option->id ); |
701 | 14.3k | GetOptionDefault( option, &dflt ); |
702 | 14.3k | CopyOptionValue( doc, option, &value[ixVal], &dflt ); |
703 | 14.3k | } |
704 | 138 | TY_(FreeDeclaredTags)( doc, tagtype_null ); |
705 | 138 | } |
706 | | |
707 | | |
708 | | void TY_(TakeConfigSnapshot)( TidyDocImpl* doc ) |
709 | 138 | { |
710 | 138 | uint ixVal; |
711 | 138 | const TidyOptionImpl* option = option_defs; |
712 | 138 | const TidyOptionValue* value = &doc->config.value[ 0 ]; |
713 | 138 | TidyOptionValue* snap = &doc->config.snapshot[ 0 ]; |
714 | | |
715 | | /* @jsd: do NOT mess with user-specified settings until |
716 | | * absolutely necessary, and ensure that we can |
717 | | * can restore them immediately after the need. |
718 | | */ |
719 | | // TY_(AdjustConfig)( doc ); /* Make sure it's consistent */ |
720 | 14.4k | for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal ) |
721 | 14.3k | { |
722 | 14.3k | assert( ixVal == (uint) option->id ); |
723 | 14.3k | CopyOptionValue( doc, option, &snap[ixVal], &value[ixVal] ); |
724 | 14.3k | } |
725 | 138 | } |
726 | | |
727 | | |
728 | | void TY_(ResetConfigToSnapshot)( TidyDocImpl* doc ) |
729 | 0 | { |
730 | 0 | uint ixVal; |
731 | 0 | const TidyOptionImpl* option = option_defs; |
732 | 0 | TidyOptionValue* value = &doc->config.value[ 0 ]; |
733 | 0 | const TidyOptionValue* snap = &doc->config.snapshot[ 0 ]; |
734 | 0 | uint changedUserTags; |
735 | 0 | Bool needReparseTagsDecls = NeedReparseTagDecls( doc, value, snap, |
736 | 0 | &changedUserTags ); |
737 | | |
738 | 0 | for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal ) |
739 | 0 | { |
740 | 0 | assert( ixVal == (uint) option->id ); |
741 | 0 | CopyOptionValue( doc, option, &value[ixVal], &snap[ixVal] ); |
742 | 0 | } |
743 | 0 | if ( needReparseTagsDecls ) |
744 | 0 | ReparseTagDecls( doc, changedUserTags ); |
745 | 0 | } |
746 | | |
747 | | |
748 | | void TY_(CopyConfig)( TidyDocImpl* docTo, TidyDocImpl* docFrom ) |
749 | 0 | { |
750 | 0 | if ( docTo != docFrom ) |
751 | 0 | { |
752 | 0 | uint ixVal; |
753 | 0 | const TidyOptionImpl* option = option_defs; |
754 | 0 | const TidyOptionValue* from = &docFrom->config.value[ 0 ]; |
755 | 0 | TidyOptionValue* to = &docTo->config.value[ 0 ]; |
756 | 0 | uint changedUserTags; |
757 | 0 | Bool needReparseTagsDecls = NeedReparseTagDecls( docTo, to, from, |
758 | 0 | &changedUserTags ); |
759 | |
|
760 | 0 | TY_(TakeConfigSnapshot)( docTo ); |
761 | 0 | for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal ) |
762 | 0 | { |
763 | 0 | assert( ixVal == (uint) option->id ); |
764 | 0 | CopyOptionValue( docTo, option, &to[ixVal], &from[ixVal] ); |
765 | 0 | } |
766 | 0 | if ( needReparseTagsDecls ) |
767 | 0 | ReparseTagDecls( docTo, changedUserTags ); |
768 | | |
769 | | /* @jsd: do NOT mess with user-specified settings until |
770 | | * absolutely necessary, and ensure that we can |
771 | | * can restore them immediately after the need. |
772 | | */ |
773 | | // TY_(AdjustConfig)( docTo ); /* Make sure it's consistent */ |
774 | 0 | } |
775 | 0 | } |
776 | | |
777 | | |
778 | | #ifdef _DEBUG |
779 | | |
780 | | /* Debug accessor functions will be type-safe and assert option type match */ |
781 | | ulong TY_(_cfgGet)( TidyDocImpl* doc, TidyOptionId optId ) |
782 | | { |
783 | | assert( optId < N_TIDY_OPTIONS ); |
784 | | return doc->config.value[ optId ].v; |
785 | | } |
786 | | |
787 | | Bool TY_(_cfgGetBool)( TidyDocImpl* doc, TidyOptionId optId ) |
788 | | { |
789 | | ulong val = TY_(_cfgGet)( doc, optId ); |
790 | | const TidyOptionImpl* opt = &option_defs[ optId ]; |
791 | | assert( opt && opt->type == TidyBoolean ); |
792 | | return (Bool) val; |
793 | | } |
794 | | |
795 | | TidyTriState TY_(_cfgGetAutoBool)( TidyDocImpl* doc, TidyOptionId optId ) |
796 | | { |
797 | | ulong val = TY_(_cfgGet)( doc, optId ); |
798 | | const TidyOptionImpl* opt = &option_defs[ optId ]; |
799 | | assert( opt && opt->type == TidyInteger |
800 | | && opt->parser == ParsePickList ); |
801 | | return (TidyTriState) val; |
802 | | } |
803 | | |
804 | | ctmbstr TY_(_cfgGetString)( TidyDocImpl* doc, TidyOptionId optId ) |
805 | | { |
806 | | const TidyOptionImpl* opt; |
807 | | |
808 | | assert( optId < N_TIDY_OPTIONS ); |
809 | | opt = &option_defs[ optId ]; |
810 | | assert( opt && opt->type == TidyString ); |
811 | | return doc->config.value[ optId ].p; |
812 | | } |
813 | | #endif |
814 | | |
815 | | |
816 | | static tchar GetC( TidyConfigImpl* config ) |
817 | 0 | { |
818 | 0 | if ( config->cfgIn ) |
819 | 0 | return TY_(ReadChar)( config->cfgIn ); |
820 | 0 | return EndOfStream; |
821 | 0 | } |
822 | | |
823 | | |
824 | | static tchar FirstChar( TidyConfigImpl* config ) |
825 | 0 | { |
826 | 0 | config->c = GetC( config ); |
827 | 0 | return config->c; |
828 | 0 | } |
829 | | |
830 | | |
831 | | static tchar AdvanceChar( TidyConfigImpl* config ) |
832 | 0 | { |
833 | 0 | if ( config->c != EndOfStream ) |
834 | 0 | config->c = GetC( config ); |
835 | 0 | return config->c; |
836 | 0 | } |
837 | | |
838 | | |
839 | | static tchar SkipWhite( TidyConfigImpl* config ) |
840 | 0 | { |
841 | 0 | while ( TY_(IsWhite)(config->c) && !TY_(IsNewline)(config->c) ) |
842 | 0 | config->c = GetC( config ); |
843 | 0 | return config->c; |
844 | 0 | } |
845 | | |
846 | | |
847 | | /* skip over line continuations to start of next property */ |
848 | | static uint NextProperty( TidyConfigImpl* config ) |
849 | 0 | { |
850 | 0 | do |
851 | 0 | { |
852 | | /* skip to end of line */ |
853 | 0 | while ( config->c != '\n' && config->c != '\r' && config->c != EndOfStream ) |
854 | 0 | config->c = GetC( config ); |
855 | | |
856 | | /* treat \r\n \r or \n as line ends */ |
857 | 0 | if ( config->c == '\r' ) |
858 | 0 | config->c = GetC( config ); |
859 | |
|
860 | 0 | if ( config->c == '\n' ) |
861 | 0 | config->c = GetC( config ); |
862 | 0 | } |
863 | 0 | while ( TY_(IsWhite)(config->c) ); /* line continuation? */ |
864 | |
|
865 | 0 | return config->c; |
866 | 0 | } |
867 | | |
868 | | |
869 | | /* |
870 | | Todd Lewis contributed this code for expanding ~/foo or ~your/foo according |
871 | | to $HOME and your user name. This will work partially on any system which |
872 | | defines $HOME. Support for ~user/foo will work on systems that support |
873 | | getpwnam(userid), namely Unix/Linux. |
874 | | */ |
875 | | static ctmbstr ExpandTilde( TidyDocImpl* doc, ctmbstr filename ) |
876 | 0 | { |
877 | 0 | char *home_dir = NULL; |
878 | |
|
879 | 0 | if ( !filename ) |
880 | 0 | return NULL; |
881 | | |
882 | 0 | if ( filename[0] != '~' ) |
883 | 0 | return filename; |
884 | | |
885 | 0 | if (filename[1] == '/') |
886 | 0 | { |
887 | 0 | home_dir = getenv("HOME"); |
888 | 0 | if (home_dir) { |
889 | 0 | ++filename; |
890 | 0 | } |
891 | | #ifdef _WIN32 |
892 | | else if (strlen(filename) >= 3) { /* at least '~/+1' */ |
893 | | /* no HOME env in Windows - got for HOMEDRIVE=C: HOMEPATH=\Users\user */ |
894 | | char * hd = getenv("HOMEDRIVE"); |
895 | | char * hp = getenv("HOMEPATH"); |
896 | | if (hd && hp) { |
897 | | ctmbstr s = TidyDocAlloc(doc, _MAX_PATH); |
898 | | strcpy(s, hd); |
899 | | strcat(s, hp); |
900 | | strcat(s, "\\"); |
901 | | strcat(s, &filename[2]); |
902 | | return s; |
903 | | } |
904 | | |
905 | | } |
906 | | #endif /* _WIN32 */ |
907 | |
|
908 | 0 | } |
909 | 0 | #ifdef SUPPORT_GETPWNAM |
910 | 0 | else |
911 | 0 | { |
912 | 0 | struct passwd *passwd = NULL; |
913 | 0 | ctmbstr s = filename + 1; |
914 | 0 | tmbstr t; |
915 | |
|
916 | 0 | while ( *s && *s != '/' ) |
917 | 0 | s++; |
918 | |
|
919 | 0 | if ( (t = TidyDocAlloc(doc, s - filename)) ) |
920 | 0 | { |
921 | 0 | memcpy(t, filename+1, s-filename-1); |
922 | 0 | t[s-filename-1] = 0; |
923 | |
|
924 | 0 | passwd = getpwnam(t); |
925 | |
|
926 | 0 | TidyDocFree(doc, t); |
927 | 0 | } |
928 | |
|
929 | 0 | if ( passwd ) |
930 | 0 | { |
931 | 0 | filename = s; |
932 | 0 | home_dir = passwd->pw_dir; |
933 | 0 | } |
934 | 0 | } |
935 | 0 | #endif /* SUPPORT_GETPWNAM */ |
936 | |
|
937 | 0 | if ( home_dir ) |
938 | 0 | { |
939 | 0 | uint len = TY_(tmbstrlen)(filename) + TY_(tmbstrlen)(home_dir) + 1; |
940 | 0 | tmbstr p = (tmbstr)TidyDocAlloc( doc, len ); |
941 | 0 | TY_(tmbstrcpy)( p, home_dir ); |
942 | 0 | TY_(tmbstrcat)( p, filename ); |
943 | 0 | return (ctmbstr) p; |
944 | 0 | } |
945 | 0 | return (ctmbstr) filename; |
946 | 0 | } |
947 | | |
948 | | |
949 | | Bool TIDY_CALL tidyFileExists( TidyDoc tdoc, ctmbstr filename ) |
950 | 0 | { |
951 | 0 | TidyDocImpl* doc = tidyDocToImpl( tdoc ); |
952 | 0 | ctmbstr fname = (tmbstr) ExpandTilde( doc, filename ); |
953 | 0 | #ifndef NO_ACCESS_SUPPORT |
954 | 0 | Bool exists = ( access(fname, 0) == 0 ); |
955 | | #else |
956 | | Bool exists; |
957 | | /* at present */ |
958 | | FILE* fin = fopen(fname, "r"); |
959 | | if (fin != NULL) |
960 | | fclose(fin); |
961 | | exists = ( fin != NULL ); |
962 | | #endif |
963 | 0 | if ( fname != filename ) |
964 | 0 | TidyDocFree( doc, (tmbstr) fname ); |
965 | 0 | return exists; |
966 | 0 | } |
967 | | |
968 | | |
969 | | int TY_(ParseConfigFile)( TidyDocImpl* doc, ctmbstr file ) |
970 | 0 | { |
971 | 0 | return TY_(ParseConfigFileEnc)( doc, file, "ascii" ); |
972 | 0 | } |
973 | | |
974 | | /* open the file and parse its contents |
975 | | */ |
976 | | int TY_(ParseConfigFileEnc)( TidyDocImpl* doc, ctmbstr file, ctmbstr charenc ) |
977 | 0 | { |
978 | 0 | enum { tidy_max_name = 64 }; |
979 | 0 | uint opterrs = doc->optionErrors; |
980 | 0 | tmbstr fname = (tmbstr) ExpandTilde( doc, file ); |
981 | 0 | TidyConfigImpl* cfg = &doc->config; |
982 | 0 | FILE* fin = fopen( fname, "r" ); |
983 | 0 | int enc = TY_(CharEncodingId)( doc, charenc ); |
984 | |
|
985 | 0 | if ( fin == NULL || enc < 0 ) |
986 | 0 | { |
987 | 0 | TY_(ReportFileError)( doc, fname, FILE_CANT_OPEN_CFG ); |
988 | 0 | return -1; |
989 | 0 | } |
990 | 0 | else |
991 | 0 | { |
992 | 0 | tchar c; |
993 | 0 | cfg->cfgIn = TY_(FileInput)( doc, fin, enc ); |
994 | 0 | c = FirstChar( cfg ); |
995 | | |
996 | 0 | for ( c = SkipWhite(cfg); c != EndOfStream; c = NextProperty(cfg) ) |
997 | 0 | { |
998 | 0 | uint ix = 0; |
999 | 0 | tmbchar name[ tidy_max_name ] = {0}; |
1000 | | |
1001 | | /* // or # start a comment */ |
1002 | 0 | if ( c == '/' || c == '#' ) |
1003 | 0 | continue; |
1004 | | |
1005 | 0 | while ( ix < sizeof(name)-1 && c != '\n' && c != EndOfStream && c != ':' ) |
1006 | 0 | { |
1007 | 0 | name[ ix++ ] = (tmbchar) c; /* Option names all ASCII */ |
1008 | 0 | c = AdvanceChar( cfg ); |
1009 | 0 | } |
1010 | |
|
1011 | 0 | if ( c == ':' ) |
1012 | 0 | { |
1013 | 0 | Bool isDeprecated = isOptionDeprecated( name ); |
1014 | 0 | const TidyOptionImpl* option = TY_(lookupOption)( name ); |
1015 | 0 | c = AdvanceChar( cfg ); |
1016 | 0 | if ( option && !isDeprecated ) |
1017 | 0 | option->parser( doc, option ); |
1018 | 0 | else |
1019 | 0 | { |
1020 | 0 | if ( (NULL != doc->pOptCallback) || (NULL != doc->pConfigCallback) || isDeprecated ) |
1021 | 0 | { |
1022 | 0 | TidyConfigImpl* cfg = &doc->config; |
1023 | 0 | tmbchar buf[8192]; |
1024 | 0 | uint i = 0; |
1025 | 0 | tchar delim = 0; |
1026 | 0 | Bool waswhite = yes; |
1027 | 0 | Bool response = no; |
1028 | |
|
1029 | 0 | tchar c = SkipWhite( cfg ); |
1030 | |
|
1031 | 0 | if ( c == '"' || c == '\'' ) |
1032 | 0 | { |
1033 | 0 | delim = c; |
1034 | 0 | c = AdvanceChar( cfg ); |
1035 | 0 | } |
1036 | |
|
1037 | 0 | while ( i < sizeof(buf)-2 && c != EndOfStream && c != '\r' && c != '\n' ) |
1038 | 0 | { |
1039 | 0 | if ( delim && c == delim ) |
1040 | 0 | break; |
1041 | | |
1042 | 0 | if ( TY_(IsWhite)(c) ) |
1043 | 0 | { |
1044 | 0 | if ( waswhite ) |
1045 | 0 | { |
1046 | 0 | c = AdvanceChar( cfg ); |
1047 | 0 | continue; |
1048 | 0 | } |
1049 | 0 | c = ' '; |
1050 | 0 | } |
1051 | 0 | else |
1052 | 0 | waswhite = no; |
1053 | | |
1054 | 0 | buf[i++] = (tmbchar) c; |
1055 | 0 | c = AdvanceChar( cfg ); |
1056 | 0 | } |
1057 | 0 | buf[i] = '\0'; |
1058 | | |
1059 | 0 | if ( doc->pOptCallback ) |
1060 | 0 | response = (*doc->pOptCallback)( name, buf ); |
1061 | |
|
1062 | 0 | if ( doc->pConfigCallback ) |
1063 | 0 | response = response || (*doc->pConfigCallback)( tidyImplToDoc(doc), name, buf ); |
1064 | |
|
1065 | 0 | if ( !response && isDeprecated ) |
1066 | 0 | response = subDeprecatedOption( doc, name, buf); |
1067 | |
|
1068 | 0 | if ( response == no ) |
1069 | 0 | TY_(ReportUnknownOption)( doc, name ); |
1070 | 0 | } |
1071 | 0 | else |
1072 | 0 | TY_(ReportUnknownOption)( doc, name ); |
1073 | 0 | } |
1074 | 0 | } |
1075 | 0 | } |
1076 | |
|
1077 | 0 | TY_(freeFileSource)(&cfg->cfgIn->source, yes); |
1078 | 0 | TY_(freeStreamIn)( cfg->cfgIn ); |
1079 | 0 | cfg->cfgIn = NULL; |
1080 | 0 | } |
1081 | | |
1082 | 0 | if ( fname != (tmbstr) file ) |
1083 | 0 | TidyDocFree( doc, fname ); |
1084 | | |
1085 | | /* @jsd: do NOT mess with user-specified settings until |
1086 | | * absolutely necessary, and ensure that we can |
1087 | | * can restore them immediately after the need. |
1088 | | */ |
1089 | | // TY_(AdjustConfig)( docTo ); /* Make sure it's consistent */ |
1090 | | |
1091 | | /* any new config errors? If so, return warning status. */ |
1092 | 0 | return (doc->optionErrors > opterrs ? 1 : 0); |
1093 | 0 | } |
1094 | | |
1095 | | |
1096 | | /* returns false if unknown option, missing parameter, |
1097 | | ** or option doesn't use parameter |
1098 | | */ |
1099 | | Bool TY_(ParseConfigOption)( TidyDocImpl* doc, ctmbstr optnam, ctmbstr optval ) |
1100 | 0 | { |
1101 | 0 | const TidyOptionImpl* option = TY_(lookupOption)( optnam ); |
1102 | 0 | Bool isDeprecated = isOptionDeprecated( optnam ); |
1103 | 0 | Bool status = ( option != NULL ) && !isDeprecated; |
1104 | 0 | if ( !status ) |
1105 | 0 | { |
1106 | | /* Unknown or deprecated, so check to see if the user application |
1107 | | wants to deal with it first. */ |
1108 | 0 | if (NULL != doc->pOptCallback) |
1109 | 0 | status = (*doc->pOptCallback)( optnam, optval ); |
1110 | 0 | if (NULL != doc->pConfigCallback ) |
1111 | 0 | status = status || (*doc->pConfigCallback)( tidyImplToDoc(doc), optnam, optval ); |
1112 | 0 | if (!status && isDeprecated) |
1113 | 0 | status = subDeprecatedOption( doc, optnam, optval); |
1114 | 0 | if (!status) |
1115 | 0 | TY_(ReportUnknownOption)( doc, optnam ); |
1116 | 0 | } |
1117 | 0 | else |
1118 | 0 | status = TY_(ParseConfigValue)( doc, option->id, optval ); |
1119 | |
|
1120 | 0 | return status; |
1121 | 0 | } |
1122 | | |
1123 | | |
1124 | | /* returns false if unknown option, missing parameter, |
1125 | | ** or option doesn't use parameter |
1126 | | */ |
1127 | | Bool TY_(ParseConfigValue)( TidyDocImpl* doc, TidyOptionId optId, ctmbstr optval ) |
1128 | 0 | { |
1129 | 0 | const TidyOptionImpl* option = NULL; |
1130 | | /* #472: fail status if there is a NULL parser. @ralfjunker */ |
1131 | 0 | Bool status = ( optId < N_TIDY_OPTIONS |
1132 | 0 | && (option = option_defs + optId)->parser |
1133 | 0 | && optval != NULL ); |
1134 | |
|
1135 | 0 | if ( !status ) |
1136 | 0 | if ( option ) |
1137 | 0 | TY_(ReportBadArgument)(doc, option->name); |
1138 | 0 | else |
1139 | 0 | { |
1140 | | /* If optId < N_TIDY_OPTIONS then option remains unassigned, |
1141 | | and we have to fall back to an ugly error message. */ |
1142 | 0 | enum { sizeBuf = 11 }; /* uint_max is 10 characters */ |
1143 | 0 | char buffer[sizeBuf]; |
1144 | 0 | TY_(tmbsnprintf(buffer, sizeBuf, "%u", optId)); |
1145 | 0 | TY_(ReportUnknownOption(doc, buffer)); |
1146 | 0 | } |
1147 | 0 | else |
1148 | 0 | { |
1149 | 0 | TidyBuffer inbuf; /* Set up input source */ |
1150 | 0 | tidyBufInitWithAllocator( &inbuf, doc->allocator ); |
1151 | 0 | tidyBufAttach( &inbuf, (byte*)optval, TY_(tmbstrlen)(optval)+1 ); |
1152 | 0 | if (optId == TidyOutFile) |
1153 | 0 | doc->config.cfgIn = TY_(BufferInput)( doc, &inbuf, RAW ); |
1154 | 0 | else |
1155 | 0 | doc->config.cfgIn = TY_(BufferInput)( doc, &inbuf, RAW ); /* Issue #468 - Was ASCII! */ |
1156 | 0 | doc->config.c = GetC( &doc->config ); |
1157 | |
|
1158 | 0 | status = option->parser( doc, option ); |
1159 | |
|
1160 | 0 | TY_(freeStreamIn)(doc->config.cfgIn); /* Release input source */ |
1161 | 0 | doc->config.cfgIn = NULL; |
1162 | 0 | tidyBufDetach( &inbuf ); |
1163 | 0 | } |
1164 | 0 | return status; |
1165 | 0 | } |
1166 | | |
1167 | | |
1168 | | /* ensure that char encodings are self consistent */ |
1169 | | Bool TY_(AdjustCharEncoding)( TidyDocImpl* doc, int encoding ) |
1170 | 69 | { |
1171 | 69 | int outenc = -1; |
1172 | 69 | int inenc = -1; |
1173 | | |
1174 | 69 | switch( encoding ) |
1175 | 69 | { |
1176 | 0 | case MACROMAN: |
1177 | 0 | inenc = MACROMAN; |
1178 | 0 | outenc = ASCII; |
1179 | 0 | break; |
1180 | | |
1181 | 0 | case WIN1252: |
1182 | 0 | inenc = WIN1252; |
1183 | 0 | outenc = ASCII; |
1184 | 0 | break; |
1185 | | |
1186 | 0 | case IBM858: |
1187 | 0 | inenc = IBM858; |
1188 | 0 | outenc = ASCII; |
1189 | 0 | break; |
1190 | | |
1191 | 0 | case ASCII: |
1192 | 0 | inenc = LATIN1; |
1193 | 0 | outenc = ASCII; |
1194 | 0 | break; |
1195 | | |
1196 | 0 | case LATIN0: |
1197 | 0 | inenc = LATIN0; |
1198 | 0 | outenc = ASCII; |
1199 | 0 | break; |
1200 | | |
1201 | 0 | case RAW: |
1202 | 0 | case LATIN1: |
1203 | 69 | case UTF8: |
1204 | 69 | #ifndef NO_NATIVE_ISO2022_SUPPORT |
1205 | 69 | case ISO2022: |
1206 | 69 | #endif |
1207 | | |
1208 | 69 | case UTF16LE: |
1209 | 69 | case UTF16BE: |
1210 | 69 | case UTF16: |
1211 | 69 | case SHIFTJIS: |
1212 | 69 | case BIG5: |
1213 | 69 | inenc = outenc = encoding; |
1214 | 69 | break; |
1215 | 69 | } |
1216 | | |
1217 | 69 | if ( inenc >= 0 ) |
1218 | 69 | { |
1219 | 69 | TY_(SetOptionInt)( doc, TidyCharEncoding, encoding ); |
1220 | 69 | TY_(SetOptionInt)( doc, TidyInCharEncoding, inenc ); |
1221 | 69 | TY_(SetOptionInt)( doc, TidyOutCharEncoding, outenc ); |
1222 | 69 | return yes; |
1223 | 69 | } |
1224 | 0 | return no; |
1225 | 69 | } |
1226 | | |
1227 | | |
1228 | | /* ensure that config is self consistent */ |
1229 | | void TY_(AdjustConfig)( TidyDocImpl* doc ) |
1230 | 69 | { |
1231 | 69 | if ( cfgBool(doc, TidyEncloseBlockText) ) |
1232 | 0 | TY_(SetOptionBool)( doc, TidyEncloseBodyText, yes ); |
1233 | | |
1234 | 69 | if ( cfgAutoBool(doc, TidyIndentContent) == TidyNoState ) |
1235 | 69 | TY_(SetOptionInt)( doc, TidyIndentSpaces, 0 ); |
1236 | | |
1237 | | /* disable wrapping */ |
1238 | 69 | if ( cfg(doc, TidyWrapLen) == 0 ) |
1239 | 69 | TY_(SetOptionInt)( doc, TidyWrapLen, 0x7FFFFFFF ); |
1240 | | |
1241 | | /* Word 2000 needs o:p to be declared as inline */ |
1242 | 69 | if ( cfgBool(doc, TidyWord2000) ) |
1243 | 0 | { |
1244 | 0 | doc->config.defined_tags |= tagtype_inline; |
1245 | 0 | TY_(DefineTag)( doc, tagtype_inline, "o:p" ); |
1246 | 0 | } |
1247 | | |
1248 | | /* #480701 disable XHTML output flag if both output-xhtml and xml input are set */ |
1249 | 69 | if ( cfgBool(doc, TidyXmlTags) ) |
1250 | 69 | TY_(SetOptionBool)( doc, TidyXhtmlOut, no ); |
1251 | | |
1252 | | /* XHTML is written in lower case */ |
1253 | 69 | if ( cfgBool(doc, TidyXhtmlOut) ) |
1254 | 0 | { |
1255 | 0 | TY_(SetOptionBool)( doc, TidyXmlOut, yes ); |
1256 | 0 | TY_(SetOptionBool)( doc, TidyUpperCaseTags, no ); |
1257 | 0 | TY_(SetOptionInt)( doc, TidyUpperCaseAttrs, no ); |
1258 | | /* TY_(SetOptionBool)( doc, TidyXmlPIs, yes ); */ |
1259 | 0 | } |
1260 | | |
1261 | | /* if XML in, then XML out */ |
1262 | 69 | if ( cfgBool(doc, TidyXmlTags) ) |
1263 | 69 | { |
1264 | 69 | TY_(SetOptionBool)( doc, TidyXmlOut, yes ); |
1265 | 69 | TY_(SetOptionBool)( doc, TidyXmlPIs, yes ); |
1266 | 69 | } |
1267 | | |
1268 | | /* #427837 - fix by Dave Raggett 02 Jun 01 |
1269 | | ** generate <?xml version="1.0" encoding="iso-8859-1"?> |
1270 | | ** if the output character encoding is Latin-1 etc. |
1271 | | */ |
1272 | 69 | if ( cfg(doc, TidyOutCharEncoding) != ASCII && |
1273 | 69 | cfg(doc, TidyOutCharEncoding) != UTF8 && |
1274 | 69 | cfg(doc, TidyOutCharEncoding) != UTF16 && |
1275 | 69 | cfg(doc, TidyOutCharEncoding) != UTF16BE && |
1276 | 69 | cfg(doc, TidyOutCharEncoding) != UTF16LE && |
1277 | 69 | cfg(doc, TidyOutCharEncoding) != RAW && |
1278 | 69 | cfgBool(doc, TidyXmlOut) ) |
1279 | 0 | { |
1280 | 0 | TY_(SetOptionBool)( doc, TidyXmlDecl, yes ); |
1281 | 0 | } |
1282 | | |
1283 | | /* XML requires end tags */ |
1284 | 69 | if ( cfgBool(doc, TidyXmlOut) ) |
1285 | 69 | { |
1286 | | /* XML requires a BOM on output if using UTF-16 encoding */ |
1287 | 69 | ulong enc = cfg( doc, TidyOutCharEncoding ); |
1288 | 69 | if ( enc == UTF16LE || enc == UTF16BE || enc == UTF16 ) |
1289 | 0 | TY_(SetOptionInt)( doc, TidyOutputBOM, yes ); |
1290 | | |
1291 | 69 | TY_(SetOptionBool)( doc, TidyQuoteAmpersand, yes ); |
1292 | 69 | TY_(SetOptionBool)( doc, TidyOmitOptionalTags, no ); |
1293 | 69 | } |
1294 | | |
1295 | | /* Setup the indent character. */ |
1296 | 69 | if cfgBool(doc, TidyPPrintTabs) |
1297 | 0 | doc->indent_char = '\t'; |
1298 | 69 | else |
1299 | 69 | doc->indent_char = ' '; |
1300 | 69 | } |
1301 | | |
1302 | | |
1303 | | /* A service to ParseList(), keeps option values nicely formatted and |
1304 | | coordinates additions to the internal lists. Within Tidy, this function |
1305 | | might be used to programmatically add individual values to items that use |
1306 | | this service. |
1307 | | */ |
1308 | | void TY_(DeclareListItem)( TidyDocImpl* doc, const TidyOptionImpl* opt, ctmbstr name ) |
1309 | 0 | { |
1310 | 0 | ctmbstr prvval = cfgStr( doc, opt->id ); |
1311 | 0 | tmbstr catval = NULL; |
1312 | 0 | ctmbstr theval = name; |
1313 | 0 | if ( prvval ) |
1314 | 0 | { |
1315 | 0 | uint len = TY_(tmbstrlen)(name) + TY_(tmbstrlen)(prvval) + 3; |
1316 | 0 | catval = TY_(tmbstrndup)( doc->allocator, prvval, len ); |
1317 | 0 | TY_(tmbstrcat)( catval, ", " ); |
1318 | 0 | TY_(tmbstrcat)( catval, name ); |
1319 | 0 | theval = catval; |
1320 | 0 | } |
1321 | |
|
1322 | 0 | switch ( opt->id ) |
1323 | 0 | { |
1324 | 0 | case TidyPriorityAttributes: |
1325 | 0 | TY_(DefinePriorityAttribute)( doc, name ); |
1326 | 0 | break; |
1327 | | |
1328 | 0 | case TidyMuteReports: |
1329 | 0 | TY_(DefineMutedMessage)( doc, opt, name ); |
1330 | 0 | break; |
1331 | | |
1332 | 0 | case TidyInlineTags: |
1333 | 0 | case TidyBlockTags: |
1334 | 0 | case TidyEmptyTags: |
1335 | 0 | case TidyPreTags: |
1336 | 0 | case TidyCustomTags: |
1337 | 0 | TY_(DeclareUserTag)( doc, opt, name ); |
1338 | 0 | break; |
1339 | | |
1340 | 0 | default: |
1341 | 0 | break; |
1342 | 0 | } |
1343 | | |
1344 | 0 | SetOptionValue( doc, opt->id, theval ); |
1345 | 0 | if ( catval ) |
1346 | 0 | TidyDocFree( doc, catval ); |
1347 | 0 | } |
1348 | | |
1349 | | |
1350 | | /* a space or comma separated list of items */ |
1351 | | Bool ParseList( TidyDocImpl* doc, const TidyOptionImpl* option ) |
1352 | 0 | { |
1353 | 0 | TidyConfigImpl* cfg = &doc->config; |
1354 | 0 | tmbchar buf[1024]; |
1355 | 0 | uint i = 0, nItems = 0; |
1356 | 0 | uint c; |
1357 | 0 | TidyConfigChangeCallback callback = doc->pConfigChangeCallback; |
1358 | 0 | tmbstr oldbuff = NULL; |
1359 | | |
1360 | | /* Handle comparing before and after for the config change callback. |
1361 | | We have do handle this manually, because otherwise TY_(DeclareListItem) |
1362 | | would fire a callback for EVERY list item being added. */ |
1363 | 0 | doc->pConfigChangeCallback = NULL; |
1364 | 0 | if ( callback ) |
1365 | 0 | { |
1366 | 0 | TidyOptionValue* oldval = &(doc->config.value[ option->id ]); |
1367 | 0 | oldbuff = TY_(tmbstrdup)( doc->allocator, oldval->p ); |
1368 | 0 | } |
1369 | |
|
1370 | 0 | SetOptionValue( doc, option->id, NULL ); |
1371 | | |
1372 | | /* Given an empty string, so signal success. */ |
1373 | 0 | if ( cfg->c == EndOfStream ) |
1374 | 0 | return yes; |
1375 | | |
1376 | 0 | c = SkipWhite( cfg ); |
1377 | |
|
1378 | 0 | do |
1379 | 0 | { |
1380 | 0 | if (c == ' ' || c == '\t' || c == ',') |
1381 | 0 | { |
1382 | 0 | c = AdvanceChar( cfg ); |
1383 | 0 | continue; |
1384 | 0 | } |
1385 | | |
1386 | 0 | if ( c == '\r' || c == '\n' ) |
1387 | 0 | { |
1388 | 0 | uint c2 = AdvanceChar( cfg ); |
1389 | 0 | if ( c == '\r' && c2 == '\n' ) |
1390 | 0 | c = AdvanceChar( cfg ); |
1391 | 0 | else |
1392 | 0 | c = c2; |
1393 | |
|
1394 | 0 | if ( !TY_(IsWhite)(c) ) |
1395 | 0 | { |
1396 | 0 | buf[i] = 0; |
1397 | 0 | TY_(UngetChar)( c, cfg->cfgIn ); |
1398 | 0 | TY_(UngetChar)( '\n', cfg->cfgIn ); |
1399 | 0 | break; |
1400 | 0 | } |
1401 | 0 | } |
1402 | | |
1403 | 0 | while ( i < sizeof(buf)-2 && c != EndOfStream && !TY_(IsWhite)(c) && c != ',' ) |
1404 | 0 | { |
1405 | 0 | buf[i++] = (tmbchar) c; |
1406 | 0 | c = AdvanceChar( cfg ); |
1407 | 0 | } |
1408 | |
|
1409 | 0 | buf[i] = '\0'; |
1410 | 0 | if (i == 0) /* Skip empty attribute definition. Possible when */ |
1411 | 0 | continue; /* there is a trailing space on the line. */ |
1412 | | |
1413 | | /* add attribute to array */ |
1414 | 0 | TY_(DeclareListItem)( doc, option, buf ); |
1415 | |
|
1416 | 0 | i = 0; |
1417 | 0 | ++nItems; |
1418 | 0 | } |
1419 | 0 | while ( c != EndOfStream ); |
1420 | | |
1421 | 0 | if ( i > 0 ) |
1422 | 0 | TY_(DeclareListItem)( doc, option, buf ); |
1423 | | |
1424 | | /* If there's a callback, compare the old and new values, and fire |
1425 | | the callback appropriately. */ |
1426 | 0 | if ( callback ) |
1427 | 0 | { |
1428 | 0 | TidyOptionValue* val = &(doc->config.value[ option->id ]); |
1429 | 0 | Bool fire_callback = OptionChangedValuesDiffer( val->p, oldbuff); |
1430 | |
|
1431 | 0 | doc->pConfigChangeCallback = callback; |
1432 | |
|
1433 | 0 | if ( oldbuff ) |
1434 | 0 | TidyFree( doc->allocator, oldbuff ); |
1435 | |
|
1436 | 0 | if ( fire_callback ) |
1437 | 0 | PerformOptionChangedCallback( doc, option ); |
1438 | 0 | } |
1439 | |
|
1440 | 0 | return ( nItems > 0 ); |
1441 | 0 | } |
1442 | | |
1443 | | |
1444 | | /* unsigned integers */ |
1445 | | Bool ParseInt( TidyDocImpl* doc, const TidyOptionImpl* entry ) |
1446 | 0 | { |
1447 | 0 | ulong number = 0; |
1448 | 0 | Bool digits = no; |
1449 | 0 | TidyConfigImpl* cfg = &doc->config; |
1450 | 0 | tchar c = SkipWhite( cfg ); |
1451 | |
|
1452 | 0 | while ( TY_(IsDigit)(c) ) |
1453 | 0 | { |
1454 | 0 | number = c - '0' + (10 * number); |
1455 | 0 | digits = yes; |
1456 | 0 | c = AdvanceChar( cfg ); |
1457 | 0 | } |
1458 | |
|
1459 | 0 | if ( !digits ) |
1460 | 0 | TY_(ReportBadArgument)( doc, entry->name ); |
1461 | 0 | else |
1462 | 0 | TY_(SetOptionInt)( doc, entry->id, number ); |
1463 | 0 | return digits; |
1464 | 0 | } |
1465 | | |
1466 | | |
1467 | | /* a string excluding whitespace */ |
1468 | | Bool FUNC_UNUSED ParseName( TidyDocImpl* doc, const TidyOptionImpl* option ) |
1469 | 0 | { |
1470 | 0 | tmbchar buf[ 1024 ] = {0}; |
1471 | 0 | uint i = 0; |
1472 | 0 | uint c = SkipWhite( &doc->config ); |
1473 | 0 |
|
1474 | 0 | while ( i < sizeof(buf)-2 && c != EndOfStream && !TY_(IsWhite)(c) ) |
1475 | 0 | { |
1476 | 0 | buf[i++] = (tmbchar) c; |
1477 | 0 | c = AdvanceChar( &doc->config ); |
1478 | 0 | } |
1479 | 0 | buf[i] = 0; |
1480 | 0 |
|
1481 | 0 | if ( i == 0 ) |
1482 | 0 | TY_(ReportBadArgument)( doc, option->name ); |
1483 | 0 | else |
1484 | 0 | SetOptionValue( doc, option->id, buf ); |
1485 | 0 | return ( i > 0 ); |
1486 | 0 | } |
1487 | | |
1488 | | |
1489 | | /* #508936 - CSS class naming for -clean option */ |
1490 | | Bool ParseCSS1Selector( TidyDocImpl* doc, const TidyOptionImpl* option ) |
1491 | 0 | { |
1492 | 0 | TidyConfigImpl* cfg = &doc->config; |
1493 | 0 | char buf[256] = {0}; |
1494 | 0 | uint i = 0; |
1495 | 0 | uint c; |
1496 | | |
1497 | | /* Given an empty string, so signal success. */ |
1498 | 0 | if ( cfg->c == EndOfStream ) |
1499 | 0 | { |
1500 | 0 | SetOptionValue( doc, option->id, NULL ); |
1501 | 0 | return yes; |
1502 | 0 | } |
1503 | | |
1504 | 0 | c = SkipWhite( cfg ); |
1505 | |
|
1506 | 0 | while ( i < sizeof(buf)-2 && c != EndOfStream && !TY_(IsWhite)(c) ) |
1507 | 0 | { |
1508 | 0 | buf[i++] = (tmbchar) c; |
1509 | 0 | c = AdvanceChar( &doc->config ); |
1510 | 0 | } |
1511 | 0 | buf[i] = '\0'; |
1512 | |
|
1513 | 0 | if ( i == 0 ) { |
1514 | 0 | return no; |
1515 | 0 | } |
1516 | 0 | else if ( !TY_(IsCSS1Selector)(buf) ) { |
1517 | 0 | TY_(ReportBadArgument)( doc, option->name ); |
1518 | 0 | return no; |
1519 | 0 | } |
1520 | | |
1521 | 0 | buf[i] = 0; /* Is #697 - Do *not* add '-' */ |
1522 | | #if 0 /* Is #697 - Is this still required? KEEP HISTORY - DO NOT DELETE */ |
1523 | | buf[i++] = '-'; /* Make sure any escaped Unicode is terminated */ |
1524 | | buf[i] = 0; /* so valid class names are generated after */ |
1525 | | /* Tidy appends last digits. */ |
1526 | | #endif /* Is #697 - Is this still required? */ |
1527 | |
|
1528 | 0 | SetOptionValue( doc, option->id, buf ); |
1529 | 0 | return yes; |
1530 | 0 | } |
1531 | | |
1532 | | |
1533 | | /* Given a string, return the picklist value from an arbitrary picklist. */ |
1534 | | static Bool GetPickListValue( ctmbstr value, PickListItems* pickList, uint *result ) |
1535 | 0 | { |
1536 | 0 | const PickListItem *item = NULL; |
1537 | 0 | uint ix = 0; |
1538 | |
|
1539 | 0 | while ( (item = &(*pickList)[ ix ]) && item->label ) |
1540 | 0 | { |
1541 | 0 | ctmbstr input; |
1542 | 0 | uint i = 0; |
1543 | 0 | while ( ( input = &(*item->inputs[i]) ) ) |
1544 | 0 | { |
1545 | 0 | if (TY_(tmbstrcasecmp)(value, input) == 0 ) |
1546 | 0 | { |
1547 | 0 | *result = ix; |
1548 | 0 | return yes; |
1549 | 0 | } |
1550 | 0 | ++i; |
1551 | 0 | } |
1552 | 0 | ++ix; |
1553 | 0 | } |
1554 | | |
1555 | 0 | return no; |
1556 | 0 | } |
1557 | | |
1558 | | |
1559 | | /* A general parser for anything using pick lists. This provides the engine to |
1560 | | determine the proper option value, and can be used by parsers in addition to |
1561 | | ParsePickList that require special handling. |
1562 | | */ |
1563 | | static Bool GetParsePickListValue( TidyDocImpl* doc, const TidyOptionImpl* entry, uint *result ) |
1564 | 0 | { |
1565 | 0 | TidyConfigImpl* cfg = &doc->config; |
1566 | 0 | tchar c = SkipWhite( cfg ); |
1567 | 0 | tmbchar work[ 16 ] = {0}; |
1568 | 0 | tmbstr cp = work, end = work + sizeof(work); |
1569 | |
|
1570 | 0 | while ( c!=EndOfStream && cp < end && !TY_(IsWhite)(c) && c != '\r' && c != '\n' ) |
1571 | 0 | { |
1572 | 0 | *cp++ = (tmbchar) c; |
1573 | 0 | c = AdvanceChar( cfg ); |
1574 | 0 | } |
1575 | |
|
1576 | 0 | if ( GetPickListValue( work, entry->pickList, result ) != yes ) |
1577 | 0 | { |
1578 | 0 | TY_(ReportBadArgument)( doc, entry->name ); |
1579 | 0 | return no; |
1580 | 0 | } |
1581 | | |
1582 | 0 | return yes; |
1583 | 0 | } |
1584 | | |
1585 | | |
1586 | | /* A general parser for anything using pick lists that don't require special |
1587 | | handling. |
1588 | | */ |
1589 | | Bool ParsePickList( TidyDocImpl* doc, const TidyOptionImpl* entry ) |
1590 | 0 | { |
1591 | 0 | uint value; |
1592 | | |
1593 | 0 | if ( GetParsePickListValue( doc, entry, &value ) ) |
1594 | 0 | { |
1595 | 0 | if ( entry->type == TidyBoolean ) |
1596 | 0 | TY_(SetOptionBool)( doc, entry->id, value ); |
1597 | 0 | else if ( entry->type == TidyInteger ) |
1598 | 0 | TY_(SetOptionInt)( doc, entry->id, value ); |
1599 | 0 | return yes; |
1600 | 0 | } |
1601 | | |
1602 | 0 | TY_(ReportBadArgument)( doc, entry->name ); |
1603 | 0 | return no; |
1604 | 0 | } |
1605 | | |
1606 | | |
1607 | | /*\ |
1608 | | * 20150515 - support using tabs instead of spaces - Issue #108 |
1609 | | * Sets the indent character to a tab if on, and set indent space count to 1 |
1610 | | * and sets indent character to a space if off. |
1611 | | \*/ |
1612 | | Bool ParseTabs( TidyDocImpl* doc, const TidyOptionImpl* entry ) |
1613 | 0 | { |
1614 | 0 | uint flag = 0; |
1615 | 0 | Bool status = GetParsePickListValue( doc, entry, &flag ); |
1616 | | |
1617 | 0 | if ( status ) { |
1618 | 0 | Bool tabs = flag != 0 ? yes : no; |
1619 | 0 | TY_(SetOptionBool)( doc, entry->id, tabs ); |
1620 | 0 | if (tabs) { |
1621 | 0 | TY_(SetOptionInt)( doc, TidyIndentSpaces, 1 ); |
1622 | 0 | } else { |
1623 | | /* optional - TY_(ResetOptionToDefault)( doc, TidyIndentSpaces ); */ |
1624 | 0 | } |
1625 | 0 | } |
1626 | 0 | return status; |
1627 | 0 | } |
1628 | | |
1629 | | |
1630 | | /* a string including whitespace */ |
1631 | | /* munges whitespace sequences */ |
1632 | | Bool ParseString( TidyDocImpl* doc, const TidyOptionImpl* option ) |
1633 | 0 | { |
1634 | 0 | TidyConfigImpl* cfg = &doc->config; |
1635 | 0 | tmbchar buf[8192]; |
1636 | 0 | uint i = 0; |
1637 | 0 | tchar delim = 0; |
1638 | 0 | Bool waswhite = yes; |
1639 | |
|
1640 | 0 | tchar c = SkipWhite( cfg ); |
1641 | |
|
1642 | 0 | if ( c == '"' || c == '\'' ) |
1643 | 0 | { |
1644 | 0 | delim = c; |
1645 | 0 | c = AdvanceChar( cfg ); |
1646 | 0 | } |
1647 | |
|
1648 | 0 | while ( i < sizeof(buf)-2 && c != EndOfStream && c != '\r' && c != '\n' ) |
1649 | 0 | { |
1650 | 0 | if ( delim && c == delim ) |
1651 | 0 | break; |
1652 | | |
1653 | 0 | if ( TY_(IsWhite)(c) ) |
1654 | 0 | { |
1655 | 0 | if ( waswhite ) |
1656 | 0 | { |
1657 | 0 | c = AdvanceChar( cfg ); |
1658 | 0 | continue; |
1659 | 0 | } |
1660 | 0 | c = ' '; |
1661 | 0 | } |
1662 | 0 | else |
1663 | 0 | waswhite = no; |
1664 | | |
1665 | 0 | buf[i++] = (tmbchar) c; |
1666 | 0 | c = AdvanceChar( cfg ); |
1667 | 0 | } |
1668 | 0 | buf[i] = '\0'; |
1669 | |
|
1670 | 0 | SetOptionValue( doc, option->id, buf ); |
1671 | 0 | return yes; |
1672 | 0 | } |
1673 | | |
1674 | | |
1675 | | Bool ParseCharEnc( TidyDocImpl* doc, const TidyOptionImpl* option ) |
1676 | 0 | { |
1677 | 0 | tmbchar buf[64] = {0}; |
1678 | 0 | uint i = 0; |
1679 | 0 | int enc = ASCII; |
1680 | 0 | Bool validEncoding = yes; |
1681 | 0 | tchar c = SkipWhite( &doc->config ); |
1682 | |
|
1683 | 0 | while ( i < sizeof(buf)-2 && c != EndOfStream && !TY_(IsWhite)(c) ) |
1684 | 0 | { |
1685 | 0 | buf[i++] = (tmbchar) TY_(ToLower)( c ); |
1686 | 0 | c = AdvanceChar( &doc->config ); |
1687 | 0 | } |
1688 | 0 | buf[i] = 0; |
1689 | |
|
1690 | 0 | enc = TY_(CharEncodingId)( doc, buf ); |
1691 | |
|
1692 | 0 | if ( enc < 0 ) |
1693 | 0 | { |
1694 | 0 | validEncoding = no; |
1695 | 0 | TY_(ReportBadArgument)( doc, option->name ); |
1696 | 0 | } |
1697 | 0 | else |
1698 | 0 | TY_(SetOptionInt)( doc, option->id, enc ); |
1699 | |
|
1700 | 0 | if ( validEncoding && option->id == TidyCharEncoding ) |
1701 | 0 | TY_(AdjustCharEncoding)( doc, enc ); |
1702 | 0 | return validEncoding; |
1703 | 0 | } |
1704 | | |
1705 | | |
1706 | | int TY_(CharEncodingId)( TidyDocImpl* ARG_UNUSED(doc), ctmbstr charenc ) |
1707 | 69 | { |
1708 | 69 | int enc = TY_(GetCharEncodingFromOptName)( charenc ); |
1709 | 69 | return enc; |
1710 | 69 | } |
1711 | | |
1712 | | |
1713 | | ctmbstr TY_(CharEncodingName)( int encoding ) |
1714 | 0 | { |
1715 | 0 | ctmbstr encodingName = TY_(GetEncodingNameFromTidyId)(encoding); |
1716 | |
|
1717 | 0 | if (!encodingName) |
1718 | 0 | encodingName = "unknown"; |
1719 | |
|
1720 | 0 | return encodingName; |
1721 | 0 | } |
1722 | | |
1723 | | |
1724 | | ctmbstr TY_(CharEncodingOptName)( int encoding ) |
1725 | 0 | { |
1726 | 0 | ctmbstr encodingName = TY_(GetEncodingOptNameFromTidyId)(encoding); |
1727 | |
|
1728 | 0 | if (!encodingName) |
1729 | 0 | encodingName = "unknown"; |
1730 | |
|
1731 | 0 | return encodingName; |
1732 | 0 | } |
1733 | | |
1734 | | |
1735 | | /* |
1736 | | doctype: html5 | omit | auto | strict | loose | <fpi> |
1737 | | |
1738 | | where the fpi is a string similar to |
1739 | | |
1740 | | "-//ACME//DTD HTML 3.14159//EN" |
1741 | | */ |
1742 | | Bool ParseDocType( TidyDocImpl* doc, const TidyOptionImpl* option ) |
1743 | 0 | { |
1744 | 0 | Bool status = yes; |
1745 | 0 | uint value; |
1746 | 0 | TidyConfigImpl* cfg = &doc->config; |
1747 | 0 | tchar c; |
1748 | | |
1749 | | /* Given an empty string, so signal success. */ |
1750 | 0 | if ( cfg->c == EndOfStream ) |
1751 | 0 | { |
1752 | 0 | SetOptionValue( doc, option->id, NULL ); |
1753 | 0 | return yes; |
1754 | 0 | } |
1755 | | |
1756 | 0 | c = SkipWhite( cfg ); |
1757 | | |
1758 | | |
1759 | | /* "-//ACME//DTD HTML 3.14159//EN" or similar */ |
1760 | |
|
1761 | 0 | if ( c == '"' || c == '\''|| c == '-' || c == '+' ) |
1762 | 0 | { |
1763 | 0 | status = ParseString(doc, option); |
1764 | 0 | if (status) |
1765 | 0 | { |
1766 | 0 | TY_(SetOptionInt)( doc, TidyDoctypeMode, TidyDoctypeUser ); |
1767 | 0 | } |
1768 | 0 | return status; |
1769 | 0 | } |
1770 | | |
1771 | 0 | if ( (status = GetParsePickListValue( doc, option, &value ) ) ) |
1772 | 0 | { |
1773 | 0 | TY_(SetOptionInt)( doc, TidyDoctypeMode, value ); |
1774 | 0 | } |
1775 | 0 | else |
1776 | 0 | { |
1777 | 0 | TY_(ReportBadArgument)( doc, option->name ); |
1778 | 0 | } |
1779 | | |
1780 | 0 | return status; |
1781 | 0 | } |
1782 | | |
1783 | | |
1784 | | /* Use TidyOptionId as iterator. |
1785 | | ** Send index of 1st option after TidyOptionUnknown as start of list. |
1786 | | */ |
1787 | | TidyIterator TY_(getOptionList)( TidyDocImpl* ARG_UNUSED(doc) ) |
1788 | 0 | { |
1789 | 0 | return (TidyIterator) (size_t)1; |
1790 | 0 | } |
1791 | | |
1792 | | |
1793 | | /* Check if this item is last valid option. |
1794 | | ** If so, zero out iterator. |
1795 | | */ |
1796 | | const TidyOptionImpl* TY_(getNextOption)( TidyDocImpl* ARG_UNUSED(doc), |
1797 | | TidyIterator* iter ) |
1798 | 0 | { |
1799 | 0 | const TidyOptionImpl* option = NULL; |
1800 | 0 | size_t optId; |
1801 | 0 | assert( iter != NULL ); |
1802 | 0 | optId = (size_t) *iter; |
1803 | 0 | if ( optId > TidyUnknownOption && optId < N_TIDY_OPTIONS ) |
1804 | 0 | { |
1805 | 0 | option = &option_defs[ optId ]; |
1806 | 0 | optId++; |
1807 | 0 | } |
1808 | 0 | *iter = (TidyIterator) ( optId < N_TIDY_OPTIONS ? optId : (size_t)0 ); |
1809 | 0 | return option; |
1810 | 0 | } |
1811 | | |
1812 | | |
1813 | | /* Use a 1-based array index as iterator: 0 == end-of-list |
1814 | | */ |
1815 | | TidyIterator TY_(getOptionPickList)( const TidyOptionImpl* option ) |
1816 | 0 | { |
1817 | 0 | size_t ix = 0; |
1818 | 0 | if ( option && option->pickList ) |
1819 | 0 | ix = 1; |
1820 | 0 | return (TidyIterator) ix; |
1821 | 0 | } |
1822 | | |
1823 | | |
1824 | | ctmbstr TY_(getNextOptionPick)( const TidyOptionImpl* option, |
1825 | | TidyIterator* iter ) |
1826 | 0 | { |
1827 | 0 | size_t ix; |
1828 | 0 | ctmbstr val = NULL; |
1829 | 0 | const PickListItem *item= NULL; |
1830 | 0 | assert( option!=NULL && iter != NULL ); |
1831 | | |
1832 | 0 | ix = (size_t) *iter; |
1833 | | |
1834 | 0 | if ( option->pickList ) |
1835 | 0 | { |
1836 | 0 | if ( ix > 0 && ix < TIDY_PL_SIZE && option->pickList ) |
1837 | 0 | { |
1838 | 0 | item = &(*option->pickList)[ ix-1 ]; |
1839 | 0 | val = item->label; |
1840 | 0 | } |
1841 | 0 | item = &(*option->pickList)[ ix ]; |
1842 | 0 | *iter = (TidyIterator) ( val && item->label ? ix + 1 : (size_t)0 ); |
1843 | 0 | } |
1844 | | |
1845 | 0 | return val; |
1846 | 0 | } |
1847 | | |
1848 | | |
1849 | | static int WriteOptionString( const TidyOptionImpl* option, |
1850 | | ctmbstr sval, StreamOut* out ) |
1851 | 0 | { |
1852 | 0 | ctmbstr cp = option->name; |
1853 | 0 | while ( *cp ) |
1854 | 0 | TY_(WriteChar)( *cp++, out ); |
1855 | 0 | TY_(WriteChar)( ':', out ); |
1856 | 0 | TY_(WriteChar)( ' ', out ); |
1857 | 0 | cp = sval; |
1858 | 0 | while ( *cp ) |
1859 | 0 | TY_(WriteChar)( *cp++, out ); |
1860 | 0 | TY_(WriteChar)( '\n', out ); |
1861 | 0 | return 0; |
1862 | 0 | } |
1863 | | |
1864 | | |
1865 | | static int WriteOptionInt( const TidyOptionImpl* option, uint ival, StreamOut* out ) |
1866 | 0 | { |
1867 | 0 | tmbchar sval[ 32 ] = {0}; |
1868 | 0 | TY_(tmbsnprintf)(sval, sizeof(sval), "%u", ival ); |
1869 | 0 | return WriteOptionString( option, sval, out ); |
1870 | 0 | } |
1871 | | |
1872 | | |
1873 | | static int WriteOptionBool( const TidyOptionImpl* option, Bool bval, StreamOut* out ) |
1874 | 0 | { |
1875 | 0 | ctmbstr sval = bval ? "yes" : "no"; |
1876 | 0 | return WriteOptionString( option, sval, out ); |
1877 | 0 | } |
1878 | | |
1879 | | |
1880 | | static int WriteOptionPick( const TidyOptionImpl* option, uint ival, StreamOut* out ) |
1881 | 0 | { |
1882 | 0 | uint ix = 0; |
1883 | 0 | const PickListItem *item = NULL; |
1884 | | |
1885 | 0 | if ( option-> pickList ) |
1886 | 0 | { |
1887 | 0 | while ( (item = &(*option->pickList)[ ix ]) && item->label && ix<ival ) |
1888 | 0 | { |
1889 | 0 | ++ix; |
1890 | 0 | } |
1891 | 0 | if ( ix==ival && item->label ) |
1892 | 0 | return WriteOptionString( option, item->label, out ); |
1893 | 0 | } |
1894 | | |
1895 | 0 | return -1; |
1896 | 0 | } |
1897 | | |
1898 | | |
1899 | | Bool TY_(ConfigDiffThanSnapshot)( TidyDocImpl* doc ) |
1900 | 0 | { |
1901 | 0 | int diff = memcmp( &doc->config.value, &doc->config.snapshot, |
1902 | 0 | N_TIDY_OPTIONS * sizeof(uint) ); |
1903 | 0 | return ( diff != 0 ); |
1904 | 0 | } |
1905 | | |
1906 | | |
1907 | | Bool TY_(ConfigDiffThanDefault)( TidyDocImpl* doc ) |
1908 | 0 | { |
1909 | 0 | Bool diff = no; |
1910 | 0 | const TidyOptionImpl* option = option_defs + 1; |
1911 | 0 | const TidyOptionValue* val = doc->config.value + 1; |
1912 | 0 | for ( /**/; !diff && option && option->name; ++option, ++val ) |
1913 | 0 | { |
1914 | 0 | diff = !OptionValueEqDefault( option, val ); |
1915 | 0 | } |
1916 | 0 | return diff; |
1917 | 0 | } |
1918 | | |
1919 | | |
1920 | | static int SaveConfigToStream( TidyDocImpl* doc, StreamOut* out ) |
1921 | 0 | { |
1922 | 0 | int rc = 0; |
1923 | 0 | const TidyOptionImpl* option; |
1924 | 0 | for ( option=option_defs+1; 0==rc && option && option->name; ++option ) |
1925 | 0 | { |
1926 | 0 | const TidyOptionValue* val = &doc->config.value[ option->id ]; |
1927 | 0 | if ( option->parser == NULL ) |
1928 | 0 | continue; |
1929 | 0 | if ( OptionValueEqDefault( option, val ) && option->id != TidyDoctype) |
1930 | 0 | continue; |
1931 | | |
1932 | 0 | if ( option->id == TidyDoctype ) /* Special case */ |
1933 | 0 | { |
1934 | 0 | ulong dtmode = cfg( doc, TidyDoctypeMode ); |
1935 | 0 | if ( dtmode == TidyDoctypeUser ) |
1936 | 0 | { |
1937 | 0 | tmbstr t; |
1938 | | |
1939 | | /* add 2 double quotes */ |
1940 | 0 | if (( t = (tmbstr)TidyDocAlloc( doc, TY_(tmbstrlen)( val->p ) + 2 ) )) |
1941 | 0 | { |
1942 | 0 | t[0] = '\"'; t[1] = 0; |
1943 | | |
1944 | 0 | TY_(tmbstrcat)( t, val->p ); |
1945 | 0 | TY_(tmbstrcat)( t, "\"" ); |
1946 | 0 | rc = WriteOptionString( option, t, out ); |
1947 | | |
1948 | 0 | TidyDocFree( doc, t ); |
1949 | 0 | } |
1950 | 0 | } |
1951 | 0 | else if ( dtmode == option_defs[TidyDoctypeMode].dflt ) |
1952 | 0 | continue; |
1953 | 0 | else |
1954 | 0 | rc = WriteOptionPick( option, dtmode, out ); |
1955 | 0 | } |
1956 | 0 | else if ( option->pickList) |
1957 | 0 | rc = WriteOptionPick( option, val->v, out ); |
1958 | 0 | else |
1959 | 0 | { |
1960 | 0 | switch ( option->type ) |
1961 | 0 | { |
1962 | 0 | case TidyString: |
1963 | 0 | rc = WriteOptionString( option, val->p, out ); |
1964 | 0 | break; |
1965 | 0 | case TidyInteger: |
1966 | 0 | rc = WriteOptionInt( option, val->v, out ); |
1967 | 0 | break; |
1968 | 0 | case TidyBoolean: |
1969 | 0 | rc = WriteOptionBool( option, val->v ? yes : no, out ); |
1970 | 0 | break; |
1971 | 0 | } |
1972 | 0 | } |
1973 | 0 | } |
1974 | 0 | return rc; |
1975 | 0 | } |
1976 | | |
1977 | | |
1978 | | int TY_(SaveConfigFile)( TidyDocImpl* doc, ctmbstr cfgfil ) |
1979 | 0 | { |
1980 | 0 | int status = -1; |
1981 | 0 | StreamOut* out = NULL; |
1982 | 0 | uint outenc = cfg( doc, TidyOutCharEncoding ); |
1983 | 0 | uint nl = cfg( doc, TidyNewline ); |
1984 | 0 | FILE* fout = fopen( cfgfil, "wb" ); |
1985 | 0 | if ( fout ) |
1986 | 0 | { |
1987 | 0 | out = TY_(FileOutput)( doc, fout, outenc, nl ); |
1988 | 0 | status = SaveConfigToStream( doc, out ); |
1989 | 0 | fclose( fout ); |
1990 | 0 | TidyDocFree( doc, out ); |
1991 | 0 | } |
1992 | 0 | return status; |
1993 | 0 | } |
1994 | | |
1995 | | |
1996 | | int TY_(SaveConfigSink)( TidyDocImpl* doc, TidyOutputSink* sink ) |
1997 | 0 | { |
1998 | 0 | uint outenc = cfg( doc, TidyOutCharEncoding ); |
1999 | 0 | uint nl = cfg( doc, TidyNewline ); |
2000 | 0 | StreamOut* out = TY_(UserOutput)( doc, sink, outenc, nl ); |
2001 | 0 | int status = SaveConfigToStream( doc, out ); |
2002 | 0 | TidyDocFree( doc, out ); |
2003 | 0 | return status; |
2004 | 0 | } |
2005 | | |