Coverage Report

Created: 2023-11-27 08:14

/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,&current[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