// @(#)root/html:$Name:  $:$Id: THtml.cxx,v 1.125 2006/12/05 17:17:37 brun Exp $
// Author: Nenad Buncic (18/10/95), Axel Naumann <mailto:axel@fnal.gov> (09/28/01)

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TBaseClass.h"
#include "TVirtualPad.h"
#include "TClass.h"
#include "TClassTable.h"
#include "TDataMember.h"
#include "TDataType.h"
#include "TGlobal.h"
#include "TDatime.h"
#include "TEnv.h"
#include "TError.h"
#include "THtml.h"
#include "TMethod.h"
#include "TMethodArg.h"
#include "TSystem.h"
#include "TObjString.h"
#include "TInterpreter.h"
#include "TRegexp.h"
#include "Riostream.h"
#include "TPluginManager.h"
#include "TPaveText.h"
#include "TClassEdit.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <list>
#include <vector>
#include <algorithm>
#include <sstream>

THtml *gHtml = 0;

const Int_t kSpaceNum = 1;
const char *formatStr = "%12s %5s %s";

enum ESortType { kCaseInsensitive, kCaseSensitive };
enum EFileType { kSource, kInclude, kTree };
std::set<std::string>  THtml::fgKeywords;

////////////////////////////////////////////////////////////////////////////////
// BEGIN_HTML <!-- 
/* --><p>The THtml class is designed to easily document
classes, code, and code related text files (like change logs). It generates HTML 
pages conforming to the XHTML 1.0 transitional specifications; an example of 
these pages is ROOT's own <a href="http://root.cern.ch/root/html/ClassIndex.html">
reference guide</a>. This page was verified to be valid XHTML 1.0 transitional, 
which proves that all pages generated by THtml can be valid, as long as the user 
provided XHTML (documentation, header, etc) is valid. You can check the current 
THtml by clicking this icon: 
<a href="http://validator.w3.org/check?uri=referer"><img
        src="http://www.w3.org/Icons/valid-xhtml10"
        alt="Valid XHTML 1.0 Transitional" height="31" width="88" style="border: none;"/></a></p>
Overview:
<ol style="list-style-type: upper-roman;"><li><a href="#conf">Configuration</a>
  <ol><li><a href="#conf:input">Input files</a></li>
  <li><a href="#conf:output">Output directory</a></li>
  <li><a href="#conf:liblink">Linking other documentation</a></li>
  <li><a href="#conf:classdoc">Recognizing class documentation</a></li>
  <li><a href="#conf:tags">Author, copyright, etc.</a></li>
  <li><a href="#conf:header">Header and footer</a></li>
  <li><a href="#conf:search">Links to searches, home page, ViewCVS</a></li>
  <li><a href="#conf:charset">HTML Charset</a></li>
  </ol></li>
  <li><a href="#syntax">Documentation syntax</a>
  <ol><li><a href="#syntax:classdesc">Class description</a></li>
  <li><a href="#syntax:classidx">Class index</a></li>
  <li><a href="#syntax:meth">Method documentation</a></li>
  <li><a href="#syntax:datamem">Data member documentation</a></li>
  <li><a href="#syntax:beginhtml"><tt>BEGIN<!-- -->_HTML</tt> <tt>END<!-- -->_HTML</tt>: include 'raw' HTML</a></li>
  </ol></li>
  <li><a href="#CSSJS">Style sheet, JavaScript</a></li>
  <li><a href="#charts">Class Charts</a></li>
  <li><a href="#usage">Usage</a></li>
  <li><a href="#confvar">Configuration variables</a></li>
</ol>

<h3><a name="conf">I. Configuration</a></h3>

<h4><a name="conf:input">I.1 Input files</a></h4>

<p>In your .rootrc, define Root.Html.SourceDir to point to directories containing 
.cxx and .h files (see: <a href="http://root.cern.ch/root/html/TEnv.html">TEnv</a>) 
of the classes you want to document.</p>

<p>Example:</p><pre>
  Root.Html.SourceDir:  .:src:include
  Root.Html.Root:       http://root.cern.ch/root/html</pre>


<h4><a name="conf:output">I.2 Output directory</a></h4>

<p>The output directory can be specified using the Root.Html.OutputDir
configuration variable (default value: "htmldoc"). If that directory 
doesn't exist <a href="http://root.cern.ch/root/html/THtml.html">THtml</a>
will create it.</p>

<p>Example:</p><pre>
  Root.Html.OutputDir:         htmldoc</pre>

<h4><a name="conf:liblink">I.3 Linking other documentation</a></h4>

<p>When trying to document a class, THtml searches for a source file in 
the directories set via SetSourceDir(). If it cannot find it, it assumes
that this class must have been documented before. Based on the library
this class is defined in, it checks the configuration variable
<tt>Root.Html.LibName</tt>, and creates a link using its value.</p>

<p>Example:<br/>
If a class MyClass is defined in class mylibs/libMyLib.so, and .rootrc
contains</p><pre>
  Root.Html.MyLib: ../mylib/</pre>
<p>THtml will create a link to "../mylib/MyClass.html".</p>

<p>The library name association can be set up using the rootmap facility.
For the library in the example above, which contains a dictionary 
generated from the linkdef MyLinkdef.h, the command to generate the
rootmap file is</p>
<pre>  $ rlibmap -f -r rootmap -l mylib/libMyLib.so -d libCore.so -c MyLinkdef.h</pre>
<p>Here, <tt>-r</tt> specifies that the entries for libMyLib should be updated,
<tt>-l</tt> specifies the library we're dealing with, <tt>-d</tt> its 
dependencies, and <tt>-c</tt> its linkdef. The rootmap file must be within
one of the <tt>LD_LIBRARY_PATH</tt> (or <tt>PATH</tt> for Windows) directories
when ROOT is started, otherwise ROOT will not use it.</p>

<h4><a name="conf:classdoc">I.4 Recognizing class documentation</a></h4>

<p>The class documentation has to appear in the header file containing the
class, right in front of its declaration. It is introduced by a string
defined by Root.Html.Description. See the section on 
<a href="#syntax">documentation syntax</a> for further details.</p>

<p>Example:</p><pre>
  Root.Html.Description:       //____________________</pre>


<h4><a name="conf:tags">I.5 Author, copyright, etc.</a></h4>

<p>During the conversion, 
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will look for 
some strings ("tags") in the source file, which have to appear right in
front of e.g. the author's name, copyright notice, etc. These tags can be
defined with the following environment variables: Root.Html.Author,
Root.Html.LastUpdate and Root.Html.Copyright.</p>

<p>If the LastUpdate tag is not found, the current date and time are used.
This is useful when using
<a href="http://root.cern.ch/root/html/THtml.html#THtml:MakeAll">THtml::MakeAll()</a>'s 
default option force=kFALSE, in which case 
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> generates 
documentation only for changed classes.</p>

Authors can be a comma separated list of author entries. Each entry has
one of the following two formats
<ul><li><tt>Name (non-alpha)</tt>.
<p><a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will generate an 
HTML link for <tt>Name</tt>, taking the Root.Html.XWho configuration
variable (defaults to "http://consult.cern.ch/xwho/people?") and adding 
all parts of the name with spaces replaces by '+'. Non-alphanumerical 
characters are printed out behind <tt>Name</tt>.</p>

<p>Example:</p>
<tt>// Author: Enrico Fermi</tt> appears in the source file.
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will generate the link
<tt>http://consult.cern.ch/xwho/people?Enrico+Fermi</tt>. This works well for
people at CERN.</li>

<li><tt>Name &lt;link&gt; Info</tt>.
<p><a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will generate 
an HTML link for <tt>Name</tt> as specified by <tt>link</tt> and print 
<tt>Info</tt> behind <tt>Name</tt>.</p>

<p>Example:</p>
<tt>// Author: Enrico Fermi &lt;http://www.enricos-home.it&gt;</tt> or<br/>
<tt>// Author: Enrico Fermi &lt;mailto:enrico@fnal.gov&gt;</tt> in the
source file. That's world compatible.</li>
</ul>

<p>Example (with defaults given):</p><pre>
      Root.Html.Author:     // Author:
      Root.Html.LastUpdate: // @(#)
      Root.Html.Copyright:  * Copyright
      Root.Html.XWho:       http://consult.cern.ch/xwho/people?</pre>


<h4><a name="conf:header">I.6 Header and footer</a></h4>

<p><a href="http://root.cern.ch/root/html/THtml.html">THtml</a> generates 
a default header and footer for all pages. You can
specify your own versions with the configuration variables Root.Html.Header
and Root.Html.Footer. Both variables default to "", using the standard Root
versions. If it has a "+" appended, <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will
write both versions (user and root) to a file, for the header in the order
1st root, 2nd user, and for the footer 1st user, 2nd root (the root
versions containing "&lt;html&gt;" and &lt;/html&gt; tags, resp).</p>

<p>If you want to replace root's header you have to write a file containing
all HTML elements necessary starting with the &lt;doctype&gt; tag and ending with
(and including) the &lt;body&gt; tag. If you add your header it will be added
directly after Root's &lt;body&gt; tag. Any occurrence of the string <tt>%TITLE%</tt>
in the user's header file will be replaced by
a sensible, automatically generated title. If the header is generated for a
class, occurrences of <tt>%CLASS%</tt> will be replaced by the current class's name,
<tt>%SRCFILE%</tt> and <tt>%INCFILE%</tt> by the name of the source and header file, resp.
(as given by <a href="http://root.cern.ch/root/html/TClass.html#TClass:GetImplFileLine">TClass::GetImplFileName()</a>,
<a href="http://root.cern.ch/root/html/TClass.html#TClass:GetImplFileLine">TClass::GetDeclFileName()</a>).
If the header is not generated for a class, they will be replaced by "".</p>

<p>Root's footer starts with the tag &lt;!--SIGNATURE--&gt;. It includes the
author(s), last update, copyright, the links to the Root home page, to the
user home page, to the index file (ClassIndex.html), to the top of the page
and <tt>this page is automatically generated</tt> infomation. It ends with the
tags <tt>&lt;/body&gt;&lt;/html&gt;</tt>. If you want to replace it, 
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will search for some
tags in your footer: Occurrences of the strings <tt>%AUTHOR%</tt>, <tt>%UPDATE%</tt>, and
<tt>%COPYRIGHT%</tt> are replaced by their
corresponding values before writing the html file. The <tt>%AUTHOR%</tt> tag will be
replaced by the exact string that follows Root.Html.Author, no link
generation will occur.</p>


<h4><a name="conf:search">I.7 Links to searches, home page, ViewCVS</a></h4>

<p>Additional parameters can be set by Root.Html.Homepage (address of the
user's home page), Root.Html.SearchEngine (search engine for the class
documentation), Root.Html.Search (search URL), and a ViewCVS base URL 
Root.Html.ViewCVS. All default to "".</p>

<p>Examples:</p><pre>
      Root.Html.Homepage:     http://www.enricos-home.it
      Root.Html.SearchEngine: http://root.cern.ch/root/Search.phtml
      Root.Html.Search:       http://www.google.com/search?q=%s+site%3Aroot.cern.ch%2Froot%2Fhtml</pre>


<h4><a name="conf:charset">I.8 HTML Charset</a></h4>

<p>XHTML 1.0 transitional recommends the specification of the charset in the
content type meta tag, see e.g. <a href="http://www.w3.org/TR/2002/REC-xhtml1-20020801/">http://www.w3.org/TR/2002/REC-xhtml1-20020801/</a>
<a href="http://root.cern.ch/root/html/THtml.html">THtml</a> generates it for the HTML output files. It defaults to ISO-8859-1, and
can be changed using Root.Html.Charset.</p>

<p>Example:</p><pre>
      Root.Html.Charset:      EUC-JP</pre>

<h3><a name="syntax">II. Documentation syntax</a></h3>
<h4><a name="syntax:classdesc">II.1 Class description</a></h4>

<p>A class description block, which must be placed before the first
member function, has a following form:</p>
<pre>
////////////////////////////////////////////////////////////////
//                                                            //
// TMyClass                                                   //
//                                                            //
// This is the description block.                             //
//                                                            //
////////////////////////////////////////////////////////////////
</pre>
<p>The environment variable Root.Html.Description 
(see: <a href="http://root.cern.ch/root/html/TEnv.html">TEnv</a>) contains
the delimiter string (default value: <tt>//_________________</tt>). It means
that you can also write your class description block like this:</p>
<pre>
   //_____________________________________________________________
   // A description of the class starts with the line above, and
   // will take place here !
   //
</pre>
<p>Note that <b><i>everything</i></b> until the first non-commented line is considered
as a valid class description block.</p>

<h4><a name="syntax:classidx">II.2 Class index</a></h4>

<p>All classes to be documented will have an entry in the ClassIndex.html,
showing their name with a link to their documentation page and a miniature
description. This discription for e.g. the class MyClass has to be given
in MyClass's header as a comment right after ClassDef(MyClass, n).</p>

<h4><a name="syntax:meth">II.3 Method documentation</a></h4>
<p>A member function description block starts immediately after '{'
and looks like this:</p>
<pre>
   void TWorld::HelloWorldFunc(string *text)
   {
      // This is an example of description for the
      // TWorld member function

      helloWorld.Print( text );
   }
</pre>
Like in a class description block, <b><i>everything</i></b> until the first
non-commented line is considered as a valid member function
description block.

<h4><a name="syntax:datamem">II.4 Data member documentation</a></h4>

<p>Data members are documented by putting a C++ comment behind their 
declaration in the header file, e.g.</p>
<pre>
   int fIAmADataMember; // this is a data member
</pre>

<h4><a name="syntax:beginhtml">II.5 <tt>BEGIN<!-- -->_HTML</tt> <tt>END<!-- -->_HTML</tt>: include 'raw' HTML</a></h4>

<p>You can insert pure html code into your documentation comments. During the
generation of the documentation, this code will be inserted as is
into the html file.</p>
<p>Pure html code must be surrounded by the keywords 
<tt>BEGIN<!-- -->_HTML</tt> and <tt>END<!-- -->_HTML</tt>, where the
case is ignored.
Examples of pure html code are given e.g. in 
<a href="http://root.cern.ch/root/html/TDataMember.html">TDataMember</a>
and <a href="http://root.cern.ch/root/html/TMinuit.html">TMinuit</a>. The
following trick can be used to hide
the surrounding <tt>/<!-- -->*</tt> <tt>*<!-- -->/</tt> from the documentation:</p>
<pre>
   // start doc
   // BEGIN<!-- -->_HTML &lt;!--
   /<!-- -->* --&gt;
   We can now write HTML code, like &lt;a href="something"&gt;something&lt;/a&gt;
   &lt;!-- *<!-- -->/
   // --&gt; END<!-- -->_HTML
   // end doc
</pre>

<h3><a name="CSSJS">III. Style sheet, JavaScript</a></h3>

<p>The documentation pages share a common set of javascript and CSS files. They
are generated automatically when running <a href="#THtml:MakeAll">MakeAll()</a>; 
they can be generated on 
demand by calling <a href="#THtml:CreateJavascript">CreateJavascript()</a>
and <a href="#THtml:CreateStyleSheet">CreateStyleSheet()</a>, respectively.</p>


<h3><a name="charts">IV. Class Charts</a></h3>
THtml can generate a number of graphical representations for a class, which
are displayed as a tabbed set of imaged ontop of the class description.
It can show the inheritance, inherited and hidden members, directly and 
indirectly included files, and library dependencies.

These graphs are generated using the <a href="http://www.graphviz.org/">Graphviz</a>
package. You can install it from <a href="http://www.graphviz.org">http://www.graphviz.org</a>.
You can either put it into your $PATH, or tell THtml where to find it by calling
<a href="#THtml:SetDotDir">SetDotDir()</a>.


<h3><a name="usage">V. Usage</a></h3>
These are typical things people do with THtml:
<pre>
    root[] <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> html;                // create a <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> object
    root[] html.MakeAll()             // generate documentation for all changed classes
    root[] html.MakeClass("TMyClass") // create documentation for TMyClass only
</pre>
To "beautify" (i.e. create links to documentation for class names etc) some text 
file or macro, use:
<pre>
    root[] html.Convert( "hsimple.C", "Histogram example" )
</pre>


<h3><a name="confvar">VI. Configuration variables</a></h3>

<p>Here is a list of all configuration variables that are known to THtml.
You can set them in your .rootrc file, see 
<a href="http://root.cern.ch/root/html/TEnv.html">TEnv</a>.</p>

<pre>
  Root.Html.OutputDir    (default: htmldoc)
  Root.Html.SourceDir    (default: .:src/:include/)
  Root.Html.Author       (default: // Author:) - start tag for authors
  Root.Html.LastUpdate   (default: // @(#)) - start tag for last update
  Root.Html.Copyright    (default:  * Copyright) - start tag for copyright notice
  Root.Html.Description  (default: //____________________ ) - start tag for class descr
  Root.Html.HomePage     (default: ) - URL to the user defined home page
  Root.Html.Header       (default: ) - location of user defined header
  Root.Html.Footer       (default: ) - location of user defined footer
  Root.Html.Root         (default: ) - URL of Root's class documentation
  Root.Html.SearchEngine (default: ) - link to the search engine
  Root.Html.Search       (defualt: ) - link to search by replacing "%s" with user input
  Root.Html.ViewCVS      (default: ) - URL of ViewCVS base
  Root.Html.XWho         (default: http://consult.cern.ch/xwho/people?) - URL of CERN's xWho
  Root.Html.Charset      (default: ISO-8859-1) - HTML character set
</pre><!--
*/ 
// --> END_HTML
////////////////////////////////////////////////////////////////////////////////

ClassImp(THtml)
//______________________________________________________________________________
THtml::THtml(): fFoundDot(-1), fCurrentClass(0), fDocContext(kIgnore), 
                fEscFlag(kFALSE), fHierarchyLines(0)
{
   // Create a THtml object.
   // In case output directory does not exist an error
   // will be printed and gHtml stays 0 also zombie bit will be set.

   SetEscape();

   // get prefix for source directory
   fSourcePrefix = gEnv->GetValue("Root.Html.SourcePrefix", "");

   // check for source directory
   fSourceDir = gEnv->GetValue("Root.Html.SourceDir", "./:src/:include/");

   // check for output directory
   fOutputDir = gEnv->GetValue("Root.Html.OutputDir", "htmldoc");

   fXwho =
       gEnv->GetValue("Root.Html.XWho",
                      "http://consult.cern.ch/xwho/people?");

   fClasses.SetOwner();
   fModules.SetOwner();
   Int_t st;
   Long64_t sSize;
   Long_t sId, sFlags, sModtime;
   if ((st =
        gSystem->GetPathInfo(fOutputDir, &sId, &sSize, &sFlags, &sModtime))
       || !(sFlags & 2)) {
      if (st == 0) {
         Error("THtml", "output directory %s is an existing file",
               fOutputDir.Data());
         MakeZombie();
         return;
      }
      // Try creating directory
      if (gSystem->MakeDirectory(fOutputDir) == -1) {
         Error("THtml", "output directory %s does not exist", fOutputDir.Data());
         MakeZombie();
         return;
      }
   }
   // insert html object in the list of special ROOT objects
   if (!gHtml) {
      gHtml = this;
      gROOT->GetListOfSpecials()->Add(gHtml);
   }

   if (fgKeywords.empty()) {
      fgKeywords.insert("asm");
      fgKeywords.insert("auto");
      fgKeywords.insert("bool");
      fgKeywords.insert("break");
      fgKeywords.insert("case");
      fgKeywords.insert("catch");
      fgKeywords.insert("char");
      fgKeywords.insert("class");
      fgKeywords.insert("const");
      fgKeywords.insert("const_cast");
      fgKeywords.insert("continue");
      fgKeywords.insert("default");
      fgKeywords.insert("delete");
      fgKeywords.insert("do");
      fgKeywords.insert("double");
      fgKeywords.insert("dynamic_cast");
      fgKeywords.insert("else");
      fgKeywords.insert("enum");
      fgKeywords.insert("explicit");
      fgKeywords.insert("export");
      fgKeywords.insert("extern");
      fgKeywords.insert("false");
      fgKeywords.insert("float");
      fgKeywords.insert("for");
      fgKeywords.insert("friend");
      fgKeywords.insert("goto");
      fgKeywords.insert("if");
      fgKeywords.insert("inline");
      fgKeywords.insert("int");
      fgKeywords.insert("long");
      fgKeywords.insert("mutable");
      fgKeywords.insert("namespace");
      fgKeywords.insert("new");
      fgKeywords.insert("operator");
      fgKeywords.insert("private");
      fgKeywords.insert("protected");
      fgKeywords.insert("public");
      fgKeywords.insert("register");
      fgKeywords.insert("reinterpret_cast");
      fgKeywords.insert("return");
      fgKeywords.insert("short");
      fgKeywords.insert("signed");
      fgKeywords.insert("sizeof");
      fgKeywords.insert("static");
      fgKeywords.insert("static_cast");
      fgKeywords.insert("struct");
      fgKeywords.insert("switch");
      fgKeywords.insert("template");
      fgKeywords.insert("this");
      fgKeywords.insert("throw");
      fgKeywords.insert("true");
      fgKeywords.insert("try");
      fgKeywords.insert("typedef");
      fgKeywords.insert("typeid");
      fgKeywords.insert("typename");
      fgKeywords.insert("union");
      fgKeywords.insert("unsigned");
      fgKeywords.insert("using");
      fgKeywords.insert("virtual");
      fgKeywords.insert("void");
      fgKeywords.insert("volatile");
      fgKeywords.insert("wchar_t");
      fgKeywords.insert("while");
   }
}


//______________________________________________________________________________
THtml::~THtml()
{
// Default destructor

   fClasses.Clear();
   fModules.Clear();
   if (gHtml == this) {
      gROOT->GetListOfSpecials()->Remove(gHtml);
      gHtml = 0;
   }
}

//______________________________________________________________________________
bool IsNamespace(TClass*cl)
{
   // Check whether cl is a namespace
   return (cl->Property() & kIsNamespace);
}

//______________________________________________________________________________
int CaseSensitiveSort(const void *name1, const void *name2)
{
// Friend function for sorting strings, case sensitive
//
//
// Input: name1 - pointer to the first string
//        name2 - pointer to the second string
//
//  NOTE: This function compares its arguments and returns an integer less
//        than, equal to, or greater than zero, depending on whether name1
//        is lexicographically less than, equal to, or greater than name2.
//
//

   return (strcmp(*((char **) name1), *((char **) name2)));
}


//______________________________________________________________________________
int CaseInsensitiveSort(const void *name1, const void *name2)
{
// Friend function for sorting strings, case insensitive
//
//
// Input: name1 - pointer to the first string
//        name2 - pointer to the second string
//
//  NOTE: This function compares its arguments and returns an integer less
//        than, equal to, or greater than zero, depending on whether name1
//        is lexicographically less than, equal to, or greater than name2,
//        but characters are forced to lower-case prior to comparison.
//
//

   return (strcasecmp(*((char **) name1), *((char **) name2)));
}

namespace {
   typedef std::vector<std::string> Words_t;
   typedef Words_t::const_iterator SectionStart_t;
   class TSectionInfo {
   public:
      TSectionInfo(SectionStart_t start, size_t chars, size_t size):
         fStart(start), fChars(chars), fSize(size) {};

         SectionStart_t fStart;
         size_t fChars;
         size_t fSize;
   };
   typedef std::list<TSectionInfo> SectionStarts_t;

   void Sections_BuildIndex(SectionStarts_t& sectionStarts,
      SectionStart_t begin, SectionStart_t end,
      size_t maxPerSection)
   {
      // for each assumed section border, check that previous entry's
      // char[selectionChar] differs, else move section start forward

      SectionStart_t cursor = begin;
      if (sectionStarts.empty() || sectionStarts.back().fStart != cursor)
         sectionStarts.push_back(TSectionInfo(cursor, 1, 0));

      SectionStarts_t::iterator prevSection = sectionStarts.end();
      --prevSection;

      while (cursor != end) {
         size_t numLeft = end - cursor;
         size_t assumedNumSections = (numLeft + maxPerSection - 1 ) / maxPerSection;
         size_t step = ((numLeft + assumedNumSections - 1) / assumedNumSections);
         if (!step || step >= numLeft) return;
         cursor += step;
         if (cursor == end) break;

         SectionStart_t addWhichOne = prevSection->fStart;

         size_t selectionChar=1;
         for (; selectionChar <= cursor->length() && addWhichOne == prevSection->fStart;
            ++selectionChar) {
            SectionStart_t checkPrev = cursor;
            while (--checkPrev != prevSection->fStart
               && !strncasecmp(checkPrev->c_str(), cursor->c_str(), selectionChar));

            SectionStart_t checkNext = cursor;
            while (++checkNext != end
               && !strncasecmp(checkNext->c_str(), cursor->c_str(), selectionChar));

            // if the previous matching one is closer but not previous section start, take it!
            if (checkPrev != prevSection->fStart)
               if ((cursor - checkPrev) <= (checkNext - cursor))
                  addWhichOne = ++checkPrev;
               else if (checkNext != end
                  && (size_t)(checkNext - cursor) < maxPerSection) {
                  addWhichOne = checkNext;
               }
         }
         if (addWhichOne == prevSection->fStart)
            addWhichOne = cursor;

         selectionChar = 1;
         while (selectionChar <= prevSection->fStart->length()
            && selectionChar <= addWhichOne->length()
            && !strncasecmp(prevSection->fStart->c_str(), addWhichOne->c_str(), selectionChar))
            ++selectionChar;

         sectionStarts.push_back(TSectionInfo(addWhichOne, selectionChar, 0));
         cursor = addWhichOne;
         ++prevSection;
      } // while cursor != end
   }

