tidy-ext 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. data/.gitignore +4 -0
  2. data/LICENSE +50 -0
  3. data/README +12 -0
  4. data/Rakefile +60 -0
  5. data/VERSION +1 -0
  6. data/ext/tidy/access.c +3310 -0
  7. data/ext/tidy/access.h +279 -0
  8. data/ext/tidy/alloc.c +107 -0
  9. data/ext/tidy/attrask.c +209 -0
  10. data/ext/tidy/attrdict.c +2398 -0
  11. data/ext/tidy/attrdict.h +122 -0
  12. data/ext/tidy/attrget.c +213 -0
  13. data/ext/tidy/attrs.c +1911 -0
  14. data/ext/tidy/attrs.h +374 -0
  15. data/ext/tidy/buffio.c +232 -0
  16. data/ext/tidy/buffio.h +118 -0
  17. data/ext/tidy/charsets.c +1032 -0
  18. data/ext/tidy/charsets.h +14 -0
  19. data/ext/tidy/clean.c +2674 -0
  20. data/ext/tidy/clean.h +87 -0
  21. data/ext/tidy/config.c +1746 -0
  22. data/ext/tidy/config.h +153 -0
  23. data/ext/tidy/entities.c +419 -0
  24. data/ext/tidy/entities.h +24 -0
  25. data/ext/tidy/extconf.rb +5 -0
  26. data/ext/tidy/fileio.c +106 -0
  27. data/ext/tidy/fileio.h +46 -0
  28. data/ext/tidy/forward.h +69 -0
  29. data/ext/tidy/iconvtc.c +105 -0
  30. data/ext/tidy/iconvtc.h +15 -0
  31. data/ext/tidy/istack.c +373 -0
  32. data/ext/tidy/lexer.c +3825 -0
  33. data/ext/tidy/lexer.h +617 -0
  34. data/ext/tidy/localize.c +1882 -0
  35. data/ext/tidy/mappedio.c +329 -0
  36. data/ext/tidy/mappedio.h +16 -0
  37. data/ext/tidy/message.h +207 -0
  38. data/ext/tidy/parser.c +4408 -0
  39. data/ext/tidy/parser.h +76 -0
  40. data/ext/tidy/platform.h +636 -0
  41. data/ext/tidy/pprint.c +2276 -0
  42. data/ext/tidy/pprint.h +93 -0
  43. data/ext/tidy/ruby-tidy.c +195 -0
  44. data/ext/tidy/streamio.c +1407 -0
  45. data/ext/tidy/streamio.h +222 -0
  46. data/ext/tidy/tagask.c +286 -0
  47. data/ext/tidy/tags.c +955 -0
  48. data/ext/tidy/tags.h +235 -0
  49. data/ext/tidy/tidy-int.h +129 -0
  50. data/ext/tidy/tidy.h +1097 -0
  51. data/ext/tidy/tidyenum.h +622 -0
  52. data/ext/tidy/tidylib.c +1751 -0
  53. data/ext/tidy/tmbstr.c +306 -0
  54. data/ext/tidy/tmbstr.h +92 -0
  55. data/ext/tidy/utf8.c +539 -0
  56. data/ext/tidy/utf8.h +52 -0
  57. data/ext/tidy/version.h +14 -0
  58. data/ext/tidy/win32tc.c +795 -0
  59. data/ext/tidy/win32tc.h +19 -0
  60. data/spec/spec_helper.rb +5 -0
  61. data/spec/tidy/compat_spec.rb +44 -0
  62. data/spec/tidy/remote_uri_spec.rb +14 -0
  63. data/spec/tidy/test1.html +5 -0
  64. data/spec/tidy/tidy_spec.rb +34 -0
  65. metadata +125 -0
