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