   void Sections_SetSize(SectionStarts_t& sectionStarts, const Words_t &words)
   {
      // Update the length of the sections
      for (SectionStarts_t::iterator iSectionStart = sectionStarts.begin();
         iSectionStart != sectionStarts.end(); ++iSectionStart) {
         SectionStarts_t::iterator next = iSectionStart;
         ++next;
         if (next == sectionStarts.end()) {
            iSectionStart->fSize = (words.end() - iSectionStart->fStart);
            break;
         }
         iSectionStart->fSize = (next->fStart - iSectionStart->fStart);
      }
   }

   void Sections_PostMerge(SectionStarts_t& sectionStarts, const size_t maxPerSection)
   {
      // Merge sections that ended up being too small, up to maxPerSection entries
      for (SectionStarts_t::iterator iSectionStart = sectionStarts.begin();
         iSectionStart != sectionStarts.end();) {
         SectionStarts_t::iterator iNextSectionStart = iSectionStart;
         ++iNextSectionStart;
         if (iNextSectionStart == sectionStarts.end()) break;
         if (iNextSectionStart->fSize + iSectionStart->fSize < maxPerSection) {
            iSectionStart->fSize += iNextSectionStart->fSize;
            sectionStarts.erase(iNextSectionStart);
         } else ++iSectionStart;
      }
   }

   void GetIndexChars(const Words_t& words, UInt_t numSectionsIn,
      std::vector<std::string> &sectionMarkersOut)
   {
      // Given a list of words (class names, in this case), this function builds an
      // optimal set of about numSectionIn sections (even if almost all words start 
      // with a "T"...), and returns the significant characters for each section start 
      // in sectionMarkersOut.

      const size_t maxPerSection = (words.size() + numSectionsIn - 1)/ numSectionsIn;
      SectionStarts_t sectionStarts;
      Sections_BuildIndex(sectionStarts, words.begin(), words.end(), maxPerSection);
      Sections_SetSize(sectionStarts, words);
      Sections_PostMerge(sectionStarts, maxPerSection);

      // convert to index markers
      sectionMarkersOut.clear();
      sectionMarkersOut.resize(sectionStarts.size());
      size_t idx = 0;
      for (SectionStarts_t::iterator iSectionStart = sectionStarts.begin();
         iSectionStart != sectionStarts.end(); ++iSectionStart)
         sectionMarkersOut[idx++] =
            iSectionStart->fStart->substr(0, iSectionStart->fChars);
   }

   void GetIndexChars(const char** wordsIn, UInt_t numWordsIn, UInt_t numSectionsIn,
      std::vector<std::string> &sectionMarkersOut)
   {
      // initialize word vector
      Words_t words(numWordsIn);
      for (UInt_t iWord = 0; iWord < numWordsIn; ++iWord)
         words[iWord] = wordsIn[iWord];
      GetIndexChars(words, numSectionsIn, sectionMarkersOut);
   }

   void GetIndexChars(const std::list<std::string>& wordsIn, UInt_t numSectionsIn,
      std::vector<std::string> &sectionMarkersOut)
   {
      // initialize word vector
      Words_t words(wordsIn.size());
      size_t idx = 0;
      for (std::list<std::string>::const_iterator iWord = wordsIn.begin(); iWord != wordsIn.end(); ++iWord)
         words[idx++] = *iWord;
      GetIndexChars(words, numSectionsIn, sectionMarkersOut);
   }

   // std::list::sort(with_stricmp_predicate) doesn't work with Solaris CC...
   void sort_strlist_stricmp(std::list<std::string>& l)
   {
      // sort strings ignoring case - easier for humans
      struct posList {
         const char* str;
         std::list<std::string>::const_iterator pos;
      };
      posList* carr = new posList[l.size()];
      size_t idx = 0;
      for (std::list<std::string>::const_iterator iS = l.begin(); iS != l.end(); ++iS) {
         carr[idx].pos = iS;
         carr[idx++].str = iS->c_str();
      }
      qsort(&carr[0].str, idx, sizeof(posList), CaseInsensitiveSort);
      std::list<std::string> lsort;
      for (idx = 0; idx < l.size(); ++idx) {
         lsort.push_back(*carr[idx].pos);
      }
      delete [] carr;
      l.swap(lsort);
   }

   class TMethodWrapper: public TObject {
   public:
      TMethodWrapper(const TMethod* m): fMeth(m) {}

      static void SetClass(const TClass* cl) { fgClass = cl; }

      const char* GetName() const { return fMeth->GetName(); }
      Int_t GetNargs() const { return fMeth->GetNargs(); }
      const TMethod* GetMethod() const { return fMeth; }
      Bool_t IsSortable() const { return kTRUE; }

      Int_t Compare(const TObject *obj) const {
         const TMethodWrapper* m = dynamic_cast<const TMethodWrapper*>(obj);
         if (!m) return 1;

         Int_t ret = strcasecmp(GetName(), m->GetName());
         if (ret == 0) {
            if (GetNargs() < m->GetNargs()) return -1;
            else if (GetNargs() > m->GetNargs()) return 1;
            if (GetMethod()->GetClass()->InheritsFrom(m->GetMethod()->GetClass()))
               return -1;
            else
               return 1;
         }

         const char* l(GetName());
         const char* r(m->GetName());
         if (l[0] == '~' && r[0] == '~') {
            ++l;
            ++r;
         }
         if (fgClass->InheritsFrom(l)) {
            if (fgClass->InheritsFrom(r)) {
               if (gROOT->GetClass(l)->InheritsFrom(r))
                  return -1;
               else return 1;
            } else return -1;
         } else if (fgClass->InheritsFrom(r))
            return 1;

         if (l[0] == '~') return -1;
         if (r[0] == '~') return 1;
         return (ret < 0) ? -1 : 1;
      }

   private:
      static const TClass* fgClass; // current class, defining inheritance sort order
      const TMethod* fMeth; // my method
   };
   const TClass* TMethodWrapper::fgClass = 0;

   class TModuleDocInfo;
   //____________________________________________________________________
   //
   // Cache doc info for all known classes
   //
   class TClassDocInfo: public TObject {
   public:
      // initialize the object
      TClassDocInfo(TClass* cl, const char* filename):
         fClass(cl), fModule(0), fHtmlFileName(filename), fSelected(kTRUE) { }
      virtual ~TClassDocInfo() {}

      TClass* GetClass() const { return fClass; }
      virtual const char* GetName() const { return fClass ? fClass->GetName() : "(UNKNOWN)"; }
      const char* GetHtmlFileName() const { return fHtmlFileName; }

      void SetModule(TModuleDocInfo* module) { fModule = module; }
      TModuleDocInfo* GetModule() const { return fModule; }

      void SetSelected(Bool_t sel = kTRUE) { fSelected = sel; }
      Bool_t IsSelected() const { return fSelected; }

      ULong_t Hash() const { return fClass ? fClass->Hash() : (ULong_t)-1; }

      Bool_t IsSortable() const { return kTRUE; }
      Int_t Compare(const TObject* obj) const { return fClass ? fClass->Compare(obj) : obj < this; }

   private:
      TClassDocInfo();
      TClass* fClass; // class represented by this info object
      TModuleDocInfo* fModule; // module this class is in
      TString fHtmlFileName; // name of the HTML doc file
      Bool_t fSelected; // selected fro doc output
   };

   //____________________________________________________________________
   //
   // Cache doc info for all known modules
   //
   class TModuleDocInfo: public TNamed {
   public:
      TModuleDocInfo(const char* name, const char* doc = ""):
         TNamed(name, doc) {}
      virtual ~TModuleDocInfo() {}

      void SetDoc(const char* doc) { SetTitle(doc); }
      const char* GetDoc() const { return GetTitle(); }

      void AddClass(TClassDocInfo* cl) { fClasses.Add(cl); }
      TList* GetClasses() { return &fClasses; }

      std::ostringstream& Stream() { return fStream; }

   private:
      TList   fClasses;
      std::ostringstream fStream;
   };

   // map of lib name to map of module names contained in lib, and their library dependencies
   class MapModuleDepMap: public std::map<std::string, std::set<std::string> > {
   public:
      MapModuleDepMap() {}
   };
   typedef std::map<std::string, MapModuleDepMap > LibDep_t;
   LibDep_t setLibDeps;
}


//______________________________________________________________________________
void THtml::AddClassMethodsRecursive(TBaseClass* bc, TList methodNames[3])
{
   // Add accessible (i.e. non-private) methods of base class bc 
   // and its base classes' methods to methodNames.
   // If bc==0, we add fCurrentClass's methods (and also private functions).

   // make a loop on member functions
   TClass *cl = fCurrentClass;
   if (bc)
      cl = bc->GetClassPointer(kFALSE);
   if (!cl) return;

   TMethod *method;
   TIter nextMethod(cl->GetListOfMethods());

   while ((method = (TMethod *) nextMethod())) {

      if (!strcmp(method->GetName(), "Dictionary") ||
          !strcmp(method->GetName(), "Class_Version") ||
          !strcmp(method->GetName(), "Class_Name") ||
          !strcmp(method->GetName(), "DeclFileName") ||
          !strcmp(method->GetName(), "DeclFileLine") ||
          !strcmp(method->GetName(), "ImplFileName") ||
          !strcmp(method->GetName(), "ImplFileLine") ||
          bc && (method->GetName()[0] == '~' // d'tor
             || !strcmp(method->GetName(), method->GetReturnTypeName())) // c'tor
          )
         continue;


      Int_t mtype = 0;
      if (kIsPrivate & method->Property())
         mtype = 0;
      else if (kIsProtected & method->Property())
         mtype = 1;
      else if (kIsPublic & method->Property())
         mtype = 2;

      if (bc) {
         if (mtype == 0) continue;
         if (bc->Property() & kIsPrivate)
            mtype = 0;
         else if ((bc->Property() & kIsProtected) && mtype == 2)
            mtype = 1;
      }

      Bool_t hidden = kFALSE;
      for (Int_t access = 0; !hidden && access < 3; ++access) {
         TMethodWrapper* other = (TMethodWrapper*) methodNames[access].FindObject(method->GetName());
         hidden |= (other) && (other->GetMethod()->GetClass() != method->GetClass());
      }
      if (!hidden)
         methodNames[mtype].Add(new TMethodWrapper(method));
   }

   TIter iBase(cl->GetListOfBases());
   TBaseClass* base = 0;
   while ((base = (TBaseClass*)iBase()))
      AddClassMethodsRecursive(base, methodNames);
}

void  THtml::AddClassDataMembersRecursive(TBaseClass* bc, TList datamembers[6]) {
   // Add data members of fCurrentClass and of bc to datamembers, recursively.
   // Real data members are in idx 0..2 (public, protected, private access),
   // enum constants in idx 3..5.

   // make a loop on member functions
   TClass *cl = fCurrentClass;
   if (bc)
      cl = bc->GetClassPointer(kFALSE);
   if (!cl) return;

   TDataMember *dm;
   TIter nextDM(cl->GetListOfDataMembers());

   while ((dm = (TDataMember *) nextDM())) {
      if (!strcmp(dm->GetName(), "fgIsA"))
         continue;
      Int_t mtype = 0;
      if (kIsPrivate & dm->Property())
         mtype = 0;
      else if (kIsProtected & dm->Property())
         mtype = 1;
      else if (kIsPublic & dm->Property())
         mtype = 2;

      if (bc) {
         if (mtype == 0) continue;
         if (bc->Property() & kIsPrivate)
            mtype = 0;
         else if ((bc->Property() & kIsProtected) && mtype == 2)
            mtype = 1;
      }

      const Int_t flagEnumConst = G__BIT_ISENUM | G__BIT_ISCONSTANT | G__BIT_ISSTATIC;
      if ((dm->Property() & flagEnumConst) == flagEnumConst
         && dm->GetDataType() && dm->GetDataType()->GetType() == kInt_t)
         mtype += 3;

      datamembers[mtype].Add(dm);
   }

   TIter iBase(cl->GetListOfBases());
   TBaseClass* base = 0;
   while ((base = (TBaseClass*)iBase()))
      AddClassDataMembersRecursive(base, datamembers);
}

//______________________________________________________________________________
void THtml::Class2Html(Bool_t force)
{
// Create HTML files for a single class.
//

   gROOT->GetListOfGlobals(kTRUE);

   // create a filename
   TString filename(fCurrentClass->GetName());
   NameSpace2FileName(filename);

   gSystem->ExpandPathName(fOutputDir);
   gSystem->PrependPathName(fOutputDir, filename);

   filename += ".html";

   // needed for list of methods,...
   fParseContext.clear();
   fParseContext.push_back(kCode);
   fDocContext = kIgnore;

   if (force || IsModified(fCurrentClass, kSource)) {

      // open class file
      ofstream classFile;
      classFile.open(filename, ios::out);

      if (classFile.good()) {

         Printf(formatStr, "", fCounter.Data(), filename.Data());

         // write a HTML header for the classFile file
         WriteHtmlHeader(classFile, fCurrentClass->GetName(), "", fCurrentClass);

         // show box with lib, include
         // needs to go first to allow title on the left
         const char* lib=fCurrentClass->GetSharedLibs();
         const char* incl=GetDeclFileName(fCurrentClass);
         if (incl) incl=gSystem->BaseName(incl);
         if (lib && strlen(lib)|| incl && strlen(incl)) {
            classFile << "<table class=\"libinfo\"><tr><td>";
            if (lib) {
               char* libDup=StrDup(lib);
               char* libDupSpace=strchr(libDup,' ');
               if (libDupSpace) *libDupSpace=0;
               char* libDupEnd=libDup+strlen(libDup);
               while (libDupEnd!=libDup)
                  if (*(--libDupEnd)=='.') {
                     *libDupEnd=0;
                     break;
                  }
               classFile << "library: "
                         << libDup;
               delete[] libDup;
            }
            if (incl) {
               if (lib)
                  classFile << "<br />";
               classFile << "#include \""
                         << incl << "\"";
            }
            classFile << "</td></tr></table>"
                      << endl;
         }

         // make a link to the description
         classFile << "<!--BEGIN-->" << endl;
         classFile << "<center>" << endl;
         classFile << "<h1>";
         ReplaceSpecialChars(classFile, fCurrentClass->GetName());
         classFile << "</h1>" << endl;
         classFile << "<hr width=\"300\" />" << endl;
         TString currClassNameMangled(fCurrentClass->GetName());
         NameSpace2FileName(currClassNameMangled);
         classFile << "<!--SDL--><em><a href=\"#" << currClassNameMangled;
         if (IsNamespace(fCurrentClass)) {
            classFile << ":description\">namespace description</a>";
         } else {
            classFile << ":description\">class description</a>";
         }

         // make a link to the '.cxx' file
         TString classFileName(fCurrentClass->GetName());
         NameSpace2FileName(classFileName);

         const char* headerFileName = GetDeclFileName(fCurrentClass);
         if (headerFileName && !headerFileName[0])
            headerFileName = 0;
         const char* sourceFileName = GetImplFileName(fCurrentClass);
         if (sourceFileName && !sourceFileName[0])
            sourceFileName = 0;

         if (headerFileName)
            classFile << " - <a href=\"src/" << classFileName
                      << ".h.html\">header file</a>";

         if (sourceFileName)
            classFile << " - <a href=\"src/" << classFileName
                      << ".cxx.html\">source file</a>";

         if (!IsNamespace(fCurrentClass) && !HaveDot()) {
            // make a link to the inheritance tree (postscript)
            classFile << " - <a href=\"" << classFileName << "_Tree.pdf\"";
            classFile << ">inheritance tree (.pdf)</a>";
         }
         const char* viewCVSLink = gEnv->GetValue("Root.Html.ViewCVS","");
         if (viewCVSLink && !viewCVSLink[0])
            viewCVSLink = 0;
         if (viewCVSLink && (headerFileName || sourceFileName)) {
            classFile << "<br />";
            if (headerFileName)
               classFile << "<a href=\"" << viewCVSLink << headerFileName << "\">viewCVS header</a>";
            if (headerFileName && sourceFileName)
               classFile << " - ";
            if (sourceFileName)
               classFile << "<a href=\"" << viewCVSLink << sourceFileName << "\">viewCVS source</a>";
         }

         classFile << "</em>" << endl;
         classFile << "<hr width=\"300\" />" << endl;
         classFile << "</center>" << endl;


         // make a link to the '.h.html' file
         TString headerHtmlFileName = fCurrentClass->GetName();
         NameSpace2FileName(headerHtmlFileName);
         gSystem->PrependPathName("src", headerHtmlFileName);
         headerHtmlFileName += ".h.html";

         classFile << "<h2>class ";
         ReplaceSpecialChars(classFile, fCurrentClass->GetName());

         // copy .h file to the Html output directory
         TString declf(GetDeclFileName(fCurrentClass));
         GetSourceFileName(declf);
         if (declf.Length())
            CopyHtmlFile(declf);

         // make a loop on base classes
         Bool_t first = kTRUE;
         TBaseClass *inheritFrom;
         TIter nextBase(fCurrentClass->GetListOfBases());

         while ((inheritFrom = (TBaseClass *) nextBase())) {
            if (first) {
               classFile << ": ";
               first = kFALSE;
            } else
               classFile << ", ";
            classFile << "public ";

            // get a class
            TClass *classInh =
                GetClass((const char *) inheritFrom->GetName());

            TString htmlFile;
            GetHtmlFileName(classInh, htmlFile);

            if (htmlFile.Length()) {
               classFile << "<a href=\"";

               // make a link to the base class
               classFile << htmlFile;
               classFile << "\">";
               ReplaceSpecialChars(classFile, inheritFrom->GetName());
               classFile << "</a>";
            } else
               ReplaceSpecialChars(classFile, inheritFrom->GetName());
         }

         classFile << "</h2>" << endl;


         // create an html inheritance tree
         if (!IsNamespace(fCurrentClass))
            if (!ClassDotCharts(classFile))
               ClassHtmlTree(classFile, fCurrentClass);

         // loop to get a pointers to method names
         TList methodNames[3];
         AddClassMethodsRecursive(0, methodNames);
         TMethodWrapper::SetClass(fCurrentClass);

         classFile << endl << "<div id=\"functions\">" << endl;
         classFile << "<h3><a name=\"" << fCurrentClass->GetName()
            << ":Function_Members\"></a>Function Members (Methods)</h3>" << endl;

         classFile << "<div id=\"dispopt\">Display options:<br /><form id=\"formdispopt\" action=\"#\">" << endl
            << "<input id=\"dispoptCBInh\" type=\"checkbox\" "
            "onclick=\"javascript:CBChanged(this);\" "
            "title=\"Select to display inherited members\" />Show inherited<br />" << endl
            << "<input id=\"dispoptCBPub\" type=\"checkbox\" checked=\"checked\" "
            "onclick=\"javascript:CBChanged(this);\" "
            "title=\"Select to display protected and private members\" />Show non-public<br />"
            << "</form></div>";

         const char* tab4nbsp="&nbsp;&nbsp;&nbsp;&nbsp;";
         if (fCurrentClass->Property() & kIsAbstract)
            classFile << "&nbsp;<br /><b>"
                      << tab4nbsp << "This is an abstract class, constructors will not be documented.<br />" << endl
                      << tab4nbsp << "Look at the <a href=\""
                      << GetFileName((const char *) GetDeclFileName(fCurrentClass))
                      << "\">header</a> to check for available constructors.</b><br />" << endl;

         for (Int_t access = 2; access >=0 && !IsNamespace(fCurrentClass); --access) {
            if (methodNames[access].GetEntries() == 0)
               continue;
            methodNames[access].SetOwner();
            methodNames[access].Sort();
            classFile << "<div class=\"access\" ";
            const char* accessID [] = {"priv", "prot", "publ"};
            const char* accesstxt[] = {"private", "protected", "public"};

            classFile << "id=\"func" << accessID[access] << "\"><b>"
               << accesstxt[access] << ":</b>" << endl
               << "<table class=\"func\" id=\"tabfunc" << accessID[access] << "\">" << endl;

            TIter iMethWrap(&methodNames[access]);
            TMethodWrapper *methWrap = 0;
            while ((methWrap = (TMethodWrapper*) iMethWrap())) {
               const TMethod* method = methWrap->GetMethod();

               // it's a c'tor - Cint stores the class name as return type
               Bool_t isctor = (!strcmp(method->GetName(), method->GetReturnTypeName()));
               // it's a d'tor - Cint stores "void" as return type
               Bool_t isdtor = (!isctor && method->GetName()[0] == '~');

               classFile << "<tr class=\"func";
               if (method->GetClass() != fCurrentClass)
                  classFile << "inh";
               classFile << "\"><td class=\"funcret\">";
               if (kIsVirtual & method->Property())
                  if (!isdtor)
                     classFile << "virtual ";
                  else
                     classFile << " virtual";

               if (kIsStatic & method->Property())
                  classFile << "static ";

               if (!isctor && !isdtor)
                  ExpandKeywords(classFile, method->GetReturnTypeName());

               TString mangled(method->GetClass()->GetName());
               NameSpace2FileName(mangled);
               classFile << "</td><td class=\"funcname\"><a href=\"";
               if (method->GetClass() != fCurrentClass) {
                  TString htmlFile;
                  GetHtmlFileName(method->GetClass(), htmlFile);
                  classFile << htmlFile;
               }
               classFile << "#" << mangled;
               classFile << ":";
               mangled = method->GetName();
               NameSpace2FileName(mangled);
               classFile << mangled << "\">";
               if (method->GetClass() != fCurrentClass) {
                  classFile << "<span class=\"baseclass\">";
                  ReplaceSpecialChars(classFile, method->GetClass()->GetName());
                  classFile << "::</span>";
               }
               ReplaceSpecialChars(classFile, method->GetName());
               classFile << "</a>";

               ExpandKeywords(classFile, const_cast<TMethod*>(method)->GetSignature());
               classFile << "</td></tr>" << endl;
            }
            classFile << endl << "</table></div>" << endl;
         }

         classFile << "</div>" << endl; // class="functions"

         // make a loop on data members
         TList datamembers[6];
         AddClassDataMembersRecursive(0, datamembers);
         Bool_t haveDataMembers = (datamembers[0].GetEntries() ||
                                   datamembers[1].GetEntries() ||
                                   datamembers[2].GetEntries() ||
                                   datamembers[3].GetEntries() ||
                                   datamembers[4].GetEntries() ||
                                   datamembers[5].GetEntries());
         if (haveDataMembers) {
            classFile << endl << "<div id=\"datamembers\">" << endl;
            classFile << "<h3><a name=\"" << fCurrentClass->GetName()
               << ":Data_Members\"></a>Data Members</h3>" << endl;
         }

         for (Int_t access = 5; access >= 0 && !IsNamespace(fCurrentClass); --access) {
            if (datamembers[access].GetEntries() == 0)
               continue;

            classFile << "<div class=\"access\" ";
            const char* what = "data";
            if (access > 2) what = "enum";
            const char* accessID [] = {"priv", "prot", "publ"};
            const char* accesstxt[] = {"private", "protected", "public"};

            classFile << "id=\"" << what << accessID[access%3] << "\"><b>"
               << accesstxt[access%3] << ":</b>" << endl
               << "<table class=\"data\" id=\"tab" << what << accessID[access%3] << "\">" << endl;

            TIter iDM(&datamembers[access]);
            TDataMember *member = 0;
            TString prevEnumName;
            Bool_t prevIsInh = kTRUE;

            while ((member = (TDataMember*) iDM())) {
               Bool_t haveNewEnum = access > 2 && prevEnumName != member->GetTypeName();
               if (haveNewEnum) {
                  if (prevEnumName.Length()) {
                     classFile << "<tr class=\"data";
                     if (prevIsInh)
                        classFile << "inh";
                     classFile << "\"><td class=\"datatype\">};</td><td></td><td></td></tr>" << endl;
                  }
                  prevEnumName = member->GetTypeName();
               }

               classFile << "<tr class=\"data";
               prevIsInh = (member->GetClass() != fCurrentClass);
               if (prevIsInh)
                  classFile << "inh";
               classFile << "\"><td class=\"datatype\">";
               if (haveNewEnum) {
                  TString enumName(member->GetTypeName());
                  TString myScope(fCurrentClass->GetName());
                  myScope += "::";
                  enumName.ReplaceAll(myScope, "");
                  if (enumName.EndsWith("::"))
                     enumName += "<i>[unnamed]</i>";
                  if (!enumName.BeginsWith("enum "))
                     classFile << "enum ";
                  classFile << enumName;
                  classFile << " { ";
               } else
                  if (access < 3) {
                     if (member->Property() & G__BIT_ISSTATIC)
                        classFile << "static ";
                     ExpandKeywords(classFile, member->GetFullTypeName());
                  }

               TString mangled(member->GetClass()->GetName());
               NameSpace2FileName(mangled);
               classFile << "</td><td class=\"dataname\"><a ";
               if (member->GetClass() != fCurrentClass) {
                  classFile << "href=\"";
                  TString htmlFile;
                  GetHtmlFileName(member->GetClass(), htmlFile);
                  classFile << htmlFile << "#";
               } else
                  classFile << "name=\"";
               classFile << mangled;
               classFile << ":";
               mangled = member->GetName();
               NameSpace2FileName(mangled);
               classFile << mangled << "\">";
               if (member->GetClass() == fCurrentClass)
                  classFile << "</a>";
               if (access < 3 && member->GetClass() != fCurrentClass) {
                  classFile << "<span class=\"baseclass\">";
                  ReplaceSpecialChars(classFile, member->GetClass()->GetName());
                  classFile << "::</span>";
               }
               ReplaceSpecialChars(classFile, member->GetName());

               // Add the dimensions to "array" members
               for (Int_t indx = 0; indx < member->GetArrayDim(); ++indx)
                  if (member->GetMaxIndex(indx) <= 0)
                     break;
                  else
                     classFile << "[" << member->GetMaxIndex(indx) << "]";

               if (member->GetClass() != fCurrentClass)
                  classFile << "</a>";
               classFile << "</td><td class=\"datadesc\">";
               ReplaceSpecialChars(classFile, member->GetTitle());
               classFile << "</td></tr>" << endl;
            } // for members

            if (prevEnumName.Length()) {
               classFile << "<tr class=\"data";
               if (prevIsInh)
                  classFile << "inh";
               classFile << "\"><td class=\"datatype\">};</td><td></td><td></td></tr>" << endl;
            }
            classFile << endl << "</table></div>" << endl;
         } // for access

         if (haveDataMembers)
            classFile << "</div>" << endl; // datamembers

         // process a '.cxx' file
         ClassDescription(classFile);


         // close a file
         classFile.close();

      } else
         Error("Make", "Can't open file '%s' !", filename.Data());
   } else
      Printf(formatStr, "-no change-", fCounter.Data(), filename.Data());
}

