diff options
Diffstat (limited to 'src/doxygen.cpp')
-rw-r--r-- | src/doxygen.cpp | 10772 |
1 files changed, 10772 insertions, 0 deletions
diff --git a/src/doxygen.cpp b/src/doxygen.cpp new file mode 100644 index 0000000..2ad5bc5 --- /dev/null +++ b/src/doxygen.cpp @@ -0,0 +1,10772 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2011 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#include "qtbc.h" +#include <qfileinfo.h> +#include <qfile.h> +#include <qdir.h> +#include <qdict.h> +#include <qregexp.h> +#include <qstrlist.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <qtextcodec.h> +#include <unistd.h> +#include <errno.h> + +#include "version.h" +#include "doxygen.h" +#include "scanner.h" +#include "entry.h" +#include "index.h" +#include "logos.h" +#include "instdox.h" +#include "message.h" +#include "config.h" +#include "util.h" +#include "pre.h" +#include "tagreader.h" +#include "dot.h" +#include "msc.h" +#include "docparser.h" +#include "dirdef.h" +#include "outputlist.h" +#include "declinfo.h" +#include "htmlgen.h" +#include "latexgen.h" +#include "mangen.h" +#include "language.h" +#include "debug.h" +#include "htmlhelp.h" +#include "qhp.h" +#include "indexlog.h" +#include "ftvhelp.h" +#include "defargs.h" +#include "rtfgen.h" +#include "xmlgen.h" +#include "defgen.h" +#include "perlmodgen.h" +#include "reflist.h" +#include "pagedef.h" +#include "bufstr.h" +#include "commentcnv.h" +#include "cmdmapper.h" +#include "searchindex.h" +#include "parserintf.h" +#include "htags.h" +#include "pyscanner.h" +#include "fortranscanner.h" +#include "dbusxmlscanner.h" +#include "code.h" +#include "objcache.h" +#include "store.h" +#include "marshal.h" +#include "portable.h" +#include "vhdlscanner.h" +#include "vhdldocgen.h" +#include "eclipsehelp.h" + +#include "layout.h" + +#define RECURSE_ENTRYTREE(func,var) \ + do { if (var->children()) { \ + EntryNavListIterator eli(*var->children()); \ + for (;eli.current();++eli) func(eli.current()); \ + } } while(0) + + +#if !defined(_WIN32) || defined(__CYGWIN__) +#include <signal.h> +#define HAS_SIGNALS +#endif + +// globally accessible variables +ClassSDict *Doxygen::classSDict = 0; +ClassSDict *Doxygen::hiddenClasses = 0; +NamespaceSDict *Doxygen::namespaceSDict = 0; +MemberNameSDict *Doxygen::memberNameSDict = 0; +MemberNameSDict *Doxygen::functionNameSDict = 0; +FileNameList *Doxygen::inputNameList = 0; // all input files +FileNameDict *Doxygen::inputNameDict = 0; +GroupSDict *Doxygen::groupSDict = 0; +FormulaList Doxygen::formulaList; // all formulas +FormulaDict Doxygen::formulaDict(1009); // all formulas +FormulaDict Doxygen::formulaNameDict(1009); // the label name of all formulas +PageSDict *Doxygen::pageSDict = 0; +PageSDict *Doxygen::exampleSDict = 0; +SectionDict Doxygen::sectionDict(257); // all page sections +StringDict Doxygen::aliasDict(257); // aliases +FileNameDict *Doxygen::includeNameDict = 0; // include names +FileNameDict *Doxygen::exampleNameDict = 0; // examples +FileNameDict *Doxygen::imageNameDict = 0; // images +FileNameDict *Doxygen::dotFileNameDict = 0; // dot files +FileNameDict *Doxygen::mscFileNameDict = 0; // dot files +StringDict Doxygen::namespaceAliasDict(257); // all namespace aliases +StringDict Doxygen::tagDestinationDict(257); // all tag locations +QDict<void> Doxygen::expandAsDefinedDict(257); // all macros that should be expanded +QIntDict<MemberGroupInfo> Doxygen::memGrpInfoDict(1009); // dictionary of the member groups heading +PageDef *Doxygen::mainPage = 0; +bool Doxygen::insideMainPage = FALSE; // are we generating docs for the main page? +FTextStream Doxygen::tagFile; +NamespaceDef *Doxygen::globalScope = 0; +QDict<RefList> *Doxygen::xrefLists = new QDict<RefList>; // dictionary of cross-referenced item lists +bool Doxygen::parseSourcesNeeded = FALSE; +QTime Doxygen::runningTime; +SearchIndex * Doxygen::searchIndex=0; +QDict<DefinitionIntf> *Doxygen::symbolMap; +bool Doxygen::outputToWizard=FALSE; +QDict<int> * Doxygen::htmlDirMap = 0; +QCache<LookupInfo> Doxygen::lookupCache(50000,50000); +DirSDict *Doxygen::directories; +SDict<DirRelation> Doxygen::dirRelations(257); +ParserManager *Doxygen::parserManager = 0; +QCString Doxygen::htmlFileExtension; +bool Doxygen::suppressDocWarnings = FALSE; +ObjCache *Doxygen::symbolCache = 0; +Store *Doxygen::symbolStorage; +QCString Doxygen::objDBFileName; +QCString Doxygen::entryDBFileName; +bool Doxygen::gatherDefines = TRUE; +IndexList Doxygen::indexList; +int Doxygen::subpageNestingLevel = 0; +bool Doxygen::userComments = FALSE; +QCString Doxygen::spaces; + +// locally accessible globals +static QDict<EntryNav> g_classEntries(1009); +static StringList g_inputFiles; +static QDict<void> g_compoundKeywordDict(7); // keywords recognised as compounds +static OutputList *g_outputList = 0; // list of output generating objects +static QDict<FileDef> g_usingDeclarations(1009); // used classes +static FileStorage *g_storage = 0; +static bool g_successfulRun = FALSE; +static bool g_dumpSymbolMap = FALSE; +static bool g_dumpConfigAsXML = FALSE; + + + +void clearAll() +{ + g_inputFiles.clear(); + //g_excludeNameDict.clear(); + //delete g_outputList; g_outputList=0; + + Doxygen::classSDict->clear(); + Doxygen::namespaceSDict->clear(); + Doxygen::pageSDict->clear(); + Doxygen::exampleSDict->clear(); + Doxygen::inputNameList->clear(); + Doxygen::formulaList.clear(); + Doxygen::sectionDict.clear(); + Doxygen::inputNameDict->clear(); + Doxygen::includeNameDict->clear(); + Doxygen::exampleNameDict->clear(); + Doxygen::imageNameDict->clear(); + Doxygen::dotFileNameDict->clear(); + Doxygen::mscFileNameDict->clear(); + Doxygen::formulaDict.clear(); + Doxygen::formulaNameDict.clear(); + Doxygen::tagDestinationDict.clear(); + delete Doxygen::mainPage; Doxygen::mainPage=0; +} + +void statistics() +{ + fprintf(stderr,"--- inputNameDict stats ----\n"); + Doxygen::inputNameDict->statistics(); + fprintf(stderr,"--- includeNameDict stats ----\n"); + Doxygen::includeNameDict->statistics(); + fprintf(stderr,"--- exampleNameDict stats ----\n"); + Doxygen::exampleNameDict->statistics(); + fprintf(stderr,"--- imageNameDict stats ----\n"); + Doxygen::imageNameDict->statistics(); + fprintf(stderr,"--- dotFileNameDict stats ----\n"); + Doxygen::dotFileNameDict->statistics(); + fprintf(stderr,"--- mscFileNameDict stats ----\n"); + Doxygen::mscFileNameDict->statistics(); + //fprintf(stderr,"--- g_excludeNameDict stats ----\n"); + //g_excludeNameDict.statistics(); + fprintf(stderr,"--- aliasDict stats ----\n"); + Doxygen::aliasDict.statistics(); + fprintf(stderr,"--- typedefDict stats ----\n"); + fprintf(stderr,"--- namespaceAliasDict stats ----\n"); + Doxygen::namespaceAliasDict.statistics(); + fprintf(stderr,"--- formulaDict stats ----\n"); + Doxygen::formulaDict.statistics(); + fprintf(stderr,"--- formulaNameDict stats ----\n"); + Doxygen::formulaNameDict.statistics(); + fprintf(stderr,"--- tagDestinationDict stats ----\n"); + Doxygen::tagDestinationDict.statistics(); + fprintf(stderr,"--- g_compoundKeywordDict stats ----\n"); + g_compoundKeywordDict.statistics(); + fprintf(stderr,"--- expandAsDefinedDict stats ----\n"); + Doxygen::expandAsDefinedDict.statistics(); + fprintf(stderr,"--- memGrpInfoDict stats ----\n"); + Doxygen::memGrpInfoDict.statistics(); +} + + + +static void addMemberDocs(EntryNav *rootNav,MemberDef *md, const char *funcDecl, + ArgumentList *al,bool over_load,NamespaceSDict *nl=0); +static void findMember(EntryNav *rootNav, + QCString funcDecl, + bool overloaded, + bool isFunc + ); + + +struct STLInfo +{ + const char *className; + const char *baseClass1; + const char *baseClass2; + const char *templType1; + const char *templName1; + const char *templType2; + const char *templName2; + bool virtualInheritance; + bool iterators; +}; + +static STLInfo g_stlinfo[] = +{ + // className baseClass1 baseClass2 templType1 templName1 templType2 templName2 virtInheritance // iterators + { "allocator", 0, 0, "T", "elements", 0, 0, FALSE, FALSE }, + { "auto_ptr", 0, 0, "T", "ptr", 0, 0, FALSE, FALSE }, + { "ios_base", 0, 0, 0, 0, 0, 0, FALSE, FALSE }, + { "basic_ios", "ios_base", 0, "Char", 0, 0, 0, FALSE, FALSE }, + { "basic_istream", "basic_ios<Char>", 0, "Char", 0, 0, 0, TRUE, FALSE }, + { "basic_ostream", "basic_ios<Char>", 0, "Char", 0, 0, 0, TRUE, FALSE }, + { "basic_iostream", "basic_istream<Char>", "basic_ostream<Char>", "Char", 0, 0, 0, FALSE, FALSE }, + { "basic_ifstream", "basic_istream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE }, + { "basic_ofstream", "basic_ostream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE }, + { "basic_fstream", "basic_iostream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE }, + { "basic_istringstream", "basic_istream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE }, + { "basic_ostringstream", "basic_ostream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE }, + { "basic_stringstream", "basic_iostream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE }, + { "ios", "basic_ios<char>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "wios", "basic_ios<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "istream", "basic_istream<char>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "wistream", "basic_istream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "ostream", "basic_ostream<char>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "wostream", "basic_ostream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "ifstream", "basic_ifstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "wifstream", "basic_ifstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "ofstream", "basic_ofstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "wofstream", "basic_ofstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "fstream", "basic_fstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "wfstream", "basic_fstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "istringstream", "basic_istringstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "wistringstream", "basic_istringstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "ostringstream", "basic_ostringstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "wostringstream", "basic_ostringstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "stringstream", "basic_stringstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "wstringstream", "basic_stringstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "basic_string", 0, 0, "Char", 0, 0, 0, FALSE, TRUE }, + { "string", "basic_string<char>", 0, 0, 0, 0, 0, FALSE, TRUE }, + { "wstring", "basic_string<wchar_t>", 0, 0, 0, 0, 0, FALSE, TRUE }, + { "complex", 0, 0, 0, 0, 0, 0, FALSE, FALSE }, + { "bitset", 0, 0, "Bits", 0, 0, 0, FALSE, FALSE }, + { "deque", 0, 0, "T", "elements", 0, 0, FALSE, TRUE }, + { "list", 0, 0, "T", "elements", 0, 0, FALSE, TRUE }, + { "map", 0, 0, "K", "keys", "T", "elements", FALSE, TRUE }, + { "multimap", 0, 0, "K", "keys", "T", "elements", FALSE, TRUE }, + { "set", 0, 0, "K", "keys", 0, 0, FALSE, TRUE }, + { "multiset", 0, 0, "K", "keys", 0, 0, FALSE, TRUE }, + { "vector", 0, 0, "T", "elements", 0, 0, FALSE, TRUE }, + { "queue", 0, 0, "T", "elements", 0, 0, FALSE, FALSE }, + { "priority_queue", 0, 0, "T", "elements", 0, 0, FALSE, FALSE }, + { "stack", 0, 0, "T", "elements", 0, 0, FALSE, FALSE }, + { "valarray", 0, 0, "T", "elements", 0, 0, FALSE, FALSE }, + { "exception", 0, 0, 0, 0, 0, 0, FALSE, FALSE }, + { "bad_alloc", "exception", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "bad_cast", "exception", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "bad_typeid", "exception", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "logic_error", "exception", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "ios_base::failure", "exception", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "runtime_error", "exception", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "bad_exception", "exception", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "domain_error", "logic_error", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "invalid_argument", "logic_error", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "length_error", "logic_error", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "out_of_range", "logic_error", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "range_error", "runtime_error", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "overflow_error", "runtime_error", 0, 0, 0, 0, 0, FALSE, FALSE }, + { "underflow_error", "runtime_error", 0, 0, 0, 0, 0, FALSE, FALSE }, + { 0, 0, 0, 0, 0, 0, 0, FALSE, FALSE } +}; + +static void addSTLMember(EntryNav *rootNav,const char *type,const char *name) +{ + Entry *memEntry = new Entry; + memEntry->name = name; + memEntry->type = type; + memEntry->protection = Private; + memEntry->section = Entry::VARIABLE_SEC; + memEntry->brief = "STL member"; + memEntry->hidden = FALSE; + memEntry->artificial = TRUE; + //memEntry->parent = root; + //root->addSubEntry(memEntry); + EntryNav *memEntryNav = new EntryNav(rootNav,memEntry); + memEntryNav->setEntry(memEntry); + rootNav->addChild(memEntryNav); +} + +static void addSTLIterator(EntryNav *classEntryNav,const char *name) +{ + Entry *iteratorClassEntry = new Entry; + iteratorClassEntry->fileName = "[STL]"; + iteratorClassEntry->startLine = 1; + iteratorClassEntry->name = name; + iteratorClassEntry->section = Entry::CLASS_SEC; + iteratorClassEntry->brief = "STL iterator class"; + iteratorClassEntry->hidden = FALSE; + iteratorClassEntry->artificial= TRUE; + EntryNav *iteratorClassEntryNav = new EntryNav(classEntryNav,iteratorClassEntry); + iteratorClassEntryNav->setEntry(iteratorClassEntry); + classEntryNav->addChild(iteratorClassEntryNav); +} + + +static void addSTLClasses(EntryNav *rootNav) +{ + Entry *namespaceEntry = new Entry; + namespaceEntry->fileName = "[STL]"; + namespaceEntry->startLine = 1; + //namespaceEntry->parent = rootNav->entry(); + namespaceEntry->name = "std"; + namespaceEntry->section = Entry::NAMESPACE_SEC; + namespaceEntry->brief = "STL namespace"; + namespaceEntry->hidden = FALSE; + namespaceEntry->artificial= TRUE; + //root->addSubEntry(namespaceEntry); + EntryNav *namespaceEntryNav = new EntryNav(rootNav,namespaceEntry); + namespaceEntryNav->setEntry(namespaceEntry); + rootNav->addChild(namespaceEntryNav); + + STLInfo *info = g_stlinfo; + while (info->className) + { + //printf("Adding STL class %s\n",info->className); + QCString fullName = info->className; + fullName.prepend("std::"); + + // add fake Entry for the class + Entry *classEntry = new Entry; + classEntry->fileName = "[STL]"; + classEntry->startLine = 1; + classEntry->name = fullName; + //classEntry->parent = namespaceEntry; + classEntry->section = Entry::CLASS_SEC; + classEntry->brief = "STL class"; + classEntry->hidden = FALSE; + classEntry->artificial= TRUE; + //namespaceEntry->addSubEntry(classEntry); + EntryNav *classEntryNav = new EntryNav(namespaceEntryNav,classEntry); + classEntryNav->setEntry(classEntry); + namespaceEntryNav->addChild(classEntryNav); + + // add template arguments to class + if (info->templType1) + { + ArgumentList *al = new ArgumentList; + Argument *a=new Argument; + a->type="typename"; + a->name=info->templType1; + al->append(a); + if (info->templType2) // another template argument + { + a=new Argument; + a->type="typename"; + a->name=info->templType2; + al->append(a); + } + classEntry->tArgLists = new QList<ArgumentList>; + classEntry->tArgLists->setAutoDelete(TRUE); + classEntry->tArgLists->append(al); + } + // add member variables + if (info->templName1) + { + addSTLMember(classEntryNav,info->templType1,info->templName1); + } + if (info->templName2) + { + addSTLMember(classEntryNav,info->templType2,info->templName2); + } + if (info->baseClass1) + { + classEntry->extends->append(new BaseInfo(info->baseClass1,Public,info->virtualInheritance?Virtual:Normal)); + } + if (info->baseClass2) + { + classEntry->extends->append(new BaseInfo(info->baseClass2,Public,info->virtualInheritance?Virtual:Normal)); + } + if (info->iterators) + { + // add iterator class + addSTLIterator(classEntryNav,fullName+"::iterator"); + addSTLIterator(classEntryNav,fullName+"::const_iterator"); + addSTLIterator(classEntryNav,fullName+"::reverse_iterator"); + addSTLIterator(classEntryNav,fullName+"::const_reverse_iterator"); + } + info++; + } +} + +//---------------------------------------------------------------------------- + +static Definition *findScopeFromQualifiedName(Definition *startScope,const QCString &n, + FileDef *fileScope=0); + +static void addPageToContext(PageDef *pd,EntryNav *rootNav) +{ + if (rootNav->parent()) // add the page to it's scope + { + QCString scope = rootNav->parent()->name(); + if (rootNav->parent()->section()==Entry::PACKAGEDOC_SEC) + { + scope=substitute(scope,".","::"); + } + scope = stripAnonymousNamespaceScope(scope); + scope+="::"+pd->name(); + Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope); + if (d) + { + pd->setPageScope(d); + } + } +} + +static void addRelatedPage(EntryNav *rootNav) +{ + Entry *root = rootNav->entry(); + GroupDef *gd=0; + QListIterator<Grouping> gli(*root->groups); + Grouping *g; + for (;(g=gli.current());++gli) + { + if (!g->groupname.isEmpty() && (gd=Doxygen::groupSDict->find(g->groupname))) break; + } + //printf("---> addRelatedPage() %s gd=%p\n",root->name.data(),gd); + QCString doc; + if (root->brief.isEmpty()) + { + doc=root->doc+root->inbodyDocs; + } + else + { + doc=root->brief+"\n\n"+root->doc+root->inbodyDocs; + } + PageDef *pd = addRelatedPage(root->name,root->args,doc,root->anchors, + root->fileName,root->startLine, + root->sli, + gd,rootNav->tagInfo() + ); + if (pd) + { + pd->addSectionsToDefinition(root->anchors); + addPageToContext(pd,rootNav); + } +} + +static void buildGroupListFiltered(EntryNav *rootNav,bool additional, bool includeExternal) +{ + if (rootNav->section()==Entry::GROUPDOC_SEC && !rootNav->name().isEmpty() && + ((!includeExternal && rootNav->tagInfo()==0) || + ( includeExternal && rootNav->tagInfo()!=0)) + ) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) || + (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional)) + { + GroupDef *gd = Doxygen::groupSDict->find(root->name); + //printf("Processing group '%s': add=%d ext=%d gd=%p\n", + // root->type.data(),additional,includeExternal,gd); + + if (gd) + { + if ( !gd->hasGroupTitle() ) + { + gd->setGroupTitle( root->type ); + } + else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type ) + { + warn( root->fileName,root->startLine, + "group %s: ignoring title \"%s\" that does not match old title \"%s\"\n", + qPrint(root->name), qPrint(root->type), qPrint(gd->groupTitle()) ); + } + gd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + gd->setDocumentation( root->doc, root->docFile, root->docLine ); + gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine ); + gd->addSectionsToDefinition(root->anchors); + gd->setRefItems(root->sli); + } + else + { + if (rootNav->tagInfo()) + { + gd = new GroupDef(root->fileName,root->startLine,root->name,root->type,rootNav->tagInfo()->fileName); + gd->setReference(rootNav->tagInfo()->tagName); + } + else + { + gd = new GroupDef(root->fileName,root->startLine,root->name,root->type); + } + gd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + gd->setDocumentation(root->doc,root->docFile,root->docLine); + gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine ); + gd->addSectionsToDefinition(root->anchors); + Doxygen::groupSDict->append(root->name,gd); + gd->setRefItems(root->sli); + } + } + + rootNav->releaseEntry(); + } + if (rootNav->children()) + { + EntryNavListIterator eli(*rootNav->children()); + EntryNav *e; + for (;(e=eli.current());++eli) + { + buildGroupListFiltered(e,additional,includeExternal); + } + } +} + +static void buildGroupList(EntryNav *rootNav) +{ + // --- first process only local groups + // first process the @defgroups blocks + buildGroupListFiltered(rootNav,FALSE,FALSE); + // then process the @addtogroup, @weakgroup blocks + buildGroupListFiltered(rootNav,TRUE,FALSE); + + // --- then also process external groups + // first process the @defgroups blocks + buildGroupListFiltered(rootNav,FALSE,TRUE); + // then process the @addtogroup, @weakgroup blocks + buildGroupListFiltered(rootNav,TRUE,TRUE); +} + +static void findGroupScope(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::GROUPDOC_SEC && !rootNav->name().isEmpty() && + rootNav->parent() && !rootNav->parent()->name().isEmpty()) + { + GroupDef *gd; + if ((gd=Doxygen::groupSDict->find(rootNav->name()))) + { + QCString scope = rootNav->parent()->name(); + if (rootNav->parent()->section()==Entry::PACKAGEDOC_SEC) + { + scope=substitute(scope,".","::"); + } + scope = stripAnonymousNamespaceScope(scope); + scope+="::"+gd->name(); + Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope); + if (d) + { + gd->setGroupScope(d); + } + } + } + RECURSE_ENTRYTREE(findGroupScope,rootNav); +} + +static void organizeSubGroupsFiltered(EntryNav *rootNav,bool additional) +{ + if (rootNav->section()==Entry::GROUPDOC_SEC && !rootNav->name().isEmpty()) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) || + (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional)) + { + GroupDef *gd; + if ((gd=Doxygen::groupSDict->find(root->name))) + { + //printf("adding %s to group %s\n",root->name.data(),gd->name().data()); + addGroupToGroups(root,gd); + } + } + + rootNav->releaseEntry(); + } + if (rootNav->children()) + { + EntryNavListIterator eli(*rootNav->children()); + EntryNav *e; + for (;(e=eli.current());++eli) + { + organizeSubGroupsFiltered(e,additional); + } + } +} + +static void organizeSubGroups(EntryNav *rootNav) +{ + //printf("Defining groups\n"); + // first process the @defgroups blocks + organizeSubGroupsFiltered(rootNav,FALSE); + //printf("Additional groups\n"); + // then process the @addtogroup, @weakgroup blocks + organizeSubGroupsFiltered(rootNav,TRUE); +} + +//---------------------------------------------------------------------- + +static void buildFileList(EntryNav *rootNav) +{ + if (((rootNav->section()==Entry::FILEDOC_SEC) || + ((rootNav->section() & Entry::FILE_MASK) && Config_getBool("EXTRACT_ALL"))) && + !rootNav->name().isEmpty() && !rootNav->tagInfo() // skip any file coming from tag files + ) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + bool ambig; + FileDef *fd=findFileDef(Doxygen::inputNameDict,root->name,ambig); + //printf("**************** root->name=%s fd=%p\n",root->name.data(),fd); + if (fd && !ambig) + { +#if 0 + if ((!root->doc.isEmpty() && !fd->documentation().isEmpty()) || + (!root->brief.isEmpty() && !fd->briefDescription().isEmpty())) + { + warn( + root->fileName,root->startLine, + "warning: file %s already documented. " + "Skipping documentation.", + root->name.data() + ); + } + else +#endif + { + //printf("Adding documentation!\n"); + // using FALSE in setDocumentation is small hack to make sure a file + // is documented even if a \file command is used without further + // documentation + fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE); + fd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + fd->addSectionsToDefinition(root->anchors); + fd->setRefItems(root->sli); + QListIterator<Grouping> gli(*root->groups); + Grouping *g; + for (;(g=gli.current());++gli) + { + GroupDef *gd=0; + if (!g->groupname.isEmpty() && (gd=Doxygen::groupSDict->find(g->groupname))) + { + gd->addFile(fd); + fd->makePartOfGroup(gd); + //printf("File %s: in group %s\n",fd->name().data(),s->data()); + } + } + } + } + else + { + const char *fn = root->fileName.data(); + QCString text; + text.sprintf("warning: the name `%s' supplied as " + "the second argument in the \\file statement ", + qPrint(root->name) + ); + if (ambig) // name is ambiguous + { + text+="matches the following input files:\n"; + text+=showFileDefMatches(Doxygen::inputNameDict,root->name); + text+="Please use a more specific name by " + "including a (larger) part of the path!"; + } + else // name is not an input file + { + text+="is not an input file"; + } + warn(fn,root->startLine,text); + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(buildFileList,rootNav); +} + +static void addIncludeFile(ClassDef *cd,FileDef *ifd,Entry *root) +{ + if ( + (!root->doc.stripWhiteSpace().isEmpty() || + !root->brief.stripWhiteSpace().isEmpty() || + Config_getBool("EXTRACT_ALL") + ) && root->protection!=Private + ) + { + //printf(">>>>>> includeFile=%s\n",root->includeFile.data()); + + bool local=Config_getBool("FORCE_LOCAL_INCLUDES"); + QCString includeFile = root->includeFile; + if (!includeFile.isEmpty() && includeFile.at(0)=='"') + { + local = TRUE; + includeFile=includeFile.mid(1,includeFile.length()-2); + } + else if (!includeFile.isEmpty() && includeFile.at(0)=='<') + { + local = FALSE; + includeFile=includeFile.mid(1,includeFile.length()-2); + } + + bool ambig; + FileDef *fd=0; + // see if we need to include a verbatim copy of the header file + //printf("root->includeFile=%s\n",root->includeFile.data()); + if (!includeFile.isEmpty() && + (fd=findFileDef(Doxygen::inputNameDict,includeFile,ambig))==0 + ) + { // explicit request + QCString text; + text.sprintf("warning: the name `%s' supplied as " + "the argument of the \\class, \\struct, \\union, or \\include command ", + qPrint(includeFile) + ); + if (ambig) // name is ambiguous + { + text+="matches the following input files:\n"; + text+=showFileDefMatches(Doxygen::inputNameDict,root->includeFile); + text+="Please use a more specific name by " + "including a (larger) part of the path!"; + } + else // name is not an input file + { + text+="is not an input file"; + } + warn(root->fileName,root->startLine,text); + } + else if (includeFile.isEmpty() && ifd && + // see if the file extension makes sense + guessSection(ifd->name())==Entry::HEADER_SEC) + { // implicit assumption + fd=ifd; + } + + // if a file is found, we mark it as a source file. + if (fd) + { + QCString iName = !root->includeName.isEmpty() ? + root->includeName : includeFile; + if (!iName.isEmpty()) // user specified include file + { + if (iName.at(0)=='<') local=FALSE; // explicit override + if (iName.at(0)=='"' || iName.at(0)=='<') + { + iName=iName.mid(1,iName.length()-2); // strip quotes or brackets + } + if (iName.isEmpty()) + { + iName=fd->name(); + } + } + else if (!Config_getList("STRIP_FROM_INC_PATH").isEmpty()) + { + iName=stripFromIncludePath(fd->absFilePath()); + } + else // use name of the file containing the class definition + { + iName=fd->name(); + } + if (fd->generateSourceFile()) // generate code for header + { + cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty()); + } + else // put #include in the class documentation without link + { + cd->setIncludeFile(0,iName,local,TRUE); + } + } + } +} + +#if 0 +static bool addNamespace(Entry *root,ClassDef *cd) +{ + // see if this class is defined inside a namespace + if (root->section & Entry::COMPOUND_MASK) + { + Entry *e = root->parent; + while (e) + { + if (e->section==Entry::NAMESPACE_SEC) + { + NamespaceDef *nd=0; + QCString nsName = stripAnonymousNamespaceScope(e->name); + //printf("addNameSpace() trying: %s\n",nsName.data()); + if (!nsName.isEmpty() && nsName.at(0)!='@' && + (nd=getResolvedNamespace(nsName)) + ) + { + cd->setNamespace(nd); + cd->setOuterScope(nd); + nd->insertClass(cd); + return TRUE; + } + } + e=e->parent; + } + } + return FALSE; +} +#endif + +#if 0 +static Definition *findScope(Entry *root,int level=0) +{ + if (root==0) return 0; + //printf("start findScope name=%s\n",root->name.data()); + Definition *result=0; + if (root->section&Entry::SCOPE_MASK) + { + result = findScope(root->parent,level+1); // traverse to the root of the tree + if (result) + { + //printf("Found %s inside %s at level %d\n",root->name.data(),result->name().data(),level); + // TODO: look at template arguments + result = result->findInnerCompound(root->name); + } + else // reached the global scope + { + // TODO: look at template arguments + result = Doxygen::globalScope->findInnerCompound(root->name); + //printf("Found in globalScope %s at level %d\n",result->name().data(),level); + } + } + //printf("end findScope(%s,%d)=%s\n",root->name.data(), + // level,result==0 ? "<none>" : result->name().data()); + return result; +} +#endif + +/*! returns the Definition object belonging to the first \a level levels of + * full qualified name \a name. Creates an artificial scope if the scope is + * not found and set the parent/child scope relation if the scope is found. + */ +static Definition *buildScopeFromQualifiedName(const QCString name,int level) +{ + int i=0; + int p=0,l; + Definition *prevScope=Doxygen::globalScope; + QCString fullScope; + while (i<level) + { + int idx=getScopeFragment(name,p,&l); + QCString nsName = name.mid(idx,l); + if (nsName.isEmpty()) return prevScope; + if (!fullScope.isEmpty()) fullScope+="::"; + fullScope+=nsName; + NamespaceDef *nd=Doxygen::namespaceSDict->find(fullScope); + Definition *innerScope = nd; + ClassDef *cd=0; + if (nd==0) cd = getClass(fullScope); + if (nd==0 && cd) // scope is a class + { + innerScope = cd; + } + else if (nd==0 && cd==0) // scope is not known! + { + // introduce bogus namespace + //printf("++ adding dummy namespace %s to %s\n",nsName.data(),prevScope->name().data()); + nd=new NamespaceDef( + "[generated]",1,fullScope); + + // add namespace to the list + Doxygen::namespaceSDict->inSort(fullScope,nd); + innerScope = nd; + } + else // scope is a namespace + { + } + // make the parent/child scope relation + prevScope->addInnerCompound(innerScope); + innerScope->setOuterScope(prevScope); + // proceed to the next scope fragment + p=idx+l+2; + prevScope=innerScope; + i++; + } + return prevScope; +} + +static Definition *findScopeFromQualifiedName(Definition *startScope,const QCString &n, + FileDef *fileScope) +{ + //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? startScope->name().data() : 0, n.data()); + Definition *resultScope=startScope; + if (resultScope==0) resultScope=Doxygen::globalScope; + QCString scope=stripTemplateSpecifiersFromScope(n,FALSE); + int l1=0,i1; + i1=getScopeFragment(scope,0,&l1); + if (i1==-1) + { + //printf(">no fragments!\n"); + return resultScope; + } + int p=i1+l1,l2=0,i2; + while ((i2=getScopeFragment(scope,p,&l2))!=-1) + { + QCString nestedNameSpecifier = scope.mid(i1,l1); + Definition *orgScope = resultScope; + //printf(" nestedNameSpecifier=%s\n",nestedNameSpecifier.data()); + resultScope = resultScope->findInnerCompound(nestedNameSpecifier); + //printf(" resultScope=%p\n",resultScope); + if (resultScope==0) + { + NamespaceSDict *usedNamespaces; + if (orgScope==Doxygen::globalScope && fileScope && + (usedNamespaces = fileScope->getUsedNamespaces())) + // also search for used namespaces + { + NamespaceSDict::Iterator ni(*usedNamespaces); + NamespaceDef *nd; + for (ni.toFirst();((nd=ni.current()) && resultScope==0);++ni) + { + // restart search within the used namespace + resultScope = findScopeFromQualifiedName(nd,n,fileScope); + } + if (resultScope) + { + // for a nested class A::I in used namespace N, we get + // N::A::I while looking for A, so we should compare + // resultScope->name() against scope.left(i2+l2) + //printf(" -> result=%s scope=%s\n",resultScope->name().data(),scope.data()); + if (rightScopeMatch(resultScope->name(),scope.left(i2+l2))) + { + break; + } + goto nextFragment; + } + } + + // also search for used classes. Complication: we haven't been able + // to put them in the right scope yet, because we are still resolving + // the scope relations! + // Therefore loop through all used classes and see if there is a right + // scope match between the used class and nestedNameSpecifier. + QDictIterator<FileDef> ui(g_usingDeclarations); + FileDef *usedFd; + for (ui.toFirst();(usedFd=ui.current());++ui) + { + //printf("Checking using class %s\n",ui.currentKey()); + if (rightScopeMatch(ui.currentKey(),nestedNameSpecifier)) + { + // ui.currentKey() is the fully qualified name of nestedNameSpecifier + // so use this instead. + QCString fqn = QCString(ui.currentKey())+ + scope.right(scope.length()-p); + resultScope = buildScopeFromQualifiedName(fqn,fqn.contains("::")); + //printf("Creating scope from fqn=%s result %p\n",fqn.data(),resultScope); + if (resultScope) + { + //printf("> Match! resultScope=%s\n",resultScope->name().data()); + return resultScope; + } + } + } + + //printf("> name %s not found in scope %s\n",nestedNameSpecifier.data(),orgScope->name().data()); + return 0; + } + nextFragment: + i1=i2; + l1=l2; + p=i2+l2; + } + //printf(">findScopeFromQualifiedName scope %s\n",resultScope->name().data()); + return resultScope; +} + +ArgumentList *getTemplateArgumentsFromName( + const QCString &name, + const QList<ArgumentList> *tArgLists) +{ + if (tArgLists==0) return 0; + + QListIterator<ArgumentList> ali(*tArgLists); + // for each scope fragment, check if it is a template and advance through + // the list if so. + int i,p=0; + while ((i=name.find("::",p))!=-1) + { + NamespaceDef *nd = Doxygen::namespaceSDict->find(name.left(i)); + if (nd==0) + { + ClassDef *cd = getClass(name.left(i)); + if (cd) + { + if (cd->templateArguments()) + { + ++ali; + } + } + } + p=i+2; + } + return ali.current(); +} + +static ClassDef::CompoundType convertToCompoundType(int section,int specifier) +{ + ClassDef::CompoundType sec=ClassDef::Class; + if (specifier&Entry::Struct) + sec=ClassDef::Struct; + else if (specifier&Entry::Union) + sec=ClassDef::Union; + else if (specifier&Entry::Interface) + sec=ClassDef::Interface; + else if (specifier&Entry::Protocol) + sec=ClassDef::Protocol; + else if (specifier&Entry::Category) + sec=ClassDef::Category; + else if (specifier&Entry::Exception) + sec=ClassDef::Exception; + + switch(section) + { + //case Entry::UNION_SEC: + case Entry::UNIONDOC_SEC: + sec=ClassDef::Union; + break; + //case Entry::STRUCT_SEC: + case Entry::STRUCTDOC_SEC: + sec=ClassDef::Struct; + break; + //case Entry::INTERFACE_SEC: + case Entry::INTERFACEDOC_SEC: + sec=ClassDef::Interface; + break; + //case Entry::PROTOCOL_SEC: + case Entry::PROTOCOLDOC_SEC: + sec=ClassDef::Protocol; + break; + //case Entry::CATEGORY_SEC: + case Entry::CATEGORYDOC_SEC: + sec=ClassDef::Category; + break; + //case Entry::EXCEPTION_SEC: + case Entry::EXCEPTIONDOC_SEC: + sec=ClassDef::Exception; + break; + } + return sec; +} + + +static void addClassToContext(EntryNav *rootNav) +{ + //printf("Loading entry for rootNav=%p name=%s\n",rootNav,rootNav->name().data()); + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + //NamespaceDef *nd = 0; + FileDef *fd = rootNav->fileDef(); + + QCString scName; + if (rootNav->parent()->section()&Entry::SCOPE_MASK) + { + scName=rootNav->parent()->name(); + } + // name without parent's scope + QCString fullName = root->name; + + // strip off any template parameters (but not those for specializations) + fullName=stripTemplateSpecifiersFromScope(fullName); + + // name with scope (if not present already) + QCString qualifiedName = fullName; + if (!scName.isEmpty() && !leftScopeMatch(fullName,scName)) + { + qualifiedName.prepend(scName+"::"); + } + + // see if we already found the class before + ClassDef *cd = getClass(qualifiedName); + + Debug::print(Debug::Classes,0, " Found class with name %s (qualifiedName=%s -> cd=%p)\n", + cd ? cd->name().data() : root->name.data(), qualifiedName.data(),cd); + + if (cd) + { + fullName=cd->name(); + Debug::print(Debug::Classes,0," Existing class %s!\n",cd->name().data()); + //if (cd->templateArguments()==0) + //{ + // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,root->scopeSpec.data()); + // cd->setTemplateArguments(tArgList); + //} + + cd->setDocumentation(root->doc,root->docFile,root->docLine); + cd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + + if (root->bodyLine!=-1 && cd->getStartBodyLine()==-1) + { + cd->setBodySegment(root->bodyLine,root->endBodyLine); + cd->setBodyDef(fd); + } + //cd->setName(fullName); // change name to match docs + + if (cd->templateArguments()==0) + { + // this happens if a template class declared with @class is found + // before the actual definition. + ArgumentList *tArgList = + getTemplateArgumentsFromName(cd->name(),root->tArgLists); + cd->setTemplateArguments(tArgList); + } + + cd->setCompoundType(convertToCompoundType(root->section,root->spec)); + } + else // new class + { + ClassDef::CompoundType sec = convertToCompoundType(root->section,root->spec); + + QCString className; + QCString namespaceName; + extractNamespaceName(fullName,className,namespaceName); + + //printf("New class: fullname %s namespace `%s' name=`%s' brief=`%s' docs=`%s'\n", + // fullName.data(),namespaceName.data(),className.data(),root->brief.data(),root->doc.data()); + + QCString tagName; + QCString refFileName; + if (rootNav->tagInfo()) + { + tagName = rootNav->tagInfo()->tagName; + refFileName = rootNav->tagInfo()->fileName; + } + cd=new ClassDef(root->fileName,root->startLine,fullName,sec, + tagName,refFileName); + Debug::print(Debug::Classes,0," New class `%s' (sec=0x%08x)! #tArgLists=%d\n", + fullName.data(),root->section,root->tArgLists ? (int)root->tArgLists->count() : -1); + cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition + cd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + cd->setLanguage(root->lang); + cd->setHidden(root->hidden); + cd->setArtificial(root->artificial); + cd->setTypeConstraints(root->typeConstr); + //printf("new ClassDef %s tempArgList=%p specScope=%s\n",fullName.data(),root->tArgList,root->scopeSpec.data()); + + ArgumentList *tArgList = + getTemplateArgumentsFromName(fullName,root->tArgLists); + //printf("class %s template args=%s\n",fullName.data(), + // tArgList ? tempArgListToString(tArgList).data() : "<none>"); + cd->setTemplateArguments(tArgList); + cd->setProtection(root->protection); + cd->setIsStatic(root->stat); + + // file definition containing the class cd + cd->setBodySegment(root->bodyLine,root->endBodyLine); + cd->setBodyDef(fd); + + // see if the class is found inside a namespace + //bool found=addNamespace(root,cd); + + + // the empty string test is needed for extract all case + cd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + cd->insertUsedFile(root->fileName); + + // add class to the list + //printf("ClassDict.insert(%s)\n",resolveDefines(fullName).data()); + Doxygen::classSDict->append(fullName,cd); + + } + + cd->addSectionsToDefinition(root->anchors); + if (!root->subGrouping) cd->setSubGrouping(FALSE); + if (cd->hasDocumentation()) + { + addIncludeFile(cd,fd,root); + } + if (fd && (root->section & Entry::COMPOUND_MASK)) + { + //printf(">> Inserting class `%s' in file `%s' (root->fileName=`%s')\n", + // cd->name().data(), + // fd->name().data(), + // root->fileName.data() + // ); + cd->setFileDef(fd); + fd->insertClass(cd); + } + addClassToGroups(root,cd); + cd->setRefItems(root->sli); + + rootNav->releaseEntry(); +} + +//---------------------------------------------------------------------- +// build a list of all classes mentioned in the documentation +// and all classes that have a documentation block before their definition. +static void buildClassList(EntryNav *rootNav) +{ + if ( + ((rootNav->section() & Entry::COMPOUND_MASK) || + rootNav->section()==Entry::OBJCIMPL_SEC) && !rootNav->name().isEmpty() + ) + { + addClassToContext(rootNav); + } + RECURSE_ENTRYTREE(buildClassList,rootNav); +} + +static void buildClassDocList(EntryNav *rootNav) +{ + if ( + (rootNav->section() & Entry::COMPOUNDDOC_MASK) && !rootNav->name().isEmpty() + ) + { + addClassToContext(rootNav); + } + RECURSE_ENTRYTREE(buildClassDocList,rootNav); +} + +static void resolveClassNestingRelations() +{ + ClassSDict::Iterator cli(*Doxygen::classSDict); + for (cli.toFirst();cli.current();++cli) cli.current()->visited=FALSE; + + bool done=FALSE; + int iteration=0; + while (!done) + { + done=TRUE; + ++iteration; + ClassDef *cd=0; + for (cli.toFirst();(cd=cli.current());++cli) + { + if (!cd->visited) + { + QCString name = stripAnonymousNamespaceScope(cd->name()); + //printf("processing=%s, iteration=%d\n",cd->name().data(),iteration); + // also add class to the correct structural context + Definition *d = findScopeFromQualifiedName(Doxygen::globalScope, + name,cd->getFileDef()); + if (d) + { + //printf("****** adding %s to scope %s in iteration %d\n",cd->name().data(),d->name().data(),iteration); + d->addInnerCompound(cd); + cd->setOuterScope(d); + cd->visited=TRUE; + done=FALSE; + } + //else + //{ + // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",cd->name().data(),iteration); + //} + } + } + } + + //give warnings for unresolved compounds + ClassDef *cd=0; + for (cli.toFirst();(cd=cli.current());++cli) + { + if (!cd->visited) + { + QCString name = stripAnonymousNamespaceScope(cd->name()); + //printf("processing unresolved=%s, iteration=%d\n",cd->name().data(),iteration); + /// create the scope artificially + // anyway, so we can at least relate scopes properly. + Definition *d = buildScopeFromQualifiedName(name,name.contains("::")); + if (d!=cd && !cd->getDefFileName().isEmpty()) + // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; } + // for this case doxygen assumes the exitance of a namespace N::N in which C is to be found! + // also avoid warning for stuff imported via a tagfile. + { + d->addInnerCompound(cd); + cd->setOuterScope(d); + warn(cd->getDefFileName(),cd->getDefLine(), + "warning: Internal inconsistency: scope for class %s not " + "found!",name.data() + ); + } + } + } +} + +void distributeClassGroupRelations() +{ + static bool inlineGroupedClasses = Config_getBool("INLINE_GROUPED_CLASSES"); + if (!inlineGroupedClasses) return; + //printf("** distributeClassGroupRelations()\n"); + + ClassSDict::Iterator cli(*Doxygen::classSDict); + for (cli.toFirst();cli.current();++cli) cli.current()->visited=FALSE; + + ClassDef *cd; + for (cli.toFirst();(cd=cli.current());++cli) + { + //printf("Checking %s\n",cd->name().data()); + // distribute the group to nested classes as well + if (!cd->visited && cd->partOfGroups()!=0 && cd->getInnerClasses()) + { + //printf(" Candidate for merging\n"); + ClassSDict::Iterator ncli(*cd->getInnerClasses()); + ClassDef *ncd; + GroupDef *gd = cd->partOfGroups()->at(0); + for (ncli.toFirst();(ncd=ncli.current());++ncli) + { + if (ncd->partOfGroups()==0) + { + //printf(" Adding %s to group '%s'\n",ncd->name().data(), + // gd->groupTitle()); + ncd->makePartOfGroup(gd); + gd->addClass(ncd); + } + } + cd->visited=TRUE; // only visit every class once + } + } +} + +//---------------------------------------------------------------------- +// build a list of all namespaces mentioned in the documentation +// and all namespaces that have a documentation block before their definition. +static void buildNamespaceList(EntryNav *rootNav) +{ + if ( + (rootNav->section()==Entry::NAMESPACE_SEC || + rootNav->section()==Entry::NAMESPACEDOC_SEC || + rootNav->section()==Entry::PACKAGEDOC_SEC + ) && + !rootNav->name().isEmpty() + ) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + //printf("** buildNamespaceList(%s)\n",root->name.data()); + + QCString fName = root->name; + if (root->section==Entry::PACKAGEDOC_SEC) + { + fName=substitute(fName,".","::"); + } + + QCString fullName = stripAnonymousNamespaceScope(fName); + if (!fullName.isEmpty()) + { + //printf("Found namespace %s in %s at line %d\n",root->name.data(), + // root->fileName.data(), root->startLine); + NamespaceDef *nd; + if ((nd=Doxygen::namespaceSDict->find(fullName))) // existing namespace + { +#if 0 + if (!root->doc.isEmpty() || !root->brief.isEmpty()) // block contains docs + { + if (nd->documentation().isEmpty() && !root->doc.isEmpty()) + { +#endif + nd->setDocumentation(root->doc,root->docFile,root->docLine); + nd->setName(fullName); // change name to match docs + nd->addSectionsToDefinition(root->anchors); +#if 0 + } + else if (!nd->documentation().isEmpty() && !root->doc.isEmpty()) + { + warn( + root->fileName,root->startLine, + "warning: namespace %s already has a detailed description found in file %s at line %d. " + "Skipping the documentation found here.", + fullName.data(),nd->docFile().data(),nd->docLine()); + } + if (nd->briefDescription().isEmpty() && !root->brief.isEmpty()) + { +#endif + nd->setBriefDescription(root->brief,root->briefFile,root->briefLine); +#if 0 + nd->setName(fullName); // change name to match docs + } + else if (!nd->briefDescription().isEmpty() && !root->brief.isEmpty()) + { + warn(root->fileName,root->startLine, + "warning: namespace %s already has a brief description found in file %s at line %d. " + "Skipping the documentation found here.", + fullName.data(),nd->docFile().data(),nd->docLine() + ); + } + } +#endif + + // file definition containing the namespace nd + FileDef *fd=rootNav->fileDef(); + // insert the namespace in the file definition + if (fd) fd->insertNamespace(nd); + addNamespaceToGroups(root,nd); + nd->setRefItems(root->sli); + } + else // fresh namespace + { + QCString tagName; + QCString tagFileName; + if (rootNav->tagInfo()) + { + tagName=rootNav->tagInfo()->tagName; + tagFileName=rootNav->tagInfo()->fileName; + } + //printf("++ new namespace %s\n",fullName.data()); + NamespaceDef *nd=new NamespaceDef(root->fileName,root->startLine,fullName,tagName,tagFileName); + nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition + nd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + nd->addSectionsToDefinition(root->anchors); + nd->setHidden(root->hidden); + nd->setArtificial(root->artificial); + + //printf("Adding namespace to group\n"); + addNamespaceToGroups(root,nd); + nd->setRefItems(root->sli); + + // file definition containing the namespace nd + FileDef *fd=rootNav->fileDef(); + // insert the namespace in the file definition + if (fd) fd->insertNamespace(nd); + + // the empty string test is needed for extract all case + nd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + nd->insertUsedFile(root->fileName); + nd->setBodySegment(root->bodyLine,root->endBodyLine); + nd->setBodyDef(fd); + // add class to the list + Doxygen::namespaceSDict->inSort(fullName,nd); + + // also add namespace to the correct structural context + Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName); + //printf("adding namespace %s to context %s\n",nd->name().data(),d?d->name().data():"<none>"); + if (d==0) // we didn't find anything, create the scope artificially + // anyway, so we can at least relate scopes properly. + { + Definition *d = buildScopeFromQualifiedName(fullName,fullName.contains("::")); + d->addInnerCompound(nd); + nd->setOuterScope(d); + // TODO: Due to the order in which the tag file is written + // a nested class can be found before its parent! + } + else + { + d->addInnerCompound(nd); + nd->setOuterScope(d); + } + } + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(buildNamespaceList,rootNav); +} + +//---------------------------------------------------------------------- + +static NamespaceDef *findUsedNamespace(NamespaceSDict *unl, + const QCString &name) +{ + NamespaceDef *usingNd =0; + if (unl) + { + //printf("Found namespace dict %d\n",unl->count()); + NamespaceSDict::Iterator unli(*unl); + NamespaceDef *und; + for (unli.toFirst();(und=unli.current());++unli) + { + QCString uScope=und->name()+"::"; + usingNd = getResolvedNamespace(uScope+name); + //printf("Also trying with scope=`%s' usingNd=%p\n",(uScope+name).data(),usingNd); + } + } + return usingNd; +} + +static void findUsingDirectives(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::USINGDIR_SEC) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + //printf("Found using directive %s at line %d of %s\n", + // root->name.data(),root->startLine,root->fileName.data()); + QCString name=substitute(root->name,".","::"); + if (!name.isEmpty()) + { + NamespaceDef *usingNd = 0; + NamespaceDef *nd = 0; + FileDef *fd = rootNav->fileDef(); + QCString nsName; + + // see if the using statement was found inside a namespace or inside + // the global file scope. + if (rootNav->parent() && rootNav->parent()->section()==Entry::NAMESPACE_SEC && + (fd==0 || !fd->isJava()) // not a .java file + ) + { + nsName=stripAnonymousNamespaceScope(rootNav->parent()->name()); + if (!nsName.isEmpty()) + { + nd = getResolvedNamespace(nsName); + } + } + + // find the scope in which the `using' namespace is defined by prepending + // the possible scopes in which the using statement was found, starting + // with the most inner scope and going to the most outer scope (i.e. + // file scope). + int scopeOffset = nsName.length(); + do + { + QCString scope=scopeOffset>0 ? + nsName.left(scopeOffset)+"::" : QCString(); + usingNd = getResolvedNamespace(scope+name); + //printf("Trying with scope=`%s' usingNd=%p\n",(scope+name).data(),usingNd); + if (scopeOffset==0) + { + scopeOffset=-1; + } + else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1) + { + scopeOffset=0; + } + } while (scopeOffset>=0 && usingNd==0); + + if (usingNd==0 && nd) // not found, try used namespaces in this scope + // or in one of the parent namespace scopes + { + NamespaceDef *pnd = nd; + while (pnd && usingNd==0) + { + // also try with one of the used namespaces found earlier + usingNd = findUsedNamespace(pnd->getUsedNamespaces(),name); + + // goto the parent + Definition *s = pnd->getOuterScope(); + if (s && s->definitionType()==Definition::TypeNamespace) + { + pnd = (NamespaceDef*)s; + } + else + { + pnd = 0; + } + } + } + if (usingNd==0 && fd) // still nothing, also try used namespace in the + // global scope + { + usingNd = findUsedNamespace(fd->getUsedNamespaces(),name); + } + + //printf("%s -> %s\n",name.data(),usingNd?usingNd->name().data():"<none>"); + + // add the namespace the correct scope + if (usingNd) + { + //printf("using fd=%p nd=%p\n",fd,nd); + if (nd) + { + //printf("Inside namespace %s\n",nd->name().data()); + nd->addUsingDirective(usingNd); + } + else if (fd) + { + //printf("Inside file %s\n",fd->name().data()); + fd->addUsingDirective(usingNd); + } + } + else // unknown namespace, but add it anyway. + { + //printf("++ new unknown namespace %s\n",name.data()); + NamespaceDef *nd=new NamespaceDef(root->fileName,root->startLine,name); + nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition + nd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + nd->addSectionsToDefinition(root->anchors); + //printf("** Adding namespace %s hidden=%d\n",name.data(),root->hidden); + nd->setHidden(root->hidden); + nd->setArtificial(TRUE); + + QListIterator<Grouping> gli(*root->groups); + Grouping *g; + for (;(g=gli.current());++gli) + { + GroupDef *gd=0; + if (!g->groupname.isEmpty() && (gd=Doxygen::groupSDict->find(g->groupname))) + gd->addNamespace(nd); + } + + // insert the namespace in the file definition + if (fd) + { + fd->insertNamespace(nd); + fd->addUsingDirective(nd); + } + + // the empty string test is needed for extract all case + nd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + nd->insertUsedFile(root->fileName); + // add class to the list + Doxygen::namespaceSDict->inSort(name,nd); + nd->setRefItems(root->sli); + } + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(findUsingDirectives,rootNav); +} + +//---------------------------------------------------------------------- + +static void buildListOfUsingDecls(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::USINGDECL_SEC && + !(rootNav->parent()->section()&Entry::COMPOUND_MASK) // not a class/struct member + ) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + QCString name = substitute(root->name,".","::"); + if (g_usingDeclarations.find(name)==0) + { + FileDef *fd = rootNav->fileDef(); + if (fd) + { + g_usingDeclarations.insert(name,fd); + } + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(buildListOfUsingDecls,rootNav); +} + + +static void findUsingDeclarations(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::USINGDECL_SEC && + !(rootNav->parent()->section()&Entry::COMPOUND_MASK) // not a class/struct member + ) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + //printf("Found using declaration %s at line %d of %s inside section %x\n", + // root->name.data(),root->startLine,root->fileName.data(), + // rootNav->parent()->section()); + if (!root->name.isEmpty()) + { + ClassDef *usingCd = 0; + NamespaceDef *nd = 0; + FileDef *fd = rootNav->fileDef(); + QCString scName; + + // see if the using statement was found inside a namespace or inside + // the global file scope. + if (rootNav->parent()->section() == Entry::NAMESPACE_SEC) + { + scName=rootNav->parent()->name(); + if (!scName.isEmpty()) + { + nd = getResolvedNamespace(scName); + } + } + + // Assume the using statement was used to import a class. + // Find the scope in which the `using' namespace is defined by prepending + // the possible scopes in which the using statement was found, starting + // with the most inner scope and going to the most outer scope (i.e. + // file scope). + + QCString name = substitute(root->name,".","::"); //Java/C# scope->internal + usingCd = getClass(name); + if (usingCd==0) + { + usingCd = Doxygen::hiddenClasses->find(name); + } + + //printf("%s -> %p\n",root->name.data(),usingCd); + if (usingCd==0) // definition not in the input => add an artificial class + { + Debug::print(Debug::Classes,0," New using class `%s' (sec=0x%08x)! #tArgLists=%d\n", + name.data(),root->section,root->tArgLists ? (int)root->tArgLists->count() : -1); + usingCd = new ClassDef( + "<using>",1, + name,ClassDef::Class); + Doxygen::hiddenClasses->append(root->name,usingCd); + usingCd->setArtificial(TRUE); + } + else + { + Debug::print(Debug::Classes,0," Found used class %s in scope=%s\n", + usingCd->name().data(),nd?nd->name().data():fd->name().data()); + } + + if (usingCd) // add the class to the correct scope + { + if (nd) + { + //printf("Inside namespace %s\n",nd->name().data()); + nd->addUsingDeclaration(usingCd); + } + else if (fd) + { + //printf("Inside file %s\n",fd->name().data()); + fd->addUsingDeclaration(usingCd); + } + } + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(findUsingDeclarations,rootNav); +} + +//---------------------------------------------------------------------- + +static void findUsingDeclImports(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::USINGDECL_SEC && + (rootNav->parent()->section()&Entry::COMPOUND_MASK) // in a class/struct member + ) + { + //printf("Found using declaration %s at line %d of %s inside section %x\n", + // root->name.data(),root->startLine,root->fileName.data(), + // root->parent->section); + QCString fullName=removeRedundantWhiteSpace(rootNav->parent()->name()); + fullName=stripAnonymousNamespaceScope(fullName); + fullName=stripTemplateSpecifiersFromScope(fullName); + ClassDef *cd = getClass(fullName); + if (cd) + { + //printf("found class %s\n",cd->name().data()); + int i=rootNav->name().find("::"); + if (i!=-1) + { + QCString scope=rootNav->name().left(i); + QCString memName=rootNav->name().right(rootNav->name().length()-i-2); + ClassDef *bcd = getResolvedClass(cd,0,scope); // todo: file in fileScope parameter + if (bcd) + { + //printf("found class %s\n",bcd->name().data()); + MemberNameInfoSDict *mndict=bcd->memberNameInfoSDict(); + if (mndict) + { + MemberNameInfo *mni = mndict->find(memName); + if (mni) + { + MemberNameInfoIterator mnii(*mni); + MemberInfo *mi; + for ( ; (mi=mnii.current()) ; ++mnii ) + { + MemberDef *md = mi->memberDef; + if (md && md->protection()!=Private) + { + + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + //printf("found member %s\n",mni->memberName()); + MemberDef *newMd = 0; + { + LockingPtr<ArgumentList> templAl = md->templateArguments(); + LockingPtr<ArgumentList> al = md->templateArguments(); + newMd = new MemberDef( + root->fileName,root->startLine, + md->typeString(),memName,md->argsString(), + md->excpString(),root->protection,root->virt, + md->isStatic(),Member,md->memberType(), + templAl.pointer(),al.pointer() + ); + } + newMd->setMemberClass(cd); + cd->insertMember(newMd); + if (!root->doc.isEmpty() || !root->brief.isEmpty()) + { + newMd->setDocumentation(root->doc,root->docFile,root->docLine); + newMd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + newMd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + } + else + { + newMd->setDocumentation(md->documentation(),md->docFile(),md->docLine()); + newMd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine()); + newMd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine()); + } + newMd->setDefinition(md->definition()); + newMd->enableCallGraph(root->callGraph); + newMd->enableCallerGraph(root->callerGraph); + newMd->setBitfields(md->bitfieldString()); + newMd->addSectionsToDefinition(root->anchors); + newMd->setBodySegment(md->getStartBodyLine(),md->getEndBodyLine()); + newMd->setBodyDef(md->getBodyDef()); + newMd->setInitializer(md->initializer()); + newMd->setMaxInitLines(md->initializerLines()); + newMd->setMemberGroupId(root->mGrpId); + newMd->setMemberSpecifiers(md->getMemberSpecifiers()); + + rootNav->releaseEntry(); + } + } + } + } + } + } + } + + } + RECURSE_ENTRYTREE(findUsingDeclImports,rootNav); +} + +//---------------------------------------------------------------------- + +static void findIncludedUsingDirectives() +{ + // first mark all files as not visited + FileNameListIterator fnli(*Doxygen::inputNameList); + FileName *fn; + for (fnli.toFirst();(fn=fnli.current());++fnli) + { + FileNameIterator fni(*fn); + FileDef *fd; + for (;(fd=fni.current());++fni) + { + fd->visited=FALSE; + } + } + // then recursively add using directives found in #include files + // to files that have not been visited. + for (fnli.toFirst();(fn=fnli.current());++fnli) + { + FileNameIterator fni(*fn); + FileDef *fd; + for (fni.toFirst();(fd=fni.current());++fni) + { + if (!fd->visited) + { + //printf("----- adding using directives for file %s\n",fd->name().data()); + fd->addIncludedUsingDirectives(); + } + } + } +} + +//---------------------------------------------------------------------- + +static MemberDef *addVariableToClass( + EntryNav *rootNav, + ClassDef *cd, + MemberDef::MemberType mtype, + const QCString &name, + bool fromAnnScope, + MemberDef *fromAnnMemb, + Protection prot, + Relationship related) +{ + Entry *root = rootNav->entry(); + + QCString qualScope = cd->qualifiedNameWithTemplateParameters(); + QCString scopeSeparator="::"; + if (Config_getBool("OPTIMIZE_OUTPUT_JAVA")) + { + qualScope = substitute(qualScope,"::","."); + scopeSeparator="."; + } + Debug::print(Debug::Variables,0, + " class variable:\n" + " `%s' `%s'::`%s' `%s' prot=`%d ann=%d init=`%s'\n", + root->type.data(), + qualScope.data(), + name.data(), + root->args.data(), + root->protection, + fromAnnScope, + root->initializer.data() + ); + + QCString def; + if (!root->type.isEmpty()) + { + if (related || mtype==MemberDef::Friend || Config_getBool("HIDE_SCOPE_NAMES")) + { + def=root->type+" "+name+root->args; + } + else + { + def=root->type+" "+qualScope+scopeSeparator+name+root->args; + } + } + else + { + if (Config_getBool("HIDE_SCOPE_NAMES")) + { + def=name+root->args; + } + else + { + def=qualScope+scopeSeparator+name+root->args; + } + } + def.stripPrefix("static "); + + // see if the member is already found in the same scope + // (this may be the case for a static member that is initialized + // outside the class) + MemberName *mn=Doxygen::memberNameSDict->find(name); + if (mn) + { + MemberNameIterator mni(*mn); + MemberDef *md; + for (mni.toFirst();(md=mni.current());++mni) + { + //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n", + // md->getClassDef(),cd,root->type.data(),md->typeString()); + if (md->getClassDef()==cd && + removeRedundantWhiteSpace(root->type)==md->typeString()) + // member already in the scope + { + + if (root->lang==SrcLangExt_ObjC && + root->mtype==Property && + md->memberType()==MemberDef::Variable) + { // Objective-C 2.0 property + // turn variable into a property + md->setProtection(root->protection); + cd->reclassifyMember(md,MemberDef::Property); + } + addMemberDocs(rootNav,md,def,0,FALSE); + //printf(" Member already found!\n"); + return md; + } + } + } + + // new member variable, typedef or enum value + MemberDef *md=new MemberDef( + root->fileName,root->startLine, + root->type,name,root->args,0, + prot,Normal,root->stat,related, + mtype,0,0); + md->setTagInfo(rootNav->tagInfo()); + md->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope()) + //md->setDefFile(root->fileName); + //md->setDefLine(root->startLine); + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setDefinition(def); + md->setBitfields(root->bitfields); + md->addSectionsToDefinition(root->anchors); + md->setFromAnonymousScope(fromAnnScope); + md->setFromAnonymousMember(fromAnnMemb); + //md->setIndentDepth(indentDepth); + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setInitializer(root->initializer); + md->setMaxInitLines(root->initLines); + md->setMemberGroupId(root->mGrpId); + md->setMemberSpecifiers(root->spec); + md->setReadAccessor(root->read); + md->setWriteAccessor(root->write); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + md->setHidden(root->hidden); + md->setArtificial(root->artificial); + addMemberToGroups(root,md); + //if (root->mGrpId!=-1) + //{ + // printf("memberdef %s in memberGroup %d\n",name.data(),root->mGrpId); + // md->setMemberGroup(memberGroupDict[root->mGrpId]); + // + md->setBodyDef(rootNav->fileDef()); + + //printf(" Adding member=%s\n",md->name().data()); + // add the member to the global list + if (mn) + { + mn->append(md); + } + else // new variable name + { + mn = new MemberName(name); + mn->append(md); + //printf("Adding memberName=%s\n",mn->memberName()); + //Doxygen::memberNameDict.insert(name,mn); + //Doxygen::memberNameList.append(mn); + Doxygen::memberNameSDict->append(name,mn); + // add the member to the class + } + //printf(" New member adding to %s (%p)!\n",cd->name().data(),cd); + cd->insertMember(md); + md->setRefItems(root->sli); + + //TODO: insert FileDef instead of filename strings. + cd->insertUsedFile(root->fileName); + rootNav->changeSection(Entry::EMPTY_SEC); + return md; +} + +//---------------------------------------------------------------------- + +static MemberDef *addVariableToFile( + EntryNav *rootNav, + MemberDef::MemberType mtype, + const QCString &scope, + const QCString &name, + bool fromAnnScope, + /*int indentDepth,*/ + MemberDef *fromAnnMemb) +{ + Entry *root = rootNav->entry(); + Debug::print(Debug::Variables,0, + " global variable:\n" + " type=`%s' scope=`%s' name=`%s' args=`%s' prot=`%d mtype=%d\n", + root->type.data(), + scope.data(), + name.data(), + root->args.data(), + root->protection, + mtype + ); + + FileDef *fd = rootNav->fileDef(); + + // see if we have a typedef that should hide a struct or union + if (mtype==MemberDef::Typedef && Config_getBool("TYPEDEF_HIDES_STRUCT")) + { + QCString type = root->type; + type.stripPrefix("typedef "); + if (type.left(7)=="struct " || type.left(6)=="union ") + { + type.stripPrefix("struct "); + type.stripPrefix("union "); + static QRegExp re("[a-z_A-Z][a-z_A-Z0-9]*"); + int l,s; + s = re.match(type,0,&l); + if (s>=0) + { + QCString typeValue = type.mid(s,l); + ClassDef *cd = getClass(typeValue); + if (cd) + { + // this typedef should hide compound name cd, so we + // change the name that is displayed from cd. + cd->setClassName(name); + cd->setDocumentation(root->doc,root->docFile,root->docLine); + cd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + return 0; + } + } + } + } + + // see if the function is inside a namespace + NamespaceDef *nd = 0; + QCString nscope; + if (!scope.isEmpty()) + { + if (scope.find('@')!=-1) return 0; // anonymous scope! + //nscope=removeAnonymousScopes(scope); + //if (!nscope.isEmpty()) + //{ + nd = getResolvedNamespace(scope); + //} + } + QCString def; + + // determine the definition of the global variable + if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@' && + !Config_getBool("HIDE_SCOPE_NAMES") + ) + // variable is inside a namespace, so put the scope before the name + { + static bool optimizeForJava = Config_getBool("OPTIMIZE_OUTPUT_JAVA"); + QCString sep="::"; + if (optimizeForJava) sep="."; + + if (!root->type.isEmpty()) + { + def=root->type+" "+nd->name()+sep+name+root->args; + } + else + { + def=nd->name()+sep+name+root->args; + } + } + else + { + if (!root->type.isEmpty() && !root->name.isEmpty()) + { + if (name.at(0)=='@') // dummy variable representing anonymous union + def=root->type; + else + def=root->type+" "+name+root->args; + } + else + { + def=name+root->args; + } + } + def.stripPrefix("static "); + + MemberName *mn=Doxygen::functionNameSDict->find(name); + if (mn) + { + //QCString nscope=removeAnonymousScopes(scope); + //NamespaceDef *nd=0; + //if (!nscope.isEmpty()) + if (!scope.isEmpty()) + { + nd = getResolvedNamespace(scope); + } + MemberNameIterator mni(*mn); + MemberDef *md; + for (mni.toFirst();(md=mni.current());++mni) + { + if ( + ((nd==0 && md->getNamespaceDef()==0 && md->getFileDef() && + root->fileName==md->getFileDef()->absFilePath() + ) // both variable names in the same file + || (nd!=0 && md->getNamespaceDef()==nd) // both in same namespace + ) + && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables + && !md->isEnumerate() // in C# an enum value and enum can have the same name + ) + // variable already in the scope + { + if (md->getFileDef() && + ! // not a php array + ( + (getLanguageFromFileName(md->getFileDef()->name())==SrcLangExt_PHP) && + (md->argsString()!=root->args && root->args.find('[')!=-1) + ) + ) + // not a php array variable + { + + Debug::print(Debug::Variables,0, + " variable already found: scope=%s\n",md->getOuterScope()->name().data()); + addMemberDocs(rootNav,md,def,0,FALSE); + md->setRefItems(root->sli); + return md; + } + } + } + } + Debug::print(Debug::Variables,0, + " new variable, nd=%s!\n",nd?nd->name().data():"<global>"); + // new global variable, enum value or typedef + MemberDef *md=new MemberDef( + root->fileName,root->startLine, + root->type,name,root->args,0, + Public, Normal,root->stat,Member, + mtype,0,0); + md->setTagInfo(rootNav->tagInfo()); + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->addSectionsToDefinition(root->anchors); + md->setFromAnonymousScope(fromAnnScope); + md->setFromAnonymousMember(fromAnnMemb); + md->setInitializer(root->initializer); + md->setMaxInitLines(root->initLines); + md->setMemberGroupId(root->mGrpId); + md->setDefinition(def); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + md->setExplicitExternal(root->explicitExternal); + //md->setOuterScope(fd); + if (!root->explicitExternal) + { + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setBodyDef(fd); + } + addMemberToGroups(root,md); + + md->setRefItems(root->sli); + if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') + { + md->setNamespace(nd); + nd->insertMember(md); + } + + // add member to the file (we do this even if we have already inserted + // it into the namespace. + if (fd) + { + md->setFileDef(fd); + fd->insertMember(md); + } + + // add member definition to the list of globals + if (mn) + { + mn->append(md); + } + else + { + mn = new MemberName(name); + mn->append(md); + Doxygen::functionNameSDict->append(name,mn); + } + rootNav->changeSection(Entry::EMPTY_SEC); + return md; +} + +/*! See if the return type string \a type is that of a function pointer + * \returns -1 if this is not a function pointer variable or + * the index at which the brace of (...*name) was found. + */ +static int findFunctionPtr(const QCString &type,int lang, int *pLength=0) +{ + if (lang == SrcLangExt_F90) return -1; // Fortran does not have function pointers + static const QRegExp re("([^)]*[\\*\\^][^)]*)"); + int i=-1,l; + if (!type.isEmpty() && // return type is non-empty + (i=re.match(type,0,&l))!=-1 && // contains (...*...) + type.find("operator")==-1 && // not an operator + (type.find(")(")==-1 || type.find("typedef ")!=-1) + // not a function pointer return type + ) + { + if (pLength) *pLength=l; + //printf("findFunctionPtr=%d\n",i); + return i; + } + else + { + //printf("findFunctionPtr=%d\n",-1); + return -1; + } +} + + +/*! Returns TRUE iff \a type is a class within scope \a context. + * Used to detect variable declarations that look like function prototypes. + */ +static bool isVarWithConstructor(EntryNav *rootNav) +{ + static QRegExp initChars("[0-9\"'&*!^]+"); + static QRegExp idChars("[a-z_A-Z][a-z_A-Z0-9]*"); + bool result=FALSE; + bool typeIsClass; + QCString type; + Definition *ctx = 0; + FileDef *fd = 0; + int ti; + + //printf("isVarWithConstructor(%s)\n",rootNav->name().data()); + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + if (rootNav->parent()->section() & Entry::COMPOUND_MASK) + { // inside a class + result=FALSE; + goto done; + } + else if ((fd = rootNav->fileDef()) && + (fd->name().right(2)==".c" || fd->name().right(2)==".h") + ) + { // inside a .c file + result=FALSE; + goto done; + } + if (root->type.isEmpty()) + { + result=FALSE; + goto done; + } + if (!rootNav->parent()->name().isEmpty()) + { + ctx=Doxygen::namespaceSDict->find(rootNav->parent()->name()); + } + type = root->type; + // remove qualifiers + findAndRemoveWord(type,"const"); + findAndRemoveWord(type,"static"); + findAndRemoveWord(type,"volatile"); + //if (type.left(6)=="const ") type=type.right(type.length()-6); + typeIsClass=getResolvedClass(ctx,fd,type)!=0; + if (!typeIsClass && (ti=type.find('<'))!=-1) + { + typeIsClass=getResolvedClass(ctx,fd,type.left(ti))!=0; + } + if (typeIsClass) // now we still have to check if the arguments are + // types or values. Since we do not have complete type info + // we need to rely on heuristics :-( + { + //printf("typeIsClass\n"); + ArgumentList *al = root->argList; + if (al==0 || al->isEmpty()) + { + result=FALSE; // empty arg list -> function prototype. + goto done; + } + ArgumentListIterator ali(*al); + Argument *a; + for (ali.toFirst();(a=ali.current());++ali) + { + if (!a->name.isEmpty() || !a->defval.isEmpty()) + { + if (a->name.find(initChars)==0) + { + result=TRUE; + } + else + { + result=FALSE; // arg has (type,name) pair -> function prototype + } + goto done; + } + if (a->type.isEmpty() || getResolvedClass(ctx,fd,a->type)!=0) + { + result=FALSE; // arg type is a known type + goto done; + } + if (checkIfTypedef(ctx,fd,a->type)) + { + //printf("%s:%d: false (arg is typedef)\n",__FILE__,__LINE__); + result=FALSE; // argument is a typedef + goto done; + } + if (a->type.at(a->type.length()-1)=='*' || + a->type.at(a->type.length()-1)=='&') + // type ends with * or & => pointer or reference + { + result=FALSE; + goto done; + } + if (a->type.find(initChars)==0) + { + result=TRUE; // argument type starts with typical initializer char + goto done; + } + QCString resType=resolveTypeDef(ctx,a->type); + if (resType.isEmpty()) resType=a->type; + int len; + if (idChars.match(resType,0,&len)==0) // resType starts with identifier + { + resType=resType.left(len); + //printf("resType=%s\n",resType.data()); + if (resType=="int" || resType=="long" || resType=="float" || + resType=="double" || resType=="char" || resType=="signed" || + resType=="const" || resType=="unsigned" || resType=="void") + { + result=FALSE; // type keyword -> function prototype + goto done; + } + } + } + result=TRUE; + } + +done: + //printf("isVarWithConstructor(%s,%s)=%d\n",rootNav->parent()->name().data(), + // root->type.data(),result); + rootNav->releaseEntry(); + return result; +} + +static void addVariable(EntryNav *rootNav,int isFuncPtr=-1) +{ + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + Debug::print(Debug::Variables,0, + "VARIABLE_SEC: \n" + " type=`%s' name=`%s' args=`%s' bodyLine=`%d' mGrpId=%d\n", + root->type.data(), + root->name.data(), + root->args.data(), + root->bodyLine, + root->mGrpId + ); + //printf("root->parent->name=%s\n",root->parent->name.data()); + + if (root->type.isEmpty() && root->name.find("operator")==-1 && + (root->name.find('*')!=-1 || root->name.find('&')!=-1)) + { + // recover from parse error caused by redundant braces + // like in "int *(var[10]);", which is parsed as + // type="" name="int *" args="(var[10])" + + root->type=root->name; + static const QRegExp reName("[a-z_A-Z][a-z_A-Z0-9]*"); + int l; + int i=root->args.isEmpty() ? -1 : reName.match(root->args,0,&l); + root->name=root->args.mid(i,l); + root->args=root->args.mid(i+l,root->args.find(')',i+l)-i-l); + //printf("new: type=`%s' name=`%s' args=`%s'\n", + // root->type.data(),root->name.data(),root->args.data()); + } + else + { + int i=isFuncPtr; + if (i==-1) i=findFunctionPtr(root->type,root->lang); // for typedefs isFuncPtr is not yet set + if (i!=-1) // function pointer + { + int ai = root->type.find('[',i); + if (ai>i) // function pointer array + { + root->args.prepend(root->type.right(root->type.length()-ai)); + root->type=root->type.left(ai); + } + else if (root->type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]" + { + root->type=root->type.left(root->type.length()-1); + root->args.prepend(")"); + //printf("root->type=%s root->args=%s\n",root->type.data(),root->args.data()); + } + } + else if (root->type.find("typedef ")!=-1 && root->type.right(2)=="()") // typedef void (func)(int) + { + root->type=root->type.left(root->type.length()-1); + root->args.prepend(")"); + } + } + + QCString scope,name=removeRedundantWhiteSpace(root->name); + + // find the scope of this variable + EntryNav *p = rootNav->parent(); + while ((p->section() & Entry::SCOPE_MASK)) + { + QCString scopeName = p->name(); + if (!scopeName.isEmpty()) + { + scope.prepend(scopeName); + break; + } + p=p->parent(); + } + + MemberDef::MemberType mtype; + QCString type=root->type.stripWhiteSpace(); + ClassDef *cd=0; + bool isRelated=FALSE; + bool isMemberOf=FALSE; + + QCString classScope=stripAnonymousNamespaceScope(scope); + classScope=stripTemplateSpecifiersFromScope(classScope,FALSE); + QCString annScopePrefix=scope.left(scope.length()-classScope.length()); + + if (root->name.findRev("::")!=-1) + { + if (root->type=="friend class" || root->type=="friend struct" || + root->type=="friend union") + { + cd=getClass(scope); + if (cd) + { + addVariableToClass(rootNav, // entry + cd, // class to add member to + MemberDef::Friend, // type of member + name, // name of the member + FALSE, // from Anonymous scope + 0, // anonymous member + Public, // protection + Member // related to a class + ); + } + } + goto nextMember; + /* skip this member, because it is a + * static variable definition (always?), which will be + * found in a class scope as well, but then we know the + * correct protection level, so only then it will be + * inserted in the correct list! + */ + } + + if (type=="@") + mtype=MemberDef::EnumValue; + else if (type.left(8)=="typedef ") + mtype=MemberDef::Typedef; + else if (type.left(7)=="friend ") + mtype=MemberDef::Friend; + else if (root->mtype==Property) + mtype=MemberDef::Property; + else if (root->mtype==Event) + mtype=MemberDef::Event; + else + mtype=MemberDef::Variable; + + if (!root->relates.isEmpty()) // related variable + { + isRelated=TRUE; + isMemberOf=(root->relatesType == MemberOf); + if (getClass(root->relates)==0 && !scope.isEmpty()) + scope=mergeScopes(scope,root->relates); + else + scope=root->relates; + } + + cd=getClass(scope); + if (cd==0 && classScope!=scope) cd=getClass(classScope); + if (cd) + { + MemberDef *md=0; + + // if cd is an anonymous scope we insert the member + // into a non-anonymous scope as well. This is needed to + // be able to refer to it using \var or \fn + + //int indentDepth=0; + int si=scope.find('@'); + //int anonyScopes = 0; + bool added=FALSE; + + if (si!=-1) // anonymous scope + { + QCString pScope; + ClassDef *pcd=0; + pScope = scope.left(QMAX(si-2,0)); + if (!pScope.isEmpty()) + pScope.prepend(annScopePrefix); + else if (annScopePrefix.length()>2) + pScope=annScopePrefix.left(annScopePrefix.length()-2); + if (name.at(0)!='@') + { + if (!pScope.isEmpty() && (pcd=getClass(pScope))) + { + md=addVariableToClass(rootNav, // entry + pcd, // class to add member to + mtype, // member type + name, // member name + TRUE, // from anonymous scope + 0, // from anonymous member + root->protection, + isMemberOf ? Foreign : isRelated ? Related : Member + ); + added=TRUE; + } + else // anonymous scope inside namespace or file => put variable in the global scope + { + if (mtype==MemberDef::Variable) + { + md=addVariableToFile(rootNav,mtype,pScope,name,TRUE,0); + } + added=TRUE; + } + } + } + + //printf("name=`%s' scope=%s scope.right=%s\n", + // name.data(),scope.data(), + // scope.right(scope.length()-si).data()); + addVariableToClass(rootNav, // entry + cd, // class to add member to + mtype, // member type + name, // name of the member + FALSE, // from anonymous scope + md, // from anonymous member + root->protection, + isMemberOf ? Foreign : isRelated ? Related : Member); + } + else if (!name.isEmpty()) // global variable + { + //printf("Inserting member in global scope %s!\n",scope.data()); + addVariableToFile(rootNav,mtype,scope,name,FALSE,/*0,*/0); + } + +nextMember: + rootNav->releaseEntry(); +} + +//---------------------------------------------------------------------- +// Searches the Entry tree for typedef documentation sections. +// If found they are stored in their class or in the global list. +static void buildTypedefList(EntryNav *rootNav) +{ + //printf("buildVarList(%s)\n",rootNav->name().data()); + if (!rootNav->name().isEmpty() && + rootNav->section()==Entry::VARIABLE_SEC && + rootNav->type().find("typedef ")!=-1 // its a typedef + ) + { + addVariable(rootNav); + } + if (rootNav->children()) + { + EntryNavListIterator eli(*rootNav->children()); + EntryNav *e; + for (;(e=eli.current());++eli) + { + if (e->section()!=Entry::ENUM_SEC) + { + buildTypedefList(e); + } + } + } +} + +//---------------------------------------------------------------------- +// Searches the Entry tree for Variable documentation sections. +// If found they are stored in their class or in the global list. + +static void buildVarList(EntryNav *rootNav) +{ + //printf("buildVarList(%s)\n",rootNav->name().data()); + int isFuncPtr=-1; + if (!rootNav->name().isEmpty() && + (rootNav->type().isEmpty() || g_compoundKeywordDict.find(rootNav->type())==0) && + ( + (rootNav->section()==Entry::VARIABLE_SEC // it's a variable + ) || + (rootNav->section()==Entry::FUNCTION_SEC && // or maybe a function pointer variable + (isFuncPtr=findFunctionPtr(rootNav->type(),rootNav->lang()))!=-1 + ) || + (rootNav->section()==Entry::FUNCTION_SEC && // class variable initialized by constructor + isVarWithConstructor(rootNav) + ) + ) + ) // documented variable + { + addVariable(rootNav,isFuncPtr); + } + if (rootNav->children()) + { + EntryNavListIterator eli(*rootNav->children()); + EntryNav *e; + for (;(e=eli.current());++eli) + { + if (e->section()!=Entry::ENUM_SEC) + { + buildVarList(e); + } + } + } +} + +//---------------------------------------------------------------------- +// Searches the Entry tree for Function sections. +// If found they are stored in their class or in the global list. + +static void addMethodToClass(EntryNav *rootNav,ClassDef *cd, + const QCString &rname,bool isFriend) +{ + Entry *root = rootNav->entry(); + FileDef *fd=rootNav->fileDef(); + + int l,i=-1; + static QRegExp re("([a-z_A-Z0-9: ]*[ &*]+[ ]*"); + + if (!root->type.isEmpty() && (i=re.match(root->type,0,&l))!=-1) // function variable + { + root->args+=root->type.right(root->type.length()-i-l); + root->type=root->type.left(i+l); + } + + QCString name=removeRedundantWhiteSpace(rname); + if (name.left(2)=="::") name=name.right(name.length()-2); + + MemberDef::MemberType mtype; + if (isFriend) mtype=MemberDef::Friend; + else if (root->mtype==Signal) mtype=MemberDef::Signal; + else if (root->mtype==Slot) mtype=MemberDef::Slot; + else if (root->mtype==DCOP) mtype=MemberDef::DCOP; + else mtype=MemberDef::Function; + + // strip redundant template specifier for constructors + if ((fd==0 || getLanguageFromFileName(fd->name())==SrcLangExt_Cpp) && + name.left(9)!="operator " && (i=name.find('<'))!=-1 && name.find('>')!=-1) + { + name=name.left(i); + } + + //printf("root->name=`%s; root->args=`%s' root->argList=`%s'\n", + // root->name.data(),root->args.data(),argListToString(root->argList).data() + // ); + + // adding class member + MemberDef *md=new MemberDef( + root->fileName,root->startLine, + root->type,name,root->args,root->exception, + root->protection,root->virt, + root->stat && root->relatesType != MemberOf, + root->relates.isEmpty() ? Member : + root->relatesType == MemberOf ? Foreign : Related, + mtype,root->tArgLists ? root->tArgLists->last() : 0,root->argList); + md->setTagInfo(rootNav->tagInfo()); + md->setMemberClass(cd); + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setDocsForDefinition(!root->proto); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setMemberSpecifiers(root->spec); + md->setMemberGroupId(root->mGrpId); + md->setTypeConstraints(root->typeConstr); + md->setBodyDef(fd); + md->setFileDef(fd); + //md->setScopeTemplateArguments(root->tArgList); + md->addSectionsToDefinition(root->anchors); + QCString def; + QCString qualScope = cd->qualifiedNameWithTemplateParameters(); + QCString scopeSeparator="::"; + if (Config_getBool("OPTIMIZE_OUTPUT_JAVA")) + { + qualScope = substitute(qualScope,"::","."); + scopeSeparator="."; + } + if (!root->relates.isEmpty() || isFriend || Config_getBool("HIDE_SCOPE_NAMES")) + { + if (!root->type.isEmpty()) + { + if (root->argList) + { + def=root->type+" "+name; + } + else + { + def=root->type+" "+name+root->args; + } + } + else + { + if (root->argList) + { + def=name; + } + else + { + def=name+root->args; + } + } + } + else + { + if (!root->type.isEmpty()) + { + if (root->argList) + { + def=root->type+" "+qualScope+scopeSeparator+name; + } + else + { + def=root->type+" "+qualScope+scopeSeparator+name+root->args; + } + } + else + { + if (root->argList) + { + def=qualScope+scopeSeparator+name; + } + else + { + def=qualScope+scopeSeparator+name+root->args; + } + } + } + if (def.left(7)=="friend ") def=def.right(def.length()-7); + md->setDefinition(def); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + + Debug::print(Debug::Functions,0, + " Func Member:\n" + " `%s' `%s'::`%s' `%s' proto=%d\n" + " def=`%s'\n", + root->type.data(), + qualScope.data(), + rname.data(), + root->args.data(), + root->proto, + def.data() + ); + + // add member to the global list of all members + //printf("Adding member=%s class=%s\n",md->name().data(),cd->name().data()); + MemberName *mn; + if ((mn=Doxygen::memberNameSDict->find(name))) + { + mn->append(md); + } + else + { + mn = new MemberName(name); + mn->append(md); + Doxygen::memberNameSDict->append(name,mn); + } + + // add member to the class cd + cd->insertMember(md); + // add file to list of used files + cd->insertUsedFile(root->fileName); + + addMemberToGroups(root,md); + rootNav->changeSection(Entry::EMPTY_SEC); + md->setRefItems(root->sli); +} + + +static void buildFunctionList(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::FUNCTION_SEC) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + Debug::print(Debug::Functions,0, + "FUNCTION_SEC:\n" + " `%s' `%s'::`%s' `%s' relates=`%s' relatesType=`%d' file=`%s' line=`%d' bodyLine=`%d' #tArgLists=%d mGrpId=%d spec=%d proto=%d docFile=%s\n", + root->type.data(), + rootNav->parent()->name().data(), + root->name.data(), + root->args.data(), + root->relates.data(), + root->relatesType, + root->fileName.data(), + root->startLine, + root->bodyLine, + root->tArgLists ? (int)root->tArgLists->count() : -1, + root->mGrpId, + root->spec, + root->proto, + root->docFile.data() + ); + + bool isFriend=root->type.find("friend ")!=-1; + QCString rname = removeRedundantWhiteSpace(root->name); + //printf("rname=%s\n",rname.data()); + + QCString scope=rootNav->parent()->name(); //stripAnonymousNamespaceScope(root->parent->name); + if (!rname.isEmpty() && scope.find('@')==-1) + { + ClassDef *cd=0; + // check if this function's parent is a class + scope=stripTemplateSpecifiersFromScope(scope,FALSE); + + FileDef *rfd=rootNav->fileDef(); + + int memIndex=rname.findRev("::"); + + cd=getClass(scope); + if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A + { + // strip scope from name + rname=rname.right(rname.length()-rootNav->parent()->name().length()-2); + } + + NamespaceDef *nd = 0; + bool isMember=FALSE; + if (memIndex!=-1) + { + int ts=rname.find('<'); + int te=rname.find('>'); + if (memIndex>0 && (ts==-1 || te==-1)) + { + // note: the following code was replaced by inMember=TRUE to deal with a + // function rname='X::foo' of class X inside a namespace also called X... + // bug id 548175 + //nd = Doxygen::namespaceSDict->find(rname.left(memIndex)); + //isMember = nd==0; + //if (nd) + //{ + // // strip namespace scope from name + // scope=rname.left(memIndex); + // rname=rname.right(rname.length()-memIndex-2); + //} + isMember = TRUE; + } + else + { + isMember=memIndex<ts || memIndex>te; + } + } + + static QRegExp re("([a-z_A-Z0-9: ]*[ &*]+[ ]*"); + if (!rootNav->parent()->name().isEmpty() && + (rootNav->parent()->section() & Entry::COMPOUND_MASK) && + cd && + // do some fuzzy things to exclude function pointers + (root->type.isEmpty() || + (root->type.find(re,0)==-1 || root->args.find(")[")!=-1) || // type contains ..(..* and args not )[.. -> function pointer + root->type.find(")(")!=-1 || root->type.find("operator")!=-1 // type contains ..)(.. and not "operator" + ) + ) + { + Debug::print(Debug::Functions,0," --> member %s of class %s!\n", + rname.data(),cd->name().data()); + addMethodToClass(rootNav,cd,rname,isFriend); + } + else if (!((rootNav->parent()->section() & Entry::COMPOUND_MASK) + || rootNav->parent()->section()==Entry::OBJCIMPL_SEC + ) && + !isMember && + (root->relates.isEmpty() || root->relatesType == Duplicate) && + root->type.left(7)!="extern " && root->type.left(8)!="typedef " + ) + // no member => unrelated function + { + /* check the uniqueness of the function name in the file. + * A file could contain a function prototype and a function definition + * or even multiple function prototypes. + */ + bool found=FALSE; + MemberName *mn; + MemberDef *md=0; + if ((mn=Doxygen::functionNameSDict->find(rname))) + { + Debug::print(Debug::Functions,0," --> function %s already found!\n",rname.data()); + MemberNameIterator mni(*mn); + for (mni.toFirst();(!found && (md=mni.current()));++mni) + { + NamespaceDef *mnd = md->getNamespaceDef(); + NamespaceDef *rnd = 0; + //printf("root namespace=%s\n",rootNav->parent()->name().data()); + QCString fullScope = scope; + QCString parentScope = rootNav->parent()->name(); + if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope)) + { + if (!scope.isEmpty()) fullScope.prepend("::"); + fullScope.prepend(parentScope); + } + //printf("fullScope=%s\n",fullScope.data()); + rnd = getResolvedNamespace(fullScope); + FileDef *mfd = md->getFileDef(); + QCString nsName,rnsName; + if (mnd) nsName = mnd->name().copy(); + if (rnd) rnsName = rnd->name().copy(); + //printf("matching arguments for %s%s %s%s\n", + // md->name().data(),md->argsString(),rname.data(),argListToString(root->argList).data()); + LockingPtr<ArgumentList> mdAl = md->argumentList(); + LockingPtr<ArgumentList> mdTempl = md->templateArguments(); + + // in case of template functions, we need to check if the + // functions have the same number of template parameters + bool sameNumTemplateArgs = TRUE; + if (mdTempl!=0 && root->tArgLists) + { + if (mdTempl->count()!=root->tArgLists->getLast()->count()) + { + sameNumTemplateArgs = FALSE; + } + } + if ( + matchArguments2(md->getOuterScope(),mfd,mdAl.pointer(), + rnd ? rnd : Doxygen::globalScope,rfd,root->argList, + FALSE) && + sameNumTemplateArgs + ) + { + GroupDef *gd=0; + if (root->groups->first()!=0) + { + gd = Doxygen::groupSDict->find(root->groups->first()->groupname.data()); + } + //printf("match!\n"); + //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,nsName.data(),rnsName.data()); + // see if we need to create a new member + found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace + ((mnd==0 && rnd==0 && mfd!=0 && // no external reference and + mfd->absFilePath()==root->fileName // prototype in the same file + ) + ); + // otherwise, allow a duplicate global member with the same argument list + if (!found && gd && gd==md->getGroupDef()) + { + // member is already in the group, so we don't want to add it again. + found=TRUE; + } + + //printf("combining function with prototype found=%d in namespace %s\n", + // found,nsName.data()); + + if (found) + { + // merge argument lists + mergeArguments(mdAl.pointer(),root->argList,!root->doc.isEmpty()); + // merge documentation + if (md->documentation().isEmpty() && !root->doc.isEmpty()) + { + ArgumentList *argList = new ArgumentList; + stringToArgumentList(root->args,argList); + if (root->proto) + { + //printf("setDeclArgumentList to %p\n",argList); + md->setDeclArgumentList(argList); + } + else + { + md->setArgumentList(argList); + } + } + + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setDocsForDefinition(!root->proto); + if (md->getStartBodyLine()!=-1 && md->getStartBodyLine()==-1) + { + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setBodyDef(rfd); + } + + if (md->briefDescription().isEmpty() && !root->brief.isEmpty()) + { + md->setArgsString(root->args); + } + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + + md->addSectionsToDefinition(root->anchors); + + md->enableCallGraph(md->hasCallGraph() || root->callGraph); + md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph); + + // merge ingroup specifiers + if (md->getGroupDef()==0 && root->groups->first()!=0) + { + addMemberToGroups(root,md); + } + else if (md->getGroupDef()!=0 && root->groups->count()==0) + { + //printf("existing member is grouped, new member not\n"); + root->groups->append(new Grouping(md->getGroupDef()->name(), md->getGroupPri())); + } + else if (md->getGroupDef()!=0 && root->groups->first()!=0) + { + //printf("both members are grouped\n"); + } + + // if md is a declaration and root is the corresponding + // definition, then turn md into a definition. + if (md->isPrototype() && !root->proto) + { + md->setPrototype(FALSE); + } + } + } + } + } + if (!found) /* global function is unique with respect to the file */ + { + Debug::print(Debug::Functions,0," --> new function %s found!\n",rname.data()); + //printf("New function type=`%s' name=`%s' args=`%s' bodyLine=%d\n", + // root->type.data(),rname.data(),root->args.data(),root->bodyLine); + + // new global function + ArgumentList *tArgList = root->tArgLists ? root->tArgLists->last() : 0; + QCString name=removeRedundantWhiteSpace(rname); + md=new MemberDef( + root->fileName,root->startLine, + root->type,name,root->args,root->exception, + root->protection,root->virt,root->stat,Member, + MemberDef::Function,tArgList,root->argList); + + md->setTagInfo(rootNav->tagInfo()); + //md->setDefFile(root->fileName); + //md->setDefLine(root->startLine); + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setPrototype(root->proto); + md->setDocsForDefinition(!root->proto); + md->setTypeConstraints(root->typeConstr); + //md->setBody(root->body); + md->setBodySegment(root->bodyLine,root->endBodyLine); + FileDef *fd=rootNav->fileDef(); + md->setBodyDef(fd); + md->addSectionsToDefinition(root->anchors); + md->setMemberSpecifiers(root->spec); + md->setMemberGroupId(root->mGrpId); + + // see if the function is inside a namespace that was not part of + // the name already (in that case nd should be non-zero already) + if (nd==0 && rootNav->parent()->section() == Entry::NAMESPACE_SEC ) + { + //QCString nscope=removeAnonymousScopes(rootNav->parent()->name()); + QCString nscope=rootNav->parent()->name(); + if (!nscope.isEmpty()) + { + nd = getResolvedNamespace(nscope); + } + } + + if (!scope.isEmpty()) + { + if (Config_getBool("OPTIMIZE_OUTPUT_JAVA")) + { + scope = substitute(scope,"::",".")+"."; + } + else + { + scope+="::"; + } + } + + QCString def; + if (!root->type.isEmpty()) + { + if (root->argList) + { + def=root->type+" "+scope+name; + } + else + { + def=root->type+" "+scope+name+root->args; + } + } + else + { + if (root->argList) + { + def=scope+name.copy(); + } + else + { + def=scope+name+root->args; + } + } + Debug::print(Debug::Functions,0, + " Global Function:\n" + " `%s' `%s'::`%s' `%s' proto=%d\n" + " def=`%s'\n", + root->type.data(), + rootNav->parent()->name().data(), + rname.data(), + root->args.data(), + root->proto, + def.data() + ); + md->setDefinition(def); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + //if (root->mGrpId!=-1) + //{ + // md->setMemberGroup(memberGroupDict[root->mGrpId]); + //} + + md->setRefItems(root->sli); + if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') + { + // add member to namespace + md->setNamespace(nd); + nd->insertMember(md); + } + if (fd) + { + // add member to the file (we do this even if we have already + // inserted it into the namespace) + md->setFileDef(fd); + fd->insertMember(md); + } + + // add member to the list of file members + //printf("Adding member=%s\n",md->name().data()); + MemberName *mn; + if ((mn=Doxygen::functionNameSDict->find(name))) + { + mn->append(md); + } + else + { + mn = new MemberName(name); + mn->append(md); + Doxygen::functionNameSDict->append(name,mn); + } + addMemberToGroups(root,md); + if (root->relatesType == Simple) // if this is a relatesalso command, + // allow find Member to pick it up + { + rootNav->changeSection(Entry::EMPTY_SEC); // Otherwise we have finished + // with this entry. + + } + } + else + { + FileDef *fd=rootNav->fileDef(); + if (fd) + { + // add member to the file (we do this even if we have already + // inserted it into the namespace) + fd->insertMember(md); + } + } + + //printf("unrelated function %d `%s' `%s' `%s'\n", + // root->parent->section,root->type.data(),rname.data(),root->args.data()); + } + else + { + Debug::print(Debug::Functions,0," --> %s not processed!\n",rname.data()); + } + } + else if (rname.isEmpty()) + { + warn(root->fileName,root->startLine, + "warning: Illegal member name found." + ); + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(buildFunctionList,rootNav); +} + +//---------------------------------------------------------------------- + +static void findFriends() +{ + //printf("findFriends()\n"); + MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict); + MemberName *fn; + for (;(fn=fnli.current());++fnli) // for each global function name + { + //printf("Function name=`%s'\n",fn->memberName()); + MemberName *mn; + if ((mn=Doxygen::memberNameSDict->find(fn->memberName()))) + { // there are members with the same name + //printf("Function name is also a member name\n"); + MemberNameIterator fni(*fn); + MemberDef *fmd; + for (;(fmd=fni.current());++fni) // for each function with that name + { + MemberNameIterator mni(*mn); + MemberDef *mmd; + for (;(mmd=mni.current());++mni) // for each member with that name + { + //printf("Checking for matching arguments + // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n", + // mmd->isRelated(),mmd->isFriend(),mmd->isFunction()); + LockingPtr<ArgumentList> mmdAl = mmd->argumentList(); + LockingPtr<ArgumentList> fmdAl = fmd->argumentList(); + if ((mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) && + matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmdAl.pointer(), + fmd->getOuterScope(), fmd->getFileDef(), fmdAl.pointer(), + TRUE + ) + + ) // if the member is related and the arguments match then the + // function is actually a friend. + { + mergeArguments(mmdAl.pointer(),fmdAl.pointer()); + if (!fmd->documentation().isEmpty()) + { + mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine()); + } + else if (!mmd->documentation().isEmpty()) + { + fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine()); + } + if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty()) + { + mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine()); + } + else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty()) + { + fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine()); + } + if (!fmd->inbodyDocumentation().isEmpty()) + { + mmd->setInbodyDocumentation(fmd->inbodyDocumentation(),fmd->inbodyFile(),fmd->inbodyLine()); + } + else if (!mmd->inbodyDocumentation().isEmpty()) + { + fmd->setInbodyDocumentation(mmd->inbodyDocumentation(),mmd->inbodyFile(),mmd->inbodyLine()); + } + //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine()); + if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1) + { + mmd->setBodySegment(fmd->getStartBodyLine(),fmd->getEndBodyLine()); + mmd->setBodyDef(fmd->getBodyDef()); + //mmd->setBodyMember(fmd); + } + else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1) + { + fmd->setBodySegment(mmd->getStartBodyLine(),mmd->getEndBodyLine()); + fmd->setBodyDef(mmd->getBodyDef()); + //fmd->setBodyMember(mmd); + } + mmd->setDocsForDefinition(fmd->isDocsForDefinition()); + + mmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph()); + mmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph()); + fmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph()); + fmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph()); + } + } + } + } + } +} + +//---------------------------------------------------------------------- + +static void transferArgumentDocumentation(ArgumentList *decAl,ArgumentList *defAl) +{ + if (decAl && defAl) + { + ArgumentListIterator decAli(*decAl); + ArgumentListIterator defAli(*defAl); + Argument *decA,*defA; + for (decAli.toFirst(),defAli.toFirst(); + (decA=decAli.current()) && (defA=defAli.current()); + ++decAli,++defAli) + { + //printf("Argument decA->name=%s (doc=%s) defA->name=%s (doc=%s)\n", + // decA->name.data(),decA->docs.data(), + // defA->name.data(),defA->docs.data() + // ); + if (decA->docs.isEmpty() && !defA->docs.isEmpty()) + { + decA->docs = defA->docs.copy(); + } + else if (defA->docs.isEmpty() && !decA->docs.isEmpty()) + { + defA->docs = decA->docs.copy(); + } + } + } +} + +static void transferFunctionDocumentation() +{ + //printf("---- transferFunctionDocumentation()\n"); + + // find matching function declaration and definitions. + MemberNameSDict::Iterator mnli(*Doxygen::functionNameSDict); + MemberName *mn; + for (;(mn=mnli.current());++mnli) + { + //printf("memberName=%s count=%d\n",mn->memberName(),mn->count()); + MemberDef *mdef=0,*mdec=0; + MemberNameIterator mni1(*mn); + /* find a matching function declaration and definition for this function */ + for (;(mdec=mni1.current());++mni1) + { + //printf("mdec=%s isPrototype()=%d\n",mdec->name().data(),mdec->isPrototype()); + if (mdec->isPrototype() || + (mdec->isVariable() && mdec->isExternal()) + ) + { + MemberNameIterator mni2(*mn); + for (;(mdef=mni2.current());++mni2) + { + if ( + (mdef->isFunction() && !mdef->isStatic() && !mdef->isPrototype()) || + (mdef->isVariable() && !mdef->isExternal() && !mdef->isStatic()) + ) + { + //printf("mdef=(%p,%s) mdec=(%p,%s)\n", + // mdef, mdef ? mdef->name().data() : "", + // mdec, mdec ? mdec->name().data() : ""); + + LockingPtr<ArgumentList> mdefAl = mdef->argumentList(); + LockingPtr<ArgumentList> mdecAl = mdec->argumentList(); + if (matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdefAl.pointer(), + mdec->getOuterScope(),mdec->getFileDef(),mdecAl.pointer(), + TRUE + ) + ) /* match found */ + { + //printf("Found member %s: definition in %s (doc=`%s') and declaration in %s (doc=`%s')\n", + // mn->memberName(), + // mdef->getFileDef()->name().data(),mdef->documentation().data(), + // mdec->getFileDef()->name().data(),mdec->documentation().data() + // ); + + // first merge argument documentation + transferArgumentDocumentation(mdecAl.pointer(),mdefAl.pointer()); + + /* copy documentation between function definition and declaration */ + if (!mdec->briefDescription().isEmpty()) + { + mdef->setBriefDescription(mdec->briefDescription(),mdec->briefFile(),mdec->briefLine()); + } + else if (!mdef->briefDescription().isEmpty()) + { + mdec->setBriefDescription(mdef->briefDescription(),mdef->briefFile(),mdef->briefLine()); + } + if (!mdef->documentation().isEmpty()) + { + //printf("transfering docs mdef->mdec (%s->%s)\n",mdef->argsString(),mdec->argsString()); + mdec->setDocumentation(mdef->documentation(),mdef->docFile(),mdef->docLine()); + mdec->setDocsForDefinition(mdef->isDocsForDefinition()); + if (mdefAl!=0) + { + ArgumentList *mdefAlComb = new ArgumentList; + stringToArgumentList(mdef->argsString(),mdefAlComb); + transferArgumentDocumentation(mdefAl.pointer(),mdefAlComb); + mdec->setArgumentList(mdefAlComb); + } + } + else if (!mdec->documentation().isEmpty()) + { + //printf("transfering docs mdec->mdef (%s->%s)\n",mdec->argsString(),mdef->argsString()); + mdef->setDocumentation(mdec->documentation(),mdec->docFile(),mdec->docLine()); + mdef->setDocsForDefinition(mdec->isDocsForDefinition()); + if (mdecAl!=0) + { + ArgumentList *mdecAlComb = new ArgumentList; + stringToArgumentList(mdec->argsString(),mdecAlComb); + transferArgumentDocumentation(mdecAl.pointer(),mdecAlComb); + mdef->setDeclArgumentList(mdecAlComb); + } + } + if (!mdef->inbodyDocumentation().isEmpty()) + { + mdec->setInbodyDocumentation(mdef->inbodyDocumentation(),mdef->inbodyFile(),mdef->inbodyLine()); + } + else if (!mdec->inbodyDocumentation().isEmpty()) + { + mdef->setInbodyDocumentation(mdec->inbodyDocumentation(),mdec->inbodyFile(),mdec->inbodyLine()); + } + if (mdec->getStartBodyLine()!=-1 && mdef->getStartBodyLine()==-1) + { + //printf("body mdec->mdef %d-%d\n",mdec->getStartBodyLine(),mdef->getEndBodyLine()); + mdef->setBodySegment(mdec->getStartBodyLine(),mdec->getEndBodyLine()); + mdef->setBodyDef(mdec->getBodyDef()); + //mdef->setBodyMember(mdec); + } + else if (mdef->getStartBodyLine()!=-1 && mdec->getStartBodyLine()==-1) + { + //printf("body mdef->mdec %d-%d\n",mdef->getStartBodyLine(),mdec->getEndBodyLine()); + mdec->setBodySegment(mdef->getStartBodyLine(),mdef->getEndBodyLine()); + mdec->setBodyDef(mdef->getBodyDef()); + //mdec->setBodyMember(mdef); + } + mdec->mergeMemberSpecifiers(mdef->getMemberSpecifiers()); + mdef->mergeMemberSpecifiers(mdec->getMemberSpecifiers()); + + + // copy group info. + if (mdec->getGroupDef()==0 && mdef->getGroupDef()!=0) + { + mdec->setGroupDef(mdef->getGroupDef(), + mdef->getGroupPri(), + mdef->docFile(), + mdef->docLine(), + mdef->hasDocumentation(), + mdef + ); + } + else if (mdef->getGroupDef()==0 && mdec->getGroupDef()!=0) + { + mdef->setGroupDef(mdec->getGroupDef(), + mdec->getGroupPri(), + mdec->docFile(), + mdec->docLine(), + mdec->hasDocumentation(), + mdec + ); + } + + + mdec->mergeRefItems(mdef); + mdef->mergeRefItems(mdec); + + mdef->setMemberDeclaration(mdec); + mdec->setMemberDefinition(mdef); + + mdef->enableCallGraph(mdec->hasCallGraph() || mdef->hasCallGraph()); + mdef->enableCallerGraph(mdec->hasCallerGraph() || mdef->hasCallerGraph()); + mdec->enableCallGraph(mdec->hasCallGraph() || mdef->hasCallGraph()); + mdec->enableCallerGraph(mdec->hasCallerGraph() || mdef->hasCallerGraph()); + } + } + } + } + } + } +} + +//---------------------------------------------------------------------- + +static void transferFunctionReferences() +{ + MemberNameSDict::Iterator mnli(*Doxygen::functionNameSDict); + MemberName *mn; + for (;(mn=mnli.current());++mnli) + { + MemberDef *md,*mdef=0,*mdec=0; + MemberNameIterator mni(*mn); + /* find a matching function declaration and definition for this function */ + for (;(md=mni.current());++mni) + { + if (md->isPrototype()) + mdec=md; + else if (md->isVariable() && md->isExternal()) + mdec=md; + + if (md->isFunction() && !md->isStatic() && !md->isPrototype()) + mdef=md; + else if (md->isVariable() && !md->isExternal() && !md->isStatic()) + mdef=md; + } + if (mdef && mdec) + { + LockingPtr<ArgumentList> mdefAl = mdef->argumentList(); + LockingPtr<ArgumentList> mdecAl = mdec->argumentList(); + if ( + matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdefAl.pointer(), + mdec->getOuterScope(),mdec->getFileDef(),mdecAl.pointer(), + TRUE + ) + ) /* match found */ + { + LockingPtr<MemberSDict> defDict = mdef->getReferencesMembers(); + LockingPtr<MemberSDict> decDict = mdec->getReferencesMembers(); + if (defDict!=0) + { + MemberSDict::Iterator msdi(*defDict); + MemberDef *rmd; + for (msdi.toFirst();(rmd=msdi.current());++msdi) + { + if (decDict==0 || decDict->find(rmd->name())==0) + { + mdec->addSourceReferences(rmd); + } + } + } + if (decDict!=0) + { + MemberSDict::Iterator msdi(*decDict); + MemberDef *rmd; + for (msdi.toFirst();(rmd=msdi.current());++msdi) + { + if (defDict==0 || defDict->find(rmd->name())==0) + { + mdef->addSourceReferences(rmd); + } + } + } + + defDict = mdef->getReferencedByMembers(); + decDict = mdec->getReferencedByMembers(); + if (defDict!=0) + { + MemberSDict::Iterator msdi(*defDict); + MemberDef *rmd; + for (msdi.toFirst();(rmd=msdi.current());++msdi) + { + if (decDict==0 || decDict->find(rmd->name())==0) + { + mdec->addSourceReferencedBy(rmd); + } + } + } + if (decDict!=0) + { + MemberSDict::Iterator msdi(*decDict); + MemberDef *rmd; + for (msdi.toFirst();(rmd=msdi.current());++msdi) + { + if (defDict==0 || defDict->find(rmd->name())==0) + { + mdef->addSourceReferencedBy(rmd); + } + } + } + } + } + } +} + +//---------------------------------------------------------------------- + +static void transferRelatedFunctionDocumentation() +{ + // find match between function declaration and definition for + // related functions + MemberNameSDict::Iterator mnli(*Doxygen::functionNameSDict); + MemberName *mn; + for (mnli.toFirst();(mn=mnli.current());++mnli) + { + MemberDef *md; + MemberNameIterator mni(*mn); + /* find a matching function declaration and definition for this function */ + for (mni.toFirst();(md=mni.current());++mni) // for each global function + { + //printf(" Function `%s'\n",md->name().data()); + MemberName *rmn; + if ((rmn=Doxygen::memberNameSDict->find(md->name()))) // check if there is a member with the same name + { + //printf(" Member name found\n"); + MemberDef *rmd; + MemberNameIterator rmni(*rmn); + for (rmni.toFirst();(rmd=rmni.current());++rmni) // for each member with the same name + { + LockingPtr<ArgumentList> mdAl = md->argumentList(); + LockingPtr<ArgumentList> rmdAl = rmd->argumentList(); + //printf(" Member found: related=`%d'\n",rmd->isRelated()); + if ((rmd->isRelated() || rmd->isForeign()) && // related function + matchArguments2( md->getOuterScope(), md->getFileDef(), mdAl.pointer(), + rmd->getOuterScope(),rmd->getFileDef(),rmdAl.pointer(), + TRUE + ) + ) + { + //printf(" Found related member `%s'\n",md->name().data()); + if (rmd->relatedAlso()) + md->setRelatedAlso(rmd->relatedAlso()); + else if (rmd->isForeign()) + md->makeForeign(); + else + md->makeRelated(); + } + } + } + } + } +} + +//---------------------------------------------------------------------- + +/*! make a dictionary of all template arguments of class cd + * that are part of the base class name. + * Example: A template class A with template arguments <R,S,T> + * that inherits from B<T,T,S> will have T and S in the dictionary. + */ +static QDict<int> *getTemplateArgumentsInName(ArgumentList *templateArguments,const QCString &name) +{ + QDict<int> *templateNames = new QDict<int>(17); + templateNames->setAutoDelete(TRUE); + static QRegExp re("[a-z_A-Z][a-z_A-Z0-9:]*"); + if (templateArguments) + { + ArgumentListIterator ali(*templateArguments); + Argument *arg; + int count=0; + for (ali.toFirst();(arg=ali.current());++ali,count++) + { + int i,p=0,l; + while ((i=re.match(name,p,&l))!=-1) + { + QCString n = name.mid(i,l); + if (n==arg->name) + { + if (templateNames->find(n)==0) + { + templateNames->insert(n,new int(count)); + } + } + p=i+l; + } + } + } + return templateNames; +} + +/*! Searches a class from within \a context and \a cd and returns its + * definition if found (otherwise 0 is returned). + */ +static ClassDef *findClassWithinClassContext(Definition *context,ClassDef *cd,const QCString &name) +{ + FileDef *fd=cd->getFileDef(); + ClassDef *result=0; + if (context && cd!=context) + { + result = getResolvedClass(context,0,name,0,0,TRUE,TRUE); + } + if (result==0) + { + result = getResolvedClass(cd,fd,name,0,0,TRUE,TRUE); + } + if (result==0) // try direct class, needed for namespaced classes imported via tag files (see bug624095) + { + result = getClass(name); + } + //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n", + // name.data(), + // context ? context->name().data() : "<none>", + // cd ? cd->name().data() : "<none>", + // result ? result->name().data() : "<none>", + // Doxygen::classSDict.find(name) + // ); + return result; +} + +enum FindBaseClassRelation_Mode +{ + TemplateInstances, + DocumentedOnly, + Undocumented +}; + +static bool findClassRelation( + EntryNav *rootNav, + Definition *context, + ClassDef *cd, + BaseInfo *bi, + QDict<int> *templateNames, + /*bool insertUndocumented*/ + FindBaseClassRelation_Mode mode, + bool isArtificial + ); + + +static void findUsedClassesForClass(EntryNav *rootNav, + Definition *context, + ClassDef *masterCd, + ClassDef *instanceCd, + bool isArtificial, + ArgumentList *actualArgs=0, + QDict<int> *templateNames=0 + ) +{ + masterCd->visited=TRUE; + ArgumentList *formalArgs = masterCd->templateArguments(); + if (masterCd->memberNameInfoSDict()) + { + MemberNameInfoSDict::Iterator mnili(*masterCd->memberNameInfoSDict()); + MemberNameInfo *mni; + for (;(mni=mnili.current());++mnili) + { + MemberNameInfoIterator mnii(*mni); + MemberInfo *mi; + for (mnii.toFirst();(mi=mnii.current());++mnii) + { + MemberDef *md=mi->memberDef; + if (md->isVariable()) // for each member variable in this class + { + //printf(" Found variable %s in class %s\n",md->name().data(),masterCd->name().data()); + QCString type=removeRedundantWhiteSpace(md->typeString()); + QCString typedefValue = resolveTypeDef(masterCd,type); + if (!typedefValue.isEmpty()) + { + type = typedefValue; + } + int pos=0; + QCString usedClassName; + QCString templSpec; + bool found=FALSE; + // the type can contain template variables, replace them if present + if (actualArgs) + { + type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs); + } + + //printf(" template substitution gives=%s\n",type.data()); + while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec)!=-1) + { + // find the type (if any) that matches usedClassName + ClassDef *typeCd = getResolvedClass(masterCd, + masterCd->getFileDef(), + usedClassName, + 0,0, + FALSE,TRUE + ); + //printf("====> usedClassName=%s -> typeCd=%s\n", + // usedClassName.data(),typeCd?typeCd->name().data():"<none>"); + if (typeCd) + { + usedClassName = typeCd->name(); + } + + int sp=usedClassName.find('<'); + if (sp==-1) sp=0; + int si=usedClassName.findRev("::",sp); + if (si!=-1) + { + // replace any namespace aliases + replaceNamespaceAliases(usedClassName,si); + } + // add any template arguments to the class + QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec); + //printf(" usedName=%s\n",usedName.data()); + + bool delTempNames=FALSE; + if (templateNames==0) + { + templateNames = getTemplateArgumentsInName(formalArgs,usedName); + delTempNames=TRUE; + } + BaseInfo bi(usedName,Public,Normal); + findClassRelation(rootNav,context,instanceCd,&bi,templateNames,TemplateInstances,isArtificial); + + if (masterCd->templateArguments()) + { + ArgumentListIterator ali(*masterCd->templateArguments()); + Argument *arg; + int count=0; + for (ali.toFirst();(arg=ali.current());++ali,++count) + { + if (arg->name==usedName) // type is a template argument + { + found=TRUE; + Debug::print(Debug::Classes,0," New used class `%s'\n", usedName.data()); + + ClassDef *usedCd = Doxygen::hiddenClasses->find(usedName); + if (usedCd==0) + { + usedCd = new ClassDef( + masterCd->getDefFileName(),masterCd->getDefLine(), + usedName,ClassDef::Class); + //printf("making %s a template argument!!!\n",usedCd->name().data()); + usedCd->makeTemplateArgument(); + usedCd->setUsedOnly(TRUE); + Doxygen::hiddenClasses->append(usedName,usedCd); + } + if (usedCd) + { + if (isArtificial) usedCd->setArtificial(TRUE); + Debug::print(Debug::Classes,0," Adding used class `%s' (1)\n", usedCd->name().data()); + instanceCd->addUsedClass(usedCd,md->name()); + usedCd->addUsedByClass(instanceCd,md->name()); + } + } + } + } + + if (!found) + { + ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName); + //printf("Looking for used class %s: result=%s master=%s\n", + // usedName.data(),usedCd?usedCd->name().data():"<none>",masterCd?masterCd->name().data():"<none>"); + + if (usedCd) + { + found=TRUE; + Debug::print(Debug::Classes,0," Adding used class `%s' (2)\n", usedCd->name().data()); + instanceCd->addUsedClass(usedCd,md->name()); // class exists + usedCd->addUsedByClass(instanceCd,md->name()); + } + } + if (delTempNames) + { + delete templateNames; + templateNames=0; + } + } + if (!found && !type.isEmpty()) // used class is not documented in any scope + { + ClassDef *usedCd = Doxygen::hiddenClasses->find(type); + if (usedCd==0 && !Config_getBool("HIDE_UNDOC_RELATIONS")) + { + if (type.right(2)=="(*" || type.right(2)=="(^") // type is a function pointer + { + type+=md->argsString(); + } + Debug::print(Debug::Classes,0," New undocumented used class `%s'\n", type.data()); + usedCd = new ClassDef( + masterCd->getDefFileName(),masterCd->getDefLine(), + type,ClassDef::Class); + usedCd->setUsedOnly(TRUE); + Doxygen::hiddenClasses->append(type,usedCd); + } + if (usedCd) + { + if (isArtificial) usedCd->setArtificial(TRUE); + Debug::print(Debug::Classes,0," Adding used class `%s' (3)\n", usedCd->name().data()); + instanceCd->addUsedClass(usedCd,md->name()); + usedCd->addUsedByClass(instanceCd,md->name()); + } + } + } + } + } + } + else + { + //printf("no members for class %s (%p)\n",masterCd->name().data(),masterCd); + } +} + +static void findBaseClassesForClass( + EntryNav *rootNav, + Definition *context, + ClassDef *masterCd, + ClassDef *instanceCd, + FindBaseClassRelation_Mode mode, + bool isArtificial, + ArgumentList *actualArgs=0, + QDict<int> *templateNames=0 + ) +{ + Entry *root = rootNav->entry(); + //if (masterCd->visited) return; + masterCd->visited=TRUE; + // The base class could ofcouse also be a non-nested class + ArgumentList *formalArgs = masterCd->templateArguments(); + QListIterator<BaseInfo> bii(*root->extends); + BaseInfo *bi=0; + for (bii.toFirst();(bi=bii.current());++bii) + { + //printf("masterCd=%s bi->name='%s' #actualArgs=%d\n", + // masterCd->localName().data(),bi->name.data(),actualArgs?(int)actualArgs->count():-1); + bool delTempNames=FALSE; + if (templateNames==0) + { + templateNames = getTemplateArgumentsInName(formalArgs,bi->name); + delTempNames=TRUE; + } + BaseInfo tbi(bi->name,bi->prot,bi->virt); + if (actualArgs) // substitute the formal template arguments of the base class + { + tbi.name = substituteTemplateArgumentsInString(bi->name,formalArgs,actualArgs); + } + //printf("bi->name=%s tbi.name=%s\n",bi->name.data(),tbi.name.data()); + + if (mode==DocumentedOnly) + { + // find a documented base class in the correct scope + if (!findClassRelation(rootNav,context,instanceCd,&tbi,templateNames,DocumentedOnly,isArtificial)) + { + if (!Config_getBool("HIDE_UNDOC_RELATIONS")) + { + // no documented base class -> try to find an undocumented one + findClassRelation(rootNav,context,instanceCd,&tbi,templateNames,Undocumented,isArtificial); + } + } + } + else if (mode==TemplateInstances) + { + findClassRelation(rootNav,context,instanceCd,&tbi,templateNames,TemplateInstances,isArtificial); + } + if (delTempNames) + { + delete templateNames; + templateNames=0; + } + } +} + +//---------------------------------------------------------------------- + +static bool findTemplateInstanceRelation(Entry *root, + Definition *context, + ClassDef *templateClass,const QCString &templSpec, + QDict<int> *templateNames, + bool isArtificial) +{ + Debug::print(Debug::Classes,0," derived from template %s with parameters %s\n", + templateClass->name().data(),templSpec.data()); + //printf("findTemplateInstanceRelation(base=%s templSpec=%s templateNames=", + // templateClass->name().data(),templSpec.data()); + //if (templateNames) + //{ + // QDictIterator<int> qdi(*templateNames); + // int *tempArgIndex; + // for (;(tempArgIndex=qdi.current());++qdi) + // { + // printf("(%s->%d) ",qdi.currentKey(),*tempArgIndex); + // } + //} + //printf("\n"); + + bool existingClass = (templSpec == + tempArgListToString(templateClass->templateArguments()) + ); + if (existingClass) return TRUE; + + bool freshInstance=FALSE; + ClassDef *instanceClass = templateClass->insertTemplateInstance( + root->fileName,root->startLine,templSpec,freshInstance); + if (isArtificial) instanceClass->setArtificial(TRUE); + instanceClass->setLanguage(root->lang); + + if (freshInstance) + { + Debug::print(Debug::Classes,0," found fresh instance '%s'!\n",instanceClass->name().data()); + Doxygen::classSDict->append(instanceClass->name(),instanceClass); + instanceClass->setTemplateBaseClassNames(templateNames); + + // search for new template instances caused by base classes of + // instanceClass + EntryNav *templateRootNav = g_classEntries.find(templateClass->name()); + if (templateRootNav) + { + bool unloadNeeded=FALSE; + Entry *templateRoot = templateRootNav->entry(); + if (templateRoot==0) // not yet loaded + { + templateRootNav->loadEntry(g_storage); + templateRoot = templateRootNav->entry(); + ASSERT(templateRoot!=0); // now it should really be loaded + unloadNeeded=TRUE; + } + + Debug::print(Debug::Classes,0," template root found %s templSpec=%s!\n", + templateRoot->name.data(),templSpec.data()); + ArgumentList *templArgs = new ArgumentList; + stringToArgumentList(templSpec,templArgs); + findBaseClassesForClass(templateRootNav,context,templateClass,instanceClass, + TemplateInstances,isArtificial,templArgs,templateNames); + + findUsedClassesForClass(templateRootNav,context,templateClass,instanceClass, + isArtificial,templArgs,templateNames); + delete templArgs; + + if (unloadNeeded) // still cleanup to do + { + templateRootNav->releaseEntry(); + } + } + else + { + Debug::print(Debug::Classes,0," no template root entry found!\n"); + // TODO: what happened if we get here? + } + + //Debug::print(Debug::Classes,0," Template instance %s : \n",instanceClass->name().data()); + //ArgumentList *tl = templateClass->templateArguments(); + } + else + { + Debug::print(Debug::Classes,0," instance already exists!\n"); + } + return TRUE; +} + +static bool isRecursiveBaseClass(const QCString &scope,const QCString &name) +{ + QCString n=name; + int index=n.find('<'); + if (index!=-1) + { + n=n.left(index); + } + bool result = rightScopeMatch(scope,n); + return result; +} + +/*! Searches for the end of a template in prototype \a s starting from + * character position \a startPos. If the end was found the position + * of the closing \> is returned, otherwise -1 is returned. + * + * Handles exotic cases such as + * \code + * Class<(id<0)> + * Class<bits<<2> + * Class<"<"> + * Class<'<'> + * Class<(")<")> + * \endcode + */ +static int findEndOfTemplate(const QCString &s,int startPos) +{ + // locate end of template + int e=startPos; + int brCount=1; + int roundCount=0; + int len = s.length(); + bool insideString=FALSE; + bool insideChar=FALSE; + char pc = 0; + while (e<len && brCount!=0) + { + char c=s.at(e); + switch(c) + { + case '<': + if (!insideString && !insideChar) + { + if (e<len-1 && s.at(e+1)=='<') + e++; + else if (roundCount==0) + brCount++; + } + break; + case '>': + if (!insideString && !insideChar) + { + if (e<len-1 && s.at(e+1)=='>') + e++; + else if (roundCount==0) + brCount--; + } + break; + case '(': + if (!insideString && !insideChar) + roundCount++; + break; + case ')': + if (!insideString && !insideChar) + roundCount--; + break; + case '"': + if (!insideChar) + { + if (insideString && pc!='\\') + insideString=FALSE; + else + insideString=TRUE; + } + break; + case '\'': + if (!insideString) + { + if (insideChar && pc!='\\') + insideChar=FALSE; + else + insideChar=TRUE; + } + break; + } + pc = c; + e++; + } + return brCount==0 ? e : -1; +} + +static bool findClassRelation( + EntryNav *rootNav, + Definition *context, + ClassDef *cd, + BaseInfo *bi, + QDict<int> *templateNames, + FindBaseClassRelation_Mode mode, + bool isArtificial + ) +{ + //printf("findClassRelation(class=%s base=%s templateNames=", + // cd->name().data(),bi->name.data()); + //if (templateNames) + //{ + // QDictIterator<int> qdi(*templateNames); + // int *tempArgIndex; + // for (;(tempArgIndex=qdi.current());++qdi) + // { + // printf("(%s->%d) ",qdi.currentKey(),*tempArgIndex); + // } + //} + //printf("\n"); + + Entry *root = rootNav->entry(); + + QCString biName=bi->name; + bool explicitGlobalScope=FALSE; + //printf("findClassRelation: biName=`%s'\n",biName.data()); + if (biName.left(2)=="::") // explicit global scope + { + biName=biName.right(biName.length()-2); + explicitGlobalScope=TRUE; + } + + EntryNav *parentNode=rootNav->parent(); + bool lastParent=FALSE; + do // for each parent scope, starting with the largest scope + // (in case of nested classes) + { + QCString scopeName= parentNode ? parentNode->name().data() : ""; + int scopeOffset=explicitGlobalScope ? 0 : scopeName.length(); + do // try all parent scope prefixes, starting with the largest scope + { + //printf("scopePrefix=`%s' biName=`%s'\n", + // scopeName.left(scopeOffset).data(),biName.data()); + + QCString baseClassName=biName; + if (scopeOffset>0) + { + baseClassName.prepend(scopeName.left(scopeOffset)+"::"); + } + //QCString stripped; + //baseClassName=stripTemplateSpecifiersFromScope + // (removeRedundantWhiteSpace(baseClassName),TRUE, + // &stripped); + MemberDef *baseClassTypeDef=0; + QCString templSpec; + ClassDef *baseClass=getResolvedClass(explicitGlobalScope ? Doxygen::globalScope : context, + cd->getFileDef(), + baseClassName, + &baseClassTypeDef, + &templSpec, + mode==Undocumented, + TRUE + ); + //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n", + // baseClassName.data(),baseClass,cd,explicitGlobalScope); + //printf(" scope=`%s' baseClassName=`%s' baseClass=%s templSpec=%s\n", + // cd ? cd->name().data():"<none>", + // baseClassName.data(), + // baseClass?baseClass->name().data():"<none>", + // templSpec.data() + // ); + //if (baseClassName.left(root->name.length())!=root->name || + // baseClassName.at(root->name.length())!='<' + // ) // Check for base class with the same name. + // // If found then look in the outer scope for a match + // // and prevent recursion. + if (!isRecursiveBaseClass(rootNav->name(),baseClassName) || explicitGlobalScope) + { + Debug::print( + Debug::Classes,0," class relation %s inherited/used by %s found (%s and %s) templSpec='%s'\n", + baseClassName.data(), + rootNav->name().data(), + (bi->prot==Private)?"private":((bi->prot==Protected)?"protected":"public"), + (bi->virt==Normal)?"normal":"virtual", + templSpec.data() + ); + + int i=baseClassName.find('<'); + int si=baseClassName.findRev("::",i==-1 ? baseClassName.length() : i); + if (si==-1) si=0; + if (baseClass==0 && i!=-1) + // base class has template specifiers + { + // TODO: here we should try to find the correct template specialization + // but for now, we only look for the unspecializated base class. + int e=findEndOfTemplate(baseClassName,i+1); + //printf("baseClass==0 i=%d e=%d\n",i,e); + if (e!=-1) // end of template was found at e + { + templSpec=removeRedundantWhiteSpace(baseClassName.mid(i,e-i)); + baseClassName=baseClassName.left(i)+baseClassName.right(baseClassName.length()-e); + baseClass=getResolvedClass(explicitGlobalScope ? Doxygen::globalScope : context, + cd->getFileDef(), + baseClassName, + &baseClassTypeDef, + 0, //&templSpec, + mode==Undocumented, + TRUE + ); + //printf("baseClass=%p -> baseClass=%s templSpec=%s\n", + // baseClass,baseClassName.data(),templSpec.data()); + } + } + else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also + // know it is a template, so see if + // we can also link to the explicit + // instance (for instance if a class + // derived from a template argument) + { + //printf("baseClass=%p templSpec=%s\n",baseClass,templSpec.data()); + ClassDef *templClass=getClass(baseClass->name()+templSpec); + if (templClass) + { + // use the template instance instead of the template base. + baseClass = templClass; + templSpec.resize(0); + } + } + + //printf("cd=%p baseClass=%p\n",cd,baseClass); + bool found=baseClass!=0 && (baseClass!=cd || mode==TemplateInstances); + //printf("1. found=%d\n",found); + if (!found && si!=-1) + { + QCString tmpTemplSpec; + // replace any namespace aliases + replaceNamespaceAliases(baseClassName,si); + baseClass=getResolvedClass(explicitGlobalScope ? Doxygen::globalScope : context, + cd->getFileDef(), + baseClassName, + &baseClassTypeDef, + &tmpTemplSpec, + mode==Undocumented, + TRUE + ); + found=baseClass!=0 && baseClass!=cd; + if (found) templSpec = tmpTemplSpec; + } + //printf("2. found=%d\n",found); + + //printf("root->name=%s biName=%s baseClassName=%s\n", + // root->name.data(),biName.data(),baseClassName.data()); + if (cd->isCSharp() && i!=-1) // C# generic -> add internal -g postfix + { + baseClassName+="-g"; + templSpec.resize(0); + } + + if (!found) + { + baseClass=findClassWithinClassContext(context,cd,baseClassName); + //printf("findClassWithinClassContext(%s,%s)=%p\n", + // cd->name().data(),baseClassName.data(),baseClass); + found = baseClass!=0 && baseClass!=cd; + + } + if (!found) + { + // for PHP the "use A\B as C" construct map class C to A::B, so we lookup + // the class name also in the alias mapping. + QCString *aliasName = Doxygen::namespaceAliasDict[baseClassName]; + if (aliasName) // see if it is indeed a class. + { + baseClass=getClass(*aliasName); + found = baseClass!=0 && baseClass!=cd; + } + } + bool isATemplateArgument = templateNames!=0 && templateNames->find(biName)!=0; + // make templSpec canonical + // warning: the following line doesn't work for Mixin classes (see bug 560623) + // templSpec = getCanonicalTemplateSpec(cd, cd->getFileDef(), templSpec); + + //printf("3. found=%d\n",found); + if (found) + { + Debug::print(Debug::Classes,0," Documented base class `%s' templSpec=%s\n",biName.data(),templSpec.isEmpty()?"":templSpec.data()); + // add base class to this class + + // if templSpec is not empty then we should "instantiate" + // the template baseClass. A new ClassDef should be created + // to represent the instance. To be able to add the (instantiated) + // members and documentation of a template class + // (inserted in that template class at a later stage), + // the template should know about its instances. + // the instantiation process, should be done in a recursive way, + // since instantiating a template may introduce new inheritance + // relations. + if (!templSpec.isEmpty() && mode==TemplateInstances) + { + // if baseClass is actually a typedef then we should not + // instantiate it, since typedefs are in a different namespace + // see bug531637 for an example where this would otherwise hang + // doxygen + if (baseClassTypeDef==0) + { + //printf(" => findTemplateInstanceRelation: %p\n",baseClassTypeDef); + findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,isArtificial); + } + } + else if (mode==DocumentedOnly || mode==Undocumented) + { + //printf(" => insert base class\n"); + QCString usedName; + if (baseClassTypeDef || cd->isCSharp()) + { + usedName=biName; + //printf("***** usedName=%s templSpec=%s\n",usedName.data(),templSpec.data()); + } + if (Config_getBool("SIP_SUPPORT")) bi->prot=Public; + cd->insertBaseClass(baseClass,usedName,bi->prot,bi->virt,templSpec); + // add this class as super class to the base class + baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec); + } + return TRUE; + } + else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument)) + { + Debug::print(Debug::Classes,0, + " New undocumented base class `%s' baseClassName=%s\n", + biName.data(),baseClassName.data() + ); + baseClass=0; + if (isATemplateArgument) + { + baseClass=Doxygen::hiddenClasses->find(baseClassName); + if (baseClass==0) + { + baseClass=new ClassDef(root->fileName,root->startLine, + baseClassName,ClassDef::Class); + Doxygen::hiddenClasses->append(baseClassName,baseClass); + if (isArtificial) baseClass->setArtificial(TRUE); + } + } + else + { + baseClass=Doxygen::classSDict->find(baseClassName); + //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n", + // baseClassName.data(),baseClass,biName.data(),templSpec.data()); + if (baseClass==0) + { + baseClass=new ClassDef(root->fileName,root->startLine, + baseClassName,ClassDef::Class); + Doxygen::classSDict->append(baseClassName,baseClass); + if (isArtificial) baseClass->setArtificial(TRUE); + } + } + // add base class to this class + cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec); + // add this class as super class to the base class + baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec); + // the undocumented base was found in this file + baseClass->insertUsedFile(root->fileName); + baseClass->setOuterScope(Doxygen::globalScope); + return TRUE; + } + else + { + Debug::print(Debug::Classes,0," Base class `%s' not found\n",biName.data()); + } + } + else + { + if (mode!=TemplateInstances) + { + warn(root->fileName,root->startLine, + "Detected potential recursive class relation " + "between class %s and base class %s!\n", + root->name.data(),baseClassName.data() + ); + } + // for mode==TemplateInstance this case is quite common and + // indicates a relation between a template class and a template + // instance with the same name. + } + if (scopeOffset==0) + { + scopeOffset=-1; + } + else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1) + { + scopeOffset=0; + } + //printf("new scopeOffset=`%d'",scopeOffset); + } while (scopeOffset>=0); + + if (parentNode==0) + { + lastParent=TRUE; + } + else + { + parentNode=parentNode->parent(); + } + } while (lastParent); + + return FALSE; +} + +//---------------------------------------------------------------------- +// Computes the base and super classes for each class in the tree + +static bool isClassSection(EntryNav *rootNav) +{ + if ( !rootNav->name().isEmpty() ) + { + if (rootNav->section() & Entry::COMPOUND_MASK) + // is it a compound (class, struct, union, interface ...) + { + return TRUE; + } + else if (rootNav->section() & Entry::COMPOUNDDOC_MASK) + // is it a documentation block with inheritance info. + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + bool extends = root->extends->count()>0; + rootNav->releaseEntry(); + if (extends) return TRUE; + } + } + return FALSE; +} + + +/*! Builds a dictionary of all entry nodes in the tree starting with \a root + */ +static void findClassEntries(EntryNav *rootNav) +{ + if (isClassSection(rootNav)) + { + g_classEntries.insert(rootNav->name(),rootNav); + } + RECURSE_ENTRYTREE(findClassEntries,rootNav); +} + +/*! Using the dictionary build by findClassEntries(), this + * function will look for additional template specialization that + * exists as inheritance relations only. These instances will be + * added to the template they are derived from. + */ +static void findInheritedTemplateInstances() +{ + ClassSDict::Iterator cli(*Doxygen::classSDict); + for (cli.toFirst();cli.current();++cli) cli.current()->visited=FALSE; + QDictIterator<EntryNav> edi(g_classEntries); + EntryNav *rootNav; + for (;(rootNav=edi.current());++edi) + { + ClassDef *cd; + // strip any anonymous scopes first + QCString bName=stripAnonymousNamespaceScope(rootNav->name()); + bName=stripTemplateSpecifiersFromScope(bName); + Debug::print(Debug::Classes,0," Inheritance: Class %s : \n",bName.data()); + if ((cd=getClass(bName))) + { + rootNav->loadEntry(g_storage); + //printf("Class %s %d\n",cd->name().data(),root->extends->count()); + findBaseClassesForClass(rootNav,cd,cd,cd,TemplateInstances,FALSE); + rootNav->releaseEntry(); + } + } +} + +static void findUsedTemplateInstances() +{ + ClassSDict::Iterator cli(*Doxygen::classSDict); + for (cli.toFirst();cli.current();++cli) cli.current()->visited=FALSE; + QDictIterator<EntryNav> edi(g_classEntries); + EntryNav *rootNav; + for (;(rootNav=edi.current());++edi) + { + ClassDef *cd; + // strip any anonymous scopes first + QCString bName=stripAnonymousNamespaceScope(rootNav->name()); + bName=stripTemplateSpecifiersFromScope(bName); + Debug::print(Debug::Classes,0," Usage: Class %s : \n",bName.data()); + if ((cd=getClass(bName))) + { + rootNav->loadEntry(g_storage); + findUsedClassesForClass(rootNav,cd,cd,cd,TRUE); + rootNav->releaseEntry(); + } + } +} + +static void computeClassRelations() +{ + ClassSDict::Iterator cli(*Doxygen::classSDict); + for (cli.toFirst();cli.current();++cli) cli.current()->visited=FALSE; + QDictIterator<EntryNav> edi(g_classEntries); + EntryNav *rootNav; + for (;(rootNav=edi.current());++edi) + { + ClassDef *cd; + + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + // strip any anonymous scopes first + QCString bName=stripAnonymousNamespaceScope(rootNav->name()); + bName=stripTemplateSpecifiersFromScope(bName); + Debug::print(Debug::Classes,0," Relations: Class %s : \n",bName.data()); + if ((cd=getClass(bName))) + { + findBaseClassesForClass(rootNav,cd,cd,cd,DocumentedOnly,FALSE); + } + if ((cd==0 || (!cd->hasDocumentation() && !cd->isReference())) && + bName.right(2)!="::") + { + if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name + (guessSection(root->fileName)==Entry::HEADER_SEC || + Config_getBool("EXTRACT_LOCAL_CLASSES")) && // not defined in source file + (root->protection!=Private || Config_getBool("EXTRACT_PRIVATE")) && // hidden by protection + !Config_getBool("HIDE_UNDOC_CLASSES") // undocumented class are visible + ) + warn_undoc( + root->fileName,root->startLine, + "warning: Compound %s is not documented.", + root->name.data() + ); + } + + rootNav->releaseEntry(); + } +} + +static void computeTemplateClassRelations() +{ + QDictIterator<EntryNav> edi(g_classEntries); + EntryNav *rootNav; + for (;(rootNav=edi.current());++edi) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + QCString bName=stripAnonymousNamespaceScope(root->name); + bName=stripTemplateSpecifiersFromScope(bName); + ClassDef *cd=getClass(bName); + // strip any anonymous scopes first + QDict<ClassDef> *templInstances = 0; + if (cd && (templInstances=cd->getTemplateInstances())) + { + Debug::print(Debug::Classes,0," Template class %s : \n",cd->name().data()); + QDictIterator<ClassDef> tdi(*templInstances); + ClassDef *tcd; + for (tdi.toFirst();(tcd=tdi.current());++tdi) // for each template instance + { + Debug::print(Debug::Classes,0," Template instance %s : \n",tcd->name().data()); + QCString templSpec = tdi.currentKey(); + ArgumentList *templArgs = new ArgumentList; + stringToArgumentList(templSpec,templArgs); + QList<BaseInfo> *baseList=root->extends; + BaseInfo *bi=baseList->first(); + while (bi) // for each base class of the template + { + // check if the base class is a template argument + BaseInfo tbi(bi->name,bi->prot,bi->virt); + ArgumentList *tl = cd->templateArguments(); + if (tl) + { + QDict<int> *baseClassNames = tcd->getTemplateBaseClassNames(); + QDict<int> *templateNames = getTemplateArgumentsInName(tl,bi->name); + // for each template name that we inherit from we need to + // substitute the formal with the actual arguments + QDict<int> *actualTemplateNames = new QDict<int>(17); + actualTemplateNames->setAutoDelete(TRUE); + QDictIterator<int> qdi(*templateNames); + for (qdi.toFirst();qdi.current();++qdi) + { + int templIndex = *qdi.current(); + Argument *actArg = 0; + if (templIndex<(int)templArgs->count()) + { + actArg=templArgs->at(templIndex); + } + if (actArg!=0 && + baseClassNames!=0 && + baseClassNames->find(actArg->type)!=0 && + actualTemplateNames->find(actArg->type)==0 + ) + { + actualTemplateNames->insert(actArg->type,new int(templIndex)); + } + } + delete templateNames; + + tbi.name = substituteTemplateArgumentsInString(bi->name,tl,templArgs); + // find a documented base class in the correct scope + if (!findClassRelation(rootNav,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE)) + { + // no documented base class -> try to find an undocumented one + findClassRelation(rootNav,cd,tcd,&tbi,actualTemplateNames,Undocumented,FALSE); + } + delete actualTemplateNames; + } + bi=baseList->next(); + } + delete templArgs; + } // class has no base classes + } + + rootNav->releaseEntry(); + } +} + +//----------------------------------------------------------------------- +// compute the references (anchors in HTML) for each function in the file + +static void computeMemberReferences() +{ + ClassSDict::Iterator cli(*Doxygen::classSDict); + ClassDef *cd=0; + for (cli.toFirst();(cd=cli.current());++cli) + { + cd->computeAnchors(); + } + FileName *fn=Doxygen::inputNameList->first(); + while (fn) + { + FileDef *fd=fn->first(); + while (fd) + { + fd->computeAnchors(); + fd=fn->next(); + } + fn=Doxygen::inputNameList->next(); + } + NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); + NamespaceDef *nd=0; + for (nli.toFirst();(nd=nli.current());++nli) + { + nd->computeAnchors(); + } + GroupSDict::Iterator gli(*Doxygen::groupSDict); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + gd->computeAnchors(); + } +} + +//---------------------------------------------------------------------- + +static void addListReferences() +{ + MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict); + MemberName *mn=0; + for (mnli.toFirst();(mn=mnli.current());++mnli) + { + MemberNameIterator mni(*mn); + MemberDef *md=0; + for (mni.toFirst();(md=mni.current());++mni) + { + md->visited=FALSE; + } + } + MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict); + for (fnli.toFirst();(mn=fnli.current());++fnli) + { + MemberNameIterator mni(*mn); + MemberDef *md=0; + for (mni.toFirst();(md=mni.current());++mni) + { + md->visited=FALSE; + } + } + + ClassSDict::Iterator cli(*Doxygen::classSDict); + ClassDef *cd=0; + for (cli.toFirst();(cd=cli.current());++cli) + { + cd->addListReferences(); + } + FileName *fn=Doxygen::inputNameList->first(); + while (fn) + { + FileDef *fd=fn->first(); + while (fd) + { + fd->addListReferences(); + fd=fn->next(); + } + fn=Doxygen::inputNameList->next(); + } + NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); + NamespaceDef *nd=0; + for (nli.toFirst();(nd=nli.current());++nli) + { + nd->addListReferences(); + } + GroupSDict::Iterator gli(*Doxygen::groupSDict); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + gd->addListReferences(); + } + PageSDict::Iterator pdi(*Doxygen::pageSDict); + PageDef *pd=0; + for (pdi.toFirst();(pd=pdi.current());++pdi) + { + QCString name = pd->getOutputFileBase(); + if (pd->getGroupDef()) + { + name = pd->getGroupDef()->getOutputFileBase(); + } + { + LockingPtr< QList<ListItemInfo> > xrefItems = pd->xrefListItems(); + addRefItem(xrefItems.pointer(), + name, + theTranslator->trPage(TRUE,TRUE), + name,pd->title(),0); + } + } + DirSDict::Iterator ddi(*Doxygen::directories); + DirDef *dd = 0; + for (ddi.toFirst();(dd=ddi.current());++ddi) + { + QCString name = dd->getOutputFileBase(); + //if (dd->getGroupDef()) + //{ + // name = dd->getGroupDef()->getOutputFileBase(); + //} + LockingPtr< QList<ListItemInfo> > xrefItems = dd->xrefListItems(); + addRefItem(xrefItems.pointer(), + name, + theTranslator->trDir(TRUE,TRUE), + name,dd->displayName(),0); + } +} + +//---------------------------------------------------------------------- + +static void generateXRefPages() +{ + QDictIterator<RefList> di(*Doxygen::xrefLists); + RefList *rl; + for (di.toFirst();(rl=di.current());++di) + { + rl->generatePage(); + } +} + +//---------------------------------------------------------------------- +// Copy the documentation in entry `root' to member definition `md' and +// set the function declaration of the member to `funcDecl'. If the boolean +// over_load is set the standard overload text is added. + +static void addMemberDocs(EntryNav *rootNav, + MemberDef *md, const char *funcDecl, + ArgumentList *al, + bool over_load, + NamespaceSDict * + ) +{ + Entry *root = rootNav->entry(); + //printf("addMemberDocs: `%s'::`%s' `%s' funcDecl=`%s' mSpec=%d\n", + // root->parent->name.data(),md->name().data(),md->argsString(),funcDecl,root->spec); + QCString fDecl=funcDecl; + // strip extern specifier + fDecl.stripPrefix("extern "); + md->setDefinition(fDecl); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + ClassDef *cd=md->getClassDef(); + NamespaceDef *nd=md->getNamespaceDef(); + QCString fullName; + if (cd) + fullName = cd->name(); + else if (nd) + fullName = nd->name(); + + if (!fullName.isEmpty()) fullName+="::"; + fullName+=md->name(); + FileDef *rfd=rootNav->fileDef(); + + // TODO determine scope based on root not md + Definition *rscope = md->getOuterScope(); + + LockingPtr<ArgumentList> mdAl = md->argumentList(); + if (al) + { + //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty()); + mergeArguments(mdAl.pointer(),al,!root->doc.isEmpty()); + } + else + { + if ( + matchArguments2( md->getOuterScope(), md->getFileDef(), mdAl.pointer(), + rscope,rfd,root->argList, + TRUE + ) + ) + { + //printf("merging arguments (2)\n"); + mergeArguments(mdAl.pointer(),root->argList,!root->doc.isEmpty()); + } + } + if (over_load) // the \overload keyword was used + { + QCString doc=getOverloadDocs(); + if (!root->doc.isEmpty()) + { + doc+="<p>"; + doc+=root->doc; + } + md->setDocumentation(doc,root->docFile,root->docLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setDocsForDefinition(!root->proto); + } + else + { + //printf("overwrite!\n"); + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setDocsForDefinition(!root->proto); + + //printf("overwrite!\n"); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + + if ( + (md->inbodyDocumentation().isEmpty() || + !rootNav->parent()->name().isEmpty() + ) && !root->inbodyDocs.isEmpty() + ) + { + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + } + } + + //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n", + // md->initializer().data(),md->initializer().isEmpty(), + // root->initializer.data(),root->initializer.isEmpty() + // ); + if (md->initializer().isEmpty() && !root->initializer.isEmpty()) + { + //printf("setInitializer\n"); + md->setInitializer(root->initializer); + } + + md->setMaxInitLines(root->initLines); + + if (rfd) + { + if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1) + ) + { + //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine); + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setBodyDef(rfd); + } + + md->setRefItems(root->sli); + } + + md->enableCallGraph(md->hasCallGraph() || root->callGraph); + md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph); + + md->mergeMemberSpecifiers(root->spec); + md->addSectionsToDefinition(root->anchors); + addMemberToGroups(root,md); + if (cd) cd->insertUsedFile(root->fileName); + //printf("root->mGrpId=%d\n",root->mGrpId); + if (root->mGrpId!=-1) + { + if (md->getMemberGroupId()!=-1) + { + if (md->getMemberGroupId()!=root->mGrpId) + { + warn( + root->fileName,root->startLine, + "warning: member %s belongs to two different groups. The second " + "one found here will be ignored.", + md->name().data() + ); + } + } + else // set group id + { + //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,md->name().data()); + md->setMemberGroupId(root->mGrpId); + } + } +} + +//---------------------------------------------------------------------- +// find a class definition given the scope name and (optionally) a +// template list specifier + +static ClassDef *findClassDefinition(FileDef *fd,NamespaceDef *nd, + const char *scopeName) +{ + ClassDef *tcd = getResolvedClass(nd,fd,scopeName,0,0,TRUE,TRUE); + return tcd; +} + + +//---------------------------------------------------------------------- +// Adds the documentation contained in `root' to a global function +// with name `name' and argument list `args' (for overloading) and +// function declaration `decl' to the corresponding member definition. + +static bool findGlobalMember(EntryNav *rootNav, + const QCString &namespaceName, + const char *name, + const char *tempArg, + const char *, + const char *decl) +{ + Entry *root = rootNav->entry(); + Debug::print(Debug::FindMembers,0, + "2. findGlobalMember(namespace=%s,name=%s,tempArg=%s,decl=%s)\n", + namespaceName.data(),name,tempArg,decl); + QCString n=name; + if (n.isEmpty()) return FALSE; + if (n.find("::")!=-1) return FALSE; // skip undefined class members + MemberName *mn=Doxygen::functionNameSDict->find(n+tempArg); // look in function dictionary + if (mn==0) + { + mn=Doxygen::functionNameSDict->find(n); // try without template arguments + } + if (mn) // function name defined + { + Debug::print(Debug::FindMembers,0,"3. Found function scope\n"); + //int count=0; + MemberNameIterator mni(*mn); + MemberDef *md; + bool found=FALSE; + for (mni.toFirst();(md=mni.current()) && !found;++mni) + { + NamespaceDef *nd=md->getNamespaceDef(); + + //printf("Namespace namespaceName=%s nd=%s\n", + // namespaceName.data(),nd ? nd->name().data() : "<none>"); + + FileDef *fd=rootNav->fileDef(); + //printf("File %s\n",fd ? fd->name().data() : "<none>"); + NamespaceSDict *nl = fd ? fd->getUsedNamespaces() : 0; + //SDict<Definition> *cl = fd ? fd->getUsedClasses() : 0; + //printf("NamespaceList %p\n",nl); + + // search in the list of namespaces that are imported via a + // using declaration + bool viaUsingDirective = nl && nd && nl->find(nd->qualifiedName())!=0; + + if ((namespaceName.isEmpty() && nd==0) || // not in a namespace + (nd && nd->name()==namespaceName) || // or in the same namespace + viaUsingDirective // member in `using' namespace + ) + { + Debug::print(Debug::FindMembers,0,"4. Try to add member `%s' to scope `%s'\n", + md->name().data(),namespaceName.data()); + QCString nsName = nd ? nd->name().data() : ""; + + NamespaceDef *rnd = 0; + if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceSDict->find(namespaceName); + + LockingPtr<ArgumentList> mdAl = md->argumentList(); + bool matching= + (mdAl==0 && root->argList->count()==0) || + md->isVariable() || md->isTypedef() || /* in case of function pointers */ + matchArguments2(md->getOuterScope(),md->getFileDef(),mdAl.pointer(), + rnd ? rnd : Doxygen::globalScope,fd,root->argList, + FALSE); + + // for template members we need to check if the number of + // template arguments is the same, otherwise we are dealing with + // different functions. + if (matching && root->tArgLists) + { + LockingPtr<ArgumentList> mdTempl = md->templateArguments(); + if (mdTempl!=0) + { + if (root->tArgLists->getLast()->count()!=mdTempl->count()) + { + matching=FALSE; + } + } + } + + + //printf("%s<->%s\n", + // argListToString(md->argumentList()).data(), + // argListToString(root->argList).data()); + + // for static members we also check if the comment block was found in + // the same file. This is needed because static members with the same + // name can be in different files. Thus it would be wrong to just + // put the comment block at the first syntactically matching member. + if (matching && md->isStatic() && + md->getDefFileName()!=root->fileName && + mn->count()>1) + { + matching = FALSE; + } + + if (matching) // add docs to the member + { + Debug::print(Debug::FindMembers,0,"5. Match found\n"); + addMemberDocs(rootNav,md,decl,root->argList,FALSE); + found=TRUE; + } + } + } + if (!found && root->relatesType != Duplicate) // no match + { + QCString fullFuncDecl=decl; + if (root->argList) fullFuncDecl+=argListToString(root->argList,TRUE); + QCString warnMsg = + QCString("warning: no matching file member found for \n")+fullFuncDecl; + if (mn->count()>0) + { + warnMsg+="Possible candidates:\n"; + for (mni.toFirst();(md=mni.current());++mni) + { + warnMsg+=" "; + warnMsg+=md->declaration(); + warnMsg+='\n'; + } + } + warn(root->fileName,root->startLine,warnMsg); + } + } + else // got docs for an undefined member! + { + if (root->type!="friend class" && + root->type!="friend struct" && + root->type!="friend union" && + (!Config_getBool("TYPEDEF_HIDES_STRUCT") || + root->type.find("typedef ")==-1) + ) + { + warn(root->fileName,root->startLine, + "warning: documented function `%s' was not declared or defined.",decl + ); + } + } + return TRUE; +} + +static bool isSpecialization( + const QList<ArgumentList> &srcTempArgLists, + const QList<ArgumentList> &dstTempArgLists + ) +{ + QListIterator<ArgumentList> srclali(srcTempArgLists); + QListIterator<ArgumentList> dstlali(dstTempArgLists); + for (;srclali.current();++srclali,++dstlali) + { + ArgumentList *sal = srclali.current(); + ArgumentList *dal = dstlali.current(); + if (!(sal && dal && sal->count()==dal->count())) return TRUE; + } + return FALSE; +} + + +static QCString substituteTemplatesInString( + const QList<ArgumentList> &srcTempArgLists, + const QList<ArgumentList> &dstTempArgLists, + ArgumentList *funcTempArgList, // can be used to match template specializations + const QCString &src + ) +{ + QCString dst; + QRegExp re( "[A-Za-z_][A-Za-z_0-9]*"); + //printf("type=%s\n",sa->type.data()); + int i,p=0,l; + while ((i=re.match(src,p,&l))!=-1) // for each word in srcType + { + bool found=FALSE; + dst+=src.mid(p,i-p); + QCString name=src.mid(i,l); + + QListIterator<ArgumentList> srclali(srcTempArgLists); + QListIterator<ArgumentList> dstlali(dstTempArgLists); + for (;srclali.current() && !found;++srclali,++dstlali) + { + ArgumentListIterator tsali(*srclali.current()); + ArgumentListIterator tdali(*dstlali.current()); + Argument *tsa =0,*tda=0, *fa=0; + if (funcTempArgList) + { + fa=funcTempArgList->first(); + } + + for (tsali.toFirst();(tsa=tsali.current()) && !found;++tsali) + { + tda = tdali.current(); + //if (tda) printf("tsa=%s|%s tda=%s|%s\n", + // tsa->type.data(),tsa->name.data(), + // tda->type.data(),tda->name.data()); + if (name==tsa->name) + { + if (tda && tda->name.isEmpty()) + { + int vc=0; + if (tda->type.left(6)=="class ") vc=6; + else if (tda->type.left(9)=="typename ") vc=9; + if (vc>0) // convert type=="class T" to type=="class" name=="T" + { + tda->name = tda->type.mid(vc); + tda->type = tda->type.left(vc-1); + } + } + if (tda && !tda->name.isEmpty()) + { + name=tda->name; // substitute + found=TRUE; + } + else if (fa) + { + name=fa->type; + found=TRUE; + } + } + if (tda) + ++tdali; + else if (fa) + fa=funcTempArgList->next(); + } + //printf(" srcList='%s' dstList='%s faList='%s'\n", + // argListToString(srclali.current()).data(), + // argListToString(dstlali.current()).data(), + // funcTempArgList ? argListToString(funcTempArgList).data() : "<none>"); + } + dst+=name; + p=i+l; + } + dst+=src.right(src.length()-p); + //printf(" substituteTemplatesInString(%s)=%s\n", + // src.data(),dst.data()); + return dst; +} + +static void substituteTemplatesInArgList( + const QList<ArgumentList> &srcTempArgLists, + const QList<ArgumentList> &dstTempArgLists, + ArgumentList *src, + ArgumentList *dst, + ArgumentList *funcTempArgs = 0 + ) +{ + ArgumentListIterator sali(*src); + Argument *sa=0; + Argument *da=dst->first(); + + for (sali.toFirst();(sa=sali.current());++sali) // for each member argument + { + QCString dstType = substituteTemplatesInString( + srcTempArgLists,dstTempArgLists,funcTempArgs, + sa->type); + QCString dstArray = substituteTemplatesInString( + srcTempArgLists,dstTempArgLists,funcTempArgs, + sa->array); + if (da==0) + { + da=new Argument(*sa); + dst->append(da); + da->type=dstType; + da->array=dstArray; + da=0; + } + else + { + da->type=dstType; + da->type=dstArray; + da=dst->next(); + } + } + dst->constSpecifier = src->constSpecifier; + dst->volatileSpecifier = src->volatileSpecifier; + dst->pureSpecifier = src->pureSpecifier; + //printf("substituteTemplatesInArgList: replacing %s with %s\n", + // argListToString(src).data(),argListToString(dst).data() + // ); +} + + + +/*! This function tries to find a member (in a documented class/file/namespace) + * that corresponds to the function/variable declaration given in \a funcDecl. + * + * The boolean \a overloaded is used to specify whether or not a standard + * overload documentation line should be generated. + * + * The boolean \a isFunc is a hint that indicates that this is a function + * instead of a variable or typedef. + */ +static void findMember(EntryNav *rootNav, + QCString funcDecl, + bool overloaded, + bool isFunc + ) +{ + Entry *root = rootNav->entry(); + + Debug::print(Debug::FindMembers,0, + "findMember(root=%p,funcDecl=`%s',related=`%s',overload=%d," + "isFunc=%d mGrpId=%d tArgList=%p (#=%d) " + "spec=%d lang=%x\n", + root,funcDecl.data(),root->relates.data(),overloaded,isFunc,root->mGrpId, + root->tArgLists,root->tArgLists ? root->tArgLists->count() : 0, + root->spec,root->lang + ); + + QCString scopeName; + QCString className; + QCString namespaceName; + QCString funcType; + QCString funcName; + QCString funcArgs; + QCString funcTempList; + QCString exceptions; + QCString funcSpec; + bool isRelated=FALSE; + bool isMemberOf=FALSE; + bool isFriend=FALSE; + bool done; + do + { + done=TRUE; + if (funcDecl.stripPrefix("friend ")) // treat friends as related members + { + isFriend=TRUE; + done=FALSE; + } + if (funcDecl.stripPrefix("inline ")) + { + root->spec|=Entry::Inline; + done=FALSE; + } + if (funcDecl.stripPrefix("explicit ")) + { + root->spec|=Entry::Explicit; + done=FALSE; + } + if (funcDecl.stripPrefix("mutable ")) + { + root->spec|=Entry::Mutable; + done=FALSE; + } + if (funcDecl.stripPrefix("virtual ")) + { + done=FALSE; + } + } while (!done); + + // delete any ; from the function declaration + int sep; + while ((sep=funcDecl.find(';'))!=-1) + { + funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace(); + } + + // make sure the first character is a space to simplify searching. + if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" "); + + // remove some superfluous spaces + funcDecl= substitute( + substitute( + substitute(funcDecl,"~ ","~"), + ":: ","::" + ), + " ::","::" + ).stripWhiteSpace(); + + //printf("funcDecl=`%s'\n",funcDecl.data()); + if (isFriend && funcDecl.left(6)=="class ") + { + //printf("friend class\n"); + funcDecl=funcDecl.right(funcDecl.length()-6); + funcName = funcDecl.copy(); + } + else if (isFriend && funcDecl.left(7)=="struct ") + { + funcDecl=funcDecl.right(funcDecl.length()-7); + funcName = funcDecl.copy(); + } + else + { + // extract information from the declarations + parseFuncDecl(funcDecl,root->lang==SrcLangExt_ObjC,scopeName,funcType,funcName, + funcArgs,funcTempList,exceptions + ); + } + //printf("scopeName=`%s' funcType=`%s' funcName=`%s' funcArgs=`%s'\n", + // scopeName.data(),funcType.data(),funcName.data(),funcArgs.data()); + + // the class name can also be a namespace name, we decide this later. + // if a related class name is specified and the class name could + // not be derived from the function declaration, then use the + // related field. + //printf("scopeName=`%s' className=`%s' namespaceName=`%s'\n", + // scopeName.data(),className.data(),namespaceName.data()); + if (!root->relates.isEmpty()) + { // related member, prefix user specified scope + isRelated=TRUE; + isMemberOf=(root->relatesType == MemberOf); + if (getClass(root->relates)==0 && !scopeName.isEmpty()) + { + scopeName= mergeScopes(scopeName,root->relates); + } + else + { + scopeName = root->relates; + } + } + + if (root->relates.isEmpty() && rootNav->parent() && + ((rootNav->parent()->section()&Entry::SCOPE_MASK) || + (rootNav->parent()->section()==Entry::OBJCIMPL_SEC) + ) && + !rootNav->parent()->name().isEmpty()) // see if we can combine scopeName + // with the scope in which it was found + { + QCString joinedName = rootNav->parent()->name()+"::"+scopeName; + if (!scopeName.isEmpty() && + (getClass(joinedName) || Doxygen::namespaceSDict->find(joinedName))) + { + scopeName = joinedName; + } + else + { + scopeName = mergeScopes(rootNav->parent()->name(),scopeName); + } + } + else // see if we can prefix a namespace or class that is used from the file + { + FileDef *fd=rootNav->fileDef(); + if (fd) + { + NamespaceSDict *fnl = fd->getUsedNamespaces(); + if (fnl) + { + QCString joinedName; + NamespaceDef *fnd; + NamespaceSDict::Iterator nsdi(*fnl); + for (nsdi.toFirst();(fnd=nsdi.current());++nsdi) + { + joinedName = fnd->name()+"::"+scopeName; + if (Doxygen::namespaceSDict->find(joinedName)) + { + scopeName=joinedName; + break; + } + } + } + } + } + scopeName=stripTemplateSpecifiersFromScope( + removeRedundantWhiteSpace(scopeName),FALSE,&funcSpec); + + // funcSpec contains the last template specifiers of the given scope. + // If this method does not have any template arguments or they are + // empty while funcSpec is not empty we assume this is a + // specialization of a method. If not, we clear the funcSpec and treat + // this as a normal method of a template class. + if (!(root->tArgLists && + root->tArgLists->count()>0 && + root->tArgLists->first()->count()==0 + ) + ) + { + funcSpec.resize(0); + } + + // split scope into a namespace and a class part + extractNamespaceName(scopeName,className,namespaceName,TRUE); + //printf("scopeName=`%s' className=`%s' namespaceName=`%s'\n", + // scopeName.data(),className.data(),namespaceName.data()); + + //namespaceName=removeAnonymousScopes(namespaceName); + if (namespaceName.find('@')!=-1) return; // skip stuff in anonymous namespace... + + //printf("namespaceName=`%s' className=`%s'\n",namespaceName.data(),className.data()); + // merge class and namespace scopes again + scopeName.resize(0); + if (!namespaceName.isEmpty()) + { + if (className.isEmpty()) + { + scopeName=namespaceName; + } + else if (!root->relates.isEmpty() || // relates command with explicit scope + !getClass(className)) // class name only exists in a namespace + { + scopeName=namespaceName+"::"+className; + } + else + { + scopeName=className; + } + } + else if (!className.isEmpty()) + { + scopeName=className; + } + //printf("new scope=`%s'\n",scopeName.data()); + + QCString tempScopeName=scopeName; + ClassDef *cd=getClass(scopeName); + if (cd) + { + if (root->tArgLists) root->tArgLists->first(); + if (funcSpec.isEmpty()) + { + tempScopeName=cd->qualifiedNameWithTemplateParameters(root->tArgLists); + } + else + { + tempScopeName=scopeName+funcSpec; + } + } + //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n", + // scopeName.data(),cd,root->tArgLists,tempScopeName.data()); + + //printf("scopeName=`%s' className=`%s'\n",scopeName.data(),className.data()); + // rebuild the function declaration (needed to get the scope right). + if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool("HIDE_SCOPE_NAMES")) + { + if (!funcType.isEmpty()) + { + if (isFunc) // a function -> we use argList for the arguments + { + funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList; + } + else + { + funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs; + } + } + else + { + if (isFunc) // a function => we use argList for the arguments + { + funcDecl=tempScopeName+"::"+funcName+funcTempList; + } + else // variable => add `argument' list + { + funcDecl=tempScopeName+"::"+funcName+funcArgs; + } + } + } + else // build declaration without scope + { + if (!funcType.isEmpty()) // but with a type + { + if (isFunc) // function => omit argument list + { + funcDecl=funcType+" "+funcName+funcTempList; + } + else // variable => add `argument' list + { + funcDecl=funcType+" "+funcName+funcArgs; + } + } + else // no type + { + if (isFunc) + { + funcDecl=funcName+funcTempList; + } + else + { + funcDecl=funcName+funcArgs; + } + } + } + + if (funcType=="template class" && !funcTempList.isEmpty()) + return; // ignore explicit template instantiations + + Debug::print(Debug::FindMembers,0, + "findMember() Parse results:\n" + " namespaceName=`%s'\n" + " className=`%s`\n" + " funcType=`%s'\n" + " funcSpec=`%s'\n" + " funcName=`%s'\n" + " funcArgs=`%s'\n" + " funcTempList=`%s'\n" + " funcDecl=`%s'\n" + " related=`%s'\n" + " exceptions=`%s'\n" + " isRelated=%d\n" + " isMemberOf=%d\n" + " isFriend=%d\n" + " isFunc=%d\n\n", + namespaceName.data(),className.data(), + funcType.data(),funcSpec.data(),funcName.data(),funcArgs.data(),funcTempList.data(), + funcDecl.data(),root->relates.data(),exceptions.data(),isRelated,isMemberOf,isFriend, + isFunc + ); + + MemberName *mn=0; + if (!funcName.isEmpty()) // function name is valid + { + Debug::print(Debug::FindMembers,0, + "1. funcName=`%s'\n",funcName.data()); + if (funcName.left(9)=="operator ") // strip class scope from cast operator + { + funcName = substitute(funcName,className+"::",""); + } + if (!funcTempList.isEmpty()) // try with member specialization + { + mn=Doxygen::memberNameSDict->find(funcName+funcTempList); + } + if (mn==0) // try without specialization + { + mn=Doxygen::memberNameSDict->find(funcName); + } + if (!isRelated && mn) // function name already found + { + Debug::print(Debug::FindMembers,0, + "2. member name exists (%d members with this name)\n",mn->count()); + if (!className.isEmpty()) // class name is valid + { + if (funcSpec.isEmpty()) // not a member specialization + { + int count=0; + int noMatchCount=0; + MemberNameIterator mni(*mn); + MemberDef *md; + bool memFound=FALSE; + for (mni.toFirst();!memFound && (md=mni.current());++mni) + { + ClassDef *cd=md->getClassDef(); + Debug::print(Debug::FindMembers,0, + "3. member definition found, " + "scope needed=`%s' scope=`%s' args=`%s' fileName=%s\n", + scopeName.data(),cd ? cd->name().data() : "<none>", + md->argsString(), + root->fileName.data()); + //printf("Member %s (member scopeName=%s) (this scopeName=%s) classTempList=%s\n",md->name().data(),cd->name().data(),scopeName.data(),classTempList.data()); + FileDef *fd=rootNav->fileDef(); + NamespaceDef *nd=0; + if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName); + + //printf("scopeName %s->%s\n",scopeName.data(), + // stripTemplateSpecifiersFromScope(scopeName,FALSE).data()); + + ClassDef *tcd=findClassDefinition(fd,nd,scopeName); + if (tcd==0 && stripAnonymousNamespaceScope(cd->name())==scopeName) + { + // don't be fooled by anonymous scopes + tcd=cd; + } + //printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n", + // scopeName.data(),nd?nd->name().data():"<none>",tcd,tcd?tcd->name().data():"",cd); + + if (cd && tcd==cd) // member's classes match + { + Debug::print(Debug::FindMembers,0, + "4. class definition %s found\n",cd->name().data()); + + // get the template parameter lists found at the member declaration + QList<ArgumentList> declTemplArgs; + cd->getTemplateParameterLists(declTemplArgs); + LockingPtr<ArgumentList> templAl = md->templateArguments(); + if (templAl!=0) + { + declTemplArgs.append(templAl.pointer()); + } + + // get the template parameter lists found at the member definition + QList<ArgumentList> *defTemplArgs = root->tArgLists; + //printf("defTemplArgs=%p\n",defTemplArgs); + + // do we replace the decl argument lists with the def argument lists? + bool substDone=FALSE; + ArgumentList *argList=0; + + /* substitute the occurrences of class template names in the + * argument list before matching + */ + LockingPtr<ArgumentList> mdAl = md->argumentList(); + if (declTemplArgs.count()>0 && defTemplArgs && + declTemplArgs.count()==defTemplArgs->count() && + mdAl.pointer() + ) + { + /* the function definition has template arguments + * and the class definition also has template arguments, so + * we must substitute the template names of the class by that + * of the function definition before matching. + */ + argList = new ArgumentList; + substituteTemplatesInArgList(declTemplArgs,*defTemplArgs, + mdAl.pointer(),argList); + + substDone=TRUE; + } + else /* no template arguments, compare argument lists directly */ + { + argList = mdAl.pointer(); + } + + Debug::print(Debug::FindMembers,0, + "5. matching `%s'<=>`%s' className=%s namespaceName=%s\n", + argListToString(argList,TRUE).data(),argListToString(root->argList,TRUE).data(), + className.data(),namespaceName.data() + ); + + bool matching= + md->isVariable() || md->isTypedef() || // needed for function pointers + (mdAl.pointer()==0 && root->argList->count()==0) || + matchArguments2( + md->getClassDef(),md->getFileDef(),argList, + cd,fd,root->argList, + TRUE); + + Debug::print(Debug::FindMembers,0, + "6. match results of matchArguments2 = %d\n",matching); + + if (substDone) // found a new argument list + { + if (matching) // replace member's argument list + { + md->setDefinitionTemplateParameterLists(root->tArgLists); + md->setArgumentList(argList); // new owner of the list => no delete + } + else // no match + { + if (!funcTempList.isEmpty() && + isSpecialization(declTemplArgs,*defTemplArgs)) + { + // check if we are dealing with a partial template + // specialization. In this case we add it to the class + // even though the member arguments do not match. + + // TODO: copy other aspects? + root->protection=md->protection(); // copy protection level + addMethodToClass(rootNav,cd,md->name(),isFriend); + return; + } + delete argList; + } + } + if (matching) + { + addMemberDocs(rootNav,md,funcDecl,0,overloaded,0/* TODO */); + count++; + memFound=TRUE; + } + } + else if (cd && cd!=tcd) // we did find a class with the same name as cd + // but in a different namespace + { + noMatchCount++; + } + } + if (count==0 && rootNav->parent() && + rootNav->parent()->section()==Entry::OBJCIMPL_SEC) + { + goto localObjCMethod; + } + if (count==0 && !(isFriend && funcType=="class")) + { + int candidates=0; + ClassDef *ccd = 0, *ecd = 0; + MemberDef *cmd = 0, *emd = 0; + if (mn->count()>0) + { + //printf("Assume template class\n"); + for (mni.toFirst();(md=mni.current());++mni) + { + ccd=md->getClassDef(); + cmd=md; + //printf("ccd->name()==%s className=%s\n",ccd->name().data(),className.data()); + if (ccd!=0 && rightScopeMatch(ccd->name(),className)) + { + LockingPtr<ArgumentList> templAl = md->templateArguments(); + if (root->tArgLists && templAl!=0 && + root->tArgLists->getLast()->count()<=templAl->count()) + { + addMethodToClass(rootNav,ccd,md->name(),isFriend); + return; + } + if (md->argsString()==argListToString(root->argList,TRUE,FALSE)) + { // exact argument list match -> remember + ecd = ccd; + emd = cmd; + } + candidates++; + } + } + } + static bool strictProtoMatching = Config_getBool("STRICT_PROTO_MATCHING"); + if (!strictProtoMatching) + { + if (candidates==1 && ccd && cmd) + { + // we didn't find an actual match on argument lists, but there is only 1 member with this + // name in the same scope, so that has to be the one. + addMemberDocs(rootNav,cmd,funcDecl,0,overloaded,0); + return; + } + else if (candidates>1 && ecd && emd) + { + // we didn't find a unique match using type resolution, + // but one of the matches has the exact same signature so + // we take that one. + addMemberDocs(rootNav,emd,funcDecl,0,overloaded,0); + return; + } + } + + QCString warnMsg = "warning: no "; + if (noMatchCount>1) warnMsg+="uniquely "; + warnMsg+="matching class member found for \n"; + + if (root->tArgLists) + { + QListIterator<ArgumentList> alli(*root->tArgLists); + ArgumentList *al; + for (;(al=alli.current());++alli) + { + warnMsg+=" template "; + warnMsg+=tempArgListToString(al); + warnMsg+='\n'; + } + } + QCString fullFuncDecl=funcDecl.copy(); + if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE); + + warnMsg+=" "; + warnMsg+=fullFuncDecl; + warnMsg+='\n'; + + if (candidates>0) + { + warnMsg+="Possible candidates:\n"; + for (mni.toFirst();(md=mni.current());++mni) + { + ClassDef *cd=md->getClassDef(); + if (cd!=0 && rightScopeMatch(cd->name(),className)) + { + LockingPtr<ArgumentList> templAl = md->templateArguments(); + if (templAl!=0) + { + warnMsg+=" template "; + warnMsg+=tempArgListToString(templAl.pointer()); + warnMsg+='\n'; + } + warnMsg+=" "; + if (md->typeString()) + { + warnMsg+=md->typeString(); + warnMsg+=' '; + } + QCString qScope = cd->qualifiedNameWithTemplateParameters(); + if (!qScope.isEmpty()) + warnMsg+=qScope+"::"+md->name(); + if (md->argsString()) + warnMsg+=md->argsString(); + if (noMatchCount>1) + { + QCString lineFile; + lineFile.sprintf(" at line %d of file ",md->getDefLine()); + warnMsg+=lineFile+md->getDefFileName(); + } + + warnMsg+='\n'; + } + } + } + warn_simple(root->fileName,root->startLine,warnMsg); + } + } + else if (cd) // member specialization + { + MemberNameIterator mni(*mn); + MemberDef *declMd=0; + MemberDef *md=0; + for (mni.toFirst();(md=mni.current());++mni) + { + if (md->getClassDef()==cd) + { + // TODO: we should probably also check for matching arguments + declMd = md; + break; + } + } + MemberDef::MemberType mtype=MemberDef::Function; + ArgumentList *tArgList = new ArgumentList; + // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists); + md=new MemberDef( + root->fileName,root->startLine, + funcType,funcName,funcArgs,exceptions, + declMd ? declMd->protection() : root->protection, + root->virt,root->stat,Member, + mtype,tArgList,root->argList); + //printf("new specialized member %s args=`%s'\n",md->name().data(),funcArgs.data()); + md->setTagInfo(rootNav->tagInfo()); + md->setMemberClass(cd); + md->setTemplateSpecialization(TRUE); + md->setTypeConstraints(root->typeConstr); + md->setDefinition(funcDecl); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setDocsForDefinition(!root->proto); + md->setPrototype(root->proto); + md->addSectionsToDefinition(root->anchors); + md->setBodySegment(root->bodyLine,root->endBodyLine); + FileDef *fd=rootNav->fileDef(); + md->setBodyDef(fd); + md->setMemberSpecifiers(root->spec); + md->setMemberGroupId(root->mGrpId); + mn->append(md); + cd->insertMember(md); + md->setRefItems(root->sli); + delete tArgList; + } + else + { + //printf("*** Specialized member %s of unknown scope %s%s found!\n", + // scopeName.data(),funcName.data(),funcArgs.data()); + } + } + else if (overloaded) // check if the function belongs to only one class + { + // for unique overloaded member we allow the class to be + // omitted, this is to be Qt compatable. Using this should + // however be avoided, because it is error prone + MemberNameIterator mni(*mn); + MemberDef *md=mni.toFirst(); + ASSERT(md); + ClassDef *cd=md->getClassDef(); + ASSERT(cd); + QCString className=cd->name().copy(); + ++mni; + bool unique=TRUE; + for (;(md=mni.current());++mni) + { + ClassDef *cd=md->getClassDef(); + if (className!=cd->name()) unique=FALSE; + } + if (unique) + { + MemberDef::MemberType mtype; + if (root->mtype==Signal) mtype=MemberDef::Signal; + else if (root->mtype==Slot) mtype=MemberDef::Slot; + else if (root->mtype==DCOP) mtype=MemberDef::DCOP; + else mtype=MemberDef::Function; + + // new overloaded member function + ArgumentList *tArgList = + getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists); + //printf("new related member %s args=`%s'\n",md->name().data(),funcArgs.data()); + MemberDef *md=new MemberDef( + root->fileName,root->startLine, + funcType,funcName,funcArgs,exceptions, + root->protection,root->virt,root->stat,Related, + mtype,tArgList,root->argList); + md->setTagInfo(rootNav->tagInfo()); + md->setTypeConstraints(root->typeConstr); + md->setMemberClass(cd); + md->setDefinition(funcDecl); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + QCString doc=getOverloadDocs(); + doc+="<p>"; + doc+=root->doc; + md->setDocumentation(doc,root->docFile,root->docLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setDocsForDefinition(!root->proto); + md->setPrototype(root->proto); + md->addSectionsToDefinition(root->anchors); + md->setBodySegment(root->bodyLine,root->endBodyLine); + FileDef *fd=rootNav->fileDef(); + md->setBodyDef(fd); + md->setMemberSpecifiers(root->spec); + md->setMemberGroupId(root->mGrpId); + mn->append(md); + cd->insertMember(md); + cd->insertUsedFile(root->fileName); + md->setRefItems(root->sli); + } + } + else // unrelated function with the same name as a member + { + if (!findGlobalMember(rootNav,namespaceName,funcName,funcTempList,funcArgs,funcDecl)) + { + QCString fullFuncDecl=funcDecl.copy(); + if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE); + warn(root->fileName,root->startLine, + "warning: Cannot determine class for function\n%s", + fullFuncDecl.data() + ); + } + } + } + else if (isRelated && !root->relates.isEmpty()) + { + Debug::print(Debug::FindMembers,0,"2. related function\n" + " scopeName=%s className=%s\n",scopeName.data(),className.data()); + if (className.isEmpty()) className=root->relates; + ClassDef *cd; + //printf("scopeName=`%s' className=`%s'\n",scopeName.data(),className.data()); + if ((cd=getClass(scopeName))) + { + bool newMember=TRUE; // assume we have a new member + bool newMemberName=FALSE; + bool isDefine=FALSE; + { + MemberName *mn = Doxygen::functionNameSDict->find(funcName); + if (mn) + { + MemberDef *md = mn->first(); + while (md && !isDefine) + { + isDefine = isDefine || md->isDefine(); + md = mn->next(); + } + } + } + + FileDef *fd=rootNav->fileDef(); + + if ((mn=Doxygen::memberNameSDict->find(funcName))==0) + { + mn=new MemberName(funcName); + newMemberName=TRUE; // we create a new member name + } + else + { + MemberDef *rmd=mn->first(); + while (rmd && newMember) // see if we got another member with matching arguments + { + LockingPtr<ArgumentList> rmdAl = rmd->argumentList(); + + newMember= + !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmdAl.pointer(), + cd,fd,root->argList, + TRUE); + if (newMember) rmd=mn->next(); + } + if (!newMember && rmd) // member already exists as rmd -> add docs + { + //printf("addMemberDocs for related member %s\n",root->name.data()); + //rmd->setMemberDefTemplateArguments(root->mtArgList); + addMemberDocs(rootNav,rmd,funcDecl,0,overloaded); + } + } + + if (newMember) // need to create a new member + { + MemberDef::MemberType mtype; + if (isDefine) + mtype=MemberDef::Define; + else if (root->mtype==Signal) + mtype=MemberDef::Signal; + else if (root->mtype==Slot) + mtype=MemberDef::Slot; + else if (root->mtype==DCOP) + mtype=MemberDef::DCOP; + else + mtype=MemberDef::Function; + + //printf("New related name `%s' `%d'\n",funcName.data(), + // root->argList ? (int)root->argList->count() : -1); + + // new related (member) function +#if 0 // removed as it doesn't handle related template functions correctly + ArgumentList *tArgList = + getTemplateArgumentsFromName(scopeName+"::"+funcName,root->tArgLists); + MemberDef *md=new MemberDef( + root->fileName,root->startLine, + funcType,funcName,funcArgs,exceptions, + root->protection,root->virt,root->stat,TRUE, + mtype,tArgList,funcArgs.isEmpty() ? 0 : root->argList); +#endif + // first note that we pass: + // (root->tArgLists ? root->tArgLists->last() : 0) + // for the template arguments fo the new "member." + // this accurately reflects the template arguments of + // the related function, which don't have to do with + // those of the related class. + MemberDef *md=new MemberDef( + root->fileName,root->startLine, + funcType,funcName,funcArgs,exceptions, + root->protection,root->virt, + root->stat && !isMemberOf, + isMemberOf ? Foreign : isRelated ? Related : Member, + mtype, + (root->tArgLists ? root->tArgLists->last() : 0), + funcArgs.isEmpty() ? 0 : root->argList); + // + // we still have the problem that + // MemberDef::writeDocumentation() in memberdef.cpp + // writes the template argument list for the class, + // as if this member is a member of the class. + // fortunately, MemberDef::writeDocumentation() has + // a special mechanism that allows us to totally + // override the set of template argument lists that + // are printed. We use that and set it to the + // template argument lists of the related function. + // + md->setDefinitionTemplateParameterLists(root->tArgLists); + + md->setTagInfo(rootNav->tagInfo()); + + + + //printf("Related member name=`%s' decl=`%s' bodyLine=`%d'\n", + // funcName.data(),funcDecl.data(),root->bodyLine); + + // try to find the matching line number of the body from the + // global function list + bool found=FALSE; + if (root->bodyLine==-1) + { + MemberName *rmn=Doxygen::functionNameSDict->find(funcName); + if (rmn) + { + MemberDef *rmd=rmn->first(); + while (rmd && !found) // see if we got another member with matching arguments + { + LockingPtr<ArgumentList> rmdAl = rmd->argumentList(); + // check for matching argument lists + if ( + matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmdAl.pointer(), + cd,fd,root->argList, + TRUE) + ) + { + found=TRUE; + } + if (!found) rmd=rmn->next(); + } + if (rmd) // member found -> copy line number info + { + md->setBodySegment(rmd->getStartBodyLine(),rmd->getEndBodyLine()); + md->setBodyDef(rmd->getBodyDef()); + //md->setBodyMember(rmd); + } + } + } + if (!found) // line number could not be found or is available in this + // entry + { + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setBodyDef(fd); + } + + //if (root->mGrpId!=-1) + //{ + // md->setMemberGroup(memberGroupDict[root->mGrpId]); + //} + md->setMemberClass(cd); + md->setMemberSpecifiers(root->spec); + md->setDefinition(funcDecl); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setDocsForDefinition(!root->proto); + md->setPrototype(root->proto); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + md->addSectionsToDefinition(root->anchors); + md->setMemberGroupId(root->mGrpId); + //md->setMemberDefTemplateArguments(root->mtArgList); + mn->append(md); + cd->insertMember(md); + cd->insertUsedFile(root->fileName); + md->setRefItems(root->sli); + if (root->relatesType == Duplicate) md->setRelatedAlso(cd); + addMemberToGroups(root,md); + //printf("Adding member=%s\n",md->name().data()); + if (newMemberName) + { + //Doxygen::memberNameList.append(mn); + //Doxygen::memberNameDict.insert(funcName,mn); + Doxygen::memberNameSDict->append(funcName,mn); + } + } + if (root->relatesType == Duplicate) + { + if (!findGlobalMember(rootNav,namespaceName,funcName,funcTempList,funcArgs,funcDecl)) + { + QCString fullFuncDecl=funcDecl.copy(); + if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE); + warn(root->fileName,root->startLine, + "warning: Cannot determine file/namespace for relatedalso function\n%s", + fullFuncDecl.data() + ); + } + } + } + else + { + warn_undoc(root->fileName,root->startLine, + "warning: class `%s' for related function `%s' is not " + "documented.", + className.data(),funcName.data() + ); + } + } + else if (rootNav->parent() && rootNav->parent()->section()==Entry::OBJCIMPL_SEC) + { +localObjCMethod: + ClassDef *cd; + //printf("scopeName=`%s' className=`%s'\n",scopeName.data(),className.data()); + if (Config_getBool("EXTRACT_LOCAL_METHODS") && (cd=getClass(scopeName))) + { + //printf("Local objective C method `%s' of class `%s' found\n",root->name.data(),cd->name().data()); + MemberDef *md=new MemberDef( + root->fileName,root->startLine, + funcType,funcName,funcArgs,exceptions, + root->protection,root->virt,root->stat,Member, + MemberDef::Function,0,root->argList); + md->setTagInfo(rootNav->tagInfo()); + md->makeImplementationDetail(); + md->setMemberClass(cd); + md->setDefinition(funcDecl); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setDocsForDefinition(!root->proto); + md->setPrototype(root->proto); + md->addSectionsToDefinition(root->anchors); + md->setBodySegment(root->bodyLine,root->endBodyLine); + FileDef *fd=rootNav->fileDef(); + md->setBodyDef(fd); + md->setMemberSpecifiers(root->spec); + md->setMemberGroupId(root->mGrpId); + cd->insertMember(md); + cd->insertUsedFile(root->fileName); + md->setRefItems(root->sli); + if ((mn=Doxygen::memberNameSDict->find(root->name))) + { + mn->append(md); + } + else + { + mn = new MemberName(root->name); + mn->append(md); + Doxygen::memberNameSDict->append(root->name,mn); + } + } + else + { + // local objective C method found for class without interface + } + } + else // unrelated not overloaded member found + { + bool globMem = findGlobalMember(rootNav,namespaceName,funcName,funcTempList,funcArgs,funcDecl); + if (className.isEmpty() && !globMem) + { + warn(root->fileName,root->startLine, + "warning: class for member `%s' cannot " + "be found.", funcName.data() + ); + } + else if (!className.isEmpty() && !globMem) + { + warn(root->fileName,root->startLine, + "warning: member `%s' of class `%s' cannot be found", + funcName.data(),className.data()); + } + } + } + else + { + // this should not be called + warn(root->fileName,root->startLine, + "warning: member with no name found."); + } + return; +} + +//---------------------------------------------------------------------- +// find the members corresponding to the different documentation blocks +// that are extracted from the sources. + +static void filterMemberDocumentation(EntryNav *rootNav) +{ + Entry *root = rootNav->entry(); + int i=-1,l; + Debug::print(Debug::FindMembers,0, + "findMemberDocumentation(): root->type=`%s' root->inside=`%s' root->name=`%s' root->args=`%s' section=%x root->spec=%d root->mGrpId=%d\n", + root->type.data(),root->inside.data(),root->name.data(),root->args.data(),root->section,root->spec,root->mGrpId + ); + //printf("rootNav->parent()->name()=%s\n",rootNav->parent()->name().data()); + bool isFunc=TRUE; + + if (root->relatesType == Duplicate && !root->relates.isEmpty()) + { + QCString tmp = root->relates; + root->relates.resize(0); + filterMemberDocumentation(rootNav); + root->relates = tmp; + } + + if ( // detect func variable/typedef to func ptr + (i=findFunctionPtr(root->type,root->lang,&l))!=-1 + ) + { + //printf("Fixing function pointer!\n"); + // fix type and argument + root->args.prepend(root->type.right(root->type.length()-i-l)); + root->type=root->type.left(i+l); + //printf("Results type=%s,name=%s,args=%s\n",root->type.data(),root->name.data(),root->args.data()); + isFunc=FALSE; + } + else if ((root->type.left(8)=="typedef " && root->args.find('(')!=-1)) + // detect function types marked as functions + { + isFunc=FALSE; + } + + //printf("Member %s isFunc=%d\n",root->name.data(),isFunc); + if (root->section==Entry::MEMBERDOC_SEC) + { + //printf("Documentation for inline member `%s' found args=`%s'\n", + // root->name.data(),root->args.data()); + //if (root->relates.length()) printf(" Relates %s\n",root->relates.data()); + if (root->type.isEmpty()) + { + findMember(rootNav,root->name+root->args+root->exception,FALSE,isFunc); + } + else + { + findMember(rootNav,root->type+" "+root->name+root->args+root->exception,FALSE,isFunc); + } + } + else if (root->section==Entry::OVERLOADDOC_SEC) + { + //printf("Overloaded member %s found\n",root->name.data()); + findMember(rootNav,root->name,TRUE,isFunc); + } + else if + ((root->section==Entry::FUNCTION_SEC // function + || + (root->section==Entry::VARIABLE_SEC && // variable + !root->type.isEmpty() && // with a type + g_compoundKeywordDict.find(root->type)==0 // that is not a keyword + // (to skip forward declaration of class etc.) + ) + ) + ) + { + //printf("Documentation for member `%s' found args=`%s' excp=`%s'\n", + // root->name.data(),root->args.data(),root->exception.data()); + //if (root->relates.length()) printf(" Relates %s\n",root->relates.data()); + //printf("Inside=%s\n Relates=%s\n",root->inside.data(),root->relates.data()); + if (root->type=="friend class" || root->type=="friend struct" || + root->type=="friend union") + { + findMember(rootNav, + root->type+" "+ + root->name, + FALSE,FALSE); + + } + else if (!root->type.isEmpty()) + { + findMember(rootNav, + root->type+" "+ + root->inside+ + root->name+ + root->args+ + root->exception, + FALSE,isFunc); + } + else + { + findMember(rootNav, + root->inside+ + root->name+ + root->args+ + root->exception, + FALSE,isFunc); + } + } + else if (root->section==Entry::DEFINE_SEC && !root->relates.isEmpty()) + { + findMember(rootNav,root->name+root->args,FALSE,!root->args.isEmpty()); + } + else if (root->section==Entry::VARIABLEDOC_SEC) + { + //printf("Documentation for variable %s found\n",root->name.data()); + //if (!root->relates.isEmpty()) printf(" Relates %s\n",root->relates.data()); + findMember(rootNav,root->name,FALSE,FALSE); + } + else + { + // skip section + //printf("skip section\n"); + } +} + +static void findMemberDocumentation(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::MEMBERDOC_SEC || + rootNav->section()==Entry::OVERLOADDOC_SEC || + rootNav->section()==Entry::FUNCTION_SEC || + rootNav->section()==Entry::VARIABLE_SEC || + rootNav->section()==Entry::VARIABLEDOC_SEC || + rootNav->section()==Entry::DEFINE_SEC + ) + { + rootNav->loadEntry(g_storage); + + filterMemberDocumentation(rootNav); + + rootNav->releaseEntry(); + } + if (rootNav->children()) + { + EntryNavListIterator eli(*rootNav->children()); + EntryNav *e; + for (;(e=eli.current());++eli) + { + if (e->section()!=Entry::ENUM_SEC) findMemberDocumentation(e); + } + } +} + +//---------------------------------------------------------------------- + +static void findObjCMethodDefinitions(EntryNav *rootNav) +{ + if (rootNav->children()) + { + EntryNavListIterator eli(*rootNav->children()); + EntryNav *objCImplNav; + for (;(objCImplNav=eli.current());++eli) + { + if (objCImplNav->section()==Entry::OBJCIMPL_SEC && objCImplNav->children()) + { + EntryNavListIterator seli(*objCImplNav->children()); + EntryNav *objCMethodNav; + for (;(objCMethodNav=seli.current());++seli) + { + if (objCMethodNav->section()==Entry::FUNCTION_SEC) + { + objCMethodNav->loadEntry(g_storage); + Entry *objCMethod = objCMethodNav->entry(); + + //Printf(" Found ObjC method definition %s\n",objCMethod->name.data()); + findMember(objCMethodNav, objCMethod->type+" "+objCImplNav->name()+"::"+ + objCMethod->name+" "+objCMethod->args, FALSE,TRUE); + objCMethod->section=Entry::EMPTY_SEC; + + objCMethodNav->releaseEntry(); + } + } + } + } + } +} + +//---------------------------------------------------------------------- +// find and add the enumeration to their classes, namespaces or files + +static void findEnums(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::ENUM_SEC) + // non anonymous enumeration + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + MemberDef *md=0; + ClassDef *cd=0; + FileDef *fd=0; + NamespaceDef *nd=0; + MemberNameSDict *mnsd=0; + bool isGlobal; + bool isRelated=FALSE; + bool isMemberOf=FALSE; + //printf("Found enum with name `%s' relates=%s\n",root->name.data(),root->relates.data()); + int i; + + QCString name; + QCString scope; + + if ((i=root->name.findRev("::"))!=-1) // scope is specified + { + scope=root->name.left(i); // extract scope + name=root->name.right(root->name.length()-i-2); // extract name + if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope); + } + else // no scope, check the scope in which the docs where found + { + if (( rootNav->parent()->section() & Entry::SCOPE_MASK ) + && !rootNav->parent()->name().isEmpty() + ) // found enum docs inside a compound + { + scope=rootNav->parent()->name(); + if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope); + } + name=root->name; + } + + if (!root->relates.isEmpty()) + { // related member, prefix user specified scope + isRelated=TRUE; + isMemberOf=(root->relatesType == MemberOf); + if (getClass(root->relates)==0 && !scope.isEmpty()) + scope=mergeScopes(scope,root->relates); + else + scope=root->relates.copy(); + if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope); + } + + if (cd && !name.isEmpty()) // found a enum inside a compound + { + //printf("Enum `%s'::`%s'\n",cd->name(),name.data()); + fd=0; + mnsd=Doxygen::memberNameSDict; + isGlobal=FALSE; + } + else if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') // found enum inside namespace + { + mnsd=Doxygen::functionNameSDict; + isGlobal=TRUE; + } + else // found a global enum + { + fd=rootNav->fileDef(); + mnsd=Doxygen::functionNameSDict; + isGlobal=TRUE; + } + + if (!name.isEmpty()) + { + // new enum type + md = new MemberDef( + root->fileName,root->startLine, + 0,name,0,0, + root->protection,Normal,FALSE, + isMemberOf ? Foreign : isRelated ? Related : Member, + MemberDef::Enumeration, + 0,0); + md->setTagInfo(rootNav->tagInfo()); + if (!isGlobal) md->setMemberClass(cd); else md->setFileDef(fd); + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setBodyDef(rootNav->fileDef()); + //printf("Enum %s definition at line %d of %s: protection=%d\n", + // root->name.data(),root->bodyLine,root->fileName.data(),root->protection); + md->addSectionsToDefinition(root->anchors); + md->setMemberGroupId(root->mGrpId); + md->enableCallGraph(root->callGraph); + md->enableCallerGraph(root->callerGraph); + //printf("%s::setRefItems(%d)\n",md->name().data(),root->sli?root->sli->count():-1); + md->setRefItems(root->sli); + //printf("found enum %s nd=%p\n",name.data(),nd); + bool defSet=FALSE; + if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') + { + if (isRelated || Config_getBool("HIDE_SCOPE_NAMES")) + { + md->setDefinition(name); + } + else + { + md->setDefinition(nd->name()+"::"+name); + } + //printf("definition=%s\n",md->definition()); + defSet=TRUE; + md->setNamespace(nd); + nd->insertMember(md); + } + + // even if we have already added the enum to a namespace, we still + // also want to add it to other appropriate places such as file + // or class. + if (isGlobal) + { + if (!defSet) md->setDefinition(name); + if (fd==0 && rootNav->parent()) + { + fd=rootNav->parent()->fileDef(); + } + if (fd) + { + md->setFileDef(fd); + fd->insertMember(md); + } + } + else if (cd) + { + if (isRelated || Config_getBool("HIDE_SCOPE_NAMES")) + { + md->setDefinition(name); + } + else + { + md->setDefinition(cd->name()+"::"+name); + } + cd->insertMember(md); + cd->insertUsedFile(root->fileName); + } + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setDocsForDefinition(!root->proto); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + + //printf("Adding member=%s\n",md->name().data()); + MemberName *mn; + if ((mn=(*mnsd)[name])) + { + // this is used if the same enum is in multiple namespaces/classes + mn->append(md); + } + else // new enum name + { + mn = new MemberName(name); + mn->append(md); + mnsd->append(name,mn); + //printf("add %s to new memberName. Now %d members\n", + // name.data(),mn->count()); + } + addMemberToGroups(root,md); + +#if 0 + if (rootNav->children()) + { + EntryNavListIterator eli(*rootNav->children()); + EntryNav *e; + for (;(e=eli.current());++eli) + { + //printf("e->name=%s isRelated=%d\n",e->name.data(),isRelated); + MemberName *fmn=0; + MemberNameSDict *emnsd = isRelated ? Doxygen::functionNameSDict : mnsd; + if (!e->name().isEmpty() && (fmn=(*emnsd)[e->name()])) + // get list of members with the same name as the field + { + MemberNameIterator fmni(*fmn); + MemberDef *fmd; + for (fmni.toFirst(); (fmd=fmni.current()) ; ++fmni) + { + if (fmd->isEnumValue()) + { + //printf("found enum value with same name\n"); + if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') + { + NamespaceDef *fnd=fmd->getNamespaceDef(); + if (fnd==nd) // enum value is inside a namespace + { + md->insertEnumField(fmd); + fmd->setEnumScope(md); + } + } + else if (isGlobal) + { + FileDef *ffd=fmd->getFileDef(); + if (ffd==fd) // enum value has file scope + { + md->insertEnumField(fmd); + fmd->setEnumScope(md); + } + } + else if (isRelated && cd) // reparent enum value to + // match the enum's scope + { + md->insertEnumField(fmd); // add field def to list + fmd->setEnumScope(md); // cross ref with enum name + fmd->setEnumClassScope(cd); // cross ref with enum name + fmd->setOuterScope(cd); + fmd->makeRelated(); + cd->insertMember(fmd); + } + else + { + ClassDef *fcd=fmd->getClassDef(); + if (fcd==cd) // enum value is inside a class + { + //printf("Inserting enum field %s in enum scope %s\n", + // fmd->name().data(),md->name().data()); + md->insertEnumField(fmd); // add field def to list + fmd->setEnumScope(md); // cross ref with enum name + } + } + } + } + } + } + } +#endif + } + + rootNav->releaseEntry(); + } + else + { + RECURSE_ENTRYTREE(findEnums,rootNav); + } +} + +//---------------------------------------------------------------------- + +static void addEnumValuesToEnums(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::ENUM_SEC) + // non anonymous enumeration + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + ClassDef *cd=0; + FileDef *fd=0; + NamespaceDef *nd=0; + MemberNameSDict *mnsd=0; + bool isGlobal; + bool isRelated=FALSE; + //printf("Found enum with name `%s' relates=%s\n",root->name.data(),root->relates.data()); + int i; + + QCString name; + QCString scope; + + if ((i=root->name.findRev("::"))!=-1) // scope is specified + { + scope=root->name.left(i); // extract scope + name=root->name.right(root->name.length()-i-2); // extract name + if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope); + } + else // no scope, check the scope in which the docs where found + { + if (( rootNav->parent()->section() & Entry::SCOPE_MASK ) + && !rootNav->parent()->name().isEmpty() + ) // found enum docs inside a compound + { + scope=rootNav->parent()->name(); + if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope); + } + name=root->name; + } + + if (!root->relates.isEmpty()) + { // related member, prefix user specified scope + isRelated=TRUE; + if (getClass(root->relates)==0 && !scope.isEmpty()) + scope=mergeScopes(scope,root->relates); + else + scope=root->relates.copy(); + if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope); + } + + if (cd && !name.isEmpty()) // found a enum inside a compound + { + //printf("Enum in class `%s'::`%s'\n",cd->name().data(),name.data()); + fd=0; + mnsd=Doxygen::memberNameSDict; + isGlobal=FALSE; + } + else if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') // found enum inside namespace + { + //printf("Enum in namespace `%s'::`%s'\n",nd->name().data(),name.data()); + mnsd=Doxygen::functionNameSDict; + isGlobal=TRUE; + } + else // found a global enum + { + fd=rootNav->fileDef(); + //printf("Enum in file `%s': `%s'\n",fd->name().data(),name.data()); + mnsd=Doxygen::functionNameSDict; + isGlobal=TRUE; + } + + if (!name.isEmpty()) + { + MemberName *mn = mnsd->find(name); // for all members with this name + if (mn) + { + MemberNameIterator mni(*mn); + MemberDef *md; + for (mni.toFirst(); (md=mni.current()) ; ++mni) // for each enum in this list + { + if (md->isEnumerate() && rootNav->children()) + { + EntryNavListIterator eli(*rootNav->children()); // for each enum value + EntryNav *e; + for (;(e=eli.current());++eli) + { + SrcLangExt sle; + if (rootNav->fileDef() && + ( (sle=getLanguageFromFileName(rootNav->fileDef()->name()))==SrcLangExt_CSharp + || sle==SrcLangExt_Java || sle==SrcLangExt_XML + ) + ) + { + // Unlike C++, for C# enum value are only inside the enum + // scope, so we must create them here and only add them to the + // enum + e->loadEntry(g_storage); + Entry *root = e->entry(); + if (md->qualifiedName()==rootNav->name()) // enum value scope matches that of the enum + { + MemberDef *fmd=new MemberDef( + root->fileName,root->startLine, + root->type,root->name,root->args,0, + Public, Normal,root->stat,Member, + MemberDef::EnumValue,0,0); + if (md->getClassDef()) fmd->setMemberClass(md->getClassDef()); + else if (md->getNamespaceDef()) fmd->setNamespace(md->getNamespaceDef()); + else if (md->getFileDef()) fmd->setFileDef(md->getFileDef()); + fmd->setOuterScope(md->getOuterScope()); + fmd->setTagInfo(e->tagInfo()); + fmd->setDocumentation(root->doc,root->docFile,root->docLine); + fmd->setBriefDescription(root->brief,root->briefFile,root->briefLine); + fmd->addSectionsToDefinition(root->anchors); + fmd->setInitializer(root->initializer); + fmd->setMaxInitLines(root->initLines); + fmd->setMemberGroupId(root->mGrpId); + fmd->setExplicitExternal(root->explicitExternal); + fmd->setRefItems(root->sli); + if (fmd) + { + md->insertEnumField(fmd); + fmd->setEnumScope(md); + } + } + e->releaseEntry(); + } + else + { + //printf("e->name=%s isRelated=%d\n",e->name().data(),isRelated); + MemberName *fmn=0; + MemberNameSDict *emnsd = isRelated ? Doxygen::functionNameSDict : mnsd; + if (!e->name().isEmpty() && (fmn=(*emnsd)[e->name()])) + // get list of members with the same name as the field + { + MemberNameIterator fmni(*fmn); + MemberDef *fmd; + for (fmni.toFirst(); (fmd=fmni.current()) ; ++fmni) + { + if (fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope + { + //printf("found enum value with same name %s in scope %s\n", + // fmd->name().data(),fmd->getOuterScope()->name().data()); + if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') + { + NamespaceDef *fnd=fmd->getNamespaceDef(); + if (fnd==nd) // enum value is inside a namespace + { + md->insertEnumField(fmd); + fmd->setEnumScope(md); + } + } + else if (isGlobal) + { + FileDef *ffd=fmd->getFileDef(); + if (ffd==fd) // enum value has file scope + { + md->insertEnumField(fmd); + fmd->setEnumScope(md); + } + } + else if (isRelated && cd) // reparent enum value to + // match the enum's scope + { + md->insertEnumField(fmd); // add field def to list + fmd->setEnumScope(md); // cross ref with enum name + fmd->setEnumClassScope(cd); // cross ref with enum name + fmd->setOuterScope(cd); + fmd->makeRelated(); + cd->insertMember(fmd); + } + else + { + ClassDef *fcd=fmd->getClassDef(); + if (fcd==cd) // enum value is inside a class + { + //printf("Inserting enum field %s in enum scope %s\n", + // fmd->name().data(),md->name().data()); + md->insertEnumField(fmd); // add field def to list + fmd->setEnumScope(md); // cross ref with enum name + } + } + } + } + } + } + } + } + } + } + } + + rootNav->releaseEntry(); + } + else + { + RECURSE_ENTRYTREE(addEnumValuesToEnums,rootNav); + } +} + + +//---------------------------------------------------------------------- +// find the documentation blocks for the enumerations + +static void findEnumDocumentation(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::ENUMDOC_SEC + && !rootNav->name().isEmpty() + && rootNav->name().at(0)!='@' // skip anonymous enums + ) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + //printf("Found docs for enum with name `%s' in context %s\n", + // root->name.data(),root->parent->name.data()); + int i; + QCString name; + QCString scope; + if ((i=root->name.findRev("::"))!=-1) // scope is specified as part of the name + { + name=root->name.right(root->name.length()-i-2); // extract name + scope=root->name.left(i); // extract scope + //printf("Scope=`%s' Name=`%s'\n",scope.data(),name.data()); + } + else // just the name + { + name=root->name; + } + if (( rootNav->parent()->section() & Entry::SCOPE_MASK ) + && !rootNav->parent()->name().isEmpty() + ) // found enum docs inside a compound + { + if (!scope.isEmpty()) scope.prepend("::"); + scope.prepend(rootNav->parent()->name()); + } + ClassDef *cd=getClass(scope); + + if (!name.isEmpty()) + { + bool found=FALSE; + if (cd) + { + //printf("Enum: scope=`%s' name=`%s'\n",cd->name(),name.data()); + QCString className=cd->name().copy(); + MemberName *mn=Doxygen::memberNameSDict->find(name); + if (mn) + { + MemberNameIterator mni(*mn); + MemberDef *md; + for (mni.toFirst();(md=mni.current()) && !found;++mni) + { + ClassDef *cd=md->getClassDef(); + if (cd && cd->name()==className && md->isEnumerate()) + { + // documentation outside a compound overrides the documentation inside it +#if 0 + if (!md->documentation() || rootNav->parent()->name().isEmpty()) +#endif + { + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setDocsForDefinition(!root->proto); + } + + // brief descriptions inside a compound override the documentation + // outside it +#if 0 + if (!md->briefDescription() || !rootNav->parent()->name().isEmpty()) +#endif + { + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + } + + if (!md->inbodyDocumentation() || !rootNav->parent()->name().isEmpty()) + { + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + } + + if (root->mGrpId!=-1 && md->getMemberGroupId()==-1) + { + md->setMemberGroupId(root->mGrpId); + } + + md->addSectionsToDefinition(root->anchors); + md->setRefItems(root->sli); + + GroupDef *gd=md->getGroupDef(); + if (gd==0 &&root->groups->first()!=0) // member not grouped but out-of-line documentation is + { + addMemberToGroups(root,md); + } + + found=TRUE; + } + } + } + else + { + //printf("MemberName %s not found!\n",name.data()); + } + } + else // enum outside class + { + //printf("Enum outside class: %s grpId=%d\n",name.data(),root->mGrpId); + MemberName *mn=Doxygen::functionNameSDict->find(name); + if (mn) + { + MemberNameIterator mni(*mn); + MemberDef *md; + for (mni.toFirst();(md=mni.current()) && !found;++mni) + { + if (md->isEnumerate()) + { + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setDocsForDefinition(!root->proto); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->addSectionsToDefinition(root->anchors); + md->setMemberGroupId(root->mGrpId); + + GroupDef *gd=md->getGroupDef(); + if (gd==0 && root->groups->first()!=0) // member not grouped but out-of-line documentation is + { + addMemberToGroups(root,md); + } + + found=TRUE; + } + } + } + } + if (!found) + { + warn(root->fileName,root->startLine, + "warning: Documentation for undefined enum `%s' found.", + name.data() + ); + } + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(findEnumDocumentation,rootNav); +} + +// seach for each enum (member or function) in mnl if it has documented +// enum values. +static void findDEV(const MemberNameSDict &mnsd) +{ + MemberName *mn; + MemberNameSDict::Iterator mnli(mnsd); + // for each member name + for (mnli.toFirst();(mn=mnli.current());++mnli) + { + MemberDef *md; + MemberNameIterator mni(*mn); + // for each member definition + for (mni.toFirst();(md=mni.current());++mni) + { + if (md->isEnumerate()) // member is an enum + { + LockingPtr<MemberList> fmdl = md->enumFieldList(); + int documentedEnumValues=0; + if (fmdl!=0) // enum has values + { + MemberListIterator fmni(*fmdl); + MemberDef *fmd; + // for each enum value + for (fmni.toFirst();(fmd=fmni.current());++fmni) + { + if (fmd->isLinkableInProject()) documentedEnumValues++; + } + } + // at least one enum value is documented + if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE); + } + } + } +} + +// seach for each enum (member or function) if it has documented enum +// values. +static void findDocumentedEnumValues() +{ + findDEV(*Doxygen::memberNameSDict); + findDEV(*Doxygen::functionNameSDict); +} + +//---------------------------------------------------------------------- + +static void addMembersToIndex() +{ + MemberName *mn; + MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict); + // for each member name + for (mnli.toFirst();(mn=mnli.current());++mnli) + { + MemberDef *md; + MemberNameIterator mni(*mn); + // for each member definition + for (mni.toFirst();(md=mni.current());++mni) + { + addClassMemberNameToIndex(md); + } + } + MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict); + // for each member name + for (fnli.toFirst();(mn=fnli.current());++fnli) + { + MemberDef *md; + MemberNameIterator mni(*mn); + // for each member definition + for (mni.toFirst();(md=mni.current());++mni) + { + if (md->getNamespaceDef()) + { + addNamespaceMemberNameToIndex(md); + } + else + { + addFileMemberNameToIndex(md); + } + } + } +} + +//---------------------------------------------------------------------- +// computes the relation between all members. For each member `m' +// the members that override the implementation of `m' are searched and +// the member that `m' overrides is searched. + +static void computeMemberRelations() +{ + MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict); + MemberName *mn; + for ( ; (mn=mnli.current()) ; ++mnli ) // for each member name + { + MemberNameIterator mdi(*mn); + MemberDef *md; + for ( ; (md=mdi.current()) ; ++mdi ) // for each member with a specific name + { + MemberDef *bmd = mn->first(); // for each other member with the same name + while (bmd) + { + ClassDef *mcd = md->getClassDef(); + if (mcd && mcd->baseClasses()) + { + ClassDef *bmcd = bmd->getClassDef(); + //printf("Check relation between `%s'::`%s' (%p) and `%s'::`%s' (%p)\n", + // mcd->name().data(),md->name().data(),md, + // bmcd->name().data(),bmd->name().data(),bmd + // ); + if (md!=bmd && bmcd && mcd && bmcd!=mcd && mcd->isBaseClass(bmcd,TRUE)) + { + //printf(" derived scope\n"); + LockingPtr<ArgumentList> bmdAl = bmd->argumentList(); + LockingPtr<ArgumentList> mdAl = md->argumentList(); + //printf(" Base argList=`%s'\n Super argList=`%s'\n", + // argListToString(bmdAl.pointer()).data(), + // argListToString(mdAl.pointer()).data() + // ); + if ( + matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmdAl.pointer(), + md->getOuterScope(), md->getFileDef(), mdAl.pointer(), + TRUE + ) + ) + { + //printf(" match found!\n"); + if (mcd && bmcd && + mcd->isLinkable() && bmcd->isLinkable() + ) + { + MemberDef *rmd; + if ((rmd=md->reimplements())==0 || + minClassDistance(mcd,bmcd)<minClassDistance(mcd,rmd->getClassDef()) + ) + { + //printf("setting (new) reimplements member\n"); + md->setReimplements(bmd); + } + //printf("%s: add reimplements member %s\n",mcd->name().data(),bmcd->name().data()); + //md->setImplements(bmd); + //printf("%s: add reimplementedBy member %s\n",bmcd->name().data(),mcd->name().data()); + bmd->insertReimplementedBy(md); + } + } + } + } + bmd = mn->next(); + } + } + } +} + + +//---------------------------------------------------------------------------- +//static void computeClassImplUsageRelations() +//{ +// ClassDef *cd; +// ClassSDict::Iterator cli(*Doxygen::classSDict); +// for (;(cd=cli.current());++cli) +// { +// cd->determineImplUsageRelation(); +// } +//} + +//---------------------------------------------------------------------------- + +static void createTemplateInstanceMembers() +{ + ClassSDict::Iterator cli(*Doxygen::classSDict); + ClassDef *cd; + // for each class + for (cli.toFirst();(cd=cli.current());++cli) + { + // that is a template + QDict<ClassDef> *templInstances = cd->getTemplateInstances(); + if (templInstances) + { + QDictIterator<ClassDef> qdi(*templInstances); + ClassDef *tcd=0; + // for each instance of the template + for (qdi.toFirst();(tcd=qdi.current());++qdi) + { + tcd->addMembersToTemplateInstance(cd,qdi.currentKey()); + } + } + } +} + +//---------------------------------------------------------------------------- + +// builds the list of all members for each class + +static void buildCompleteMemberLists() +{ + ClassDef *cd; + // merge members of categories into the class they extend + ClassSDict::Iterator cli(*Doxygen::classSDict); + for (cli.toFirst();(cd=cli.current());++cli) + { + int i=cd->name().find('('); + if (i!=-1) // it is an Objective-C category + { + QCString baseName=cd->name().left(i); + ClassDef *baseClass=Doxygen::classSDict->find(baseName); + if (baseClass) + { + //printf("*** merging members of category %s into %s\n", + // cd->name().data(),baseClass->name().data()); + baseClass->mergeCategory(cd); + } + } + } + // merge the member list of base classes into the inherited classes. + for (cli.toFirst();(cd=cli.current());++cli) + { + if (// !cd->isReference() && // not an external class + cd->subClasses()==0 && // is a root of the hierarchy + cd->baseClasses()) // and has at least one base class + { + //printf("*** merging members for %s\n",cd->name().data()); + cd->mergeMembers(); + } + } + // now sort the member list of all classes. + for (cli.toFirst();(cd=cli.current());++cli) + { + if (cd->memberNameInfoSDict()) cd->memberNameInfoSDict()->sort(); + } +} + +//---------------------------------------------------------------------------- + +static void generateFileSources() +{ + if (documentedHtmlFiles==0) return; + if (Doxygen::inputNameList->count()>0) + { + FileNameListIterator fnli(*Doxygen::inputNameList); + FileName *fn; + for (;(fn=fnli.current());++fnli) + { + FileNameIterator fni(*fn); + FileDef *fd; + for (;(fd=fni.current());++fni) + { + if (fd->generateSourceFile()) // sources need to be shown in the output + { + msg("Generating code for file %s...\n",fd->docName().data()); + fd->writeSource(*g_outputList); + } + else if (!fd->isReference() && Doxygen::parseSourcesNeeded) + // we needed to parse the sources even if we do not show them + { + msg("Parsing code for file %s...\n",fd->docName().data()); + fd->parseSource(); + } + } + } + } +} + +//---------------------------------------------------------------------------- + +static void generateFileDocs() +{ + if (documentedHtmlFiles==0) return; + + if (Doxygen::inputNameList->count()>0) + { + FileNameListIterator fnli(*Doxygen::inputNameList); + FileName *fn; + for (fnli.toFirst();(fn=fnli.current());++fnli) + { + FileNameIterator fni(*fn); + FileDef *fd; + for (fni.toFirst();(fd=fni.current());++fni) + { + bool doc = fd->isLinkableInProject(); + if (doc) + { + msg("Generating docs for file %s...\n",fd->docName().data()); + fd->writeDocumentation(*g_outputList); + } + } + } + } +} + +//---------------------------------------------------------------------------- + +static void addSourceReferences() +{ + // add source references for class definitions + ClassSDict::Iterator cli(*Doxygen::classSDict); + ClassDef *cd=0; + for (cli.toFirst();(cd=cli.current());++cli) + { + FileDef *fd=cd->getBodyDef(); + if (fd && cd->isLinkableInProject() && cd->getStartBodyLine()!=-1) + { + fd->addSourceRef(cd->getStartBodyLine(),cd,0); + } + } + // add source references for namespace definitions + NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); + NamespaceDef *nd=0; + for (nli.toFirst();(nd=nli.current());++nli) + { + FileDef *fd=nd->getBodyDef(); + if (fd && nd->isLinkableInProject() && nd->getStartBodyLine()!=-1) + { + fd->addSourceRef(nd->getStartBodyLine(),nd,0); + } + } + + // add source references for member names + MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict); + MemberName *mn=0; + for (mnli.toFirst();(mn=mnli.current());++mnli) + { + MemberNameIterator mni(*mn); + MemberDef *md=0; + for (mni.toFirst();(md=mni.current());++mni) + { + //printf("class member %s: def=%s body=%d link?=%d\n", + // md->name().data(), + // md->getBodyDef()?md->getBodyDef()->name().data():"<none>", + // md->getStartBodyLine(),md->isLinkableInProject()); + FileDef *fd=md->getBodyDef(); + if (fd && + md->getStartBodyLine()!=-1 && + md->isLinkableInProject() && + (fd->generateSourceFile() || Doxygen::parseSourcesNeeded) + ) + { + //printf("Found member `%s' in file `%s' at line `%d' def=%s\n", + // md->name().data(),fd->name().data(),md->getStartBodyLine(),md->getOuterScope()->name().data()); + fd->addSourceRef(md->getStartBodyLine(),md->getOuterScope(),md); + } + } + } + MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict); + for (fnli.toFirst();(mn=fnli.current());++fnli) + { + MemberNameIterator mni(*mn); + MemberDef *md=0; + for (mni.toFirst();(md=mni.current());++mni) + { + FileDef *fd=md->getBodyDef(); + //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n", + // md->name().data(), + // md->getStartBodyLine(),md->getEndBodyLine(),fd, + // md->isLinkableInProject(), + // Doxygen::parseSourcesNeeded); + if (fd && + md->getStartBodyLine()!=-1 && + md->isLinkableInProject() && + (fd->generateSourceFile() || Doxygen::parseSourcesNeeded) + ) + { + //printf("Found member `%s' in file `%s' at line `%d' def=%s\n", + // md->name().data(),fd->name().data(),md->getStartBodyLine(),md->getOuterScope()->name().data()); + fd->addSourceRef(md->getStartBodyLine(),md->getOuterScope(),md); + } + } + } +} + +//---------------------------------------------------------------------------- + +static void sortMemberLists() +{ + // sort class member lists + ClassSDict::Iterator cli(*Doxygen::classSDict); + ClassDef *cd=0; + for (cli.toFirst();(cd=cli.current());++cli) + { + cd->sortMemberLists(); + } + + // sort namespace member lists + NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); + NamespaceDef *nd=0; + for (nli.toFirst();(nd=nli.current());++nli) + { + nd->sortMemberLists(); + } + + // sort file member lists + FileNameListIterator fnli(*Doxygen::inputNameList); + FileName *fn; + for (;(fn=fnli.current());++fnli) + { + FileNameIterator fni(*fn); + FileDef *fd; + for (;(fd=fni.current());++fni) + { + fd->sortMemberLists(); + } + } + + // sort group member lists + GroupSDict::Iterator gli(*Doxygen::groupSDict); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + gd->sortMemberLists(); + } +} + +//---------------------------------------------------------------------------- +// generate the documentation of all classes + +static void generateClassList(ClassSDict &classSDict) +{ + ClassSDict::Iterator cli(classSDict); + for ( ; cli.current() ; ++cli ) + { + ClassDef *cd=cli.current(); + + //printf("cd=%s getOuterScope=%p global=%p\n",cd->name().data(),cd->getOuterScope(),Doxygen::globalScope); + if ((cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file + cd->getOuterScope()==Doxygen::globalScope // only look at global classes + ) && !cd->isHidden() && !cd->isEmbeddedInGroupDocs() + ) + { + // skip external references, anonymous compounds and + // template instances + if ( cd->isLinkableInProject() && cd->templateMaster()==0) + { + msg("Generating docs for compound %s...\n",cd->name().data()); + + cd->writeDocumentation(*g_outputList); + cd->writeMemberList(*g_outputList); + } + // even for undocumented classes, the inner classes can be documented. + cd->writeDocumentationForInnerClasses(*g_outputList); + } + } +} + +static void generateClassDocs() +{ + // write the installdox script if necessary + if (Config_getBool("GENERATE_HTML") && + (Config_getList("TAGFILES").count()>0 || + Config_getBool("SEARCHENGINE") + ) + ) + { + writeInstallScript(); + } + + msg("Generating annotated compound index...\n"); + writeAnnotatedIndex(*g_outputList); + + //if (Config_getBool("ALPHABETICAL_INDEX")) + //{ + msg("Generating alphabetical compound index...\n"); + writeAlphabeticalIndex(*g_outputList); + //} + + msg("Generating hierarchical class index...\n"); + writeHierarchicalIndex(*g_outputList); + + msg("Generating member index...\n"); + writeClassMemberIndex(*g_outputList); + + if (Doxygen::exampleSDict->count()>0) + { + msg("Generating example index...\n"); + } + + generateClassList(*Doxygen::classSDict); + generateClassList(*Doxygen::hiddenClasses); +} + +//---------------------------------------------------------------------------- + +static void inheritDocumentation() +{ + MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict); + MemberName *mn; + //int count=0; + for (;(mn=mnli.current());++mnli) + { + MemberNameIterator mni(*mn); + MemberDef *md; + for (;(md=mni.current());++mni) + { + //printf("%04d Member `%s'\n",count++,md->name().data()); + if (md->documentation().isEmpty() && md->briefDescription().isEmpty()) + { // no documentation yet + MemberDef *bmd = md->reimplements(); + while (bmd && bmd->documentation().isEmpty() && + bmd->briefDescription().isEmpty() + ) + { // search up the inheritance tree for a documentation member + //printf("bmd=%s class=%s\n",bmd->name().data(),bmd->getClassDef()->name().data()); + bmd = bmd->reimplements(); + } + if (bmd) // copy the documentation from the reimplemented member + { + md->setInheritsDocsFrom(bmd); + md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine()); + md->setDocsForDefinition(bmd->isDocsForDefinition()); + md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine()); + md->copyArgumentNames(bmd); + md->setInbodyDocumentation(bmd->inbodyDocumentation(),bmd->inbodyFile(),bmd->inbodyLine()); + } + } + } + } +} + +//---------------------------------------------------------------------------- + +static void combineUsingRelations() +{ + // for each file + FileNameListIterator fnli(*Doxygen::inputNameList); + FileName *fn; + for (fnli.toFirst();(fn=fnli.current());++fnli) + { + FileNameIterator fni(*fn); + FileDef *fd; + for (fni.toFirst();(fd=fni.current());++fni) + { + fd->visited=FALSE; + } + } + for (fnli.toFirst();(fn=fnli.current());++fnli) + { + FileNameIterator fni(*fn); + FileDef *fd; + for (fni.toFirst();(fd=fni.current());++fni) + { + fd->combineUsingRelations(); + } + } + + // for each namespace + NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); + NamespaceDef *nd; + for (nli.toFirst() ; (nd=nli.current()) ; ++nli ) + { + nd->visited=FALSE; + } + for (nli.toFirst() ; (nd=nli.current()) ; ++nli ) + { + nd->combineUsingRelations(); + } +} + +//---------------------------------------------------------------------------- + +static void addMembersToMemberGroup() +{ + // for each class + ClassSDict::Iterator cli(*Doxygen::classSDict); + ClassDef *cd; + for ( ; (cd=cli.current()) ; ++cli ) + { + cd->addMembersToMemberGroup(); + } + // for each file + FileName *fn=Doxygen::inputNameList->first(); + while (fn) + { + FileDef *fd=fn->first(); + while (fd) + { + fd->addMembersToMemberGroup(); + fd=fn->next(); + } + fn=Doxygen::inputNameList->next(); + } + // for each namespace + NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); + NamespaceDef *nd; + for ( ; (nd=nli.current()) ; ++nli ) + { + nd->addMembersToMemberGroup(); + } + // for each group + GroupSDict::Iterator gli(*Doxygen::groupSDict); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + gd->addMembersToMemberGroup(); + } +} + +//---------------------------------------------------------------------------- + +static void distributeMemberGroupDocumentation() +{ + // for each class + ClassSDict::Iterator cli(*Doxygen::classSDict); + ClassDef *cd; + for ( ; (cd=cli.current()) ; ++cli ) + { + cd->distributeMemberGroupDocumentation(); + } + // for each file + FileName *fn=Doxygen::inputNameList->first(); + while (fn) + { + FileDef *fd=fn->first(); + while (fd) + { + fd->distributeMemberGroupDocumentation(); + fd=fn->next(); + } + fn=Doxygen::inputNameList->next(); + } + // for each namespace + NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); + NamespaceDef *nd; + for ( ; (nd=nli.current()) ; ++nli ) + { + nd->distributeMemberGroupDocumentation(); + } + // for each group + GroupSDict::Iterator gli(*Doxygen::groupSDict); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + gd->distributeMemberGroupDocumentation(); + } +} + +//---------------------------------------------------------------------------- + +static void findSectionsInDocumentation() +{ + // for each class + ClassSDict::Iterator cli(*Doxygen::classSDict); + ClassDef *cd; + for ( ; (cd=cli.current()) ; ++cli ) + { + cd->findSectionsInDocumentation(); + } + // for each file + FileName *fn=Doxygen::inputNameList->first(); + while (fn) + { + FileDef *fd=fn->first(); + while (fd) + { + fd->findSectionsInDocumentation(); + fd=fn->next(); + } + fn=Doxygen::inputNameList->next(); + } + // for each namespace + NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); + NamespaceDef *nd; + for ( ; (nd=nli.current()) ; ++nli ) + { + nd->findSectionsInDocumentation(); + } + // for each group + GroupSDict::Iterator gli(*Doxygen::groupSDict); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + gd->findSectionsInDocumentation(); + } + // for each page + PageSDict::Iterator pdi(*Doxygen::pageSDict); + PageDef *pd=0; + for (pdi.toFirst();(pd=pdi.current());++pdi) + { + pd->findSectionsInDocumentation(); + } + if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation(); +} + +static void flushCachedTemplateRelations() +{ + // remove all references to classes from the cache + // as there can be new template instances in the inheritance path + // to this class. Optimization: only remove those classes that + // have inheritance instances as direct or indirect sub classes. + QCacheIterator<LookupInfo> ci(Doxygen::lookupCache); + LookupInfo *li=0; + for (ci.toFirst();(li=ci.current());++ci) + { + if (li->classDef) + { + Doxygen::lookupCache.remove(ci.currentKey()); + } + } + // remove all cached typedef resolutions whose target is a + // template class as this may now be a template instance + MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict); + MemberName *fn; + for (;(fn=fnli.current());++fnli) // for each global function name + { + MemberNameIterator fni(*fn); + MemberDef *fmd; + for (;(fmd=fni.current());++fni) // for each function with that name + { + if (fmd->isTypedefValCached()) + { + ClassDef *cd = fmd->getCachedTypedefVal(); + if (cd->isTemplate()) fmd->invalidateTypedefValCache(); + } + } + } + MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict); + for (;(fn=mnli.current());++mnli) // for each class method name + { + MemberNameIterator mni(*fn); + MemberDef *fmd; + for (;(fmd=mni.current());++mni) // for each function with that name + { + if (fmd->isTypedefValCached()) + { + ClassDef *cd = fmd->getCachedTypedefVal(); + if (cd->isTemplate()) fmd->invalidateTypedefValCache(); + } + } + } +} + +//---------------------------------------------------------------------------- + +static void flushUnresolvedRelations() +{ + // Remove all unresolved references to classes from the cache. + // This is needed before resolving the inheritance relations, since + // it would otherwise not find the inheritance relation + // for C in the example below, as B::I was already found to be unresolvable + // (which is correct if you igore the inheritance relation between A and B). + // + // class A { class I {} }; + // class B : public A {}; + // class C : public B::I {}; + // + QCacheIterator<LookupInfo> ci(Doxygen::lookupCache); + LookupInfo *li=0; + for (ci.toFirst();(li=ci.current());++ci) + { + if (li->classDef==0 && li->typeDef==0) + { + Doxygen::lookupCache.remove(ci.currentKey()); + } + } + + MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict); + MemberName *fn; + for (;(fn=fnli.current());++fnli) // for each global function name + { + MemberNameIterator fni(*fn); + MemberDef *fmd; + for (;(fmd=fni.current());++fni) // for each function with that name + { + fmd->invalidateCachedArgumentTypes(); + } + } + MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict); + for (;(fn=mnli.current());++mnli) // for each class method name + { + MemberNameIterator mni(*fn); + MemberDef *fmd; + for (;(fmd=mni.current());++mni) // for each function with that name + { + fmd->invalidateCachedArgumentTypes(); + } + } + +} + +//---------------------------------------------------------------------------- + +static void findDefineDocumentation(EntryNav *rootNav) +{ + if ((rootNav->section()==Entry::DEFINEDOC_SEC || + rootNav->section()==Entry::DEFINE_SEC) && !rootNav->name().isEmpty() + ) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + //printf("found define `%s' `%s' brief=`%s' doc=`%s'\n", + // root->name.data(),root->args.data(),root->brief.data(),root->doc.data()); + + if (rootNav->tagInfo() && !root->name.isEmpty()) // define read from a tag file + { + MemberDef *md=new MemberDef("<tagfile>",1, + "#define",root->name,root->args,0, + Public,Normal,FALSE,Member,MemberDef::Define,0,0); + md->setTagInfo(rootNav->tagInfo()); + //printf("Searching for `%s' fd=%p\n",filePathName.data(),fd); + md->setFileDef(rootNav->parent()->fileDef()); + //printf("Adding member=%s\n",md->name().data()); + MemberName *mn; + if ((mn=Doxygen::functionNameSDict->find(root->name))) + { + mn->append(md); + } + else + { + mn = new MemberName(root->name); + mn->append(md); + Doxygen::functionNameSDict->append(root->name,mn); + } + } + MemberName *mn=Doxygen::functionNameSDict->find(root->name); + if (mn) + { + int count=0; + MemberDef *md=mn->first(); + while (md) + { + if (md->memberType()==MemberDef::Define) count++; + md=mn->next(); + } + if (count==1) + { + md=mn->first(); + while (md) + { + if (md->memberType()==MemberDef::Define) + { + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setDocsForDefinition(!root->proto); + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + if (md->inbodyDocumentation().isEmpty()) + { + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + } + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setBodyDef(rootNav->fileDef()); + md->addSectionsToDefinition(root->anchors); + md->setMaxInitLines(root->initLines); + md->setRefItems(root->sli); + if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId); + addMemberToGroups(root,md); + } + md=mn->next(); + } + } + else if (count>1 && + (!root->doc.isEmpty() || + !root->brief.isEmpty() || + root->bodyLine!=-1 + ) + ) + // multiple defines don't know where to add docs + // but maybe they are in different files together with their documentation + { + md=mn->first(); + while (md) + { + if (md->memberType()==MemberDef::Define) + { + FileDef *fd=md->getFileDef(); + if (fd && fd->absFilePath()==root->fileName) + // doc and define in the same file assume they belong together. + { +#if 0 + if (md->documentation().isEmpty()) +#endif + { + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setDocsForDefinition(!root->proto); + } +#if 0 + if (md->briefDescription().isEmpty()) +#endif + { + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + } + if (md->inbodyDocumentation().isEmpty()) + { + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + } + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setBodyDef(rootNav->fileDef()); + md->addSectionsToDefinition(root->anchors); + md->setRefItems(root->sli); + if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId); + addMemberToGroups(root,md); + } + } + md=mn->next(); + } + //warn("warning: define %s found in the following files:\n",root->name.data()); + //warn("Cannot determine where to add the documentation found " + // "at line %d of file %s. \n", + // root->startLine,root->fileName.data()); + } + } + else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found + { + static bool preEnabled = Config_getBool("ENABLE_PREPROCESSING"); + if (preEnabled) + { + warn(root->fileName,root->startLine, + "warning: documentation for unknown define %s found.\n", + root->name.data() + ); + } + else + { + warn(root->fileName,root->startLine, + "warning: found documented #define but ignoring it because " + "ENABLE_PREPROCESSING is NO.\n", + root->name.data() + ); + } + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(findDefineDocumentation,rootNav); +} + +//---------------------------------------------------------------------------- + +static void findDirDocumentation(EntryNav *rootNav) +{ + if (rootNav->section() == Entry::DIRDOC_SEC) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + QCString normalizedName = root->name; + normalizedName = substitute(normalizedName,"\\","/"); + //printf("root->docFile=%s normalizedName=%s\n", + // root->docFile.data(),normalizedName.data()); + if (root->docFile==normalizedName) // current dir? + { + int lastSlashPos=normalizedName.findRev('/'); + if (lastSlashPos!=-1) // strip file name + { + normalizedName=normalizedName.left(lastSlashPos); + } + } + if (normalizedName.at(normalizedName.length()-1)!='/') + { + normalizedName+='/'; + } + DirDef *dir,*matchingDir=0; + SDict<DirDef>::Iterator sdi(*Doxygen::directories); + for (sdi.toFirst();(dir=sdi.current());++sdi) + { + //printf("Dir: %s<->%s\n",dir->name().data(),normalizedName.data()); + if (dir->name().right(normalizedName.length())==normalizedName) + { + if (matchingDir) + { + warn(root->fileName,root->startLine, + "warning: \\dir command matches multiple directories.\n" + " Applying the command for directory %s\n" + " Ignoring the command for directory %s\n", + matchingDir->name().data(),dir->name().data() + ); + } + else + { + matchingDir=dir; + } + } + } + if (matchingDir) + { + //printf("Match for with dir %s\n",matchingDir->name().data()); + matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine); + matchingDir->setDocumentation(root->doc,root->docFile,root->docLine); + matchingDir->setRefItems(root->sli); + addDirToGroups(root,matchingDir); + } + else + { + warn(root->fileName,root->startLine,"warning: No matching " + "directory found for command \\dir %s\n",normalizedName.data()); + } + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(findDirDocumentation,rootNav); +} + + +//---------------------------------------------------------------------------- +// create a (sorted) list of separate documentation pages + +static void buildPageList(EntryNav *rootNav) +{ + if (rootNav->section() == Entry::PAGEDOC_SEC) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + if (!root->name.isEmpty()) + { + addRelatedPage(rootNav); + } + + rootNav->releaseEntry(); + } + else if (rootNav->section() == Entry::MAINPAGEDOC_SEC) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + QCString title=root->args.stripWhiteSpace(); + if (title.isEmpty()) title=theTranslator->trMainPage(); + QCString name = Config_getBool("GENERATE_TREEVIEW")?"main":"index"; + addRefItem(root->sli, + name, + "page", + name, + title, + 0 + ); + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(buildPageList,rootNav); +} + +static void findMainPage(EntryNav *rootNav) +{ + if (rootNav->section() == Entry::MAINPAGEDOC_SEC) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + if (Doxygen::mainPage==0) + { + //printf("Found main page! \n======\n%s\n=======\n",root->doc.data()); + QCString title=root->args.stripWhiteSpace(); + QCString indexName=Config_getBool("GENERATE_TREEVIEW")?"main":"index"; + Doxygen::mainPage = new PageDef(root->fileName,root->startLine, + indexName, root->brief+root->doc+root->inbodyDocs,title); + //setFileNameForSections(root->anchors,"index",Doxygen::mainPage); + Doxygen::mainPage->setFileName(indexName); + addPageToContext(Doxygen::mainPage,rootNav); + + // a page name is a label as well! + SectionInfo *si=new SectionInfo( + indexName, + Doxygen::mainPage->name(), + Doxygen::mainPage->title(), + SectionInfo::Page); + Doxygen::sectionDict.insert(indexName,si); + Doxygen::mainPage->addSectionsToDefinition(root->anchors); + } + else + { + warn(root->fileName,root->startLine, + "warning: found more than one \\mainpage comment block! Skipping this " + "block." + ); + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(findMainPage,rootNav); +} + +static void computePageRelations(EntryNav *rootNav) +{ + if ((rootNav->section()==Entry::PAGEDOC_SEC || + rootNav->section()==Entry::MAINPAGEDOC_SEC + ) + && !rootNav->name().isEmpty() + ) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + PageDef *pd = root->section==Entry::PAGEDOC_SEC ? + Doxygen::pageSDict->find(root->name) : + Doxygen::mainPage; + if (pd) + { + QListIterator<BaseInfo> bii(*root->extends); + BaseInfo *bi; + for (bii.toFirst();(bi=bii.current());++bii) + { + PageDef *subPd = Doxygen::pageSDict->find(bi->name); + if (subPd) + { + pd->addInnerCompound(subPd); + //printf("*** Added subpage relation: %s->%s\n", + // pd->name().data(),subPd->name().data()); + } + } + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(computePageRelations,rootNav); +} + +static void checkPageRelations() +{ + PageSDict::Iterator pdi(*Doxygen::pageSDict); + PageDef *pd=0; + for (pdi.toFirst();(pd=pdi.current());++pdi) + { + Definition *ppd = pd->getOuterScope(); + while (ppd) + { + if (ppd==pd) + { + err("warning: page defined at line %d of file %s with label %s is a subpage " + "of itself! Please remove this cyclic dependency.\n", + pd->docLine(),pd->docFile().data(),pd->name().data()); + exit(1); + } + ppd=ppd->getOuterScope(); + } + } +} + +//---------------------------------------------------------------------------- + +static void resolveUserReferences() +{ + QDictIterator<SectionInfo> sdi(Doxygen::sectionDict); + SectionInfo *si; + for (;(si=sdi.current());++sdi) + { + //printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n", + // si->label.data(),si->definition?si->definition->name().data():"<none>", + // si->fileName.data()); + PageDef *pd=0; + + // hack: the items of a todo/test/bug/deprecated list are all fragments from + // different files, so the resulting section's all have the wrong file + // name (not from the todo/test/bug/deprecated list, but from the file in + // which they are defined). We correct this here by looking at the + // generated section labels! + QDictIterator<RefList> rli(*Doxygen::xrefLists); + RefList *rl; + for (rli.toFirst();(rl=rli.current());++rli) + { + QCString label="_"+rl->listName(); // "_todo", "_test", ... + if (si->label.left(label.length())==label) + { + si->fileName=rl->listName(); + si->generated=TRUE; + break; + } + } + + //printf("start: si->label=%s si->fileName=%s\n",si->label.data(),si->fileName.data()); + if (!si->generated) + { + // if this section is in a page and the page is in a group, then we + // have to adjust the link file name to point to the group. + if (!si->fileName.isEmpty() && + (pd=Doxygen::pageSDict->find(si->fileName)) && + pd->getGroupDef()) + { + si->fileName=pd->getGroupDef()->getOutputFileBase().copy(); + } + + if (si->definition) + { + // TODO: there should be one function in Definition that returns + // the file to link to, so we can avoid the following tests. + GroupDef *gd=0; + if (si->definition->definitionType()==Definition::TypeMember) + { + gd = ((MemberDef *)si->definition)->getGroupDef(); + } + + if (gd) + { + si->fileName=gd->getOutputFileBase().copy(); + } + else + { + //si->fileName=si->definition->getOutputFileBase().copy(); + //printf("Setting si->fileName to %s\n",si->fileName.data()); + } + } + } + //printf("end: si->label=%s si->fileName=%s\n",si->label.data(),si->fileName.data()); + } +} + + +//---------------------------------------------------------------------------- +// generate all separate documentation pages + + +static void generatePageDocs() +{ + //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageSDict->count()); + if (documentedPages==0) return; + PageSDict::Iterator pdi(*Doxygen::pageSDict); + PageDef *pd=0; + for (pdi.toFirst();(pd=pdi.current());++pdi) + { + if (!pd->getGroupDef() && !pd->isReference()) + { + msg("Generating docs for page %s...\n",pd->name().data()); + Doxygen::insideMainPage=TRUE; + pd->writeDocumentation(*g_outputList); + Doxygen::insideMainPage=FALSE; + } + } +} + +//---------------------------------------------------------------------------- +// create a (sorted) list & dictionary of example pages + +static void buildExampleList(EntryNav *rootNav) +{ + if (rootNav->section()==Entry::EXAMPLE_SEC && !rootNav->name().isEmpty()) + { + rootNav->loadEntry(g_storage); + Entry *root = rootNav->entry(); + + if (Doxygen::exampleSDict->find(root->name)) + { + warn(root->fileName,root->startLine, + "warning: Example %s was already documented. Ignoring " + "documentation found here.", + root->name.data() + ); + } + else + { + PageDef *pd=new PageDef(root->fileName,root->startLine, + root->name,root->brief+root->doc+root->inbodyDocs,root->args); + pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE)); + pd->addSectionsToDefinition(root->anchors); + //pi->addSections(root->anchors); + + Doxygen::exampleSDict->inSort(root->name,pd); + //we don't add example to groups + //addExampleToGroups(root,pd); + } + + rootNav->releaseEntry(); + } + RECURSE_ENTRYTREE(buildExampleList,rootNav); +} + +//---------------------------------------------------------------------------- +// prints the Entry tree (for debugging) + +void printNavTree(EntryNav *rootNav,int indent) +{ + QCString indentStr; + indentStr.fill(' ',indent); + msg("%s%s (sec=0x%x)\n", + indentStr.isEmpty()?"":indentStr.data(), + rootNav->name().isEmpty()?"<empty>":rootNav->name().data(), + rootNav->section()); + if (rootNav->children()) + { + EntryNavListIterator eli(*rootNav->children()); + for (;eli.current();++eli) printNavTree(eli.current(),indent+2); + } +} + + +//---------------------------------------------------------------------------- +// generate the example documentation + +static void generateExampleDocs() +{ + g_outputList->disable(OutputGenerator::Man); + PageSDict::Iterator pdi(*Doxygen::exampleSDict); + PageDef *pd=0; + for (pdi.toFirst();(pd=pdi.current());++pdi) + { + msg("Generating docs for example %s...\n",pd->name().data()); + resetCCodeParserState(); + QCString n=pd->getOutputFileBase(); + startFile(*g_outputList,n,n,pd->name()); + startTitle(*g_outputList,n); + g_outputList->docify(pd->name()); + endTitle(*g_outputList,n,0); + g_outputList->startContents(); + g_outputList->parseDoc(pd->docFile(), // file + pd->docLine(), // startLine + pd, // context + 0, // memberDef + pd->documentation()+"\n\n\\include "+pd->name(), // docs + TRUE, // index words + TRUE, // is example + pd->name() + ); + g_outputList->endContents(); + endFile(*g_outputList); + } + g_outputList->enable(OutputGenerator::Man); +} + +//---------------------------------------------------------------------------- +// generate module pages + +static void generateGroupDocs() +{ + GroupSDict::Iterator gli(*Doxygen::groupSDict); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + if (!gd->isReference()) + { + gd->writeDocumentation(*g_outputList); + } + } +} + +//---------------------------------------------------------------------------- + +//static void generatePackageDocs() +//{ +// writePackageIndex(*g_outputList); +// +// if (Doxygen::packageDict.count()>0) +// { +// PackageSDict::Iterator pdi(Doxygen::packageDict); +// PackageDef *pd; +// for (pdi.toFirst();(pd=pdi.current());++pdi) +// { +// pd->writeDocumentation(*g_outputList); +// } +// } +//} + +//---------------------------------------------------------------------------- +// generate module pages + +static void generateNamespaceDocs() +{ + writeNamespaceIndex(*g_outputList); + + NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); + NamespaceDef *nd; + // for each namespace... + for (;(nd=nli.current());++nli) + { + + if (nd->isLinkableInProject()) + { + msg("Generating docs for namespace %s\n",nd->name().data()); + nd->writeDocumentation(*g_outputList); + } + + // for each class in the namespace... + ClassSDict::Iterator cli(*nd->getClassSDict()); + for ( ; cli.current() ; ++cli ) + { + ClassDef *cd=cli.current(); + if ( ( cd->isLinkableInProject() && + cd->templateMaster()==0 + ) // skip external references, anonymous compounds and + // template instances and nested classes + && !cd->isHidden() + ) + { + msg("Generating docs for compound %s...\n",cd->name().data()); + + cd->writeDocumentation(*g_outputList); + cd->writeMemberList(*g_outputList); + } + cd->writeDocumentationForInnerClasses(*g_outputList); + } + } +} + +#if defined(_WIN32) +static QCString fixSlashes(QCString &s) +{ + QCString result; + uint i; + for (i=0;i<s.length();i++) + { + switch(s.at(i)) + { + case '/': + case '\\': + result+="\\\\"; + break; + default: + result+=s.at(i); + } + } + return result; +} +#endif + + +//---------------------------------------------------------------------------- +// generate files for the search engine + +//static void generateSearchIndex() +//{ +// if (Config_getBool("SEARCHENGINE") && Config_getBool("GENERATE_HTML")) +// { +// // create search index +// QCString fileName; +// writeSearchButton(Config_getString("HTML_OUTPUT")); +// +//#if !defined(_WIN32) +// // create cgi script +// fileName = Config_getString("HTML_OUTPUT")+"/"+Config_getString("CGI_NAME"); +// QFile f(fileName); +// if (f.open(IO_WriteOnly)) +// { +// QTextStream t(&f); +// t << "#!/bin/sh" << endl +// << "DOXYSEARCH=" << Config_getString("BIN_ABSPATH") << "/doxysearch" << endl +// << "DOXYPATH=\"" << Config_getString("DOC_ABSPATH") << " "; +// +// QStrList &extDocPaths=Config_getList("EXT_DOC_PATHS"); +// char *s= extDocPaths.first(); +// while (s) +// { +// t << s << " "; +// s=extDocPaths.next(); +// } +// +// t << "\"" << endl +// << "if [ -f $DOXYSEARCH ]" << endl +// << "then" << endl +// << " $DOXYSEARCH $DOXYPATH" << endl +// << "else" << endl +// << " echo \"Content-Type: text/html\"" << endl +// << " echo \"\"" << endl +// << " echo \"<h2>error: $DOXYSEARCH not found. Check cgi script!</h2>\"" << endl +// << "fi" << endl; +// +// f.close(); +// struct stat stat_struct; +// stat(fileName,&stat_struct); +// chmod(fileName,stat_struct.st_mode|S_IXUSR|S_IXGRP|S_IXOTH); +// } +// else +// { +// err("error: Cannot open file %s for writing\n",fileName.data()); +// } +//#else /* Windows platform */ +// // create cgi program +// fileName = Config_getString("CGI_NAME").copy(); +// if (fileName.right(4)==".cgi") +// fileName=fileName.left(fileName.length()-4); +// fileName+=".c"; +// fileName.prepend(Config_getString("HTML_OUTPUT")+"/"); +// QFile f(fileName); +// if (f.open(IO_WriteOnly)) +// { +// QTextStream t(&f); +// t << "#include <stdio.h>" << endl; +// t << "#include <stdlib.h>" << endl; +// t << "#include <process.h>" << endl; +// t << endl; +// t << "const char *DOXYSEARCH = \"" << +// fixSlashes(Config_getString("BIN_ABSPATH")) << "\\\\doxysearch.exe\";" << endl; +// t << "const char *DOXYPATH = \"" << +// fixSlashes(Config_getString("DOC_ABSPATH")) << "\";" << endl; +// t << endl; +// t << "int main(void)" << endl; +// t << "{" << endl; +// t << " char buf[1024];" << endl; +// t << " sprintf(buf,\"%s %s\",DOXYSEARCH,DOXYPATH);" << endl; +// t << " if (system(buf))" << endl; +// t << " {" << endl; +// t << " printf(\"Content-Type: text/html\\n\\n\");" << endl; +// t << " printf(\"<h2>error: failed to execute %s</h2>\\n\",DOXYSEARCH);" << endl; +// t << " exit(1);" << endl; +// t << " }" << endl; +// t << " return 0;" << endl; +// t << "}" << endl; +// f.close(); +// } +// else +// { +// err("error: Cannot open file %s for writing\n",fileName.data()); +// } +//#endif /* !defined(_WIN32) */ +// +// // create config file +// fileName = Config_getString("HTML_OUTPUT")+"/search.cfg"; +// f.setName(fileName); +// if (f.open(IO_WriteOnly)) +// { +// QTextStream t(&f); +// t << Config_getString("DOC_URL") << "/" << endl +// << Config_getString("CGI_URL") << "/" << Config_getString("CGI_NAME") << endl; +// f.close(); +// } +// else +// { +// err("error: Cannot open file %s for writing\n",fileName.data()); +// } +// //g_outputList->generateExternalIndex(); +// g_outputList->pushGeneratorState(); +// g_outputList->disableAllBut(OutputGenerator::Html); +// startFile(*g_outputList,"header"+Doxygen::htmlFileExtension,0,"Search Engine",TRUE); +// g_outputList->endPlainFile(); +// g_outputList->startPlainFile("footer"+Doxygen::htmlFileExtension); +// endFile(*g_outputList,TRUE); +// g_outputList->popGeneratorState(); +// } +//} + +//---------------------------------------------------------------------------- + +static bool openOutputFile(const char *outFile,QFile &f) +{ + bool fileOpened=FALSE; + bool writeToStdout=(outFile[0]=='-' && outFile[1]=='\0'); + if (writeToStdout) // write to stdout + { + fileOpened = f.open(IO_WriteOnly,stdout); + } + else // write to file + { + QFileInfo fi(outFile); + if (fi.exists()) // create a backup + { + QDir dir=fi.dir(); + QFileInfo backup(fi.fileName()+".bak"); + if (backup.exists()) // remove existing backup + dir.remove(backup.fileName()); + dir.rename(fi.fileName(),fi.fileName()+".bak"); + } + f.setName(outFile); + fileOpened = f.open(IO_WriteOnly|IO_Translate); + } + return fileOpened; +} + +/*! Generate a template version of the configuration file. + * If the \a shortList parameter is TRUE a configuration file without + * comments will be generated. + */ +static void generateConfigFile(const char *configFile,bool shortList, + bool updateOnly=FALSE) +{ + QFile f; + bool fileOpened=openOutputFile(configFile,f); + bool writeToStdout=(configFile[0]=='-' && configFile[1]=='\0'); + if (fileOpened) + { + FTextStream t(&f); + Config::instance()->writeTemplate(t,shortList,updateOnly); + if (!writeToStdout) + { + if (!updateOnly) + { + msg("\n\nConfiguration file `%s' created.\n\n",configFile); + msg("Now edit the configuration file and enter\n\n"); + if (strcmp(configFile,"Doxyfile") || strcmp(configFile,"doxyfile")) + msg(" doxygen %s\n\n",configFile); + else + msg(" doxygen\n\n"); + msg("to generate the documentation for your project\n\n"); + } + else + { + msg("\n\nConfiguration file `%s' updated.\n\n",configFile); + } + } + } + else + { + err("error: Cannot open file %s for writing\n",configFile); + exit(1); + } +} + +//---------------------------------------------------------------------------- +// read and parse a tag file + +//static bool readLineFromFile(QFile &f,QCString &s) +//{ +// char c=0; +// s.resize(0); +// while (!f.atEnd() && (c=f.getch())!='\n') s+=c; +// return f.atEnd(); +//} + +//---------------------------------------------------------------------------- + +static void readTagFile(Entry *root,const char *tl) +{ + QCString tagLine = tl; + QCString fileName; + QCString destName; + int eqPos = tagLine.find('='); + if (eqPos!=-1) // tag command contains a destination + { + fileName = tagLine.left(eqPos).stripWhiteSpace(); + destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace(); + QFileInfo fi(fileName); + Doxygen::tagDestinationDict.insert(fi.fileName(),new QCString(destName)); + //printf("insert tagDestination %s->%s\n",fi.fileName().data(),destName.data()); + } + else + { + fileName = tagLine; + } + + QFileInfo fi(fileName); + if (!fi.exists() || !fi.isFile()) + { + err("error: Tag file `%s' does not exist or is not a file. Skipping it...\n", + fileName.data()); + return; + } + + if (!destName.isEmpty()) + msg("Reading tag file `%s', location `%s'...\n",fileName.data(),destName.data()); + else + msg("Reading tag file `%s'...\n",fileName.data()); + + parseTagFile(root,fi.absFilePath(),fi.fileName()); + +} + +//---------------------------------------------------------------------------- +static void copyFile(const QCString &src,const QCString &dest) +{ + QFile sf(src); + if (sf.open(IO_ReadOnly)) + { + QFileInfo fi(src); + QFile df(dest); + if (df.open(IO_WriteOnly)) + { + char *buffer = new char[fi.size()]; + sf.readBlock(buffer,fi.size()); + df.writeBlock(buffer,fi.size()); + df.flush(); + delete[] buffer; + } + else + { + err("error: could not write to file %s\n",dest.data()); + } + } + else + { + err("error: could not open user specified file %s\n",src.data()); + } +} + +static void copyStyleSheet() +{ + QCString &htmlStyleSheet = Config_getString("HTML_STYLESHEET"); + if (!htmlStyleSheet.isEmpty()) + { + QFileInfo fi(htmlStyleSheet); + if (!fi.exists()) + { + err("Style sheet '%s' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet.data()); + htmlStyleSheet.resize(0); // revert to the default + } + else + { + QCString destFileName = Config_getString("HTML_OUTPUT")+"/"+fi.fileName().data(); + copyFile(htmlStyleSheet,destFileName); + } + } +} + +static void copyLogo() +{ + QCString &projectLogo = Config_getString("PROJECT_LOGO"); + if (!projectLogo.isEmpty()) + { + QFileInfo fi(projectLogo); + if (!fi.exists()) + { + err("Project logo '%s' specified by PROJECT_LOGO does not exist!\n",projectLogo.data()); + projectLogo.resize(0); // revert to the default + } + else + { + QCString destFileName = Config_getString("HTML_OUTPUT")+"/"+fi.fileName().data(); + copyFile(projectLogo,destFileName); + } + } +} + +static void copyExtraFiles() +{ + QStrList files = Config_getList("HTML_EXTRA_FILES"); + uint i; + for (i=0; i<files.count(); ++i) + { + QCString fileName(files.at(i)); + + if (!fileName.isEmpty()) + { + QFileInfo fi(fileName); + if (!fi.exists()) + { + err("Extra HTML file '%s' specified in HTML_EXTRA_FILES does not exist!\n", fileName.data()); + } + else + { + QCString destFileName = Config_getString("HTML_OUTPUT")+"/"+fi.fileName().data(); + Doxygen::indexList.addImageFile(fi.fileName().data()); + copyFile(fileName, destFileName); + } + } + } +} + +//! parse the list of input files +static void parseFiles(Entry *root,EntryNav *rootNav) +{ +#if 0 + void *cd = 0; + QCString inpEncoding = Config_getString("INPUT_ENCODING"); + bool needsTranscoding = !inpEncoding.isEmpty(); + if (needsTranscoding) + { + if (!(cd = portable_iconv_open("UTF-8", inpEncoding))) + { + err("error: unsupported character enconding: '%s'",inpEncoding.data()); + exit(1); + } + } +#endif + + QCString *s=g_inputFiles.first(); + while (s) + { + QCString fileName=*s; + QCString extension; + int ei = fileName.findRev('.'); + if (ei!=-1) extension=fileName.right(fileName.length()-ei); + ParserInterface *parser = Doxygen::parserManager->getParser(extension); + + QFileInfo fi(fileName); + BufStr preBuf(fi.size()+4096); + + if (Config_getBool("ENABLE_PREPROCESSING") && + parser->needsPreprocessing(extension)) + { + BufStr inBuf(fi.size()+4096); + msg("Preprocessing %s...\n",s->data()); + readInputFile(fileName,inBuf); + preprocessFile(fileName,inBuf,preBuf); + } + else // no preprocessing + { + msg("Reading %s...\n",s->data()); + readInputFile(fileName,preBuf); + } + + BufStr convBuf(preBuf.curPos()+1024); + + // convert multi-line C++ comments to C style comments + convertCppComments(&preBuf,&convBuf,fileName); + + convBuf.addChar('\0'); + + // use language parse to parse the file + parser->parseInput(fileName,convBuf.data(),root); + + // store the Entry tree in a file and create an index to + // navigate/load entries + bool ambig; + FileDef *fd=findFileDef(Doxygen::inputNameDict,fileName,ambig); + ASSERT(fd!=0); + root->createNavigationIndex(rootNav,g_storage,fd); + + s=g_inputFiles.next(); + } +} + +// resolves a path that may include symlinks, if a recursive symlink is +// found an empty string is returned. +static QCString resolveSymlink(QCString path) +{ + int sepPos=0; + int oldPos=0; + QFileInfo fi; + QDict<void> nonSymlinks; + QDict<void> known; + QCString result = path; + QCString oldPrefix = "/"; + do + { +#ifdef WIN32 + // UNC path, skip server and share name + if (sepPos==0 && (result.left(2)=="//" || result.left(2)=="\\\\")) + sepPos = result.find('/',2); + if (sepPos!=-1) + sepPos = result.find('/',sepPos+1); +#else + sepPos = result.find('/',sepPos+1); +#endif + QCString prefix = sepPos==-1 ? result : result.left(sepPos); + if (nonSymlinks.find(prefix)==0) + { + fi.setFile(prefix); + if (fi.isSymLink()) + { + QString target = fi.readLink(); + bool isRelative = QFileInfo(target).isRelative(); + if (isRelative) + { + target = QDir::cleanDirPath(oldPrefix+"/"+target.data()); + } + if (sepPos!=-1) + { + if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/') + { + target+='/'; + } + target+=result.mid(sepPos); + } + result = QDir::cleanDirPath(target).data(); + sepPos = 0; + if (known.find(result)) return QCString(); // recursive symlink! + known.insert(result,(void*)0x8); + if (isRelative) + { + sepPos = oldPos; + } + else // link to absolute path + { + sepPos = 0; + oldPrefix = "/"; + } + } + else + { + nonSymlinks.insert(prefix,(void*)0x8); + oldPrefix = prefix; + } + oldPos = sepPos; + } + } + while (sepPos!=-1); + return QDir::cleanDirPath(result).data(); +} + +static QDict<void> g_pathsVisited(1009); + +//---------------------------------------------------------------------------- +// Read all files matching at least one pattern in `patList' in the +// directory represented by `fi'. +// The directory is read iff the recusiveFlag is set. +// The contents of all files is append to the input string + +int readDir(QFileInfo *fi, + FileNameList *fnList, + FileNameDict *fnDict, + StringDict *exclDict, + QStrList *patList, + QStrList *exclPatList, + StringList *resultList, + StringDict *resultDict, + bool errorIfNotExist, + bool recursive, + QDict<void> *killDict + ) +{ + QString dirName = fi->absFilePath(); + if (fi->isSymLink()) + { + dirName = resolveSymlink(dirName.data()); + if (dirName.isEmpty()) return 0; // recusive symlink + if (g_pathsVisited.find(dirName)) return 0; // already visited path + g_pathsVisited.insert(dirName,(void*)0x8); + } + QDir dir(dirName); + dir.setFilter( QDir::Files | QDir::Dirs | QDir::Hidden ); + int totalSize=0; + msg("Searching for files in directory %s\n", fi->absFilePath().data()); + //printf("killDict=%p count=%d\n",killDict,killDict->count()); + + const QFileInfoList *list = dir.entryInfoList(); + if (list) + { + QFileInfoListIterator it( *list ); + QFileInfo *cfi; + + while ((cfi=it.current())) + { + if (exclDict==0 || exclDict->find(cfi->absFilePath())==0) + { // file should not be excluded + //printf("killDict->find(%s)\n",cfi->absFilePath().data()); + if (!cfi->exists() || !cfi->isReadable()) + { + if (errorIfNotExist) + { + err("warning: source %s is not a readable file or directory... skipping.\n",cfi->absFilePath().data()); + } + } + else if (cfi->isFile() && + (!Config_getBool("EXCLUDE_SYMLINKS") || !cfi->isSymLink()) && + (patList==0 || patternMatch(*cfi,patList)) && + !patternMatch(*cfi,exclPatList) && + (killDict==0 || killDict->find(cfi->absFilePath())==0) + ) + { + totalSize+=cfi->size()+cfi->absFilePath().length()+4; + QCString name=convertToQCString(cfi->fileName()); + //printf("New file %s\n",name.data()); + if (fnDict) + { + FileDef *fd=new FileDef(cfi->dirPath()+"/",name); + FileName *fn=0; + if (!name.isEmpty() && (fn=(*fnDict)[name])) + { + fn->append(fd); + } + else + { + fn = new FileName(cfi->absFilePath(),name); + fn->append(fd); + if (fnList) fnList->inSort(fn); + fnDict->insert(name,fn); + } + } + QCString *rs=0; + if (resultList || resultDict) + { + rs=new QCString(cfi->absFilePath()); + } + if (resultList) resultList->append(rs); + if (resultDict) resultDict->insert(cfi->absFilePath(),rs); + if (killDict) killDict->insert(cfi->absFilePath(),(void *)0x8); + } + else if (recursive && + (!Config_getBool("EXCLUDE_SYMLINKS") || !cfi->isSymLink()) && + cfi->isDir() && cfi->fileName()!="." && + !patternMatch(*cfi,exclPatList) && + cfi->fileName()!="..") + { + cfi->setFile(cfi->absFilePath()); + totalSize+=readDir(cfi,fnList,fnDict,exclDict, + patList,exclPatList,resultList,resultDict,errorIfNotExist, + recursive,killDict); + } + } + ++it; + } + } + return totalSize; +} + + +//---------------------------------------------------------------------------- +// read a file or all files in a directory and append their contents to the +// input string. The names of the files are appended to the `fiList' list. + +int readFileOrDirectory(const char *s, + FileNameList *fnList, + FileNameDict *fnDict, + StringDict *exclDict, + QStrList *patList, + QStrList *exclPatList, + StringList *resultList, + StringDict *resultDict, + bool recursive, + bool errorIfNotExist, + QDict<void> *killDict + ) +{ + //printf("killDict=%p count=%d\n",killDict,killDict->count()); + // strip trailing slashes + if (s==0) return 0; + QCString fs = s; + char lc = fs.at(fs.length()-1); + if (lc=='/' || lc=='\\') fs = fs.left(fs.length()-1); + + QFileInfo fi(fs); + //printf("readFileOrDirectory(%s)\n",s); + int totalSize=0; + { + if (exclDict==0 || exclDict->find(fi.absFilePath())==0) + { + if (!fi.exists() || !fi.isReadable()) + { + if (errorIfNotExist) + { + err("warning: source %s is not a readable file or directory... skipping.\n",s); + } + } + else if (!Config_getBool("EXCLUDE_SYMLINKS") || !fi.isSymLink()) + { + if (fi.isFile()) + { + //printf("killDict->find(%s)\n",fi.absFilePath().data()); + if (killDict==0 || killDict->find(fi.absFilePath())==0) + { + totalSize+=fi.size()+fi.absFilePath().length()+4; //readFile(&fi,fiList,input); + //fiList->inSort(new FileInfo(fi)); + QCString name=convertToQCString(fi.fileName()); + //printf("New file %s\n",name.data()); + if (fnDict) + { + FileDef *fd=new FileDef(fi.dirPath(TRUE)+"/",name); + FileName *fn=0; + if (!name.isEmpty() && (fn=(*fnDict)[name])) + { + fn->append(fd); + } + else + { + fn = new FileName(fi.absFilePath(),name); + fn->append(fd); + if (fnList) fnList->inSort(fn); + fnDict->insert(name,fn); + } + } + QCString *rs=0; + if (resultList || resultDict) + { + rs=new QCString(fi.absFilePath()); + if (resultList) resultList->append(rs); + if (resultDict) resultDict->insert(fi.absFilePath(),rs); + } + + if (killDict) killDict->insert(fi.absFilePath(),(void *)0x8); + } + } + else if (fi.isDir()) // readable dir + { + totalSize+=readDir(&fi,fnList,fnDict,exclDict,patList, + exclPatList,resultList,resultDict,errorIfNotExist, + recursive,killDict); + } + } + } + } + return totalSize; +} + +//---------------------------------------------------------------------------- + +void readFormulaRepository() +{ + QFile f(Config_getString("HTML_OUTPUT")+"/formula.repository"); + if (f.open(IO_ReadOnly)) // open repository + { + msg("Reading formula repository...\n"); + QTextStream t(&f); + QCString line; + while (!t.eof()) + { + line=t.readLine(); + int se=line.find(':'); // find name and text separator. + if (se==-1) + { + err("warning: formula.repository is corrupted!\n"); + break; + } + else + { + QCString formName = line.left(se); + QCString formText = line.right(line.length()-se-1); + Formula *f=new Formula(formText); + Doxygen::formulaList.append(f); + Doxygen::formulaDict.insert(formText,f); + Doxygen::formulaNameDict.insert(formName,f); + } + } + } +} + +//---------------------------------------------------------------------------- + +static void expandAliases() +{ + QDictIterator<QCString> adi(Doxygen::aliasDict); + QCString *s; + for (adi.toFirst();(s=adi.current());++adi) + { + *s = expandAlias(adi.currentKey(),*s); + } +} + +//---------------------------------------------------------------------------- + +static void escapeAliases() +{ + QDictIterator<QCString> adi(Doxygen::aliasDict); + QCString *s; + for (adi.toFirst();(s=adi.current());++adi) + { + QCString value=*s,newValue; + int in,p=0; + // for each \n in the alias command value + while ((in=value.find("\\n",p))!=-1) + { + newValue+=value.mid(p,in-p); + // expand \n's except if \n is part of a built-in command. + if (value.mid(in,5)!="\\note" && + value.mid(in,5)!="\\name" && + value.mid(in,10)!="\\namespace" && + value.mid(in,14)!="\\nosubgrouping" + ) + { + newValue+="\\_linebr "; + } + else + { + newValue+="\\n"; + } + p=in+2; + } + newValue+=value.mid(p,value.length()-p); + *s=newValue; + //printf("Alias %s has value %s\n",adi.currentKey().data(),s->data()); + } +} + +//---------------------------------------------------------------------------- + +void readAliases() +{ + // add aliases to a dictionary + Doxygen::aliasDict.setAutoDelete(TRUE); + QStrList &aliasList = Config_getList("ALIASES"); + const char *s=aliasList.first(); + while (s) + { + if (Doxygen::aliasDict[s]==0) + { + QCString alias=s; + int i=alias.find('='); + if (i>0) + { + QCString name=alias.left(i).stripWhiteSpace(); + QCString value=alias.right(alias.length()-i-1); + //printf("Alias: found name=`%s' value=`%s'\n",name.data(),value.data()); + if (!name.isEmpty()) + { + QCString *dn=Doxygen::aliasDict[name]; + if (dn==0) // insert new alias + { + Doxygen::aliasDict.insert(name,new QCString(value)); + } + else // overwrite previous alias + { + *dn=value; + } + } + } + } + s=aliasList.next(); + } + expandAliases(); + escapeAliases(); +} + +//---------------------------------------------------------------------------- + +static void dumpSymbol(FTextStream &t,Definition *d) +{ + QCString anchor; + if (d->definitionType()==Definition::TypeMember) + { + MemberDef *md = (MemberDef *)d; + anchor=":"+md->anchor(); + } + QCString scope; + if (d->getOuterScope() && d->getOuterScope()!=Doxygen::globalScope) + { + scope = d->getOuterScope()->getOutputFileBase()+Doxygen::htmlFileExtension; + } + t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('" + << d->getOutputFileBase()+Doxygen::htmlFileExtension+anchor << "','" + << scope << "','" + << d->name() << "','" + << d->getDefFileName() << "','" + << d->getDefLine() + << "');" << endl; +} + +static void dumpSymbolMap() +{ + QFile f("symbols.sql"); + if (f.open(IO_WriteOnly)) + { + FTextStream t(&f); + QDictIterator<DefinitionIntf> di(*Doxygen::symbolMap); + DefinitionIntf *intf; + for (;(intf=di.current());++di) + { + if (intf->definitionType()==DefinitionIntf::TypeSymbolList) // list of symbols + { + DefinitionListIterator dli(*(DefinitionList*)intf); + Definition *d; + // for each symbol + for (dli.toFirst();(d=dli.current());++dli) + { + dumpSymbol(t,d); + } + } + else // single symbol + { + Definition *d = (Definition *)intf; + if (d!=Doxygen::globalScope) dumpSymbol(t,d); + } + } + } +} + +//---------------------------------------------------------------------------- + +void dumpConfigAsXML() +{ + QFile f("config.xml"); + if (f.open(IO_WriteOnly)) + { + FTextStream t(&f); + Config::instance()->writeXML(t); + } +} + +//---------------------------------------------------------------------------- +// print the usage of doxygen + +static void usage(const char *name) +{ + msg("Doxygen version %s\nCopyright Dimitri van Heesch 1997-2011\n\n",versionString); + msg("You can use doxygen in a number of ways:\n\n"); + msg("1) Use doxygen to generate a template configuration file:\n"); + msg(" %s [-s] -g [configName]\n\n",name); + msg(" If - is used for configName doxygen will write to standard output.\n\n"); + msg("2) Use doxygen to update an old configuration file:\n"); + msg(" %s [-s] -u [configName]\n\n",name); + msg("3) Use doxygen to generate documentation using an existing "); + msg("configuration file:\n"); + msg(" %s [configName]\n\n",name); + msg(" If - is used for configName doxygen will read from standard input.\n\n"); + msg("4) Use doxygen to generate a template file controlling the layout of the\n"); + msg(" generated documentation:\n"); + msg(" %s -l layoutFileName.xml\n\n",name); + msg("5) Use doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"); + msg(" RTF: %s -w rtf styleSheetFile\n",name); + msg(" HTML: %s -w html headerFile footerFile styleSheetFile [configFile]\n",name); + msg(" LaTeX: %s -w latex headerFile footerFile styleSheetFile [configFile]\n\n",name); + msg("6) Use doxygen to generate an rtf extensions file\n"); + msg(" RTF: %s -e rtf extensionsFile\n\n",name); + msg("If -s is specified the comments in the config file will be omitted.\n"); + msg("If configName is omitted `Doxyfile' will be used as a default.\n\n"); + exit(1); +} + +//---------------------------------------------------------------------------- +// read the argument of option `c' from the comment argument list and +// update the option index `optind'. + +static const char *getArg(int argc,char **argv,int &optind) +{ + char *s=0; + if (strlen(&argv[optind][2])>0) + s=&argv[optind][2]; + else if (optind+1<argc && argv[optind+1][0]!='-') + s=argv[++optind]; + return s; +} + +//---------------------------------------------------------------------------- + +extern void commentScanTest(); + +void initDoxygen() +{ + setlocale(LC_ALL,""); + setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8 + setlocale(LC_NUMERIC,"C"); + + //Doxygen::symbolMap->setAutoDelete(TRUE); + + Doxygen::runningTime.start(); + initPreprocessor(); + + Doxygen::parserManager = new ParserManager; + Doxygen::parserManager->registerParser("c", new CLanguageScanner, TRUE); + Doxygen::parserManager->registerParser("python", new PythonLanguageScanner); + Doxygen::parserManager->registerParser("fortran", new FortranLanguageScanner); + Doxygen::parserManager->registerParser("vhdl", new VHDLLanguageScanner); + Doxygen::parserManager->registerParser("dbusxml", new DBusXMLScanner); + + // register any additional parsers here... + + initDefaultExtensionMapping(); + initClassMemberIndices(); + initNamespaceMemberIndices(); + initFileMemberIndices(); + + Doxygen::symbolMap = new QDict<DefinitionIntf>(1000); + Doxygen::inputNameList = new FileNameList; + Doxygen::inputNameList->setAutoDelete(TRUE); + Doxygen::memberNameSDict = new MemberNameSDict(10000); + Doxygen::memberNameSDict->setAutoDelete(TRUE); + Doxygen::functionNameSDict = new MemberNameSDict(10000); + Doxygen::functionNameSDict->setAutoDelete(TRUE); + Doxygen::groupSDict = new GroupSDict(17); + Doxygen::groupSDict->setAutoDelete(TRUE); + Doxygen::globalScope = new NamespaceDef("<globalScope>",1,"<globalScope>"); + Doxygen::namespaceSDict = new NamespaceSDict(20); + Doxygen::namespaceSDict->setAutoDelete(TRUE); + Doxygen::classSDict = new ClassSDict(1009); + Doxygen::classSDict->setAutoDelete(TRUE); + Doxygen::hiddenClasses = new ClassSDict(257); + Doxygen::hiddenClasses->setAutoDelete(TRUE); + Doxygen::directories = new DirSDict(17); + Doxygen::directories->setAutoDelete(TRUE); + Doxygen::pageSDict = new PageSDict(1009); // all doc pages + Doxygen::pageSDict->setAutoDelete(TRUE); + Doxygen::exampleSDict = new PageSDict(1009); // all examples + Doxygen::exampleSDict->setAutoDelete(TRUE); + Doxygen::inputNameDict = new FileNameDict(10007); + Doxygen::includeNameDict = new FileNameDict(10007); + Doxygen::exampleNameDict = new FileNameDict(1009); + Doxygen::exampleNameDict->setAutoDelete(TRUE); + Doxygen::imageNameDict = new FileNameDict(257); + Doxygen::dotFileNameDict = new FileNameDict(257); + Doxygen::mscFileNameDict = new FileNameDict(257); + Doxygen::sectionDict.setAutoDelete(TRUE); + Doxygen::memGrpInfoDict.setAutoDelete(TRUE); + Doxygen::tagDestinationDict.setAutoDelete(TRUE); + Doxygen::lookupCache.setAutoDelete(TRUE); + Doxygen::dirRelations.setAutoDelete(TRUE); +} + +void cleanUpDoxygen() +{ + delete Doxygen::inputNameDict; + delete Doxygen::includeNameDict; + delete Doxygen::exampleNameDict; + delete Doxygen::imageNameDict; + delete Doxygen::dotFileNameDict; + delete Doxygen::mscFileNameDict; + delete Doxygen::mainPage; + delete Doxygen::pageSDict; + delete Doxygen::exampleSDict; + delete Doxygen::globalScope; + delete Doxygen::xrefLists; + delete Doxygen::parserManager; + cleanUpPreprocessor(); + delete theTranslator; + delete g_outputList; + Mappers::freeMappers(); + codeFreeScanner(); + + if (Doxygen::symbolMap) + { + // iterate through Doxygen::symbolMap and delete all + // DefinitionList objects, since they have no owner + QDictIterator<DefinitionIntf> dli(*Doxygen::symbolMap); + DefinitionIntf *di; + for (dli.toFirst();(di=dli.current());) + { + if (di->definitionType()==DefinitionIntf::TypeSymbolList) + { + DefinitionIntf *tmp = Doxygen::symbolMap->take(dli.currentKey()); + delete (DefinitionList *)tmp; + } + else + { + ++dli; + } + } + } + + delete Doxygen::inputNameList; + delete Doxygen::memberNameSDict; + delete Doxygen::functionNameSDict; + delete Doxygen::groupSDict; + delete Doxygen::classSDict; + delete Doxygen::hiddenClasses; + delete Doxygen::namespaceSDict; + delete Doxygen::directories; + + //delete Doxygen::symbolMap; <- we cannot do this unless all static lists + // (such as Doxygen::namespaceSDict) + // with objects based on Definition are made + // dynamic first +} + +void readConfiguration(int argc, char **argv) +{ + /************************************************************************** + * Handle arguments * + **************************************************************************/ + + int optind=1; + const char *configName=0; + const char *layoutName=0; + const char *debugLabel; + const char *formatName; + bool genConfig=FALSE; + bool shortList=FALSE; + bool updateConfig=FALSE; + bool genLayout=FALSE; + while (optind<argc && argv[optind][0]=='-' && + (isalpha(argv[optind][1]) || argv[optind][1]=='?' || + argv[optind][1]=='-') + ) + { + switch(argv[optind][1]) + { + case 'g': + genConfig=TRUE; + configName=getArg(argc,argv,optind); + if (strcmp(argv[optind+1],"-")==0) + { configName="-"; optind++; } + if (!configName) + { configName="Doxyfile"; } + break; + case 'l': + genLayout=TRUE; + layoutName=getArg(argc,argv,optind); + if (!layoutName) + { layoutName="DoxygenLayout.xml"; } + break; + case 'd': + debugLabel=getArg(argc,argv,optind); + Debug::setFlag(debugLabel); + break; + case 's': + shortList=TRUE; + break; + case 'u': + updateConfig=TRUE; + break; + case 'e': + formatName=getArg(argc,argv,optind); + if (!formatName) + { + err("error: option -e is missing format specifier rtf.\n"); + cleanUpDoxygen(); + exit(1); + } + if (stricmp(formatName,"rtf")==0) + { + if (optind+1>=argc) + { + err("error: option \"-e rtf\" is missing an extensions file name\n"); + cleanUpDoxygen(); + exit(1); + } + QFile f; + if (openOutputFile(argv[optind+1],f)) + { + RTFGenerator::writeExtensionsFile(f); + } + cleanUpDoxygen(); + exit(1); + } + err("error: option \"-e\" has invalid format specifier.\n"); + cleanUpDoxygen(); + exit(1); + break; + case 'w': + formatName=getArg(argc,argv,optind); + if (!formatName) + { + err("error: option -w is missing format specifier rtf, html or latex\n"); + cleanUpDoxygen(); + exit(1); + } + if (stricmp(formatName,"rtf")==0) + { + if (optind+1>=argc) + { + err("error: option \"-w rtf\" is missing a style sheet file name\n"); + cleanUpDoxygen(); + exit(1); + } + QFile f; + if (openOutputFile(argv[optind+1],f)) + { + RTFGenerator::writeStyleSheetFile(f); + } + cleanUpDoxygen(); + exit(1); + } + else if (stricmp(formatName,"html")==0) + { + if (optind+4<argc || QFileInfo("Doxyfile").exists()) + { + QCString df = optind+4<argc ? argv[optind+4] : QCString("Doxyfile"); + if (!Config::instance()->parse(df)) + { + err("error opening or reading configuration file %s!\n",argv[optind+4]); + cleanUpDoxygen(); + exit(1); + } + Config::instance()->substituteEnvironmentVars(); + Config::instance()->convertStrToVal(); + // avoid bootstrapping issues when the config file already + // refers to the files that we are supposed to parse. + Config_getString("HTML_HEADER")=""; + Config_getString("HTML_FOOTER")=""; + Config::instance()->check(); + } + else + { + Config::instance()->init(); + } + if (optind+3>=argc) + { + err("error: option \"-w html\" does not have enough arguments\n"); + cleanUpDoxygen(); + exit(1); + } + + QCString outputLanguage=Config_getEnum("OUTPUT_LANGUAGE"); + if (!setTranslator(outputLanguage)) + { + err("warning: Output language %s not supported! Using English instead.\n", outputLanguage.data()); + } + + QFile f; + if (openOutputFile(argv[optind+1],f)) + { + HtmlGenerator::writeHeaderFile(f, argv[optind+3]); + } + f.close(); + if (openOutputFile(argv[optind+2],f)) + { + HtmlGenerator::writeFooterFile(f); + } + f.close(); + if (openOutputFile(argv[optind+3],f)) + { + HtmlGenerator::writeStyleSheetFile(f); + } + cleanUpDoxygen(); + exit(0); + } + else if (stricmp(formatName,"latex")==0) + { + if (optind+4<argc) // use config file to get settings + { + if (!Config::instance()->parse(argv[optind+4])) + { + err("error opening or reading configuration file %s!\n",argv[optind+4]); + exit(1); + } + Config::instance()->substituteEnvironmentVars(); + Config::instance()->convertStrToVal(); + Config_getString("LATEX_HEADER")=""; + Config::instance()->check(); + } + else // use default config + { + Config::instance()->init(); + } + if (optind+3>=argc) + { + err("error: option \"-w latex\" does not have enough arguments\n"); + cleanUpDoxygen(); + exit(1); + } + + QCString outputLanguage=Config_getEnum("OUTPUT_LANGUAGE"); + if (!setTranslator(outputLanguage)) + { + err("warning: Output language %s not supported! Using English instead.\n", outputLanguage.data()); + } + + QFile f; + if (openOutputFile(argv[optind+1],f)) + { + LatexGenerator::writeHeaderFile(f); + } + f.close(); + if (openOutputFile(argv[optind+2],f)) + { + LatexGenerator::writeFooterFile(f); + } + f.close(); + if (openOutputFile(argv[optind+3],f)) + { + LatexGenerator::writeStyleSheetFile(f); + } + cleanUpDoxygen(); + exit(0); + } + else + { + err("error: Illegal format specifier %s: should be one of rtf, html, or latex\n",formatName); + cleanUpDoxygen(); + exit(1); + } + break; + case 'm': + g_dumpSymbolMap = TRUE; + break; + case 'x': + g_dumpConfigAsXML = TRUE; + break; + case '-': + if (strcmp(&argv[optind][2],"help")==0) + { + usage(argv[0]); + } + else if (strcmp(&argv[optind][2],"version")==0) + { + msg("%s\n",versionString); + cleanUpDoxygen(); + exit(0); + } + break; + case 'b': + setvbuf(stdout,NULL,_IONBF,0); + Doxygen::outputToWizard=TRUE; + break; + case 'h': + case '?': + usage(argv[0]); + break; + default: + err("Unknown option -%c\n",argv[optind][1]); + usage(argv[0]); + } + optind++; + } + + /************************************************************************** + * Parse or generate the config file * + **************************************************************************/ + + Config::instance()->init(); + + if (genConfig) + { + if (g_dumpConfigAsXML) + { + checkConfiguration(); + generateConfigFile(configName,shortList); + dumpConfigAsXML(); + exit(0); + } + else + { + generateConfigFile(configName,shortList); + } + cleanUpDoxygen(); + exit(0); + } + if (genLayout) + { + writeDefaultLayoutFile(layoutName); + cleanUpDoxygen(); + exit(0); + } + + QFileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile"); + if (optind>=argc) + { + if (configFileInfo1.exists()) + { + configName="Doxyfile"; + } + else if (configFileInfo2.exists()) + { + configName="doxyfile"; + } + else + { + err("Doxyfile not found and no input file specified!\n"); + usage(argv[0]); + } + } + else + { + QFileInfo fi(argv[optind]); + if (fi.exists() || strcmp(argv[optind],"-")==0) + { + configName=argv[optind]; + } + else + { + err("error: configuration file %s not found!\n",argv[optind]); + usage(argv[0]); + } + } + + + if (!Config::instance()->parse(configName)) + { + err("error: could not open or read configuration file %s!\n",configName); + cleanUpDoxygen(); + exit(1); + } + + if (updateConfig) + { + generateConfigFile(configName,shortList,TRUE); + cleanUpDoxygen(); + exit(0); + } + + /* Perlmod wants to know the path to the config file.*/ + QFileInfo configFileInfo(configName); + setPerlModDoxyfile(configFileInfo.absFilePath().data()); + +} + +/** check and resolve config options */ +void checkConfiguration() +{ + + Config::instance()->substituteEnvironmentVars(); + Config::instance()->convertStrToVal(); + Config::instance()->check(); + + initWarningFormat(); +} + +/** adjust globals that depend on configuration settings. */ +void adjustConfiguration() +{ + QCString outputLanguage=Config_getEnum("OUTPUT_LANGUAGE"); + if (!setTranslator(outputLanguage)) + { + err("warning: Output language %s not supported! Using English instead.\n", + outputLanguage.data()); + } + QStrList &includePath = Config_getList("INCLUDE_PATH"); + char *s=includePath.first(); + while (s) + { + QFileInfo fi(s); + addSearchDir(fi.absFilePath()); + s=includePath.next(); + } + + /* Set the global html file extension. */ + Doxygen::htmlFileExtension = Config_getString("HTML_FILE_EXTENSION"); + + + Doxygen::xrefLists->setAutoDelete(TRUE); + + Doxygen::parseSourcesNeeded = Config_getBool("CALL_GRAPH") || + Config_getBool("CALLER_GRAPH") || + Config_getBool("REFERENCES_RELATION") || + Config_getBool("REFERENCED_BY_RELATION"); + + /************************************************************************** + * Add custom extension mappings + **************************************************************************/ + + QStrList &extMaps = Config_getList("EXTENSION_MAPPING"); + char *mapping = extMaps.first(); + while (mapping) + { + QCString mapStr = mapping; + int i; + if ((i=mapStr.find('='))!=-1) + { + QCString ext=mapStr.left(i).stripWhiteSpace().lower(); + QCString language=mapStr.mid(i+1).stripWhiteSpace().lower(); + if (!updateLanguageMapping(ext,language)) + { + err("Failed to map file extension '%s' to unsupported language '%s'.\n" + "Check the EXTENSION_MAPPING setting in the config file.\n", + ext.data(),language.data()); + } + else + { + msg("Adding custom extension mapping: .%s will be treated as language %s\n", + ext.data(),language.data()); + } + } + mapping = extMaps.next(); + } + + + // add predefined macro name to a dictionary + QStrList &expandAsDefinedList =Config_getList("EXPAND_AS_DEFINED"); + s=expandAsDefinedList.first(); + while (s) + { + if (Doxygen::expandAsDefinedDict[s]==0) + { + Doxygen::expandAsDefinedDict.insert(s,(void *)666); + } + s=expandAsDefinedList.next(); + } + + // read aliases and store them in a dictionary + readAliases(); + + // store number of spaces in a tab into Doxygen::spaces + int &tabSize = Config_getInt("TAB_SIZE"); + Doxygen::spaces.resize(tabSize+1); + int sp;for (sp=0;sp<tabSize;sp++) Doxygen::spaces.at(sp)=' '; + Doxygen::spaces.at(tabSize)='\0'; +} + +#ifdef HAS_SIGNALS +static void stopDoxygen(int) +{ + QDir thisDir; + msg("Cleaning up...\n"); + if (!Doxygen::entryDBFileName.isEmpty()) + { + thisDir.remove(Doxygen::entryDBFileName); + } + if (!Doxygen::objDBFileName.isEmpty()) + { + thisDir.remove(Doxygen::objDBFileName); + } + exit(1); +} +#endif + +static void exitDoxygen() +{ + if (!g_successfulRun) // premature exit + { + QDir thisDir; + msg("Exiting...\n"); + if (!Doxygen::entryDBFileName.isEmpty()) + { + thisDir.remove(Doxygen::entryDBFileName); + } + if (!Doxygen::objDBFileName.isEmpty()) + { + thisDir.remove(Doxygen::objDBFileName); + } + } +} + +static QCString createOutputDirectory(const QCString &baseDirName, + const char *formatDirOption, + const char *defaultDirName) +{ + // Note the & on the next line, we modify the formatDirOption! + QCString &formatDirName = Config_getString(formatDirOption); + if (formatDirName.isEmpty()) + { + formatDirName = baseDirName + defaultDirName; + } + else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':')) + { + formatDirName.prepend(baseDirName+'/'); + } + QDir formatDir(formatDirName); + if (!formatDir.exists() && !formatDir.mkdir(formatDirName)) + { + err("Could not create output directory %s\n", formatDirName.data()); + cleanUpDoxygen(); + exit(1); + } + return formatDirName; +} + +static QCString getQchFileName() +{ + QCString const & qchFile = Config_getString("QCH_FILE"); + if (!qchFile.isEmpty()) + { + return qchFile; + } + + QCString const & projectName = Config_getString("PROJECT_NAME"); + QCString const & versionText = Config_getString("PROJECT_NUMBER"); + + return QCString("../qch/") + + (projectName.isEmpty() ? QCString("index") : projectName) + + (versionText.isEmpty() ? QCString("") : QCString("-") + versionText) + + QCString(".qch"); +} + +void searchInputFiles(StringList &inputFiles) +{ + QStrList &exclPatterns = Config_getList("EXCLUDE_PATTERNS"); + bool alwaysRecursive = Config_getBool("RECURSIVE"); + StringDict excludeNameDict(1009); + excludeNameDict.setAutoDelete(TRUE); + + // gather names of all files in the include path + msg("Searching for include files...\n"); + QStrList &includePathList = Config_getList("INCLUDE_PATH"); + char *s=includePathList.first(); + while (s) + { + QStrList &pl = Config_getList("INCLUDE_FILE_PATTERNS"); + if (pl.count()==0) + { + pl = Config_getList("FILE_PATTERNS"); + } + readFileOrDirectory(s,0,Doxygen::includeNameDict,0,&pl, + &exclPatterns,0,0, + alwaysRecursive); + s=includePathList.next(); + } + + msg("Searching for example files...\n"); + QStrList &examplePathList = Config_getList("EXAMPLE_PATH"); + s=examplePathList.first(); + while (s) + { + readFileOrDirectory(s,0,Doxygen::exampleNameDict,0, + &Config_getList("EXAMPLE_PATTERNS"), + 0,0,0, + (alwaysRecursive || Config_getBool("EXAMPLE_RECURSIVE"))); + s=examplePathList.next(); + } + + msg("Searching for images...\n"); + QStrList &imagePathList=Config_getList("IMAGE_PATH"); + s=imagePathList.first(); + while (s) + { + readFileOrDirectory(s,0,Doxygen::imageNameDict,0,0, + 0,0,0, + alwaysRecursive); + s=imagePathList.next(); + } + + msg("Searching for dot files...\n"); + QStrList &dotFileList=Config_getList("DOTFILE_DIRS"); + s=dotFileList.first(); + while (s) + { + readFileOrDirectory(s,0,Doxygen::dotFileNameDict,0,0, + 0,0,0, + alwaysRecursive); + s=dotFileList.next(); + } + + msg("Searching for msc files...\n"); + QStrList &mscFileList=Config_getList("MSCFILE_DIRS"); + s=mscFileList.first(); + while (s) + { + readFileOrDirectory(s,0,Doxygen::mscFileNameDict,0,0, + 0,0,0, + alwaysRecursive); + s=dotFileList.next(); + } + + + msg("Searching for files to exclude\n"); + QStrList &excludeList = Config_getList("EXCLUDE"); + s=excludeList.first(); + while (s) + { + readFileOrDirectory(s,0,0,0,&Config_getList("FILE_PATTERNS"), + 0,0,&excludeNameDict, + alwaysRecursive, + FALSE); + s=excludeList.next(); + } + + /************************************************************************** + * Determine Input Files * + **************************************************************************/ + + msg("Searching for files to process...\n"); + QDict<void> *killDict = new QDict<void>(10007); + int inputSize=0; + QStrList &inputList=Config_getList("INPUT"); + inputFiles.setAutoDelete(TRUE); + s=inputList.first(); + while (s) + { + QCString path=s; + uint l = path.length(); + // strip trailing slashes + if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1); + + inputSize+=readFileOrDirectory( + path, + Doxygen::inputNameList, + Doxygen::inputNameDict, + &excludeNameDict, + &Config_getList("FILE_PATTERNS"), + &exclPatterns, + &inputFiles,0, + alwaysRecursive, + TRUE, + killDict); + s=inputList.next(); + } + delete killDict; +} + + +void parseInput() +{ + atexit(exitDoxygen); + + + /************************************************************************** + * Make sure the output directory exists + **************************************************************************/ + QCString &outputDirectory = Config_getString("OUTPUT_DIRECTORY"); + if (outputDirectory.isEmpty()) + { + outputDirectory=QDir::currentDirPath(); + } + else + { + QDir dir(outputDirectory); + if (!dir.exists()) + { + dir.setPath(QDir::currentDirPath()); + if (!dir.mkdir(outputDirectory)) + { + err("error: tag OUTPUT_DIRECTORY: Output directory `%s' does not " + "exist and cannot be created\n",outputDirectory.data()); + cleanUpDoxygen(); + exit(1); + } + else if (!Config_getBool("QUIET")) + { + err("Notice: Output directory `%s' does not exist. " + "I have created it for you.\n", outputDirectory.data()); + } + dir.cd(outputDirectory); + } + outputDirectory=dir.absPath(); + } + + /************************************************************************** + * Initialize global lists and dictionaries + **************************************************************************/ + + int cacheSize = Config_getInt("SYMBOL_CACHE_SIZE"); + if (cacheSize<0) cacheSize=0; + if (cacheSize>9) cacheSize=9; + Doxygen::symbolCache = new ObjCache(16+cacheSize); // 16 -> room for 65536 elements, + // ~2.0 MByte "overhead" + Doxygen::symbolStorage = new Store; + +#ifdef HAS_SIGNALS + signal(SIGINT, stopDoxygen); +#endif + + uint pid = portable_pid(); + Doxygen::objDBFileName.sprintf("doxygen_objdb_%d.tmp",pid); + Doxygen::objDBFileName.prepend(outputDirectory+"/"); + Doxygen::entryDBFileName.sprintf("doxygen_entrydb_%d.tmp",pid); + Doxygen::entryDBFileName.prepend(outputDirectory+"/"); + + if (Doxygen::symbolStorage->open(Doxygen::objDBFileName)==-1) + { + err("Failed to open temporary file %s\n",Doxygen::objDBFileName.data()); + exit(1); + } + + + /************************************************************************** + * Initialize some global constants + **************************************************************************/ + + g_compoundKeywordDict.insert("template class",(void *)8); + g_compoundKeywordDict.insert("template struct",(void *)8); + g_compoundKeywordDict.insert("class",(void *)8); + g_compoundKeywordDict.insert("struct",(void *)8); + g_compoundKeywordDict.insert("union",(void *)8); + g_compoundKeywordDict.insert("interface",(void *)8); + g_compoundKeywordDict.insert("exception",(void *)8); + + + /************************************************************************** + * Check/create output directorties * + **************************************************************************/ + + QCString htmlOutput; + bool &generateHtml = Config_getBool("GENERATE_HTML"); + if (generateHtml) + htmlOutput = createOutputDirectory(outputDirectory,"HTML_OUTPUT","/html"); + + QCString xmlOutput; + bool &generateXml = Config_getBool("GENERATE_XML"); + if (generateXml) + xmlOutput = createOutputDirectory(outputDirectory,"XML_OUTPUT","/xml"); + + QCString latexOutput; + bool &generateLatex = Config_getBool("GENERATE_LATEX"); + if (generateLatex) + latexOutput = createOutputDirectory(outputDirectory,"LATEX_OUTPUT","/latex"); + + QCString rtfOutput; + bool &generateRtf = Config_getBool("GENERATE_RTF"); + if (generateRtf) + rtfOutput = createOutputDirectory(outputDirectory,"RTF_OUTPUT","/rtf"); + + QCString manOutput; + bool &generateMan = Config_getBool("GENERATE_MAN"); + if (generateMan) + manOutput = createOutputDirectory(outputDirectory,"MAN_OUTPUT","/man"); + + + if (Config_getBool("HAVE_DOT")) + { + QCString curFontPath = Config_getString("DOT_FONTPATH"); + if (curFontPath.isEmpty()) + { + portable_getenv("DOTFONTPATH"); + QCString newFontPath = "."; + if (!curFontPath.isEmpty()) + { + newFontPath+=portable_pathListSeparator(); + newFontPath+=curFontPath; + } + portable_setenv("DOTFONTPATH",newFontPath); + } + else + { + portable_setenv("DOTFONTPATH",curFontPath); + } + } + + + + /************************************************************************** + * Handle layout file * + **************************************************************************/ + + LayoutDocManager::instance().init(); + QCString layoutFileName = Config_getString("LAYOUT_FILE"); + bool defaultLayoutUsed = FALSE; + if (layoutFileName.isEmpty()) + { + layoutFileName = "DoxygenLayout.xml"; + defaultLayoutUsed = TRUE; + } + + QFile layoutFile(layoutFileName); + if (layoutFile.open(IO_ReadOnly)) + { + msg("Parsing layout file %s...\n",layoutFileName.data()); + QTextStream t(&layoutFile); + t.setEncoding(QTextStream::Latin1); + LayoutDocManager::instance().parse(t); + } + else if (!defaultLayoutUsed) + { + err("warning: failed to open layout file '%s' for reading!\n",layoutFileName.data()); + } + + /************************************************************************** + * Read and preprocess input * + **************************************************************************/ + + // prevent search in the output directories + QStrList &exclPatterns = Config_getList("EXCLUDE_PATTERNS"); + if (generateHtml) exclPatterns.append(htmlOutput); + if (generateXml) exclPatterns.append(xmlOutput); + if (generateLatex) exclPatterns.append(latexOutput); + if (generateRtf) exclPatterns.append(rtfOutput); + if (generateMan) exclPatterns.append(manOutput); + + + searchInputFiles(g_inputFiles); + + // Notice: the order of the function calls below is very important! + + if (Config_getBool("GENERATE_HTML")) + { + readFormulaRepository(); + } + + /************************************************************************** + * Handle Tag Files * + **************************************************************************/ + + g_storage = new FileStorage; + g_storage->setName(Doxygen::entryDBFileName); + if (!g_storage->open(IO_WriteOnly)) + { + err("Failed to create temporary storage file %s\n", + Doxygen::entryDBFileName.data()); + exit(1); + } + Entry *root=new Entry; + EntryNav *rootNav = new EntryNav(0,root); + rootNav->setEntry(root); + msg("Reading and parsing tag files\n"); + + QStrList &tagFileList = Config_getList("TAGFILES"); + char *s=tagFileList.first(); + while (s) + { + readTagFile(root,s); + root->createNavigationIndex(rootNav,g_storage,0); + s=tagFileList.next(); + } + + /************************************************************************** + * Parse source files * + **************************************************************************/ + + parseFiles(root,rootNav); + g_storage->close(); + if (!g_storage->open(IO_ReadOnly)) + { + err("Failed to open temporary storage file %s for reading", + Doxygen::entryDBFileName.data()); + exit(1); + } + + //printNavTree(rootNav,0); + + // we are done with input scanning now, so free up the buffers used by flex + // (can be around 4MB) + preFreeScanner(); + scanFreeScanner(); + pyscanFreeScanner(); + + //delete rootNav; + //g_storage.close(); + //exit(1); + + /************************************************************************** + * Gather information * + **************************************************************************/ + + msg("Building group list...\n"); + buildGroupList(rootNav); + organizeSubGroups(rootNav); + + msg("Building directory list...\n"); + buildDirectories(); + findDirDocumentation(rootNav); + + if (Config_getBool("BUILTIN_STL_SUPPORT")) + { + addSTLClasses(rootNav); + } + + msg("Building namespace list...\n"); + buildNamespaceList(rootNav); + findUsingDirectives(rootNav); + + msg("Building file list...\n"); + buildFileList(rootNav); + //generateFileTree(); + + msg("Building class list...\n"); + buildClassList(rootNav); + + msg("Associating documentation with classes...\n"); + buildClassDocList(rootNav); + + // build list of using declarations here (global list) + buildListOfUsingDecls(rootNav); + + msg("Computing nesting relations for classes...\n"); + resolveClassNestingRelations(); + distributeClassGroupRelations(); + + // calling buildClassList may result in cached relations that + // become invalid after resolveClassNestingRelations(), that's why + // we need to clear the cache here + Doxygen::lookupCache.clear(); + // we don't need the list of using declaration anymore + g_usingDeclarations.clear(); + + msg("Building example list...\n"); + buildExampleList(rootNav); + + msg("Searching for enumerations...\n"); + findEnums(rootNav); + + // Since buildVarList calls isVarWithConstructor + // and this calls getResolvedClass we need to process + // typedefs first so the relations between classes via typedefs + // are properly resolved. See bug 536385 for an example. + msg("Searching for documented typedefs...\n"); + buildTypedefList(rootNav); + + msg("Searching for members imported via using declarations...\n"); + findUsingDeclImports(rootNav); + // this should be after buildTypedefList in order to properly import + // used typedefs + findUsingDeclarations(rootNav); + + msg("Searching for included using directives...\n"); + findIncludedUsingDirectives(); + + msg("Searching for documented variables...\n"); + buildVarList(rootNav); + + msg("Building member list...\n"); // using class info only ! + buildFunctionList(rootNav); + + msg("Searching for friends...\n"); + findFriends(); + + msg("Searching for documented defines...\n"); + findDefineDocumentation(rootNav); + + findClassEntries(rootNav); + msg("Computing class inheritance relations...\n"); + findInheritedTemplateInstances(); + msg("Computing class usage relations...\n"); + findUsedTemplateInstances(); + + msg("Flushing cached template relations that have become invalid...\n"); + flushCachedTemplateRelations(); + + msg("Creating members for template instances...\n"); + createTemplateInstanceMembers(); + + msg("Computing class relations...\n"); + computeTemplateClassRelations(); + flushUnresolvedRelations(); + + computeClassRelations(); + + if (Config_getBool("OPTIMIZE_OUTPUT_VHDL")) + VhdlDocGen::computeVhdlComponentRelations(); + + g_classEntries.clear(); + + msg("Add enum values to enums...\n"); + addEnumValuesToEnums(rootNav); + findEnumDocumentation(rootNav); + + msg("Searching for member function documentation...\n"); + findObjCMethodDefinitions(rootNav); + findMemberDocumentation(rootNav); // may introduce new members ! + + transferRelatedFunctionDocumentation(); + transferFunctionDocumentation(); + + msg("Building page list...\n"); + buildPageList(rootNav); + + msg("Search for main page...\n"); + findMainPage(rootNav); + + msg("Computing page relations...\n"); + computePageRelations(rootNav); + checkPageRelations(); + + msg("Determining the scope of groups...\n"); + findGroupScope(rootNav); + + msg("Sorting lists...\n"); + Doxygen::memberNameSDict->sort(); + Doxygen::functionNameSDict->sort(); + Doxygen::hiddenClasses->sort(); + Doxygen::classSDict->sort(); + + msg("Freeing entry tree\n"); + delete rootNav; + g_storage->close(); + delete g_storage; + g_storage=0; + + QDir thisDir; + thisDir.remove(Doxygen::entryDBFileName); + + msg("Determining which enums are documented\n"); + findDocumentedEnumValues(); + + msg("Computing member relations...\n"); + computeMemberRelations(); + + msg("Building full member lists recursively...\n"); + buildCompleteMemberLists(); + + msg("Adding members to member groups.\n"); + addMembersToMemberGroup(); + + if (Config_getBool("DISTRIBUTE_GROUP_DOC")) + { + msg("Distributing member group documentation.\n"); + distributeMemberGroupDocumentation(); + } + + msg("Computing member references...\n"); + computeMemberReferences(); + + if (Config_getBool("INHERIT_DOCS")) + { + msg("Inheriting documentation...\n"); + inheritDocumentation(); + } + + // compute the shortest possible names of all files + // without loosing the uniqueness of the file names. + msg("Generating disk names...\n"); + Doxygen::inputNameList->generateDiskNames(); + + msg("Adding source references...\n"); + addSourceReferences(); + + msg("Adding xrefitems...\n"); + addListReferences(); + generateXRefPages(); + + msg("Sorting member lists...\n"); + sortMemberLists(); + + if (Config_getBool("SHOW_DIRECTORIES") && Config_getBool("DIRECTORY_GRAPH")) + { + msg("Computing dependencies between directories...\n"); + computeDirDependencies(); + } + + msg("Counting data structures...\n"); + countDataStructures(); + + msg("Resolving user defined references...\n"); + resolveUserReferences(); + + msg("Finding anchors and sections in the documentation...\n"); + findSectionsInDocumentation(); + + transferFunctionReferences(); + + msg("Combining using relations...\n"); + combineUsingRelations(); + + msg("Adding members to index pages...\n"); + addMembersToIndex(); +} + +void generateOutput() +{ + /************************************************************************** + * Initialize output generators * + **************************************************************************/ + + //// dump all symbols + //SDict<DefinitionList>::Iterator sdi(Doxygen::symbolMap); + //DefinitionList *dl; + //for (sdi.toFirst();(dl=sdi.current());++sdi) + //{ + // DefinitionListIterator dli(*dl); + // Definition *d; + // printf("Symbol: "); + // for (dli.toFirst();(d=dli.current());++dli) + // { + // printf("%s ",d->qualifiedName().data()); + // } + // printf("\n"); + //} + if (g_dumpSymbolMap) + { + dumpSymbolMap(); + exit(0); + } + + initDocParser(); + + g_outputList = new OutputList(TRUE); + if (Config_getBool("GENERATE_HTML")) + { + g_outputList->add(new HtmlGenerator); + HtmlGenerator::init(); + + bool generateHtmlHelp = Config_getBool("GENERATE_HTMLHELP"); + bool generateEclipseHelp = Config_getBool("GENERATE_ECLIPSEHELP"); + bool generateQhp = Config_getBool("GENERATE_QHP"); + bool generateTreeView = Config_getBool("GENERATE_TREEVIEW"); + bool generateDocSet = Config_getBool("GENERATE_DOCSET"); + if (generateEclipseHelp) Doxygen::indexList.addIndex(new EclipseHelp); + if (generateHtmlHelp) Doxygen::indexList.addIndex(new HtmlHelp); + if (generateQhp) Doxygen::indexList.addIndex(new Qhp); + if (generateTreeView) Doxygen::indexList.addIndex(new FTVHelp(TRUE)); + if (generateDocSet) Doxygen::indexList.addIndex(new DocSets); + Doxygen::indexList.initialize(); + HtmlGenerator::writeTabData(); + +#if 0 + if (Config_getBool("GENERATE_INDEXLOG")) Doxygen::indexList.addIndex(new IndexLog); +#endif + //if (Config_getBool("HTML_DYNAMIC_SECTIONS")) HtmlGenerator::generateSectionImages(); + copyStyleSheet(); + copyLogo(); + copyExtraFiles(); + if (!generateTreeView && Config_getBool("USE_INLINE_TREES")) + { + FTVHelp::generateTreeViewImages(); + } + } + if (Config_getBool("GENERATE_LATEX")) + { + g_outputList->add(new LatexGenerator); + LatexGenerator::init(); + } + if (Config_getBool("GENERATE_MAN")) + { + g_outputList->add(new ManGenerator); + ManGenerator::init(); + } + if (Config_getBool("GENERATE_RTF")) + { + g_outputList->add(new RTFGenerator); + RTFGenerator::init(); + } + + if (Config_getBool("USE_HTAGS")) + { + Htags::useHtags = TRUE; + QCString htmldir = Config_getString("HTML_OUTPUT"); + if (!Htags::execute(htmldir)) + err("error: USE_HTAGS is YES but htags(1) failed. \n"); + if (!Htags::loadFilemap(htmldir)) + err("error: htags(1) ended normally but failed to load the filemap. \n"); + } + + /************************************************************************** + * Generate documentation * + **************************************************************************/ + + QFile *tag=0; + QCString &generateTagFile = Config_getString("GENERATE_TAGFILE"); + if (!generateTagFile.isEmpty()) + { + tag=new QFile(generateTagFile); + if (!tag->open(IO_WriteOnly)) + { + err("error: cannot open tag file %s for writing\n", + generateTagFile.data() + ); + cleanUpDoxygen(); + exit(1); + } + Doxygen::tagFile.setDevice(tag); + Doxygen::tagFile << "<?xml version='1.0' encoding='ISO-8859-1' standalone='yes' ?>" << endl; + Doxygen::tagFile << "<tagfile>" << endl; + } + + if (Config_getBool("GENERATE_HTML")) writeDoxFont(Config_getString("HTML_OUTPUT")); + if (Config_getBool("GENERATE_LATEX")) writeDoxFont(Config_getString("LATEX_OUTPUT")); + if (Config_getBool("GENERATE_RTF")) writeDoxFont(Config_getString("RTF_OUTPUT")); + + msg("Generating style sheet...\n"); + //printf("writing style info\n"); + QCString genString = + theTranslator->trGeneratedAt(dateToString(TRUE),Config_getString("PROJECT_NAME")); + g_outputList->writeStyleInfo(0); // write first part + g_outputList->disableAllBut(OutputGenerator::Latex); + g_outputList->parseText(genString); + g_outputList->writeStyleInfo(1); // write second part + //parseText(*g_outputList,theTranslator->trWrittenBy()); + g_outputList->writeStyleInfo(2); // write third part + g_outputList->parseText(genString); + g_outputList->writeStyleInfo(3); // write fourth part + //parseText(*g_outputList,theTranslator->trWrittenBy()); + g_outputList->writeStyleInfo(4); // write last part + g_outputList->enableAll(); + + static bool searchEngine = Config_getBool("SEARCHENGINE"); + static bool serverBasedSearch = Config_getBool("SERVER_BASED_SEARCH"); + + // generate search indices (need to do this before writing other HTML + // pages as these contain a drop down menu with options depending on + // what categories we find in this function. + if (Config_getBool("GENERATE_HTML") && searchEngine) + { + msg("Generating search indices...\n"); + QCString searchDirName = Config_getString("HTML_OUTPUT")+"/search"; + QDir searchDir(searchDirName); + if (!searchDir.exists() && !searchDir.mkdir(searchDirName)) + { + err("error: Could not create search results directory '%s' $PWD='%s'\n", + searchDirName.data(),QDir::currentDirPath().data()); + exit(1); + } + HtmlGenerator::writeSearchData(searchDirName); + if (!serverBasedSearch) // client side search index + { + writeJavascriptSearchIndex(); + } + } + + //statistics(); + + // count the number of documented elements in the lists we have built. + // If the result is 0 we do not generate the lists and omit the + // corresponding links in the index. + msg("Generating index page...\n"); + writeIndex(*g_outputList); + + msg("Generating page index...\n"); + writePageIndex(*g_outputList); + + msg("Generating example documentation...\n"); + generateExampleDocs(); + + msg("Generating file sources...\n"); + if (!Htags::useHtags) + { + generateFileSources(); + } + + msg("Generating file documentation...\n"); + generateFileDocs(); + + msg("Generating page documentation...\n"); + generatePageDocs(); + + msg("Generating group documentation...\n"); + generateGroupDocs(); + + msg("Generating group index...\n"); + writeGroupIndex(*g_outputList); + + msg("Generating class documentation...\n"); + generateClassDocs(); + + if (Config_getBool("HAVE_DOT") && Config_getBool("GRAPHICAL_HIERARCHY")) + { + msg("Generating graphical class hierarchy...\n"); + writeGraphicalClassHierarchy(*g_outputList); + } + + msg("Generating namespace index...\n"); + generateNamespaceDocs(); + + msg("Generating namespace member index...\n"); + writeNamespaceMemberIndex(*g_outputList); + + if (Config_getBool("GENERATE_LEGEND")) + { + msg("Generating graph info page...\n"); + writeGraphInfo(*g_outputList); + } + + if (Config_getBool("SHOW_DIRECTORIES")) + { + msg("Generating directory documentation...\n"); + generateDirDocs(*g_outputList); + } + + msg("Generating file index...\n"); + writeFileIndex(*g_outputList); + + if (Config_getBool("SHOW_DIRECTORIES")) + { + msg("Generating directory index...\n"); + writeDirIndex(*g_outputList); + } + + msg("Generating example index...\n"); + writeExampleIndex(*g_outputList); + + msg("Generating file member index...\n"); + writeFileMemberIndex(*g_outputList); + + + //writeDirDependencyGraph(Config_getString("HTML_OUTPUT")); + + if (Doxygen::formulaList.count()>0 && Config_getBool("GENERATE_HTML") + && !Config_getBool("USE_MATHJAX")) + { + msg("Generating bitmaps for formulas in HTML...\n"); + Doxygen::formulaList.generateBitmaps(Config_getString("HTML_OUTPUT")); + } + + //if (Config_getBool("GENERATE_HTML") && Config_getBool("GENERATE_HTMLHELP")) + //{ + // HtmlHelp::getInstance()->finalize(); + //} + //if (Config_getBool("GENERATE_HTML") && Config_getBool("GENERATE_TREEVIEW")) + //{ + // FTVHelp::getInstance()->finalize(); + //} + + msg("finalizing index lists...\n"); + Doxygen::indexList.finalize(); + + if (!Config_getString("GENERATE_TAGFILE").isEmpty()) + { + Doxygen::tagFile << "</tagfile>" << endl; + delete tag; + } + + if (Config_getBool("DOT_CLEANUP")) + { + if (Config_getBool("GENERATE_HTML")) + removeDoxFont(Config_getString("HTML_OUTPUT")); + if (Config_getBool("GENERATE_RTF")) + removeDoxFont(Config_getString("RTF_OUTPUT")); + if (Config_getBool("GENERATE_LATEX")) + removeDoxFont(Config_getString("LATEX_OUTPUT")); + } + + if (Config_getBool("GENERATE_XML")) + { + msg("Generating XML output...\n"); + generateXML(); + } + if (Config_getBool("GENERATE_AUTOGEN_DEF")) + { + msg("Generating AutoGen DEF output...\n"); + generateDEF(); + } + if (Config_getBool("GENERATE_PERLMOD")) + { + msg("Generating Perl module output...\n"); + generatePerlMod(); + } + if (Config_getBool("GENERATE_HTML") && searchEngine && serverBasedSearch) + { + msg("Generating search index\n"); + HtmlGenerator::writeSearchPage(); + Doxygen::searchIndex->write(Config_getString("HTML_OUTPUT")+"/search/search.idx"); + } + + if (Config_getBool("GENERATE_RTF")) + { + msg("Combining RTF output...\n"); + if (!RTFGenerator::preProcessFileInplace(Config_getString("RTF_OUTPUT"),"refman.rtf")) + { + err("An error occurred during post-processing the RTF files!\n"); + } + } + + if (Config_getBool("HAVE_DOT")) + { + DotManager::instance()->run(); + } + + if (Config_getBool("GENERATE_HTML") && + Config_getBool("GENERATE_HTMLHELP") && + !Config_getString("HHC_LOCATION").isEmpty()) + { + msg("Running html help compiler...\n"); + QString oldDir = QDir::currentDirPath(); + QDir::setCurrent(Config_getString("HTML_OUTPUT")); + portable_sysTimerStart(); + if (portable_system(Config_getString("HHC_LOCATION"), "index.hhp", FALSE)) + { + err("error: failed to run html help compiler on index.hhp\n"); + } + portable_sysTimerStop(); + QDir::setCurrent(oldDir); + } + if ( Config_getBool("GENERATE_HTML") && + Config_getBool("GENERATE_QHP") && + !Config_getString("QHG_LOCATION").isEmpty()) + { + msg("Running qhelpgenerator...\n"); + QCString const qhpFileName = Qhp::getQhpFileName(); + QCString const qchFileName = getQchFileName(); + + QCString const args = QCString().sprintf("%s -o \"%s\"", qhpFileName.data(), qchFileName.data()); + QString const oldDir = QDir::currentDirPath(); + QDir::setCurrent(Config_getString("HTML_OUTPUT")); + portable_sysTimerStart(); + if (portable_system(Config_getString("QHG_LOCATION"), args.data(), FALSE)) + { + err("error: failed to run qhelpgenerator on index.qhp\n"); + } + portable_sysTimerStop(); + QDir::setCurrent(oldDir); + } + + + if (Debug::isFlagSet(Debug::Time)) + { + msg("Total elapsed time: %.3f seconds\n(of which %.3f seconds waiting for external tools to finish)\n", + ((double)Doxygen::runningTime.elapsed())/1000.0, + portable_getSysElapsedTime() + ); + } + else + { + msg("finished...\n"); + } + + /************************************************************************** + * Start cleaning up * + **************************************************************************/ + + //Doxygen::symbolCache->printStats(); + //Doxygen::symbolStorage->printStats(); + cleanUpDoxygen(); + + finializeDocParser(); + Doxygen::symbolStorage->close(); + QDir thisDir; + thisDir.remove(Doxygen::objDBFileName); + Config::deleteInstance(); + QTextCodec::deleteAllCodecs(); + delete Doxygen::symbolCache; + delete Doxygen::symbolMap; + delete Doxygen::symbolStorage; + g_successfulRun=TRUE; +} + |