OSDN Git Service

フォルダ名変更
[wordring-tm/wordring-tm.git] / third-party / tidy-html5-master / src / tidylib.c
1 /* tidylib.c -- internal library definitions
2
3   (c) 1998-2008 (W3C) MIT, ERCIM, Keio University
4   See tidy.h for the copyright notice.
5
6   Defines HTML Tidy API implemented by tidy library.
7
8   Very rough initial cut for discussion purposes.
9
10   Public interface is const-correct and doesn't explicitly depend
11   on any globals.  Thus, thread-safety may be introduced w/out
12   changing the interface.
13
14   Looking ahead to a C++ wrapper, C functions always pass
15   this-equivalent as 1st arg.
16
17   Created 2001-05-20 by Charles Reitzel
18
19 */
20
21 #include <errno.h>
22
23 #include "tidy-int.h"
24 #include "parser.h"
25 #include "clean.h"
26 #include "gdoc.h"
27 #include "config.h"
28 #include "message.h"
29 #include "pprint.h"
30 #include "entities.h"
31 #include "tmbstr.h"
32 #include "utf8.h"
33 #include "mappedio.h"
34
35 #ifdef TIDY_WIN32_MLANG_SUPPORT
36 #include "win32tc.h"
37 #endif
38 #if !defined(NDEBUG) && defined(_MSC_VER)
39 #include "sprtf.h"
40 #endif
41
42 /* Create/Destroy a Tidy "document" object */
43 static TidyDocImpl* tidyDocCreate( TidyAllocator *allocator );
44 static void         tidyDocRelease( TidyDocImpl* impl );
45
46 static int          tidyDocStatus( TidyDocImpl* impl );
47
48 /* Parse Markup */
49 static int          tidyDocParseFile( TidyDocImpl* impl, ctmbstr htmlfil );
50 static int          tidyDocParseStdin( TidyDocImpl* impl );
51 static int          tidyDocParseString( TidyDocImpl* impl, ctmbstr content );
52 static int          tidyDocParseBuffer( TidyDocImpl* impl, TidyBuffer* inbuf );
53 static int          tidyDocParseSource( TidyDocImpl* impl, TidyInputSource* docIn );
54
55
56 /* Execute post-parse diagnostics and cleanup.
57 ** Note, the order is important.  You will get different
58 ** results from the diagnostics depending on if they are run
59 ** pre-or-post repair.
60 */
61 static int          tidyDocRunDiagnostics( TidyDocImpl* doc );
62 static void         tidyDocReportDoctype( TidyDocImpl* doc );
63 static int          tidyDocCleanAndRepair( TidyDocImpl* doc );
64
65
66 /* Save cleaned up file to file/buffer/sink */
67 static int          tidyDocSaveFile( TidyDocImpl* impl, ctmbstr htmlfil );
68 static int          tidyDocSaveStdout( TidyDocImpl* impl );
69 static int          tidyDocSaveString( TidyDocImpl* impl, tmbstr buffer, uint* buflen );
70 static int          tidyDocSaveBuffer( TidyDocImpl* impl, TidyBuffer* outbuf );
71 static int          tidyDocSaveSink( TidyDocImpl* impl, TidyOutputSink* docOut );
72 static int          tidyDocSaveStream( TidyDocImpl* impl, StreamOut* out );
73
74 #ifdef NEVER
75 TidyDocImpl* tidyDocToImpl( TidyDoc tdoc )
76 {
77   return (TidyDocImpl*) tdoc;
78 }
79 TidyDoc      tidyImplToDoc( TidyDocImpl* impl )
80 {
81   return (TidyDoc) impl;
82 }
83
84 Node*        tidyNodeToImpl( TidyNode tnod )
85 {
86   return (Node*) tnod;
87 }
88 TidyNode     tidyImplToNode( Node* node )
89 {
90   return (TidyNode) node;
91 }
92
93 AttVal*      tidyAttrToImpl( TidyAttr tattr )
94 {
95   return (AttVal*) tattr;
96 }
97 TidyAttr     tidyImplToAttr( AttVal* attval )
98 {
99   return (TidyAttr) attval;
100 }
101
102 const TidyOptionImpl* tidyOptionToImpl( TidyOption topt )
103 {
104   return (const TidyOptionImpl*) topt;
105 }
106 TidyOption   tidyImplToOption( const TidyOptionImpl* option )
107 {
108   return (TidyOption) option;
109 }
110 #endif
111
112 /* Tidy public interface
113 **
114 ** Most functions return an integer:
115 **
116 ** 0    -> SUCCESS
117 ** >0   -> WARNING
118 ** <0   -> ERROR
119 **
120 */
121
122 TidyDoc TIDY_CALL       tidyCreate(void)
123 {
124   TidyDocImpl* impl = tidyDocCreate( &TY_(g_default_allocator) );
125   return tidyImplToDoc( impl );
126 }
127
128 TidyDoc TIDY_CALL tidyCreateWithAllocator( TidyAllocator *allocator )
129 {
130   TidyDocImpl* impl = tidyDocCreate( allocator );
131   return tidyImplToDoc( impl );
132 }
133
134 void TIDY_CALL          tidyRelease( TidyDoc tdoc )
135 {
136   TidyDocImpl* impl = tidyDocToImpl( tdoc );
137   tidyDocRelease( impl );
138 }
139
140 TidyDocImpl* tidyDocCreate( TidyAllocator *allocator )
141 {
142     TidyDocImpl* doc = (TidyDocImpl*)TidyAlloc( allocator, sizeof(TidyDocImpl) );
143     TidyClearMemory( doc, sizeof(*doc) );
144     doc->allocator = allocator;
145
146     TY_(InitMap)();
147     TY_(InitTags)( doc );
148     TY_(InitAttrs)( doc );
149     TY_(InitConfig)( doc );
150     TY_(InitPrintBuf)( doc );
151
152     /* By default, wire tidy messages to standard error.
153     ** Document input will be set by parsing routines.
154     ** Document output will be set by pretty print routines.
155     ** Config input will be set by config parsing routines.
156     ** But we need to start off with a way to report errors.
157     */
158     doc->errout = TY_(StdErrOutput)();
159     return doc;
160 }
161
162 void          tidyDocRelease( TidyDocImpl* doc )
163 {
164     /* doc in/out opened and closed by parse/print routines */
165     if ( doc )
166     {
167         assert( doc->docIn == NULL );
168         assert( doc->docOut == NULL );
169
170         TY_(ReleaseStreamOut)( doc, doc->errout );
171         doc->errout = NULL;
172
173         TY_(FreePrintBuf)( doc );
174         TY_(FreeNode)(doc, &doc->root);
175         TidyClearMemory(&doc->root, sizeof(Node));
176
177         if (doc->givenDoctype)
178             TidyDocFree(doc, doc->givenDoctype);
179
180         TY_(FreeConfig)( doc );
181         TY_(FreeAttrTable)( doc );
182         TY_(FreeTags)( doc );
183         /*\ 
184          *  Issue #186 - Now FreeNode depend on the doctype, so the lexer is needed
185          *  to determine which hash is to be used, so free it last.
186         \*/
187         TY_(FreeLexer)( doc );
188         TidyDocFree( doc, doc );
189     }
190 }
191
192 /* Let application store a chunk of data w/ each Tidy tdocance.
193 ** Useful for callbacks.
194 */
195 void TIDY_CALL        tidySetAppData( TidyDoc tdoc, void* appData )
196 {
197   TidyDocImpl* impl = tidyDocToImpl( tdoc );
198   if ( impl )
199     impl->appData = appData;
200 }
201 void* TIDY_CALL       tidyGetAppData( TidyDoc tdoc )
202 {
203   TidyDocImpl* impl = tidyDocToImpl( tdoc );
204   if ( impl )
205     return impl->appData;
206   return NULL;
207 }
208
209 ctmbstr TIDY_CALL     tidyReleaseDate(void)
210 {
211     return TY_(ReleaseDate)();
212 }
213
214
215 /* Get/set configuration options
216 */
217 Bool TIDY_CALL        tidySetOptionCallback( TidyDoc tdoc, TidyOptCallback pOptCallback )
218 {
219   TidyDocImpl* impl = tidyDocToImpl( tdoc );
220   if ( impl )
221   {
222     impl->pOptCallback = pOptCallback;
223     return yes;
224   }
225   return no;
226 }
227
228
229 int TIDY_CALL     tidyLoadConfig( TidyDoc tdoc, ctmbstr cfgfil )
230 {
231     TidyDocImpl* impl = tidyDocToImpl( tdoc );
232     if ( impl )
233         return TY_(ParseConfigFile)( impl, cfgfil );
234     return -EINVAL;
235 }
236
237 int TIDY_CALL     tidyLoadConfigEnc( TidyDoc tdoc, ctmbstr cfgfil, ctmbstr charenc )
238 {
239     TidyDocImpl* impl = tidyDocToImpl( tdoc );
240     if ( impl )
241         return TY_(ParseConfigFileEnc)( impl, cfgfil, charenc );
242     return -EINVAL;
243 }
244
245 int TIDY_CALL         tidySetCharEncoding( TidyDoc tdoc, ctmbstr encnam )
246 {
247     TidyDocImpl* impl = tidyDocToImpl( tdoc );
248     if ( impl )
249     {
250         int enc = TY_(CharEncodingId)( impl, encnam );
251         if ( enc >= 0 && TY_(AdjustCharEncoding)(impl, enc) )
252             return 0;
253
254         TY_(ReportBadArgument)( impl, "char-encoding" );
255     }
256     return -EINVAL;
257 }
258
259 int TIDY_CALL           tidySetInCharEncoding( TidyDoc tdoc, ctmbstr encnam )
260 {
261     TidyDocImpl* impl = tidyDocToImpl( tdoc );
262     if ( impl )
263     {
264         int enc = TY_(CharEncodingId)( impl, encnam );
265         if ( enc >= 0 && TY_(SetOptionInt)( impl, TidyInCharEncoding, enc ) )
266             return 0;
267
268         TY_(ReportBadArgument)( impl, "in-char-encoding" );
269     }
270     return -EINVAL;
271 }
272
273 int TIDY_CALL           tidySetOutCharEncoding( TidyDoc tdoc, ctmbstr encnam )
274 {
275     TidyDocImpl* impl = tidyDocToImpl( tdoc );
276     if ( impl )
277     {
278         int enc = TY_(CharEncodingId)( impl, encnam );
279         if ( enc >= 0 && TY_(SetOptionInt)( impl, TidyOutCharEncoding, enc ) )
280             return 0;
281
282         TY_(ReportBadArgument)( impl, "out-char-encoding" );
283     }
284     return -EINVAL;
285 }
286
287 TidyOptionId TIDY_CALL tidyOptGetIdForName( ctmbstr optnam )
288 {
289     const TidyOptionImpl* option = TY_(lookupOption)( optnam );
290     if ( option )
291         return option->id;
292     return N_TIDY_OPTIONS;  /* Error */
293 }
294
295 TidyIterator TIDY_CALL  tidyGetOptionList( TidyDoc tdoc )
296 {
297     TidyDocImpl* impl = tidyDocToImpl( tdoc );
298     if ( impl )
299         return TY_(getOptionList)( impl );
300     return (TidyIterator) -1;
301 }
302
303 TidyOption TIDY_CALL    tidyGetNextOption( TidyDoc tdoc, TidyIterator* pos )
304 {
305     TidyDocImpl* impl = tidyDocToImpl( tdoc );
306     const TidyOptionImpl* option = NULL;
307     if ( impl )
308         option = TY_(getNextOption)( impl, pos );
309     else if ( pos )
310         *pos = 0;
311     return tidyImplToOption( option );
312 }
313
314
315 TidyOption TIDY_CALL    tidyGetOption( TidyDoc ARG_UNUSED(tdoc), TidyOptionId optId )
316 {
317     const TidyOptionImpl* option = TY_(getOption)( optId );
318     return tidyImplToOption( option );
319 }
320 TidyOption TIDY_CALL    tidyGetOptionByName( TidyDoc ARG_UNUSED(doc), ctmbstr optnam )
321 {
322     const TidyOptionImpl* option = TY_(lookupOption)( optnam );
323     return tidyImplToOption( option );
324 }
325
326 TidyOptionId TIDY_CALL  tidyOptGetId( TidyOption topt )
327 {
328     const TidyOptionImpl* option = tidyOptionToImpl( topt );
329     if ( option )
330         return option->id;
331     return N_TIDY_OPTIONS;
332 }
333 ctmbstr TIDY_CALL       tidyOptGetName( TidyOption topt )
334 {
335     const TidyOptionImpl* option = tidyOptionToImpl( topt );
336     if ( option )
337         return option->name;
338     return NULL;
339 }
340 TidyOptionType TIDY_CALL tidyOptGetType( TidyOption topt )
341 {
342     const TidyOptionImpl* option = tidyOptionToImpl( topt );
343     if ( option )
344         return option->type;
345     return (TidyOptionType) -1;
346 }
347 TidyConfigCategory TIDY_CALL tidyOptGetCategory( TidyOption topt )
348 {
349     const TidyOptionImpl* option = tidyOptionToImpl( topt );
350     if ( option )
351         return option->category;
352     return (TidyConfigCategory) -1;
353 }
354 ctmbstr TIDY_CALL       tidyOptGetDefault( TidyOption topt )
355 {
356     const TidyOptionImpl* option = tidyOptionToImpl( topt );
357     if ( option && option->type == TidyString )
358         return (ctmbstr) option->dflt;
359     return NULL;
360 }
361 ulong TIDY_CALL          tidyOptGetDefaultInt( TidyOption topt )
362 {
363     const TidyOptionImpl* option = tidyOptionToImpl( topt );
364     if ( option && option->type != TidyString )
365         return option->dflt;
366     return ~0U;
367 }
368 Bool TIDY_CALL          tidyOptGetDefaultBool( TidyOption topt )
369 {
370     const TidyOptionImpl* option = tidyOptionToImpl( topt );
371     if ( option && option->type != TidyString )
372         return ( option->dflt ? yes : no );
373     return no;
374 }
375 Bool TIDY_CALL          tidyOptIsReadOnly( TidyOption topt )
376 {
377     const TidyOptionImpl* option = tidyOptionToImpl( topt );
378     if ( option  )
379         return ( option->parser == NULL );
380     return yes;
381 }
382
383
384 TidyIterator TIDY_CALL  tidyOptGetPickList( TidyOption topt )
385 {
386     const TidyOptionImpl* option = tidyOptionToImpl( topt );
387     if ( option )
388       return TY_(getOptionPickList)( option );
389     return (TidyIterator) -1;
390 }
391 ctmbstr TIDY_CALL       tidyOptGetNextPick( TidyOption topt, TidyIterator* pos )
392 {
393     const TidyOptionImpl* option = tidyOptionToImpl( topt );
394     if ( option )
395         return TY_(getNextOptionPick)( option, pos );
396     return NULL;
397 }
398
399
400 ctmbstr TIDY_CALL       tidyOptGetValue( TidyDoc tdoc, TidyOptionId optId )
401 {
402   TidyDocImpl* impl = tidyDocToImpl( tdoc );
403   ctmbstr optval = NULL;
404   if ( impl )
405     optval = cfgStr( impl, optId );
406   return optval;
407 }
408 Bool TIDY_CALL        tidyOptSetValue( TidyDoc tdoc, TidyOptionId optId, ctmbstr val )
409 {
410   TidyDocImpl* impl = tidyDocToImpl( tdoc );
411   if ( impl )
412     return TY_(ParseConfigValue)( impl, optId, val );
413   return no;
414 }
415 Bool TIDY_CALL        tidyOptParseValue( TidyDoc tdoc, ctmbstr optnam, ctmbstr val )
416 {
417   TidyDocImpl* impl = tidyDocToImpl( tdoc );
418   if ( impl )
419     return TY_(ParseConfigOption)( impl, optnam, val );
420   return no;
421 }
422
423 ulong TIDY_CALL        tidyOptGetInt( TidyDoc tdoc, TidyOptionId optId )
424 {
425     TidyDocImpl* impl = tidyDocToImpl( tdoc );
426     ulong opti = 0;
427     if ( impl )
428         opti = cfg( impl, optId );
429     return opti;
430 }
431
432 Bool TIDY_CALL        tidyOptSetInt( TidyDoc tdoc, TidyOptionId optId, ulong val )
433 {
434     TidyDocImpl* impl = tidyDocToImpl( tdoc );
435     if ( impl )
436         return TY_(SetOptionInt)( impl, optId, val );
437     return no;
438 }
439
440 Bool TIDY_CALL         tidyOptGetBool( TidyDoc tdoc, TidyOptionId optId )
441 {
442     TidyDocImpl* impl = tidyDocToImpl( tdoc );
443     Bool optb = no;
444     if ( impl )
445     {
446         const TidyOptionImpl* option = TY_(getOption)( optId );
447         if ( option )
448         {
449             optb = cfgBool( impl, optId );
450         }
451     }
452     return optb;
453 }
454
455 Bool TIDY_CALL        tidyOptSetBool( TidyDoc tdoc, TidyOptionId optId, Bool val )
456 {
457     TidyDocImpl* impl = tidyDocToImpl( tdoc );
458     if ( impl )
459         return TY_(SetOptionBool)( impl, optId, val );
460     return no;
461 }
462
463 ctmbstr TIDY_CALL       tidyOptGetEncName( TidyDoc tdoc, TidyOptionId optId )
464 {
465   uint enc = tidyOptGetInt( tdoc, optId );
466   return TY_(CharEncodingOptName)( enc );
467 }
468
469 ctmbstr TIDY_CALL       tidyOptGetCurrPick( TidyDoc tdoc, TidyOptionId optId )
470 {
471     const TidyOptionImpl* option = TY_(getOption)( optId );
472     if ( option && option->pickList )
473     {
474         uint ix, pick = tidyOptGetInt( tdoc, optId );
475         const ctmbstr* pL = option->pickList;
476         for ( ix=0; *pL && ix < pick; ++ix )
477             ++pL;
478         if ( *pL )
479             return *pL;
480     }
481     return NULL;
482 }
483
484
485 TidyIterator TIDY_CALL tidyOptGetDeclTagList( TidyDoc tdoc )
486 {
487     TidyDocImpl* impl = tidyDocToImpl( tdoc );
488     TidyIterator declIter = 0;
489     if ( impl )
490         declIter = TY_(GetDeclaredTagList)( impl );
491     return declIter;
492 }
493
494 ctmbstr TIDY_CALL       tidyOptGetNextDeclTag( TidyDoc tdoc, TidyOptionId optId,
495                                      TidyIterator* iter )
496 {
497     TidyDocImpl* impl = tidyDocToImpl( tdoc );
498     ctmbstr tagnam = NULL;
499     if ( impl )
500     {
501         UserTagType tagtyp = tagtype_null;
502         if ( optId == TidyInlineTags )
503             tagtyp = tagtype_inline;
504         else if ( optId == TidyBlockTags )
505             tagtyp = tagtype_block;
506         else if ( optId == TidyEmptyTags )
507             tagtyp = tagtype_empty;
508         else if ( optId == TidyPreTags )
509             tagtyp = tagtype_pre;
510         if ( tagtyp != tagtype_null )
511             tagnam = TY_(GetNextDeclaredTag)( impl, tagtyp, iter );
512     }
513     return tagnam;
514 }
515
516 ctmbstr TIDY_CALL tidyOptGetDoc( TidyDoc ARG_UNUSED(tdoc), TidyOption opt )
517 {
518     const TidyOptionId optId = tidyOptGetId( opt );
519     const TidyOptionDoc* docDesc = TY_(OptGetDocDesc)( optId );
520     return docDesc ? docDesc->doc : NULL;
521 }
522
523 TidyIterator TIDY_CALL tidyOptGetDocLinksList( TidyDoc ARG_UNUSED(tdoc), TidyOption opt )
524 {
525     const TidyOptionId optId = tidyOptGetId( opt );
526     const TidyOptionDoc* docDesc = TY_(OptGetDocDesc)( optId );
527     if (docDesc && docDesc->links)
528         return (TidyIterator)docDesc->links;
529     return (TidyIterator)NULL;
530 }
531
532 TidyOption TIDY_CALL tidyOptGetNextDocLinks( TidyDoc tdoc, TidyIterator* pos )
533 {
534     const TidyOptionId* curr = (const TidyOptionId *)*pos;
535     TidyOption opt;
536
537     if (*curr == TidyUnknownOption)
538     {
539         *pos = (TidyIterator)NULL;
540         return (TidyOption)0;
541     }
542     opt = tidyGetOption(tdoc, *curr);
543     curr++;
544     *pos = (*curr == TidyUnknownOption ) ?
545         (TidyIterator)NULL:(TidyIterator)curr;
546     return opt;
547 }
548
549 int TIDY_CALL tidyOptSaveFile( TidyDoc tdoc, ctmbstr cfgfil )
550 {
551     TidyDocImpl* impl = tidyDocToImpl( tdoc );
552     if ( impl )
553         return TY_(SaveConfigFile)( impl, cfgfil );
554     return -EINVAL;
555 }
556
557 int TIDY_CALL tidyOptSaveSink( TidyDoc tdoc, TidyOutputSink* sink )
558 {
559     TidyDocImpl* impl = tidyDocToImpl( tdoc );
560     if ( impl )
561         return TY_(SaveConfigSink)( impl, sink );
562     return -EINVAL;
563 }
564
565 Bool TIDY_CALL tidyOptSnapshot( TidyDoc tdoc )
566 {
567     TidyDocImpl* impl = tidyDocToImpl( tdoc );
568     if ( impl )
569     {
570         TY_(TakeConfigSnapshot)( impl );
571         return yes;
572     }
573     return no;
574 }
575 Bool TIDY_CALL tidyOptResetToSnapshot( TidyDoc tdoc )
576 {
577     TidyDocImpl* impl = tidyDocToImpl( tdoc );
578     if ( impl )
579     {
580         TY_(ResetConfigToSnapshot)( impl );
581         return yes;
582     }
583     return no;
584 }
585 Bool TIDY_CALL tidyOptResetAllToDefault( TidyDoc tdoc )
586 {
587     TidyDocImpl* impl = tidyDocToImpl( tdoc );
588     if ( impl )
589     {
590         TY_(ResetConfigToDefault)( impl );
591         return yes;
592     }
593     return no;
594 }
595
596 Bool TIDY_CALL tidyOptResetToDefault( TidyDoc tdoc, TidyOptionId optId )
597 {
598     TidyDocImpl* impl = tidyDocToImpl( tdoc );
599     if ( impl )
600         return TY_(ResetOptionToDefault)( impl, optId );
601     return no;
602 }
603
604 Bool TIDY_CALL tidyOptDiffThanDefault( TidyDoc tdoc )
605 {
606     TidyDocImpl* impl = tidyDocToImpl( tdoc );
607     if ( impl )
608         return TY_(ConfigDiffThanDefault)( impl );
609     return no;
610 }
611 Bool TIDY_CALL          tidyOptDiffThanSnapshot( TidyDoc tdoc )
612 {
613     TidyDocImpl* impl = tidyDocToImpl( tdoc );
614     if ( impl )
615         return TY_(ConfigDiffThanSnapshot)( impl );
616     return no;
617 }
618
619 Bool TIDY_CALL tidyOptCopyConfig( TidyDoc to, TidyDoc from )
620 {
621     TidyDocImpl* docTo = tidyDocToImpl( to );
622     TidyDocImpl* docFrom = tidyDocToImpl( from );
623     if ( docTo && docFrom )
624     {
625         TY_(CopyConfig)( docTo, docFrom );
626         return yes;
627     }
628     return no;
629 }
630
631
632 /* I/O and Message handling interface
633 **
634 ** By default, Tidy will define, create and use
635 ** tdocances of input and output handlers for
636 ** standard C buffered I/O (i.e. FILE* stdin,
637 ** FILE* stdout and FILE* stderr for content
638 ** input, content output and diagnostic output,
639 ** respectively.  A FILE* cfgFile input handler
640 ** will be used for config files.  Command line
641 ** options will just be set directly.
642 */
643
644 /* Use TidyReportFilter to filter messages by diagnostic level:
645 ** info, warning, etc.  Just set diagnostic output
646 ** handler to redirect all diagnostics output.  Return true
647 ** to proceed with output, false to cancel.
648 */
649 Bool TIDY_CALL        tidySetReportFilter( TidyDoc tdoc, TidyReportFilter filt )
650 {
651   TidyDocImpl* impl = tidyDocToImpl( tdoc );
652   if ( impl )
653   {
654     impl->mssgFilt = filt;
655     return yes;
656   }
657   return no;
658 }
659
660 Bool TIDY_CALL        tidySetReportFilter2( TidyDoc tdoc, TidyReportFilter2 filt )
661 {
662   TidyDocImpl* impl = tidyDocToImpl( tdoc );
663   if ( impl )
664   {
665     impl->mssgFilt2 = filt;
666     return yes;
667   }
668   return no;
669 }
670
671 #if 0   /* Not yet */
672 int         tidySetContentOutputSink( TidyDoc tdoc, TidyOutputSink* outp )
673 {
674   TidyDocImpl* impl = tidyDocToImpl( tdoc );
675   if ( impl )
676   {
677     impl->docOut = outp;
678     return 0;
679   }
680   return -EINVAL;
681 }
682 int         tidySetDiagnosticOutputSink( TidyDoc tdoc, TidyOutputSink* outp )
683 {
684   TidyDocImpl* impl = tidyDocToImpl( tdoc );
685   if ( impl )
686   {
687     impl->msgOut = outp;
688     return 0;
689   }
690   return -EINVAL;
691 }
692
693
694 /* Library helpers
695 */
696 cmbstr       tidyLookupMessage( TidyDoc tdoc, int errorNo )
697 {
698   TidyDocImpl* impl = tidyDocToImpl( tdoc );
699   cmbstr mssg = NULL;
700   if ( impl )
701     mssg = tidyMessage_Lookup( impl->messages, errorNo );
702   return mssg;
703 }
704 #endif
705
706
707 FILE* TIDY_CALL   tidySetErrorFile( TidyDoc tdoc, ctmbstr errfilnam )
708 {
709     TidyDocImpl* impl = tidyDocToImpl( tdoc );
710     if ( impl )
711     {
712         FILE* errout = fopen( errfilnam, "wb" );
713         if ( errout )
714         {
715             uint outenc = cfg( impl, TidyOutCharEncoding );
716             uint nl = cfg( impl, TidyNewline );
717             TY_(ReleaseStreamOut)( impl, impl->errout );
718             impl->errout = TY_(FileOutput)( impl, errout, outenc, nl );
719             return errout;
720         }
721         else /* Emit message to current error sink */
722             TY_(FileError)( impl, errfilnam, TidyError );
723     }
724     return NULL;
725 }
726
727 int TIDY_CALL    tidySetErrorBuffer( TidyDoc tdoc, TidyBuffer* errbuf )
728 {
729     TidyDocImpl* impl = tidyDocToImpl( tdoc );
730     if ( impl )
731     {
732         uint outenc = cfg( impl, TidyOutCharEncoding );
733         uint nl = cfg( impl, TidyNewline );
734         TY_(ReleaseStreamOut)( impl, impl->errout );
735         impl->errout = TY_(BufferOutput)( impl, errbuf, outenc, nl );
736         return ( impl->errout ? 0 : -ENOMEM );
737     }
738     return -EINVAL;
739 }
740
741 int TIDY_CALL    tidySetErrorSink( TidyDoc tdoc, TidyOutputSink* sink )
742 {
743     TidyDocImpl* impl = tidyDocToImpl( tdoc );
744     if ( impl )
745     {
746         uint outenc = cfg( impl, TidyOutCharEncoding );
747         uint nl = cfg( impl, TidyNewline );
748         TY_(ReleaseStreamOut)( impl, impl->errout );
749         impl->errout = TY_(UserOutput)( impl, sink, outenc, nl );
750         return ( impl->errout ? 0 : -ENOMEM );
751     }
752     return -EINVAL;
753 }
754
755
756 /* Document info */
757 int TIDY_CALL        tidyStatus( TidyDoc tdoc )
758 {
759     TidyDocImpl* impl = tidyDocToImpl( tdoc );
760     int tidyStat = -EINVAL;
761     if ( impl )
762         tidyStat = tidyDocStatus( impl );
763     return tidyStat;
764 }
765 int TIDY_CALL        tidyDetectedHtmlVersion( TidyDoc ARG_UNUSED(tdoc) )
766 {
767 /*    TidyDocImpl* impl = tidyDocToImpl( tdoc ); */
768     return 0;
769 }
770 Bool TIDY_CALL        tidyDetectedXhtml( TidyDoc ARG_UNUSED(tdoc) )
771 {
772 /*    TidyDocImpl* impl = tidyDocToImpl( tdoc ); */
773     return no;
774 }
775 Bool TIDY_CALL        tidyDetectedGenericXml( TidyDoc ARG_UNUSED(tdoc) )
776 {
777 /*    TidyDocImpl* impl = tidyDocToImpl( tdoc ); */
778     return no;
779 }
780
781 uint TIDY_CALL       tidyErrorCount( TidyDoc tdoc )
782 {
783     TidyDocImpl* impl = tidyDocToImpl( tdoc );
784     uint count = 0xFFFFFFFF;
785     if ( impl )
786         count = impl->errors;
787     return count;
788 }
789 uint TIDY_CALL       tidyWarningCount( TidyDoc tdoc )
790 {
791     TidyDocImpl* impl = tidyDocToImpl( tdoc );
792     uint count = 0xFFFFFFFF;
793     if ( impl )
794         count = impl->warnings;
795     return count;
796 }
797 uint TIDY_CALL       tidyAccessWarningCount( TidyDoc tdoc )
798 {
799     TidyDocImpl* impl = tidyDocToImpl( tdoc );
800     uint count = 0xFFFFFFFF;
801     if ( impl )
802         count = impl->accessErrors;
803     return count;
804 }
805 uint TIDY_CALL       tidyConfigErrorCount( TidyDoc tdoc )
806 {
807     TidyDocImpl* impl = tidyDocToImpl( tdoc );
808     uint count = 0xFFFFFFFF;
809     if ( impl )
810         count = impl->optionErrors;
811     return count;
812 }
813
814
815 /* Error reporting functions
816 */
817 void TIDY_CALL         tidyErrorSummary( TidyDoc tdoc )
818 {
819     TidyDocImpl* impl = tidyDocToImpl( tdoc );
820     if ( impl )
821         TY_(ErrorSummary)( impl );
822 }
823 void TIDY_CALL         tidyGeneralInfo( TidyDoc tdoc )
824 {
825     TidyDocImpl* impl = tidyDocToImpl( tdoc );
826     if ( impl )
827         TY_(GeneralInfo)( impl );
828 }
829
830
831 /* I/O Functions
832 **
833 ** Initial version supports only whole-file operations.
834 ** Do not expose Tidy StreamIn or Out data structures - yet.
835 */
836
837 /* Parse/load Functions
838 **
839 ** HTML/XHTML version determined from input.
840 */
841 int TIDY_CALL  tidyParseFile( TidyDoc tdoc, ctmbstr filnam )
842 {
843     TidyDocImpl* doc = tidyDocToImpl( tdoc );
844     return tidyDocParseFile( doc, filnam );
845 }
846 int TIDY_CALL  tidyParseStdin( TidyDoc tdoc )
847 {
848     TidyDocImpl* doc = tidyDocToImpl( tdoc );
849     return tidyDocParseStdin( doc );
850 }
851 int TIDY_CALL  tidyParseString( TidyDoc tdoc, ctmbstr content )
852 {
853     TidyDocImpl* doc = tidyDocToImpl( tdoc );
854     return tidyDocParseString( doc, content );
855 }
856 int TIDY_CALL  tidyParseBuffer( TidyDoc tdoc, TidyBuffer* inbuf )
857 {
858     TidyDocImpl* doc = tidyDocToImpl( tdoc );
859     return tidyDocParseBuffer( doc, inbuf );
860 }
861 int TIDY_CALL  tidyParseSource( TidyDoc tdoc, TidyInputSource* source )
862 {
863     TidyDocImpl* doc = tidyDocToImpl( tdoc );
864     return tidyDocParseSource( doc, source );
865 }
866
867
868 int   tidyDocParseFile( TidyDocImpl* doc, ctmbstr filnam )
869 {
870 #ifdef _WIN32
871     return TY_(DocParseFileWithMappedFile)( doc, filnam );
872 #else
873     int status = -ENOENT;
874     FILE* fin = fopen( filnam, "rb" );
875
876 #if PRESERVE_FILE_TIMES
877     struct stat sbuf = {0};
878     /* get last modified time */
879     TidyClearMemory( &doc->filetimes, sizeof(doc->filetimes) );
880     if ( fin && cfgBool(doc,TidyKeepFileTimes) &&
881          fstat(fileno(fin), &sbuf) != -1 )
882     {
883           doc->filetimes.actime  = sbuf.st_atime;
884           doc->filetimes.modtime = sbuf.st_mtime;
885     }
886 #endif
887
888     if ( fin )
889     {
890         StreamIn* in = TY_(FileInput)( doc, fin, cfg( doc, TidyInCharEncoding ));
891         if ( !in )
892         {
893             fclose( fin );
894             return status;
895         }
896         status = TY_(DocParseStream)( doc, in );
897         TY_(freeFileSource)(&in->source, yes);
898         TY_(freeStreamIn)(in);
899     }
900     else /* Error message! */
901         TY_(FileError)( doc, filnam, TidyError );
902     return status;
903 #endif
904 }
905
906 int   tidyDocParseStdin( TidyDocImpl* doc )
907 {
908     StreamIn* in = TY_(FileInput)( doc, stdin, cfg( doc, TidyInCharEncoding ));
909     int status = TY_(DocParseStream)( doc, in );
910     TY_(freeStreamIn)(in);
911     return status;
912 }
913
914 int   tidyDocParseBuffer( TidyDocImpl* doc, TidyBuffer* inbuf )
915 {
916     int status = -EINVAL;
917     if ( inbuf )
918     {
919         StreamIn* in = TY_(BufferInput)( doc, inbuf, cfg( doc, TidyInCharEncoding ));
920         status = TY_(DocParseStream)( doc, in );
921         TY_(freeStreamIn)(in);
922     }
923     return status;
924 }
925
926 int   tidyDocParseString( TidyDocImpl* doc, ctmbstr content )
927 {
928     int status = -EINVAL;
929     TidyBuffer inbuf;
930     StreamIn* in = NULL;
931
932     if ( content )
933     {
934         tidyBufInitWithAllocator( &inbuf, doc->allocator );
935         tidyBufAttach( &inbuf, (byte*)content, TY_(tmbstrlen)(content)+1 );
936         in = TY_(BufferInput)( doc, &inbuf, cfg( doc, TidyInCharEncoding ));
937         status = TY_(DocParseStream)( doc, in );
938         tidyBufDetach( &inbuf );
939         TY_(freeStreamIn)(in);
940     }
941     return status;
942 }
943
944 int   tidyDocParseSource( TidyDocImpl* doc, TidyInputSource* source )
945 {
946     StreamIn* in = TY_(UserInput)( doc, source, cfg( doc, TidyInCharEncoding ));
947     int status = TY_(DocParseStream)( doc, in );
948     TY_(freeStreamIn)(in);
949     return status;
950 }
951
952
953 /* Print/save Functions
954 **
955 */
956 int TIDY_CALL        tidySaveFile( TidyDoc tdoc, ctmbstr filnam )
957 {
958     TidyDocImpl* doc = tidyDocToImpl( tdoc );
959     return tidyDocSaveFile( doc, filnam );
960 }
961 int TIDY_CALL        tidySaveStdout( TidyDoc tdoc )
962 {
963     TidyDocImpl* doc = tidyDocToImpl( tdoc );
964     return tidyDocSaveStdout( doc );
965 }
966 int TIDY_CALL        tidySaveString( TidyDoc tdoc, tmbstr buffer, uint* buflen )
967 {
968     TidyDocImpl* doc = tidyDocToImpl( tdoc );
969     return tidyDocSaveString( doc, buffer, buflen );
970 }
971 int TIDY_CALL        tidySaveBuffer( TidyDoc tdoc, TidyBuffer* outbuf )
972 {
973     TidyDocImpl* doc = tidyDocToImpl( tdoc );
974     return tidyDocSaveBuffer( doc, outbuf );
975 }
976 int TIDY_CALL        tidySaveSink( TidyDoc tdoc, TidyOutputSink* sink )
977 {
978     TidyDocImpl* doc = tidyDocToImpl( tdoc );
979     return tidyDocSaveSink( doc, sink );
980 }
981
982 int         tidyDocSaveFile( TidyDocImpl* doc, ctmbstr filnam )
983 {
984     int status = -ENOENT;
985     FILE* fout = NULL;
986
987     /* Don't zap input file if no output */
988     if ( doc->errors > 0 &&
989          cfgBool(doc, TidyWriteBack) && !cfgBool(doc, TidyForceOutput) )
990         status = tidyDocStatus( doc );
991     else
992         fout = fopen( filnam, "wb" );
993
994     if ( fout )
995     {
996         uint outenc = cfg( doc, TidyOutCharEncoding );
997         uint nl = cfg( doc, TidyNewline );
998         StreamOut* out = TY_(FileOutput)( doc, fout, outenc, nl );
999
1000         status = tidyDocSaveStream( doc, out );
1001
1002         fclose( fout );
1003         TidyDocFree( doc, out );
1004
1005 #if PRESERVE_FILE_TIMES
1006         if ( doc->filetimes.actime )
1007         {
1008             /* set file last accessed/modified times to original values */
1009             utime( filnam, &doc->filetimes );
1010             TidyClearMemory( &doc->filetimes, sizeof(doc->filetimes) );
1011         }
1012 #endif /* PRESERVFILETIMES */
1013     }
1014     if ( status < 0 ) /* Error message! */
1015         TY_(FileError)( doc, filnam, TidyError );
1016     return status;
1017 }
1018
1019
1020
1021 /* Note, _setmode() does NOT work on Win2K Pro w/ VC++ 6.0 SP3.
1022 ** The code has been left in in case it works w/ other compilers
1023 ** or operating systems.  If stdout is in Text mode, be aware that
1024 ** it will garble UTF16 documents.  In text mode, when it encounters
1025 ** a single byte of value 10 (0xA), it will insert a single byte
1026 ** value 13 (0xD) just before it.  This has the effect of garbling
1027 ** the entire document.
1028 */
1029
1030 #if !defined(NO_SETMODE_SUPPORT)
1031
1032 #if defined(_WIN32) || defined(OS2_OS)
1033 #include <fcntl.h>
1034 #include <io.h>
1035 #endif
1036
1037 #endif
1038
1039 int         tidyDocSaveStdout( TidyDocImpl* doc )
1040 {
1041 #if !defined(NO_SETMODE_SUPPORT)
1042
1043 #if defined(_WIN32) || defined(OS2_OS)
1044     int oldstdoutmode = -1, oldstderrmode = -1;
1045 #endif
1046
1047 #endif
1048     int status = 0;
1049     uint outenc = cfg( doc, TidyOutCharEncoding );
1050     uint nl = cfg( doc, TidyNewline );
1051     StreamOut* out = TY_(FileOutput)( doc, stdout, outenc, nl );
1052
1053 #if !defined(NO_SETMODE_SUPPORT)
1054
1055 #if defined(_WIN32) || defined(OS2_OS)
1056     oldstdoutmode = setmode( fileno(stdout), _O_BINARY );
1057     oldstderrmode = setmode( fileno(stderr), _O_BINARY );
1058 #endif
1059
1060 #endif
1061
1062     if ( 0 == status )
1063       status = tidyDocSaveStream( doc, out );
1064
1065     fflush(stdout);
1066     fflush(stderr);
1067
1068 #if !defined(NO_SETMODE_SUPPORT)
1069
1070 #if defined(_WIN32) || defined(OS2_OS)
1071     if ( oldstdoutmode != -1 )
1072         oldstdoutmode = setmode( fileno(stdout), oldstdoutmode );
1073     if ( oldstderrmode != -1 )
1074         oldstderrmode = setmode( fileno(stderr), oldstderrmode );
1075 #endif
1076
1077 #endif
1078
1079     TidyDocFree( doc, out );
1080     return status;
1081 }
1082
1083 int         tidyDocSaveString( TidyDocImpl* doc, tmbstr buffer, uint* buflen )
1084 {
1085     uint outenc = cfg( doc, TidyOutCharEncoding );
1086     uint nl = cfg( doc, TidyNewline );
1087     TidyBuffer outbuf;
1088     StreamOut* out;
1089     int status;
1090
1091     tidyBufInitWithAllocator( &outbuf, doc->allocator );
1092     out = TY_(BufferOutput)( doc, &outbuf, outenc, nl );
1093     status = tidyDocSaveStream( doc, out );
1094
1095     if ( outbuf.size > *buflen )
1096         status = -ENOMEM;
1097     else
1098         memcpy( buffer, outbuf.bp, outbuf.size );
1099
1100     *buflen = outbuf.size;
1101     tidyBufFree( &outbuf );
1102     TidyDocFree( doc, out );
1103     return status;
1104 }
1105
1106 int         tidyDocSaveBuffer( TidyDocImpl* doc, TidyBuffer* outbuf )
1107 {
1108     int status = -EINVAL;
1109     if ( outbuf )
1110     {
1111         uint outenc = cfg( doc, TidyOutCharEncoding );
1112         uint nl = cfg( doc, TidyNewline );
1113         StreamOut* out = TY_(BufferOutput)( doc, outbuf, outenc, nl );
1114
1115         status = tidyDocSaveStream( doc, out );
1116         TidyDocFree( doc, out );
1117     }
1118     return status;
1119 }
1120
1121 int         tidyDocSaveSink( TidyDocImpl* doc, TidyOutputSink* sink )
1122 {
1123     uint outenc = cfg( doc, TidyOutCharEncoding );
1124     uint nl = cfg( doc, TidyNewline );
1125     StreamOut* out = TY_(UserOutput)( doc, sink, outenc, nl );
1126     int status = tidyDocSaveStream( doc, out );
1127     TidyDocFree( doc, out );
1128     return status;
1129 }
1130
1131 int         tidyDocStatus( TidyDocImpl* doc )
1132 {
1133     if ( doc->errors > 0 )
1134         return 2;
1135     if ( doc->warnings > 0 || doc->accessErrors > 0 )
1136         return 1;
1137     return 0;
1138 }
1139
1140
1141
1142 int TIDY_CALL        tidyCleanAndRepair( TidyDoc tdoc )
1143 {
1144     TidyDocImpl* impl = tidyDocToImpl( tdoc );
1145     if ( impl )
1146       return tidyDocCleanAndRepair( impl );
1147     return -EINVAL;
1148 }
1149
1150 int TIDY_CALL        tidyRunDiagnostics( TidyDoc tdoc )
1151 {
1152     TidyDocImpl* impl = tidyDocToImpl( tdoc );
1153     if ( impl )
1154       return tidyDocRunDiagnostics( impl );
1155     return -EINVAL;
1156 }
1157
1158 int TIDY_CALL        tidyReportDoctype( TidyDoc tdoc )
1159 {
1160     int iret = -EINVAL;
1161     TidyDocImpl* impl = tidyDocToImpl( tdoc );
1162     if ( impl ) {
1163       tidyDocReportDoctype( impl );
1164       iret = 0;
1165     }
1166     return iret;
1167 }
1168
1169 /* Workhorse functions.
1170 **
1171 ** Parse requires input source, all input config items
1172 ** and diagnostic sink to have all been set before calling.
1173 **
1174 ** Emit likewise requires that document sink and all
1175 ** pretty printing options have been set.
1176 */
1177 static ctmbstr integrity = "\nPanic - tree has lost its integrity\n";
1178
1179 int         TY_(DocParseStream)( TidyDocImpl* doc, StreamIn* in )
1180 {
1181     Bool xmlIn = cfgBool( doc, TidyXmlTags );
1182     int bomEnc;
1183
1184     assert( doc != NULL && in != NULL );
1185     assert( doc->docIn == NULL );
1186     doc->docIn = in;
1187
1188     TY_(TakeConfigSnapshot)( doc );    /* Save config state */
1189     TY_(FreeAnchors)( doc );
1190
1191     TY_(FreeNode)(doc, &doc->root);
1192     TidyClearMemory(&doc->root, sizeof(Node));
1193
1194     if (doc->givenDoctype)
1195         TidyDocFree(doc, doc->givenDoctype);
1196     /*\ 
1197      *  Issue #186 - Now FreeNode depend on the doctype, so the lexer is needed
1198      *  to determine which hash is to be used, so free it last.
1199     \*/
1200     TY_(FreeLexer)( doc );
1201     doc->givenDoctype = NULL;
1202
1203     doc->lexer = TY_(NewLexer)( doc );
1204     /* doc->lexer->root = &doc->root; */
1205     doc->root.line = doc->lexer->lines;
1206     doc->root.column = doc->lexer->columns;
1207     doc->inputHadBOM = no;
1208
1209     bomEnc = TY_(ReadBOMEncoding)(in);
1210
1211     if (bomEnc != -1)
1212     {
1213         in->encoding = bomEnc;
1214         TY_(SetOptionInt)(doc, TidyInCharEncoding, bomEnc);
1215     }
1216
1217 #ifdef TIDY_WIN32_MLANG_SUPPORT
1218     if (in->encoding > WIN32MLANG)
1219         TY_(Win32MLangInitInputTranscoder)(in, in->encoding);
1220 #endif /* TIDY_WIN32_MLANG_SUPPORT */
1221
1222     /* Tidy doesn't alter the doctype for generic XML docs */
1223     if ( xmlIn )
1224     {
1225         TY_(ParseXMLDocument)( doc );
1226         if ( !TY_(CheckNodeIntegrity)( &doc->root ) )
1227             TidyPanic( doc->allocator, integrity );
1228     }
1229     else
1230     {
1231         doc->warnings = 0;
1232         TY_(ParseDocument)( doc );
1233         if ( !TY_(CheckNodeIntegrity)( &doc->root ) )
1234             TidyPanic( doc->allocator, integrity );
1235     }
1236
1237 #ifdef TIDY_WIN32_MLANG_SUPPORT
1238     TY_(Win32MLangUninitInputTranscoder)(in);
1239 #endif /* TIDY_WIN32_MLANG_SUPPORT */
1240
1241     doc->docIn = NULL;
1242     return tidyDocStatus( doc );
1243 }
1244
1245 int         tidyDocRunDiagnostics( TidyDocImpl* doc )
1246 {
1247     Bool quiet = cfgBool( doc, TidyQuiet );
1248     Bool force = cfgBool( doc, TidyForceOutput );
1249
1250     if ( !quiet )
1251     {
1252
1253         TY_(ReportMarkupVersion)( doc );
1254         TY_(ReportNumWarnings)( doc );
1255     }
1256
1257     if ( doc->errors > 0 && !force )
1258         TY_(NeedsAuthorIntervention)( doc );
1259
1260      return tidyDocStatus( doc );
1261 }
1262
1263 void         tidyDocReportDoctype( TidyDocImpl* doc )
1264 {
1265         TY_(ReportMarkupVersion)( doc );
1266 }
1267
1268
1269 /* ######################################################################################
1270    HTML5 STUFF
1271  */
1272 #if !defined(NDEBUG) && defined(_MSC_VER)
1273 extern void show_not_html5(void);
1274 /* -----------------------------
1275 List tags that do not have version HTML5 (HT50|XH50)
1276
1277 acronym applet basefont big center dir font frame frameset isindex
1278 listing noframes plaintext rb rbc rtc strike tt xmp nextid
1279 align bgsound blink comment ilayer layer marquee multicol nobr noembed
1280 nolayer nosave server servlet spacer
1281
1282 Listed total 35 tags that do not have version 393216
1283    ------------------------------ */
1284
1285 static void list_not_html5(void)
1286 {
1287     static Bool done_list = no;
1288     if (done_list == no) {
1289         done_list = yes;
1290         show_not_html5();
1291     }
1292 }
1293 #endif
1294
1295 /* What about <blink>, <s> stike-through, <u> underline */
1296 static struct _html5Info
1297 {
1298     const char *tag;
1299     uint id;
1300 } const html5Info[] = {
1301     {"acronym", TidyTag_ACRONYM},
1302     {"applet", TidyTag_APPLET  },
1303     {"basefont",TidyTag_BASEFONT },
1304     { "big", TidyTag_BIG },
1305     { "center", TidyTag_CENTER },
1306     { "dir", TidyTag_DIR },
1307     { "font", TidyTag_FONT },
1308     { "frame", TidyTag_FRAME},
1309     { "frameset", TidyTag_FRAMESET},
1310     { "noframes", TidyTag_NOFRAMES },
1311     { "strike", TidyTag_STRIKE },
1312     { "tt", TidyTag_TT },
1313     { 0, 0 }
1314 };
1315 Bool inRemovedInfo( uint tid )
1316 {
1317     int i;
1318     for (i = 0; ; i++) {
1319         if (html5Info[i].tag == 0)
1320             break;
1321         if (html5Info[i].id == tid)
1322             return yes;
1323     }
1324     return no;
1325 }
1326
1327 static Bool BadBody5( Node* node )
1328 {
1329     if (TY_(AttrGetById)(node, TidyAttr_BACKGROUND) ||
1330         TY_(AttrGetById)(node, TidyAttr_BGCOLOR)    ||
1331         TY_(AttrGetById)(node, TidyAttr_TEXT)       ||
1332         TY_(AttrGetById)(node, TidyAttr_LINK)       ||
1333         TY_(AttrGetById)(node, TidyAttr_VLINK)      ||
1334         TY_(AttrGetById)(node, TidyAttr_ALINK))
1335     {
1336         return yes;
1337     }
1338     return no;
1339 }
1340
1341 static Bool nodeHasAlignAttr( Node *node )
1342 {
1343     /* #define attrIsALIGN(av) AttrIsId( av, TidyAttr_ALIGN  ) */
1344     AttVal* av;
1345     for ( av = node->attributes; av != NULL; av = av->next ) {
1346         if (attrIsALIGN(av))
1347             return yes;
1348     }
1349     return no;
1350 }
1351
1352 /* see http://www.whatwg.org/specs/web-apps/current-work/multipage/obsolete.html#obsolete */
1353
1354 void TY_(CheckHTML5)( TidyDocImpl* doc, Node* node )
1355 {
1356     /* Lexer* lexer = doc->lexer; */
1357     Bool clean = cfgBool( doc, TidyMakeClean );
1358     Node* body = TY_(FindBody)( doc );
1359     Bool warn = yes;    /* should this be a warning, error, or report??? */
1360 #if !defined(NDEBUG) && defined(_MSC_VER)
1361 //    list_not_html5();
1362 #endif
1363     while (node)
1364     {
1365         if ( nodeHasAlignAttr( node ) ) {
1366             /*\
1367              * Is this for ALL elements that accept an 'align' attribute, or should
1368              * this be a sub-set test
1369             \*/
1370             TY_(ReportWarning)(doc, node, node, BAD_ALIGN_HTML5);
1371         }
1372         if ( node == body ) {
1373             if ( BadBody5(body) ) {
1374                 /* perhaps need a new/different warning for this, like
1375                  * The background 'attribute" on the body element is obsolete. Use CSS instead.
1376                  * but how to pass an attribute name to be embedded in the message.
1377                 \*/
1378                 TY_(ReportWarning)(doc, node, body, BAD_BODY_HTML5);
1379             }
1380         } else
1381         if ( nodeIsACRONYM(node) ) {
1382             if (clean) {
1383                 /* replace with 'abbr' with warning to that effect 
1384                  * maybe should use static void RenameElem( TidyDocImpl* doc, Node* node, TidyTagId tid )
1385                  */
1386                 TY_(CoerceNode)(doc, node, TidyTag_ABBR, warn, no);
1387             } else {
1388                 /* sadly, this stops writing of the tidied document, unless 'forced'
1389                    TY_(ReportError)(doc, node, node, REMOVED_HTML5); 
1390                    so go back to a 'warning' for now...
1391                 */
1392                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1393             }
1394         } else 
1395         if ( nodeIsAPPLET(node) ) {
1396             if (clean) {
1397                 /* replace with 'object' with warning to that effect 
1398                  * maybe should use static void RenameElem( TidyDocImpl* doc, Node* node, TidyTagId tid )
1399                  */
1400                 TY_(CoerceNode)(doc, node, TidyTag_OBJECT, warn, no);
1401             } else {
1402                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1403             }
1404         } else
1405         if ( nodeIsBASEFONT(node) ) {
1406             /*\ 
1407              * basefont: CSS equivalen 'font-size', 'font-family' and 'color' on body or class on each subsequent element
1408              * Difficult - If it is the first body element, then could consider adding that
1409              *  to the <body> as a whole, else could perhaps apply it to all subsequent element.
1410              *  But also in consideration is the fact that it was NOT supported in many browsers
1411              *  For now just report a warning
1412             \*/
1413                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1414         } else
1415         if ( nodeIsBIG(node) ) {
1416             /*\
1417              * big: CSS equivalent      'font-size:larger'
1418              * so could replace the <big> ... </big> with 
1419              * <span style="font-size: larger"> ... </span>
1420              * then replace <big> with <span>
1421              * Need to think about that...
1422              * Could use -
1423              *   TY_(AddStyleProperty)( doc, node, "font-size: larger" );
1424              *   TY_(CoerceNode)(doc, node, TidyTag_SPAN, no, no);
1425              * Alternatively generated a <style> but how to get the style name
1426              * TY_(AddAttribute)( doc, node, "class", "????" );
1427              * Also maybe need a specific message like
1428              * Element '%s' replaced with 'span' with a 'font-size: larger style attribute
1429              * maybe should use static void RenameElem( TidyDocImpl* doc, Node* node, TidyTagId tid )
1430              *
1431             \*/
1432             if (clean) {
1433                 TY_(AddStyleProperty)( doc, node, "font-size: larger" );
1434                 TY_(CoerceNode)(doc, node, TidyTag_SPAN, warn, no);
1435             } else {
1436                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1437             }
1438         } else
1439         if ( nodeIsCENTER(node) ) {
1440             /*\
1441              * center: CSS equivalent   'text-align:center'
1442              *  and 'margin-left:auto; margin-right:auto' on descendant blocks
1443              * Tidy already handles this if 'clean' by SILENTLY generating the <style>
1444              * and adding a <div class="c1"> around the elements.
1445              * see: static Bool Center2Div( TidyDocImpl* doc, Node *node, Node **pnode)
1446             \*/
1447                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1448         } else 
1449         if ( nodeIsDIR(node) ) {
1450             /*\
1451              *  dir: replace by <ul>
1452              *  Tidy already actions this and issues a warning
1453              *  Should this be CHANGED???
1454             \*/
1455                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1456         } else
1457         if ( nodeIsFONT(node) ) {
1458             /*\
1459              * Tidy already handles this -
1460              * If 'clean' replaced by CSS, else 
1461              * if is NOT clean, and doctype html5 then warnings issued
1462              * done in Bool Font2Span( TidyDocImpl* doc, Node *node, Node **pnode ) (I think?)
1463              *
1464             \*/
1465                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1466         } else
1467         if (( nodesIsFRAME(node) ) || ( nodeIsFRAMESET(node) ) || ( nodeIsNOFRAMES(node) )) {
1468             /*\
1469              * YOW: What to do here?????? Maybe <iframe>????
1470             \*/
1471                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1472         } else
1473         if ( nodeIsSTRIKE(node) ) {
1474             /*\
1475              * strike: CSS equivalent   'text-decoration:line-through'
1476              * maybe should use static void RenameElem( TidyDocImpl* doc, Node* node, TidyTagId tid )
1477             \*/
1478             if (clean) {
1479                 TY_(AddStyleProperty)( doc, node, "text-decoration: line-through" );
1480                 TY_(CoerceNode)(doc, node, TidyTag_SPAN, warn, no);
1481             } else {
1482                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1483             }
1484         } else
1485         if ( nodeIsTT(node) ) {
1486             /*\
1487              * tt: CSS equivalent       'font-family:monospace'
1488              * Tidy presently does nothing. Tidy5 issues a warning
1489              * But like the 'clean' <font> replacement this could also be replaced with CSS
1490              * maybe should use static void RenameElem( TidyDocImpl* doc, Node* node, TidyTagId tid )
1491              *
1492             \*/
1493             if (clean) {
1494                 TY_(AddStyleProperty)( doc, node, "font-family: monospace" );
1495                 TY_(CoerceNode)(doc, node, TidyTag_SPAN, warn, no);
1496             } else {
1497                 TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1498             }
1499         } else
1500         if (TY_(nodeIsElement)(node)) {
1501             if (node->tag) {
1502                 if ((!(node->tag->versions & VERS_HTML5))||(inRemovedInfo(node->tag->id))) {
1503                     /* issue warning for elements like 'markquee' */
1504                     TY_(ReportWarning)(doc, node, node, REMOVED_HTML5);
1505                 }
1506             }
1507         }
1508
1509         if (node->content)
1510             TY_(CheckHTML5)( doc, node->content );
1511
1512         node = node->next;
1513     }
1514 }
1515 /* END HTML5 STUFF
1516    ######################################################################################
1517  */
1518
1519 #if !defined(NDEBUG) && defined(_MSC_VER)
1520 /* *** FOR DEBUG ONLY *** */
1521 const char *dbg_get_lexer_type( void *vp )
1522 {
1523     Node *node = (Node *)vp;
1524     switch ( node->type )
1525     {
1526     case RootNode:      return "Root";
1527     case DocTypeTag:    return "DocType";
1528     case CommentTag:    return "Comment";
1529     case ProcInsTag:    return "ProcIns";
1530     case TextNode:      return "Text";
1531     case StartTag:      return "StartTag";
1532     case EndTag:        return "EndTag";
1533     case StartEndTag:   return "StartEnd";
1534     case CDATATag:      return "CDATA";
1535     case SectionTag:    return "Section";
1536     case AspTag:        return "Asp";
1537     case JsteTag:       return "Jste";
1538     case PhpTag:        return "Php";
1539     case XmlDecl:       return "XmlDecl";
1540     }
1541     return "Uncased";
1542 }
1543
1544 /* NOTE: THis matches the above lexer type, except when element has a name */
1545 const char *dbg_get_element_name( void *vp )
1546 {
1547     Node *node = (Node *)vp;
1548     switch ( node->type )
1549     {
1550     case TidyNode_Root:       return "Root";
1551     case TidyNode_DocType:    return "DocType";
1552     case TidyNode_Comment:    return "Comment";
1553     case TidyNode_ProcIns:    return "ProcIns";
1554     case TidyNode_Text:       return "Text";
1555     case TidyNode_CDATA:      return "CDATA";
1556     case TidyNode_Section:    return "Section";
1557     case TidyNode_Asp:        return "Asp";
1558     case TidyNode_Jste:       return "Jste";
1559     case TidyNode_Php:        return "Php";
1560     case TidyNode_XmlDecl:    return "XmlDecl";
1561
1562     case TidyNode_Start:
1563     case TidyNode_End:
1564     case TidyNode_StartEnd:
1565     default:
1566         if (node->element)
1567             return node->element;
1568     }
1569     return "Unknown";
1570 }
1571
1572 void dbg_show_node( TidyDocImpl* doc, Node *node, int caller, int indent )
1573 {
1574     AttVal* av;
1575     ctmbstr call = "";
1576     ctmbstr name = dbg_get_element_name(node);
1577     ctmbstr type = dbg_get_lexer_type(node);
1578     ctmbstr impl = node->implicit ? "implicit" : "";
1579     switch ( caller )
1580     {
1581     case 1: call = "discard";   break;
1582     case 2: call = "trim";      break;
1583     case 3: call = "test";      break;
1584     }
1585     while (indent--)
1586         SPRTF(" ");
1587     if (strcmp(type,name))
1588         SPRTF("%s %s %s %s", type, name, impl, call );
1589     else
1590         SPRTF("%s %s %s", name, impl, call );
1591     for (av = node->attributes; av; av = av->next) {
1592         name = av->attribute;
1593         if (name) {
1594             SPRTF(" %s",name);
1595             if (av->value) {
1596                 SPRTF("=\"%s\"", av->value);
1597             }
1598         }
1599     }
1600     SPRTF("\n");
1601 }
1602
1603 void dbg_show_all_nodes( TidyDocImpl* doc, Node *node, int indent )
1604 {
1605     while (node)
1606     {
1607         dbg_show_node( doc, node, 0, indent );
1608         dbg_show_all_nodes( doc, node->content, indent + 1 );
1609         node = node->next;
1610     }
1611 }
1612
1613 #endif
1614
1615 int         tidyDocCleanAndRepair( TidyDocImpl* doc )
1616 {
1617     Bool word2K   = cfgBool( doc, TidyWord2000 );
1618     Bool logical  = cfgBool( doc, TidyLogicalEmphasis );
1619     Bool clean    = cfgBool( doc, TidyMakeClean );
1620     Bool gdoc     = cfgBool( doc, TidyGDocClean );
1621     Bool dropFont = cfgBool( doc, TidyDropFontTags );
1622     Bool htmlOut  = cfgBool( doc, TidyHtmlOut );
1623     Bool xmlOut   = cfgBool( doc, TidyXmlOut );
1624     Bool xhtmlOut = cfgBool( doc, TidyXhtmlOut );
1625     Bool xmlDecl  = cfgBool( doc, TidyXmlDecl );
1626     Bool tidyMark = cfgBool( doc, TidyMark );
1627     Bool tidyXmlTags = cfgBool( doc, TidyXmlTags );
1628     Bool wantNameAttr = cfgBool( doc, TidyAnchorAsName );
1629     Bool mergeEmphasis = cfgBool( doc, TidyMergeEmphasis );
1630     ctmbstr sdef = NULL;
1631     Node* node;
1632
1633 #if !defined(NDEBUG) && defined(_MSC_VER)
1634     SPRTF("All nodes BEFORE clean and repair\n");
1635     dbg_show_all_nodes( doc, &doc->root, 0  );
1636 #endif
1637     if (tidyXmlTags)
1638        return tidyDocStatus( doc );
1639
1640     /* simplifies <b><b> ... </b> ...</b> etc. */
1641     if ( mergeEmphasis )
1642         TY_(NestedEmphasis)( doc, &doc->root );
1643
1644     /* cleans up <dir>indented text</dir> etc. */
1645     TY_(List2BQ)( doc, &doc->root );
1646     TY_(BQ2Div)( doc, &doc->root );
1647
1648     /* replaces i by em and b by strong */
1649     if ( logical )
1650         TY_(EmFromI)( doc, &doc->root );
1651
1652     if ( word2K && TY_(IsWord2000)(doc) )
1653     {
1654         /* prune Word2000's <![if ...]> ... <![endif]> */
1655         TY_(DropSections)( doc, &doc->root );
1656
1657         /* drop style & class attributes and empty p, span elements */
1658         TY_(CleanWord2000)( doc, &doc->root );
1659         TY_(DropEmptyElements)(doc, &doc->root);
1660     }
1661
1662     /* replaces presentational markup by style rules */
1663     if ( clean || dropFont )
1664         TY_(CleanDocument)( doc );
1665
1666     /* clean up html exported by Google Docs */
1667     if ( gdoc )
1668         TY_(CleanGoogleDocument)( doc );
1669
1670     /*  Move terminating <br /> tags from out of paragraphs  */
1671     /*!  Do we want to do this for all block-level elements?  */
1672
1673     /* This is disabled due to http://tidy.sf.net/bug/681116 */
1674 #if 0
1675     FixBrakes( doc, TY_(FindBody)( doc ));
1676 #endif
1677
1678     /*  Reconcile http-equiv meta element with output encoding  */
1679     if (cfg( doc, TidyOutCharEncoding) != RAW
1680 #ifndef NO_NATIVE_ISO2022_SUPPORT
1681         && cfg( doc, TidyOutCharEncoding) != ISO2022
1682 #endif
1683         )
1684         TY_(VerifyHTTPEquiv)( doc, TY_(FindHEAD)( doc ));
1685
1686     if ( !TY_(CheckNodeIntegrity)( &doc->root ) )
1687         TidyPanic( doc->allocator, integrity );
1688
1689     /* remember given doctype for reporting */
1690     node = TY_(FindDocType)(doc);
1691     sdef = tidyOptGetValue((TidyDoc)doc, TidyDoctype );
1692     if (!sdef)
1693         sdef = tidyOptGetCurrPick((TidyDoc) doc, TidyDoctypeMode );
1694     if (sdef && (strcmp(sdef,"html5") == 0)) {
1695         TY_(CheckHTML5)( doc, &doc->root );
1696     }
1697     if (node)
1698     {
1699         AttVal* fpi = TY_(GetAttrByName)(node, "PUBLIC");
1700         if (AttrHasValue(fpi))
1701         {
1702             if (doc->givenDoctype)
1703                 TidyDocFree(doc, doc->givenDoctype);
1704             doc->givenDoctype = TY_(tmbstrdup)(doc->allocator,fpi->value);
1705         }
1706     }
1707
1708     if ( doc->root.content )
1709     {
1710         /* If we had XHTML input but want HTML output */
1711         if ( htmlOut && doc->lexer->isvoyager )
1712         {
1713             Node* node = TY_(FindDocType)(doc);
1714             /* Remove reference, but do not free */
1715             if (node)
1716               TY_(RemoveNode)(node);
1717         }
1718
1719         if (xhtmlOut && !htmlOut)
1720         {
1721             TY_(SetXHTMLDocType)(doc);
1722             TY_(FixAnchors)(doc, &doc->root, wantNameAttr, yes);
1723             TY_(FixXhtmlNamespace)(doc, yes);
1724             TY_(FixLanguageInformation)(doc, &doc->root, yes, yes);
1725         }
1726         else
1727         {
1728             TY_(FixDocType)(doc);
1729             TY_(FixAnchors)(doc, &doc->root, wantNameAttr, yes);
1730             TY_(FixXhtmlNamespace)(doc, no);
1731             TY_(FixLanguageInformation)(doc, &doc->root, no, yes);
1732         }
1733
1734         if (tidyMark )
1735             TY_(AddGenerator)(doc);
1736     }
1737
1738     /* ensure presence of initial <?xml version="1.0"?> */
1739     if ( xmlOut && xmlDecl )
1740         TY_(FixXmlDecl)( doc );
1741
1742 #if !defined(NDEBUG) && defined(_MSC_VER)
1743     SPRTF("All nodes AFTER clean and repair\n");
1744     dbg_show_all_nodes( doc, &doc->root, 0  );
1745 #endif
1746     return tidyDocStatus( doc );
1747 }
1748
1749 static
1750 Bool showBodyOnly( TidyDocImpl* doc, TidyTriState bodyOnly )
1751 {
1752     Node* node;
1753
1754     switch( bodyOnly )
1755     {
1756     case TidyNoState:
1757         return no;
1758     case TidyYesState:
1759         return yes;
1760     default:
1761         node = TY_(FindBody)( doc );
1762         if (node && node->implicit )
1763             return yes;
1764     }
1765     return no;
1766 }
1767
1768
1769 int         tidyDocSaveStream( TidyDocImpl* doc, StreamOut* out )
1770 {
1771     Bool showMarkup  = cfgBool( doc, TidyShowMarkup );
1772     Bool forceOutput = cfgBool( doc, TidyForceOutput );
1773 #if SUPPORT_UTF16_ENCODINGS
1774     Bool outputBOM   = ( cfgAutoBool(doc, TidyOutputBOM) == TidyYesState );
1775     Bool smartBOM    = ( cfgAutoBool(doc, TidyOutputBOM) == TidyAutoState );
1776 #endif
1777     Bool xmlOut      = cfgBool( doc, TidyXmlOut );
1778     Bool xhtmlOut    = cfgBool( doc, TidyXhtmlOut );
1779     TidyTriState bodyOnly    = cfgAutoBool( doc, TidyBodyOnly );
1780
1781     Bool dropComments = cfgBool(doc, TidyHideComments);
1782     Bool makeClean    = cfgBool(doc, TidyMakeClean);
1783     Bool asciiChars   = cfgBool(doc, TidyAsciiChars);
1784     Bool makeBare     = cfgBool(doc, TidyMakeBare);
1785     Bool escapeCDATA  = cfgBool(doc, TidyEscapeCdata);
1786     TidyAttrSortStrategy sortAttrStrat = cfg(doc, TidySortAttributes);
1787
1788     if (escapeCDATA)
1789         TY_(ConvertCDATANodes)(doc, &doc->root);
1790
1791     if (dropComments)
1792         TY_(DropComments)(doc, &doc->root);
1793
1794     if (makeClean)
1795     {
1796         /* noop */
1797         TY_(DropFontElements)(doc, &doc->root, NULL);
1798     }
1799
1800     if ((makeClean && asciiChars) || makeBare)
1801         TY_(DowngradeTypography)(doc, &doc->root);
1802
1803     if (makeBare)
1804         /* Note: no longer replaces &nbsp; in */
1805         /* attribute values / non-text tokens */
1806         TY_(NormalizeSpaces)(doc->lexer, &doc->root);
1807     else
1808         TY_(ReplacePreformattedSpaces)(doc, &doc->root);
1809
1810     if ( sortAttrStrat != TidySortAttrNone )
1811         TY_(SortAttributes)(&doc->root, sortAttrStrat);
1812
1813     if ( showMarkup && (doc->errors == 0 || forceOutput) )
1814     {
1815 #if SUPPORT_UTF16_ENCODINGS
1816         /* Output a Byte Order Mark if required */
1817         if ( outputBOM || (doc->inputHadBOM && smartBOM) )
1818             TY_(outBOM)( out );
1819 #endif
1820
1821         /* No longer necessary. No DOCTYPE == HTML 3.2,
1822         ** which gives you only the basic character entities,
1823         ** which are safe in any browser.
1824         ** if ( !TY_(FindDocType)(doc) )
1825         **    TY_(SetOptionBool)( doc, TidyNumEntities, yes );
1826         */
1827
1828         doc->docOut = out;
1829         if ( xmlOut && !xhtmlOut )
1830             TY_(PPrintXMLTree)( doc, NORMAL, 0, &doc->root );
1831         else if ( showBodyOnly( doc, bodyOnly ) )
1832             TY_(PrintBody)( doc );
1833         else
1834             TY_(PPrintTree)( doc, NORMAL, 0, &doc->root );
1835
1836         TY_(PFlushLine)( doc, 0 );
1837         doc->docOut = NULL;
1838     }
1839
1840     TY_(ResetConfigToSnapshot)( doc );
1841     return tidyDocStatus( doc );
1842 }
1843
1844 /* Tree traversal functions
1845 **
1846 ** The big issue here is the degree to which we should mimic
1847 ** a DOM and/or SAX nodes.
1848 **
1849 ** Is it 100% possible (and, if so, how difficult is it) to
1850 ** emit SAX events from this API?  If SAX events are possible,
1851 ** is that 100% of data needed to build a DOM?
1852 */
1853
1854 TidyNode TIDY_CALL   tidyGetRoot( TidyDoc tdoc )
1855 {
1856     TidyDocImpl* impl = tidyDocToImpl( tdoc );
1857     Node* node = NULL;
1858     if ( impl )
1859         node = &impl->root;
1860     return tidyImplToNode( node );
1861 }
1862
1863 TidyNode TIDY_CALL   tidyGetHtml( TidyDoc tdoc )
1864 {
1865   TidyDocImpl* impl = tidyDocToImpl( tdoc );
1866   Node* node = NULL;
1867   if ( impl )
1868       node = TY_(FindHTML)( impl );
1869   return tidyImplToNode( node );
1870 }
1871
1872 TidyNode TIDY_CALL    tidyGetHead( TidyDoc tdoc )
1873 {
1874   TidyDocImpl* impl = tidyDocToImpl( tdoc );
1875   Node* node = NULL;
1876   if ( impl )
1877       node = TY_(FindHEAD)( impl );
1878   return tidyImplToNode( node );
1879 }
1880
1881 TidyNode TIDY_CALL    tidyGetBody( TidyDoc tdoc )
1882 {
1883   TidyDocImpl* impl = tidyDocToImpl( tdoc );
1884   Node* node = NULL;
1885   if ( impl )
1886       node = TY_(FindBody)( impl );
1887   return tidyImplToNode( node );
1888 }
1889
1890 /* parent / child */
1891 TidyNode TIDY_CALL    tidyGetParent( TidyNode tnod )
1892 {
1893   Node* nimp = tidyNodeToImpl( tnod );
1894   return tidyImplToNode( nimp->parent );
1895 }
1896 TidyNode TIDY_CALL    tidyGetChild( TidyNode tnod )
1897 {
1898   Node* nimp = tidyNodeToImpl( tnod );
1899   return tidyImplToNode( nimp->content );
1900 }
1901
1902 /* siblings */
1903 TidyNode TIDY_CALL    tidyGetNext( TidyNode tnod )
1904 {
1905   Node* nimp = tidyNodeToImpl( tnod );
1906   return tidyImplToNode( nimp->next );
1907 }
1908 TidyNode TIDY_CALL    tidyGetPrev( TidyNode tnod )
1909 {
1910   Node* nimp = tidyNodeToImpl( tnod );
1911   return tidyImplToNode( nimp->prev );
1912 }
1913
1914 /* Node info */
1915 TidyNodeType TIDY_CALL tidyNodeGetType( TidyNode tnod )
1916 {
1917   Node* nimp = tidyNodeToImpl( tnod );
1918   TidyNodeType ntyp = TidyNode_Root;
1919   if ( nimp )
1920     ntyp = (TidyNodeType) nimp->type;
1921   return ntyp;
1922 }
1923
1924 uint TIDY_CALL tidyNodeLine( TidyNode tnod )
1925 {
1926   Node* nimp = tidyNodeToImpl( tnod );
1927   uint line = 0;
1928   if ( nimp )
1929     line = nimp->line;
1930   return line;
1931 }
1932 uint TIDY_CALL tidyNodeColumn( TidyNode tnod )
1933 {
1934   Node* nimp = tidyNodeToImpl( tnod );
1935   uint col = 0;
1936   if ( nimp )
1937     col = nimp->column;
1938   return col;
1939 }
1940
1941 ctmbstr TIDY_CALL        tidyNodeGetName( TidyNode tnod )
1942 {
1943   Node* nimp = tidyNodeToImpl( tnod );
1944   ctmbstr nnam = NULL;
1945   if ( nimp )
1946     nnam = nimp->element;
1947   return nnam;
1948 }
1949
1950
1951 Bool TIDY_CALL  tidyNodeHasText( TidyDoc tdoc, TidyNode tnod )
1952 {
1953   TidyDocImpl* doc = tidyDocToImpl( tdoc );
1954   if ( doc )
1955       return TY_(nodeHasText)( doc, tidyNodeToImpl(tnod) );
1956   return no;
1957 }
1958
1959
1960 Bool TIDY_CALL  tidyNodeGetText( TidyDoc tdoc, TidyNode tnod, TidyBuffer* outbuf )
1961 {
1962   TidyDocImpl* doc = tidyDocToImpl( tdoc );
1963   Node* nimp = tidyNodeToImpl( tnod );
1964   if ( doc && nimp && outbuf )
1965   {
1966       uint outenc     = cfg( doc, TidyOutCharEncoding );
1967       uint nl         = cfg( doc, TidyNewline );
1968       StreamOut* out  = TY_(BufferOutput)( doc, outbuf, outenc, nl );
1969       Bool xmlOut     = cfgBool( doc, TidyXmlOut );
1970       Bool xhtmlOut   = cfgBool( doc, TidyXhtmlOut );
1971
1972       doc->docOut = out;
1973       if ( xmlOut && !xhtmlOut )
1974           TY_(PPrintXMLTree)( doc, NORMAL, 0, nimp );
1975       else
1976           TY_(PPrintTree)( doc, NORMAL, 0, nimp );
1977
1978       TY_(PFlushLine)( doc, 0 );
1979       doc->docOut = NULL;
1980
1981       TidyDocFree( doc, out );
1982       return yes;
1983   }
1984   return no;
1985 }
1986
1987 Bool TIDY_CALL tidyNodeGetValue( TidyDoc tdoc, TidyNode tnod, TidyBuffer* buf )
1988 {
1989     TidyDocImpl *doc = tidyDocToImpl( tdoc );
1990     Node *node = tidyNodeToImpl( tnod );
1991     if ( doc == NULL || node == NULL || buf == NULL )
1992         return no;
1993
1994     switch( node->type ) {
1995     case TextNode:
1996     case CDATATag:
1997     case CommentTag:
1998     case ProcInsTag:
1999     case SectionTag:
2000     case AspTag:
2001     case JsteTag:
2002     case PhpTag:
2003     {
2004         tidyBufClear( buf );
2005         tidyBufAppend( buf, doc->lexer->lexbuf + node->start,
2006                        node->end - node->start );
2007         break;
2008     }
2009     default:
2010         /* The node doesn't have a value */
2011         return no;
2012     }
2013
2014     return yes;
2015 }
2016
2017 Bool TIDY_CALL tidyNodeIsProp( TidyDoc ARG_UNUSED(tdoc), TidyNode tnod )
2018 {
2019   Node* nimp = tidyNodeToImpl( tnod );
2020   Bool isProprietary = yes;
2021   if ( nimp )
2022   {
2023     switch ( nimp->type )
2024     {
2025     case RootNode:
2026     case DocTypeTag:
2027     case CommentTag:
2028     case XmlDecl:
2029     case ProcInsTag:
2030     case TextNode:
2031     case CDATATag:
2032         isProprietary = no;
2033         break;
2034
2035     case SectionTag:
2036     case AspTag:
2037     case JsteTag:
2038     case PhpTag:
2039         isProprietary = yes;
2040         break;
2041
2042     case StartTag:
2043     case EndTag:
2044     case StartEndTag:
2045         isProprietary = ( nimp->tag
2046                           ? (nimp->tag->versions&VERS_PROPRIETARY)!=0
2047                           : yes );
2048         break;
2049     }
2050   }
2051   return isProprietary;
2052 }
2053
2054 TidyTagId TIDY_CALL tidyNodeGetId(TidyNode tnod)
2055 {
2056     Node* nimp = tidyNodeToImpl(tnod);
2057
2058     TidyTagId tagId = TidyTag_UNKNOWN;
2059     if (nimp && nimp->tag)
2060         tagId = nimp->tag->id;
2061
2062     return tagId;
2063 }
2064
2065
2066 /* Null for non-element nodes and all pure HTML
2067 cmbstr       tidyNodeNsLocal( TidyNode tnod )
2068 {
2069 }
2070 cmbstr       tidyNodeNsPrefix( TidyNode tnod )
2071 {
2072 }
2073 cmbstr       tidyNodeNsUri( TidyNode tnod )
2074 {
2075 }
2076 */
2077
2078 /* Iterate over attribute values */
2079 TidyAttr TIDY_CALL   tidyAttrFirst( TidyNode tnod )
2080 {
2081   Node* nimp = tidyNodeToImpl( tnod );
2082   AttVal* attval = NULL;
2083   if ( nimp )
2084     attval = nimp->attributes;
2085   return tidyImplToAttr( attval );
2086 }
2087 TidyAttr TIDY_CALL    tidyAttrNext( TidyAttr tattr )
2088 {
2089   AttVal* attval = tidyAttrToImpl( tattr );
2090   AttVal* nxtval = NULL;
2091   if ( attval )
2092     nxtval = attval->next;
2093   return tidyImplToAttr( nxtval );
2094 }
2095
2096 ctmbstr TIDY_CALL       tidyAttrName( TidyAttr tattr )
2097 {
2098   AttVal* attval = tidyAttrToImpl( tattr );
2099   ctmbstr anam = NULL;
2100   if ( attval )
2101     anam = attval->attribute;
2102   return anam;
2103 }
2104 ctmbstr TIDY_CALL       tidyAttrValue( TidyAttr tattr )
2105 {
2106   AttVal* attval = tidyAttrToImpl( tattr );
2107   ctmbstr aval = NULL;
2108   if ( attval )
2109     aval = attval->value;
2110   return aval;
2111 }
2112
2113 /* Null for pure HTML
2114 ctmbstr       tidyAttrNsLocal( TidyAttr tattr )
2115 {
2116 }
2117 ctmbstr       tidyAttrNsPrefix( TidyAttr tattr )
2118 {
2119 }
2120 ctmbstr       tidyAttrNsUri( TidyAttr tattr )
2121 {
2122 }
2123 */
2124
2125 TidyAttrId TIDY_CALL tidyAttrGetId( TidyAttr tattr )
2126 {
2127   AttVal* attval = tidyAttrToImpl( tattr );
2128   TidyAttrId attrId = TidyAttr_UNKNOWN;
2129   if ( attval && attval->dict )
2130     attrId = attval->dict->id;
2131   return attrId;
2132 }
2133 Bool TIDY_CALL tidyAttrIsProp( TidyAttr tattr )
2134 {
2135   /*
2136     You cannot tell whether an attribute is proprietary without
2137     knowing on which element it occurs in the general case, but
2138     this function cannot know the element. As a result, it does
2139     not work anymore. Do not use.
2140   */
2141   return no;
2142 }
2143
2144 /*
2145  * local variables:
2146  * mode: c
2147  * indent-tabs-mode: nil
2148  * c-basic-offset: 4
2149  * eval: (c-set-offset 'substatement-open 0)
2150  * end:
2151  */