//______________________________________________________________________________
void THtml::CreateSourceOutputStream(std::ofstream& out, const char* extension, 
                                     TString& sourceHtmlFileName)
{
   // Open a Class.cxx.html file, where Class is defined by classPtr, and .cxx.html by extension
   // It's created in fOutputDir/src. If successful, the HTML header is written to out.

   gSystem->ExpandPathName(fOutputDir);
   TString sourceHtmlDir("src");
   gSystem->PrependPathName(fOutputDir, sourceHtmlDir);
   // create directory if necessary
   if (gSystem->AccessPathName(sourceHtmlDir))
      gSystem->MakeDirectory(sourceHtmlDir);
   sourceHtmlFileName = fCurrentClass->GetName();
   NameSpace2FileName(sourceHtmlFileName);
   gSystem->PrependPathName(sourceHtmlDir, sourceHtmlFileName);
   sourceHtmlFileName += extension;
   out.open(sourceHtmlFileName);
   if (!out) {
      Warning("LocateMethodsInSource", "Can't open beautified source file '%s' for writing!",
         sourceHtmlFileName.Data());
      sourceHtmlFileName.Remove(0);
      return;
   }

   // write a HTML header
   TString title(fCurrentClass->GetName());
   title += " - source file";
   WriteHtmlHeader(out, title, "../", fCurrentClass);
   out << "<pre class=\"code\">" << std::endl;
}

