Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TDocOutput.cxx
Go to the documentation of this file.
1 // @(#)root/html:$Id: 7ff9b72609794c66acf6b369c4eeddfbfc63cf55 $
2 // Author: Axel Naumann 2007-01-09
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "TDocOutput.h"
13 
14 #include "Riostream.h"
15 #include "TClassDocOutput.h"
16 #include "TClassEdit.h"
17 #include "TDataMember.h"
18 #include "TDataType.h"
19 #include "TDocInfo.h"
20 #include "TDocParser.h"
21 #include "TEnv.h"
22 #include "THtml.h"
23 #include "TInterpreter.h"
24 #include "TMethod.h"
25 #include "TPRegexp.h"
26 #include "TROOT.h"
27 #include "TSystem.h"
28 #include "TUrl.h"
29 #include "TVirtualMutex.h"
30 #include "TVirtualPad.h"
31 #include "TVirtualViewer3D.h"
32 #include <vector>
33 #include <list>
34 #include <set>
35 #include <sstream>
36 #include <stdlib.h>
37 
38 namespace {
39 
40  typedef std::vector<std::string> Words_t;
41  typedef Words_t::const_iterator SectionStart_t;
42 
43  class TSectionInfo {
44  public:
45  TSectionInfo(SectionStart_t start, size_t chars, size_t size):
46  fStart(start), fChars(chars), fSize(size) {};
47 
48  SectionStart_t fStart;
49  size_t fChars;
50  size_t fSize;
51  };
52  typedef std::list<TSectionInfo> SectionStarts_t;
53 
54  static void Sections_BuildIndex(SectionStarts_t& sectionStarts,
55  SectionStart_t begin, SectionStart_t end,
56  size_t maxPerSection)
57  {
58  // for each assumed section border, check that previous entry's
59  // char[selectionChar] differs, else move section start forward
60 
61  SectionStart_t cursor = begin;
62  if (sectionStarts.empty() || sectionStarts.back().fStart != cursor)
63  sectionStarts.push_back(TSectionInfo(cursor, 1, 0));
64 
65  SectionStarts_t::iterator prevSection = sectionStarts.end();
66  --prevSection;
67 
68  while (cursor != end) {
69  size_t numLeft = end - cursor;
70  size_t assumedNumSections = (numLeft + maxPerSection - 1 ) / maxPerSection;
71  size_t step = ((numLeft + assumedNumSections - 1) / assumedNumSections);
72  if (!step || step >= numLeft) return;
73  cursor += step;
74  if (cursor == end) break;
75 
76  SectionStart_t addWhichOne = prevSection->fStart;
77 
78  size_t selectionChar=1;
79  for (; selectionChar <= cursor->length() && addWhichOne == prevSection->fStart;
80  ++selectionChar) {
81  SectionStart_t checkPrev = cursor;
82  while (--checkPrev != prevSection->fStart
83  && !strncasecmp(checkPrev->c_str(), cursor->c_str(), selectionChar)) { }
84 
85  SectionStart_t checkNext = cursor;
86  while (++checkNext != end
87  && !strncasecmp(checkNext->c_str(), cursor->c_str(), selectionChar)) { }
88 
89  // if the previous matching one is closer but not previous section start, take it!
90  if (checkPrev != prevSection->fStart) {
91  if ((cursor - checkPrev) <= (checkNext - cursor))
92  addWhichOne = ++checkPrev;
93  else if (checkNext != end
94  && (size_t)(checkNext - cursor) < maxPerSection) {
95  addWhichOne = checkNext;
96  }
97  }
98  }
99  if (addWhichOne == prevSection->fStart)
100  addWhichOne = cursor;
101 
102  selectionChar = 1;
103  while (selectionChar <= prevSection->fStart->length()
104  && selectionChar <= addWhichOne->length()
105  && !strncasecmp(prevSection->fStart->c_str(), addWhichOne->c_str(), selectionChar))
106  ++selectionChar;
107 
108  sectionStarts.push_back(TSectionInfo(addWhichOne, selectionChar, 0));
109  cursor = addWhichOne;
110  ++prevSection;
111  } // while cursor != end
112  }
113 
114  static void Sections_SetSize(SectionStarts_t& sectionStarts, const Words_t &words)
115  {
116  // Update the length of the sections
117  for (SectionStarts_t::iterator iSectionStart = sectionStarts.begin();
118  iSectionStart != sectionStarts.end(); ++iSectionStart) {
119  SectionStarts_t::iterator next = iSectionStart;
120  ++next;
121  if (next == sectionStarts.end()) {
122  iSectionStart->fSize = (words.end() - iSectionStart->fStart);
123  break;
124  }
125  iSectionStart->fSize = (next->fStart - iSectionStart->fStart);
126  }
127  }
128 
129  static void Sections_PostMerge(SectionStarts_t& sectionStarts, const size_t maxPerSection)
130  {
131  // Merge sections that ended up being too small, up to maxPerSection entries
132  for (SectionStarts_t::iterator iSectionStart = sectionStarts.begin();
133  iSectionStart != sectionStarts.end();) {
134  SectionStarts_t::iterator iNextSectionStart = iSectionStart;
135  ++iNextSectionStart;
136  if (iNextSectionStart == sectionStarts.end()) break;
137  if (iNextSectionStart->fSize + iSectionStart->fSize < maxPerSection) {
138  iSectionStart->fSize += iNextSectionStart->fSize;
139  sectionStarts.erase(iNextSectionStart);
140  } else ++iSectionStart;
141  }
142  }
143 
144  static void GetIndexChars(const Words_t& words, UInt_t numSectionsIn,
145  std::vector<std::string> &sectionMarkersOut)
146  {
147  // Given a list of words (class names, in this case), this function builds an
148  // optimal set of about numSectionIn sections (even if almost all words start
149  // with a "T"...), and returns the significant characters for each section start
150  // in sectionMarkersOut.
151 
152  const size_t maxPerSection = (words.size() + numSectionsIn - 1)/ numSectionsIn;
153  SectionStarts_t sectionStarts;
154  Sections_BuildIndex(sectionStarts, words.begin(), words.end(), maxPerSection);
155  Sections_SetSize(sectionStarts, words);
156  Sections_PostMerge(sectionStarts, maxPerSection);
157 
158  // convert to index markers
159  sectionMarkersOut.clear();
160  sectionMarkersOut.resize(sectionStarts.size());
161  size_t idx = 0;
162  for (SectionStarts_t::iterator iSectionStart = sectionStarts.begin();
163  iSectionStart != sectionStarts.end(); ++iSectionStart)
164  sectionMarkersOut[idx++] =
165  iSectionStart->fStart->substr(0, iSectionStart->fChars);
166  }
167 
168  static void GetIndexChars(const std::list<std::string>& wordsIn, UInt_t numSectionsIn,
169  std::vector<std::string> &sectionMarkersOut)
170  {
171  // initialize word vector
172  Words_t words(wordsIn.size());
173  size_t idx = 0;
174  for (std::list<std::string>::const_iterator iWord = wordsIn.begin(); iWord != wordsIn.end(); ++iWord)
175  words[idx++] = *iWord;
176  GetIndexChars(words, numSectionsIn, sectionMarkersOut);
177  }
178 
179 }
180 
181 extern "C" { // std::qsort on solaris wants the sorter to be extern "C"
182 
183  /////////////////////////////////////////////////////////////////////////////
184  /// Friend function for sorting strings, case insensitive
185  ///
186  ///
187  /// Input: name1 - pointer to the first string
188  /// name2 - pointer to the second string
189  ///
190  /// NOTE: This function compares its arguments and returns an integer less
191  /// than, equal to, or greater than zero, depending on whether name1
192  /// is lexicographically less than, equal to, or greater than name2,
193  /// but characters are forced to lower-case prior to comparison.
194  ///
195  ///
196 
197  static int CaseInsensitiveSort(const void *name1, const void *name2)
198  {
199  return (strcasecmp(*((char **) name1), *((char **) name2)));
200  }
201 }
202 
203 namespace {
204 
205  // std::list::sort(with_stricmp_predicate) doesn't work with Solaris CC...
206  static void sort_strlist_stricmp(std::vector<std::string>& l)
207  {
208  // sort strings ignoring case - easier for humans
209  struct posList {
210  const char* str;
211  size_t pos;
212  };
213  posList* carr = new posList[l.size()];
214  size_t idx = 0;
215  for (size_t iS = 0, iSE = l.size(); iS < iSE; ++iS) {
216  carr[idx].pos = iS;
217  carr[idx++].str = l[iS].c_str();
218  }
219  qsort(&carr[0].str, idx, sizeof(posList), CaseInsensitiveSort);
220  std::vector<std::string> lsort(l.size());
221  for (size_t iS = 0, iSE = l.size(); iS < iSE; ++iS) {
222  lsort[iS].swap(l[carr[iS].pos]);
223  }
224  delete [] carr;
225  l.swap(lsort);
226  }
227 
228 }
229 
230 //______________________________________________________________________________
231 //
232 // THtml generated documentation is written to file by TDocOutput. So far only
233 // output of HTML is implemented. Customization of the output should be done
234 // with THtml's interfaces - TDocOutput should not be used nor re-implemented
235 // directly.
236 //
237 // TDocOutput generates the index tables:
238 // * classes (THtml invokes TClassDocOutput for each),
239 // * inheritance hierarchy,
240 // * types and typedefs,
241 // * libraries,
242 // * the product index, and
243 // * the module index (including the links to per-module documentation).
244 // It invokes AT&T's GraphViz tool (dot) if available; charts benefit a lot
245 // from it.
246 //
247 // TDocOutput also writes all pages' header and footer, which can be customized
248 // by calling THtml::SetHeader(), THtml::SetFooter().
249 //______________________________________________________________________________
250 
251 ClassImp(TDocOutput);
252 
253 ////////////////////////////////////////////////////////////////////////////////
254 
255 TDocOutput::TDocOutput(THtml& html): fHtml(&html)
256 {}
257 
258 ////////////////////////////////////////////////////////////////////////////////
259 
260 TDocOutput::~TDocOutput()
261 {}
262 
263 ////////////////////////////////////////////////////////////////////////////////
264 /// Add a link around str, with title comment.
265 /// Update str so it surrounds the link.
266 
267 void TDocOutput::AddLink(TSubString& str, TString& link, const char* comment)
268 {
269  // prepend "./" to allow callers to replace a different relative directory
270  if (ReferenceIsRelative(link) && !link.BeginsWith("./"))
271  link.Prepend("./");
272  link.Prepend("<a href=\"");
273  link += "\"";
274  if (comment && strlen(comment)) {
275  link += " title=\"";
276  TString description(comment);
277  ReplaceSpecialChars(description);
278  description.ReplaceAll("\"", "&quot;");
279  link += description;
280  link += "\"";
281  }
282  link += ">";
283 
284  str.String().Insert(str.Start() + str.Length(), "</a>");
285  str.String().Insert(str.Start(), link);
286 
287  TString &strString = str.String();
288  TSubString update = strString(str.Start(), str.Length() + link.Length() + 4);
289  str = update;
290 }
291 
292 ////////////////////////////////////////////////////////////////////////////////
293 /// adjust the path of links for source files, which are in src/, but need
294 /// to point to relpath (usually "../"). Simply replaces "=\"./" by "=\"../"
295 
296 void TDocOutput::AdjustSourcePath(TString& line, const char* relpath /*= "../"*/)
297 {
298  TString replWithRelPath("=\"@!@");
299  line.ReplaceAll("=\"../", replWithRelPath + "../" + relpath);
300  line.ReplaceAll("=\"./", replWithRelPath + relpath);
301  line.ReplaceAll("=\"@!@","=\"");
302 }
303 
304 ////////////////////////////////////////////////////////////////////////////////
305 /// Convert a text file into a html file.
306 /// outfilename doesn't have an extension yet; up to us to decide.
307 /// We generate HTML, so our extension is ".html".
308 /// See THtml::Convert() for the other parameters.
309 
310 void TDocOutput::Convert(std::istream& in, const char* infilename,
311  const char* outfilename, const char *title,
312  const char *relpath /*= "../"*/, Int_t includeOutput /*=0*/,
313  const char* context /*= ""*/,
314  TGClient* gclient /*= 0*/)
315 {
316  TString htmlFilename(outfilename);
317  htmlFilename += ".html";
318 
319  std::ofstream out(htmlFilename);
320 
321  if (!out.good()) {
322  Error("Convert", "Can't open file '%s' !", htmlFilename.Data());
323  return;
324  }
325 
326  // write a HTML header
327  WriteHtmlHeader(out, title, relpath);
328 
329  if (context && context[0])
330  out << context << std::endl;
331  else if (title && title[0])
332  out << "<h1 class=\"convert\">" << title << "</h1>" << std::endl;
333 
334  Int_t numReuseCanvases = 0;
335  if (includeOutput && !(includeOutput & THtml::kForceOutput)) {
336  void* dirHandle = gSystem->OpenDirectory(gSystem->DirName(htmlFilename));
337  if (dirHandle) {
338  FileStat_t infile_stat;
339  if (!gSystem->GetPathInfo(infilename, infile_stat)) {
340  // can stat.
341  const char* outfile = 0;
342  TString firstCanvasFileBase(gSystem->BaseName(outfilename));
343  firstCanvasFileBase += "_0.png";
344  // first check whether the firstCanvasFile exists:
345  Bool_t haveFirstCanvasFile = false;
346  while ((outfile = gSystem->GetDirEntry(dirHandle))) {
347  if (firstCanvasFileBase == outfile) {
348  haveFirstCanvasFile = true;
349  break;
350  }
351  }
352  gSystem->FreeDirectory(dirHandle);
353 
354  FileStat_t outfile_stat;
355  TString firstCanvasFile = outfilename;
356  firstCanvasFile += "_0.png";
357  Int_t maxIdx = -1;
358  if (haveFirstCanvasFile && !gSystem->GetPathInfo(firstCanvasFile, outfile_stat)
359  && outfile_stat.fMtime > infile_stat.fMtime) {
360  // the first canvas file exists and it is newer than the script, so we reuse
361  // the canvas files. We need to know how many there are:
362  dirHandle = gSystem->OpenDirectory(gSystem->DirName(htmlFilename));
363  TString stem(gSystem->BaseName(outfilename));
364  stem += "_";
365  TString dir(gSystem->DirName(htmlFilename));
366  while ((outfile = gSystem->GetDirEntry(dirHandle))) {
367  if (strncmp(outfile, stem, stem.Length()))
368  continue;
369  const char* posext = strrchr(outfile, '.');
370  if (!posext || strcmp(posext, ".png"))
371  continue;
372 
373  // extract the mod time of the PNG file
374  if (gSystem->GetPathInfo(dir + "/" + outfile, outfile_stat))
375  // can't stat!
376  continue;
377 
378  if (outfile_stat.fMtime > infile_stat.fMtime) {
379  ++numReuseCanvases;
380  // The canvas PNG is newer than the script, so
381  // extract the index of the canvas
382  TString idxStr(outfile + stem.Length());
383  idxStr.Remove(idxStr.Length() - 4);
384  Int_t idx = idxStr.Atoi();
385  if (maxIdx < idx)
386  maxIdx = idx;
387  }
388  }
389  gSystem->FreeDirectory(dirHandle);
390  if (maxIdx + 1 != numReuseCanvases)
391  // bad: the number of canvases to reuse noes not correspond to the highest index we saw.
392  // we will need to regenerate everything.
393  numReuseCanvases = 0;
394  }
395  } // infile can be stat'ed
396  } // can open output directory
397  } // canvases wanted
398 
399  if (numReuseCanvases)
400  Printf("Convert: %s (reusing %d saved canvas%s)", htmlFilename.Data(), numReuseCanvases, (numReuseCanvases > 1 ? "es" : ""));
401  else
402  Printf("Convert: %s", htmlFilename.Data());
403 
404  UInt_t nCanvases = numReuseCanvases;
405  if (includeOutput) {
406  if (!numReuseCanvases) {
407  // need to run the script
408  if (includeOutput & THtml::kSeparateProcessOutput) {
409  TString baseInFileName = gSystem->BaseName(infilename);
410  TPMERegexp reOutFile(baseInFileName + "_[[:digit:]]+\\.png");
411 
412  // remove all files matching what saveScriptOutput.C could produce:
413  void* outdirH = gSystem->OpenDirectory(gSystem->DirName(outfilename));
414  if (outdirH) {
415  // the directory exists.
416  const char* outdirE = 0;
417  while ((outdirE = gSystem->GetDirEntry(outdirH))) {
418  if (reOutFile.Match(outdirE)) {
419  gSystem->Unlink(outdirE);
420  }
421  }
422  gSystem->FreeDirectory(outdirH);
423  }
424 
425  gSystem->Exec(TString::Format("ROOT_HIST=0 root.exe -l -q %s $ROOTSYS/etc/html/saveScriptOutput.C\\(\\\"%s\\\",\\\"%s\\\",%d\\)",
426  gROOT->IsBatch() ? "-b" : "",
427  infilename,
428  gSystem->DirName(outfilename),
429  includeOutput & THtml::kCompiledOutput));
430 
431  // determine how many output files were created:
432  outdirH = gSystem->OpenDirectory(gSystem->DirName(outfilename));
433  if (outdirH) {
434  // the directory exists.
435  const char* outdirE = 0;
436  while ((outdirE = gSystem->GetDirEntry(outdirH))) {
437  if (reOutFile.Match(outdirE)) {
438  ++nCanvases;
439  }
440  }
441  gSystem->FreeDirectory(outdirH);
442  }
443  } else {
444  // run in this ROOT process
445  TString pwd(gSystem->pwd());
446  gSystem->cd(gSystem->DirName(infilename));
447 
448  TList* gClientGetListOfWindows = 0;
449  TObject* gClientGetDefaultRoot = 0;
450  std::set<TObject*> previousWindows;
451  if (gclient) {
452  gROOT->ProcessLine(TString::Format("*((TList**)0x%lx) = ((TGClient*)0x%lx)->GetListOfWindows();",
453  (ULong_t)&gClientGetListOfWindows, (ULong_t)gclient));
454  gROOT->ProcessLine(TString::Format("*((TObject**)0x%lx) = ((TGClient*)0x%lx)->GetDefaultRoot();",
455  (ULong_t)&gClientGetDefaultRoot, (ULong_t)gclient));
456  TObject* win = 0;
457  TIter iWin(gClientGetListOfWindows);
458  while((win = iWin())) {
459  TObject* winGetParent = 0;
460  gROOT->ProcessLine(TString::Format("*((TObject**)0x%lx) = ((TGWindow*)0x%lx)->GetParent();",
461  (ULong_t)&winGetParent, (ULong_t)win));
462  if (winGetParent == gClientGetDefaultRoot)
463  previousWindows.insert(win);
464  }
465  } else {
466  if (gROOT->GetListOfCanvases()->GetSize())
467  previousWindows.insert(gROOT->GetListOfCanvases()->Last());
468  }
469  TIter iTimer(gSystem->GetListOfTimers());
470  std::set<TObject*> timersBefore;
471  TObject* timerOld = 0;
472  while ((timerOld = iTimer()))
473  timersBefore.insert(timerOld);
474 
475  TString cmd(".x ");
476  cmd += gSystem->BaseName(infilename);
477  if (includeOutput & THtml::kCompiledOutput)
478  cmd += "+";
479  gInterpreter->SaveContext();
480  gInterpreter->SaveGlobalsContext();
481  Int_t err;
482  gROOT->ProcessLine(cmd, &err);
483  gSystem->ProcessEvents();
484  gSystem->cd(pwd);
485 
486  if (err == TInterpreter::kNoError) {
487  if (gclient) {
488  TClass* clRootCanvas = TClass::GetClass("TRootCanvas");
489  TClass* clGMainFrame = TClass::GetClass("TGMainFrame");
490  TObject* win = 0;
491  TIter iWin(gClientGetListOfWindows);
492  while((win = iWin())) {
493  TObject* winGetParent = 0;
494  gROOT->ProcessLine(TString::Format("*((TObject**)0x%lx) = ((TGWindow*)0x%lx)->GetParent();",
495  (ULong_t)&winGetParent, (ULong_t)win));
496  Bool_t winIsMapped = kFALSE;
497  if (winGetParent == gClientGetDefaultRoot)
498  gROOT->ProcessLine(TString::Format("*((Bool_t*)0x%lx) = ((TGWindow*)0x%lx)->IsMapped();",
499  (ULong_t)&winIsMapped, (ULong_t)win));
500  if (winIsMapped && previousWindows.find(win) == previousWindows.end()
501  && win->InheritsFrom(clGMainFrame)) {
502  gROOT->ProcessLine(TString::Format("((TGWindow*)0x%lx)->MapRaised();", (ULong_t)win));
503  Bool_t isRootCanvas = win->InheritsFrom(clRootCanvas);
504  Bool_t hasEditor = false;
505  if (isRootCanvas) {
506  gROOT->ProcessLine(TString::Format("*((Bool_t*)0x%lx) = ((TRootCanvas*)0x%lx)->HasEditor();",
507  (ULong_t)&hasEditor, (ULong_t)win));
508  }
509  if (isRootCanvas && !hasEditor) {
510  TVirtualPad* pad = 0;
511  gROOT->ProcessLine(TString::Format("*((TVirtualPad**)0x%lx) = ((TRootCanvas*)0x%lx)->Canvas();",
512  (ULong_t)&pad, (ULong_t)win));
513  if (!pad->HasViewer3D() || pad->GetViewer3D()->InheritsFrom("TViewer3DPad")) {
514  pad->SaveAs(TString::Format("%s_%d.png", outfilename, nCanvases++));
515  }
516  } else
517  gROOT->ProcessLine(TString::Format("((TGWindow*)0x%lx)->SaveAs(\"%s_%d.png\");",
518  (ULong_t)win, outfilename, nCanvases++));
519  }
520  }
521  } else {
522  // no gClient
523  TVirtualPad* pad = 0;
524  TVirtualPad* last = 0;
525  if (!previousWindows.empty())
526  last = (TVirtualPad*) *previousWindows.begin();
527  TIter iCanvas(gROOT->GetListOfCanvases());
528  while ((pad = (TVirtualPad*) iCanvas())) {
529  if (last) {
530  if (last == pad) last = 0;
531  continue;
532  }
533  pad->SaveAs(TString::Format("%s_%d.png", outfilename, nCanvases++));
534  }
535  }
536  gInterpreter->Reset();
537  gInterpreter->ResetGlobals();
538  TIter iTimerRemove(gSystem->GetListOfTimers());
539  TTimer* timer = 0;
540  while ((timer = (TTimer*) iTimerRemove()))
541  if (timersBefore.find(timer) == timersBefore.end())
542  gSystem->RemoveTimer(timer);
543  }
544  } // run script in this ROOT process
545  }
546  out << "<table><tr><td style=\"vertical-align:top;padding-right:2em;\">" << std::endl;
547  }
548  out << "<div class=\"listing\"><pre class=\"listing\">" << std::endl;
549 
550  TDocParser parser(*this);
551  parser.Convert(out, in, relpath, (includeOutput) /* determines whether it's code or not */,
552  kFALSE /*interpretDirectives*/);
553 
554  out << "</pre></div>" << std::endl;
555 
556  WriteLineNumbers(out, parser.GetLineNumber(), gSystem->BaseName(infilename));
557 
558  if (includeOutput) {
559  out << "</td><td style=\"vertical-align:top;\">" << std::endl;
560  out << "<table>" << std::endl;
561  for (UInt_t i = 0; i < nCanvases; ++i) {
562  TString pngname = TString::Format("%s_%d.png", gSystem->BaseName(outfilename), i);
563  out << "<tr><td><a href=\"" << pngname << "\">" << std::endl
564  << "<img src=\"" << pngname << "\" id=\"canv" << i << "\" alt=\"thumb\" style=\"border:none;width:22em;\" "
565  "onmouseover=\"javascript:canv" << i << ".style.width='auto';\" />" << std::endl
566  << "</a></td></tr>" << std::endl;
567  }
568  out << "</table>" << std::endl;
569  out << "</td></tr></table>" << std::endl;
570  }
571 
572  // write a HTML footer
573  WriteHtmlFooter(out, relpath);
574 }
575 
576 ////////////////////////////////////////////////////////////////////////////////
577 /// Copy file to HTML directory
578 ///
579 ///
580 /// Input: sourceName - source file name (fully qualified i.e. file system path)
581 /// destName - optional destination name, if not
582 /// specified it would be the same
583 /// as the source file name
584 ///
585 /// Output: TRUE if file is successfully copied, or
586 /// FALSE if it's not
587 ///
588 ///
589 /// NOTE: The destination directory is always fHtml->GetOutputDir()
590 ///
591 
592 Bool_t TDocOutput::CopyHtmlFile(const char *sourceName, const char *destName)
593 {
594  R__LOCKGUARD(GetHtml()->GetMakeClassMutex());
595 
596  TString sourceFile(sourceName);
597 
598  if (!sourceFile.Length()) {
599  Error("Copy", "Can't copy file '%s' to '%s' directory - source file name invalid!", sourceName,
600  fHtml->GetOutputDir().Data());
601  return kFALSE;
602  }
603 
604  // destination file name
605  TString destFile;
606  if (!destName || !*destName)
607  destFile = gSystem->BaseName(sourceFile);
608  else
609  destFile = gSystem->BaseName(destName);
610 
611  gSystem->PrependPathName(fHtml->GetOutputDir(), destFile);
612 
613  // Get info about a file
614  Long64_t size;
615  Long_t id, flags, sModtime, dModtime;
616  sModtime = 0;
617  dModtime = 0;
618  if (gSystem->GetPathInfo(sourceFile, &id, &size, &flags, &sModtime)
619  || gSystem->GetPathInfo(destFile, &id, &size, &flags, &dModtime)
620  || sModtime > dModtime)
621  if (gSystem->CopyFile(sourceFile, destFile, kTRUE) < 0) {
622  Error("Copy", "Can't copy file '%s' to '%s'!",
623  sourceFile.Data(), destFile.Data());
624  return kFALSE;
625  }
626 
627  return kTRUE;
628 }
629 
630 
631 
632 ////////////////////////////////////////////////////////////////////////////////
633 /// Create a hierarchical class list
634 /// The algorithm descends from the base classes and branches into
635 /// all derived classes. Mixing classes are displayed several times.
636 ///
637 ///
638 
639 void TDocOutput::CreateHierarchy()
640 {
641  // if (CreateHierarchyDot()) return;
642 
643  TString filename("ClassHierarchy.html");
644  gSystem->PrependPathName(fHtml->GetOutputDir(), filename);
645 
646  // open out file
647  std::ofstream out(filename);
648 
649  if (!out.good()) {
650  Error("CreateHierarchy", "Can't open file '%s' !", filename.Data());
651  return;
652  }
653 
654  Printf(fHtml->GetCounterFormat(), "", fHtml->GetCounter(), filename.Data());
655 
656  // write out header
657  WriteHtmlHeader(out, "Class Hierarchy");
658 
659  WriteTopLinks(out, 0);
660 
661  out << "<h1>Class Hierarchy</h1>" << std::endl;
662 
663 
664  // loop on all classes
665  TClassDocInfo* cdi = 0;
666  TIter iClass(fHtml->GetListOfClasses());
667  while ((cdi = (TClassDocInfo*)iClass())) {
668  if (!cdi->HaveSource())
669  continue;
670 
671  // get class
672  TDictionary *dictPtr = cdi->GetClass();
673  TClass *basePtr = dynamic_cast<TClass*>(dictPtr);
674  if (basePtr == 0) {
675  if (!dictPtr)
676  Warning("THtml::CreateHierarchy", "skipping class %s\n", cdi->GetName());
677  continue;
678  }
679 
680  TClassDocOutput cdo(*fHtml, basePtr, 0);
681  cdo.CreateClassHierarchy(out, cdi->GetHtmlFileName());
682  }
683 
684  // write out footer
685  WriteHtmlFooter(out);
686 }
687 
688 ////////////////////////////////////////////////////////////////////////////////
689 /// Create index of all classes
690 ///
691 
692 void TDocOutput::CreateClassIndex()
693 {
694  // create CSS file, we need it
695  fHtml->CreateAuxiliaryFiles();
696 
697  TString filename("ClassIndex.html");
698  gSystem->PrependPathName(fHtml->GetOutputDir(), filename);
699 
700  // open indexFile file
701  std::ofstream indexFile(filename.Data());
702 
703  if (!indexFile.good()) {
704  Error("CreateClassIndex", "Can't open file '%s' !", filename.Data());
705  return;
706  }
707 
708  Printf(fHtml->GetCounterFormat(), "", fHtml->GetCounter(), filename.Data());
709 
710  // write indexFile header
711  WriteHtmlHeader(indexFile, "Class Index");
712 
713  WriteTopLinks(indexFile, 0);
714 
715  indexFile << "<h1>Class Index</h1>" << std::endl;
716 
717  WriteModuleLinks(indexFile);
718 
719  std::vector<std::string> indexChars;
720  if (fHtml->GetListOfClasses()->GetSize() > 10) {
721  std::vector<std::string> classNames;
722  {
723  TIter iClass(fHtml->GetListOfClasses());
724  TClassDocInfo* cdi = 0;
725  while ((cdi = (TClassDocInfo*)iClass()))
726  if (cdi->IsSelected() && cdi->HaveSource())
727  classNames.push_back(cdi->GetName());
728  }
729 
730  if (classNames.size() > 10) {
731  indexFile << "<div id=\"indxShortX\"><h4>Jump to</h4>" << std::endl;
732  // find index chars
733  GetIndexChars(classNames, 50 /*sections*/, indexChars);
734  for (UInt_t iIdxEntry = 0; iIdxEntry < indexChars.size(); ++iIdxEntry) {
735  indexFile << "<a href=\"#idx" << iIdxEntry << "\">";
736  ReplaceSpecialChars(indexFile, indexChars[iIdxEntry].c_str());
737  indexFile << "</a>" << std::endl;
738  }
739  indexFile << "</div><br />" << std::endl;
740  }
741  }
742 
743  indexFile << "<ul id=\"indx\">" << std::endl;
744 
745  // loop on all classes
746  UInt_t currentIndexEntry = 0;
747  TIter iClass(fHtml->GetListOfClasses());
748  TClassDocInfo* cdi = 0;
749  Int_t i = 0;
750  while ((cdi = (TClassDocInfo*)iClass())) {
751  if (!cdi->IsSelected() || !cdi->HaveSource())
752  continue;
753 
754  // get class
755  TDictionary *currentDict = cdi->GetClass();
756  TClass* currentClass = dynamic_cast<TClass*>(currentDict);
757  if (!currentClass) {
758  if (!currentDict)
759  Warning("THtml::CreateClassIndex", "skipping class %s\n", cdi->GetName());
760  continue;
761  }
762 
763  indexFile << "<li class=\"idxl" << (i++)%2 << "\">";
764  if (currentIndexEntry < indexChars.size()
765  && !strncmp(indexChars[currentIndexEntry].c_str(), cdi->GetName(),
766  indexChars[currentIndexEntry].length()))
767  indexFile << "<a name=\"idx" << currentIndexEntry++ << "\"></a>";
768 
769  TString htmlFile(cdi->GetHtmlFileName());
770  if (htmlFile.Length()) {
771  indexFile << "<a href=\"";
772  indexFile << htmlFile;
773  indexFile << "\"><span class=\"typename\">";
774  ReplaceSpecialChars(indexFile, cdi->GetName());
775  indexFile << "</span></a> ";
776  } else {
777  indexFile << "<span class=\"typename\">";
778  ReplaceSpecialChars(indexFile, cdi->GetName());
779  indexFile << "</span> ";
780  }
781 
782  // write title == short doc
783  ReplaceSpecialChars(indexFile, currentClass->GetTitle());
784  indexFile << "</li>" << std::endl;
785  }
786 
787  indexFile << "</ul>" << std::endl;
788 
789  // write indexFile footer
790  WriteHtmlFooter(indexFile);
791 }
792 
793 
794 ////////////////////////////////////////////////////////////////////////////////
795 /// Create the class index for each module, picking up documentation from the
796 /// module's TModuleDocInfo::GetInputPath() plus the (possibly relative)
797 /// THtml::GetModuleDocPath(). Also creates the library dependency plot if dot
798 /// exists, see THtml::HaveDot().
799 
800 void TDocOutput::CreateModuleIndex()
801 {
802  const char* title = "LibraryDependencies";
803  TString dotfilename(title);
804  gSystem->PrependPathName(fHtml->GetOutputDir(), dotfilename);
805 
806  std::ofstream libDepDotFile(dotfilename + ".dot");
807  libDepDotFile << "digraph G {" << std::endl
808  << "ratio=compress;" << std::endl
809  << "node [fontsize=22,labeldistance=0.1];" << std::endl
810  << "edge [len=0.01];" << std::endl
811  << "fontsize=22;" << std::endl
812  << "size=\"16,16\";" << std::endl
813  << "overlap=false;" << std::endl
814  << "splines=true;" << std::endl
815  << "K=0.1;" << std::endl;
816 
817  TModuleDocInfo* module = 0;
818  TIter iterModule(fHtml->GetListOfModules());
819 
820  std::stringstream sstrCluster;
821  std::stringstream sstrDeps;
822  while ((module = (TModuleDocInfo*)iterModule())) {
823  if (!module->IsSelected())
824  continue;
825 
826  std::vector<std::string> indexChars;
827  TString filename(module->GetName());
828  filename.ToUpper();
829  filename.ReplaceAll("/","_");
830  filename += "_Index.html";
831  gSystem->PrependPathName(fHtml->GetOutputDir(), filename);
832  std::ofstream outputFile(filename.Data());
833  if (!outputFile.good()) {
834  Error("CreateModuleIndex", "Can't open file '%s' !", filename.Data());
835  continue;
836  }
837  Printf(fHtml->GetCounterFormat(), "", fHtml->GetCounter(), filename.Data());
838 
839  TString htmltitle("Index of ");
840  TString moduletitle(module->GetName());
841  moduletitle.ToUpper();
842  htmltitle += moduletitle;
843  WriteHtmlHeader(outputFile, htmltitle);
844 
845  WriteTopLinks(outputFile, module);
846 
847  outputFile << "<h2>" << htmltitle << "</h2>" << std::endl;
848 
849  // Module doc
850  if (GetHtml()->GetModuleDocPath().Length()) {
851  TString outdir(module->GetName());
852  gSystem->PrependPathName(GetHtml()->GetOutputDir(), outdir);
853 
854  TString moduleDocDir;
855  GetHtml()->GetPathDefinition().GetDocDir(module->GetName(), moduleDocDir);
856  ProcessDocInDir(outputFile, moduleDocDir, outdir, module->GetName());
857  }
858 
859  WriteModuleLinks(outputFile, module);
860 
861  std::list<std::string> classNames;
862  {
863  TIter iClass(module->GetClasses());
864  TClassDocInfo* cdi = 0;
865  while ((cdi = (TClassDocInfo*) iClass())) {
866  if (!cdi->IsSelected() || !cdi->HaveSource())
867  continue;
868  classNames.push_back(cdi->GetName());
869 
870  if (classNames.size() > 1) continue;
871 
872  TClass* cdiClass = dynamic_cast<TClass*>(cdi->GetClass());
873  if (!cdiClass)
874  continue;
875 
876  TString libs(cdiClass->GetSharedLibs());
877  Ssiz_t posDepLibs = libs.Index(' ');
878  TString thisLib(libs);
879  if (posDepLibs != kNPOS)
880  thisLib.Remove(posDepLibs, thisLib.Length());
881 
882  {
883  Ssiz_t posExt = thisLib.First('.');
884  if (posExt != kNPOS)
885  thisLib.Remove(posExt, thisLib.Length());
886  }
887 
888  if (!thisLib.Length())
889  continue;
890 
891  // allocate entry, even if no dependencies
892  TLibraryDocInfo *libdeps =
893  (TLibraryDocInfo*)fHtml->GetLibraryDependencies()->FindObject(thisLib);
894  if (!libdeps) {
895  libdeps = new TLibraryDocInfo(thisLib);
896  fHtml->GetLibraryDependencies()->Add(libdeps);
897  }
898  libdeps->AddModule(module->GetName());
899  if (posDepLibs != kNPOS) {
900  std::string lib;
901  for(Ssiz_t pos = posDepLibs + 1; libs[pos]; ++pos) {
902  if (libs[pos] == ' ') {
903  if (thisLib.Length() && lib.length()) {
904  size_t posExt = lib.find('.');
905  if (posExt != std::string::npos)
906  lib.erase(posExt);
907  libdeps->AddDependency(lib);
908  }
909  lib.erase();
910  } else
911  lib += libs[pos];
912  }
913  if (lib.length() && thisLib.Length()) {
914  size_t posExt = lib.find('.');
915  if (posExt != std::string::npos)
916  lib.erase(posExt);
917  libdeps->AddDependency(lib);
918  }
919  } // if dependencies
920  } // while next class in module
921  } // just a scope block
922 
923  TIter iClass(module->GetClasses());
924  TClassDocInfo* cdi = 0;
925  UInt_t count = 0;
926  UInt_t currentIndexEntry = 0;
927  while ((cdi = (TClassDocInfo*) iClass())) {
928  if (!cdi->IsSelected() || !cdi->HaveSource())
929  continue;
930 
931  TDictionary *classPtr = cdi->GetClass();
932  if (!classPtr) {
933  Error("CreateModuleIndex", "Unknown class '%s' !", cdi->GetName());
934  continue;
935  }
936 
937  if (!count) {
938  outputFile << "<h2>Class Index</h2>" << std::endl;
939 
940  if (classNames.size() > 10) {
941  outputFile << "<div id=\"indxShortX\"><h4>Jump to</h4>" << std::endl;
942  UInt_t numSections = classNames.size() / 10;
943  if (numSections < 10) numSections = 10;
944  if (numSections > 50) numSections = 50;
945  // find index chars
946  GetIndexChars(classNames, numSections, indexChars);
947  for (UInt_t iIdxEntry = 0; iIdxEntry < indexChars.size(); ++iIdxEntry) {
948  outputFile << "<a href=\"#idx" << iIdxEntry << "\">";
949  ReplaceSpecialChars(outputFile, indexChars[iIdxEntry].c_str());
950  outputFile << "</a>" << std::endl;
951  }
952  outputFile << "</div><br />" << std::endl;
953  }
954 
955  outputFile << "<ul id=\"indx\">" << std::endl;
956  }
957 
958  // write a classname to an index file
959  outputFile << "<li class=\"idxl" << (count++)%2 << "\">";
960  if (currentIndexEntry < indexChars.size()
961  && !strncmp(indexChars[currentIndexEntry].c_str(), cdi->GetName(),
962  indexChars[currentIndexEntry].length()))
963  outputFile << "<a name=\"idx" << currentIndexEntry++ << "\"></a>";
964 
965  TString htmlFile(cdi->GetHtmlFileName());
966  if (htmlFile.Length()) {
967  outputFile << "<a href=\"";
968  outputFile << htmlFile;
969  outputFile << "\"><span class=\"typename\">";
970  ReplaceSpecialChars(outputFile, classPtr->GetName());
971  outputFile << "</span></a> ";
972  } else {
973  outputFile << "<span class=\"typename\">";
974  ReplaceSpecialChars(outputFile, classPtr->GetName());
975  outputFile << "</span> ";
976  }
977 
978  // write title
979  ReplaceSpecialChars(outputFile, classPtr->GetTitle());
980  outputFile << "</li>" << std::endl;
981  }
982 
983 
984  if (count)
985  outputFile << "</ul>" << std::endl;
986 
987  // write outputFile footer
988  WriteHtmlFooter(outputFile);
989  } // while next module
990 
991  // libCint is missing as we don't have class doc for it
992  // We need it for dependencies nevertheless, so add it by hand.
993  /*
994  sstrCluster << "subgraph clusterlibCint {" << std::endl
995  << "style=filled;" << std::endl
996  << "color=lightgray;" << std::endl
997  << "label=\"libCint\";" << std::endl
998  << "\"CINT\" [style=filled,color=white,fontsize=10]" << std::endl
999  << "}" << std::endl;
1000  */
1001 
1002  // simplify the library dependencies, by removing direct links
1003  // that are equivalent to indirect ones, e.g. instead of having both
1004  // A->C, A->B->C, keep only A->B->C.
1005 
1006  TIter iLib(fHtml->GetLibraryDependencies());
1007  TLibraryDocInfo* libinfo = 0;
1008  while ((libinfo = (TLibraryDocInfo*)iLib())) {
1009  if (!libinfo->GetName() || !libinfo->GetName()[0]) continue;
1010 
1011  std::set<std::string>& deps = libinfo->GetDependencies();
1012  for (std::set<std::string>::iterator iDep = deps.begin();
1013  iDep != deps.end(); ) {
1014  Bool_t already_indirect = kFALSE;
1015  for (std::set<std::string>::const_iterator iDep2 = deps.begin();
1016  !already_indirect && iDep2 != deps.end(); ++iDep2) {
1017  if (iDep == iDep2) continue;
1018  TLibraryDocInfo* libinfo2 = (TLibraryDocInfo*)
1019  fHtml->GetLibraryDependencies()->FindObject(iDep2->c_str());
1020  if (!libinfo2) continue;
1021  const std::set<std::string>& deps2 = libinfo2->GetDependencies();
1022  already_indirect |= deps2.find(*iDep) != deps2.end();
1023  }
1024  if (already_indirect) {
1025  std::set<std::string>::iterator iRemove = iDep;
1026  // Advance the iterator before erasing the element which invalidates the iterator.
1027  ++iDep;
1028  deps.erase(iRemove);
1029  } else {
1030  ++iDep;
1031  }
1032  } // for library dependencies of module in library
1033  } // for libraries
1034 
1035  iLib.Reset();
1036  while ((libinfo = (TLibraryDocInfo*)iLib())) {
1037  if (!libinfo->GetName() || !libinfo->GetName()[0]) continue;
1038 
1039  const std::set<std::string>& modules = libinfo->GetModules();
1040  if (modules.size() > 1) {
1041  sstrCluster << "subgraph cluster" << libinfo->GetName() << " {" << std::endl
1042  << "style=filled;" << std::endl
1043  << "color=lightgray;" << std::endl
1044  << "label=\"";
1045  if (!strcmp(libinfo->GetName(), "libCore"))
1046  sstrCluster << "Everything depends on ";
1047  sstrCluster << libinfo->GetName() << "\";" << std::endl;
1048 
1049  for (std::set<std::string>::const_iterator iModule = modules.begin();
1050  iModule != modules.end(); ++iModule) {
1051  TString modURL(*iModule);
1052  modURL.ReplaceAll("/", "_");
1053  modURL.ToUpper();
1054  sstrCluster << "\"" << *iModule << "\" [style=filled,color=white,URL=\""
1055  << modURL << "_Index.html\"];" << std::endl;
1056  }
1057  sstrCluster << std::endl
1058  << "}" << std::endl;
1059  } else {
1060  // only one module
1061  TString modURL(*modules.begin());
1062  modURL.ReplaceAll("/", "_");
1063  modURL.ToUpper();
1064  sstrCluster << "\"" << *modules.begin()
1065  << "\" [label=\"" << libinfo->GetName()
1066  << "\",style=filled,color=lightgray,shape=box,URL=\""
1067  << modURL << "_Index.html\"];" << std::endl;
1068  }
1069 
1070  // GetSharedLib doesn't mention libCore or libCint; add them by hand
1071  /*
1072  if (iLibDep->first != "libCore")
1073  sstrDeps << "\"" << iModule->first << "\" -> \"BASE\" [lhead=clusterlibCore];" << std::endl;
1074  sstrDeps << "\"" << iModule->first << "\" -> \"CINT\" [lhead=clusterlibCint];" << std::endl;
1075  */
1076 
1077  const std::string& mod = *(modules.begin());
1078  const std::set<std::string>& deps = libinfo->GetDependencies();
1079  for (std::set<std::string>::const_iterator iDep = deps.begin();
1080  iDep != deps.end(); ++iDep) {
1081  // cannot create dependency on iDep directly, use its first module instead.
1082  TLibraryDocInfo* depLibInfo = (TLibraryDocInfo*)
1083  fHtml->GetLibraryDependencies()->FindObject(iDep->c_str());
1084  if (!depLibInfo || depLibInfo->GetModules().empty())
1085  continue; // ouch!
1086 
1087  const std::string& moddep = *(depLibInfo->GetModules().begin());
1088  sstrDeps << "\"" << mod << "\" -> \"" << moddep << "\";" << std::endl;
1089  }
1090  // make sure libCore ends up at the bottom
1091  sstrDeps << "\"" << mod << "\" -> \"CONT\" [style=invis];" << std::endl;
1092  } // for libs
1093 
1094  libDepDotFile << sstrCluster.str() << std::endl
1095  << sstrDeps.str();
1096  libDepDotFile << "}" << std::endl;
1097  libDepDotFile.close();
1098 
1099  std::ofstream out(dotfilename + ".html");
1100  if (!out.good()) {
1101  Error("CreateModuleIndex", "Can't open file '%s.html' !",
1102  dotfilename.Data());
1103  return;
1104  }
1105 
1106  Printf(fHtml->GetCounterFormat(), "", fHtml->GetCounter(), (dotfilename + ".html").Data());
1107  // write out header
1108  WriteHtmlHeader(out, "Library Dependencies");
1109 
1110  WriteTopLinks(out, 0);
1111 
1112  out << "<h1>Library Dependencies</h1>" << std::endl;
1113 
1114  RunDot(dotfilename, &out, kFdp);
1115 
1116  out << "<img alt=\"Library Dependencies\" class=\"classcharts\" usemap=\"#Map" << title << "\" src=\"" << title << ".png\"/>" << std::endl;
1117 
1118  // write out footer
1119  WriteHtmlFooter(out);
1120 }
1121 
1122 ////////////////////////////////////////////////////////////////////////////////
1123 /// Fetch documentation from THtml::GetProductDocDir() and put it into the
1124 /// product index page.
1125 
1126 void TDocOutput::CreateProductIndex()
1127 {
1128  //TString outFile(GetHtml()->GetProductName());
1129  //outFile += ".html";
1130  TString outFile("index.html");
1131  gSystem->PrependPathName(GetHtml()->GetOutputDir(), outFile);
1132  std::ofstream out(outFile);
1133 
1134  if (!out.good()) {
1135  Error("CreateProductIndex", "Can't open file '%s' !", outFile.Data());
1136  return;
1137  }
1138 
1139  Printf(fHtml->GetCounterFormat(), "", "", outFile.Data());
1140 
1141  WriteHtmlHeader(out, GetHtml()->GetProductName() + " Reference Guide");
1142 
1143  WriteTopLinks(out, 0);
1144 
1145  out << "<h1>" << GetHtml()->GetProductName() + " Reference Guide</h1>" << std::endl;
1146 
1147  TString prodDoc;
1148  if (GetHtml()->GetPathDefinition().GetDocDir("", prodDoc))
1149  ProcessDocInDir(out, prodDoc, GetHtml()->GetOutputDir(), "./");
1150 
1151  WriteModuleLinks(out);
1152 
1153  out << "<h2>Chapters</h2>" << std::endl
1154  << "<h3><a href=\"./ClassIndex.html\">Class Index</a></h3>" << std::endl
1155  << "<p>A complete list of all classes defined in " << GetHtml()->GetProductName() << "</p>" << std::endl
1156  << "<h3><a href=\"./ClassHierarchy.html\">Class Hierarchy</a></h3>" << std::endl
1157  << "<p>A hierarchy graph of all classes, showing each class's base and derived classes</p>" << std::endl
1158  << "<h3><a href=\"./ListOfTypes.html\">Type Index</a></h3>" << std::endl
1159  << "<p>A complete list of all types</p>" << std::endl
1160  << "<h3><a href=\"./LibraryDependencies.html\">Library Dependency</a></h3>" << std::endl
1161  << "<p>A diagram showing all of " << GetHtml()->GetProductName() << "'s libraries and their dependencies</p>" << std::endl;
1162 
1163  WriteHtmlFooter(out);
1164 }
1165 
1166 ////////////////////////////////////////////////////////////////////////////////
1167 /// Create a forwarding page for each typedef pointing to a class.
1168 
1169 void TDocOutput::CreateClassTypeDefs()
1170 {
1171  TDocParser parser(*this);
1172 
1173  TIter iClass(GetHtml()->GetListOfClasses());
1174  TClassDocInfo* cdi = 0;
1175  while ((cdi = (TClassDocInfo*) iClass())) {
1176  if (cdi->GetListOfTypedefs().IsEmpty())
1177  continue;
1178  TIter iTypedefs(&cdi->GetListOfTypedefs());
1179  TDataType* dt = 0;
1180  while ((dt = (TDataType*) iTypedefs())) {
1181  if (gDebug > 0)
1182  Info("CreateClassTypeDefs", "Creating typedef %s to class %s",
1183  dt->GetName(), cdi->GetName());
1184  // create a filename
1185  TString filename(dt->GetName());
1186  NameSpace2FileName(filename);
1187 
1188  gSystem->PrependPathName(fHtml->GetOutputDir(), filename);
1189 
1190  filename += ".html";
1191 
1192  // open class file
1193  std::ofstream outfile(filename);
1194 
1195  if (!outfile.good()) {
1196  Error("CreateClassTypeDefs", "Can't open file '%s' !", filename.Data());
1197  continue;
1198  }
1199 
1200  WriteHtmlHeader(outfile, dt->GetName());
1201 
1202  outfile << "<a name=\"TopOfPage\"></a>" << std::endl;
1203 
1204  TString dtName(dt->GetName());
1205  ReplaceSpecialChars(dtName);
1206  TString sTitle("typedef ");
1207  sTitle += dtName;
1208 
1209  TClass* cls = dynamic_cast<TClass*>(cdi->GetClass());
1210  if (cls) {
1211  // show box with lib, include
1212  // needs to go first to allow title on the left
1213  TString sInclude;
1214  TString sLib;
1215  const char* lib=cls->GetSharedLibs();
1216  GetHtml()->GetPathDefinition().GetIncludeAs(cls, sInclude);
1217  if (lib) {
1218  char* libDup=StrDup(lib);
1219  char* libDupSpace=strchr(libDup,' ');
1220  if (libDupSpace) *libDupSpace = 0;
1221  char* libDupEnd=libDup+strlen(libDup);
1222  while (libDupEnd!=libDup)
1223  if (*(--libDupEnd)=='.') {
1224  *libDupEnd=0;
1225  break;
1226  }
1227  sLib = libDup;
1228  delete[] libDup;
1229  }
1230  outfile << "<script type=\"text/javascript\">WriteFollowPageBox('"
1231  << sTitle << "','" << sLib << "','" << sInclude << "');</script>" << std::endl;
1232  }
1233 
1234  TString modulename;
1235  fHtml->GetModuleNameForClass(modulename, cls);
1236  TModuleDocInfo* module = (TModuleDocInfo*) fHtml->GetListOfModules()->FindObject(modulename);
1237  WriteTopLinks(outfile, module, dt->GetName());
1238 
1239  outfile << "<div class=\"dropshadow\"><div class=\"withshadow\">";
1240  outfile << "<h1>" << sTitle << "</h1>" << std::endl
1241  << "<div class=\"classdescr\">" << std::endl;
1242 
1243  outfile << dtName << " is a typedef to ";
1244  std::string shortClsName(fHtml->ShortType(cdi->GetName()));
1245  parser.DecorateKeywords(outfile, shortClsName.c_str());
1246  outfile << std::endl
1247  << "</div>" << std::endl
1248  << "</div></div><div style=\"clear:both;\"></div>" << std::endl;
1249 
1250  // the typedef isn't a data member, but the CSS is applicable nevertheless
1251  outfile << std::endl << "<div id=\"datamembers\">" << std::endl
1252  << "<table class=\"data\" cellspacing=\"0\">" << std::endl;
1253  outfile << "<tr class=\"data";
1254  outfile << "\"><td class=\"datatype\">typedef ";
1255  parser.DecorateKeywords(outfile, dt->GetFullTypeName());
1256  outfile << "</td><td class=\"dataname\">";
1257  ReplaceSpecialChars(outfile, dt->GetName());
1258  if (dt->GetTitle() && dt->GetTitle()[0]) {
1259  outfile << "</td><td class=\"datadesc\">";
1260  ReplaceSpecialChars(outfile, dt->GetTitle());
1261  } else outfile << "</td><td>";
1262  outfile << "</td></tr>" << std::endl
1263  << "</table></div>" << std::endl;
1264 
1265  // write footer
1266  WriteHtmlFooter(outfile);
1267 
1268  }
1269  }
1270 }
1271 
1272 ////////////////////////////////////////////////////////////////////////////////
1273 /// Create index of all data types
1274 
1275 void TDocOutput::CreateTypeIndex()
1276 {
1277  // open file
1278  TString outFile("ListOfTypes.html");
1279  gSystem->PrependPathName(fHtml->GetOutputDir(), outFile);
1280  std::ofstream typesList(outFile);
1281 
1282  if (!typesList.good()) {
1283  Error("CreateTypeIndex", "Can't open file '%s' !", outFile.Data());
1284  return;
1285  }
1286 
1287  Printf(fHtml->GetCounterFormat(), "", "", outFile.Data());
1288 
1289  // write typesList header
1290  WriteHtmlHeader(typesList, "List of data types");
1291  typesList << "<h2> List of data types </h2>" << std::endl;
1292 
1293  typesList << "<dl><dd>" << std::endl;
1294 
1295  // make loop on data types
1296  std::vector<std::string> typeNames(gROOT->GetListOfTypes()->GetSize());
1297 
1298  {
1299  TDataType *type;
1300  TIter nextType(gROOT->GetListOfTypes());
1301  size_t tnIdx = 0;
1302 
1303  while ((type = (TDataType *) nextType()))
1304  // no templates ('<' and '>'), no idea why the '(' is in here...
1305  if (*type->GetTitle() && !strchr(type->GetName(), '(')
1306  && !( strchr(type->GetName(), '<') && strchr(type->GetName(),'>'))
1307  && type->GetName())
1308  typeNames[tnIdx++] = type->GetName();
1309  typeNames.resize(tnIdx);
1310  }
1311 
1312  sort_strlist_stricmp(typeNames);
1313 
1314  std::vector<std::string> indexChars;
1315  if (typeNames.size() > 10) {
1316  typesList << "<div id=\"indxShortX\"><h4>Jump to</h4>" << std::endl;
1317  // find index chars
1318  GetIndexChars(typeNames, 10 /*sections*/, indexChars);
1319  for (UInt_t iIdxEntry = 0; iIdxEntry < indexChars.size(); ++iIdxEntry) {
1320  typesList << "<a href=\"#idx" << iIdxEntry << "\">";
1321  ReplaceSpecialChars(typesList, indexChars[iIdxEntry].c_str());
1322  typesList << "</a>" << std::endl;
1323  }
1324  typesList << "</div><br />" << std::endl;
1325  }
1326 
1327  typesList << "<ul id=\"indx\">" << std::endl;
1328 
1329  int idx = 0;
1330  UInt_t currentIndexEntry = 0;
1331 
1332  for (std::vector<std::string>::iterator iTypeName = typeNames.begin();
1333  iTypeName != typeNames.end(); ++iTypeName) {
1334  TDataType* type = gROOT->GetType(iTypeName->c_str(), kFALSE);
1335  typesList << "<li class=\"idxl" << idx%2 << "\">";
1336  if (currentIndexEntry < indexChars.size()
1337  && !strncmp(indexChars[currentIndexEntry].c_str(), iTypeName->c_str(),
1338  indexChars[currentIndexEntry].length()))
1339  typesList << "<a name=\"idx" << currentIndexEntry++ << "\"></a>" << std::endl;
1340  typesList << "<a name=\"";
1341  ReplaceSpecialChars(typesList, iTypeName->c_str());
1342  typesList << "\"><span class=\"typename\">";
1343  ReplaceSpecialChars(typesList, iTypeName->c_str());
1344  typesList << "</span></a> ";
1345  ReplaceSpecialChars(typesList, type->GetTitle());
1346  typesList << "</li>" << std::endl;
1347  ++idx;
1348  }
1349  typesList << "</ul>" << std::endl;
1350 
1351  // write typesList footer
1352  WriteHtmlFooter(typesList);
1353 
1354  // close file
1355  typesList.close();
1356 
1357 }
1358 
1359 
1360 ////////////////////////////////////////////////////////////////////////////////
1361 /// Add some colors etc to a source entity, contained in str.
1362 /// The type of what's contained in str is given by type.
1363 /// It's called e.g. by TDocParser::BeautifyLine().
1364 /// This function should assume that only str.Begin() is valid.
1365 /// When inserting into str.String(), str.Begin() must be updated.
1366 
1367 void TDocOutput::DecorateEntityBegin(TString& str, Ssiz_t& pos, TDocParser::EParseContext type)
1368 {
1369  Ssiz_t originalLen = str.Length();
1370 
1371  switch (type) {
1372  case TDocParser::kCode: break;
1373  case TDocParser::kComment:
1374  str.Insert(pos, "<span class=\"comment\">");
1375  break;
1376  case TDocParser::kDirective:
1377  break;
1378  case TDocParser::kString:
1379  str.Insert(pos, "<span class=\"string\">");
1380  break;
1381  case TDocParser::kKeyword:
1382  str.Insert(pos, "<span class=\"keyword\">");
1383  break;
1384  case TDocParser::kCPP:
1385  str.Insert(pos, "<span class=\"cpp\">");
1386  break;
1387  case TDocParser::kVerbatim:
1388  str.Insert(pos, "<pre>");
1389  break;
1390  default:
1391  Error("DecorateEntityBegin", "Unhandled / invalid entity type %d!", (Int_t)type);
1392  return;
1393  }
1394 
1395  Ssiz_t addedLen = str.Length() - originalLen;
1396  pos += addedLen;
1397 }
1398 
1399 ////////////////////////////////////////////////////////////////////////////////
1400 /// Add some colors etc to a source entity, contained in str.
1401 /// The type of what's contained in str is given by type.
1402 /// It's called e.g. by TDocParser::BeautifyLine().
1403 /// This function should assume that only str."End()"
1404 /// (i.e. str.Begin()+str.Length()) is valid.
1405 /// When inserting into str.String(), str.Length() must be updated.
1406 
1407 void TDocOutput::DecorateEntityEnd(TString& str, Ssiz_t& pos, TDocParser::EParseContext type)
1408 {
1409  Ssiz_t originalLen = str.Length();
1410 
1411  switch (type) {
1412  case TDocParser::kCode: break;
1413  case TDocParser::kComment:
1414  str.Insert(pos, "</span>");
1415  break;
1416  case TDocParser::kDirective:
1417  break;
1418  case TDocParser::kString:
1419  str.Insert(pos, "</span>");
1420  break;
1421  case TDocParser::kKeyword:
1422  str.Insert(pos, "</span>");
1423  break;
1424  case TDocParser::kCPP:
1425  str.Insert(pos, "</span>");
1426  break;
1427  case TDocParser::kVerbatim:
1428  str.Insert(pos, "</pre>");
1429  break;
1430  default:
1431  Error("DecorateEntityBegin", "Unhandled / invalid entity type %d!", (Int_t)type);
1432  return;
1433  }
1434  Ssiz_t addedLen = str.Length() - originalLen;
1435  pos += addedLen;
1436 }
1437 
1438 ////////////////////////////////////////////////////////////////////////////////
1439 /// Special author treatment; called when TDocParser::fSourceInfo[kInfoAuthor] is set.
1440 /// Modifies the author(s) description, which is a comma separated list of tokens
1441 /// either in the format
1442 /// (i) "FirstName LastName " or
1443 /// (ii) "FirstName LastName <link> more stuff"
1444 /// The first one generates an XWho link (CERN compatible),
1445 /// the second a http link (WORLD compatible), <link> being e.g.
1446 /// <mailto:user@host.bla> or <http://www.host.bla/page>.
1447 
1448 void TDocOutput::FixupAuthorSourceInfo(TString& authors)
1449 {
1450  TString original(authors);
1451  authors = "";
1452 
1453  TString author;
1454  Ssiz_t pos = 0;
1455  Bool_t firstAuthor = kTRUE;
1456  while (original.Tokenize(author, pos, ",")) {
1457  author.Strip(TString::kBoth);
1458 
1459  if (!firstAuthor)
1460  authors += ", ";
1461  firstAuthor = kFALSE;
1462 
1463  // do we have a link for the current name?
1464  Ssiz_t cLink = author.First('<'); // look for link start tag
1465  if (cLink != kNPOS) {
1466  // split NAME <LINK> POST
1467  // into <a href="LINK">NAME</a> POST
1468  Ssiz_t endLink = author.Index(">", cLink + 1);
1469  if(endLink == kNPOS)
1470  endLink = author.Length();
1471  authors += "<a href=\"";
1472  authors += author(cLink + 1, endLink - (cLink + 1));
1473  authors += "\">";
1474  authors += author(0, cLink);
1475  authors += "</a>";
1476  if (endLink != author.Length())
1477  authors += author(endLink + 1, author.Length());
1478  } else {
1479  authors += "<a href=\"";
1480  authors += fHtml->GetXwho();
1481 
1482  // separate Firstname Middlename Lastname by '+'
1483  TString namePart;
1484  Ssiz_t posNamePart = 0;
1485  Bool_t firstNamePart = kTRUE;
1486  while (author.Tokenize(namePart, posNamePart, " ")) {
1487  namePart.Strip(TString::kBoth);
1488  if (!namePart.Length())
1489  continue;
1490  if (isdigit(namePart[0])) continue; //likely a date
1491  if (!firstNamePart)
1492  authors += '+';
1493  firstNamePart = kFALSE;
1494  authors += namePart;
1495  }
1496  authors += "\">";
1497  authors += author;
1498  authors += "</a>";
1499  }
1500  } // while next author
1501 }
1502 
1503 ////////////////////////////////////////////////////////////////////////////////
1504 /// Check if file is modified
1505 ///
1506 ///
1507 /// Input: classPtr - pointer to the class
1508 /// type - file type to compare with
1509 /// values: kSource, kInclude, kTree
1510 ///
1511 /// Output: TRUE - if file is modified since last time
1512 /// FALSE - if file is up to date
1513 ///
1514 
1515 Bool_t TDocOutput::IsModified(TClass * classPtr, EFileType type)
1516 {
1517  TString sourceFile;
1518  TString classname(classPtr->GetName());
1519  TString filename;
1520  TString dir;
1521 
1522  switch (type) {
1523  case kSource:
1524  {
1525  TString declFile;
1526  if (classPtr->GetImplFileLine()) {
1527  fHtml->GetImplFileName(classPtr, kTRUE, sourceFile);
1528  }
1529  fHtml->GetDeclFileName(classPtr, kTRUE, declFile);
1530  Long64_t size;
1531  Long_t id, flags, iModtime, dModtime;
1532  if (!(gSystem->GetPathInfo(sourceFile, &id, &size, &flags, &iModtime))) {
1533  if (!(gSystem->GetPathInfo(declFile, &id, &size, &flags, &dModtime))) {
1534  if (iModtime < dModtime) {
1535  // decl is newer than impl
1536  sourceFile = declFile;
1537  }
1538  }
1539  }
1540  dir = "src";
1541  gSystem->PrependPathName(fHtml->GetOutputDir(), dir);
1542  filename = classname;
1543  NameSpace2FileName(filename);
1544  gSystem->PrependPathName(dir, filename);
1545  if (classPtr->GetImplFileLine())
1546  filename += ".cxx.html";
1547  else
1548  filename += ".h.html";
1549  break;
1550  }
1551 
1552  case kInclude:
1553  fHtml->GetDeclFileName(classPtr, kFALSE, filename);
1554  filename = gSystem->BaseName(filename);
1555  fHtml->GetDeclFileName(classPtr, kTRUE, sourceFile);
1556  gSystem->PrependPathName(fHtml->GetOutputDir(), filename);
1557  break;
1558 
1559  case kTree:
1560  fHtml->GetDeclFileName(classPtr, kTRUE, sourceFile);
1561  NameSpace2FileName(classname);
1562  gSystem->PrependPathName(fHtml->GetOutputDir(), classname);
1563  filename = classname;
1564  filename += "_Tree.pdf";
1565  break;
1566 
1567  case kDoc:
1568  {
1569  TString declFile;
1570  if (classPtr->GetImplFileLine()) {
1571  fHtml->GetImplFileName(classPtr, kTRUE, sourceFile);
1572  }
1573  fHtml->GetDeclFileName(classPtr, kTRUE, declFile);
1574  Long64_t size;
1575  Long_t id, flags, iModtime, dModtime;
1576  if (!(gSystem->GetPathInfo(sourceFile, &id, &size, &flags, &iModtime))) {
1577  if (!(gSystem->GetPathInfo(declFile, &id, &size, &flags, &dModtime))) {
1578  if (iModtime < dModtime) {
1579  // decl is newer than impl
1580  sourceFile = declFile;
1581  }
1582  }
1583  }
1584  filename = classname;
1585  NameSpace2FileName(filename);
1586  gSystem->PrependPathName(fHtml->GetOutputDir(), filename);
1587  filename += ".html";
1588  break;
1589  }
1590 
1591  default:
1592  Error("IsModified", "Unknown file type !");
1593  }
1594 
1595  R__LOCKGUARD(GetHtml()->GetMakeClassMutex());
1596 
1597  // Get info about a file
1598  Long64_t size;
1599  Long_t id, flags, sModtime, dModtime;
1600 
1601  if (!(gSystem->GetPathInfo(sourceFile, &id, &size, &flags, &sModtime))) {
1602  if (!(gSystem->GetPathInfo(filename, &id, &size, &flags, &dModtime))) {
1603  return (sModtime > dModtime);
1604  }
1605  }
1606 
1607  return kTRUE;
1608 }
1609 
1610 
1611 ////////////////////////////////////////////////////////////////////////////////
1612 /// Replace "::" in name by "__"
1613 /// Replace "<", ">", " ", ",", "~", "=" in name by "_"
1614 /// Replace "A::X<A::Y>" by "A::X<-p0Y>",
1615 /// "A::B::X<A::B::Y>" by "A::B::X<-p1Y>", etc
1616 
1617 void TDocOutput::NameSpace2FileName(TString& name)
1618 {
1619  TString encScope(name);
1620  Ssiz_t posTemplate = encScope.Index('<');
1621  if (posTemplate != kNPOS) {
1622  // strip default template params
1623  name = fHtml->ShortType(name);
1624  TString templateArgs = encScope(posTemplate, encScope.Length());
1625  encScope.Remove(posTemplate, encScope.Length());
1626  // shorten the name a bit:
1627  // convert A::B::X<A::B::Y> to A::X<-p1Y>, i.e.
1628  // the filename A__X_A__Y_ to A__X_-p1Y_
1629  // The rule: if the enclosing scope up to the N-th scope matches,
1630  // the name becomes -pN
1631  Ssiz_t posName = encScope.Last(':');
1632  if (posName != kNPOS) {
1633  Int_t numDblColumn = encScope.CountChar(':');
1634  while (numDblColumn > 1) {
1635  encScope.Remove(posName + 1, encScope.Length());
1636  numDblColumn -= 2;
1637  templateArgs.ReplaceAll(encScope, TString::Format("-p%d", numDblColumn / 2));
1638  encScope.Remove(encScope.Length() - 2, 2);
1639  posName = encScope.Last(':');
1640  if (posName == kNPOS)
1641  break; // should be handled by numDblColumn...
1642  }
1643  name.Replace(posTemplate, name.Length(), templateArgs);
1644  }
1645  }
1646 
1647  if (name.Length() > 240) { // really 240! It might get some extra prefix or extension
1648  // 8.3 is dead, but e.g. ext2 can only hold 255 chars in a file name.
1649  // So mangle name to "beginning_of_name"-h"hash"."extension", where
1650  // beginning_of_name is short enough such that the full name is <255 characters.
1651 
1652  TString hash;
1653  TDocParser::AnchorFromLine(name, hash);
1654  hash.Prepend("-h");
1655  Ssiz_t posDot = name.Last('.');
1656  TString ext;
1657  if (posDot != kNPOS)
1658  ext = name(posDot, name.Length());
1659  Ssiz_t namelen = 240 - hash.Length() - ext.Length();
1660  name = name(0, namelen) + hash + ext;
1661  }
1662 
1663  const char* replaceWhat = ":<> ,~=";
1664  for (Ssiz_t i=0; i < name.Length(); ++i)
1665  if (strchr(replaceWhat, name[i]))
1666  name[i] = '_';
1667 }
1668 
1669 ////////////////////////////////////////////////////////////////////////////////
1670 /// Write links to files indir/*.txt, indir/*.html (non-recursive) to out.
1671 /// If one of the files is called "index.{html,txt}" it will be
1672 /// included in out (instead of copying it to outdir and generating a link
1673 /// to linkdir). txt files are passed through Convert().
1674 /// The files' links are sorted alphabetically.
1675 
1676 void TDocOutput::ProcessDocInDir(std::ostream& out, const char* indir,
1677  const char* outdir, const char* linkdir)
1678 {
1679  R__LOCKGUARD(GetHtml()->GetMakeClassMutex());
1680 
1681  void * dirHandle = gSystem->OpenDirectory(indir);
1682  if (!dirHandle) return;
1683 
1684  const char* entry = 0;
1685  std::list<std::string> files;
1686  while ((entry = gSystem->GetDirEntry(dirHandle))) {
1687  FileStat_t stat;
1688  TString filename(entry);
1689  gSystem->PrependPathName(indir, filename);
1690  if (gSystem->GetPathInfo(filename, stat)) // funny ret
1691  continue;
1692  if (!R_ISREG(stat.fMode)) continue;
1693 
1694  if (TString(entry).BeginsWith("index.", TString::kIgnoreCase)) {
1695  // This is the part we put directly (verbatim) into the module index.
1696  // If it ends on ".txt" we run Convert first.
1697  if (filename.EndsWith(".txt", TString::kIgnoreCase)) {
1698  std::ifstream in(filename);
1699  if (in) {
1700  out << "<pre>"; // this is what e.g. the html directive expects
1701  TDocParser parser(*this);
1702  parser.Convert(out, in, "./", kFALSE /* no code */, kTRUE /*process Directives*/);
1703  out << "</pre>";
1704  }
1705  } else if (filename.EndsWith(".html", TString::kIgnoreCase)) {
1706  std::ifstream in(filename);
1707  TString line;
1708  while (in) {
1709  if (!line.ReadLine(in)) break;
1710  out << line << std::endl;
1711  }
1712  } else
1713  files.push_back(filename.Data());
1714  } else
1715  files.push_back(filename.Data());
1716  }
1717 
1718  std::stringstream furtherReading;
1719  files.sort();
1720  for (std::list<std::string>::const_iterator iFile = files.begin();
1721  iFile != files.end(); ++iFile) {
1722  TString filename(iFile->c_str());
1723  if (gSystem->AccessPathName(outdir))
1724  if (gSystem->mkdir(outdir, kTRUE) == -1)
1725  // bad - but let's still try to create the output
1726  Error("CreateModuleIndex", "Cannot create output directory %s", outdir);
1727 
1728  TString outfile(gSystem->BaseName(filename));
1729  gSystem->PrependPathName(outdir, outfile);
1730 
1731  if (!filename.EndsWith(".txt", TString::kIgnoreCase)
1732  && !filename.EndsWith(".html", TString::kIgnoreCase)) {
1733  // copy to outdir, who know whether it's needed...
1734  if (gSystem->CopyFile(filename, outfile, kTRUE) == -1) {
1735  Error("CreateModuleIndex", "Cannot copy file %s to %s",
1736  filename.Data(), outfile.Data());
1737  continue;
1738  }
1739  continue;
1740  }
1741 
1742  // Just copy and link this page.
1743  if (outfile.EndsWith(".txt", TString::kIgnoreCase)) {
1744  // convert first
1745  outfile.Remove(outfile.Length()-3, 3);
1746  outfile += "html";
1747  std::ifstream inFurther(filename);
1748  std::ofstream outFurther(outfile);
1749  if (inFurther && outFurther) {
1750  outFurther << "<pre>"; // this is what e.g. the html directive expects
1751  TDocParser parser(*this);
1752  parser.Convert(outFurther, inFurther, "../", kFALSE /*no code*/, kTRUE /*process Directives*/);
1753  outFurther << "</pre>";
1754  }
1755  } else {
1756  if (gSystem->CopyFile(filename, outfile, kTRUE) == -1)
1757  continue;
1758  }
1759  TString showname(gSystem->BaseName(outfile));
1760  furtherReading << "<a class=\"linkeddoc\" href=\"" << linkdir << "/" << showname << "\">";
1761  showname.Remove(showname.Length() - 5, 5); // .html
1762  showname.ReplaceAll("_", " ");
1763  ReplaceSpecialChars(furtherReading, showname);
1764  furtherReading << "</a> " << std::endl;
1765  }
1766 
1767  gSystem->FreeDirectory(dirHandle);
1768  if (furtherReading.str().length())
1769  out << "<h3>Further Reading</h3><div id=\"furtherreading\">" << std::endl
1770  << furtherReading.str() << "</div><h3>List of Classes</h3>" << std::endl;
1771 }
1772 
1773 ////////////////////////////////////////////////////////////////////////////////
1774 /// Create a reference to a class documentation page.
1775 /// str encloses the text to create the reference for (e.g. name of instance).
1776 /// comment will be added e.g. as tooltip text.
1777 /// After the reference is put into str.String(), str will enclose the reference
1778 /// and the original text. Example:
1779 /// Input:
1780 /// str.String(): "a gHtml test"
1781 /// str.Begin(): 2
1782 /// str.Length(): 5
1783 /// Output:
1784 /// str.String(): "a <a href="THtml.html">gHtml</a> test"
1785 /// str.Begin(): 2
1786 /// str.Length(): 30
1787 
1788 void TDocOutput::ReferenceEntity(TSubString& str, TClass* entity, const char* comment /*= 0*/)
1789 {
1790  TString link;
1791  fHtml->GetHtmlFileName(entity, link);
1792 
1793  if (comment && !strcmp(comment, entity->GetName()))
1794  comment = "";
1795 
1796  AddLink(str, link, comment);
1797 }
1798 
1799 ////////////////////////////////////////////////////////////////////////////////
1800 /// Create a reference to a data member documentation page.
1801 /// str encloses the text to create the reference for (e.g. name of instance).
1802 /// comment will be added e.g. as tooltip text.
1803 /// After the reference is put into str.String(), str will enclose the reference
1804 /// and the original text. Example:
1805 /// Input:
1806 /// str.String(): "a gHtml test"
1807 /// str.Begin(): 2
1808 /// str.Length(): 5
1809 /// Output:
1810 /// str.String(): "a <a href="THtml.html">gHtml</a> test"
1811 /// str.Begin(): 2
1812 /// str.Length(): 30
1813 
1814 void TDocOutput::ReferenceEntity(TSubString& str, TDataMember* entity, const char* comment /*= 0*/)
1815 {
1816  TString link;
1817  TClass* scope = entity->GetClass();
1818  fHtml->GetHtmlFileName(scope, link);
1819  link += "#";
1820 
1821  TString mangledName;
1822  if (scope) {
1823  mangledName = scope->GetName();
1824  NameSpace2FileName(mangledName);
1825  link += mangledName;
1826  link += ":";
1827  }
1828 
1829  mangledName = entity->GetName();
1830  NameSpace2FileName(mangledName);
1831  link += mangledName;
1832 
1833  TString description;
1834  if (!comment) {
1835  description = entity->GetFullTypeName();
1836  description += " ";
1837  if (scope) {
1838  description += scope->GetName();
1839  description += "::";
1840  }
1841  description += entity->GetName();
1842  comment = description.Data();
1843  }
1844 
1845  if (comment && !strcmp(comment, entity->GetName()))
1846  comment = "";
1847 
1848  AddLink(str, link, comment);
1849 }
1850 
1851 ////////////////////////////////////////////////////////////////////////////////
1852 /// Create a reference to a type documentation page.
1853 /// str encloses the text to create the reference for (e.g. name of instance).
1854 /// comment will be added e.g. as tooltip text.
1855 /// After the reference is put into str.String(), str will enclose the reference
1856 /// and the original text. Example:
1857 /// Input:
1858 /// str.String(): "a gHtml test"
1859 /// str.Begin(): 2
1860 /// str.Length(): 5
1861 /// Output:
1862 /// str.String(): "a <a href="THtml.html">gHtml</a> test"
1863 /// str.Begin(): 2
1864 /// str.Length(): 30
1865 
1866 void TDocOutput::ReferenceEntity(TSubString& str, TDataType* entity, const char* comment /*= 0*/)
1867 {
1868  TString mangledEntity(entity->GetName());
1869  NameSpace2FileName(mangledEntity);
1870 
1871  TString link;
1872  TClassDocInfo* cdi = 0;
1873  bool isClassTypedef = entity->GetType() == -1;
1874  if (isClassTypedef)
1875  /* is class/ struct / union */
1876  isClassTypedef = isClassTypedef && (entity->Property() & 7);
1877  if (isClassTypedef) {
1878  std::string shortTypeName(fHtml->ShortType(entity->GetFullTypeName()));
1879  cdi = (TClassDocInfo*) GetHtml()->GetListOfClasses()->FindObject(shortTypeName.c_str());
1880  }
1881  if (cdi) {
1882  link = mangledEntity + ".html";
1883  } else {
1884  link = "ListOfTypes.html#";
1885  link += mangledEntity;
1886  }
1887 
1888  if (comment && !strcmp(comment, entity->GetName()))
1889  comment = "";
1890 
1891  AddLink(str, link, comment);
1892 }
1893 
1894 ////////////////////////////////////////////////////////////////////////////////
1895 /// Create a reference to a method documentation page.
1896 /// str encloses the text to create the reference for (e.g. name of instance).
1897 /// comment will be added e.g. as tooltip text.
1898 /// After the reference is put into str.String(), str will enclose the reference
1899 /// and the original text. Example:
1900 /// Input:
1901 /// str.String(): "a gHtml test"
1902 /// str.Begin(): 2
1903 /// str.Length(): 5
1904 /// Output:
1905 /// str.String(): "a <a href="THtml.html">gHtml</a> test"
1906 /// str.Begin(): 2
1907 /// str.Length(): 30
1908 
1909 void TDocOutput::ReferenceEntity(TSubString& str, TMethod* entity, const char* comment /*= 0*/)
1910 {
1911  TString link;
1912  TClass* scope = entity->GetClass();
1913  fHtml->GetHtmlFileName(scope, link);
1914  link += "#";
1915 
1916  TString mangledName(scope->GetName());
1917  NameSpace2FileName(mangledName);
1918  link += mangledName;
1919  link += ":";
1920 
1921  mangledName = entity->GetName();
1922  NameSpace2FileName(mangledName);
1923  link += mangledName;
1924 
1925  TString description;
1926  if (!comment && entity->GetClass()) {
1927  TIter iMeth(scope->GetListOfMethods());
1928  TMethod* mCand = 0;
1929  while ((mCand = (TMethod*)iMeth()))
1930  if (!strcmp(mCand->GetName(), entity->GetName())) {
1931  if (description.Length()) {
1932  description += " or overloads";
1933  break;
1934  }
1935  description = mCand->GetPrototype();
1936  }
1937  comment = description.Data();
1938  }
1939 
1940  if (comment && !strcmp(comment, entity->GetName()))
1941  comment = "";
1942 
1943  AddLink(str, link, comment);
1944 }
1945 
1946 ////////////////////////////////////////////////////////////////////////////////
1947 /// Check whether reference is a relative reference, and can (or should)
1948 /// be prependen by relative paths. For HTML, check that it doesn't start
1949 /// with "http://" or "https://"
1950 
1951 Bool_t TDocOutput::ReferenceIsRelative(const char* reference) const
1952 {
1953  return !reference ||
1954  strncmp(reference, "http", 4) ||
1955  (strncmp(reference + 4, "://", 3) && strncmp(reference + 4, "s://", 4));
1956 }
1957 
1958 ////////////////////////////////////////////////////////////////////////////////
1959 /// Replace ampersand, less-than and greater-than character, writing to out.
1960 /// If 0 is returned, no replacement needs to be done.
1961 
1962 const char* TDocOutput::ReplaceSpecialChars(char c)
1963 {
1964  /*
1965  if (fEscFlag) {
1966  fEscFlag = kFALSE;
1967  return buf;
1968  } else if (c == fEsc) {
1969  // text.Remove(pos, 1); - NO! we want to keep it nevertheless!
1970  fEscFlag = kTRUE;
1971  return buf;
1972  }
1973 
1974  */
1975  switch (c) {
1976  case '<': return "&lt;";
1977  case '&': return "&amp;";
1978  case '>': return "&gt;";
1979  };
1980  return 0;
1981 }
1982 
1983 ////////////////////////////////////////////////////////////////////////////////
1984 /// Replace ampersand, less-than and greater-than character
1985 ///
1986 ///
1987 /// Input: text - text where replacement will happen,
1988 /// pos - index of char to be replaced; will point to next char to be
1989 /// replaced when function returns
1990 ///
1991 
1992 void TDocOutput::ReplaceSpecialChars(TString& text, Ssiz_t &pos)
1993 {
1994  const char c = text[pos];
1995  const char* replaced = ReplaceSpecialChars(c);
1996  if (replaced) {
1997  text.Replace(pos, 1, replaced);
1998  pos += strlen(replaced) - 1;
1999  }
2000  ++pos;
2001 }
2002 
2003 ////////////////////////////////////////////////////////////////////////////////
2004 /// Replace ampersand, less-than and greater-than character
2005 ///
2006 ///
2007 /// Input: text - text where replacement will happen,
2008 ///
2009 
2010 void TDocOutput::ReplaceSpecialChars(TString& text) {
2011  Ssiz_t pos = 0;
2012  while (pos < text.Length())
2013  ReplaceSpecialChars(text, pos);
2014 }
2015 
2016 ////////////////////////////////////////////////////////////////////////////////
2017 /// Replace ampersand, less-than and greater-than characters, writing to out
2018 ///
2019 ///
2020 /// Input: out - output file stream
2021 /// string - pointer to an array of characters
2022 ///
2023 
2024 void TDocOutput::ReplaceSpecialChars(std::ostream& out, const char *string)
2025 {
2026  while (string && *string) {
2027  const char* replaced = ReplaceSpecialChars(*string);
2028  if (replaced)
2029  out << replaced;
2030  else
2031  out << *string;
2032  string++;
2033  }
2034 }
2035 
2036 ////////////////////////////////////////////////////////////////////////////////
2037 /// Run filename".dot", creating filename".png", and - if outMap is !=0,
2038 /// filename".map", which gets then included literally into outMap.
2039 
2040 Bool_t TDocOutput::RunDot(const char* filename, std::ostream* outMap /* =0 */,
2041  EGraphvizTool gvwhat /*= kDot*/) {
2042  if (!fHtml->HaveDot())
2043  return kFALSE;
2044 
2045  TString runDot;
2046  switch (gvwhat) {
2047  case kNeato: runDot = "neato"; break;
2048  case kFdp: runDot = "fdp"; break;
2049  case kCirco: runDot = "circo"; break;
2050  default: runDot = "dot";
2051  };
2052  if (fHtml->GetDotDir() && *fHtml->GetDotDir())
2053  gSystem->PrependPathName(fHtml->GetDotDir(), runDot);
2054  runDot += " -q1 -Tpng -o";
2055  runDot += filename;
2056  runDot += ".png ";
2057  if (outMap) {
2058  runDot += "-Tcmap -o";
2059  runDot += filename;
2060  runDot += ".map ";
2061  }
2062  runDot += filename;
2063  runDot += ".dot";
2064 
2065  if (gDebug > 3)
2066  Info("RunDot", "Running: %s", runDot.Data());
2067  Int_t retDot = gSystem->Exec(runDot);
2068  if (gDebug < 4 && !retDot)
2069  gSystem->Unlink(Form("%s.dot", filename));
2070 
2071  if (!retDot && outMap) {
2072  std::ifstream inmap(Form("%s.map", filename));
2073  std::string line;
2074  std::getline(inmap, line);
2075  if (inmap && !inmap.eof()) {
2076  *outMap << "<map name=\"Map" << gSystem->BaseName(filename)
2077  << "\" id=\"Map" << gSystem->BaseName(filename) << "\">" << std::endl;
2078  while (inmap && !inmap.eof()) {
2079  if (line.compare(0, 6, "<area ") == 0) {
2080  size_t posEndTag = line.find('>');
2081  if (posEndTag != std::string::npos)
2082  line.replace(posEndTag, 1, "/>");
2083  }
2084  *outMap << line << std::endl;
2085  std::getline(inmap, line);
2086  }
2087  *outMap << "</map>" << std::endl;
2088  }
2089  inmap.close();
2090  if (gDebug < 7)
2091  gSystem->Unlink(Form("%s.map", filename));
2092  }
2093 
2094  if (retDot) {
2095  Error("RunDot", "Error running %s!", runDot.Data());
2096  fHtml->SetFoundDot(kFALSE);
2097  return kFALSE;
2098  }
2099 
2100  return kTRUE;
2101 }
2102 
2103 
2104 ////////////////////////////////////////////////////////////////////////////////
2105 /// Write HTML header
2106 ///
2107 /// Internal method invoked by the overload
2108 
2109 void TDocOutput::WriteHtmlHeader(std::ostream& out, const char *titleNoSpecial,
2110  const char* dir /*=""*/, TClass *cls /*=0*/,
2111  const char* header)
2112 {
2113  std::ifstream addHeaderFile(header);
2114 
2115  if (!addHeaderFile.good()) {
2116  Warning("THtml::WriteHtmlHeader",
2117  "Can't open html header file %s\n", header);
2118  return;
2119  }
2120 
2121  TString declFileName;
2122  if (cls) fHtml->GetDeclFileName(cls, kFALSE, declFileName);
2123  TString implFileName;
2124  if (cls) fHtml->GetImplFileName(cls, kFALSE, implFileName);
2125 
2126  const TString& charset = GetHtml()->GetCharset();
2127  TDatime date;
2128  TString strDate(date.AsString());
2129  TString line;
2130 
2131  while (!addHeaderFile.eof()) {
2132 
2133  line.ReadLine(addHeaderFile, kFALSE);
2134  if (addHeaderFile.eof())
2135  break;
2136 
2137  if (line) {
2138 
2139  if (!cls && (
2140  line.Index("%CLASS%") != kNPOS ||
2141  line.Index("%INCFILE%") != kNPOS ||
2142  line.Index("%SRCFILE%") != kNPOS))
2143  continue; // skip class line for non-class files
2144 
2145  TString txt(line);
2146 
2147  txt.ReplaceAll("%TITLE%", titleNoSpecial);
2148  txt.ReplaceAll("%DATE%", strDate);
2149  txt.ReplaceAll("%RELDIR%", dir);
2150  txt.ReplaceAll("%CHARSET%", charset);
2151 
2152  if (cls) {
2153  txt.ReplaceAll("%CLASS%", cls->GetName());
2154  txt.ReplaceAll("%INCFILE%", declFileName);
2155  txt.ReplaceAll("%SRCFILE%", implFileName);
2156  }
2157 
2158  out << txt << std::endl;
2159  }
2160  }
2161 }
2162 
2163 ////////////////////////////////////////////////////////////////////////////////
2164 /// Write HTML header
2165 ///
2166 ///
2167 /// Input: out - output file stream
2168 /// title - title for the HTML page
2169 /// cls - current class
2170 /// dir - relative directory to reach the top
2171 /// ("" for html doc, "../" for src/*cxx.html etc)
2172 ///
2173 /// evaluates the Root.Html.Header setting:
2174 /// * if not set, the standard header is written. (ROOT)
2175 /// * if set, and ends with a "+", the standard header is written and this file
2176 /// included afterwards. (ROOT, USER)
2177 /// * if set but doesn't end on "+" the file specified will be written instead
2178 /// of the standard header (USER)
2179 ///
2180 /// Any occurrence of "%TITLE%" (without the quotation marks) in the user
2181 /// provided header file will be replaced by the value of this method's
2182 /// parameter "title" before written to the output file. %CLASS% is replaced by
2183 /// the class name, %INCFILE% by the header file name as given by
2184 /// TClass::GetDeclFileName() and %SRCFILE% by the source file name as given by
2185 /// TClass::GetImplFileName(). If the header is written for a non-class page,
2186 /// i.e. cls==0, lines containing %CLASS%, %INCFILE%, or %SRCFILE% will be
2187 /// skipped.
2188 
2189 void TDocOutput::WriteHtmlHeader(std::ostream& out, const char *title,
2190  const char* dir /*=""*/, TClass *cls/*=0*/)
2191 {
2192  TString userHeader = GetHtml()->GetHeader();
2193  TString noSpecialCharTitle(title);
2194  ReplaceSpecialChars(noSpecialCharTitle);
2195 
2196  Ssiz_t lenUserHeader = userHeader.Length();
2197  // standard header output if Root.Html.Header is not set, or it's set and it ends with a "+".
2198  Bool_t bothHeaders = lenUserHeader > 0 && userHeader[lenUserHeader - 1] == '+';
2199  if (lenUserHeader == 0 || bothHeaders) {
2200  TString header("header.html");
2201  gSystem->PrependPathName(fHtml->GetEtcDir(), header);
2202  WriteHtmlHeader(out, noSpecialCharTitle, dir, cls, header);
2203  }
2204 
2205  if (lenUserHeader != 0) {
2206  if (bothHeaders)
2207  userHeader.Remove(lenUserHeader - 1);
2208  WriteHtmlHeader(out, noSpecialCharTitle, dir, cls, userHeader);
2209  };
2210 }
2211 
2212 ////////////////////////////////////////////////////////////////////////////////
2213 /// Write HTML footer
2214 ///
2215 /// Internal method invoked by the overload
2216 
2217 void TDocOutput::WriteHtmlFooter(std::ostream& out, const char* /*dir*/,
2218  const char* lastUpdate, const char* author,
2219  const char* copyright, const char* footer)
2220 {
2221  static const char* templateSITags[TDocParser::kNumSourceInfos] = { "%UPDATE%", "%AUTHOR%", "%COPYRIGHT%", "%CHANGED%", "%GENERATED%"};
2222 
2223  TString today;
2224  TDatime dtToday;
2225  today.Form("%d-%02d-%02d %02d:%02d", dtToday.GetYear(), dtToday.GetMonth(), dtToday.GetDay(), dtToday.GetHour(), dtToday.GetMinute());
2226 
2227  TString datimeString;
2228  if (!lastUpdate || !lastUpdate[0]) {
2229  lastUpdate = today;
2230  }
2231  const char* siValues[TDocParser::kNumSourceInfos] = { lastUpdate, author, copyright, lastUpdate, today };
2232 
2233  std::ifstream addFooterFile(footer);
2234 
2235  if (!addFooterFile.good()) {
2236  Warning("THtml::WriteHtmlFooter",
2237  "Can't open html footer file %s\n", footer);
2238  return;
2239  }
2240 
2241  TString line;
2242  while (!addFooterFile.eof()) {
2243 
2244  line.ReadLine(addFooterFile, kFALSE);
2245  if (addFooterFile.eof())
2246  break;
2247 
2248  if (!line)
2249  continue;
2250 
2251  for (Int_t siTag = 0; siTag < (Int_t) TDocParser::kNumSourceInfos; ++siTag) {
2252  Ssiz_t siPos = line.Index(templateSITags[siTag]);
2253  if (siPos != kNPOS) {
2254  if (siValues[siTag] && siValues[siTag][0])
2255  line.Replace(siPos, strlen(templateSITags[siTag]), siValues[siTag]);
2256  else
2257  line = ""; // skip e.g. %AUTHOR% lines if no author is set
2258  }
2259  }
2260 
2261  out << line << std::endl;
2262  }
2263 
2264 }
2265 
2266 ////////////////////////////////////////////////////////////////////////////////
2267 /// Write HTML footer
2268 ///
2269 ///
2270 /// Input: out - output file stream
2271 /// dir - usually equal to "" or "../", depends of
2272 /// current file directory position, i.e. if
2273 /// file is in the fHtml->GetOutputDir(), then dir will be ""
2274 /// lastUpdate - last update string
2275 /// author - author's name
2276 /// copyright - copyright note
2277 ///
2278 /// Allows optional user provided footer to be written. Root.Html.Footer holds
2279 /// the file name for this footer. For details see THtml::WriteHtmlHeader (here,
2280 /// the "+" means the user's footer is written in front of Root's!) Occurrences
2281 /// of %AUTHOR%, %CHANGED%, %GENERATED%, and %COPYRIGHT% in the user's file are replaced by
2282 /// their corresponding values (author, lastUpdate, today, and copyright) before
2283 /// written to out.
2284 /// If no author is set (author == "", e.g. for ClassIndex.html") skip the whole
2285 /// line of the footer template containing %AUTHOR%. Accordingly for %COPYRIGHT%.
2286 
2287 void TDocOutput::WriteHtmlFooter(std::ostream& out, const char *dir,
2288  const char *lastUpdate, const char *author,
2289  const char *copyright)
2290 {
2291  out << std::endl;
2292 
2293  TString userFooter = GetHtml()->GetFooter();
2294 
2295  if (userFooter.Length() != 0) {
2296  TString footer(userFooter);
2297  if (footer.EndsWith("+"))
2298  footer.Remove(footer.Length() - 1);
2299  WriteHtmlFooter(out, dir, lastUpdate, author, copyright, footer);
2300  };
2301 
2302  if (userFooter.Length() == 0 || userFooter.EndsWith("+")) {
2303  TString footer("footer.html");
2304  gSystem->PrependPathName(fHtml->GetEtcDir(), footer);
2305  WriteHtmlFooter(out, dir, lastUpdate, author, copyright, footer);
2306  }
2307 }
2308 
2309 ////////////////////////////////////////////////////////////////////////////////
2310 /// Create a div containing links to all topmost modules
2311 
2312 void TDocOutput::WriteModuleLinks(std::ostream& out)
2313 {
2314  if (fHtml->GetListOfModules()->GetSize()) {
2315  out << "<div id=\"indxModules\"><h4>Modules</h4>" << std::endl;
2316  // find index chars
2317  fHtml->SortListOfModules();
2318  TIter iModule(fHtml->GetListOfModules());
2319  TModuleDocInfo* module = 0;
2320  while ((module = (TModuleDocInfo*) iModule())) {
2321  if (!module->GetName() || strchr(module->GetName(), '/'))
2322  continue;
2323  if (module->IsSelected()) {
2324  TString name(module->GetName());
2325  name.ToUpper();
2326  out << "<a href=\"" << name << "_Index.html\">"
2327  << name << "</a>" << std::endl;
2328  }
2329  }
2330  out<< "</div><br />" << std::endl;
2331  }
2332 }
2333 
2334 ////////////////////////////////////////////////////////////////////////////////
2335 /// Create a div containing the line numbers (for a source listing) 1 to nLines.
2336 /// Create links to the source file's line number and anchors, such that one can
2337 /// jump to SourceFile.cxx.html#27 (using the anchor), and one can copy and paste
2338 /// the link into e.g. gdb to get the text "SourceFile.cxx:27".
2339 
2340 void TDocOutput::WriteLineNumbers(std::ostream& out, Long_t nLines, const TString& infileBase) const
2341 {
2342  out << "<div id=\"linenums\">";
2343  for (Long_t i = 0; i < nLines; ++i) {
2344  // &nbsp; to force correct line height
2345  out << "<div class=\"ln\">&nbsp;<span class=\"lnfile\">" << infileBase
2346  << ":</span><a name=\"" << i + 1 << "\" href=\"#" << i + 1
2347  << "\" class=\"ln\">" << i + 1 << "</a></div>";
2348  }
2349  out << "</div>" << std::endl;
2350 
2351 }
2352 
2353 ////////////////////////////////////////////////////////////////////////////////
2354 /// Create a div containing links to all modules
2355 
2356 void TDocOutput::WriteModuleLinks(std::ostream& out, TModuleDocInfo* super)
2357 {
2358  if (super->GetSub().GetSize()) {
2359  TString superName(super->GetName());
2360  superName.ToUpper();
2361  out << "<div id=\"indxModules\"><h4>" << superName << " Modules</h4>" << std::endl;
2362  // find index chars
2363  super->GetSub().Sort();
2364  TIter iModule(&super->GetSub());
2365  TModuleDocInfo* module = 0;
2366  while ((module = (TModuleDocInfo*) iModule())) {
2367  if (module->IsSelected()) {
2368  TString name(module->GetName());
2369  name.ToUpper();
2370  TString link(name);
2371  link.ReplaceAll("/", "_");
2372  Ssiz_t posSlash = name.Last('/');
2373  if (posSlash != kNPOS)
2374  name.Remove(0, posSlash + 1);
2375  out << "<a href=\"" << link << "_Index.html\">" << name << "</a>" << std::endl;
2376  }
2377  }
2378  out<< "</div><br />" << std::endl;
2379  }
2380 }
2381 
2382 ////////////////////////////////////////////////////////////////////////////////
2383 /// Write a search link or a search box, based on THtml::GetSearchStemURL()
2384 /// and THtml::GetSearchEngine(). The first one is preferred.
2385 
2386 void TDocOutput::WriteSearch(std::ostream& out)
2387 {
2388  // e.g. searchCmd = "http://www.google.com/search?q=%s+site%3A%u+-site%3A%u%2Fsrc%2F+-site%3A%u%2Fexamples%2F";
2389  const TString& searchCmd = GetHtml()->GetSearchStemURL();
2390  const TString& searchEngine = GetHtml()->GetSearchEngine();
2391 
2392  if (!searchCmd.Length() && !searchEngine.Length())
2393  return;
2394 
2395  if (searchCmd.Length()) {
2396  TUrl url(searchCmd);
2397  TString serverName(url.GetHost());
2398  if (serverName.Length()) {
2399  serverName.Prepend(" title=\"");
2400  serverName += "\" ";
2401  }
2402  // create search input
2403  out << "<script type=\"text/javascript\">" << std::endl
2404  << "function onSearch() {" << std::endl
2405  << "var s='" << searchCmd <<"';" << std::endl
2406  << "var ref=String(document.location.href).replace(/https?:\\/\\//,'').replace(/\\/[^\\/]*$/,'').replace(/\\//g,'%2F');" << std::endl
2407  << "window.location.href=s.replace(/%u/ig,ref).replace(/%s/ig,escape(document.searchform.t.value));" << std::endl
2408  << "return false;}" << std::endl
2409  << "</script>" << std::endl
2410  << "<form id=\"searchform\" name=\"searchform\" onsubmit=\"return onSearch()\" action=\"javascript:onSearch();\" method=\"post\">" << std::endl
2411  << "<input name=\"t\" size=\"30\" value=\"Search documentation...\" onfocus=\"if (document.searchform.t.value=='Search documentation...') document.searchform.t.value='';\"></input>" << std::endl
2412  << "<a id=\"searchlink\" " << serverName << " href=\"javascript:onSearch();\" onclick=\"return onSearch()\">Search</a></form>" << std::endl;
2413  } else if (searchEngine.Length())
2414  // create link to search engine page
2415  out << "<a class=\"descrheadentry\" href=\"" << searchEngine
2416  << "\">Search the Class Reference Guide</a>" << std::endl;
2417 }
2418 
2419 
2420 ////////////////////////////////////////////////////////////////////////////////
2421 /// make a link to the description
2422 
2423 void TDocOutput::WriteLocation(std::ostream& out, TModuleDocInfo* module, const char* classname)
2424 {
2425  out << "<div class=\"location\">" << std::endl; // location
2426  const char *productName = fHtml->GetProductName();
2427  out << "<a class=\"locationlevel\" href=\"index.html\">" << productName << "</a>" << std::endl;
2428 
2429  if (module) {
2430  TString modulename(module->GetName());
2431  modulename.ToUpper();
2432  TString modulePart;
2433  TString modulePath;
2434  Ssiz_t pos = 0;
2435  while (modulename.Tokenize(modulePart, pos, "/")) {
2436  if (pos == kNPOS && !classname)
2437  // we are documenting the module itself, no need to link it:
2438  break;
2439  if (modulePath.Length()) modulePath += "_";
2440  modulePath += modulePart;
2441  out << " &#187; <a class=\"locationlevel\" href=\"./" << modulePath << "_Index.html\">" << modulePart << "</a>" << std::endl;
2442  }
2443  }
2444 
2445  TString entityName;
2446  if (classname) entityName = classname;
2447  else if (module) {
2448  entityName = module->GetName();
2449  Ssiz_t posSlash = entityName.Last('/');
2450  if (posSlash != kNPOS)
2451  entityName.Remove(0, posSlash + 1);
2452  entityName.ToUpper();
2453  }
2454  if (entityName.Length()) {
2455  out << " &#187; <a class=\"locationlevel\" href=\"#TopOfPage\">";
2456  ReplaceSpecialChars(out, entityName);
2457  out << "</a>" << std::endl;
2458  }
2459  out << "</div>" << std::endl; // location
2460 }
2461 
2462 
2463 ////////////////////////////////////////////////////////////////////////////////
2464 /// Write the first part of the links shown ontop of each doc page;
2465 /// one <div> has to be closed by caller so additional items can still
2466 /// be added.
2467 
2468 void TDocOutput::WriteTopLinks(std::ostream& out, TModuleDocInfo* module, const char* classname,
2469  Bool_t withLocation)
2470 {
2471  out << "<div id=\"toplinks\">" << std::endl;
2472 
2473  out << "<div class=\"descrhead\"><div class=\"descrheadcontent\">" << std::endl // descrhead line 1
2474  << "<span class=\"descrtitle\">Quick Links:</span>" << std::endl;
2475 
2476  // link to the user home page (if exist)
2477  const char* userHomePage = GetHtml()->GetHomepage();
2478  const char* productName = fHtml->GetProductName();
2479  if (!productName) {
2480  productName = "";
2481  } else if (!strcmp(productName, "ROOT")) {
2482  userHomePage = "";
2483  }
2484  if (userHomePage && *userHomePage)
2485  out << "<a class=\"descrheadentry\" href=\"" << userHomePage << "\">" << productName << "</a>" << std::endl;
2486  out << "<a class=\"descrheadentry\" href=\"http://root.cern.ch\">ROOT Homepage</a>" << std::endl
2487  << "<a class=\"descrheadentry\" href=\"./ClassIndex.html\">Class Index</a>" << std::endl
2488  << "<a class=\"descrheadentry\" href=\"./ClassHierarchy.html\">Class Hierarchy</a></div>" << std::endl;
2489  WriteSearch(out);
2490  out << "</div>" << std::endl; // descrhead, line 1
2491 
2492  if (withLocation) {
2493  out << "</div>" << std::endl; //toplinks
2494  WriteLocation(out, module, classname); // descrhead line 2
2495  }
2496  // else {
2497  // Closed by caller!
2498  // out << "</div>" << std::endl; // toplinks
2499  // }
2500 
2501 }