Fix for UBSan build
[platform/upstream/doxygen.git] / src / dirdef.cpp
1 #include "md5.h"
2
3 #include "dirdef.h"
4 #include "filename.h"
5 #include "doxygen.h"
6 #include "util.h"
7 #include "outputlist.h"
8 #include "language.h"
9 #include "message.h"
10 #include "dot.h"
11 #include "layout.h"
12 #include "ftextstream.h"
13
14 //----------------------------------------------------------------------
15 // method implementation
16
17 static int g_dirCount=0;
18
19 DirDef::DirDef(const char *path) : Definition(path,1,path)
20 {
21   bool fullPathNames = Config_getBool("FULL_PATH_NAMES");
22   // get display name (stipping the paths mentioned in STRIP_FROM_PATH)
23   // get short name (last part of path)
24   m_shortName = path;
25   m_diskName = path;
26   if (m_shortName.at(m_shortName.length()-1)=='/')
27   { // strip trailing /
28     m_shortName = m_shortName.left(m_shortName.length()-1);
29   }
30   int pi=m_shortName.findRev('/');
31   if (pi!=-1) 
32   { // remove everything till the last /
33     m_shortName = m_shortName.mid(pi+1);
34   }
35   setLocalName(m_shortName);
36   m_dispName = fullPathNames ? stripFromPath(path) : m_shortName;
37   if (m_dispName.at(m_dispName.length()-1)=='/')
38   { // strip trailing /
39     m_dispName = m_dispName.left(m_dispName.length()-1);
40   }
41   
42   m_fileList   = new FileList;
43   m_usedDirs   = new QDict<UsedDir>(257);
44   m_usedDirs->setAutoDelete(TRUE);
45   m_dirCount   = g_dirCount++;
46   m_level=-1;
47   m_parent=0;
48 }
49
50 DirDef::~DirDef()
51 {
52   delete m_fileList;
53   delete m_usedDirs;
54 }
55
56 bool DirDef::isLinkableInProject() const 
57
58   return !isReference(); 
59 }
60
61 bool DirDef::isLinkable() const 
62
63   return isReference() || isLinkableInProject(); 
64 }
65
66 void DirDef::addSubDir(DirDef *subdir)
67 {
68   m_subdirs.inSort(subdir);
69   subdir->setOuterScope(this);
70   subdir->m_parent=this;
71 }
72
73 void DirDef::addFile(FileDef *fd)
74 {
75   m_fileList->inSort(fd);
76   fd->setDirDef(this);
77 }
78
79 static QCString encodeDirName(const QCString &anchor)
80 {
81   QCString result;
82
83   // convert to md5 hash
84   uchar md5_sig[16];
85   QCString sigStr(33);
86   MD5Buffer((const unsigned char *)anchor.data(),anchor.length(),md5_sig);
87   MD5SigToString(md5_sig,sigStr.data(),33);
88   return sigStr;
89
90   // old algorithm
91
92 //  int l = anchor.length(),i;
93 //  for (i=0;i<l;i++)
94 //  {
95 //    char c = anchor.at(i);
96 //    if ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'))
97 //    {
98 //      result+=c;
99 //    }
100 //    else
101 //    {
102 //      static char hexStr[]="0123456789ABCDEF";
103 //      char escChar[]={ '_', 0, 0, 0 };
104 //      escChar[1]=hexStr[c>>4];
105 //      escChar[2]=hexStr[c&0xf];
106 //      result+=escChar;
107 //    }
108 //  }
109 //  return result;
110 }
111
112 QCString DirDef::getOutputFileBase() const
113 {
114   //printf("DirDef::getOutputFileBase() %s->dir_%s\n",
115   //    m_diskName.data(),encodeDirName(m_diskName).data());
116   return "dir_"+encodeDirName(m_diskName);
117 }
118
119 void DirDef::writeDetailedDescription(OutputList &ol,const QCString &title)
120 {
121   if ((!briefDescription().isEmpty() && Config_getBool("REPEAT_BRIEF")) || 
122       !documentation().isEmpty())
123   {
124     ol.pushGeneratorState();
125       ol.disable(OutputGenerator::Html);
126       ol.writeRuler();
127     ol.popGeneratorState();
128     ol.pushGeneratorState();
129       ol.disableAllBut(OutputGenerator::Html);
130       ol.writeAnchor(0,"details");
131     ol.popGeneratorState();
132     ol.startGroupHeader();
133     ol.parseText(title);
134     ol.endGroupHeader();
135
136     // repeat brief description
137     if (!briefDescription().isEmpty() && Config_getBool("REPEAT_BRIEF"))
138     {
139       ol.parseDoc(briefFile(),briefLine(),this,0,briefDescription(),FALSE,FALSE);
140     }
141     // separator between brief and details
142     if (!briefDescription().isEmpty() && Config_getBool("REPEAT_BRIEF") && 
143         !documentation().isEmpty())
144     {
145       ol.pushGeneratorState();
146         ol.disable(OutputGenerator::Man);
147         ol.disable(OutputGenerator::RTF);
148         // ol.newParagraph();  // FIXME:PARA
149         ol.enableAll();
150         ol.disableAllBut(OutputGenerator::Man);
151         ol.writeString("\n\n");
152       ol.popGeneratorState();
153     }
154
155     // write documentation
156     if (!documentation().isEmpty())
157     {
158       ol.parseDoc(docFile(),docLine(),this,0,documentation()+"\n",TRUE,FALSE);
159     }
160   }
161 }
162
163 void DirDef::writeBriefDescription(OutputList &ol)
164 {
165   if (!briefDescription().isEmpty() && Config_getBool("BRIEF_MEMBER_DESC"))
166   {
167     ol.startParagraph();
168     ol.parseDoc(briefFile(),briefLine(),this,0,briefDescription(),TRUE,FALSE);
169     ol.pushGeneratorState();
170     ol.disable(OutputGenerator::RTF);
171     ol.writeString(" \n");
172     ol.enable(OutputGenerator::RTF);
173
174     if (Config_getBool("REPEAT_BRIEF") ||
175         !documentation().isEmpty()
176        )
177     {
178       ol.disableAllBut(OutputGenerator::Html);
179       ol.startTextLink(0,"details");
180       ol.parseText(theTranslator->trMore());
181       ol.endTextLink();
182     }
183     ol.popGeneratorState();
184
185     //ol.pushGeneratorState();
186     //ol.disable(OutputGenerator::RTF);
187     //ol.newParagraph();
188     //ol.popGeneratorState();
189     ol.endParagraph();
190   }
191   ol.writeSynopsis();
192 }
193
194 void DirDef::writeDirectoryGraph(OutputList &ol)
195 {
196   // write graph dependency graph
197   if (Config_getBool("DIRECTORY_GRAPH") && Config_getBool("HAVE_DOT"))
198   {
199     DotDirDeps dirDep(this);
200     if (!dirDep.isTrivial())
201     {
202       msg("Generating dependency graph for directory %s\n",displayName().data());
203       ol.disable(OutputGenerator::Man);
204       //ol.startParagraph();
205       ol.startDirDepGraph();
206       ol.parseText(theTranslator->trDirDepGraph(shortName()));
207       ol.endDirDepGraph(dirDep);
208       //ol.endParagraph();
209       ol.enableAll();
210     }
211   }
212 }
213
214 void DirDef::writeSubDirList(OutputList &ol)
215 {
216   // write subdir list
217   if (m_subdirs.count()>0)
218   {
219     ol.startMemberHeader("subdirs");
220     ol.parseText(theTranslator->trDir(TRUE,FALSE));
221     ol.endMemberHeader();
222     ol.startMemberList();
223     DirDef *dd=m_subdirs.first();
224     while (dd)
225     {
226       ol.startMemberDeclaration();
227       ol.startMemberItem(dd->getOutputFileBase(),0);
228       ol.parseText(theTranslator->trDir(FALSE,TRUE)+" ");
229       ol.insertMemberAlign();
230       ol.writeObjectLink(dd->getReference(),dd->getOutputFileBase(),0,dd->shortName());
231       ol.endMemberItem();
232       if (!Config_getString("GENERATE_TAGFILE").isEmpty()) 
233       {
234         Doxygen::tagFile << "    <dir>" << convertToXML(dd->displayName()) << "</dir>" << endl;
235       }
236       if (!dd->briefDescription().isEmpty() && Config_getBool("BRIEF_MEMBER_DESC"))
237       {
238         ol.startMemberDescription(dd->getOutputFileBase());
239         ol.parseDoc(briefFile(),briefLine(),dd,0,dd->briefDescription(),
240             FALSE, // indexWords
241             FALSE, // isExample
242             0,     // exampleName
243             TRUE,  // single line
244             TRUE   // link from index
245            );
246         ol.endMemberDescription();
247       }
248       ol.endMemberDeclaration(0,0);
249       dd=m_subdirs.next();
250     }
251
252     ol.endMemberList();
253   }
254 }
255
256 void DirDef::writeFileList(OutputList &ol)
257 {
258   // write file list
259   if (m_fileList->count()>0)
260   {
261     ol.startMemberHeader("files");
262     ol.parseText(theTranslator->trFile(TRUE,FALSE));
263     ol.endMemberHeader();
264     ol.startMemberList();
265     FileDef *fd=m_fileList->first();
266     while (fd)
267     {
268       ol.startMemberDeclaration();
269       ol.startMemberItem(fd->getOutputFileBase(),0);
270       ol.docify(theTranslator->trFile(FALSE,TRUE)+" ");
271       ol.insertMemberAlign();
272       if (fd->isLinkable())
273       {
274         ol.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,fd->name());
275       }
276       else
277       {
278         ol.startBold();
279         ol.docify(fd->name()); 
280         ol.endBold();
281       }
282       if (fd->generateSourceFile())
283       {
284         ol.pushGeneratorState();
285         ol.disableAllBut(OutputGenerator::Html);
286         ol.docify(" ");
287         ol.startTextLink(fd->includeName(),0);
288         ol.docify("[");
289         ol.parseText(theTranslator->trCode());
290         ol.docify("]");
291         ol.endTextLink();
292         ol.popGeneratorState();
293       }
294       if (!Config_getString("GENERATE_TAGFILE").isEmpty()) 
295       {
296         Doxygen::tagFile << "    <file>" << convertToXML(fd->name()) << "</file>" << endl;
297       }
298       ol.endMemberItem();
299       if (!fd->briefDescription().isEmpty() && Config_getBool("BRIEF_MEMBER_DESC"))
300       {
301         ol.startMemberDescription(fd->getOutputFileBase());
302         ol.parseDoc(briefFile(),briefLine(),fd,0,fd->briefDescription(),
303             FALSE, // indexWords
304             FALSE, // isExample
305             0,     // exampleName
306             TRUE,  // single line
307             TRUE   // link from index
308            );
309         ol.endMemberDescription();
310       }
311       ol.endMemberDeclaration(0,0);
312       fd=m_fileList->next();
313     }
314     ol.endMemberList();
315   }
316 }
317
318 void DirDef::startMemberDeclarations(OutputList &ol)
319 {
320   ol.startMemberSections();
321 }
322
323 void DirDef::endMemberDeclarations(OutputList &ol)
324 {
325   ol.endMemberSections();
326 }
327
328 void DirDef::writeDocumentation(OutputList &ol)
329 {
330   static bool generateTreeView = Config_getBool("GENERATE_TREEVIEW");
331   ol.pushGeneratorState();
332   
333   QCString shortTitle=theTranslator->trDirReference(m_shortName);
334   QCString title=theTranslator->trDirReference(m_dispName);
335   startFile(ol,getOutputFileBase(),name(),title,HLI_None,!generateTreeView);
336
337   if (!generateTreeView)
338   {
339     // write navigation path
340     writeNavigationPath(ol);
341     ol.endQuickIndices();
342   }
343
344   startTitle(ol,getOutputFileBase());
345   ol.pushGeneratorState();
346     ol.disableAllBut(OutputGenerator::Html);
347     ol.parseText(shortTitle);
348     ol.enableAll();
349     ol.disable(OutputGenerator::Html);
350     ol.parseText(title);
351   ol.popGeneratorState();
352   endTitle(ol,getOutputFileBase(),title);
353   ol.startContents();
354
355   if (!Config_getString("GENERATE_TAGFILE").isEmpty()) 
356   {
357     Doxygen::tagFile << "  <compound kind=\"dir\">" << endl;
358     Doxygen::tagFile << "    <name>" << convertToXML(displayName()) << "</name>" << endl;
359     Doxygen::tagFile << "    <path>" << convertToXML(name()) << "</path>" << endl;
360     Doxygen::tagFile << "    <filename>" << convertToXML(getOutputFileBase()) << Doxygen::htmlFileExtension << "</filename>" << endl;
361   }
362   
363   //---------------------------------------- start flexible part -------------------------------
364
365   SrcLangExt lang = getLanguage();
366   QListIterator<LayoutDocEntry> eli(
367       LayoutDocManager::instance().docEntries(LayoutDocManager::Directory));
368   LayoutDocEntry *lde;
369   for (eli.toFirst();(lde=eli.current());++eli)
370   {
371     switch (lde->kind())
372     {
373       case LayoutDocEntry::BriefDesc: 
374         writeBriefDescription(ol);
375         break; 
376       case LayoutDocEntry::DirGraph: 
377         writeDirectoryGraph(ol);
378         break; 
379       case LayoutDocEntry::MemberDeclStart: 
380         startMemberDeclarations(ol);
381         break; 
382       case LayoutDocEntry::DirSubDirs: 
383         writeSubDirList(ol);
384         break; 
385       case LayoutDocEntry::DirFiles: 
386         writeFileList(ol);
387         break; 
388       case LayoutDocEntry::MemberDeclEnd: 
389         endMemberDeclarations(ol);
390         break;
391       case LayoutDocEntry::DetailedDesc: 
392         {
393           LayoutDocEntrySection *ls = (LayoutDocEntrySection*)lde;
394           writeDetailedDescription(ol,ls->title(lang));
395         }
396         break;
397       case LayoutDocEntry::ClassIncludes:
398       case LayoutDocEntry::ClassInlineClasses:
399       case LayoutDocEntry::ClassInheritanceGraph:
400       case LayoutDocEntry::ClassNestedClasses:
401       case LayoutDocEntry::ClassCollaborationGraph:
402       case LayoutDocEntry::ClassAllMembersLink:
403       case LayoutDocEntry::ClassUsedFiles:
404       case LayoutDocEntry::NamespaceNestedNamespaces:
405       case LayoutDocEntry::NamespaceClasses:
406       case LayoutDocEntry::NamespaceInlineClasses:
407       case LayoutDocEntry::FileClasses:
408       case LayoutDocEntry::FileNamespaces:
409       case LayoutDocEntry::FileIncludes:
410       case LayoutDocEntry::FileIncludeGraph:
411       case LayoutDocEntry::FileIncludedByGraph: 
412       case LayoutDocEntry::FileSourceLink:
413       case LayoutDocEntry::FileInlineClasses:
414       case LayoutDocEntry::GroupClasses: 
415       case LayoutDocEntry::GroupInlineClasses: 
416       case LayoutDocEntry::GroupNamespaces:
417       case LayoutDocEntry::GroupDirs: 
418       case LayoutDocEntry::GroupNestedGroups: 
419       case LayoutDocEntry::GroupFiles:
420       case LayoutDocEntry::GroupGraph: 
421       case LayoutDocEntry::GroupPageDocs:
422       case LayoutDocEntry::AuthorSection:
423       case LayoutDocEntry::MemberGroups:
424       case LayoutDocEntry::MemberDecl:
425       case LayoutDocEntry::MemberDef:
426       case LayoutDocEntry::MemberDefStart:
427       case LayoutDocEntry::MemberDefEnd:
428         err("Internal inconsistency: member %d should not be part of "
429             "LayoutDocManager::Directory entry list\n",lde->kind());
430         break;
431     }
432   }
433
434   //---------------------------------------- end flexible part -------------------------------
435
436   if (!Config_getString("GENERATE_TAGFILE").isEmpty()) 
437   {
438     writeDocAnchorsToTagFile();
439     Doxygen::tagFile << "  </compound>" << endl;
440   }
441
442   ol.endContents();
443
444   endFileWithNavPath(this,ol);
445
446   ol.popGeneratorState();
447 }
448
449 void DirDef::setLevel()
450 {
451   if (m_level==-1) // level not set before
452   {
453     DirDef *p = parent();
454     if (p)
455     {
456       p->setLevel();
457       m_level = p->level()+1;
458     }
459     else
460     {
461       m_level = 0;
462     }
463   }
464 }
465
466 /** Add as "uses" dependency between \a this dir and \a dir,
467  *  that was caused by a dependency on file \a fd.
468  */ 
469 void DirDef::addUsesDependency(DirDef *dir,FileDef *srcFd,
470                                FileDef *dstFd,bool inherited)
471 {
472   if (this==dir) return; // do not add self-dependencies
473   //static int count=0;
474   //printf("  %d add dependency %s->%s due to %s->%s\n",
475   //    count++,shortName().data(),
476   //    dir->shortName().data(),
477   //    srcFd->name().data(),
478   //    dstFd->name().data());
479
480   // levels match => add direct dependency
481   bool added=FALSE;
482   UsedDir *usedDir = m_usedDirs->find(dir->getOutputFileBase());
483   if (usedDir) // dir dependency already present
484   {
485      FilePair *usedPair = usedDir->findFilePair(
486          srcFd->getOutputFileBase()+dstFd->getOutputFileBase());
487      if (usedPair==0) // new file dependency
488      {
489        //printf("  => new file\n");
490        usedDir->addFileDep(srcFd,dstFd); 
491        added=TRUE;
492      }
493      else
494      {
495        // dir & file dependency already added
496      }
497   }
498   else // new directory dependency
499   {
500     //printf("  => new file\n");
501     usedDir = new UsedDir(dir,inherited);
502     usedDir->addFileDep(srcFd,dstFd); 
503     m_usedDirs->insert(dir->getOutputFileBase(),usedDir);
504     added=TRUE;
505   }
506   if (added)
507   {
508     if (dir->parent())
509     {
510       // add relation to parent of used dir
511       addUsesDependency(dir->parent(),srcFd,dstFd,inherited);
512     }
513     if (parent())
514     {
515       // add relation for the parent of this dir as well
516       parent()->addUsesDependency(dir,srcFd,dstFd,TRUE);
517     }
518   }
519 }
520
521 /** Computes the dependencies between directories
522  */
523 void DirDef::computeDependencies()
524 {
525   FileList *fl = m_fileList;
526   if (fl) 
527   {
528     QListIterator<FileDef> fli(*fl);
529     FileDef *fd;
530     for (fli.toFirst();(fd=fli.current());++fli) // foreach file in dir dd
531     {
532       //printf("  File %s\n",fd->name().data());
533       //printf("** dir=%s file=%s\n",shortName().data(),fd->name().data());
534       QList<IncludeInfo> *ifl = fd->includeFileList();
535       if (ifl)
536       {
537         QListIterator<IncludeInfo> ifli(*ifl); 
538         IncludeInfo *ii;
539         for (ifli.toFirst();(ii=ifli.current());++ifli) // foreach include file
540         {
541           //printf("  > %s\n",ii->includeName.data());
542           //printf("    #include %s\n",ii->includeName.data());
543           if (ii->fileDef && ii->fileDef->isLinkable()) // linkable file
544           {
545             DirDef *usedDir = ii->fileDef->getDirDef();
546             if (usedDir)
547             {
548               // add dependency: thisDir->usedDir
549               //static int count=0;
550               //printf("      %d: add dependency %s->%s\n",count++,name().data(),usedDir->name().data());
551               addUsesDependency(usedDir,fd,ii->fileDef,FALSE);
552             }
553           } 
554         }
555       }
556     }
557   }
558 }
559
560 bool DirDef::isParentOf(DirDef *dir) const
561 {
562   if (dir->parent()==this) // this is a parent of dir 
563     return TRUE;
564   else if (dir->parent()) // repeat for the parent of dir
565     return isParentOf(dir->parent()); 
566   else
567     return FALSE;
568 }
569
570 bool DirDef::depGraphIsTrivial() const
571 {
572   return FALSE;
573 }
574
575 //----------------------------------------------------------------------
576
577 int FilePairDict::compareItems(GCI item1,GCI item2)
578 {
579   FilePair *left  = (FilePair*)item1;
580   FilePair *right = (FilePair*)item2;
581   int orderHi = stricmp(left->source()->name(),right->source()->name());
582   int orderLo = stricmp(left->destination()->name(),right->destination()->name());
583   return orderHi==0 ? orderLo : orderHi;
584 }
585
586 //----------------------------------------------------------------------
587
588 UsedDir::UsedDir(DirDef *dir,bool inherited) :
589    m_dir(dir), m_filePairs(7), m_inherited(inherited)
590 {
591   m_filePairs.setAutoDelete(TRUE);
592 }
593
594 UsedDir::~UsedDir()
595 {
596 }
597
598
599 void UsedDir::addFileDep(FileDef *srcFd,FileDef *dstFd)
600 {
601   m_filePairs.inSort(srcFd->getOutputFileBase()+dstFd->getOutputFileBase(),
602                      new FilePair(srcFd,dstFd));
603 }
604
605 FilePair *UsedDir::findFilePair(const char *name)
606 {
607   QCString n=name;
608   return n.isEmpty() ? 0 : m_filePairs.find(n);
609 }
610
611 DirDef *DirDef::createNewDir(const char *path)
612 {
613   ASSERT(path!=0);
614   DirDef *dir = Doxygen::directories->find(path);
615   if (dir==0) // new dir
616   {
617     //printf("Adding new dir %s\n",path);
618     dir = new DirDef(path);
619     //printf("createNewDir %s short=%s\n",path,dir->shortName().data());
620     Doxygen::directories->inSort(path,dir);
621   }
622   return dir;
623 }
624
625 bool DirDef::matchPath(const QCString &path,QStrList &l)
626 {
627   const char *s=l.first();
628   while (s)
629   {
630     QCString prefix = s;
631     if (stricmp(prefix.left(path.length()),path)==0) // case insensitive compare
632     {
633       return TRUE;
634     }
635     s = l.next();
636   }
637   return FALSE;
638 }
639
640 /*! strip part of \a path if it matches
641  *  one of the paths in the Config_getList("STRIP_FROM_PATH") list
642  */
643 DirDef *DirDef::mergeDirectoryInTree(const QCString &path)
644 {
645   //printf("DirDef::mergeDirectoryInTree(%s)\n",path.data());
646   int p=0,i=0;
647   DirDef *dir=0;
648   while ((i=path.find('/',p))!=-1)
649   {
650     QCString part=path.left(i+1);
651     if (!matchPath(part,Config_getList("STRIP_FROM_PATH")) && part!="/")
652     {
653       dir=createNewDir(part); 
654     }
655     p=i+1;
656   }
657   return dir;
658 }
659
660 void DirDef::writeDepGraph(FTextStream &t)
661 {
662     writeDotDirDepGraph(t,this);
663 }
664
665 //----------------------------------------------------------------------
666
667 static void writePartialDirPath(OutputList &ol,const DirDef *root,const DirDef *target)
668 {
669   if (target->parent()!=root) 
670   {
671     writePartialDirPath(ol,root,target->parent());
672     ol.writeString("&#160;/&#160;");
673   }
674   ol.writeObjectLink(target->getReference(),target->getOutputFileBase(),0,target->shortName());
675 }
676
677 static void writePartialFilePath(OutputList &ol,const DirDef *root,const FileDef *fd)
678 {
679   if (fd->getDirDef() && fd->getDirDef()!=root)
680   {
681     writePartialDirPath(ol,root,fd->getDirDef());
682     ol.writeString("&#160;/&#160;");
683   }
684   if (fd->isLinkable())
685   {
686     ol.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,fd->name());
687   }
688   else
689   {
690     ol.startBold();
691     ol.docify(fd->name());
692     ol.endBold();
693   }
694 }
695
696 void DirRelation::writeDocumentation(OutputList &ol)
697 {
698   static bool generateTreeView = Config_getBool("GENERATE_TREEVIEW");
699   ol.pushGeneratorState();
700   ol.disableAllBut(OutputGenerator::Html);
701
702   QCString shortTitle=theTranslator->trDirRelation(
703                       m_src->shortName()+" &rarr; "+
704                       m_dst->dir()->shortName());
705   QCString title=theTranslator->trDirRelation(
706                  m_src->displayName()+" -> "+
707                  m_dst->dir()->shortName());
708   startFile(ol,getOutputFileBase(),getOutputFileBase(),
709             title,HLI_None,!generateTreeView,m_src->getOutputFileBase());
710
711   if (!generateTreeView)
712   {
713     // write navigation path
714     m_src->writeNavigationPath(ol);
715     ol.endQuickIndices();
716   }
717   ol.startContents();
718
719   ol.writeString("<h3>"+shortTitle+"</h3>");
720   ol.writeString("<table class=\"dirtab\">");
721   ol.writeString("<tr class=\"dirtab\">");
722   ol.writeString("<th class=\"dirtab\">");
723   ol.parseText(theTranslator->trFileIn(m_src->pathFragment()));
724   ol.writeString("</th>");
725   ol.writeString("<th class=\"dirtab\">");
726   ol.parseText(theTranslator->trIncludesFileIn(m_dst->dir()->pathFragment()));
727   ol.writeString("</th>");
728   ol.writeString("</tr>");
729
730   SDict<FilePair>::Iterator fpi(m_dst->filePairs());
731   FilePair *fp;
732   for (fpi.toFirst();(fp=fpi.current());++fpi)
733   {
734     ol.writeString("<tr class=\"dirtab\">");
735     ol.writeString("<td class=\"dirtab\">");
736     writePartialFilePath(ol,m_src,fp->source());
737     ol.writeString("</td>");
738     ol.writeString("<td class=\"dirtab\">");
739     writePartialFilePath(ol,m_dst->dir(),fp->destination());
740     ol.writeString("</td>");
741     ol.writeString("</tr>");
742   }
743   ol.writeString("</table>");
744
745   ol.endContents();
746   
747   endFileWithNavPath(m_src,ol);
748
749   ol.popGeneratorState();
750 }
751
752 //----------------------------------------------------------------------
753 // external functions
754
755 /** In order to create stable, but unique directory names,
756  *  we compute the common part of the path shared by all directories.
757  */
758 static void computeCommonDirPrefix()
759 {
760   QCString path;
761   DirDef *dir;
762   DirSDict::Iterator sdi(*Doxygen::directories);
763   if (Doxygen::directories->count()>0) // we have at least one dir
764   {
765     // start will full path of first dir
766     sdi.toFirst();
767     dir=sdi.current();
768     path=dir->name();
769     int i=path.findRev('/',path.length()-2);
770     path=path.left(i+1);
771     bool done=FALSE;
772     if (i==-1) 
773     {
774       path="";
775     }
776     else
777     {
778       while (!done)
779       {
780         int l = path.length();
781         int count=0;
782         for (sdi.toFirst();(dir=sdi.current());++sdi)
783         {
784           QCString dirName = dir->name();
785           if (dirName.length()>path.length())
786           {
787             if (strncmp(dirName,path,l)!=0) // dirName does not start with path
788             {
789               int i=path.findRev('/',l-2);
790               if (i==-1) // no unique prefix -> stop
791               {
792                 path="";
793                 done=TRUE;
794               }
795               else // restart with shorter path
796               {
797                 path=path.left(i+1);
798                 break;
799               }
800             }
801           }
802           else // dir is shorter than path -> take path of dir as new start
803           {
804             path=dir->name();
805             int i=path.findRev('/',l-2);
806             if (i==-1) // no unique prefix -> stop
807             {
808               path="";
809               done=TRUE;
810             }
811             else // restart with shorter path
812             {
813               path=path.left(i+1);
814             }
815             break;
816           }
817           count++;
818         }
819         if (count==Doxygen::directories->count())
820           // path matches for all directories -> found the common prefix
821         {
822           done=TRUE;
823         }
824       }
825     }
826   }
827   for (sdi.toFirst();(dir=sdi.current());++sdi)
828   {
829     QCString diskName = dir->name().right(dir->name().length()-path.length());
830     dir->setDiskName(diskName);
831     //printf("set disk name: %s -> %s\n",dir->name().data(),diskName.data());
832   }
833 }
834
835 void buildDirectories()
836 {
837   // for each input file
838   FileNameListIterator fnli(*Doxygen::inputNameList); 
839   FileName *fn;
840   for (fnli.toFirst();(fn=fnli.current());++fnli)
841   {
842     FileNameIterator fni(*fn);
843     FileDef *fd;
844     for (;(fd=fni.current());++fni)
845     {
846       //printf("buildDirectories %s\n",fd->name().data());
847       if (fd->getReference().isEmpty() && !fd->isDocumentationFile())
848       {
849         DirDef *dir;
850         if ((dir=Doxygen::directories->find(fd->getPath()))==0) // new directory
851         {
852           dir = DirDef::mergeDirectoryInTree(fd->getPath());
853         }
854         if (dir) dir->addFile(fd);
855       }
856       else
857       {
858         // do something for file imported via tag files.
859       }
860     }
861   }
862
863   //DirDef *root = new DirDef("root:");
864   // compute relations between directories => introduce container dirs.
865   DirDef *dir;
866   DirSDict::Iterator sdi(*Doxygen::directories);
867   for (sdi.toFirst();(dir=sdi.current());++sdi)
868   {
869     //printf("New dir %s\n",dir->displayName().data());
870     QCString name = dir->name();
871     int i=name.findRev('/',name.length()-2);
872     if (i>0)
873     {
874       DirDef *parent = Doxygen::directories->find(name.left(i+1));
875       //if (parent==0) parent=root;
876       if (parent) 
877       {
878         parent->addSubDir(dir); 
879         //printf("DirDef::addSubdir(): Adding subdir\n%s to\n%s\n",
880         //  dir->displayName().data(), parent->displayName().data());
881       }
882     }
883   }
884   computeCommonDirPrefix();
885 }
886
887 void computeDirDependencies()
888 {
889   DirDef *dir;
890   DirSDict::Iterator sdi(*Doxygen::directories);
891   // compute nesting level for each directory
892   for (sdi.toFirst();(dir=sdi.current());++sdi)
893   {
894     dir->setLevel();
895   }
896   // compute uses dependencies between directories
897   for (sdi.toFirst();(dir=sdi.current());++sdi)
898   {
899     //printf("computeDependencies for %s: #dirs=%d\n",dir->name().data(),Doxygen::directories.count());
900     dir->computeDependencies();
901   }
902
903 }
904
905 void generateDirDocs(OutputList &ol)
906 {
907   DirDef *dir;
908   DirSDict::Iterator sdi(*Doxygen::directories);
909   for (sdi.toFirst();(dir=sdi.current());++sdi)
910   {
911     dir->writeDocumentation(ol);
912   }
913   if (Config_getBool("DIRECTORY_GRAPH"))
914   {
915     SDict<DirRelation>::Iterator rdi(Doxygen::dirRelations);
916     DirRelation *dr;
917     for (rdi.toFirst();(dr=rdi.current());++rdi)
918     {
919       dr->writeDocumentation(ol);
920     }
921   }
922 }
923