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