//______________________________________________________________________________
void THtml::AnchorFromLine(TString& anchor) {
   // Create an anchor from the given line, by hashing it and
   // convertig the hash into a custom base64 string.

   const char base64String[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.";

   // use hash of line instead of e.g. line number.
   // advantages: more stable (lines can move around, we still find them back),
   // no need for keeping a line number context
   UInt_t hash = ::Hash(fLineStripped);
   anchor.Remove(0);
   // force first letter to be [A-Za-z], to be id compatible
   anchor += base64String[hash % 52];
   hash /= 52;
   while (hash) {
      anchor += base64String[hash % 64];
      hash /= 64;
   }
}

//______________________________________________________________________________
void THtml::BeautifyLine(std::ostream &sOut, const char* relpath /*="../"*/)
{
   // Put colors around tags, create links, escape characters.
   // In short: make a nice HTML page out of C++ code, and put it into srcOut.
   // Create an anchor at the beginning of the line, and put its name into
   // anchor, if set.

   enum EBeautifyContext {
      kNothingSpecialMoveOn,
      kCommentC, /* */
      kCommentCXX, //
      kPreProc,
      kDontTouch,
      kNumBeautifyContexts
   };
   EBeautifyContext context = kNothingSpecialMoveOn;


   switch (fParseContext.back()) {
      case kCode:
         context = kNothingSpecialMoveOn;
         if (fLineStripped.Length() && fLineStripped[0] == '#') {
            context = kPreProc;
            sOut << "<span class=\"cpp\">";
            ExpandPpLine(sOut);
            sOut << "</span>" << std::endl;
            context = kNothingSpecialMoveOn;
            return;
         }
         break;
      case kBeginEndHtml:
      case kBeginEndHtmlInCComment:
         context = kDontTouch;
         break;
      case kCComment:
         context = kCommentC;
         break;
      default: ;
   }

   if (context == kDontTouch
      || kNPOS != fLine.Index("End_Html", 0, TString::kIgnoreCase)
      && kNPOS == fLine.Index("\"End_Html", 0, TString::kIgnoreCase)) {
      ReplaceSpecialChars(sOut, fLine);
      sOut << std::endl;
      return;
   }

   TString stripSubExpanded(fLineExpanded);
   Bool_t stripped = Strip(stripSubExpanded);
   TString lineExpandedDotDot(stripSubExpanded);

   // adjust relative path
   TString replWithRelPath("=\"");
   replWithRelPath += relpath;
   lineExpandedDotDot.ReplaceAll("=\"./", replWithRelPath);
   if (stripped)
      sOut << fLineExpanded(0, fLineExpanded.Index(stripSubExpanded));
   for (Int_t i = 0; i < lineExpandedDotDot.Length(); ++i)
      switch (lineExpandedDotDot[i]) {
         case '/':
            if (lineExpandedDotDot.Length() > i + 1)
               if (lineExpandedDotDot[i + 1] == '/') {
                  if (context == kPreProc) {
                     // close preproc span
                     sOut << "</span>";
                     context = kNothingSpecialMoveOn;
                  }
                  sOut << lineExpandedDotDot.Data() + i;
                  i = lineExpandedDotDot.Length();
               } else if (lineExpandedDotDot[i + 1] == '*') {
                  if (context == kPreProc) {
                     // close preproc span
                     sOut << "</span>";
                     context = kNothingSpecialMoveOn;
                  }
                  if (context == kNothingSpecialMoveOn || context == kCommentC) {
                     context = kCommentC;
                     Ssiz_t posEndComment = lineExpandedDotDot.Index("*/", i);
                     if (posEndComment == kNPOS)
                        posEndComment = lineExpandedDotDot.Length();
                     TString comment(lineExpandedDotDot(i, posEndComment - i));
                     sOut << comment;
                     // leave "*/" fot next iteration
                     i = posEndComment - 1;
                  } else
                  sOut << "/";
               } else
                  sOut << "/";
            else
               sOut << "/";
            break;
         case '*':
            if (lineExpandedDotDot.Length() > i + 1 &&
               lineExpandedDotDot[i + 1] == '/' &&
               (context == kCommentC ||
               /*can happen if CPP comment inside C comment: */
               context == kNothingSpecialMoveOn)) {
                  sOut << "*/";
                  context = kNothingSpecialMoveOn;
                  if (fParseContext.size()>1)
                     fParseContext.pop_back();

                  i += 1;
            }
            else sOut << "*";
            break;
         default:
            sOut << lineExpandedDotDot[i];
   }
   sOut << std::endl;
}

//______________________________________________________________________________
TMethod* THtml::LocateMethodInCurrentLine(Ssiz_t &posMethodName, TString& ret, TString& name, TString& params,
                             std::ostream &srcOut, TString &anchor, std::ifstream& sourceFile, Bool_t allowPureVirtual)
{
   // Search for a method starting at posMethodName, and return its return type, 
   // its name, and its arguments. If the end of arguments is not found in the 
   // current line, get a new line from sourceFile, beautify it to srcOut, creating
   // an anchor as necessary. When this function returns, posMethodName points to the
   // end of the function declaration, i.e. right after the arguments' closing bracket.
   // If posMethodName == kNPOS, we look for the first matching method in fMethodNames.

   typedef std::map<std::string /*method name*/, Int_t > MethodCount_t;

   if (posMethodName == kNPOS) {
      name.Remove(0);
      TMethod * meth = 0;
      Ssiz_t posBlock = fLine.Index('{');
      if (posBlock == kNPOS)
         posBlock = fLine.Length();
      for (MethodCount_t::iterator iMethodName = fMethodNames.begin();
         !name.Length() && iMethodName != fMethodNames.end(); ++iMethodName) {
         TString lookFor(iMethodName->first);
         lookFor += "(";
         posMethodName = fLine.Index(lookFor);
         if (posMethodName != kNPOS && posMethodName < posBlock
            && (posMethodName == 0 || !IsWord(fLine[posMethodName - 1]))) {
            meth = LocateMethodInCurrentLine(posMethodName, ret, name, params, srcOut,
               anchor, sourceFile, allowPureVirtual);
            if (name.Length())
               return meth;
         }
      }
      return 0;
   }

   name = fLine(posMethodName, fLine.Length() - posMethodName);

   // extract return type
   ret = fLine(0, posMethodName);
   if (ret.Length()) {
      while (ret.Length() && (IsName(ret[ret.Length() - 1]) || ret[ret.Length()-1] == ':'))
         ret.Remove(ret.Length() - 1, 1);
      Strip(ret);
      Bool_t didSomething = kTRUE;
      while (didSomething) {
         didSomething = kFALSE;
         if (ret.BeginsWith("inline ")) {
            didSomething = kTRUE;
            ret.Remove(0, 7);
         }
         if (ret.BeginsWith("static ")) {
            didSomething = kTRUE;
            ret.Remove(0, 7);
         }
         if (ret.BeginsWith("virtual ")) {
            didSomething = kTRUE;
            ret.Remove(0, 8);
         }
      } // while replacing static, virtual, inline
      Strip(ret);
   }

   // extract parameters
   Ssiz_t posParam = name.First('(');
   if (posParam == kNPOS ||
      // no strange return types, please
      ret.Contains("{") || ret.Contains("}") || ret.Contains("(") || ret.Contains(")")) {
      ret.Remove(0);
      name.Remove(0);
      params.Remove(0);
      return 0;
   }

   if (name.BeginsWith("operator")) {
      // op () (...)
      Ssiz_t checkOpBracketParam = posParam + 1;
      while (isspace(name[checkOpBracketParam]))
         ++checkOpBracketParam;
      if (name[checkOpBracketParam] == ')') {
         ++checkOpBracketParam;
         while (isspace(name[checkOpBracketParam]))
            ++checkOpBracketParam;
         if (name[checkOpBracketParam] == '(')
            posParam = checkOpBracketParam;
      }
   } // check for op () (...)

   if (posParam == kNPOS) {
      ret.Remove(0);
      name.Remove(0);
      params.Remove(0);
      return 0;
   }

   params = name(posParam, name.Length() - posParam);
   name.Remove(posParam);

   MethodCount_t::const_iterator iMethodName = fMethodNames.find(name.Data());
   if (iMethodName == fMethodNames.end() || iMethodName->second <= 0) {
      ret.Remove(0);
      name.Remove(0);
      params.Remove(0);
      return 0;
   }

   // find end of param
   Ssiz_t posParamEnd = 1;
   Int_t bracketLevel = 1;
   while (bracketLevel) {
      const char* paramEnd = strpbrk(params.Data() + posParamEnd, ")(\"'");
      if (!paramEnd) {
         // func with params over multiple lines
         // gotta write out this line before it gets lost
         if (!anchor.Length()) {
            // request an anchor, just in case...
            AnchorFromLine(anchor);
            if (srcOut)
               srcOut << "<a name=\"" << anchor << "\"></a>";
         }
         BeautifyLine(srcOut);

         fLine.ReadLine(sourceFile, kFALSE);
         if (sourceFile.eof()) {
            Error("LocateMethodInCurrentLine",
               "Cannot find end of signature for function %s!",
               name.Data());
            break;
         }

         // replace class names etc
         fLineExpanded = fLine;
         ExpandKeywords(fLineExpanded);
         posParamEnd = params.Length();
         params += fLine;
      } else
         posParamEnd = paramEnd - params.Data();
      switch (params[posParamEnd]) {
         case '(': ++bracketLevel; ++posParamEnd; break;
         case ')': --bracketLevel; ++posParamEnd; break;
         case '"': // skip ")"
            ++posParamEnd;
            while (params.Length() > posParamEnd && params[posParamEnd] != '"') {
               // skip '\"'
               if (params[posParamEnd] == '\\') ++posParamEnd;
               ++posParamEnd;
            }
            if (params.Length() <= posParamEnd) {
               // something is seriously wrong - skip :-/
               ret.Remove(0);
               name.Remove(0);
               params.Remove(0);
               return 0;
            }
            ++posParamEnd; // skip trailing '"'
            break;
         case '\'': // skip ')'
            ++posParamEnd;
            if (params[posParamEnd] == '\\') ++posParamEnd;
            posParamEnd += 2;
            break;
         default:
            ++posParamEnd;
      }
   } // while bracketlevel, i.e. (...(..)...)
   Ssiz_t posBlock     = params.Index('{', posParamEnd);
   Ssiz_t posSemicolon = params.Index(';', posParamEnd);
   Ssiz_t posPureVirt  = params.Index('=', posParamEnd);
   if (posSemicolon != kNPOS)
      if ((posBlock == kNPOS || (posSemicolon < posBlock)) &&
         (posPureVirt == kNPOS || !allowPureVirtual)
         && !allowPureVirtual) // allow any "func();" if pv is allowed
         params.Remove(0);

   if (params.Length())
      params.Remove(posParamEnd);

   if (!params.Length()) {
      ret.Remove(0);
      name.Remove(0);
      return 0;
   }
   // update posMethodName to point behind the method
   posMethodName = posParam + posParamEnd;
   if (fCurrentClass)
      return fCurrentClass->GetMethodAny(name);

   return 0;
}

//______________________________________________________________________________
void THtml::WriteMethod(std::ostream & out, TString& ret, 
                        TString& name, TString& params,
                        const char* filename, TString& anchor,
                        TString& comment, TString& codeOneLiner)
{
   // Write method name with return type ret and parameters param to out.
   // Build a link using file and anchor. Cooment it with comment, and
   // show the code codeOneLiner (set if the func consists of only one line
   // of code, immediately surrounded by "{","}"). Also updates fMethodNames's
   // count of method names.

   ExpandKeywords(ret);
   ExpandKeywords(params);
   out << "<div class=\"funcdoc\"><span class=\"funcname\">";
   out << ret << " <a name=\"";
   TString mangled(fCurrentClass->GetName());
   NameSpace2FileName(mangled);
   out << mangled << ":";
   mangled = name;
   NameSpace2FileName(mangled);
   out << mangled << "\" href=\"src/" << filename;
   if (anchor.Length())
      out << "#" << anchor;
   out << "\">";
   ReplaceSpecialChars(out, name);
   out << "</a>" << params << "</span><br />" << std::endl;

   if (comment.Length())
      out << "<pre>" << comment << "</pre>" << std::endl;

   if (codeOneLiner.Length()) {
      out << std::endl << "<div class=\"inlinecode\"><code class=\"inlinecode\">"
          << codeOneLiner << "</code></div>" << std::endl;
      codeOneLiner.Remove(0);
   }
   out << "</div>" << std::endl;

   typedef std::map<std::string /*method name*/, Int_t > MethodCount_t;
   MethodCount_t::iterator iMethodName = fMethodNames.find(name.Data());
   if (iMethodName != fMethodNames.end()) {
      --(iMethodName->second);
      if (iMethodName->second <= 0)
         fMethodNames.erase(iMethodName);
   }

   ret.Remove(0);
   name.Remove(0);
   params.Remove(0);
   anchor.Remove(0);
   comment.Remove(0);

   fDocContext = kIgnore;
}


//______________________________________________________________________________
Bool_t THtml::ExtractComments(const TString &lineExpandedStripped, 
                              Bool_t &foundClassDescription,
                              const char* classDescrTag,
                              TString& comment) {
// Extracts comments from the current line into comment, 
// updating whether a class description was found (foundClassDescription).
// Returns kTRUE if comment found.

   if (fParseContext.back() != kBeginEndHtml &&
      fParseContext.back() != kBeginEndHtmlInCComment &&
      fParseContext.back() != kCComment &&
      !lineExpandedStripped.BeginsWith("<span class=\"comment\">"))
      return kFALSE;

   if ((fParseContext.back() == kBeginEndHtml
      || fParseContext.back() == kBeginEndHtmlInCComment)
      && (kNPOS == fLine.Index("Begin_Html", 0, TString::kIgnoreCase)
      || kNPOS != fLine.Index("\"Begin_Html", 0, TString::kIgnoreCase))
      && (kNPOS == fLine.Index("End_Html", 0, TString::kIgnoreCase)
      || kNPOS != fLine.Index("\"End_Html", 0, TString::kIgnoreCase))) {
      // no replacement, no classdoc search if in begin/end help block
      comment += fLine + "\n";
      return kTRUE;
   }

   TString commentLine(lineExpandedStripped);
   if (commentLine.BeginsWith("<span class=\"comment\">/")) {
      if (commentLine[23] == '/')
         if (!commentLine.EndsWith("</span>"))
            return kFALSE;
         else
            commentLine.Remove(commentLine.Length()-7, 7);
      commentLine.Remove(0,22);
   }

   // remove repeating characters from the end of the line
   if (!foundClassDescription && commentLine.Length() > 3) {
      TString lineAllOneChar(commentLine);

      Ssiz_t len = lineAllOneChar.Length();
      Char_t c = lineAllOneChar[len - 1];
      // also a class doc signature: line consists of same char
      if (c == lineAllOneChar[len - 2] && c == lineAllOneChar[len - 3]) {
         TString lineAllOneCharStripped(lineAllOneChar.Strip(TString::kTrailing, c));
         if (lineAllOneCharStripped.BeginsWith("//") || lineAllOneCharStripped.BeginsWith("/*"))
            lineAllOneCharStripped.Remove(0, 2);
         Strip(lineAllOneCharStripped);
         if (!lineAllOneCharStripped.Length())
            commentLine.Remove(0);
      }
   }

   // look for start tag of class description
   if (!foundClassDescription && !comment.Length() && fDocContext == kIgnore &&
      (!commentLine.Length() || classDescrTag && lineExpandedStripped.Contains(classDescrTag))) {
      fDocContext = kDocClass;
      foundClassDescription = kTRUE;
   }

   // remove leading and trailing chars if non-word and identical, e.g.
   // * some doc *, or // some doc //
   while (fParseContext.back() != kBeginEndHtml && fParseContext.back() != kBeginEndHtmlInCComment &&
      commentLine.Length() > 2 &&
      commentLine[0] == commentLine[commentLine.Length() - 1] &&
      (commentLine[0] == '/' || commentLine[0] == '*')) {
      commentLine = commentLine.Strip(TString::kBoth, commentLine[0]);
   }

   // remove leading /*, //
   if (commentLine.Length()>1 && commentLine[0] == '/' &&
      (commentLine[1] == '/' || commentLine[1] == '*'))
      commentLine.Remove(0, 2);
   // remove trailing */
   if (commentLine.Length()>1 && commentLine[commentLine.Length() - 2] == '*' &&
      commentLine[commentLine.Length() - 1] == '/')
      commentLine.Remove(commentLine.Length()-2);

   comment += commentLine + "\n";

   return kTRUE;
} 


//______________________________________________________________________________
void THtml::LocateMethods(std::ofstream & out, const char* filename,
                          Bool_t lookForSourceInfo /*= kTRUE*/,
                          Bool_t useDocxxStyle /*= kFALSE*/,
                          Bool_t lookForClassDescr /*= kTRUE*/,
                          Bool_t allowPureVirtual /*= kFALSE*/,
                          const char* methodPattern /*= 0*/,
                          const char* sourceExt /*= 0 */)
{
   // Collect methods from the source or header file called filename.
   // It generates a beautified version of the source file on the fly;
   // the output file is given by the fCurrentClass's name, and sourceExt.
   // Documentation is extracted to out.
   //   lookForSourceInfo: if set, author, lastUpdate, and copyright are 
   //     extracted (i.e. the values contained in fSourceInfo)
   //   useDocxxStyle: if set, documentation can be in front of the method
   //     name, not only inside the method. Useful doc Doc++/Doxygen style,
   //     and inline methods.
   //   lookForClassDescr: if set, the first line matching the class description 
   //     rules is assumed to be the class description for fCurrentClass; the 
   //     description is written to out.
   //   methodPattern: if set, methods have to be prepended by this tag. Usually
   //     the class name + "::". In header files, looking for in-place function
   //     definitions, this should be 0. In that case, only functions in 
   //     fMethodNames are searched for.

   TString sourceFileName(filename);
   fCurrentFile = filename;
   GetSourceFileName(sourceFileName);
   if (!sourceFileName.Length()) {
      Error("LocateMethods", "Can't find source file '%s' for class %s!",
         GetImplFileName(fCurrentClass), fCurrentClass->GetName());
      return;
   }
   ifstream sourceFile(sourceFileName.Data());
   if (!sourceFile || !sourceFile.good()) {
      Error("LocateMethods", "Can't open file '%s' for reading!", sourceFileName.Data());
      return;
   }

   // get environment variables
   const char *sourceInfoTags[kNumSourceInfos];
   sourceInfoTags[kInfoLastUpdate] = gEnv->GetValue("Root.Html.LastUpdate", "// @(#)");
   sourceInfoTags[kInfoAuthor]     = gEnv->GetValue("Root.Html.Author", "// Author:");
   sourceInfoTags[kInfoCopyright]  = gEnv->GetValue("Root.Html.Copyright", " * Copyright");

   const char *descriptionStr =
       gEnv->GetValue("Root.Html.Description", "//____________________");

   Bool_t foundClassDescription = !lookForClassDescr;

   TString pattern(methodPattern);

   TString prevComment;
   TString codeOneLiner;
   TString methodRet;
   TString methodName;
   TString methodParam;
   TString anchor;

   Bool_t wroteMethodNowWaitingForOpenBlock = kFALSE;

   ofstream srcHtmlOut;
   TString srcHtmlOutName;
   if (sourceExt && sourceExt[0])
      CreateSourceOutputStream(srcHtmlOut, sourceExt, srcHtmlOutName);
   else {
      sourceExt = 0;
      srcHtmlOutName = fCurrentClass->GetName();
      NameSpace2FileName(srcHtmlOutName);
      gSystem->PrependPathName("src", srcHtmlOutName);
      srcHtmlOutName += ".h.html";
   }

   fParseContext.clear();
   fParseContext.push_back(kCode);
   fDocContext = kIgnore;
   fLineNo = 0;

   while (!sourceFile.eof()) {
      Bool_t needAnchor = kFALSE;

      ++fLineNo; // we count fortrany

      fLine.ReadLine(sourceFile, kFALSE);
      if (sourceFile.eof()) break;

      // replace class names etc
      fLineExpanded = fLine;
      ExpandKeywords(fLineExpanded);
      fLineStripped = fLine;
      Strip(fLineStripped);

      // remove leading and trailing spaces
      TString lineExpandedStripped(fLineExpanded);
      Strip(lineExpandedStripped);

      if (!ExtractComments(lineExpandedStripped, foundClassDescription,
                           descriptionStr, prevComment)) {
         // not a commented line

         // write previous method
         if (methodName.Length() && !wroteMethodNowWaitingForOpenBlock) {
            if (!foundClassDescription && lookForClassDescr) {
               // no class description - close it
               out << "</div>" << std::endl;
               lookForClassDescr = kFALSE;
            }
            WriteMethod(out, methodRet, methodName, methodParam,
               gSystem->BaseName(srcHtmlOutName), anchor,
               prevComment, codeOneLiner);
         } else if (fDocContext == kDocClass) {
            // write class description
            out << "<pre>" << prevComment << "</pre></div>" << std::endl;
            prevComment.Remove(0);
            fDocContext = kIgnore;
         }

         if (!wroteMethodNowWaitingForOpenBlock) {
            // check for method
            Ssiz_t posPattern = pattern.Length() ? fLine.Index(pattern) : kNPOS;
            if (posPattern != kNPOS || !pattern.Length()) {
               posPattern += pattern.Length();
               LocateMethodInCurrentLine(posPattern, methodRet, methodName,
                  methodParam, srcHtmlOut, anchor, sourceFile, allowPureVirtual);
               if (methodName.Length()) {
                  fDocContext = kDocFunc;
                  needAnchor = !anchor.Length();
                  if (!useDocxxStyle)
                     prevComment.Remove(0);
                  codeOneLiner.Remove(0);

                  wroteMethodNowWaitingForOpenBlock = fLine.Index("{", posPattern) == kNPOS;
                  wroteMethodNowWaitingForOpenBlock &= fLine.Index(";", posPattern) == kNPOS;
               } else
                  prevComment.Remove(0);
            } // pattern matches - could be a method
            else
               prevComment.Remove(0);
         } else {
            wroteMethodNowWaitingForOpenBlock &= fLine.Index("{") == kNPOS;
            wroteMethodNowWaitingForOpenBlock &= fLine.Index(";") == kNPOS;
         } // if !wroteMethodNowWaitingForOpenBlock

         if (methodName.Length() && !wroteMethodNowWaitingForOpenBlock) {
            // make sure we don't have more '{' in lineExpandedStripped than in fLine
            if (!codeOneLiner.Length() &&
                lineExpandedStripped.CountChar('{') == 1 &&
                lineExpandedStripped.CountChar('}') == 1) {
               // a one-liner
               codeOneLiner = lineExpandedStripped;
               codeOneLiner.Remove(0, codeOneLiner.Index('{'));
               codeOneLiner.Remove(codeOneLiner.Index('}') + 1);
            }
         } // if method name and '{'
      } // if comment

      // check for last update,...
      Ssiz_t posTag = kNPOS;
      if (lookForSourceInfo)
         for (Int_t si = 0; si < (Int_t) kNumSourceInfos; ++si)
            if (!fSourceInfo[si].Length() && (posTag = fLine.Index(sourceInfoTags[si])) != kNPOS)
               fSourceInfo[si] = fLine(posTag + strlen(sourceInfoTags[si]), fLine.Length() - posTag);

      if (needAnchor || fExtraLinesWithAnchor.find(fLineNo) != fExtraLinesWithAnchor.end()) {
         AnchorFromLine(anchor);
         if (sourceExt)
            srcHtmlOut << "<a name=\"" << anchor << "\"></a>";
      }
      // else anchor.Remove(0); - NO! WriteMethod will need it later!

      // write to .cxx.html
      if (sourceExt)
         BeautifyLine(srcHtmlOut);
      else if (needAnchor)
         fExtraLinesWithAnchor.insert(fLineNo);
   } // while !sourceFile.eof()

   // deal with last func
   if (methodName.Length()) {
      WriteMethod(out, methodRet, methodName, methodParam,
         gSystem->BaseName(srcHtmlOutName), anchor,
         prevComment, codeOneLiner);
   } else if (fDocContext == kDocClass) {
      // write class description
      out << prevComment << "</div>" << std::endl;
   } else if (!foundClassDescription && lookForClassDescr)
      out << "</div>" << std::endl;

   srcHtmlOut << "</pre>" << std::endl;
   WriteHtmlFooter(srcHtmlOut, "../");

   fParseContext.clear();
   fParseContext.push_back(kCode);
   fDocContext = kIgnore;
   fCurrentFile = "";
}

//______________________________________________________________________________
void THtml::LocateMethodsInSource(ofstream & out)
{
   // Given fCurrentClass, look for methods in its source file, 
   // and extract documentation to out, while beautifying the source 
   // file in parallel.

   // for Doc++ style
   const char* docxxEnv = gEnv->GetValue("Root.Html.DescriptionStyle", "");
   Bool_t useDocxxStyle = (strcmp(docxxEnv, "Doc++") == 0);

   TString pattern(fCurrentClass->GetName());
   // take unscoped version
   Ssiz_t posLastScope = kNPOS;
   while ((posLastScope = pattern.Index("::")) != kNPOS)
      pattern.Remove(0, posLastScope + 1);
   pattern += "::";

   const char* implFileName = GetImplFileName(fCurrentClass);
   if (implFileName && implFileName[0])
      LocateMethods(out, implFileName, kTRUE, useDocxxStyle, kTRUE,
         kFALSE, pattern, ".cxx.html");
   else out << "</div>" << endl; // close class descr div
}

//______________________________________________________________________________
void THtml::LocateMethodsInHeaderInline(ofstream & out)
{
   // Given fCurrentClass, look for methods in its header file, 
   // and extract documentation to out.

   // for inline methods, always allow doc before func
   Bool_t useDocxxStyle = kTRUE;

   TString pattern(fCurrentClass->GetName());
   // take unscoped version
   Ssiz_t posLastScope = kNPOS;
   while ((posLastScope = pattern.Index("::")) != kNPOS)
      pattern.Remove(0, posLastScope + 1);
   pattern += "::";

   const char* declFileName = GetDeclFileName(fCurrentClass);
   if (declFileName && declFileName[0])
      LocateMethods(out, declFileName, kFALSE, useDocxxStyle, kFALSE,
         kFALSE, pattern, 0);
}

//______________________________________________________________________________
void THtml::LocateMethodsInHeaderClassDecl(ofstream & out)
{
   // Given fCurrentClass, look for methods in its header file's
   // class declaration block, and extract documentation to out,
   // while beautifying the header file in parallel.

   const char* declFileName = GetDeclFileName(fCurrentClass);
   if (declFileName && declFileName[0])
      LocateMethods(out, declFileName, kFALSE, kTRUE, kFALSE, kTRUE, 0, ".h.html");
}

//______________________________________________________________________________
void THtml::ClassDescription(ofstream & out)
{
// This function builds the description of the class
//
//
// Input: out      - output file stream
//


   // Class Description Title
   out << "<hr />" << endl;
   out << "<!--DESCRIPTION-->";
   out << "<div class=\"classdescr\">";
   TString anchor(fCurrentClass->GetName());
   NameSpace2FileName(anchor);
   out << "<h2><a name=\"" << anchor;
   out << ":description\">Class Description</a></h2>" << endl;

   // create an array of method names
   TMethod *method;
   TIter nextMethod(fCurrentClass->GetListOfMethods());
   fMethodNames.clear();
   while ((method = (TMethod *) nextMethod())) {
      ++fMethodNames[method->GetName()];
   }

   for (Int_t si = 0; si < (Int_t) kNumSourceInfos; ++si)
      fSourceInfo[si].Remove(0);

   fExtraLinesWithAnchor.clear();
   LocateMethodsInSource(out);
   LocateMethodsInHeaderInline(out);
   LocateMethodsInHeaderClassDecl(out);
   fExtraLinesWithAnchor.clear();

   // write classFile footer
   TDatime date;
   if (!fSourceInfo[kInfoLastUpdate].Length())
      fSourceInfo[kInfoLastUpdate] = date.AsString();
   WriteHtmlFooter(out, "", fSourceInfo[kInfoLastUpdate],
      fSourceInfo[kInfoAuthor], fSourceInfo[kInfoCopyright]);
}


//______________________________________________________________________________
Bool_t THtml::HaveDot() {
   // Check whether dot is available in $PATH or in the directory set 
   // by SetDotPath()

   if (fFoundDot != -1)
      return (Bool_t)fFoundDot;

   Info("HaveDot", "Checking for Graphviz (dot)...");
   TString runDot("dot");
   if (fDotDir.Length())
      gSystem->PrependPathName(fDotDir, runDot);
   runDot += " -V";
   if (gDebug > 3)
      Info("HaveDot", "Running: %s", runDot.Data());
   if (gSystem->Exec(runDot)) {
      fFoundDot = 0;
      return kFALSE;
   }
   fFoundDot = 1;
   return kTRUE;

}

//______________________________________________________________________________
Bool_t THtml::RunDot(const char* filename, std::ostream* outMap /* =0 */) {
// Run filename".dot", creating filename".gif", and - if outMap is !=0,
// filename".map", which gets then included literally into outMap.

   if (!HaveDot())
      return kFALSE;

   TString runDot("dot");
   if (fDotDir.Length())
      gSystem->PrependPathName(fDotDir, runDot);
   runDot += " -q1 -Tgif -o";
   runDot += filename;
   runDot += ".gif ";
   if (outMap) {
      runDot += "-Tcmap -o";
      runDot += filename;
      runDot += ".map ";
   }
   runDot += filename;
   runDot += ".dot";

   if (gDebug > 3)
      Info("RunDot", "Running: %s", runDot.Data());
   Int_t retDot = gSystem->Exec(runDot);
   if (gDebug < 4 && !retDot)
      gSystem->Unlink(Form("%s.dot", filename));

   if (!retDot && outMap) {
      ifstream inmap(Form("%s.map", filename));
      std::string line;
      std::getline(inmap, line);
      if (inmap && !inmap.eof()) {
         *outMap << "<map name=\"Map" << gSystem->BaseName(filename) 
            << "\" id=\"Map" << gSystem->BaseName(filename) << "\">" << endl;
         while (inmap && !inmap.eof()) {
            if (line.compare(0, 6, "<area ") == 0) {
               size_t posEndTag = line.find('>');
               if (posEndTag != std::string::npos)
                  line.replace(posEndTag, 1, "/>");
            }
            *outMap << line << endl;
            std::getline(inmap, line);
         }
         *outMap << "</map>" << endl;
      }
      inmap.close();
      if (gDebug < 7)
         gSystem->Unlink(Form("%s.map", filename));
   }

   if (retDot) {
      Error("RunDot", "Error running %s!", runDot.Data());
      fFoundDot = 0;
      return kFALSE;
   }

   return kTRUE;
}


//______________________________________________________________________________
Bool_t THtml::CreateDotClassChartInh(const char* filename) {
// Build the class tree for one class in GraphViz/Dot format
//
//
// Input: filename - output dot file incl. path

   ofstream outdot(filename);
   outdot << "strict digraph G {" << endl
      << "rankdir=RL;" << endl
      << "compound=true;" << endl
      << "constraint=false;" << endl
      << "ranksep=0.1;" << endl
      << "nodesep=0;" << endl
      << "size=\"16,20\";" << endl
      << "ratio=compress;" << endl
      << "\"" << fCurrentClass->GetName() << "\";" << endl;

   std::stringstream ssDep;
   std::list<TClass*> writeBasesFor;
   writeBasesFor.push_back(fCurrentClass);
   Bool_t haveBases = fCurrentClass->GetListOfBases() &&
      fCurrentClass->GetListOfBases()->GetSize();
   if (haveBases) {
      outdot << "{" << endl
            << "  node [shape=plaintext,fontsize=10];" << endl;
      while (!writeBasesFor.empty()) {
         TClass* cl = writeBasesFor.front();
         writeBasesFor.pop_front();
         if (cl != fCurrentClass) {
            outdot << "  \"" << cl->GetName() << "\"";
            TClassDocInfo* cdi = (TClassDocInfo*) fClasses.FindObject(cl->GetName());
            if (cdi)
               outdot << " [URL=\"" << cdi->GetHtmlFileName() << "\"]";
            outdot << ";" << endl;
         }
         if (cl->GetListOfBases() && cl->GetListOfBases()->GetSize()) {
            ssDep << "  \"" << cl->GetName() << "\" -> {";
            TIter iBase(cl->GetListOfBases());
            TBaseClass* base = 0;
            while ((base = (TBaseClass*)iBase())) {
               ssDep << " \"" << base->GetName() << "\";";
               writeBasesFor.push_back(base->GetClassPointer());
            }
            ssDep << "}" << endl;
         }
      }
      outdot << "}" << endl; // cluster
   }

   std::set<TClass*> derivesFromMe;
   TIter iClass(&fClasses);
   TClassDocInfo* cdi = 0;
   while ((cdi = (TClassDocInfo*) iClass())) {
      TClass* cl = cdi->GetClass();
      if (!cl) continue;
      if (cl != fCurrentClass && cl->InheritsFrom(fCurrentClass))
         derivesFromMe.insert(cl);
   }
   outdot << "{" << endl
      << "  node [shape=plaintext,fontsize=10];" << endl;
   for (std::set<TClass*>::iterator iDerived = derivesFromMe.begin();
      iDerived != derivesFromMe.end(); ++iDerived) {
      TClassDocInfo* cdi = (TClassDocInfo*) fClasses.FindObject((*iDerived)->GetName());
      if (cdi)
         outdot << "  node [URL=\"" << cdi->GetHtmlFileName() << "\"];" << endl;
      outdot << "  \"" << (*iDerived)->GetName() << "\";" << endl;

      TIter iBaseOfDerived((*iDerived)->GetListOfBases());
      TBaseClass* baseDerived = 0;
      while ((baseDerived = (TBaseClass*) iBaseOfDerived())) {
         TClass* clBaseDerived = baseDerived->GetClassPointer();
         if (clBaseDerived->InheritsFrom(fCurrentClass))
            ssDep << "\"" << (*iDerived)->GetName() << "\" -> \""
               << clBaseDerived->GetName() << "\";" << endl;
      }
   }
   outdot << "}" << endl; // cluster

   outdot << ssDep.str();

   outdot << "}" << endl; // digraph

   return kTRUE;
}

//______________________________________________________________________________
Bool_t THtml::CreateDotClassChartInhMem(const char* filename) {
// Build the class tree of inherited members for one class in GraphViz/Dot format
//
// Input: filename - output dot file incl. path

   ofstream outdot(filename);
   outdot << "strict digraph G {" << endl
      << "ratio=auto;" << endl
      << "rankdir=RL;" << endl
      << "compound=true;" << endl
      << "constraint=false;" << endl
      << "ranksep=0.1;" << endl
      << "nodesep=0;" << endl;

   std::stringstream ssDep;

   std::list<TClass*> writeBasesFor;
   writeBasesFor.push_back(fCurrentClass);
   while (!writeBasesFor.empty()) {
      TClass* cl = writeBasesFor.front();
      writeBasesFor.pop_front();

      TString htmlFileName;
      TClassDocInfo* cdi = (TClassDocInfo*) fClasses.FindObject(cl->GetName());
      if (cdi)
         htmlFileName = cdi->GetHtmlFileName();

      outdot << "subgraph \"cluster" << cl->GetName() << "\" {" << endl
            << "  color=lightgray;" << endl
            << "  label=\"" << cl->GetName() << "\";" << endl;
      if (cl != fCurrentClass)
         outdot << "  URL=\"" << htmlFileName << "\"" << endl;

      outdot << "  node [style=filled,width=0.7,height=0.15,shape=plaintext,fixedsize=true,fontsize=10];" << endl;

      Bool_t haveMembers = (cl->GetListOfDataMembers() && cl->GetListOfDataMembers()->GetSize());
      Bool_t haveFuncs = cl->GetListOfMethods() && cl->GetListOfMethods()->GetSize();

      if (haveMembers) {
         outdot << "subgraph \"clusterData" << cl->GetName() << "\" {" << endl
            << "  color=white;" << endl
            << "  label=\"\";" << endl
            << "  \"nodeToCluster" << cl->GetName() << "\" [height=0,width=0,style=invis];" << endl;
         if (!haveFuncs)
            outdot << "\"nodeFromCluster" << cl->GetName() << "\" [style=invis,width=0,height=0];" << endl;

         TIter iDM(cl->GetListOfDataMembers());
         TDataMember* dm = 0;
         while ((dm = (TDataMember*) iDM())) {
            TString name(dm->GetName());
            NameSpace2FileName(name);
            outdot << "\"" << cl->GetName() << "::" << dm->GetName() << "\" [label=\""
               << dm->GetName() << "\"";
            if (dm->Property() & kIsPrivate)
               outdot << ",color=\"#FFCCCC\"";
            else if (dm->Property() & kIsProtected)
               outdot << ",color=\"#FFFF77\"";
            else
               outdot << ",color=\"#CCFFCC\"";
            outdot << "];" << endl;
         }
         outdot << "}" << endl; // cluster data
      }
      if (haveFuncs) {
         outdot << "subgraph \"clusterFunc" << cl->GetName() << "\" {" << endl
            << "  color=white;" << endl
            << "  label=\"\";" << endl;

         if (!haveMembers)
            outdot << "  \"nodeToCluster" << cl->GetName() << "\" [height=0,width=0,style=invis];" << endl;
         outdot << "\"nodeFromCluster" << cl->GetName() << "\" [style=invis,width=0,height=0];" << endl;

         TIter iMeth(cl->GetListOfMethods());
         TMethod* meth = 0;
         while ((meth = (TMethod*) iMeth())) {
            TString name(meth->GetName());
            NameSpace2FileName(name);
            outdot << "\"" << cl->GetName() << "::" << meth->GetName() << "\" [label=\""
               << meth->GetName() << "\"";
            if (cl != fCurrentClass &&
               fCurrentClass->GetMethodAny(meth->GetName()))
               outdot << ",color=\"#777777\"";
            else if (meth->Property() & kIsPrivate)
               outdot << ",color=\"#FFCCCC\"";
            else if (meth->Property() & kIsProtected)
               outdot << ",color=\"#FFFF77\"";
            else
               outdot << ",color=\"#CCFFCC\"";
            outdot << "];" << endl;
         }
         if (haveMembers)
            ssDep << "  \"nodeToCluster" << cl->GetName() << "\" -> \"nodeFromCluster" 
               << cl->GetName() << "\" [style=invis];" << endl;
         outdot << "}" << endl; // cluster func
      } else if (!haveMembers) {
         // neither data members nor func memebrs
         outdot << "  \"nodeToCluster" << cl->GetName() << "\" [height=0,width=0,style=invis];" << endl;
         outdot << "\"nodeFromCluster" << cl->GetName() << "\" [style=invis,width=0,height=0];" << endl;
      }
      outdot << "}" << endl; // cluster class

      if (cl->GetListOfBases() && cl->GetListOfBases()->GetSize()) {
         TIter iBase(cl->GetListOfBases());
         TBaseClass* base = 0;
         while ((base = (TBaseClass*)iBase())) {
            ssDep << "  \"nodeFromCluster" << cl->GetName() << "\" -> "
                  << " \"nodeToCluster" << base->GetName() << "\" [ltail=\"cluster" << cl->GetName() 
                  << "\",lhead=\"cluster" << base->GetName() << "\"";
            if (base != cl->GetListOfBases()->First())
               ssDep << ",weight=0";
            ssDep << "];" << endl;
            writeBasesFor.push_back(base->GetClassPointer());
         }
      }
   }

   outdot << ssDep.str();

   outdot << "}" << endl; // digraph

   return kTRUE;
}

//______________________________________________________________________________
Bool_t THtml::CreateDotClassChartIncl(const char* filename) {
// Build the include dependency graph for one class in 
// GraphViz/Dot format
//
// Input: filename - output dot file incl. path

   std::map<std::string, std::string> filesToParse;
   std::list<std::string> listFilesToParse;
   const char* declFileName = GetDeclFileName(fCurrentClass);
   const char* implFileName = GetImplFileName(fCurrentClass);
   if (declFileName && strlen(declFileName)) {
      char* real = gSystem->Which(fSourceDir, declFileName, kReadPermission);
      if (real) {
         filesToParse[declFileName] = real;
         listFilesToParse.push_back(declFileName);
         delete real;
      }
   }
   if (implFileName && strlen(implFileName)) {
      char* real = gSystem->Which(fSourceDir, implFileName, kReadPermission);
      if (real) {
         filesToParse[implFileName] = real;
         listFilesToParse.push_back(implFileName);
         delete real;
      }
   }

   ofstream outdot(filename);
   outdot << "strict digraph G {" << endl
      << "ratio=compress;" << endl
      << "rankdir=TB;" << endl
      << "concentrate=true;" << endl
      << "ranksep=0;" << endl
      << "nodesep=0;" << endl
      << "size=\"8,10\";" << endl
      << "node [fontsize=20,shape=plaintext];" << endl;

   for (std::list<std::string>::iterator iFile = listFilesToParse.begin();
      iFile != listFilesToParse.end(); ++iFile) {
      ifstream in(filesToParse[*iFile].c_str());
      std::string line;
      while (in && !in.eof()) {
         std::getline(in, line);
         size_t pos = 0;
         while (line[pos] == ' ' || line[pos] == '\t') ++pos;
         if (line[pos] != '#') continue;
         ++pos;
         while (line[pos] == ' ' || line[pos] == '\t') ++pos;
         if (line.compare(pos, 8, "include ") != 0) continue;
         pos += 8;
         while (line[pos] == ' ' || line[pos] == '\t') ++pos;
         if (line[pos] != '"' && line[pos] != '<')
            continue;
         char delim = line[pos];
         if (delim == '<') delim = '>';
         ++pos;
         line.erase(0, pos);
         pos = 0;
         pos = line.find(delim);
         if (pos == std::string::npos) continue;
         line.erase(pos);
         if (filesToParse.find(line) == filesToParse.end()) {
            char* filename = gSystem->Which(fSourceDir, line.c_str(), kReadPermission);
            if (!filename) continue;
            listFilesToParse.push_back(line);
            filesToParse[line] = filename;
            delete filename;
            if (*iFile == implFileName || *iFile == declFileName)
               outdot << "\"" << *iFile << "\" [style=filled,fillcolor=lightgray];" << endl;
         }
         outdot << "\"" << *iFile << "\" -> \"" << line << "\";" << endl;
      }
   }

   outdot << "}" << endl; // digraph

   return kTRUE;
}

//______________________________________________________________________________
Bool_t THtml::CreateDotClassChartLib(const char* filename) {
// Build the library dependency graph for one class in 
// GraphViz/Dot format
//
// Input: filename - output dot file incl. path

   ofstream outdot(filename);
   outdot << "strict digraph G {" << endl
      << "ratio=auto;" << endl
      << "rankdir=RL;" << endl
      << "compound=true;" << endl
      << "constraint=false;" << endl
      << "ranksep=0.7;" << endl
      << "nodesep=0.3;" << endl
      << "size=\"8,8\";" << endl
      << "ratio=compress;" << endl;

   TString libs(fCurrentClass->GetSharedLibs());
   outdot << "\"All Libraries\" [URL=\"LibraryDependencies.html\",shape=box,rank=max,fillcolor=lightgray,style=filled];" << endl;

   if (libs.Length()) {
      TString firstLib(libs);
      Ssiz_t end = firstLib.Index(' ');
      if (end != kNPOS) {
         firstLib.Remove(end, firstLib.Length());
         libs.Remove(0, end + 1);
      } else libs = "";
      Ssiz_t posExt = firstLib.First(".");
      if (posExt != kNPOS)
         firstLib.Remove(posExt, firstLib.Length());
      outdot << "\"All Libraries\" -> \"" << firstLib << "\" [style=invis];" << endl;
      outdot << "\"" << firstLib << "\" -> {" << endl;

      if (firstLib != "libCore")
         libs += " libCore";
      if (firstLib != "libCint")
         libs += " libCint";
      TString thisLib;
      for (Ssiz_t pos = 0; pos < libs.Length(); ++pos)
         if (libs[pos] != ' ')
            thisLib += libs[pos];
         else if (thisLib.Length()) {
            Ssiz_t posExt = thisLib.First(".");
            if (posExt != kNPOS)
               thisLib.Remove(posExt, thisLib.Length());
            outdot << " \"" << thisLib << "\";";
            thisLib = "";
         }
      // remaining lib
      if (thisLib.Length()) {
         Ssiz_t posExt = thisLib.First(".");
         if (posExt != kNPOS)
            thisLib.Remove(posExt, thisLib.Length());
         outdot << " \"" << thisLib << "\";";
         thisLib = "";
      }
      outdot << "}" << endl; // dependencies
   } else
      outdot << "\"No rlibmap information avaliable.\"" << endl;

   outdot << "}" << endl; // digraph

   return kTRUE;
}

//______________________________________________________________________________
Bool_t THtml::ClassDotCharts(ofstream & out)
{
// This function builds the class charts for one class in GraphViz/Dot format,
// i.e. the inheritance diagram, the include dependencies, and the library
// dependency.
//
// Input: out      - output file stream

   if (!HaveDot())
      return kFALSE;

   TString title(fCurrentClass->GetName());
   NameSpace2FileName(title);

   gSystem->ExpandPathName(fOutputDir);

   TString dir("inh");
   gSystem->PrependPathName(fOutputDir, dir);
   gSystem->MakeDirectory(dir);

   dir = "inhmem";
   gSystem->PrependPathName(fOutputDir, dir);
   gSystem->MakeDirectory(dir);

   dir = "incl";
   gSystem->PrependPathName(fOutputDir, dir);
   gSystem->MakeDirectory(dir);

   dir = "lib";
   gSystem->PrependPathName(fOutputDir, dir);
   gSystem->MakeDirectory(dir);

   TString filenameInh(title);
   gSystem->PrependPathName("inh", filenameInh);
   gSystem->PrependPathName(fOutputDir, filenameInh);
   filenameInh += "_Inh";
   if (!CreateDotClassChartInh(filenameInh + ".dot") ||
      !RunDot(filenameInh, &out))
   return kFALSE;

   TString filenameInhMem(title);
   gSystem->PrependPathName("inhmem", filenameInhMem);
   gSystem->PrependPathName(fOutputDir, filenameInhMem);
   filenameInhMem += "_InhMem";
   if (CreateDotClassChartInhMem(filenameInhMem + ".dot"))
      RunDot(filenameInhMem, &out);

   TString filenameIncl(title);
   gSystem->PrependPathName("incl", filenameIncl);
   gSystem->PrependPathName(fOutputDir, filenameIncl);
   filenameIncl += "_Incl";
   if (CreateDotClassChartIncl(filenameIncl + ".dot"))
      RunDot(filenameIncl, &out);

   TString filenameLib(title);
   gSystem->PrependPathName("lib", filenameLib);
   gSystem->PrependPathName(fOutputDir, filenameLib);
   filenameLib += "_Lib";
   if (CreateDotClassChartLib(filenameLib + ".dot"))
      RunDot(filenameLib, &out);

   out << "<div class=\"imgformattabs\">" << endl
       << "<a id=\"img" << title << "_Inh\" class=\"imgformattabsel\" href=\"javascript:SetImg('Charts','inh/" << title << "_Inh.gif');\">Inheritance</a>" << endl
       << "<a id=\"img" << title << "_InhMem\" class=\"imgformattab\" href=\"javascript:SetImg('Charts','inhmem/" << title << "_InhMem.gif');\">Inherited Members</a>" << endl
       << "<a id=\"img" << title << "_Incl\" class=\"imgformattab\" href=\"javascript:SetImg('Charts','incl/" << title << "_Incl.gif');\">Includes</a>" << endl
       << "<a id=\"img" << title << "_Lib\" class=\"imgformattab\" href=\"javascript:SetImg('Charts','lib/" << title << "_Lib.gif');\">Libraries</a><br/>" << endl
       << "</div><div class=\"formatsel\"><div class=\"formatselwidth\"></div>" << endl
       << "<img id=\"Charts\" alt=\"Class Charts\" class=\"formatsel\" usemap=\"#Map" << title << "_Inh\" src=\"inh/" << title << "_Inh.gif\"/></div>" << endl;

   return kTRUE;
}

//______________________________________________________________________________
void THtml::ClassHtmlTree(ofstream & out, TClass * classPtr,
                          ETraverse dir, int depth)
{
// This function builds the class tree for one class in HTML
// (inherited and succeeding classes, called recursively)
//
//
// Input: out      - output file stream
//        classPtr - pointer to the class
//        dir      - direction to traverse tree: up, down or both
//

   if (dir == kBoth) {
      out << "<!--INHERITANCE TREE-->" << endl;

      // draw class tree into nested tables recursively
      out << "<table><tr><td width=\"10%\"></td><td width=\"70%\">"
          << "<a href=\"ClassHierarchy.html\">Inheritance Chart</a>:</td></tr>";
      out << "<tr class=\"inhtree\"><td width=\"10%\"></td><td width=\"70%\">";

      out << "<table class=\"inhtree\"><tr><td>" << endl;
      out << "<table width=\"100%\" border=\"0\" ";
      out << "cellpadding =\"0\" cellspacing=\"2\"><tr>" << endl;
   } else {
      out << "<table><tr>";
   }

   ////////////////////////////////////////////////////////
   // Loop up to mother classes
   if (dir == kUp || dir == kBoth) {

      // make a loop on base classes
      TBaseClass *inheritFrom;
      TIter nextBase(classPtr->GetListOfBases());

      UInt_t bgcolor=255-depth*8;
      Bool_t first = kTRUE;
      while ((inheritFrom = (TBaseClass *) nextBase())) {

         if (first) {
            out << "<td><table><tr>" << endl;
            first = kFALSE;
         } else
            out << "</tr><tr>" << endl;
         out << "<td bgcolor=\""
            << Form("#%02x%02x%02x", bgcolor, bgcolor, bgcolor)
            << "\" align=\"right\">" << endl;
         // get a class
         TClass *classInh = GetClass((const char *) inheritFrom->GetName());
         if (classInh)
            ClassHtmlTree(out, classInh, kUp, depth+1);
         else
            out << "<tt>"
                << (const char *) inheritFrom->GetName()
                << "</tt>";
         out << "</td>"<< endl;
      }
      if (!first) {
         out << "</tr></table></td>" << endl; // put it in additional row in table
         out << "<td>&larr;</td>";
      }
   }

   out << "<td>" << endl; // put it in additional row in table
   ////////////////////////////////////////////////////////
   // Output Class Name

   const char *className = classPtr->GetName();
   TString htmlFile;
   GetHtmlFileName(classPtr, htmlFile);
   TString anchor(className);
   NameSpace2FileName(anchor);

   if (dir == kUp) {
      if (htmlFile) {
         out << "<center><tt><a name=\"" << anchor;
         out << "\" href=\"" << htmlFile << "\">";
         ReplaceSpecialChars(out, className);
         out << "</a></tt></center>" << endl;
      } else
         ReplaceSpecialChars(out, className);
   }

   if (dir == kBoth) {
      if (htmlFile.Length()) {
         out << "<center><big><b><tt><a name=\"" << anchor;
         out << "\" href=\"" << htmlFile << "\">";
         ReplaceSpecialChars(out, className);
         out << "</a></tt></b></big></center>" << endl;
      } else
         ReplaceSpecialChars(out, className);
   }

   out << "</td>" << endl; // put it in additional row in table

   ////////////////////////////////////////////////////////
   // Loop down to child classes

   if (dir == kDown || dir == kBoth) {

      // 1. make a list of class names
      // 2. use DescendHierarchy

      out << "<td><table><tr>" << endl;
      fHierarchyLines = 0;
      DescendHierarchy(out,classPtr,10);

      out << "</tr></table>";
      if (dir==kBoth && fHierarchyLines>=10)
         out << "</td><td align=\"left\">&nbsp;<a href=\"ClassHierarchy.html\">[more...]</a>";
      out<<"</td>" << endl;

      // free allocated memory
   }

   out << "</tr></table>" << endl;
   if (dir == kBoth)
      out << "</td></tr></table></td></tr></table>"<<endl;
}


//______________________________________________________________________________
void THtml::ClassTree(TVirtualPad * psCanvas, TClass * classPtr,
                      Bool_t force)
{
// It makes a graphical class tree
//
//
// Input: psCanvas - pointer to the current canvas
//        classPtr - pointer to the class
//

   if (psCanvas && classPtr) {
      TString filename(classPtr->GetName());
      NameSpace2FileName(filename);

      gSystem->ExpandPathName(fOutputDir);
      gSystem->PrependPathName(fOutputDir, filename);


      filename += "_Tree.pdf";

      if (IsModified(classPtr, kTree) || force) {
         // TCanvas already prints pdf being saved
         // Printf(formatStr, "", "", filename);
         classPtr->Draw("same");
         Int_t saveErrorIgnoreLevel = gErrorIgnoreLevel;
         gErrorIgnoreLevel = kWarning;
         psCanvas->SaveAs(filename);
         gErrorIgnoreLevel = saveErrorIgnoreLevel;
      } else
         Printf(formatStr, "-no change-", "", filename.Data());
   }
}


//______________________________________________________________________________
void THtml::Convert(const char *filename, const char *title,
                    const char *dirname /*= ""*/, const char *relpath /*= "../"*/)
{
// It converts a single text file to HTML
//
//
// Input: filename - name of the file to convert
//        title    - title which will be placed at the top of the HTML file
//        dirname  - optional parameter, if it's not specified, output will
//                   be placed in htmldoc/examples directory.
//        relpath  - optional parameter pointing to the THtml generated doc 
//                   on the server, relative to the current page.
//
//  NOTE: Output file name is the same as filename, but with extension .html
//

   gROOT->GetListOfGlobals(kTRUE);        // force update of this list
   CreateListOfClasses("*");

   const char *dir;

   // if it's not defined, make the "examples" as a default directory
   if (!*dirname) {
      gSystem->ExpandPathName(fOutputDir);
      dir = gSystem->ConcatFileName(fOutputDir, "examples");
   } else
      dir = dirname;

   // create directory if necessary
   if (gSystem->AccessPathName(dir))
      gSystem->MakeDirectory(dir);

   // find a file
   char *realFilename =
       gSystem->Which(fSourceDir, filename, kReadPermission);

   if (!realFilename) {
      Error("Convert", "Can't find file '%s' !", filename);
      return;
   }

   // open source file
   ifstream sourceFile;
   sourceFile.open(realFilename, ios::in);

   delete[]realFilename;
   realFilename = 0;

   if (!sourceFile.good()) {
      Error("Convert", "Can't open file '%s' !", realFilename);
      return;
   }

   // open temp file with extension '.html'
   if (gSystem->AccessPathName(dir)) {
      Error("Convert",
            "Directory '%s' doesn't exist, or it's write protected !", dir);
      return;
   }
   char *tmp1 =
       gSystem->ConcatFileName(dir, GetFileName(filename));
   char *htmlFilename = StrDup(tmp1, 16);
   strcat(htmlFilename, ".html");

   if (tmp1)
      delete[]tmp1;
   tmp1 = 0;

   ofstream tempFile;
   tempFile.open(htmlFilename, ios::out);

   if (!tempFile.good()) {
      Error("Convert", "Can't open file '%s' !", htmlFilename);
      if (htmlFilename)
         delete[]htmlFilename;
      return;
   }

   Printf("Convert: %s", htmlFilename);

   // write a HTML header
   WriteHtmlHeader(tempFile, title, relpath);

   tempFile << "<h1>" << title << "</h1>" << endl;
   tempFile << "<pre>" << endl;

   fParseContext.clear();
   fParseContext.push_back(kCComment); // so we can find "BEGIN_HTML"/"END_HTML" in plain text
   fDocContext = kIgnore;
   fLineNo = 0;

   while (!sourceFile.eof()) {
      fLine.ReadLine(sourceFile, kFALSE);
      if (sourceFile.eof())
         break;


      // remove leading spaces
      fLineExpanded = fLine;
      ExpandKeywords(fLineExpanded);
      fLineStripped = fLine;
      Strip(fLineStripped);

      if ((fParseContext.back() == kBeginEndHtml
         || fParseContext.back() == kBeginEndHtmlInCComment))
         if (kNPOS == fLine.Index("Begin_Html", 0, TString::kIgnoreCase)
            || kNPOS != fLine.Index("\"Begin_Html", 0, TString::kIgnoreCase))
            // no replacement if in begin/end help block
            tempFile << fLine << std::endl;
         else {
            fParseContext.push_back(kCode); // hide "BEGIN_HTML"
            BeautifyLine(tempFile, relpath);
            fParseContext.pop_back();
         }
      else
         if (kNPOS == fLine.Index("End_Html", 0, TString::kIgnoreCase)
            || kNPOS != fLine.Index("\"End_Html", 0, TString::kIgnoreCase))
            BeautifyLine(tempFile, relpath);
         else {
            Ssiz_t posEndHtml = fLine.Index("End_Html", 0, TString::kIgnoreCase);
            fLine.Remove(posEndHtml,8); // hide "BEGIN_HTML"
            BeautifyLine(tempFile, relpath);
         }
   }

   tempFile << "</pre>" << endl;


   // write a HTML footer
   WriteHtmlFooter(tempFile, relpath);


   // close a temp file
   tempFile.close();

   // close a source file
   sourceFile.close();
   if (htmlFilename)
      delete[]htmlFilename;
}


//______________________________________________________________________________
Bool_t THtml::CopyHtmlFile(const char *sourceName, const char *destName)
{
// Copy file to HTML directory
//
//
//  Input: sourceName - source file name
//         destName   - optional destination name, if not
//                      specified it would be the same
//                      as the source file name
//
// Output: TRUE if file is successfully copied, or
//         FALSE if it's not
//
//
//   NOTE: The destination directory is always fOutputDir
//

   // source file name
   char *tmp1 = gSystem->Which(fSourceDir, sourceName, kReadPermission);
   if (!tmp1) {
      Error("Copy", "Can't copy file '%s' to '%s/%s' - can't find source file!", sourceName,
            fOutputDir.Data(), destName);
      return kFALSE;
   }

   TString sourceFile(tmp1);
   delete[]tmp1;

   if (!sourceFile.Length()) {
      Error("Copy", "Can't copy file '%s' to '%s' directory - source file name invalid!", sourceName,
            fOutputDir.Data());
      return kFALSE;
   }

   // destination file name
   TString destFile;
   if (!destName || !*destName)
      destFile = GetFileName(sourceFile);
   else
      destFile = GetFileName(destName);

   gSystem->ExpandPathName(fOutputDir);
   gSystem->PrependPathName(fOutputDir, destFile);

   // Get info about a file
   Long64_t size;
   Long_t id, flags, sModtime, dModtime;
   sModtime = 0;
   dModtime = 0;
   if (gSystem->GetPathInfo(sourceFile, &id, &size, &flags, &sModtime)
      || gSystem->GetPathInfo(destFile, &id, &size, &flags, &dModtime)
      || sModtime > dModtime)
      gSystem->CopyFile(sourceFile, destFile, kTRUE);

   return kTRUE;
}



//______________________________________________________________________________
void THtml::CreateIndex()
{
// Create an index
//
//
// Input: classNames      - pointer to an array of class names
//        numberOfClasses - number of elements
//

   // create CSS file, we need it
   CreateStyleSheet();
   CreateJavascript();

   gSystem->ExpandPathName(fOutputDir);
   TString filename("ClassIndex.html");
   gSystem->PrependPathName(fOutputDir, filename);

   // open indexFile file
   ofstream indexFile;
   indexFile.open(filename.Data(), ios::out);

   if (indexFile.good()) {

      Printf(formatStr, "", fCounter.Data(), filename.Data());

      // write indexFile header
      WriteHtmlHeader(indexFile, "Class Index");

      indexFile << "<h1>Index</h1>" << endl;

      if (fModules.GetSize()) {
         indexFile << "<div id=\"indxModules\"><h4>Modules</h4>" << endl;
         // find index chars
         fModules.Sort();
         TIter iModule(&fModules);
         TModuleDocInfo* module = 0;
         while ((module = (TModuleDocInfo*) iModule()))
            indexFile << "<a href=\"" << module->GetName() << "_Index.html\">"
                      << module->GetName() << "</a>" << endl;
         indexFile << "</div><br />" << endl;
      }

      std::vector<std::string> indexChars;
      if (fClasses.GetSize() > 10) {
         std::vector<std::string> classNames;
         {
            TIter iClass(&fClasses);
            TClassDocInfo* cdi = 0;
            while ((cdi = (TClassDocInfo*)iClass()))
               classNames.push_back(cdi->GetName());
         }

         indexFile << "<div id=\"indxShortX\"><h4>Jump to</h4>" << endl;
         // find index chars
         GetIndexChars(classNames, 50 /*sections*/, indexChars);
         for (UInt_t iIdxEntry = 0; iIdxEntry < indexChars.size(); ++iIdxEntry) {
            indexFile << "<a href=\"#idx" << iIdxEntry << "\">" << indexChars[iIdxEntry]
                      << "</a>" << endl;
         }
         indexFile << "</div><br />" << endl;
      }

      // check for a search engine
      const char *searchEngine =
          gEnv->GetValue("Root.Html.SearchEngine", "");

      // if exists ...
      if (*searchEngine) {

         // create link to search engine page
         indexFile << "<h2><a href=\"" << searchEngine
             << "\">Search the Class Reference Guide</a></h2>" << endl;

      } else {
         const char *searchCmd =
             gEnv->GetValue("Root.Html.Search", "");

         //e.g. searchCmd = "http://www.google.com/search?q=%s+site%3Aroot.cern.ch%2Froot%2Fhtml";
         // if exists ...
         if (*searchCmd) {
            // create link to search engine page
            indexFile << "<script language=\"javascript\">" << endl
               << "function onSearch() {" << endl
               << "var s='" << searchCmd <<"';" << endl
               << "window.location.href=s.replace(/%s/ig,escape(document.searchform.t.value));" << endl
               << "return false;}" << endl
               << "</script><form action=\"javascript:onSearch();\" id=\"searchform\" name=\"searchform\" onsubmit=\"return onSearch()\">" << endl
               << "<input name=\"t\" value=\"Search documentation...\"  onfocus=\"if (document.searchform.t.value=='Search documentation...') document.searchform.t.value='';\"></input>" << endl
               << "<button type=\"submit\">Search</button></form>" << endl;
         }
      }

      indexFile << "<ul id=\"indx\">" << endl;

      // loop on all classes
      UInt_t currentIndexEntry = 0;
      TIter iClass(&fClasses);
      TClassDocInfo* cdi = 0;
      Int_t i = 0;
      while ((cdi = (TClassDocInfo*)iClass())) {
         // get class
         fCurrentClass = cdi->GetClass();
         if (fCurrentClass == 0) {
            Warning("THtml::CreateIndex", "skipping class %s\n", cdi->GetName());
            continue;
         }

         indexFile << "<li class=\"idxl" << (i++)%2 << "\"><tt>";
         if (currentIndexEntry < indexChars.size()
            && !strncmp(indexChars[currentIndexEntry].c_str(), cdi->GetName(),
                        indexChars[currentIndexEntry].length()))
            indexFile << "<a name=\"idx" << currentIndexEntry++ << "\"></a>" << endl;

         TString htmlFile(cdi->GetHtmlFileName());
         if (htmlFile.Length()) {
            indexFile << "<a name=\"";
            indexFile << cdi->GetName();
            indexFile << "\" href=\"";
            indexFile << htmlFile;
            indexFile << "\">";
            ReplaceSpecialChars(indexFile, cdi->GetName());
            indexFile << "</a>";
         } else
            ReplaceSpecialChars(indexFile, cdi->GetName());


         // write title
         indexFile << "</tt>";

         indexFile << "<a name=\"Title:";
         indexFile << fCurrentClass->GetName();
         indexFile << "\"></a>";
         ReplaceSpecialChars(indexFile, fCurrentClass->GetTitle());
         indexFile << "</li>" << endl;
      }

      indexFile << "</ul>" << endl;

      // write indexFile footer
      TDatime date;
      WriteHtmlFooter(indexFile, "", date.AsString());


      // close file
      indexFile.close();

   } else
      Error("MakeIndex", "Can't open file '%s' !", filename.Data());

   fCurrentClass = 0;
}


//______________________________________________________________________________
void THtml::CreateIndexByTopic()
{
// It creates several index files
//
//
// Input: fileNames     - pointer to an array of file names
//        numberOfNames - number of elements in the fileNames array
//        maxLen        - maximum length of a single name
//

   gSystem->ExpandPathName(fOutputDir);

   const char* title = "LibraryDependencies";
   TString filename(title);
   gSystem->PrependPathName(fOutputDir, filename);

   std::ofstream libDepDotFile(filename + ".dot");
   libDepDotFile << "strict digraph G {" << endl
                 << "ratio=auto;" << endl
                 << "rankdir=TB;" << endl
                 << "compound=true;" << endl
                 << "constraint=false;" << endl
                 << "ranksep=1;" << endl
                 << "nodesep=0.3;" << endl
                 << "ratio=compress;" << endl;


   TModuleDocInfo* module = 0;
   TIter iModule(&fModules);
   fModules.Sort();

   std::stringstream sstrCluster;
   std::stringstream sstrDeps;
   while ((module = (TModuleDocInfo*)iModule())) {
      std::vector<std::string> indexChars;
      TString filename(module->GetName());
      filename += "_Index.html";
      gSystem->PrependPathName(fOutputDir, filename);
      ofstream outputFile(filename.Data());
      if (!outputFile.good()) {
         Error("MakeIndex", "Can't open file '%s' !", filename.Data());
         continue;
      }
      Printf(formatStr, "", fCounter.Data(), filename.Data());

      TString htmltitle("Index of ");
      htmltitle += module->GetName();
      htmltitle += " classes";
      WriteHtmlHeader(outputFile, htmltitle);
      outputFile << "<h2>" << htmltitle << "</h2>" << endl;

      std::list<std::string> classNames;
      {
         TIter iClass(module->GetClasses());
         TClassDocInfo* cdi = 0;
         while ((cdi = (TClassDocInfo*) iClass())) {
            classNames.push_back(cdi->GetName());

            TString libs(cdi->GetClass()->GetSharedLibs());
            Ssiz_t posDepLibs = libs.Index(' ');
            TString thisLib(libs);
            if (posDepLibs != kNPOS)
               thisLib.Remove(posDepLibs, thisLib.Length());
            Ssiz_t posExt = thisLib.First('.');
            if (posExt != kNPOS)
               thisLib.Remove(posExt, thisLib.Length());

            if (!thisLib.Length())
               continue;

            // allocate entry, even if no dependencies
            std::set<std::string>& setDep = setLibDeps[thisLib.Data()][module->GetName()];

            if (posDepLibs != kNPOS) {
               std::string lib;
               for(Ssiz_t pos = posDepLibs + 1; libs[pos]; ++pos) {
                  if (libs[pos] == ' ') {
                     if (thisLib.Length() && lib.length()) {
                        size_t posExt = lib.find('.');
                        if (posExt != std::string::npos)
                           lib.erase(posExt);
                        setDep.insert(lib);
                     }
                     lib.erase();
                  } else
                     lib += libs[pos];
               }
               if (lib.length() && thisLib.Length()) {
                  size_t posExt = lib.find('.');
                  if (posExt != std::string::npos)
                     lib.erase(posExt);
                  setDep.insert(lib);
               }
            } // if dependencies
         } // while next class in module
      } // just a scope block

      if (classNames.size() > 10) {
         outputFile << "<div id=\"indxShortX\"><h4>Jump to</h4>" << endl;
         UInt_t numSections = classNames.size() / 10;
         if (numSections < 10) numSections = 10;
         if (numSections > 50) numSections = 50;
         // find index chars
         GetIndexChars(classNames, numSections, indexChars);
         for (UInt_t iIdxEntry = 0; iIdxEntry < indexChars.size(); ++iIdxEntry) {
            outputFile << "<a href=\"#idx" << iIdxEntry << "\">" << indexChars[iIdxEntry]
                       << "</a>" << endl;
         }
         outputFile << "</div><br />" << endl;
      }
      outputFile << "<ul id=\"indx\">" << endl;

      TIter iClass(module->GetClasses());
      TClassDocInfo* cdi = 0;
      UInt_t count = 0;
      UInt_t currentIndexEntry = 0;
      while ((cdi = (TClassDocInfo*) iClass())) {
         TClass *classPtr = cdi->GetClass();
         if (!classPtr) {
            Error("MakeIndex", "Unknown class '%s' !", cdi->GetName());
            continue;
         }

         // write a classname to an index file
         outputFile << "<li class=\"idxl" << (count++)%2 << "\"><tt>";
         if (currentIndexEntry < indexChars.size()
            && !strncmp(indexChars[currentIndexEntry].c_str(), cdi->GetName(),
                        indexChars[currentIndexEntry].length()))
            outputFile << "<a name=\"idx" << currentIndexEntry++ << "\"></a>" << endl;

         TString htmlFile(cdi->GetHtmlFileName());
         if (htmlFile.Length()) {
            outputFile << "<a name=\"";
            outputFile << classPtr->GetName();
            outputFile << "\" href=\"";
            outputFile << htmlFile;
            outputFile << "\">";
            ReplaceSpecialChars(outputFile, classPtr->GetName());
            outputFile << "</a>";
         } else
            ReplaceSpecialChars(outputFile, classPtr->GetName());


         // write title
         outputFile << "</tt><a name=\"Title:";
         outputFile << classPtr->GetName();
         outputFile << "\"></a>";
         ReplaceSpecialChars(outputFile, classPtr->GetTitle());
         outputFile << "</li>" << endl;
      }


      outputFile << "</ul>" << endl;

      // write outputFile footer
      TDatime date;
      WriteHtmlFooter(outputFile, "", date.AsString());
   } // while next module

   // libCint is missing as we don't have class doc for it
   // We need it for dependencies nevertheless, so add it by hand.
   sstrCluster << "subgraph clusterlibCint {" << endl
      << "style=filled;" << endl
      << "color=lightgray;" << endl
      << "label=\"libCint\";" << endl
      << "\"CINT\" [style=filled,color=white,fontsize=10]" << endl
      << "}" << endl;

   for (LibDep_t::iterator iLibDep = setLibDeps.begin(); iLibDep != setLibDeps.end(); ++iLibDep) {
      if (!iLibDep->first.length())
         continue;
      sstrCluster << "subgraph cluster" << iLibDep->first << " {" << endl
         << "style=filled;" << endl
         << "color=lightgray;" << endl
         << "label=\"" << iLibDep->first << "\";" << endl;

      for (std::map<std::string, std::set<std::string> >::iterator iModule = iLibDep->second.begin();
         iModule != iLibDep->second.end(); ++iModule) {
         sstrCluster << "\"" << iModule->first << "\" [style=filled,color=white,URL=\"" 
            << iModule->first << "_Index.html\",fontsize=10];" << endl;

         // GetSharedLib doesn't mention libCore or libCint; add them by hand
         if (iLibDep->first != "libCore")
            sstrDeps << "\"" << iModule->first << "\" -> \"BASE\" [lhead=clusterlibCore];" << endl;
         sstrDeps << "\"" << iModule->first << "\" -> \"CINT\" [lhead=clusterlibCint];" << endl;

         for (std::set<std::string>::iterator iLib = iModule->second.begin();
            iLib != iModule->second.end(); ++iLib) {
            const std::map<std::string, std::set<std::string> >& modDep = setLibDeps[*iLib];
            std::map<std::string, std::set<std::string> >::const_iterator iModDep = modDep.begin();
            const std::string& mod = iModDep->first;
            sstrDeps << "\"" << iModule->first << "\" -> \"" << mod << "\" [lhead=cluster" << *iLib << "];" << endl;
         }
      } // for modules in lib
      sstrCluster << endl
         << "}" << endl;
   } // for libs

   libDepDotFile << sstrCluster.str() << endl
      << sstrDeps.str();
   libDepDotFile << "}" << endl;
   libDepDotFile.close();

   ofstream out(filename + ".html");
   if (!out.good()) {
      Error("CreateIndexByTopic", "Can't open file '%s.html' !",
            filename.Data());
      return;
   }

   Printf(formatStr, "", fCounter.Data(), (filename + ".html").Data());
   // write out header
   WriteHtmlHeader(out, "Library Dependencies");
   out << "<h1>Library Dependencies</h1>" << endl;

   // check for a search engine
   const char *searchEngine =
       gEnv->GetValue("Root.Html.SearchEngine", "");

   // if exists ...
   if (*searchEngine) {

      // create link to search engine page
      out << "<h2><a href=\"" << searchEngine
          << "\">Search the Class Reference Guide</a></h2>" << endl;
   }

   RunDot(filename, &out);

   out << "<img alt=\"Library Dependencies\" class=\"formatsel\" usemap=\"#Map" << title << "\" src=\"" << title << ".gif\"/>" << endl;

   // write out footer
   TDatime date;
   WriteHtmlFooter(out, "", date.AsString());
}


//______________________________________________________________________________
Bool_t THtml::CreateHierarchyDot()
{
// Create a hierarchical class list
// The algorithm descends from the base classes and branches into
// all derived classes. Mixing classes are displayed several times.
//
//

   const char* title = "ClassHierarchy";
   gSystem->ExpandPathName(fOutputDir);
   TString filename(title);
   gSystem->PrependPathName(fOutputDir, filename);

   // open out file
   ofstream dotout(filename + ".dot");

   if (!dotout.good()) {
      Error("CreateHierarchy", "Can't open file '%s.dot' !",
            filename.Data());
      return kFALSE;
   }

   dotout << "digraph G {" << endl
          << "ratio=auto;" << endl
          << "rankdir=RL;" << endl;

   // loop on all classes
   TClassDocInfo* cdi = 0;
   TIter iClass(&fClasses);
   while ((cdi = (TClassDocInfo*)iClass())) {

      TClass *cl = cdi->GetClass();
      if (cl == 0) {
         Warning("THtml::CreateHierarchy", "skipping class %s\n", cdi->GetName());
         continue;
      }

      // Find immediate base classes
      TList *bases = cl->GetListOfBases();
      if (bases && !bases->IsEmpty()) {
         dotout << "\"" << cdi->GetName() << "\" -> { ";
         TIter iBase(bases);
         TBaseClass* base = 0;
         while ((base = (TBaseClass*) iBase())) {
            // write out current class
            if (base != bases->First())
               dotout << "; ";
            dotout << "\"" << base->GetName() << "\"";
         }
         dotout << "};" << endl;
      } else
         // write out current class - no bases
         dotout << "\"" << cdi->GetName() << "\";" << endl;

   }

   dotout << "}";
   dotout.close();

   ofstream out(filename + ".html");
   if (!out.good()) {
      Error("CreateHierarchy", "Can't open file '%s.html' !",
            filename.Data());
      return kFALSE;
   }

   Printf(formatStr, "", fCounter.Data(), (filename + ".html").Data());
   // write out header
   WriteHtmlHeader(out, "Class Hierarchy");
   out << "<h1>Class Hierarchy</h1>" << endl;

   // check for a search engine
   const char *searchEngine =
       gEnv->GetValue("Root.Html.SearchEngine", "");

   // if exists ...
   if (*searchEngine) {

      // create link to search engine page
      out << "<h2><a href=\"" << searchEngine
          << "\">Search the Class Reference Guide</a></h2>" << endl;
   }

   RunDot(filename, &out);

   out << "<img usemap=\"#Map" << title << "\" src=\"" << title << ".gif\"/>" << endl;
   // write out footer
   TDatime date;
   WriteHtmlFooter(out, "", date.AsString());
   return kTRUE;
}

//______________________________________________________________________________
void THtml::CreateHierarchy()
{
// Create a hierarchical class list
// The algorithm descends from the base classes and branches into
// all derived classes. Mixing classes are displayed several times.
//
//

   // if (CreateHierarchyDot()) return;

   gSystem->ExpandPathName(fOutputDir);
   TString filename("ClassHierarchy.html");
   gSystem->PrependPathName(fOutputDir, filename);

   // open out file
   ofstream out(filename);

   if (!out.good()) {
      Error("CreateHierarchy", "Can't open file '%s' !", filename.Data());
      return;
   }

   Printf(formatStr, "", fCounter.Data(), filename.Data());

   // write out header
   WriteHtmlHeader(out, "Class Hierarchy");
   out << "<h1>Class Hierarchy</h1>" << endl;

   // check for a search engine
   const char *searchEngine =
       gEnv->GetValue("Root.Html.SearchEngine", "");

   // if exists ...
   if (*searchEngine) {

      // create link to search engine page
      out << "<h2><a href=\"" << searchEngine
          << "\">Search the Class Reference Guide</a></h2>" << endl;
   }

   // loop on all classes
   TClassDocInfo* cdi = 0;
   TIter iClass(&fClasses);
   while ((cdi = (TClassDocInfo*)iClass())) {

      // get class
      TClass *basePtr = cdi->GetClass();
      if (basePtr == 0) {
         Warning("THtml::CreateHierarchy", "skipping class %s\n", cdi->GetName());
         continue;
      }

      // Find basic base classes
      TList *bases = basePtr->GetListOfBases();
      if (bases && !bases->IsEmpty()) {
         out << "<hr />" << endl;

         out << "<table><tr><td><ul><li><tt>";
         if (cdi->GetHtmlFileName()) {
            out << "<a name=\"" << cdi->GetName() << "\" href=\""
                << cdi->GetHtmlFileName() << "\">";
            ReplaceSpecialChars(out, cdi->GetName());
            out << "</a>";
         } else {
            ReplaceSpecialChars(out, cdi->GetName());
         }

         // find derived classes
         out << "</tt></li></ul></td>";
         fHierarchyLines = 0;
         DescendHierarchy(out,basePtr);

         out << "</tr></table>" << endl;
      }
   }

   // write out footer
   TDatime date;
   WriteHtmlFooter(out, "", date.AsString());
}

//______________________________________________________________________________
void THtml::DescendHierarchy(ofstream & out, TClass* basePtr, Int_t maxLines, Int_t depth)
{
// Descend hierarchy recursively
// loop over all classes and look for classes with base class basePtr

   if (maxLines)
      if (fHierarchyLines >= maxLines) {
         out << "<td></td>" << endl;
         return;
      }

   UInt_t numClasses = 0;

   TClassDocInfo* cdi = 0;
   TIter iClass(&fClasses);
   while ((cdi = (TClassDocInfo*)iClass()) && (!maxLines || fHierarchyLines<maxLines)) {

      TClass *classPtr = cdi->GetClass();
      if (!classPtr) continue;

      // find base classes with same name as basePtr
      TList* bases=classPtr->GetListOfBases();
      if (!bases) continue;

      TBaseClass *inheritFrom=(TBaseClass*)bases->FindObject(basePtr->GetName());
      if (!inheritFrom) continue;

      if (!numClasses)
         out << "<td>&larr;</td><td><table><tr>" << endl;
      else
         out << "</tr><tr>"<<endl;
      fHierarchyLines++;
      numClasses++;
      UInt_t bgcolor=255-depth*8;
      out << "<td bgcolor=\""
          << Form("#%02x%02x%02x", bgcolor, bgcolor, bgcolor)
          << "\">";
      out << "<table><tr><td>" << endl;

      TString htmlFile(cdi->GetHtmlFileName());
      if (htmlFile.Length()) {
         out << "<center><tt><a name=\"" << cdi->GetName() << "\" href=\""
             << htmlFile << "\">";
         ReplaceSpecialChars(out, cdi->GetName());
         out << "</a></tt></center>";
      } else {
         ReplaceSpecialChars(out, cdi->GetName());
      }
      // write title
      // commented out for now because it reduces overview
      /*
        len = strlen(classNames[i]);
        for (Int_t w = 0; w < (maxLen - len + 2); w++)
        out << ".";
        out << " ";

        out << "<a name=\"Title:";
        out << classPtr->GetName();
        out << "\">";
        ReplaceSpecialChars(out, classPtr->GetTitle());
        out << "</a></tt>" << endl;
      */

      out << "</td>" << endl;
      DescendHierarchy(out,classPtr,maxLines, depth+1);
      out << "</tr></table></td>" << endl;

   }  // loop over all classes
   if (numClasses)
      out << "</tr></table></td>" << endl;
   else
      out << "<td></td>" << endl;
}

//______________________________________________________________________________
void THtml::GetModuleName(TString& modulename, const char* filename) const 
{
   // Returns the module a class with filename belongs to.
   // For ROOT, this is determined by MODULE/src/*.cxx or MODULE/inc/*.h. 
   // Math/GenVector (MATHCORE) and Math/Matrix (SMATRIX) get special
   // treatment.
   // All classes not fitting into this layout are assigned to the
   // module USER.

   modulename = filename;
   const char* posSlash = strchr(filename, '/');
   const char *srcdir = 0;
   if (posSlash) {
      // for new ROOT install the impl file name has the form: base/src/TROOT.cxx
      srcdir = strstr(posSlash, "/src/");

      // if impl is unset, check for decl and see if it matches
      // format "base/inc/TROOT.h" - in which case it's not a USER
      // class, but a BASE class.
      if (!srcdir) srcdir=strstr(posSlash, "/inc/");
   } else srcdir = 0;

   if (srcdir && srcdir == posSlash) {
      modulename.Remove(srcdir - filename, modulename.Length());
      modulename.ToUpper();
   } else {
      if (posSlash && !strncmp(posSlash,"/Math/GenVector/", 16))
         modulename = "MATHCORE";
      else if (posSlash && !strncmp(posSlash,"/Math/Matrix", 12))
         modulename = "SMATRIX";
      else
         modulename = "USER_";
   }
}


//______________________________________________________________________________
void THtml::CreateListOfClasses(const char* filter)
{
// Create the list of all known classes

   if (fClassFilter == filter)
      return;

   Info("CreateListOfClasses", "Initializing list of known classes - this might take a while...");
   // get total number of classes
   Int_t totalNumberOfClasses = gClassTable->Classes();

   // allocate memory
   fClasses.Clear();
   fModules.Clear();

   fClassFilter = filter;

   // start from begining
   gClassTable->Init();

   TString reg = filter;
   TRegexp re(reg, kTRUE);

   for (Int_t i = 0; i < totalNumberOfClasses; i++) {

      // get class name
      const char *cname = gClassTable->Next();
      TString s = cname;

      // This is a hack for until after Cint and Reflex are one.
      if (strstr(cname, "__gnu_cxx::")) continue;

      // get class & filename - use TROOT::GetClass, as we also
      // want those classes without decl file name!
      TClass *classPtr = gROOT->GetClass((const char *) cname, kTRUE);
      if (!classPtr) continue;

      TString srcGuess;
      TString hdrGuess;
      const char *impname=GetImplFileName(classPtr);
      if (!impname || !impname[0]) {
         impname = GetDeclFileName(classPtr);
         if (impname && !impname[0]) {
            // no impl, no decl - might be a cintex dict
            // use namespace to decrypt path.
            TString impnameString(cname);
            TObjArray* arrScopes = impnameString.Tokenize("::");

            // for A::B::C, we assume B to be the module, 
            // b/inc/B/C.h the header, and b/src/C.cxx the source.
            TIter iScope(arrScopes, kIterBackward);
            TObjString *osFile   = (TObjString*)iScope();
            TObjString *osModule = 0;
            if (osFile) osModule = (TObjString*)iScope();

            if (osModule) {
               hdrGuess = osModule->String();
               hdrGuess.ToLower();
               hdrGuess += "/inc/";
               hdrGuess += osModule->String();
               hdrGuess += "/";
               hdrGuess += osFile->String();
               hdrGuess += ".h";
               char* realFile = gSystem->Which(fSourceDir, hdrGuess, kReadPermission);
               if (realFile) {
                  delete realFile;
                  fGuessedDeclFileNames[classPtr] = hdrGuess.Data();
                  impname = hdrGuess.Data();

                  // only check for source if we've found the header!
                  srcGuess = osModule->String();
                  srcGuess.ToLower();
                  srcGuess += "/src/";
                  srcGuess += osFile->String();
                  srcGuess += ".cxx";
                  realFile = gSystem->Which(fSourceDir, srcGuess, kReadPermission);
                  if (realFile) {
                     delete realFile;
                     fGuessedImplFileNames[classPtr] = srcGuess.Data();
                     impname = srcGuess.Data();
                  }
               }
            }
            delete arrScopes;
         }
      }

      if (!impname || !impname[0]) {
         cout << "WARNING class " << cname <<
            " has no implementation file name !" << endl;
         continue;
      }
      if (strstr(impname,"prec_stl/")) continue;
      if (strstr(cname, "ROOT::") && !strstr(cname,"Math::")
          && !strstr(cname,"Reflex::") && !strstr(cname,"Cintex::"))
         continue;

      TString htmlfilename;
      GetHtmlFileName(classPtr, htmlfilename);
      TClassDocInfo* cdi = new TClassDocInfo(classPtr, htmlfilename.Data());
      cdi->SetSelected(!(filter && filter[0] && strcmp(filter,"*") && s.Index(re) == kNPOS));
      fClasses.Add(cdi);

      TString modulename;
      GetModuleName(modulename, impname);
      TModuleDocInfo* module = (TModuleDocInfo*) fModules.FindObject(modulename);
      if (!module) {
         module = new TModuleDocInfo(modulename);
         fModules.Add(module);
      }
      if (module) {
         module->AddClass(cdi);
         cdi->SetModule(module);
      }
   }

   fClasses.Sort();

   Info("CreateListOfClasses", "Initializing list of known classes - DONE.");

}


//______________________________________________________________________________
void THtml::CreateListOfTypes()
{
// Create list of all data types

   // open file
   ofstream typesList;

   gSystem->ExpandPathName(fOutputDir);
   char *outFile = gSystem->ConcatFileName(fOutputDir, "ListOfTypes.html");
   typesList.open(outFile, ios::out);


   if (typesList.good()) {
      Printf(formatStr, "", "", outFile);

      // write typesList header
      WriteHtmlHeader(typesList, "List of data types");
      typesList << "<h2> List of data types </h2>" << endl;

      typesList << "<dl><dd>" << endl;

      // make loop on data types
      TDataType *type;
      TIter nextType(gROOT->GetListOfTypes());

      std::list<std::string> typeNames;
      while ((type = (TDataType *) nextType()))
         // no templates ('<' and '>'), no idea why the '(' is in here...
         if (*type->GetTitle() && !strchr(type->GetName(), '(')
             && !( strchr(type->GetName(), '<') && strchr(type->GetName(),'>'))
             && type->GetName())
               typeNames.push_back(type->GetName());
      sort_strlist_stricmp(typeNames);

      std::vector<std::string> indexChars;
      if (typeNames.size() > 10) {
         typesList << "<div id=\"indxShortX\"><h4>Jump to</h4>" << endl;
         // find index chars
         GetIndexChars(typeNames, 10 /*sections*/, indexChars);
         for (UInt_t iIdxEntry = 0; iIdxEntry < indexChars.size(); ++iIdxEntry) {
            typesList << "<a href=\"#idx" << iIdxEntry << "\">" << indexChars[iIdxEntry]
                      << "</a>" << endl;
         }
         typesList << "</div><br />" << endl;
      }

      typesList << "<ul id=\"indx\">" << endl;

      nextType.Reset();
      int idx = 0;
      UInt_t currentIndexEntry = 0;

      for (std::list<std::string>::iterator iTypeName = typeNames.begin();
         iTypeName != typeNames.end(); ++iTypeName) {
         TDataType* type = gROOT->GetType(iTypeName->c_str(), kFALSE);
         typesList << "<li class=\"idxl" << idx%2 << "\">";
         if (currentIndexEntry < indexChars.size()
            && !strncmp(indexChars[currentIndexEntry].c_str(), iTypeName->c_str(),
                        indexChars[currentIndexEntry].length()))
            typesList << "<a name=\"idx" << currentIndexEntry++ << "\"></a>" << endl;
         typesList << "<a name=\"";
         ReplaceSpecialChars(typesList, iTypeName->c_str());
         typesList << "\"><tt>";
         ReplaceSpecialChars(typesList, iTypeName->c_str());
         typesList << "</tt></a>";
         typesList << "<a name=\"Title:";
         ReplaceSpecialChars(typesList, type->GetTitle());
         typesList << "\"></a>";
         ReplaceSpecialChars(typesList, type->GetTitle());
         typesList << "</li>" << endl;
         ++idx;
      }
      typesList << "</ul>" << endl;

      // write typesList footer
      TDatime date;
      WriteHtmlFooter(typesList, "", date.AsString());

      // close file
      typesList.close();

   } else
      Error("Make", "Can't open file '%s' !", outFile);

   if (outFile)
      delete[]outFile;
}

//______________________________________________________________________________
void THtml::CreateJavascript() {
   // Write the default ROOT style sheet.

   // open file
   ofstream js;

   gSystem->ExpandPathName(fOutputDir);
   char *outFile = gSystem->ConcatFileName(fOutputDir, "ROOT.js");
   js.open(outFile, ios::out);
   if (!js.good()) {
      delete []outFile;
      return;
   }
   js << "function SetCSSValue(where,what,to){" << endl
      << "   var r='cssRules';" << endl
      << "   if(document.all && navigator.appName.indexOf('Opera')==-1)" << endl
      << "      r='rules';" << endl
      << "   var i;" << endl
      << "   for(i=0;i<document.styleSheets.length;++i) {" << endl
      << "      var cssrules=document.styleSheets[i][r];" << endl
      << "      for(j=0;j<cssrules.length;++j)" << endl
      << "         if(cssrules[j].selectorText.toUpperCase()==where.toUpperCase()) {" << endl
      << "            cssrules[j].style[what]=to;" << endl
      << "            return;" << endl
      << "         }" << endl
      << "   }" << endl
      << "}" << endl
      << "var elements=new Array('dispoptCBInh.checked','dispoptCBPub.checked');" << endl
      << "function SetValuesFromCookie() {" << endl
      << "   var i;" << endl
      << "   var arrcookie=document.cookie.split(\";\");" << endl
      << "   for(i=0; i<arrcookie.length; ++i) {" << endl
      << "      while(arrcookie[i].charAt(0)==' ') " << endl
      << "         arrcookie[i]=arrcookie[i].substring(1,arrcookie[i].length);" << endl
      << "      if (arrcookie[i].indexOf(\"ROOT\")==0) {" << endl
      << "         var arrval=arrcookie[i].substring(5).split(':');" << endl
      << "         for (i=0; i<arrval.length; ++i) {" << endl
      << "            var posdelim=elements[i].indexOf(\".\");" << endl
      << "            var what=elements[i].substring(0,posdelim);" << endl
      << "            var mem =elements[i].substring(posdelim+1);" << endl
      << "            var val=arrval[i];" << endl
      << "            if (val=='false') val=false;" << endl
      << "            else if (val=='true') val=true;" << endl
      << "            var el=document.getElementById(what);" << endl
      << "            if (!el) return;" << endl
      << "            el[mem]=val;" << endl
      << "            CBChanged(el);" << endl
      << "         }" << endl
      << "         return;" << endl
      << "      }" << endl
      << "   }" << endl
      << "}" << endl
      << "function UpdateCookie() {" << endl
      << "   var cookietxt=\"ROOT=\";" << endl
      << "   var i;" << endl
      << "   for (i=0; i<elements.length; ++i) {" << endl
      << "      var posdelim=elements[i].indexOf(\".\");" << endl
      << "      var what=elements[i].substring(0,posdelim);" << endl
      << "      var mem =elements[i].substring(posdelim+1);" << endl
      << "      var val=document.getElementById(what)[mem];" << endl
      << "      if (i>0) cookietxt+=':';" << endl
      << "      cookietxt+=val;" << endl
      << "   }" << endl
      << "   var ayear=new Date();" << endl
      << "   ayear.setTime(ayear.getTime()+31536000000);" << endl
      << "   cookietxt+=\";path=/;expires=\"+ayear.toUTCString();" << endl
      << "   document.cookie=cookietxt;" << endl
      << "}" << endl
      << "function CBChanged(cb){" << endl
      << "   if(cb.id=='dispoptCBInh') {" << endl
      << "      SetCSSValue('tr.funcinh','display',cb.checked?'':'none');" << endl
      << "      SetCSSValue('tr.datainh','display',cb.checked?'':'none');" << endl
      << "   } else if(cb.id=='dispoptCBPub') {" << endl
      << "      SetCSSValue('#funcprot','display',cb.checked?'':'none');" << endl
      << "      SetCSSValue('#funcpriv','display',cb.checked?'':'none');" << endl
      << "      SetCSSValue('#dataprot','display',cb.checked?'':'none');" << endl
      << "      SetCSSValue('#datapriv','display',cb.checked?'':'none');" << endl
      << "      SetCSSValue('#enumprot','display',cb.checked?'':'none');" << endl
      << "      SetCSSValue('#enumpriv','display',cb.checked?'':'none');" << endl
      << "   }" << endl
      << "   UpdateCookie();" << endl
      << "}" << endl
      << "function SetImg(name, file) {" << std::endl
      << "   var img=document.getElementById(name);" << std::endl
      << "   var src=img.src;" << std::endl
      << "   var posFile=src.lastIndexOf('/');" << std::endl
      << "   var numSlashes=file.split('/').length - 1;" << std::endl
      << "   for (var i=0; i<numSlashes; i++)" << std::endl
      << "     posFile=src.lastIndexOf('/',posFile - 1);" << std::endl
      << "   var oldFile=src.substr(posFile+1);" << std::endl
      << "   src=src.substr(0,posFile+1);" << std::endl
      << "   src+=file;" << std::endl
      << "   img.src=src;" << std::endl
      << "   if (img.useMap) {" << std::endl
      << "      var usemapFile=file;" << std::endl
      << "      var posUsemapExt=usemapFile.lastIndexOf('.');" << std::endl
      << "      if (posUsemapExt!=-1) usemapFile=usemapFile.substr(0,posUsemapExt);" << std::endl
      << "      var posUsemapDir=usemapFile.lastIndexOf('/');" << std::endl
      << "      if (posUsemapDir!=-1) usemapFile=usemapFile.substr(posUsemapDir+1);" << std::endl
      << "      img.useMap=\"#Map\"+usemapFile;" << std::endl
      << "   }" << std::endl
      << "   var posExt=oldFile.lastIndexOf('.');" << std::endl
      << "   oldFile=oldFile.substr(0,posExt);" << std::endl
      << "   var posDir=oldFile.lastIndexOf('/');" << std::endl
      << "   if (posDir!=-1) oldFile=oldFile.substr(posDir + 1);" << std::endl
      << "   document.getElementById(\"img\"+oldFile).className=\"imgformattab\";" << std::endl
      << "   posExt=file.lastIndexOf('.');" << std::endl
      << "   file=file.substr(0,posExt);" << std::endl
      << "   posDir=file.lastIndexOf('/');" << std::endl
      << "   if (posDir!=-1) file=file.substr(posDir + 1);" << std::endl
      << "   document.getElementById(\"img\"+file).className=\"imgformattabsel\";" << std::endl
      << "}" << std::endl;

   delete []outFile;
}


//______________________________________________________________________________
void THtml::CreateStyleSheet() {
   // Write the default ROOT style sheet.

   // open file
   ofstream styleSheet;

   gSystem->ExpandPathName(fOutputDir);
   char *outFile = gSystem->ConcatFileName(fOutputDir, "ROOT.css");
   styleSheet.open(outFile, ios::out);
   if (styleSheet.good()) {
      styleSheet
         << "a {" << std::endl
         << "   text-decoration: none;" << std::endl
         << "   font-weight: bolder;" << std::endl
         << "}" << std::endl
         << "a:link {" << std::endl
         << "   color: #0000ff;" << std::endl
         << "   background-color: inherit;" << std::endl
         << "   text-decoration: none;" << std::endl
         << "}" << std::endl
         << "a:visited {" << std::endl
         << "   color: #5500cc;" << std::endl
         << "   background-color: inherit;" << std::endl
         << "}" << std::endl
         << "a:active {" << std::endl
         << "   color: #551a8b;" << std::endl
         << "   background-color: inherit;" << std::endl
         << "   border: dotted 1px #0000ff;" << std::endl
         << "}" << std::endl
         << "a:hover {" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: #eeeeff;" << std::endl
         << "}" << std::endl
         << "#indx {" << std::endl
         << "  list-style:   none;" << std::endl
         << "  padding-left: 0em;" << std::endl
         << "  margin-left:  0em;" << std::endl
         << "}" << std::endl
         << "#indx li {" << std::endl
         << "  margin-top:    1px;" << std::endl
         << "  margin-bottom: 1px;" << std::endl
         << "  margin-left:   0px;" << std::endl
         << "  padding-left:  2em;" << std::endl
         << "  padding-bottom: 0.3em;" << std::endl
         << "  border-top:    0px hidden #afafaf;" << std::endl
         << "  border-left:   0px hidden #afafaf;" << std::endl
         << "  border-bottom: 1px hidden #ffffff;" << std::endl
         << "  border-right:  1px hidden #ffffff;" << std::endl
         << "}" << std::endl
         << "#indx li:hover {" << std::endl
         << "  border-top:    1px solid #afafaf;" << std::endl
         << "  border-left:   1px solid #afafaf;" << std::endl
         << "  border-bottom: 0px solid #ffffff;" << std::endl
         << "  border-right:  0px solid #ffffff;" << std::endl
         << "}" << std::endl
         << "#indx li.idxl0 {" << std::endl
         << "  color: inherit;" << std::endl
         << "  background-color: #e7e7ff;" << std::endl
         << "}" << std::endl
         << "#indx a {" << std::endl
         << "  font-weight: bold;" << std::endl
         << "  display:     block;" << std::endl
         << "  margin-left: -1em;" << std::endl
         << "}" << std::endl
         << "#indxShortX {" << std::endl
         << "  border: 3px solid gray;" << std::endl
         << "  padding: 8pt;" << std::endl
         << "  margin-left: 2em;" << std::endl
         << "}" << std::endl
         << "#indxShortX h4 {" << std::endl
         << "  margin-top: 0em;" << std::endl
         << "  margin-bottom: 0.5em;" << std::endl
         << "}" << std::endl
         << "#indxShortX a {" << std::endl
         << "  margin-right: 0.25em;" << std::endl
         << "  margin-left: 0.25em;" << std::endl
         << "}" << std::endl
         << "#indxModules {" << std::endl
         << "  border: 3px solid gray;" << std::endl
         << "  padding: 8pt;" << std::endl
         << "  margin-left: 2em;" << std::endl
         << "}" << std::endl
         << "#indxModules h4 {" << std::endl
         << "  margin-top: 0em;" << std::endl
         << "  margin-bottom: 0.5em;" << std::endl
         << "}" << std::endl
         << "#indxModules a {" << std::endl
         << "  margin-right: 0.25em;" << std::endl
         << "  margin-left: 0.25em;" << std::endl
         << "}" << std::endl
         << "#searchform {" << std::endl
         << "  margin-left: 2em;" << std::endl
         << "}" << std::endl
         << "" << std::endl
         << "div.funcdoc {" << std::endl
         << "   width: 97%;" << std::endl
         << "   border-bottom: solid 3px #cccccc;" << std::endl
         << "   border-left: solid 1px #cccccc;" << std::endl
         << "   margin-bottom: 1em;" << std::endl
         << "   margin-left: 0.3em;" << std::endl
         << "   padding-left: 1em;" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: White;" << std::endl
         << "}" << std::endl
         << "span.funcname {" << std::endl
         << "   margin-left: -0.7em;" << std::endl
         << "   font-weight: bolder;" << std::endl
         << "}" << std::endl
         << "span.comment {" << std::endl
         << "   background-color: #eeeeee;" << std::endl
         << "   color: Green;" << std::endl
         << "   font-weight: normal;" << std::endl
         << "}" << std::endl
         << "span.keyword {" << std::endl
         << "   color: Black;" << std::endl
         << "   background-color: inherit;" << std::endl
         << "   font-weight: normal;" << std::endl
         << "}" << std::endl
         << "span.cpp {" << std::endl
         << "   color: Gray;" << std::endl
         << "   background-color: inherit;" << std::endl
         << "   font-weight: normal;" << std::endl
         << "}" << std::endl
         << "span.string {" << std::endl
         << "   color: Teal;" << std::endl
         << "   background-color: inherit;" << std::endl
         << "   font-weight: normal;" << std::endl
         << "}" << std::endl
         << "pre.code {" << std::endl
         << "   font-weight: bolder;" << std::endl
         << "}" << std::endl
         << "div.classdescr {" << std::endl
         << "   width: 97%;" << std::endl
         << "   margin-left: 0.3em;" << std::endl
         << "   padding-left: 1em;" << std::endl
         << "   margin-bottom: 2em;" << std::endl
         << "   border-bottom: solid 3px #cccccc;" << std::endl
         << "   border-left: solid 1px #cccccc;" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: White;" << std::endl
         << "}" << std::endl
         << "div.inlinecode {" << std::endl
         << "   margin-bottom: 0.5em;" << std::endl
         << "   padding: 0.5em;" << std::endl
         << "}" << std::endl
         << "code.inlinecode {" << std::endl
         << "   padding: 0.5em;" << std::endl
         << "   border: solid 1px #ffff77;" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: #ffffdd;" << std::endl
         << "}" << std::endl
         << "body {" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: #fcfcfc;" << std::endl
         << "}" << std::endl
         << "table.inhtree {" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: White;" << std::endl
         << "   border: solid 1px Black;" << std::endl
         << "   width: 97%;" << std::endl
         << "}" << std::endl
         << "table.libinfo {" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: White;" << std::endl
         << "   padding: 2px;" << std::endl
         << "   border: solid 1px Gray; " << std::endl
         << "   float: right;" << std::endl
         << "}" << std::endl
         << "#functions {" << std::endl
         << "   margin-top: 4em;" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: White;" << std::endl
         << "}" << std::endl
         << "#datamembers {" << std::endl
         << "   margin-top: 4em;" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: White;" << std::endl
         << "}" << std::endl
         << "div.access {" << std::endl
         << "   border-left: solid 3pt black;" << std::endl
         << "   padding-left: 1em;" << std::endl
         << "   margin-left: 1em;" << std::endl
         << "   margin-bottom: 1em;" << std::endl
         << "}" << std::endl
         << "#funcpubl {" << std::endl
         << "   border-left-color: #77ff77;" << std::endl
         << "}" << std::endl
         << "#funcprot {" << std::endl
         << "   border-left-color: #ffff00;" << std::endl
         << "}" << std::endl
         << "#funcpriv {" << std::endl
         << "   border-left-color: #ff7777;" << std::endl
         << "}" << std::endl
         << "#datapubl {" << std::endl
         << "   border-left-color: #77ff77;" << std::endl
         << "}" << std::endl
         << "#dataprot {" << std::endl
         << "   border-left-color: #ffff00;" << std::endl
         << "}" << std::endl
         << "#datapriv {" << std::endl
         << "   border-left-color: #ff7777;" << std::endl
         << "}" << std::endl
         << "#enumpubl {" << std::endl
         << "   border-left-color: #77ff77;" << std::endl
         << "}" << std::endl
         << "#enumprot {" << std::endl
         << "   border-left-color: #ffff00;" << std::endl
         << "}" << std::endl
         << "#enumpriv {" << std::endl
         << "   border-left-color: #ff7777;" << std::endl
         << "}" << std::endl
         << "tr.func {" << std::endl
         << "   white-space: nowrap;" << std::endl
         << "}" << std::endl
         << "tr.data {" << std::endl
         << "   white-space: nowrap;" << std::endl
         << "}" << std::endl
         << "tr.funcinh {" << std::endl
         << "   display: none;" << std::endl
         << "   white-space: nowrap;" << std::endl
         << "}" << std::endl
         << "tr.datainh {" << std::endl
         << "   display: none;" << std::endl
         << "   white-space: nowrap;" << std::endl
         << "}" << std::endl
         << "span.baseclass {" << std::endl
         << "   font-size:x-small;" << std::endl
         << "}" << std::endl
         << "td.funcret {" << std::endl
         << "   float: right;" << std::endl
         << "   padding-right: 0.5em;" << std::endl
         << "}" << std::endl
         << "td.funcname {" << std::endl
         << "   font-weight: bolder;" << std::endl
         << "   white-space: normal;" << std::endl
         << "   text-indent: -2em;" << std::endl
         << "   padding-left: 2em;" << std::endl
         << "}" << std::endl
         << "td.datatype {" << std::endl
         << "   float: right;" << std::endl
         << "   padding-right: 0.5em;" << std::endl
         << "}" << std::endl
         << "td.dataname {" << std::endl
         << "   font-weight: bolder;" << std::endl
         << "   vertical-align: top;" << std::endl
         << "}" << std::endl
         << "td.datadesc {" << std::endl
         << "   font-style: italic;" << std::endl
         << "   padding-left: 0.5em;" << std::endl
         << "   white-space: normal;" << std::endl
         << "}" << std::endl
         << "#dispopt {" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: White;" << std::endl
         << "   padding: 2px;" << std::endl
         << "   border: solid 1px Gray; " << std::endl
         << "   float: right;" << std::endl
         << "   position: relative;" << std::endl
         << "   top: -5em;" << std::endl
         << "   z-index: 2;" << std::endl
         << "}" << std::endl
         << "#formdispopt {" << std::endl
         << "   margin: 0px 0px 0px 0px;" << std::endl
         << "}" << std::endl
         << "div.imgformattabs {" << std::endl
         << "   padding-left: 1em;" << std::endl
         << "}" << std::endl
         << "div.formatsel {" << std::endl
         << "   border: solid 2px Black;" << std::endl
         << "   margin-right: auto;" << std::endl
         << "   display: table;" << std::endl
         << "}" << std::endl
         << "div.formatselwidth {" << std::endl
         << "   width: 28em;" << std::endl
         << "   height: 0pt;" << std::endl
         << "}" << std::endl
         << "img.formatsel {" << std::endl
         << "   display: block;" << std::endl
         << "   border: none;" << std::endl
         << "   margin-left: auto;" << std::endl
         << "   margin-right: auto;" << std::endl
         << "}" << std::endl
         << "a.imgformattab {" << std::endl
         << "   border-top: solid 1px Gray;" << std::endl
         << "   border-left: solid 1px Gray;" << std::endl
         << "   border-right: solid 1px Gray;" << std::endl
         << "   border-bottom: solid 0px Black;" << std::endl
         << "   color: #777777;" << std::endl
         << "   background-color: #dddddd;" << std::endl
         << "   padding: 0px 0.4em 1px 0.4em;" << std::endl
         << "   position: relative;" << std::endl
         << "   top: +0px;" << std::endl
         << "}" << std::endl
         << "* html a.imgformattab { /* IE only, fix pos bug */" << std::endl
         << "   top: -1px;" << std::endl
         << "}" << std::endl
         << "a.imgformattab:hover {" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: White;" << std::endl
         << "}" << std::endl
         << "a.imgformattabsel {" << std::endl
         << "   border-top: solid 2px Black;" << std::endl
         << "   border-left: solid 2px Black;" << std::endl
         << "   border-right: solid 2px Black;" << std::endl
         << "   border-bottom: solid 3px White;" << std::endl
         << "   padding: 2px 0.4em 1px 0.4em;" << std::endl
         << "   position: relative;" << std::endl
         << "   top: 0px;" << std::endl
         << "   background-color: White;" << std::endl
         << "   color: Black;" << std::endl
         << "}" << std::endl
         << "* html a.imgformattabsel { /* IE only, fix pos bug */" << std::endl
         << "   top: -1px;" << std::endl
         << "}" << std::endl
         << "a.imgformattabsel:hover {" << std::endl
         << "   color: inherit;" << std::endl
         << "   background-color: White;" << std::endl
         << "}" << std::endl
         << "a.imgformattabsel:active {" << std::endl
         << "   color: Black;" << std::endl
         << "   background-color: inherit;" << std::endl
         << "   border-left: solid 2px Black;" << std::endl
         << "   border-right: solid 2px Black;" << std::endl
         << "   border-top: solid 2px Black;" << std::endl
         << "   border-bottom: solid 3px White;" << std::endl
         << "}" << std::endl;
   }
   delete []outFile;
}


//______________________________________________________________________________
void THtml::ExpandKeywords(ostream & out, const char *text)
{
   // Expand keywords in text, writing to out.
   TString str(text);
   ExpandKeywords(str);
   out << str;
}

//______________________________________________________________________________
void THtml::ExpandKeywords(TString& keyword)
{
   // Find keywords in keyword and create URLs around them. Escape characters with a 
   // special meaning for HTML. Protect "Begin_Html"/"End_Html" pairs, and set the
   // parsing context. Evaluate sequences like a::b->c.

   static Bool_t pre_is_open = kFALSE;
   // we set parse context to kCComment even for CPP comment, and note it here:
   Bool_t commentIsCPP = kFALSE;
   std::list<TClass*> currentType;

   enum {
      kNada,
      kMember,
      kScope,
      kNumAccesses
   } scoping = kNada;

   Ssiz_t i;
   currentType.push_back(0);
   for (i = 0; i < keyword.Length(); ++i) {
      if (!currentType.back())
         scoping = kNada;

      // skip until start of the word
      if (fParseContext.back() == kCode || fParseContext.back() == kCComment) {
         if (currentType.back())
            switch (keyword[i]) {
               case ':':
                  if (keyword[i + 1] == ':') {
                     scoping = kScope;
                     i += 1;
                     continue;
                  }
                  break;
               case '-':
                  if (keyword[i + 1] == '>') {
                     scoping = kMember;
                     i += 1;
                     continue;
                  }
                  break;
               case '.':
                  if (keyword[i + 1] != '.') {
                     // prevent "..."
                     scoping = kMember;
                     continue;
                  }
                  break;
            }
         switch (keyword[i]) {
            case '(':
               currentType.push_back(0);
               scoping = kNada;
               continue;
               break;
            case ')':
               if (currentType.size() > 1)
                  currentType.pop_back();
               scoping = kMember;
               continue;
               break;
         }
         if (i >= keyword.Length())
            break;
      } else // code or comment
         currentType.back() = 0;

      if (!IsWord(keyword[i])){
         if (fParseContext.back() != kBeginEndHtml && fParseContext.back() != kBeginEndHtmlInCComment) {
            Bool_t haveHtmlEscapedChar = fParseContext.back() == kString && i > 2 &&
               keyword[i] == '\'' && keyword[i-1] == ';';
            if (haveHtmlEscapedChar) {
               Ssiz_t posBegin = i - 2;
               while (posBegin > 0 && IsWord(keyword[posBegin]))
                  --posBegin;
               haveHtmlEscapedChar = posBegin > 0 &&
                  keyword[posBegin] == '&' && keyword[posBegin - 1] == '\'';
            }
            Bool_t closeString = fParseContext.back() == kString &&
               ( keyword[i] == '"' || keyword[i] == '\''
                 && (i > 1 && keyword[i - 2] == '\''
                 || i > 2 && keyword[i - 2] == '\\' && (i > 1 && keyword[i - 3] == '\''))
                 || haveHtmlEscapedChar);
            if (!fEscFlag)
               if (fParseContext.back() == kCode || fParseContext.back() == kCComment)
                  if (keyword.Length() > i + 1 && keyword[i] == '"' ||
                     keyword[i] == '\'' && (
                        // 'a'
                        keyword.Length() > i + 2 && keyword[i + 2] == '\'' ||
                        // '\a'
                        keyword.Length() > i + 3 && keyword[i + 1] == '\'' && keyword[i + 3] == '\'')) {
                     keyword.Insert(i, "<span class=\"string\">");
                     i += 21;
                     fParseContext.push_back(kString);
                     currentType.back() = 0;
                     closeString = kFALSE;
                  } else if (fParseContext.back() != kCComment &&
                     keyword.Length() > i + 1 && keyword[i] == '/' &&
                     (keyword[i+1] == '/' || keyword[i+1] == '*')) {
                     fParseContext.push_back(kCComment);
                     commentIsCPP = keyword[i+1] == '/';
                     currentType.back() = 0;
                     keyword.Insert(i, "<span class=\"comment\">");
                     i += 23;
                  } else if (fParseContext.back() == kCComment && !commentIsCPP
                     && keyword.Length() > i + 1
                     && keyword[i] == '*' && keyword[i+1] == '/') {
                     if (fParseContext.size()>1)
                        fParseContext.pop_back();

                     currentType.back() = 0;
                     keyword.Insert(i + 2, "</span>");
                     i += 9;
                  }

            ReplaceSpecialChars(keyword, i);
            if (closeString) {
               keyword.Insert(i, "</span>");
               i += 7;
               if (fParseContext.size()>1)
                  fParseContext.pop_back();

               currentType.back() = 0;
            }
            --i; // i already moved by ReplaceSpecialChar
         } else
            // protect html code from special chars
            if ((unsigned char)keyword[i]>31)
               if (keyword[i] == '<'){
                  if (!strncasecmp(keyword.Data() + i, "<pre>", 5)){
                     if (pre_is_open) {
                        keyword.Remove(i, 5);
                        continue;
                     } else {
                        pre_is_open = kTRUE;
                        i += 5;
                     }
                     currentType.back() = 0;
                  } else
                     if (!strncasecmp(keyword.Data() + i,"</pre>", 6)) {
                        if (!pre_is_open) {
                           keyword.Remove(i, 6);
                           continue;
                        } else {
                           pre_is_open = kFALSE;
                           i += 6;
                        }
                        currentType.back() = 0;
                     }
               } // i = '<'
         continue;
      } // not a word

      // get end of the word
      Ssiz_t endWord = i;
      while (endWord < keyword.Length() && IsName(keyword[endWord]))
         endWord++;

      if (fParseContext.back() != kCode && fParseContext.back() != kCComment &&
         fParseContext.back() != kBeginEndHtml && fParseContext.back() != kBeginEndHtmlInCComment) {
         // don't replace in strings, cpp, etc
         i = endWord - 1;
         continue;
      }

      TString word(keyword(i, endWord - i));

      // check if this is a HTML block
      if (fParseContext.back() == kBeginEndHtml || fParseContext.back() == kBeginEndHtmlInCComment) {
         if (!word.CompareTo("end_html", TString::kIgnoreCase) &&
            (i == 0 || keyword[i - 1] != '\"')) {
            if (fParseContext.back() == kBeginEndHtmlInCComment)
               commentIsCPP = kFALSE;
            else {
               commentIsCPP = kTRUE;
               // special case; we're skipping the "//" recognition inside BeginEndHtml,
               // but here, we need it so this line is added to the doc
               if (keyword.BeginsWith("//")) {
                  keyword.Prepend("<span class=\"comment\">");
                  i += 22;
               }
            }
            if (fParseContext.size()>1)
               fParseContext.pop_back();
            if (fParseContext.back() != kCComment)
               fParseContext.push_back(kCComment);
            pre_is_open = kTRUE;
            keyword.Replace(i, word.Length(), "<pre>");
            i += 4;
         }
         // we're in a begin/end_html block, just keep what we have
         currentType.back() = 0;
         continue;
      }
      if (fParseContext.back() == kCComment
         && !word.CompareTo("begin_html", TString::kIgnoreCase)
         && (i == 0 || keyword[i - 1] != '\"')) {
         if (commentIsCPP)
            fParseContext.push_back(kBeginEndHtml);
         else
            fParseContext.push_back(kBeginEndHtmlInCComment);
         pre_is_open = kFALSE;
         keyword.Replace(i, word.Length(), "</pre>");
         i += 5;
         currentType.back() = 0;
         continue;
      }

      // don't replace keywords in comments
      if (fParseContext.back() == kCode &&
         fgKeywords.find(word.Data()) != fgKeywords.end()) {
         keyword.Insert(i, "<span class=\"keyword\">");
         i += 22 + word.Length();
         keyword.Insert(i, "</span>");
         i += 7 - 1; // -1 for ++i
         currentType.back() = 0;
         continue;
      }

      // generic layout:
      // A::B::C::member[arr]->othermember
      // we iterate through this, first scope is A, and currentType will be set toA,
      // next we see ::B, "::" signals to use currentType,...

      TDataType* subType = 0;
      TClass* subClass = 0;
      TDataMember *datamem = 0;
      TMethod *meth = 0;
      const char* globalTypeName = 0;
      if (!currentType.size()) {
         Warning("ExpandKeywords", "type context is empty!");
         currentType.push_back(0);
      }
      TClass* lookupScope = currentType.back();
      Bool_t describe = kFALSE;
      TString description;

      if (!lookupScope && scoping == kNada)
         lookupScope = fCurrentClass;

      if (scoping == kNada) {
         subType = gROOT->GetType(word);
         if (!subType)
            subClass = GetClass(word);
         if (!subType && !subClass) {
            TGlobal *global = gROOT->GetGlobal(word);
            if (global) {
               // cannot doc globals; take at least their type...
               globalTypeName = global->GetTypeName();
               subClass = GetClass(globalTypeName);
               if (!subClass)
                  subType = gROOT->GetType(globalTypeName);
               else // hack to prevent current THtml obj from showing up - we only want gHtml
                  if (subClass == THtml::Class() && word != "gHtml")
                     subClass = 0;
               describe = (subClass || subType);
            }
         }
         if (!subType && !subClass) {
            // too bad - cannot doc yet...
            //TFunction *globFunc = gROOT->GetGlobalFunctionWithPrototype(word);
            //globFunc = 0;
         }
      }

      if (lookupScope && !subType && !subClass) {
         if (scoping == kScope) {
            TString subClassName(lookupScope->GetName());
            subClassName += "::";
            subClassName += word;
            subClass = GetClass(subClassName);
         }
         if (!subClass) {
            // also try A::B::c()
            datamem = lookupScope->GetDataMember(word);
            if (!datamem)
               meth = lookupScope->GetMethodAllAny(word);
            describe = (datamem || meth);
         }
      }
      // create the link
      TString link;
      if (subType) {
         link = "./ListOfTypes.html";
         link += "#";
         TString mangledWord;
         if (!globalTypeName)
            mangledWord = word;
         else
            mangledWord = globalTypeName;
         if (describe)
            description = mangledWord;
         NameSpace2FileName(mangledWord);
         link += mangledWord;
         currentType.back() = 0;
      } else if (subClass) {
         GetHtmlFileName(subClass, link);
         if (link.Length() && !link.BeginsWith("http://") && !link.BeginsWith("https://"))
            link.Prepend("./");
         currentType.back() = subClass;
         if (describe)
            description = subClass->GetName();
      } else if (datamem || meth) {
         TClass* scope = datamem ? datamem->GetClass() : meth->GetClass();
         GetHtmlFileName(scope, link);
         if (link.Length()) {
            if (!link.BeginsWith("http://") && !link.BeginsWith("https://"))
               link.Prepend("./");
            link += "#";
            TString mangledName(scope->GetName());
            NameSpace2FileName(mangledName);
            link += mangledName;
            link += ":";
            if (datamem) {
               mangledName = datamem->GetName();
               if (datamem->GetTypeName())
                  currentType.back() = GetClass(datamem->GetTypeName());
               if (describe) {
                  description = datamem->GetFullTypeName();
                  description += " ";
                  if (datamem->GetClass()) {
                     description += datamem->GetClass()->GetName();
                     description += "::";
                  }
                  description += datamem->GetName();
               }
            } else {
               mangledName = meth->GetName();
               TString retTypeName = meth->GetReturnTypeName();
               if (retTypeName.BeginsWith("const "))
                  retTypeName.Remove(0,6);
               Ssiz_t pos=0;
               while (IsWord(retTypeName[pos])) ++pos;
               retTypeName.Remove(pos, retTypeName.Length());
               if (retTypeName.Length())
                  currentType.back() = GetClass(retTypeName);
               if (describe && meth->GetClass()) {
                  TIter iMeth(meth->GetClass()->GetListOfMethods());
                  TMethod* mCand = 0;
                  description = "";
                  while ((mCand = (TMethod*)iMeth()))
                     if (!strcmp(mCand->GetName(), meth->GetName())) {
                        if (description.Length()) {
                           description += " or overloads";
                           break;
                        }
                        description = mCand->GetPrototype();
                     }
               }
            }
            NameSpace2FileName(mangledName);
            link += mangledName;
         }
      } else
         currentType.back() = 0;

      if (link.Length()) {
         link.Prepend("<a href=\"");
         link += "\"";
         if (description.Length()) {
            link += " title=\"";
            Ssiz_t pos = 0;
            while (pos < description.Length())
               ReplaceSpecialChars(description, pos);
            link += description;
            link += "\"";
         }
         link += ">";
         keyword.Insert(i, link);
         i += link.Length();
      }
      TString mangledWord(word);
      Ssiz_t posReplace = 0;
      ReplaceSpecialChars(mangledWord, posReplace);
      keyword.Replace(i, word.Length(), mangledWord);
      i += mangledWord.Length();
      if (link.Length()) {
         keyword.Insert(i, "</a>");
         i += 4;
      }

      --i; // due to ++i
   } // while i < keyword.Length()

   // clean up, no strings across lines
   if (fParseContext.back() == kString) {
      keyword += "</span>";
      i += 7;
      if (fParseContext.size()>1)
         fParseContext.pop_back();
      currentType.back() = 0;
   }
   // clean up, no CPP comment across lines
   if (commentIsCPP) {
      keyword += "</span>";
      i += 7;
      if (fParseContext.back() == kCComment) {// and not BeginEndHtml
         if (fParseContext.size()>1)
            fParseContext.pop_back();
      }
      currentType.back() = 0;
   }

}


//______________________________________________________________________________
void THtml::ExpandPpLine(ostream & out)
{
// Expand preprocessor statements
//
//
// Input: out  - output file stream
//
//  NOTE: Looks for the #include statements and
//        creates link to the corresponding file
//        if such file exists
//

   const char *ptr;
   const char *ptrStart;
   const char *ptrEnd;
   char *fileName;

   Bool_t linkExist = kFALSE;

   ptrEnd = strstr(fLine.Data(), "include");
   if (ptrEnd) {
      ptrEnd += 7;
      if ((ptrStart = strpbrk(ptrEnd, "<\""))) {
         ptrStart++;
         ptrEnd = strpbrk(ptrStart, ">\"");
         if (ptrEnd) {
            Int_t len = ptrEnd - ptrStart;
            fileName = new char[len + 1];
            strncpy(fileName, ptrStart, len);
            fileName[len]=0;
            char *tmpstr =
                gSystem->Which(fSourceDir, fileName, kReadPermission);
            if (tmpstr) {
               char *realFileName = StrDup(tmpstr);

               if (realFileName) {
                  CopyHtmlFile(realFileName);

                  ptr = fLine.Data();
                  while (ptr < ptrStart)
                     ReplaceSpecialChars(out, *ptr++);
                  out << "<a href=\"../" << GetFileName(realFileName) <<
                      "\">";
                  out << fileName << "</a>";
                  out << ptrEnd;

                  linkExist = kTRUE;
               }
               if (realFileName)
                  delete[]realFileName;
               if (fileName)
                  delete[]fileName;
               delete[]tmpstr;
            }
         }
      }
   }

   if (!linkExist)
      ReplaceSpecialChars(out, fLine);
}

//______________________________________________________________________________
const char *THtml::GetFileName(const char *filename)
{
// It discards any directory information inside filename
//
//
//  Input: filename - pointer to the file name
//
// Output: pointer to the string containing just a file name
//         without any other directory information, i.e.
//         '/usr/root/test.dat' will return 'test.dat'
//

   if (!filename || !filename[0]) return "";
   return gSystem->BaseName(filename);
}

//______________________________________________________________________________
void THtml::GetSourceFileName(TString& filename)
{
   // Find the source file. If filename contains a path it will be used
   // together with the possible source prefix. If not found we try
   // old algorithm, by stripping off the path and trying to find it in the
   // specified source search path.

   TString found(filename);

   if (strchr(filename, '/')
#ifdef WIN32
   || strchr(filename, '\\')
#endif
   ){
      TString found(fSourcePrefix);
      if (found.Length())
         gSystem->PrependPathName(found, filename);
      gSystem->FindFile(fSourceDir, filename, kReadPermission);
      if (filename.Length())
         return;
   }

   filename = GetFileName(filename);
   if (filename.Length())
      gSystem->FindFile(fSourceDir, filename, kReadPermission);
}

//______________________________________________________________________________
void THtml::GetHtmlFileName(TClass * classPtr, TString& filename)
{
// Return real HTML filename
//
//
//  Input: classPtr - pointer to a class
//         filename - string containing a full name
//         of the corresponding HTML file after the function returns. 
//

   filename.Remove(0);
   if (!classPtr) return;

   const char* cFilename = GetImplFileName(classPtr);
   if (!cFilename || !cFilename[0])
      cFilename = GetDeclFileName(classPtr);

   // classes without Impl/DeclFileName don't have docs,
   // and classes without docs don't have output file names
   if (!cFilename || !cFilename[0])
      return;

   // this should be a prefix
   TString varName("Root.Html.");

   const char *colon = strchr(cFilename, ':');
   if (colon)
      // old version, where source file name is prepended by "TAG:"
      varName += TString(cFilename, colon - cFilename);
   else
      // New version, check class's libname.
      // If libname is dir/libMyLib.so, check Root.Html.MyLib
      // If libname is myOtherLib.so.2.3, check Root.Html.myOtherLib
      // (i.e. remove directories, "lib" prefix, and any "extension")
      if (classPtr->GetSharedLibs()) {
         // first one is the class's lib
         TString libname(classPtr->GetSharedLibs());
         Ssiz_t posSpace = libname.First(' ');
         if (posSpace != kNPOS)
            libname.Remove(posSpace, libname.Length());
         TString libnameBase = gSystem->BaseName(libname);
         if (libnameBase.BeginsWith("lib"))
            libnameBase.Remove(0, 3);
         Ssiz_t posExt = libnameBase.First('.');
         if (posExt != '.')
            libnameBase.Remove(posExt, libnameBase.Length());
         if (libnameBase.Length())
            varName += libnameBase;
         else
            varName += "Root";
      } else
         varName += "Root";

   filename = cFilename;
   TString htmlFileName;
   if (!filename.Length() ||
      !gSystem->FindFile(fSourceDir, filename, kReadPermission)) {
      htmlFileName = gEnv->GetValue(varName, "");
      if ((!htmlFileName || !htmlFileName[0]) && varName != "Root.Html.Root")
         htmlFileName = gEnv->GetValue("Root.Html.Root", "");
   } else
      htmlFileName = "./";

   if (htmlFileName.Length()) {
      filename = htmlFileName;
      TString className(classPtr->GetName());
      NameSpace2FileName(className);
      gSystem->PrependPathName(filename, className);
      filename = className;
      filename.ReplaceAll("\\", "/");
      filename += ".html";
   } else filename.Remove(0);
}

//______________________________________________________________________________
TClass *THtml::GetClass(const char *name1)
{
//*-*-*-*-*Return pointer to class with name*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*      =================================
   if(!name1 || !name1[0]) return 0;
   // no doc for internal classes
   if (strstr(name1,"ROOT::")==name1) {
      Bool_t ret = kTRUE;
      if (strstr(name1,"Math::"))   ret = kFALSE;
      if (strstr(name1,"Reflex::")) ret = kFALSE;
      if (strstr(name1,"Cintex::")) ret = kFALSE;
      if (ret) return 0;
   }

   TClassDocInfo* cdi = (TClassDocInfo*)fClasses.FindObject(name1);
   if (!cdi) return 0;
   TClass *cl=cdi->GetClass();
   // hack to get rid of prec_stl types
   // TClassEdit checks are far too slow...
   /*
   if (cl && GetDeclFileName(cl) &&
       strstr(GetDeclFileName(cl),"prec_stl/"))
      cl = 0;
   */
   if (cl && GetDeclFileName(cl) && GetDeclFileName(cl)[0])
      return cl;
   return 0;
}

//______________________________________________________________________________
const char* THtml::GetDeclFileName(TClass * cl) const
{
   // Return declaration file name
   std::map<TClass*,std::string>::const_iterator iClDecl = fGuessedDeclFileNames.find(cl);
   if (iClDecl == fGuessedDeclFileNames.end()) return cl->GetDeclFileName();
   return iClDecl->second.c_str();
}

//______________________________________________________________________________
const char* THtml::GetImplFileName(TClass * cl) const
{
   // Return implementation file name
   std::map<TClass*,std::string>::const_iterator iClImpl = fGuessedImplFileNames.find(cl);
   if (iClImpl == fGuessedImplFileNames.end()) return cl->GetImplFileName();
   return iClImpl->second.c_str();
}

//______________________________________________________________________________
Bool_t THtml::IsModified(TClass * classPtr, const Int_t type)
{
// Check if file is modified
//
//
//  Input: classPtr - pointer to the class
//         type     - file type to compare with
//                    values: kSource, kInclude, kTree
//
// Output: TRUE     - if file is modified since last time
//         FALSE    - if file is up to date
//

   TString sourceFile;
   TString classname(classPtr->GetName());
   TString filename;
   TString dir;

   switch (type) {
   case kSource:
      if (classPtr->GetImplFileLine()) {
         sourceFile = GetImplFileName(classPtr);
         GetSourceFileName(sourceFile);
      } else {
         sourceFile = GetDeclFileName(classPtr);
         GetSourceFileName(sourceFile);
      }
      dir = "src";
      gSystem->ExpandPathName(fOutputDir);
      gSystem->PrependPathName(fOutputDir, dir);
      filename = classname;
      NameSpace2FileName(filename);
      gSystem->PrependPathName(dir, filename);
      if (classPtr->GetImplFileLine())
         filename += ".cxx.html";
      else
         filename += ".h.html";
      break;

   case kInclude:
      filename = GetDeclFileName(classPtr);
      sourceFile = filename;
      GetSourceFileName(sourceFile);
      filename = GetFileName(filename);
      gSystem->ExpandPathName(fOutputDir);
      gSystem->PrependPathName(fOutputDir, filename);
      break;

   case kTree:
      sourceFile = GetDeclFileName(classPtr);
      GetSourceFileName(sourceFile);
      NameSpace2FileName(classname);
      gSystem->ExpandPathName(fOutputDir);
      gSystem->PrependPathName(fOutputDir, classname);
      filename = classname;
      filename += "_Tree.pdf";
      break;

   default:
      Error("IsModified", "Unknown file type !");
   }

   // Get info about a file
   Long64_t size;
   Long_t id, flags, sModtime, dModtime;

   if (!(gSystem->GetPathInfo(sourceFile, &id, &size, &flags, &sModtime)))
      if (!(gSystem->GetPathInfo(filename, &id, &size, &flags, &dModtime)))
         return (sModtime > dModtime);

   return kTRUE;
}


//______________________________________________________________________________
Bool_t THtml::IsName(UChar_t c)
{
// Check if c is a valid C++ name character
//
//
//  Input: c - a single character
//
// Output: TRUE if c is a valid C++ name character
//         and FALSE if it's not.
//
//   NOTE: Valid name characters are [a..zA..Z0..9_~],
//

   Bool_t ret = kFALSE;

   if (isalnum(c) || c == '_' || c == '~')
      ret = kTRUE;

   return ret;
}


//______________________________________________________________________________
Bool_t THtml::IsWord(UChar_t c)
{
// Check if c is a valid first character for C++ name
//
//
//  Input: c - a single character
//
// Output: TRUE if c is a valid first character for C++ name,
//         and FALSE if it's not.
//
//   NOTE: Valid first characters are [a..zA..Z_~]
//

   Bool_t ret = kFALSE;

   if (isalpha(c) || c == '_' || c == '~')
      ret = kTRUE;

   return ret;
}


//______________________________________________________________________________
void THtml::MakeAll(Bool_t force, const char *filter)
{
// Produce documentation for all the classes specified in the filter (by default "*")
// To process all classes having a name starting with XX, do:
//        html.MakeAll(kFALSE,"XX*");
// If force=kFALSE (default), only the classes that have been modified since
// the previous call to this function will be generated.
// If force=kTRUE, all classes passing the filter will be processed.
//

   MakeIndex(filter);

   // CreateListOfClasses(filter); already done by MakeIndex
   TClassDocInfo* classinfo = 0;
   TIter iClassInfo(&fClasses);
   UInt_t count = 0;

   while ((classinfo = (TClassDocInfo*)iClassInfo())) {
      if (!classinfo->IsSelected())
         continue;
      fCounter.Form("%5d", fClasses.GetSize() - count++);
      MakeClass(classinfo, force);
   }

   fCounter.Remove(0);
}


//______________________________________________________________________________
void THtml::MakeClass(const char *className, Bool_t force)
{
// Make HTML files for a single class
//
//
// Input: className - name of the class to process
//
   if (!fClasses.GetSize())
      CreateListOfClasses("*");

   TClassDocInfo* cdi = (TClassDocInfo*)fClasses.FindObject(className);
   if (!cdi) {
      if (!TClassEdit::IsStdClass(className)) // stl classes won't be available, so no warning
         Error("MakeClass", "Unknown class '%s' !", className);
      return;
   }

   MakeClass(cdi, force);
}

//______________________________________________________________________________
void THtml::MakeClass(void *cdi_void, Bool_t force)
{
// Make HTML files for a single class
//
//
// Input: cdi - doc info for class to process
//
   if (!fClasses.GetSize())
      CreateListOfClasses("*");

   TClassDocInfo* cdi = (TClassDocInfo*) cdi_void;
   fCurrentClass = cdi->GetClass();

   if (!fCurrentClass) {
      if (!TClassEdit::IsStdClass(cdi->GetName())) // stl classes won't be available, so no warning
         Error("MakeClass", "Unknown class '%s' !", cdi->GetName());
      return;
   }
   TString htmlFile(cdi->GetHtmlFileName());
   if (htmlFile.Length()
       && (htmlFile.BeginsWith("http://")
           || htmlFile.BeginsWith("https://")
           || gSystem->IsAbsoluteFileName(htmlFile))
       ) {
      htmlFile.Remove(0);
      //printf("CASE skipped, class=%s, htmlFile=%s\n",className,htmlFile);
   }
   if (htmlFile.Length()) {
      Class2Html(force);
      MakeTree(cdi->GetName(), force);
   } else
      Printf(formatStr, "-skipped-", fCounter.Data(), cdi->GetName());
}


//______________________________________________________________________________
void THtml::MakeIndex(const char *filter)
{
   // It makes an index files
   // by default makes an index of all classes (if filter="*")
   // To generate an index for all classes starting with "XX", do
   //    html.MakeIndex("XX*");

   CreateListOfClasses(filter);
   CreateListOfTypes();

   // create an index
   CreateIndexByTopic();
   CreateIndex();

   // create a class hierarchy
   CreateHierarchy();
}


//______________________________________________________________________________
void THtml::MakeTree(const char *className, Bool_t force)
{
// Make an inheritance tree
//
//
// Input: className - name of the class to process
//

   // create canvas & set fill color
   Bool_t wasBatch = gROOT->IsBatch();
   if (!wasBatch)
      gROOT->SetBatch();
   TVirtualPad *psCanvas = (TVirtualPad*)gROOT->ProcessLineFast("new TCanvas(\"R__THtml\",\"psCanvas\",0,0,1000,1200);");
   if (!wasBatch)
      gROOT->SetBatch(kFALSE);

   if (!psCanvas) {
      Error("MakeTree", "Cannot create a TCanvas!");
      return;
   }


   TClass *classPtr = GetClass(className);

   if (classPtr) {

      TString htmlFile;
      GetHtmlFileName(classPtr, htmlFile);
      if (htmlFile.Length()
          && (htmlFile.BeginsWith("http://")
              || htmlFile.BeginsWith("https://")
              || gSystem->IsAbsoluteFileName(htmlFile))
          ) {
         htmlFile.Remove(0);
      }
      if (!HaveDot())
         // class tree only if no dot, otherwise it's part of charts
         if (htmlFile.Length()) {
            // make a class tree
            ClassTree(psCanvas, classPtr, force);
            htmlFile.Remove(0);
         } else
            Printf(formatStr, "-skipped-", "", className);

   } else
      Error("MakeTree", "Unknown class '%s' !", className);

   // close canvas
   psCanvas->Close();
   delete psCanvas;

}


//______________________________________________________________________________
void THtml::ReplaceSpecialChars(ostream & out, const char c)
{
// Replace ampersand, less-than and greater-than character, writing to out.
//
//
// Input: out - output file stream
//        c   - single character
//
   static TString s;
   s = c;
   Ssiz_t pos = 0;
   ReplaceSpecialChars(s, pos);
   out << s.Data();
}

//______________________________________________________________________________
void THtml::ReplaceSpecialChars(TString& text, Ssiz_t &pos)
{
// Replace ampersand, less-than and greater-than character
//
//
// Input: text - text where replacement will happen,
//        pos  - index of char to be replaced; will point to next char to be 
//               replaced when function returns
//

   const char c = text[pos];
   if (fEscFlag) {
      fEscFlag = kFALSE;
      ++pos;
      return;
   } else if (c == fEsc) {
      // text.Remove(pos, 1); - NO! we want to keep it nevertheless!
      fEscFlag = kTRUE;
      return;
   }
   switch (c) {
      case '<':
         text.Replace(pos, 1, "&lt;");
         pos += 3;
         break;
      case '&':
         text.Replace(pos, 1, "&amp;");
         pos += 4;
         break;
      case '>':
         text.Replace(pos, 1, "&gt;");
         pos += 3;
         break;
   }
   ++pos;
}


//______________________________________________________________________________
void THtml::ReplaceSpecialChars(ostream & out, const char *string)
{
// Replace ampersand, less-than and greater-than characters, writing to out
//
//
// Input: out    - output file stream
//        string - pointer to an array of characters
//

   while (string && *string) {
      ReplaceSpecialChars(out, *string);
      string++;
   }
}


//______________________________________________________________________________
void THtml::SetDeclFileName(TClass* cl, const char* filename)
{
   // Explicitly set a decl file name for TClass cl.
   fGuessedDeclFileNames[cl] = filename;
}

//______________________________________________________________________________
void THtml::SetImplFileName(TClass* cl, const char* filename)
{
   // Explicitly set a impl file name for TClass cl.
   fGuessedImplFileNames[cl] = filename;
}


//______________________________________________________________________________
void THtml::SortNames(const char **strings, Int_t num, Bool_t type)
{
// Sort strings
//
//
// Input: strings - pointer to an array of strings
//        type    - sort type
//                  values : kCaseInsensitive, kCaseSensitive
//                  default: kCaseInsensitive
//

   if (type == kCaseSensitive)
      qsort(strings, num, sizeof(strings), CaseSensitiveSort);
   else
      qsort(strings, num, sizeof(strings), CaseInsensitiveSort);
}


//______________________________________________________________________________
char *THtml::StrDup(const char *s1, Int_t n)
{
// Returns a pointer to a new string which is a duplicate
// of the string to which 's1' points.  The space for the
// new string is obtained using the 'new' operator. The new
// string has the length of 'strlen(s1) + n'.


   char *str = 0;

   if (s1) {
      if (n < 0)
         n = 0;
      str = new char[strlen(s1) + n + 1];
      if (str)
         strcpy(str, s1);
   }

   return (str);
}

//______________________________________________________________________________
Bool_t THtml::Strip(TString& str) {
   // strips ' ' and tabs from both sides of str
   Bool_t changed = str[0] == ' ' || str[0] == '\t';
   changed |= str[str.Length()] == ' ' || str[str.Length()] == '\t';
   if (!changed) return kFALSE;
   Ssiz_t i = 0;
   while (str[i] == ' ' || str[i] == '\t')
      ++i;
   str.Remove(0,i);
   i = str.Length() - 1;
   while (i >= 0 && (str[i] == ' ' || str[i] == '\t'))
      --i;
   str.Remove(i + 1, str.Length());
   return kTRUE;
}

//______________________________________________________________________________
void THtml::WriteHtmlHeader(ofstream & out, const char *title, 
                            const char* dir /*=""*/, TClass *cls/*=0*/)
{
// Write HTML header
//
//
// Input: out   - output file stream
//        title - title for the HTML page
//        cls   - current class
//        dir   - relative directory to reach the top 
//                ("" for html doc, "../" for src/*cxx.html etc)
//
// evaluates the Root.Html.Header setting:
// * if not set, the standard header is written. (ROOT)
// * if set, and ends with a "+", the standard header is written and this file 
//   included afterwards. (ROOT, USER)
// * if set but doesn't end on "+" the file specified will be written instead 
//   of the standard header (USER)
//
// Any occurrence of "%TITLE%" (without the quotation marks) in the user 
// provided header file will be replaced by the value of this method's
// parameter "title" before written to the output file. %CLASS% is replaced by
// the class name ("" if not a class), %INCFILE% by the header file name as
// given by TClass::GetDeclFileName() and %SRCFILE% by the source file name as
// given by TClass::GetImplFileName() (both "" if not a class).

   const char *addHeader = gEnv->GetValue("Root.Html.Header", "");
   const char *charset = gEnv->GetValue("Root.Html.Charset", "ISO-8859-1");

   // standard header output if Root.Html.Header is not set, or it's set and it ends with a "+".
   if (addHeader
       && (strlen(addHeader) == 0
           || addHeader[strlen(addHeader) - 1] == '+')) {
      TDatime date;
      out << "<?xml version=\"1.0\"?>" << endl;
      out << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"" << endl
          << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" << endl;
      out << "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">" << endl;
      out << "<!--                                             -->" <<
          endl;
      out << "<!-- Author: ROOT team (rootdev@pcroot.cern.ch)  -->" <<
          endl;
      out << "<!--                                             -->" <<
          endl;
      out << "<!--   Date: " << date.AsString() << "            -->" << endl;
      out << "<!--                                             -->" <<
          endl;
      out << "<head>" << endl;
      out << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" <<
          charset << "\" />" <<
          endl;
      out << "<title>";
      ReplaceSpecialChars(out, title);
      out << "</title>" << endl;
      out << "<meta name=\"rating\" content=\"General\" />" << endl;
      out << "<meta name=\"objecttype\" content=\"Manual\" />" << endl;
      out <<
          "<meta name=\"keywords\" content=\"software development, oo, object oriented, ";
      out << "unix, x11, windows, c++, html, rene brun, fons rademakers, cern\" />"
          << endl;
      out <<
          "<meta name=\"description\" content=\"ROOT - An Object Oriented Framework For Large Scale Data Analysis.\" />"
          << endl;
      out << "<link rel=\"stylesheet\" type=\"text/css\" href=\"" << dir << "ROOT.css\" id=\"ROOTstyle\" />" << endl;
      out << "<script type=\"text/javascript\" src=\"" << dir << "ROOT.js\"></script>" << endl;
      out << "</head>" << endl;

      out << "<body  onload=\"javascript:SetValuesFromCookie();\">" << endl;
   };
   // do we have an additional header?
   if (addHeader && strlen(addHeader) > 0) {
      ifstream addHeaderFile;
      char *addHeaderTmp = StrDup(addHeader);
      if (addHeaderTmp[strlen(addHeaderTmp) - 1] == '+')
         addHeaderTmp[strlen(addHeaderTmp) - 1] = 0;
      addHeaderFile.open(addHeaderTmp, ios::in);

      if (addHeaderFile.good()) {
         while (!addHeaderFile.eof()) {

            fLine.ReadLine(addHeaderFile, kFALSE);
            if (addHeaderFile.eof())
               break;

            if (fLine) {
               TString txt(fLine);
               txt.ReplaceAll("%TITLE%", title);
               txt.ReplaceAll("%CLASS%", cls?cls->GetName():"");
               txt.ReplaceAll("%INCFILE%", cls?GetDeclFileName(cls):"");
               txt.ReplaceAll("%SRCFILE%", cls?GetImplFileName(cls):"");
               out << txt << endl;
            }
         }
      } else
         Warning("THtml::WriteHtmlHeader",
                 "Can't open user html header file %s\n", addHeaderTmp);


      if (addHeaderTmp)
         delete[]addHeaderTmp;
   }

   out << "<a name=\"TopOfPage\"></a>" << endl;
}


//______________________________________________________________________________
void THtml::WriteHtmlFooter(ofstream & out, const char *dir,
                            const char *lastUpdate, const char *author,
                            const char *copyright)
{
// Write HTML footer
//
//
// Input: out        - output file stream
//        dir        - usually equal to "" or "../", depends of
//                     current file directory position, i.e. if
//                     file is in the fOutputDir, then dir will be ""
//        lastUpdate - last update string
//        author     - author's name
//        copyright  - copyright note
//
// Allows optional user provided footer to be written. Root.Html.Footer holds 
// the file name for this footer. For details see THtml::WriteHtmlHeader (here,
// the "+" means the user's footer is written in front of Root's!) Occurences 
// of %AUTHOR%, %UPDATE%, and %COPYRIGHT% in the user's file are replaced by 
// their corresponding values (author, lastUpdate, and copyright) before 
// written to out.

   out << endl;

   const char* templateSITags[kNumSourceInfos] = { "%UPDATE%", "%AUTHOR%", "%COPYRIGHT%"};
   const char *addFooter = gEnv->GetValue("Root.Html.Footer", "");
   // standard footer output if Root.Html.Footer is not set, or it's set and it ends with a "+".
   // do we have an additional footer?
   if (addFooter && strlen(addFooter) > 0) {
      ifstream addFooterFile;
      char *addFooterTmp = StrDup(addFooter);
      if (addFooterTmp[strlen(addFooterTmp) - 1] == '+')
         addFooterTmp[strlen(addFooterTmp) - 1] = 0;
      addFooterFile.open(addFooterTmp, ios::in);

      if (addFooterFile.good()) {
         while (!addFooterFile.eof()) {

            fLine.ReadLine(addFooterFile, kFALSE);
            if (addFooterFile.eof())
               break;

            if (fLine) {
               for (Int_t siTag = 0; siTag < (Int_t) kNumSourceInfos; ++siTag) {
                  Ssiz_t siPos = fLine.Index(templateSITags[siTag]);
                  if (siPos != kNPOS)
                     fLine.Replace(siPos, strlen(templateSITags[siTag]), fSourceInfo[siTag]);
               }
               out << fLine;
            }
         }
      } else
         Warning("THtml::WriteHtmlFooter",
                 "Can't open user html footer file %s\n", addFooterTmp);


      if (addFooterTmp)
         delete[]addFooterTmp;
   }

   if (addFooter
       && (strlen(addFooter) == 0
           || addFooter[strlen(addFooter) - 1] == '+')) {
      if (*author || *lastUpdate || *copyright)
         out << "<br />" << endl;

      out << "<!--SIGNATURE-->" << endl;

      // get the author( s )
      if (*author) {

         out << "<em>Author: ";

         char *auth = StrDup(author);

         char *name = strtok(auth, ",");

         Bool_t firstAuthor = kTRUE;

         /* now we have two options. name is a comma separated list of tokens
            either in the format
            (i) "FirstName LastName " or
            (ii) "FirstName LastName <link> "
            The first one generates an XWho link (CERN compatible),
            the second a http link (WORLD compatible), e.g.
            <mailto:user@host.bla> or <http://www.host.bla/page>.
          */

         do {
            char *ptr = name;
            // do we have a link for the current name?
            char *cLink = 0;

            // remove leading spaces
            while (*ptr && isspace((UChar_t)*ptr))
               ptr++;

            if (!firstAuthor)
               out << ", ";

            if (!strncmp(ptr, "Nicolas", 7)) {
               out << "<a href=http://pcbrun.cern.ch/nicolas/index.html";
               ptr += 12;
            } else {
               cLink = strchr(ptr, '<');        // look for link start tag
               if (cLink) {
                  out << "<a href=\"";
                  ptr = cLink-1;
                  for (cLink++; *cLink != 0 && *cLink != '>'; cLink++)
                     if (*cLink != ' ')
                        out << *cLink;
               } else {
                  out << "<a href=\"" << GetXwho();
                  while (*ptr && !cLink) {
                     // Valery's specific case
                     if (!strncmp(ptr, "Valery", 6)) {
                        out << "Valeri";
                        ptr += 6;
                     } else if (!strncmp(ptr, "Fine", 4)) {
                        out << "Faine";
                        ptr += 4;
                     }
                     while (*ptr && !isspace((UChar_t)*ptr))
                        out << *ptr++;

                     if (isspace((UChar_t)*ptr)) {
                        while (*ptr && isspace((UChar_t)*ptr))
                           ptr++;
                        if (isalpha(*ptr))
                           out << '+';
                        else
                           break;
                     } else
                        break;
                  }
               }
            }
            out << "\">";

            char *cCurrentPos = name;
            // remove blanks in front of and behind the name
            while (*cCurrentPos == ' ')
               cCurrentPos++;
            Bool_t bBlank = kFALSE;
            for (; cCurrentPos != ptr && *cCurrentPos != 0; cCurrentPos++) {
               if (*cCurrentPos != ' ') {
                  if (bBlank) {
                     out << ' ';
                     bBlank = kFALSE;
                  }
                  out << *cCurrentPos;
               } else
                  bBlank = kTRUE;
            }
            out << "</a>";
            while (ptr && *ptr==' ') ptr++;
            if (ptr && *ptr=='<') {
               // skip link
               while (*ptr && *ptr!='>') ptr++;
               if (ptr && *ptr=='>') ptr++;
            }
            while (ptr && *ptr==' ') ptr++;
            if (ptr && *ptr)
               out << ' ' << ptr;

            firstAuthor = kFALSE;
            name += strlen(name) + 1;

         } while ((name - auth) < (int) strlen(author)
                  && (name = strtok(name, ",")));
         out << "</em><br />" << endl;
         delete[]auth;
      }

      if (*lastUpdate)
         out << "<em>Last update: " << lastUpdate << "</em><br />" << endl;
      if (*copyright)
         out << "<em>Copyright " << copyright << "</em><br />" << endl;


      // this is a menu
      out << "<br />" << endl;
      out << "<hr />" << endl;
      out << "<center>" << endl;
      out << "<address>" << endl;

      // link to the ROOT home page
      out <<
          "<a href=\"http://root.cern.ch/root/Welcome.html\">ROOT page</a> - ";

      // link to the user home page( if exist )
      const char *userHomePage = gEnv->GetValue("Root.Html.HomePage", "");
      if (*userHomePage) {
         out << "<a href=\"";
         if (*dir) {
            if (strncmp(userHomePage, "http://", 7)
                && strncmp(userHomePage, "https://", 8)
                && !gSystem->IsAbsoluteFileName(userHomePage))
               out << dir;
         }
         out << userHomePage;
         out << "\">Home page</a> - ";
      }
      // link to the index file
      out << "<a href=\"";
      if (*dir)
         out << dir;
      out << "ClassIndex.html\">Class index</a> - ";

      // link to the hierarchy file
      out << "<a href=\"";
      if (*dir)
         out << dir;
      out << "ClassHierarchy.html\">Class Hierarchy</a> - ";

      // link to the top of the page
      out << "<a href=\"#TopOfPage\">Top of the page</a><br />" << endl;
      out << "</address>" << endl;

      out << "</center>" << endl;

      out << "<hr />" << endl;
      out << "<em>" << endl;
      out << "This page has been automatically generated. If you have any comments or suggestions ";
      out <<
          "about the page layout send a mail to <a href=\"mailto:rootdev@root.cern.ch\">ROOT support</a>, or ";
      out <<
          "contact <a href=\"mailto:rootdev@root.cern.ch\">the developers</a> with any questions or problems regarding ROOT."
          << endl;
      out << "</em>" << endl;
      out << "</body>" << endl;
      out << "</html>" << endl;
   }
}

//______________________________________________________________________________
void THtml::NameSpace2FileName(TString& name)
{
   // Replace "::" in name by "__"
   // Replace "<", ">", " ", ",", "~", "=" in name by "_"
   const char* replaceWhat = ":<> ,~=";
   for (Ssiz_t i=0; i < name.Length(); ++i)
      if (strchr(replaceWhat, name[i]))
         name[i] = '_';
}


ROOT page - Class index - Class Hierarchy - Top of the page

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.