data/ext/tidy/config.c ADDED
@@ -0,0 +1,1746 @@
1
+ /*
2
+ config.c -- read config file and manage config properties
3
+
4
+ (c) 1998-2008 (W3C) MIT, ERCIM, Keio University
5
+ See tidy.h for the copyright notice.
6
+
7
+ CVS Info :
8
+
9
+ $Author: arnaud02 $
10
+ $Date: 2008/06/18 20:18:54 $
11
+ $Revision: 1.111 $
12
+
13
+ */
14
+
15
+ /*
16
+ config files associate a property name with a value.
17
+
18
+ // comments can start at the beginning of a line
19
+ # comments can start at the beginning of a line
20
+ name: short values fit onto one line
21
+ name: a really long value that
22
+ continues on the next line
23
+
24
+ property names are case insensitive and should be less than
25
+ 60 characters in length and must start at the begining of
26
+ the line, as whitespace at the start of a line signifies a
27
+ line continuation.
28
+ */
29
+
30
+ #include "config.h"
31
+ #include "tidy-int.h"
32
+ #include "message.h"
33
+ #include "tmbstr.h"
34
+ #include "tags.h"
35
+
36
+ #ifdef WINDOWS_OS
37
+ #include <io.h>
38
+ #else
39
+ #ifdef DMALLOC
40
+ /*
41
+ macro for valloc() in dmalloc.h may conflict with declaration for valloc() in unistd.h -
42
+ we don't need (debugging for) valloc() here. dmalloc.h should come last but it doesn't.
43
+ */
44
+ #ifdef valloc
45
+ #undef valloc
46
+ #endif
47
+ #endif
48
+ #include <unistd.h>
49
+ #endif
50
+
51
+ #ifdef TIDY_WIN32_MLANG_SUPPORT
52
+ #include "win32tc.h"
53
+ #endif
54
+
55
+ void TY_(InitConfig)( TidyDocImpl* doc )
56
+ {
57
+ TidyClearMemory( &doc->config, sizeof(TidyConfigImpl) );
58
+ TY_(ResetConfigToDefault)( doc );
59
+ }
60
+
61
+ void TY_(FreeConfig)( TidyDocImpl* doc )
62
+ {
63
+ TY_(ResetConfigToDefault)( doc );
64
+ TY_(TakeConfigSnapshot)( doc );
65
+ }
66
+
67
+
68
+ /* Arrange so index can be cast to enum
69
+ */
70
+ static const ctmbstr boolPicks[] =
71
+ {
72
+ "no",
73
+ "yes",
74
+ NULL
75
+ };
76
+
77
+ static const ctmbstr autoBoolPicks[] =
78
+ {
79
+ "no",
80
+ "yes",
81
+ "auto",
82
+ NULL
83
+ };
84
+
85
+ static const ctmbstr repeatAttrPicks[] =
86
+ {
87
+ "keep-first",
88
+ "keep-last",
89
+ NULL
90
+ };
91
+
92
+ static const ctmbstr accessPicks[] =
93
+ {
94
+ "0 (Tidy Classic)",
95
+ "1 (Priority 1 Checks)",
96
+ "2 (Priority 2 Checks)",
97
+ "3 (Priority 3 Checks)",
98
+ NULL
99
+ };
100
+
101
+ static const ctmbstr charEncPicks[] =
102
+ {
103
+ "raw",
104
+ "ascii",
105
+ "latin0",
106
+ "latin1",
107
+ "utf8",
108
+ #ifndef NO_NATIVE_ISO2022_SUPPORT
109
+ "iso2022",
110
+ #endif
111
+ "mac",
112
+ "win1252",
113
+ "ibm858",
114
+
115
+ #if SUPPORT_UTF16_ENCODINGS
116
+ "utf16le",
117
+ "utf16be",
118
+ "utf16",
119
+ #endif
120
+
121
+ #if SUPPORT_ASIAN_ENCODINGS
122
+ "big5",
123
+ "shiftjis",
124
+ #endif
125
+
126
+ NULL
127
+ };
128
+
129
+ static const ctmbstr newlinePicks[] =
130
+ {
131
+ "LF",
132
+ "CRLF",
133
+ "CR",
134
+ NULL
135
+ };
136
+
137
+ static const ctmbstr doctypePicks[] =
138
+ {
139
+ "omit",
140
+ "auto",
141
+ "strict",
142
+ "transitional",
143
+ "user",
144
+ NULL
145
+ };
146
+
147
+ static const ctmbstr sorterPicks[] =
148
+ {
149
+ "none",
150
+ "alpha",
151
+ NULL
152
+ };
153
+
154
+ #define MU TidyMarkup
155
+ #define DG TidyDiagnostics
156
+ #define PP TidyPrettyPrint
157
+ #define CE TidyEncoding
158
+ #define MS TidyMiscellaneous
159
+
160
+ #define IN TidyInteger
161
+ #define BL TidyBoolean
162
+ #define ST TidyString
163
+
164
+ #define XX (TidyConfigCategory)-1
165
+ #define XY (TidyOptionType)-1
166
+
167
+ #define DLF DEFAULT_NL_CONFIG
168
+
169
+ /* If Accessibility checks not supported, make config setting read-only */
170
+ #if SUPPORT_ACCESSIBILITY_CHECKS
171
+ #define ParseAcc ParseInt
172
+ #else
173
+ #define ParseAcc NULL
174
+ #endif
175
+
176
+ static void AdjustConfig( TidyDocImpl* doc );
177
+
178
+ /* parser for integer values */
179
+ static ParseProperty ParseInt;
180
+
181
+ /* parser for 't'/'f', 'true'/'false', 'y'/'n', 'yes'/'no' or '1'/'0' */
182
+ static ParseProperty ParseBool;
183
+
184
+ /* parser for 't'/'f', 'true'/'false', 'y'/'n', 'yes'/'no', '1'/'0'
185
+ or 'auto' */
186
+ static ParseProperty ParseAutoBool;
187
+
188
+ /* a string excluding whitespace */
189
+ static ParseProperty ParseName;
190
+
191
+ /* a CSS1 selector - CSS class naming for -clean option */
192
+ static ParseProperty ParseCSS1Selector;
193
+
194
+ /* a string including whitespace */
195
+ static ParseProperty ParseString;
196
+
197
+ /* a space or comma separated list of tag names */
198
+ static ParseProperty ParseTagNames;
199
+
200
+ /* alpha */
201
+ static ParseProperty ParseSorter;
202
+
203
+ /* RAW, ASCII, LATIN0, LATIN1, UTF8, ISO2022, MACROMAN,
204
+ WIN1252, IBM858, UTF16LE, UTF16BE, UTF16, BIG5, SHIFTJIS
205
+ */
206
+ static ParseProperty ParseCharEnc;
207
+ static ParseProperty ParseNewline;
208
+
209
+ /* omit | auto | strict | loose | <fpi> */
210
+ static ParseProperty ParseDocType;
211
+
212
+ /* keep-first or keep-last? */
213
+ static ParseProperty ParseRepeatAttr;
214
+
215
+
216
+ static const TidyOptionImpl option_defs[] =
217
+ {
218
+ { TidyUnknownOption, MS, "unknown!", IN, 0, NULL, NULL },
219
+ { TidyIndentSpaces, PP, "indent-spaces", IN, 2, ParseInt, NULL },
220
+ { TidyWrapLen, PP, "wrap", IN, 68, ParseInt, NULL },
221
+ { TidyTabSize, PP, "tab-size", IN, 8, ParseInt, NULL },
222
+ { TidyCharEncoding, CE, "char-encoding", IN, ASCII, ParseCharEnc, charEncPicks },
223
+ { TidyInCharEncoding, CE, "input-encoding", IN, LATIN1, ParseCharEnc, charEncPicks },
224
+ { TidyOutCharEncoding, CE, "output-encoding", IN, ASCII, ParseCharEnc, charEncPicks },
225
+ { TidyNewline, CE, "newline", IN, DLF, ParseNewline, newlinePicks },
226
+ { TidyDoctypeMode, MU, "doctype-mode", IN, TidyDoctypeAuto, NULL, doctypePicks },
227
+ { TidyDoctype, MU, "doctype", ST, 0, ParseDocType, doctypePicks },
228
+ { TidyDuplicateAttrs, MU, "repeated-attributes", IN, TidyKeepLast, ParseRepeatAttr, repeatAttrPicks },
229
+ { TidyAltText, MU, "alt-text", ST, 0, ParseString, NULL },
230
+
231
+ /* obsolete */
232
+ { TidySlideStyle, MS, "slide-style", ST, 0, ParseName, NULL },
233
+
234
+ { TidyErrFile, MS, "error-file", ST, 0, ParseString, NULL },
235
+ { TidyOutFile, MS, "output-file", ST, 0, ParseString, NULL },
236
+ { TidyWriteBack, MS, "write-back", BL, no, ParseBool, boolPicks },
237
+ { TidyShowMarkup, PP, "markup", BL, yes, ParseBool, boolPicks },
238
+ { TidyShowWarnings, DG, "show-warnings", BL, yes, ParseBool, boolPicks },
239
+ { TidyQuiet, MS, "quiet", BL, no, ParseBool, boolPicks },
240
+ { TidyIndentContent, PP, "indent", IN, TidyNoState, ParseAutoBool, autoBoolPicks },
241
+ { TidyHideEndTags, MU, "hide-endtags", BL, no, ParseBool, boolPicks },
242
+ { TidyXmlTags, MU, "input-xml", BL, no, ParseBool, boolPicks },
243
+ { TidyXmlOut, MU, "output-xml", BL, no, ParseBool, boolPicks },
244
+ { TidyXhtmlOut, MU, "output-xhtml", BL, no, ParseBool, boolPicks },
245
+ { TidyHtmlOut, MU, "output-html", BL, no, ParseBool, boolPicks },
246
+ { TidyXmlDecl, MU, "add-xml-decl", BL, no, ParseBool, boolPicks },
247
+ { TidyUpperCaseTags, MU, "uppercase-tags", BL, no, ParseBool, boolPicks },
248
+ { TidyUpperCaseAttrs, MU, "uppercase-attributes", BL, no, ParseBool, boolPicks },
249
+ { TidyMakeBare, MU, "bare", BL, no, ParseBool, boolPicks },
250
+ { TidyMakeClean, MU, "clean", BL, no, ParseBool, boolPicks },
251
+ { TidyLogicalEmphasis, MU, "logical-emphasis", BL, no, ParseBool, boolPicks },
252
+ { TidyDropPropAttrs, MU, "drop-proprietary-attributes", BL, no, ParseBool, boolPicks },
253
+ { TidyDropFontTags, MU, "drop-font-tags", BL, no, ParseBool, boolPicks },
254
+ { TidyDropEmptyParas, MU, "drop-empty-paras", BL, yes, ParseBool, boolPicks },
255
+ { TidyFixComments, MU, "fix-bad-comments", BL, yes, ParseBool, boolPicks },
256
+ { TidyBreakBeforeBR, PP, "break-before-br", BL, no, ParseBool, boolPicks },
257
+
258
+ /* obsolete */
259
+ { TidyBurstSlides, PP, "split", BL, no, ParseBool, boolPicks },
260
+
261
+ { TidyNumEntities, MU, "numeric-entities", BL, no, ParseBool, boolPicks },
262
+ { TidyQuoteMarks, MU, "quote-marks", BL, no, ParseBool, boolPicks },
263
+ { TidyQuoteNbsp, MU, "quote-nbsp", BL, yes, ParseBool, boolPicks },
264
+ { TidyQuoteAmpersand, MU, "quote-ampersand", BL, yes, ParseBool, boolPicks },
265
+ { TidyWrapAttVals, PP, "wrap-attributes", BL, no, ParseBool, boolPicks },
266
+ { TidyWrapScriptlets, PP, "wrap-script-literals", BL, no, ParseBool, boolPicks },
267
+ { TidyWrapSection, PP, "wrap-sections", BL, yes, ParseBool, boolPicks },
268
+ { TidyWrapAsp, PP, "wrap-asp", BL, yes, ParseBool, boolPicks },
269
+ { TidyWrapJste, PP, "wrap-jste", BL, yes, ParseBool, boolPicks },
270
+ { TidyWrapPhp, PP, "wrap-php", BL, yes, ParseBool, boolPicks },
271
+ { TidyFixBackslash, MU, "fix-backslash", BL, yes, ParseBool, boolPicks },
272
+ { TidyIndentAttributes, PP, "indent-attributes", BL, no, ParseBool, boolPicks },
273
+ { TidyXmlPIs, MU, "assume-xml-procins", BL, no, ParseBool, boolPicks },
274
+ { TidyXmlSpace, MU, "add-xml-space", BL, no, ParseBool, boolPicks },
275
+ { TidyEncloseBodyText, MU, "enclose-text", BL, no, ParseBool, boolPicks },
276
+ { TidyEncloseBlockText, MU, "enclose-block-text", BL, no, ParseBool, boolPicks },
277
+ { TidyKeepFileTimes, MS, "keep-time", BL, no, ParseBool, boolPicks },
278
+ { TidyWord2000, MU, "word-2000", BL, no, ParseBool, boolPicks },
279
+ { TidyMark, MS, "tidy-mark", BL, yes, ParseBool, boolPicks },
280
+ { TidyEmacs, MS, "gnu-emacs", BL, no, ParseBool, boolPicks },
281
+ { TidyEmacsFile, MS, "gnu-emacs-file", ST, 0, ParseString, NULL },
282
+ { TidyLiteralAttribs, MU, "literal-attributes", BL, no, ParseBool, boolPicks },
283
+ { TidyBodyOnly, MU, "show-body-only", IN, no, ParseAutoBool, autoBoolPicks },
284
+ { TidyFixUri, MU, "fix-uri", BL, yes, ParseBool, boolPicks },
285
+ { TidyLowerLiterals, MU, "lower-literals", BL, yes, ParseBool, boolPicks },
286
+ { TidyHideComments, MU, "hide-comments", BL, no, ParseBool, boolPicks },
287
+ { TidyIndentCdata, MU, "indent-cdata", BL, no, ParseBool, boolPicks },
288
+ { TidyForceOutput, MS, "force-output", BL, no, ParseBool, boolPicks },
289
+ { TidyShowErrors, DG, "show-errors", IN, 6, ParseInt, NULL },
290
+ { TidyAsciiChars, CE, "ascii-chars", BL, no, ParseBool, boolPicks },
291
+ { TidyJoinClasses, MU, "join-classes", BL, no, ParseBool, boolPicks },
292
+ { TidyJoinStyles, MU, "join-styles", BL, yes, ParseBool, boolPicks },
293
+ { TidyEscapeCdata, MU, "escape-cdata", BL, no, ParseBool, boolPicks },
294
+ #if SUPPORT_ASIAN_ENCODINGS
295
+ { TidyLanguage, CE, "language", ST, 0, ParseName, NULL },
296
+ { TidyNCR, MU, "ncr", BL, yes, ParseBool, boolPicks },
297
+ #endif
298
+ #if SUPPORT_UTF16_ENCODINGS
299
+ { TidyOutputBOM, CE, "output-bom", IN, TidyAutoState, ParseAutoBool, autoBoolPicks },
300
+ #endif
301
+ { TidyReplaceColor, MU, "replace-color", BL, no, ParseBool, boolPicks },
302
+ { TidyCSSPrefix, MU, "css-prefix", ST, 0, ParseCSS1Selector, NULL },
303
+ { TidyInlineTags, MU, "new-inline-tags", ST, 0, ParseTagNames, NULL },
304
+ { TidyBlockTags, MU, "new-blocklevel-tags", ST, 0, ParseTagNames, NULL },
305
+ { TidyEmptyTags, MU, "new-empty-tags", ST, 0, ParseTagNames, NULL },
306
+ { TidyPreTags, MU, "new-pre-tags", ST, 0, ParseTagNames, NULL },
307
+ { TidyAccessibilityCheckLevel, DG, "accessibility-check", IN, 0, ParseAcc, accessPicks },
308
+ { TidyVertSpace, PP, "vertical-space", BL, no, ParseBool, boolPicks },
309
+ #if SUPPORT_ASIAN_ENCODINGS
310
+ { TidyPunctWrap, PP, "punctuation-wrap", BL, no, ParseBool, boolPicks },
311
+ #endif
312
+ { TidyMergeDivs, MU, "merge-divs", IN, TidyAutoState, ParseAutoBool, autoBoolPicks },
313
+ { TidyDecorateInferredUL, MU, "decorate-inferred-ul", BL, no, ParseBool, boolPicks },
314
+ { TidyPreserveEntities, MU, "preserve-entities", BL, no, ParseBool, boolPicks },
315
+ { TidySortAttributes, PP, "sort-attributes", IN, TidySortAttrNone,ParseSorter, sorterPicks },
316
+ { TidyMergeSpans, MU, "merge-spans", IN, TidyAutoState, ParseAutoBool, autoBoolPicks },
317
+ { TidyAnchorAsName, MU, "anchor-as-name", BL, yes, ParseBool, boolPicks },
318
+ { N_TIDY_OPTIONS, XX, NULL, XY, 0, NULL, NULL }
319
+ };
320
+
321
+ /* Should only be called by options set by name
322
+ ** thus, it is cheaper to do a few scans than set
323
+ ** up every option in a hash table.
324
+ */
325
+ const TidyOptionImpl* TY_(lookupOption)( ctmbstr s )
326
+ {
327
+ const TidyOptionImpl* np = option_defs;
328
+ for ( /**/; np < option_defs + N_TIDY_OPTIONS; ++np )
329
+ {
330
+ if ( TY_(tmbstrcasecmp)(s, np->name) == 0 )
331
+ return np;
332
+ }
333
+ return NULL;
334
+ }
335
+
336
+ const TidyOptionImpl* TY_(getOption)( TidyOptionId optId )
337
+ {
338
+ if ( optId < N_TIDY_OPTIONS )
339
+ return option_defs + optId;
340
+ return NULL;
341
+ }
342
+
343
+
344
+ static void FreeOptionValue( TidyDocImpl* doc, const TidyOptionImpl* option, TidyOptionValue* value )
345
+ {
346
+ if ( option->type == TidyString && value->p && value->p != option->pdflt )
347
+ TidyDocFree( doc, value->p );
348
+ }
349
+
350
+ static void CopyOptionValue( TidyDocImpl* doc, const TidyOptionImpl* option,
351
+ TidyOptionValue* oldval, const TidyOptionValue* newval )
352
+ {
353
+ assert( oldval != NULL );
354
+ FreeOptionValue( doc, option, oldval );
355
+
356
+ if ( option->type == TidyString )
357
+ {
358
+ if ( newval->p && newval->p != option->pdflt )
359
+ oldval->p = TY_(tmbstrdup)( doc->allocator, newval->p );
360
+ else
361
+ oldval->p = newval->p;
362
+ }
363
+ else
364
+ oldval->v = newval->v;
365
+ }
366
+
367
+
368
+ static Bool SetOptionValue( TidyDocImpl* doc, TidyOptionId optId, ctmbstr val )
369
+ {
370
+ const TidyOptionImpl* option = &option_defs[ optId ];
371
+ Bool status = ( optId < N_TIDY_OPTIONS );
372
+ if ( status )
373
+ {
374
+ assert( option->id == optId && option->type == TidyString );
375
+ FreeOptionValue( doc, option, &doc->config.value[ optId ] );
376
+ doc->config.value[ optId ].p = TY_(tmbstrdup)( doc->allocator, val );
377
+ }
378
+ return status;
379
+ }
380
+
381
+ Bool TY_(SetOptionInt)( TidyDocImpl* doc, TidyOptionId optId, ulong val )
382
+ {
383
+ Bool status = ( optId < N_TIDY_OPTIONS );
384
+ if ( status )
385
+ {
386
+ assert( option_defs[ optId ].type == TidyInteger );
387
+ doc->config.value[ optId ].v = val;
388
+ }
389
+ return status;
390
+ }
391
+
392
+ Bool TY_(SetOptionBool)( TidyDocImpl* doc, TidyOptionId optId, Bool val )
393
+ {
394
+ Bool status = ( optId < N_TIDY_OPTIONS );
395
+ if ( status )
396
+ {
397
+ assert( option_defs[ optId ].type == TidyBoolean );
398
+ doc->config.value[ optId ].v = val;
399
+ }
400
+ return status;
401
+ }
402
+
403
+ static void GetOptionDefault( const TidyOptionImpl* option,
404
+ TidyOptionValue* dflt )
405
+ {
406
+ if ( option->type == TidyString )
407
+ dflt->p = (char*)option->pdflt;
408
+ else
409
+ dflt->v = option->dflt;
410
+ }
411
+
412
+ static Bool OptionValueEqDefault( const TidyOptionImpl* option,
413
+ const TidyOptionValue* val )
414
+ {
415
+ return ( option->type == TidyString ) ?
416
+ val->p == option->pdflt :
417
+ val->v == option->dflt;
418
+ }
419
+
420
+ Bool TY_(ResetOptionToDefault)( TidyDocImpl* doc, TidyOptionId optId )
421
+ {
422
+ Bool status = ( optId > 0 && optId < N_TIDY_OPTIONS );
423
+ if ( status )
424
+ {
425
+ TidyOptionValue dflt;
426
+ const TidyOptionImpl* option = option_defs + optId;
427
+ TidyOptionValue* value = &doc->config.value[ optId ];
428
+ assert( optId == option->id );
429
+ GetOptionDefault( option, &dflt );
430
+ CopyOptionValue( doc, option, value, &dflt );
431
+ }
432
+ return status;
433
+ }
434
+
435
+ static void ReparseTagType( TidyDocImpl* doc, TidyOptionId optId )
436
+ {
437
+ ctmbstr tagdecl = cfgStr( doc, optId );
438
+ tmbstr dupdecl = TY_(tmbstrdup)( doc->allocator, tagdecl );
439
+ TY_(ParseConfigValue)( doc, optId, dupdecl );
440
+ TidyDocFree( doc, dupdecl );
441
+ }
442
+
443
+ static Bool OptionValueIdentical( const TidyOptionImpl* option,
444
+ const TidyOptionValue* val1,
445
+ const TidyOptionValue* val2 )
446
+ {
447
+ if ( option->type == TidyString )
448
+ {
449
+ if ( val1->p == val2->p )
450
+ return yes;
451
+ if ( !val1->p || !val2->p )
452
+ return no;
453
+ return TY_(tmbstrcmp)( val1->p, val2->p ) == 0;
454
+ }
455
+ else
456
+ return val1->v == val2->v;
457
+ }
458
+
459
+ static Bool NeedReparseTagDecls( const TidyOptionValue* current,
460
+ const TidyOptionValue* new,
461
+ uint *changedUserTags )
462
+ {
463
+ Bool ret = no;
464
+ uint ixVal;
465
+ const TidyOptionImpl* option = option_defs;
466
+ *changedUserTags = tagtype_null;
467
+
468
+ for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal )
469
+ {
470
+ assert( ixVal == (uint) option->id );
471
+ switch (option->id)
472
+ {
473
+ #define TEST_USERTAGS(USERTAGOPTION,USERTAGTYPE) \
474
+ case USERTAGOPTION: \
475
+ if (!OptionValueIdentical(option,&current[ixVal],&new[ixVal])) \
476
+ { \
477
+ *changedUserTags |= USERTAGTYPE; \
478
+ ret = yes; \
479
+ } \
480
+ break
481
+ TEST_USERTAGS(TidyInlineTags,tagtype_inline);
482
+ TEST_USERTAGS(TidyBlockTags,tagtype_block);
483
+ TEST_USERTAGS(TidyEmptyTags,tagtype_empty);
484
+ TEST_USERTAGS(TidyPreTags,tagtype_pre);
485
+ default:
486
+ break;
487
+ }
488
+ }
489
+ return ret;
490
+ }
491
+
492
+ static void ReparseTagDecls( TidyDocImpl* doc, uint changedUserTags )
493
+ {
494
+ #define REPARSE_USERTAGS(USERTAGOPTION,USERTAGTYPE) \
495
+ if ( changedUserTags & USERTAGTYPE ) \
496
+ { \
497
+ TY_(FreeDeclaredTags)( doc, USERTAGTYPE ); \
498
+ ReparseTagType( doc, USERTAGOPTION ); \
499
+ }
500
+ REPARSE_USERTAGS(TidyInlineTags,tagtype_inline);
501
+ REPARSE_USERTAGS(TidyBlockTags,tagtype_block);
502
+ REPARSE_USERTAGS(TidyEmptyTags,tagtype_empty);
503
+ REPARSE_USERTAGS(TidyPreTags,tagtype_pre);
504
+ }
505
+
506
+ void TY_(ResetConfigToDefault)( TidyDocImpl* doc )
507
+ {
508
+ uint ixVal;
509
+ const TidyOptionImpl* option = option_defs;
510
+ TidyOptionValue* value = &doc->config.value[ 0 ];
511
+ for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal )
512
+ {
513
+ TidyOptionValue dflt;
514
+ assert( ixVal == (uint) option->id );
515
+ GetOptionDefault( option, &dflt );
516
+ CopyOptionValue( doc, option, &value[ixVal], &dflt );
517
+ }
518
+ TY_(FreeDeclaredTags)( doc, tagtype_null );
519
+ }
520
+
521
+ void TY_(TakeConfigSnapshot)( TidyDocImpl* doc )
522
+ {
523
+ uint ixVal;
524
+ const TidyOptionImpl* option = option_defs;
525
+ const TidyOptionValue* value = &doc->config.value[ 0 ];
526
+ TidyOptionValue* snap = &doc->config.snapshot[ 0 ];
527
+
528
+ AdjustConfig( doc ); /* Make sure it's consistent */
529
+ for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal )
530
+ {
531
+ assert( ixVal == (uint) option->id );
532
+ CopyOptionValue( doc, option, &snap[ixVal], &value[ixVal] );
533
+ }
534
+ }
535
+
536
+ void TY_(ResetConfigToSnapshot)( TidyDocImpl* doc )
537
+ {
538
+ uint ixVal;
539
+ const TidyOptionImpl* option = option_defs;
540
+ TidyOptionValue* value = &doc->config.value[ 0 ];
541
+ const TidyOptionValue* snap = &doc->config.snapshot[ 0 ];
542
+ uint changedUserTags;
543
+ Bool needReparseTagsDecls = NeedReparseTagDecls( value, snap,
544
+ &changedUserTags );
545
+
546
+ for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal )
547
+ {
548
+ assert( ixVal == (uint) option->id );
549
+ CopyOptionValue( doc, option, &value[ixVal], &snap[ixVal] );
550
+ }
551
+ if ( needReparseTagsDecls )
552
+ ReparseTagDecls( doc, changedUserTags );
553
+ }
554
+
555
+ void TY_(CopyConfig)( TidyDocImpl* docTo, TidyDocImpl* docFrom )
556
+ {
557
+ if ( docTo != docFrom )
558
+ {
559
+ uint ixVal;
560
+ const TidyOptionImpl* option = option_defs;
561
+ const TidyOptionValue* from = &docFrom->config.value[ 0 ];
562
+ TidyOptionValue* to = &docTo->config.value[ 0 ];
563
+ uint changedUserTags;
564
+ Bool needReparseTagsDecls = NeedReparseTagDecls( to, from,
565
+ &changedUserTags );
566
+
567
+ TY_(TakeConfigSnapshot)( docTo );
568
+ for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal )
569
+ {
570
+ assert( ixVal == (uint) option->id );
571
+ CopyOptionValue( docTo, option, &to[ixVal], &from[ixVal] );
572
+ }
573
+ if ( needReparseTagsDecls )
574
+ ReparseTagDecls( docTo, changedUserTags );
575
+ AdjustConfig( docTo ); /* Make sure it's consistent */
576
+ }
577
+ }
578
+
579
+
580
+ #ifdef _DEBUG
581
+
582
+ /* Debug accessor functions will be type-safe and assert option type match */
583
+ ulong TY_(_cfgGet)( TidyDocImpl* doc, TidyOptionId optId )
584
+ {
585
+ assert( optId < N_TIDY_OPTIONS );
586
+ return doc->config.value[ optId ].v;
587
+ }
588
+
589
+ Bool TY_(_cfgGetBool)( TidyDocImpl* doc, TidyOptionId optId )
590
+ {
591
+ ulong val = TY_(_cfgGet)( doc, optId );
592
+ const TidyOptionImpl* opt = &option_defs[ optId ];
593
+ assert( opt && opt->type == TidyBoolean );
594
+ return (Bool) val;
595
+ }
596
+
597
+ TidyTriState TY_(_cfgGetAutoBool)( TidyDocImpl* doc, TidyOptionId optId )
598
+ {
599
+ ulong val = TY_(_cfgGet)( doc, optId );
600
+ const TidyOptionImpl* opt = &option_defs[ optId ];
601
+ assert( opt && opt->type == TidyInteger
602
+ && opt->parser == ParseAutoBool );
603
+ return (TidyTriState) val;
604
+ }
605
+
606
+ ctmbstr TY_(_cfgGetString)( TidyDocImpl* doc, TidyOptionId optId )
607
+ {
608
+ const TidyOptionImpl* opt;
609
+
610
+ assert( optId < N_TIDY_OPTIONS );
611
+ opt = &option_defs[ optId ];
612
+ assert( opt && opt->type == TidyString );
613
+ return doc->config.value[ optId ].p;
614
+ }
615
+ #endif
616
+
617
+
618
+ #if 0
619
+ /* for use with Gnu Emacs */
620
+ void SetEmacsFilename( TidyDocImpl* doc, ctmbstr filename )
621
+ {
622
+ SetOptionValue( doc, TidyEmacsFile, filename );
623
+ }
624
+ #endif
625
+
626
+ static tchar GetC( TidyConfigImpl* config )
627
+ {
628
+ if ( config->cfgIn )
629
+ return TY_(ReadChar)( config->cfgIn );
630
+ return EndOfStream;
631
+ }
632
+
633
+ static tchar FirstChar( TidyConfigImpl* config )
634
+ {
635
+ config->c = GetC( config );
636
+ return config->c;
637
+ }
638
+
639
+ static tchar AdvanceChar( TidyConfigImpl* config )
640
+ {
641
+ if ( config->c != EndOfStream )
642
+ config->c = GetC( config );
643
+ return config->c;
644
+ }
645
+
646
+ static tchar SkipWhite( TidyConfigImpl* config )
647
+ {
648
+ while ( TY_(IsWhite)(config->c) && !TY_(IsNewline)(config->c) )
649
+ config->c = GetC( config );
650
+ return config->c;
651
+ }
652
+
653
+ /* skip until end of line
654
+ static tchar SkipToEndofLine( TidyConfigImpl* config )
655
+ {
656
+ while ( config->c != EndOfStream )
657
+ {
658
+ config->c = GetC( config );
659
+ if ( config->c == '\n' || config->c == '\r' )
660
+ break;
661
+ }
662
+ return config->c;
663
+ }
664
+ */
665
+
666
+ /*
667
+ skip over line continuations
668
+ to start of next property
669
+ */
670
+ static uint NextProperty( TidyConfigImpl* config )
671
+ {
672
+ do
673
+ {
674
+ /* skip to end of line */
675
+ while ( config->c != '\n' && config->c != '\r' && config->c != EndOfStream )
676
+ config->c = GetC( config );
677
+
678
+ /* treat \r\n \r or \n as line ends */
679
+ if ( config->c == '\r' )
680
+ config->c = GetC( config );
681
+
682
+ if ( config->c == '\n' )
683
+ config->c = GetC( config );
684
+ }
685
+ while ( TY_(IsWhite)(config->c) ); /* line continuation? */
686
+
687
+ return config->c;
688
+ }
689
+
690
+ /*
691
+ Todd Lewis contributed this code for expanding
692
+ ~/foo or ~your/foo according to $HOME and your
693
+ user name. This will work partially on any system
694
+ which defines $HOME. Support for ~user/foo will
695
+ work on systems that support getpwnam(userid),
696
+ namely Unix/Linux.
697
+ */
698
+ static ctmbstr ExpandTilde( TidyDocImpl* doc, ctmbstr filename )
699
+ {
700
+ char *home_dir = NULL;
701
+
702
+ if ( !filename )
703
+ return NULL;
704
+
705
+ if ( filename[0] != '~' )
706
+ return filename;
707
+
708
+ if (filename[1] == '/')
709
+ {
710
+ home_dir = getenv("HOME");
711
+ if ( home_dir )
712
+ ++filename;
713
+ }
714
+ #ifdef SUPPORT_GETPWNAM
715
+ else
716
+ {
717
+ struct passwd *passwd = NULL;
718
+ ctmbstr s = filename + 1;
719
+ tmbstr t;
720
+
721
+ while ( *s && *s != '/' )
722
+ s++;
723
+
724
+ if ( t = TidyDocAlloc(doc, s - filename) )
725
+ {
726
+ memcpy(t, filename+1, s-filename-1);
727
+ t[s-filename-1] = 0;
728
+
729
+ passwd = getpwnam(t);
730
+
731
+ TidyDocFree(doc, t);
732
+ }
733
+
734
+ if ( passwd )
735
+ {
736
+ filename = s;
737
+ home_dir = passwd->pw_dir;
738
+ }
739
+ }
740
+ #endif /* SUPPORT_GETPWNAM */
741
+
742
+ if ( home_dir )
743
+ {
744
+ uint len = TY_(tmbstrlen)(filename) + TY_(tmbstrlen)(home_dir) + 1;
745
+ tmbstr p = (tmbstr)TidyDocAlloc( doc, len );
746
+ TY_(tmbstrcpy)( p, home_dir );
747
+ TY_(tmbstrcat)( p, filename );
748
+ return (ctmbstr) p;
749
+ }
750
+ return (ctmbstr) filename;
751
+ }
752
+
753
+ Bool TIDY_CALL tidyFileExists( TidyDoc tdoc, ctmbstr filename )
754
+ {
755
+ TidyDocImpl* doc = tidyDocToImpl( tdoc );
756
+ ctmbstr fname = (tmbstr) ExpandTilde( doc, filename );
757
+ #ifndef NO_ACCESS_SUPPORT
758
+ Bool exists = ( access(fname, 0) == 0 );
759
+ #else
760
+ Bool exists;
761
+ /* at present */
762
+ FILE* fin = fopen(fname, "r");
763
+ if (fin != NULL)
764
+ fclose(fin);
765
+ exists = ( fin != NULL );
766
+ #endif
767
+ if ( fname != filename )
768
+ TidyDocFree( doc, (tmbstr) fname );
769
+ return exists;
770
+ }
771
+
772
+
773
+ #ifndef TIDY_MAX_NAME
774
+ #define TIDY_MAX_NAME 64
775
+ #endif
776
+
777
+ int TY_(ParseConfigFile)( TidyDocImpl* doc, ctmbstr file )
778
+ {
779
+ return TY_(ParseConfigFileEnc)( doc, file, "ascii" );
780
+ }
781
+
782
+ /* open the file and parse its contents
783
+ */
784
+ int TY_(ParseConfigFileEnc)( TidyDocImpl* doc, ctmbstr file, ctmbstr charenc )
785
+ {
786
+ uint opterrs = doc->optionErrors;
787
+ tmbstr fname = (tmbstr) ExpandTilde( doc, file );
788
+ TidyConfigImpl* cfg = &doc->config;
789
+ FILE* fin = fopen( fname, "r" );
790
+ int enc = TY_(CharEncodingId)( doc, charenc );
791
+
792
+ if ( fin == NULL || enc < 0 )
793
+ {
794
+ TY_(FileError)( doc, fname, TidyConfig );
795
+ return -1;
796
+ }
797
+ else
798
+ {
799
+ tchar c;
800
+ cfg->cfgIn = TY_(FileInput)( doc, fin, enc );
801
+ c = FirstChar( cfg );
802
+
803
+ for ( c = SkipWhite(cfg); c != EndOfStream; c = NextProperty(cfg) )
804
+ {
805
+ uint ix = 0;
806
+ tmbchar name[ TIDY_MAX_NAME ] = {0};
807
+
808
+ /* // or # start a comment */
809
+ if ( c == '/' || c == '#' )
810
+ continue;
811
+
812
+ while ( ix < sizeof(name)-1 && c != '\n' && c != EndOfStream && c != ':' )
813
+ {
814
+ name[ ix++ ] = (tmbchar) c; /* Option names all ASCII */
815
+ c = AdvanceChar( cfg );
816
+ }
817
+
818
+ if ( c == ':' )
819
+ {
820
+ const TidyOptionImpl* option = TY_(lookupOption)( name );
821
+ c = AdvanceChar( cfg );
822
+ if ( option )
823
+ option->parser( doc, option );
824
+ else
825
+ {
826
+ if (NULL != doc->pOptCallback)
827
+ {
828
+ TidyConfigImpl* cfg = &doc->config;
829
+ tmbchar buf[8192];
830
+ uint i = 0;
831
+ tchar delim = 0;
832
+ Bool waswhite = yes;
833
+
834
+ tchar c = SkipWhite( cfg );
835
+
836
+ if ( c == '"' || c == '\'' )
837
+ {
838
+ delim = c;
839
+ c = AdvanceChar( cfg );
840
+ }
841
+
842
+ while ( i < sizeof(buf)-2 && c != EndOfStream && c != '\r' && c != '\n' )
843
+ {
844
+ if ( delim && c == delim )
845
+ break;
846
+
847
+ if ( TY_(IsWhite)(c) )
848
+ {
849
+ if ( waswhite )
850
+ {
851
+ c = AdvanceChar( cfg );
852
+ continue;
853
+ }
854
+ c = ' ';
855
+ }
856
+ else
857
+ waswhite = no;
858
+
859
+ buf[i++] = (tmbchar) c;
860
+ c = AdvanceChar( cfg );
861
+ }
862
+ buf[i] = '\0';
863
+ if (no == (*doc->pOptCallback)( name, buf ))
864
+ TY_(ReportUnknownOption)( doc, name );
865
+ }
866
+ else
867
+ TY_(ReportUnknownOption)( doc, name );
868
+ }
869
+ }
870
+ }
871
+
872
+ TY_(freeFileSource)(&cfg->cfgIn->source, yes);
873
+ TY_(freeStreamIn)( cfg->cfgIn );
874
+ cfg->cfgIn = NULL;
875
+ }
876
+
877
+ if ( fname != (tmbstr) file )
878
+ TidyDocFree( doc, fname );
879
+
880
+ AdjustConfig( doc );
881
+
882
+ /* any new config errors? If so, return warning status. */
883
+ return (doc->optionErrors > opterrs ? 1 : 0);
884
+ }
885
+
886
+ /* returns false if unknown option, missing parameter,
887
+ ** or option doesn't use parameter
888
+ */
889
+ Bool TY_(ParseConfigOption)( TidyDocImpl* doc, ctmbstr optnam, ctmbstr optval )
890
+ {
891
+ const TidyOptionImpl* option = TY_(lookupOption)( optnam );
892
+ Bool status = ( option != NULL );
893
+ if ( !status )
894
+ {
895
+ /* Not a standard tidy option. Check to see if the user application
896
+ recognizes it */
897
+ if (NULL != doc->pOptCallback)
898
+ status = (*doc->pOptCallback)( optnam, optval );
899
+ if (!status)
900
+ TY_(ReportUnknownOption)( doc, optnam );
901
+ }
902
+ else
903
+ status = TY_(ParseConfigValue)( doc, option->id, optval );
904
+ return status;
905
+ }
906
+
907
+ /* returns false if unknown option, missing parameter,
908
+ ** or option doesn't use parameter
909
+ */
910
+ Bool TY_(ParseConfigValue)( TidyDocImpl* doc, TidyOptionId optId, ctmbstr optval )
911
+ {
912
+ const TidyOptionImpl* option = option_defs + optId;
913
+ Bool status = ( optId < N_TIDY_OPTIONS && optval != NULL );
914
+
915
+ if ( !status )
916
+ TY_(ReportBadArgument)( doc, option->name );
917
+ else
918
+ {
919
+ TidyBuffer inbuf; /* Set up input source */
920
+ tidyBufInitWithAllocator( &inbuf, doc->allocator );
921
+ tidyBufAttach( &inbuf, (byte*)optval, TY_(tmbstrlen)(optval)+1 );
922
+ doc->config.cfgIn = TY_(BufferInput)( doc, &inbuf, ASCII );
923
+ doc->config.c = GetC( &doc->config );
924
+
925
+ status = option->parser( doc, option );
926
+
927
+ TY_(freeStreamIn)(doc->config.cfgIn); /* Release input source */
928
+ doc->config.cfgIn = NULL;
929
+ tidyBufDetach( &inbuf );
930
+ }
931
+ return status;
932
+ }
933
+
934
+
935
+ /* ensure that char encodings are self consistent */
936
+ Bool TY_(AdjustCharEncoding)( TidyDocImpl* doc, int encoding )
937
+ {
938
+ int outenc = -1;
939
+ int inenc = -1;
940
+
941
+ switch( encoding )
942
+ {
943
+ case MACROMAN:
944
+ inenc = MACROMAN;
945
+ outenc = ASCII;
946
+ break;
947
+
948
+ case WIN1252:
949
+ inenc = WIN1252;
950
+ outenc = ASCII;
951
+ break;
952
+
953
+ case IBM858:
954
+ inenc = IBM858;
955
+ outenc = ASCII;
956
+ break;
957
+
958
+ case ASCII:
959
+ inenc = LATIN1;
960
+ outenc = ASCII;
961
+ break;
962
+
963
+ case LATIN0:
964
+ inenc = LATIN0;
965
+ outenc = ASCII;
966
+ break;
967
+
968
+ case RAW:
969
+ case LATIN1:
970
+ case UTF8:
971
+ #ifndef NO_NATIVE_ISO2022_SUPPORT
972
+ case ISO2022:
973
+ #endif
974
+
975
+ #if SUPPORT_UTF16_ENCODINGS
976
+ case UTF16LE:
977
+ case UTF16BE:
978
+ case UTF16:
979
+ #endif
980
+ #if SUPPORT_ASIAN_ENCODINGS
981
+ case SHIFTJIS:
982
+ case BIG5:
983
+ #endif
984
+ inenc = outenc = encoding;
985
+ break;
986
+ }
987
+
988
+ if ( inenc >= 0 )
989
+ {
990
+ TY_(SetOptionInt)( doc, TidyCharEncoding, encoding );
991
+ TY_(SetOptionInt)( doc, TidyInCharEncoding, inenc );
992
+ TY_(SetOptionInt)( doc, TidyOutCharEncoding, outenc );
993
+ return yes;
994
+ }
995
+ return no;
996
+ }
997
+
998
+ /* ensure that config is self consistent */
999
+ void AdjustConfig( TidyDocImpl* doc )
1000
+ {
1001
+ if ( cfgBool(doc, TidyEncloseBlockText) )
1002
+ TY_(SetOptionBool)( doc, TidyEncloseBodyText, yes );
1003
+
1004
+ if ( cfgAutoBool(doc, TidyIndentContent) == TidyNoState )
1005
+ TY_(SetOptionInt)( doc, TidyIndentSpaces, 0 );
1006
+
1007
+ /* disable wrapping */
1008
+ if ( cfg(doc, TidyWrapLen) == 0 )
1009
+ TY_(SetOptionInt)( doc, TidyWrapLen, 0x7FFFFFFF );
1010
+
1011
+ /* Word 2000 needs o:p to be declared as inline */
1012
+ if ( cfgBool(doc, TidyWord2000) )
1013
+ {
1014
+ doc->config.defined_tags |= tagtype_inline;
1015
+ TY_(DefineTag)( doc, tagtype_inline, "o:p" );
1016
+ }
1017
+
1018
+ /* #480701 disable XHTML output flag if both output-xhtml and xml input are set */
1019
+ if ( cfgBool(doc, TidyXmlTags) )
1020
+ TY_(SetOptionBool)( doc, TidyXhtmlOut, no );
1021
+
1022
+ /* XHTML is written in lower case */
1023
+ if ( cfgBool(doc, TidyXhtmlOut) )
1024
+ {
1025
+ TY_(SetOptionBool)( doc, TidyXmlOut, yes );
1026
+ TY_(SetOptionBool)( doc, TidyUpperCaseTags, no );
1027
+ TY_(SetOptionBool)( doc, TidyUpperCaseAttrs, no );
1028
+ /* TY_(SetOptionBool)( doc, TidyXmlPIs, yes ); */
1029
+ }
1030
+
1031
+ /* if XML in, then XML out */
1032
+ if ( cfgBool(doc, TidyXmlTags) )
1033
+ {
1034
+ TY_(SetOptionBool)( doc, TidyXmlOut, yes );
1035
+ TY_(SetOptionBool)( doc, TidyXmlPIs, yes );
1036
+ }
1037
+
1038
+ /* #427837 - fix by Dave Raggett 02 Jun 01
1039
+ ** generate <?xml version="1.0" encoding="iso-8859-1"?>
1040
+ ** if the output character encoding is Latin-1 etc.
1041
+ */
1042
+ if ( cfg(doc, TidyOutCharEncoding) != ASCII &&
1043
+ cfg(doc, TidyOutCharEncoding) != UTF8 &&
1044
+ #if SUPPORT_UTF16_ENCODINGS
1045
+ cfg(doc, TidyOutCharEncoding) != UTF16 &&
1046
+ cfg(doc, TidyOutCharEncoding) != UTF16BE &&
1047
+ cfg(doc, TidyOutCharEncoding) != UTF16LE &&
1048
+ #endif
1049
+ cfg(doc, TidyOutCharEncoding) != RAW &&
1050
+ cfgBool(doc, TidyXmlOut) )
1051
+ {
1052
+ TY_(SetOptionBool)( doc, TidyXmlDecl, yes );
1053
+ }
1054
+
1055
+ /* XML requires end tags */
1056
+ if ( cfgBool(doc, TidyXmlOut) )
1057
+ {
1058
+ #if SUPPORT_UTF16_ENCODINGS
1059
+ /* XML requires a BOM on output if using UTF-16 encoding */
1060
+ ulong enc = cfg( doc, TidyOutCharEncoding );
1061
+ if ( enc == UTF16LE || enc == UTF16BE || enc == UTF16 )
1062
+ TY_(SetOptionInt)( doc, TidyOutputBOM, yes );
1063
+ #endif
1064
+ TY_(SetOptionBool)( doc, TidyQuoteAmpersand, yes );
1065
+ TY_(SetOptionBool)( doc, TidyHideEndTags, no );
1066
+ }
1067
+ }
1068
+
1069
+ /* unsigned integers */
1070
+ Bool ParseInt( TidyDocImpl* doc, const TidyOptionImpl* entry )
1071
+ {
1072
+ ulong number = 0;
1073
+ Bool digits = no;
1074
+ TidyConfigImpl* cfg = &doc->config;
1075
+ tchar c = SkipWhite( cfg );
1076
+
1077
+ while ( TY_(IsDigit)(c) )
1078
+ {
1079
+ number = c - '0' + (10 * number);
1080
+ digits = yes;
1081
+ c = AdvanceChar( cfg );
1082
+ }
1083
+
1084
+ if ( !digits )
1085
+ TY_(ReportBadArgument)( doc, entry->name );
1086
+ else
1087
+ TY_(SetOptionInt)( doc, entry->id, number );
1088
+ return digits;
1089
+ }
1090
+
1091
+ /* true/false or yes/no or 0/1 or "auto" only looks at 1st char */
1092
+ static Bool ParseTriState( TidyTriState theState, TidyDocImpl* doc,
1093
+ const TidyOptionImpl* entry, ulong* flag )
1094
+ {
1095
+ TidyConfigImpl* cfg = &doc->config;
1096
+ tchar c = SkipWhite( cfg );
1097
+
1098
+ if (c == 't' || c == 'T' || c == 'y' || c == 'Y' || c == '1')
1099
+ *flag = yes;
1100
+ else if (c == 'f' || c == 'F' || c == 'n' || c == 'N' || c == '0')
1101
+ *flag = no;
1102
+ else if (theState == TidyAutoState && (c == 'a' || c =='A'))
1103
+ *flag = TidyAutoState;
1104
+ else
1105
+ {
1106
+ TY_(ReportBadArgument)( doc, entry->name );
1107
+ return no;
1108
+ }
1109
+
1110
+ return yes;
1111
+ }
1112
+
1113
+ /* cr, lf or crlf */
1114
+ Bool ParseNewline( TidyDocImpl* doc, const TidyOptionImpl* entry )
1115
+ {
1116
+ int nl = -1;
1117
+ tmbchar work[ 16 ] = {0};
1118
+ tmbstr cp = work, end = work + sizeof(work);
1119
+ TidyConfigImpl* cfg = &doc->config;
1120
+ tchar c = SkipWhite( cfg );
1121
+
1122
+ while ( c!=EndOfStream && cp < end && !TY_(IsWhite)(c) && c != '\r' && c != '\n' )
1123
+ {
1124
+ *cp++ = (tmbchar) c;
1125
+ c = AdvanceChar( cfg );
1126
+ }
1127
+ *cp = 0;
1128
+
1129
+ if ( TY_(tmbstrcasecmp)(work, "lf") == 0 )
1130
+ nl = TidyLF;
1131
+ else if ( TY_(tmbstrcasecmp)(work, "crlf") == 0 )
1132
+ nl = TidyCRLF;
1133
+ else if ( TY_(tmbstrcasecmp)(work, "cr") == 0 )
1134
+ nl = TidyCR;
1135
+
1136
+ if ( nl < TidyLF || nl > TidyCR )
1137
+ TY_(ReportBadArgument)( doc, entry->name );
1138
+ else
1139
+ TY_(SetOptionInt)( doc, entry->id, nl );
1140
+ return ( nl >= TidyLF && nl <= TidyCR );
1141
+ }
1142
+
1143
+ Bool ParseBool( TidyDocImpl* doc, const TidyOptionImpl* entry )
1144
+ {
1145
+ ulong flag = 0;
1146
+ Bool status = ParseTriState( TidyNoState, doc, entry, &flag );
1147
+ if ( status )
1148
+ TY_(SetOptionBool)( doc, entry->id, flag != 0 );
1149
+ return status;
1150
+ }
1151
+
1152
+ Bool ParseAutoBool( TidyDocImpl* doc, const TidyOptionImpl* entry )
1153
+ {
1154
+ ulong flag = 0;
1155
+ Bool status = ParseTriState( TidyAutoState, doc, entry, &flag );
1156
+ if ( status )
1157
+ TY_(SetOptionInt)( doc, entry->id, flag );
1158
+ return status;
1159
+ }
1160
+
1161
+ /* a string excluding whitespace */
1162
+ Bool ParseName( TidyDocImpl* doc, const TidyOptionImpl* option )
1163
+ {
1164
+ tmbchar buf[ 1024 ] = {0};
1165
+ uint i = 0;
1166
+ uint c = SkipWhite( &doc->config );
1167
+
1168
+ while ( i < sizeof(buf)-2 && c != EndOfStream && !TY_(IsWhite)(c) )
1169
+ {
1170
+ buf[i++] = (tmbchar) c;
1171
+ c = AdvanceChar( &doc->config );
1172
+ }
1173
+ buf[i] = 0;
1174
+
1175
+ if ( i == 0 )
1176
+ TY_(ReportBadArgument)( doc, option->name );
1177
+ else
1178
+ SetOptionValue( doc, option->id, buf );
1179
+ return ( i > 0 );
1180
+ }
1181
+
1182
+ /* #508936 - CSS class naming for -clean option */
1183
+ Bool ParseCSS1Selector( TidyDocImpl* doc, const TidyOptionImpl* option )
1184
+ {
1185
+ char buf[256] = {0};
1186
+ uint i = 0;
1187
+ uint c = SkipWhite( &doc->config );
1188
+
1189
+ while ( i < sizeof(buf)-2 && c != EndOfStream && !TY_(IsWhite)(c) )
1190
+ {
1191
+ buf[i++] = (tmbchar) c;
1192
+ c = AdvanceChar( &doc->config );
1193
+ }
1194
+ buf[i] = '\0';
1195
+
1196
+ if ( i == 0 || !TY_(IsCSS1Selector)(buf) ) {
1197
+ TY_(ReportBadArgument)( doc, option->name );
1198
+ return no;
1199
+ }
1200
+
1201
+ buf[i++] = '-'; /* Make sure any escaped Unicode is terminated */
1202
+ buf[i] = 0; /* so valid class names are generated after */
1203
+ /* Tidy appends last digits. */
1204
+
1205
+ SetOptionValue( doc, option->id, buf );
1206
+ return yes;
1207
+ }
1208
+
1209
+ /* Coordinates Config update and Tags data */
1210
+ static void DeclareUserTag( TidyDocImpl* doc, TidyOptionId optId,
1211
+ UserTagType tagType, ctmbstr name )
1212
+ {
1213
+ ctmbstr prvval = cfgStr( doc, optId );
1214
+ tmbstr catval = NULL;
1215
+ ctmbstr theval = name;
1216
+ if ( prvval )
1217
+ {
1218
+ uint len = TY_(tmbstrlen)(name) + TY_(tmbstrlen)(prvval) + 3;
1219
+ catval = TY_(tmbstrndup)( doc->allocator, prvval, len );
1220
+ TY_(tmbstrcat)( catval, ", " );
1221
+ TY_(tmbstrcat)( catval, name );
1222
+ theval = catval;
1223
+ }
1224
+ TY_(DefineTag)( doc, tagType, name );
1225
+ SetOptionValue( doc, optId, theval );
1226
+ if ( catval )
1227
+ TidyDocFree( doc, catval );
1228
+ }
1229
+
1230
+ /* a space or comma separated list of tag names */
1231
+ Bool ParseTagNames( TidyDocImpl* doc, const TidyOptionImpl* option )
1232
+ {
1233
+ TidyConfigImpl* cfg = &doc->config;
1234
+ tmbchar buf[1024];
1235
+ uint i = 0, nTags = 0;
1236
+ uint c = SkipWhite( cfg );
1237
+ UserTagType ttyp = tagtype_null;
1238
+
1239
+ switch ( option->id )
1240
+ {
1241
+ case TidyInlineTags: ttyp = tagtype_inline; break;
1242
+ case TidyBlockTags: ttyp = tagtype_block; break;
1243
+ case TidyEmptyTags: ttyp = tagtype_empty; break;
1244
+ case TidyPreTags: ttyp = tagtype_pre; break;
1245
+ default:
1246
+ TY_(ReportUnknownOption)( doc, option->name );
1247
+ return no;
1248
+ }
1249
+
1250
+ SetOptionValue( doc, option->id, NULL );
1251
+ TY_(FreeDeclaredTags)( doc, ttyp );
1252
+ cfg->defined_tags |= ttyp;
1253
+
1254
+ do
1255
+ {
1256
+ if (c == ' ' || c == '\t' || c == ',')
1257
+ {
1258
+ c = AdvanceChar( cfg );
1259
+ continue;
1260
+ }
1261
+
1262
+ if ( c == '\r' || c == '\n' )
1263
+ {
1264
+ uint c2 = AdvanceChar( cfg );
1265
+ if ( c == '\r' && c2 == '\n' )
1266
+ c = AdvanceChar( cfg );
1267
+ else
1268
+ c = c2;
1269
+
1270
+ if ( !TY_(IsWhite)(c) )
1271
+ {
1272
+ buf[i] = 0;
1273
+ TY_(UngetChar)( c, cfg->cfgIn );
1274
+ TY_(UngetChar)( '\n', cfg->cfgIn );
1275
+ break;
1276
+ }
1277
+ }
1278
+
1279
+ /*
1280
+ if ( c == '\n' )
1281
+ {
1282
+ c = AdvanceChar( cfg );
1283
+ if ( !TY_(IsWhite)(c) )
1284
+ {
1285
+ buf[i] = 0;
1286
+ TY_(UngetChar)( c, cfg->cfgIn );
1287
+ TY_(UngetChar)( '\n', cfg->cfgIn );
1288
+ break;
1289
+ }
1290
+ }
1291
+ */
1292
+
1293
+ while ( i < sizeof(buf)-2 && c != EndOfStream && !TY_(IsWhite)(c) && c != ',' )
1294
+ {
1295
+ buf[i++] = (tmbchar) c;
1296
+ c = AdvanceChar( cfg );
1297
+ }
1298
+
1299
+ buf[i] = '\0';
1300
+ if (i == 0) /* Skip empty tag definition. Possible when */
1301
+ continue; /* there is a trailing space on the line. */
1302
+
1303
+ /* add tag to dictionary */
1304
+ DeclareUserTag( doc, option->id, ttyp, buf );
1305
+ i = 0;
1306
+ ++nTags;
1307
+ }
1308
+ while ( c != EndOfStream );
1309
+
1310
+ if ( i > 0 )
1311
+ DeclareUserTag( doc, option->id, ttyp, buf );
1312
+ return ( nTags > 0 );
1313
+ }
1314
+
1315
+ /* a string including whitespace */
1316
+ /* munges whitespace sequences */
1317
+
1318
+ Bool ParseString( TidyDocImpl* doc, const TidyOptionImpl* option )
1319
+ {
1320
+ TidyConfigImpl* cfg = &doc->config;
1321
+ tmbchar buf[8192];
1322
+ uint i = 0;
1323
+ tchar delim = 0;
1324
+ Bool waswhite = yes;
1325
+
1326
+ tchar c = SkipWhite( cfg );
1327
+
1328
+ if ( c == '"' || c == '\'' )
1329
+ {
1330
+ delim = c;
1331
+ c = AdvanceChar( cfg );
1332
+ }
1333
+
1334
+ while ( i < sizeof(buf)-2 && c != EndOfStream && c != '\r' && c != '\n' )
1335
+ {
1336
+ if ( delim && c == delim )
1337
+ break;
1338
+
1339
+ if ( TY_(IsWhite)(c) )
1340
+ {
1341
+ if ( waswhite )
1342
+ {
1343
+ c = AdvanceChar( cfg );
1344
+ continue;
1345
+ }
1346
+ c = ' ';
1347
+ }
1348
+ else
1349
+ waswhite = no;
1350
+
1351
+ buf[i++] = (tmbchar) c;
1352
+ c = AdvanceChar( cfg );
1353
+ }
1354
+ buf[i] = '\0';
1355
+
1356
+ SetOptionValue( doc, option->id, buf );
1357
+ return yes;
1358
+ }
1359
+
1360
+ Bool ParseCharEnc( TidyDocImpl* doc, const TidyOptionImpl* option )
1361
+ {
1362
+ tmbchar buf[64] = {0};
1363
+ uint i = 0;
1364
+ int enc = ASCII;
1365
+ Bool validEncoding = yes;
1366
+ tchar c = SkipWhite( &doc->config );
1367
+
1368
+ while ( i < sizeof(buf)-2 && c != EndOfStream && !TY_(IsWhite)(c) )
1369
+ {
1370
+ buf[i++] = (tmbchar) TY_(ToLower)( c );
1371
+ c = AdvanceChar( &doc->config );
1372
+ }
1373
+ buf[i] = 0;
1374
+
1375
+ enc = TY_(CharEncodingId)( doc, buf );
1376
+
1377
+ #ifdef TIDY_WIN32_MLANG_SUPPORT
1378
+ /* limit support to --input-encoding */
1379
+ if (option->id != TidyInCharEncoding && enc > WIN32MLANG)
1380
+ enc = -1;
1381
+ #endif
1382
+
1383
+ if ( enc < 0 )
1384
+ {
1385
+ validEncoding = no;
1386
+ TY_(ReportBadArgument)( doc, option->name );
1387
+ }
1388
+ else
1389
+ TY_(SetOptionInt)( doc, option->id, enc );
1390
+
1391
+ if ( validEncoding && option->id == TidyCharEncoding )
1392
+ TY_(AdjustCharEncoding)( doc, enc );
1393
+ return validEncoding;
1394
+ }
1395
+
1396
+
1397
+ int TY_(CharEncodingId)( TidyDocImpl* ARG_UNUSED(doc), ctmbstr charenc )
1398
+ {
1399
+ int enc = TY_(GetCharEncodingFromOptName)( charenc );
1400
+
1401
+ #ifdef TIDY_WIN32_MLANG_SUPPORT
1402
+ if (enc == -1)
1403
+ {
1404
+ uint wincp = TY_(Win32MLangGetCPFromName)(doc->allocator, charenc);
1405
+ if (wincp)
1406
+ enc = wincp;
1407
+ }
1408
+ #endif
1409
+
1410
+ return enc;
1411
+ }
1412
+
1413
+ ctmbstr TY_(CharEncodingName)( int encoding )
1414
+ {
1415
+ ctmbstr encodingName = TY_(GetEncodingNameFromTidyId)(encoding);
1416
+
1417
+ if (!encodingName)
1418
+ encodingName = "unknown";
1419
+
1420
+ return encodingName;
1421
+ }
1422
+
1423
+ ctmbstr TY_(CharEncodingOptName)( int encoding )
1424
+ {
1425
+ ctmbstr encodingName = TY_(GetEncodingOptNameFromTidyId)(encoding);
1426
+
1427
+ if (!encodingName)
1428
+ encodingName = "unknown";
1429
+
1430
+ return encodingName;
1431
+ }
1432
+
1433
+ /*
1434
+ doctype: omit | auto | strict | loose | <fpi>
1435
+
1436
+ where the fpi is a string similar to
1437
+
1438
+ "-//ACME//DTD HTML 3.14159//EN"
1439
+ */
1440
+ Bool ParseDocType( TidyDocImpl* doc, const TidyOptionImpl* option )
1441
+ {
1442
+ tmbchar buf[ 32 ] = {0};
1443
+ uint i = 0;
1444
+ Bool status = yes;
1445
+ TidyDoctypeModes dtmode = TidyDoctypeAuto;
1446
+
1447
+ TidyConfigImpl* cfg = &doc->config;
1448
+ tchar c = SkipWhite( cfg );
1449
+
1450
+ /* "-//ACME//DTD HTML 3.14159//EN" or similar */
1451
+
1452
+ if ( c == '"' || c == '\'' )
1453
+ {
1454
+ status = ParseString(doc, option);
1455
+ if (status)
1456
+ TY_(SetOptionInt)( doc, TidyDoctypeMode, TidyDoctypeUser );
1457
+
1458
+ return status;
1459
+ }
1460
+
1461
+ /* read first word */
1462
+ while ( i < sizeof(buf)-1 && c != EndOfStream && !TY_(IsWhite)(c) )
1463
+ {
1464
+ buf[i++] = (tmbchar) c;
1465
+ c = AdvanceChar( cfg );
1466
+ }
1467
+ buf[i] = '\0';
1468
+
1469
+ if ( TY_(tmbstrcasecmp)(buf, "auto") == 0 )
1470
+ dtmode = TidyDoctypeAuto;
1471
+ else if ( TY_(tmbstrcasecmp)(buf, "omit") == 0 )
1472
+ dtmode = TidyDoctypeOmit;
1473
+ else if ( TY_(tmbstrcasecmp)(buf, "strict") == 0 )
1474
+ dtmode = TidyDoctypeStrict;
1475
+ else if ( TY_(tmbstrcasecmp)(buf, "loose") == 0 ||
1476
+ TY_(tmbstrcasecmp)(buf, "transitional") == 0 )
1477
+ dtmode = TidyDoctypeLoose;
1478
+ else
1479
+ {
1480
+ TY_(ReportBadArgument)( doc, option->name );
1481
+ status = no;
1482
+ }
1483
+
1484
+ if ( status )
1485
+ TY_(SetOptionInt)( doc, TidyDoctypeMode, dtmode );
1486
+ return status;
1487
+ }
1488
+
1489
+ Bool ParseRepeatAttr( TidyDocImpl* doc, const TidyOptionImpl* option )
1490
+ {
1491
+ Bool status = yes;
1492
+ tmbchar buf[64] = {0};
1493
+ uint i = 0;
1494
+
1495
+ TidyConfigImpl* cfg = &doc->config;
1496
+ tchar c = SkipWhite( cfg );
1497
+
1498
+ while (i < sizeof(buf)-1 && c != EndOfStream && !TY_(IsWhite)(c))
1499
+ {
1500
+ buf[i++] = (tmbchar) c;
1501
+ c = AdvanceChar( cfg );
1502
+ }
1503
+ buf[i] = '\0';
1504
+
1505
+ if ( TY_(tmbstrcasecmp)(buf, "keep-first") == 0 )
1506
+ cfg->value[ TidyDuplicateAttrs ].v = TidyKeepFirst;
1507
+ else if ( TY_(tmbstrcasecmp)(buf, "keep-last") == 0 )
1508
+ cfg->value[ TidyDuplicateAttrs ].v = TidyKeepLast;
1509
+ else
1510
+ {
1511
+ TY_(ReportBadArgument)( doc, option->name );
1512
+ status = no;
1513
+ }
1514
+ return status;
1515
+ }
1516
+
1517
+ Bool ParseSorter( TidyDocImpl* doc, const TidyOptionImpl* option )
1518
+ {
1519
+ Bool status = yes;
1520
+ tmbchar buf[64] = {0};
1521
+ uint i = 0;
1522
+
1523
+ TidyConfigImpl* cfg = &doc->config;
1524
+ tchar c = SkipWhite( cfg );
1525
+
1526
+ while (i < sizeof(buf)-1 && c != EndOfStream && !TY_(IsWhite)(c))
1527
+ {
1528
+ buf[i++] = (tmbchar) c;
1529
+ c = AdvanceChar( cfg );
1530
+ }
1531
+ buf[i] = '\0';
1532
+
1533
+ if ( TY_(tmbstrcasecmp)(buf, "alpha") == 0 )
1534
+ cfg->value[ TidySortAttributes ].v = TidySortAttrAlpha;
1535
+ else if ( TY_(tmbstrcasecmp)(buf, "none") == 0)
1536
+ cfg->value[ TidySortAttributes ].v = TidySortAttrNone;
1537
+ else
1538
+ {
1539
+ TY_(ReportBadArgument)( doc, option->name );
1540
+ status = no;
1541
+ }
1542
+ return status;
1543
+ }
1544
+
1545
+ /* Use TidyOptionId as iterator.
1546
+ ** Send index of 1st option after TidyOptionUnknown as start of list.
1547
+ */
1548
+ TidyIterator TY_(getOptionList)( TidyDocImpl* ARG_UNUSED(doc) )
1549
+ {
1550
+ return (TidyIterator) (size_t)1;
1551
+ }
1552
+
1553
+ /* Check if this item is last valid option.
1554
+ ** If so, zero out iterator.
1555
+ */
1556
+ const TidyOptionImpl* TY_(getNextOption)( TidyDocImpl* ARG_UNUSED(doc),
1557
+ TidyIterator* iter )
1558
+ {
1559
+ const TidyOptionImpl* option = NULL;
1560
+ size_t optId;
1561
+ assert( iter != NULL );
1562
+ optId = (size_t) *iter;
1563
+ if ( optId > TidyUnknownOption && optId < N_TIDY_OPTIONS )
1564
+ {
1565
+ option = &option_defs[ optId ];
1566
+ optId++;
1567
+ }
1568
+ *iter = (TidyIterator) ( optId < N_TIDY_OPTIONS ? optId : (size_t)0 );
1569
+ return option;
1570
+ }
1571
+
1572
+ /* Use a 1-based array index as iterator: 0 == end-of-list
1573
+ */
1574
+ TidyIterator TY_(getOptionPickList)( const TidyOptionImpl* option )
1575
+ {
1576
+ size_t ix = 0;
1577
+ if ( option && option->pickList )
1578
+ ix = 1;
1579
+ return (TidyIterator) ix;
1580
+ }
1581
+
1582
+ ctmbstr TY_(getNextOptionPick)( const TidyOptionImpl* option,
1583
+ TidyIterator* iter )
1584
+ {
1585
+ size_t ix;
1586
+ ctmbstr val = NULL;
1587
+ assert( option!=NULL && iter != NULL );
1588
+
1589
+ ix = (size_t) *iter;
1590
+ if ( ix > 0 && ix < 16 && option->pickList )
1591
+ val = option->pickList[ ix-1 ];
1592
+ *iter = (TidyIterator) ( val && option->pickList[ix] ? ix + 1 : (size_t)0 );
1593
+ return val;
1594
+ }
1595
+
1596
+ static int WriteOptionString( const TidyOptionImpl* option,
1597
+ ctmbstr sval, StreamOut* out )
1598
+ {
1599
+ ctmbstr cp = option->name;
1600
+ while ( *cp )
1601
+ TY_(WriteChar)( *cp++, out );
1602
+ TY_(WriteChar)( ':', out );
1603
+ TY_(WriteChar)( ' ', out );
1604
+ cp = sval;
1605
+ while ( *cp )
1606
+ TY_(WriteChar)( *cp++, out );
1607
+ TY_(WriteChar)( '\n', out );
1608
+ return 0;
1609
+ }
1610
+
1611
+ static int WriteOptionInt( const TidyOptionImpl* option, uint ival, StreamOut* out )
1612
+ {
1613
+ tmbchar sval[ 32 ] = {0};
1614
+ TY_(tmbsnprintf)(sval, sizeof(sval), "%u", ival );
1615
+ return WriteOptionString( option, sval, out );
1616
+ }
1617
+
1618
+ static int WriteOptionBool( const TidyOptionImpl* option, Bool bval, StreamOut* out )
1619
+ {
1620
+ ctmbstr sval = bval ? "yes" : "no";
1621
+ return WriteOptionString( option, sval, out );
1622
+ }
1623
+
1624
+ static int WriteOptionPick( const TidyOptionImpl* option, uint ival, StreamOut* out )
1625
+ {
1626
+ uint ix;
1627
+ const ctmbstr* val = option->pickList;
1628
+ for ( ix=0; val[ix] && ix<ival; ++ix )
1629
+ /**/;
1630
+ if ( ix==ival && val[ix] )
1631
+ return WriteOptionString( option, val[ix], out );
1632
+ return -1;
1633
+ }
1634
+
1635
+ Bool TY_(ConfigDiffThanSnapshot)( TidyDocImpl* doc )
1636
+ {
1637
+ int diff = memcmp( &doc->config.value, &doc->config.snapshot,
1638
+ N_TIDY_OPTIONS * sizeof(uint) );
1639
+ return ( diff != 0 );
1640
+ }
1641
+
1642
+ Bool TY_(ConfigDiffThanDefault)( TidyDocImpl* doc )
1643
+ {
1644
+ Bool diff = no;
1645
+ const TidyOptionImpl* option = option_defs + 1;
1646
+ const TidyOptionValue* val = doc->config.value;
1647
+ for ( /**/; !diff && option && option->name; ++option, ++val )
1648
+ {
1649
+ diff = !OptionValueEqDefault( option, val );
1650
+ }
1651
+ return diff;
1652
+ }
1653
+
1654
+
1655
+ static int SaveConfigToStream( TidyDocImpl* doc, StreamOut* out )
1656
+ {
1657
+ int rc = 0;
1658
+ const TidyOptionImpl* option;
1659
+ for ( option=option_defs+1; 0==rc && option && option->name; ++option )
1660
+ {
1661
+ const TidyOptionValue* val = &doc->config.value[ option->id ];
1662
+ if ( option->parser == NULL )
1663
+ continue;
1664
+ if ( OptionValueEqDefault( option, val ) && option->id != TidyDoctype)
1665
+ continue;
1666
+
1667
+ if ( option->id == TidyDoctype ) /* Special case */
1668
+ {
1669
+ ulong dtmode = cfg( doc, TidyDoctypeMode );
1670
+ if ( dtmode == TidyDoctypeUser )
1671
+ {
1672
+ tmbstr t;
1673
+
1674
+ /* add 2 double quotes */
1675
+ if (( t = (tmbstr)TidyDocAlloc( doc, TY_(tmbstrlen)( val->p ) + 2 ) ))
1676
+ {
1677
+ t[0] = '\"'; t[1] = 0;
1678
+
1679
+ TY_(tmbstrcat)( t, val->p );
1680
+ TY_(tmbstrcat)( t, "\"" );
1681
+ rc = WriteOptionString( option, t, out );
1682
+
1683
+ TidyDocFree( doc, t );
1684
+ }
1685
+ }
1686
+ else if ( dtmode == option_defs[TidyDoctypeMode].dflt )
1687
+ continue;
1688
+ else
1689
+ rc = WriteOptionPick( option, dtmode, out );
1690
+ }
1691
+ else if ( option->pickList )
1692
+ rc = WriteOptionPick( option, val->v, out );
1693
+ else
1694
+ {
1695
+ switch ( option->type )
1696
+ {
1697
+ case TidyString:
1698
+ rc = WriteOptionString( option, val->p, out );
1699
+ break;
1700
+ case TidyInteger:
1701
+ rc = WriteOptionInt( option, val->v, out );
1702
+ break;
1703
+ case TidyBoolean:
1704
+ rc = WriteOptionBool( option, val->v ? yes : no, out );
1705
+ break;
1706
+ }
1707
+ }
1708
+ }
1709
+ return rc;
1710
+ }
1711
+
1712
+ int TY_(SaveConfigFile)( TidyDocImpl* doc, ctmbstr cfgfil )
1713
+ {
1714
+ int status = -1;
1715
+ StreamOut* out = NULL;
1716
+ uint outenc = cfg( doc, TidyOutCharEncoding );
1717
+ uint nl = cfg( doc, TidyNewline );
1718
+ FILE* fout = fopen( cfgfil, "wb" );
1719
+ if ( fout )
1720
+ {
1721
+ out = TY_(FileOutput)( doc, fout, outenc, nl );
1722
+ status = SaveConfigToStream( doc, out );
1723
+ fclose( fout );
1724
+ TidyDocFree( doc, out );
1725
+ }
1726
+ return status;
1727
+ }
1728
+
1729
+ int TY_(SaveConfigSink)( TidyDocImpl* doc, TidyOutputSink* sink )
1730
+ {
1731
+ uint outenc = cfg( doc, TidyOutCharEncoding );
1732
+ uint nl = cfg( doc, TidyNewline );
1733
+ StreamOut* out = TY_(UserOutput)( doc, sink, outenc, nl );
1734
+ int status = SaveConfigToStream( doc, out );
1735
+ TidyDocFree( doc, out );
1736
+ return status;
1737
+ }
1738
+
1739
+ /*
1740
+ * local variables:
1741
+ * mode: c
1742
+ * indent-tabs-mode: nil
1743
+ * c-basic-offset: 4
1744
+ * eval: (c-set-offset 'substatement-open 0)
1745
+ * end:
1746
+ */