/****************************************************************************** * * * * * Copyright (C) 1997-2008 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 #include #include #include #include #include #include #include #include #include #include #include #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 "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 "code.h" #include "objcache.h" #include "store.h" #include "marshal.h" #include "portable.h" #include "vhdlscanner.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 #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 StringDict Doxygen::namespaceAliasDict(257); // all namespace aliases StringDict Doxygen::tagDestinationDict(257); // all tag locations QDict Doxygen::expandAsDefinedDict(257); // all macros that should be expanded QIntDict 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? QTextStream Doxygen::tagFile; NamespaceDef *Doxygen::globalScope = 0; QDict *Doxygen::xrefLists = new QDict; // dictionary of cross-referenced item lists bool Doxygen::parseSourcesNeeded = FALSE; double Doxygen::sysElapsedTime = 0.0; QTime Doxygen::runningTime; SearchIndex * Doxygen::searchIndex=0; QDict *Doxygen::symbolMap; bool Doxygen::outputToWizard=FALSE; QDict * Doxygen::htmlDirMap = 0; QCache Doxygen::lookupCache(50000,50000); DirSDict *Doxygen::directories; SDict 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; // locally accessible globals static QDict classEntries(1009); static StringList inputFiles; static StringDict excludeNameDict(1009); // sections static QDict compoundKeywordDict(7); // keywords recognised as compounds static OutputList *outputList = 0; // list of output generating objects static QDict g_usingDeclarations(1009); // used classes static const char idMask[] = "[A-Za-z_][A-Za-z_0-9]*"; FileStorage *g_storage = 0; QCString spaces; static bool g_successfulRun = FALSE; static bool g_dumpSymbolMap = FALSE; static bool g_dumpConfigAsXML = FALSE; void clearAll() { inputFiles.clear(); excludeNameDict.clear(); delete outputList; 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::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,"--- excludeNameDict stats ----\n"); 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,"--- compoundKeywordDict stats ----\n"); 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", 0, "Char", 0, 0, 0, TRUE, FALSE }, { "basic_ostream", "basic_ios", 0, "Char", 0, 0, 0, TRUE, FALSE }, { "basic_iostream", "basic_istream", "basic_ostream", "Char", 0, 0, 0, FALSE, FALSE }, { "basic_ifstream", "basic_istream", 0, "Char", 0, 0, 0, FALSE, FALSE }, { "basic_ofstream", "basic_ostream", 0, "Char", 0, 0, 0, FALSE, FALSE }, { "basic_fstream", "basic_iostream", 0, "Char", 0, 0, 0, FALSE, FALSE }, { "basic_istringstream", "basic_istream", 0, "Char", 0, 0, 0, FALSE, FALSE }, { "basic_ostringstream", "basic_ostream", 0, "Char", 0, 0, 0, FALSE, FALSE }, { "basic_stringstream", "basic_iostream", 0, "Char", 0, 0, 0, FALSE, FALSE }, { "ios", "basic_ios", 0, 0, 0, 0, 0, FALSE, FALSE }, { "wios", "basic_ios", 0, 0, 0, 0, 0, FALSE, FALSE }, { "istream", "basic_istream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "wistream", "basic_istream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "ostream", "basic_ostream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "wostream", "basic_ostream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "ifstream", "basic_ifstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "wifstream", "basic_ifstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "ofstream", "basic_ofstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "wofstream", "basic_ofstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "fstream", "basic_fstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "wfstream", "basic_fstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "istringstream", "basic_istringstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "wistringstream", "basic_istringstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "ostringstream", "basic_ostringstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "wostringstream", "basic_ostringstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "stringstream", "basic_stringstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "wstringstream", "basic_stringstream", 0, 0, 0, 0, 0, FALSE, FALSE }, { "basic_string", 0, 0, "Char", 0, 0, 0, FALSE, TRUE }, { "string", "basic_string", 0, 0, 0, 0, 0, FALSE, TRUE }, { "wstring", "basic_string", 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; 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 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; } else { doc=root->brief+"\n\n"+root->doc; } 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) { if (rootNav->section()==Entry::GROUPDOC_SEC && !rootNav->name().isEmpty()) { //printf("Found group %s title=`%s type=%d'\n", // root->name.data(),root->type.data(),root->groupDocType); 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))) { #if 0 if ( root->groupDocType==Entry::GROUPDOC_NORMAL ) { warn(root->fileName,root->startLine, "Warning: group %s already documented. " "Skipping documentation.", root->name.data()); } else #endif { 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", root->name.data(), root->type.data(), gd->groupTitle() ); //if ( gd->briefDescription().isEmpty() ) gd->setBriefDescription(root->brief,root->briefFile,root->briefLine); //if ( !root->doc.stripWhiteSpace().isEmpty() ) gd->setDocumentation( root->doc, root->docFile, root->docLine ); gd->addSectionsToDefinition(root->anchors); gd->setRefItems(root->sli); //addGroupToGroups(root,gd); } } 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->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); } } } static void buildGroupList(EntryNav *rootNav) { // first process the @defgroups blocks buildGroupListFiltered(rootNav,FALSE); // then process the @addtogroup, @weakgroup blocks buildGroupListFiltered(rootNav,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 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); //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 ", root->name.data() ); if (ambig) // name is ambigious { 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 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 (!root->includeFile.isEmpty() && (fd=findFileDef(Doxygen::inputNameDict,root->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 ", root->includeFile.data() ); if (ambig) // name is ambigious { 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 (root->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.data() : root->includeFile.data(); bool local=FALSE; if (!iName.isEmpty()) // user specified include file { local = iName.at(0)=='"'; // is it a local include file if (local || iName.at(0)=='<') { iName=iName.mid(1,iName.length()-2); // strip quotes or brackets } } 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 ? "" : 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 (ifind(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("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 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 *tArgLists) { if (tArgLists==0) return 0; QListIterator 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: TODO: is this still true? QCString fullName = root->name; // strip off any template parameters (but not those for specializations) fullName=stripTemplateSpecifiersFromScope(fullName); // name with scope QCString qualifiedName = fullName; if (!scName.isEmpty() && !leftScopeMatch(fullName,scName)) { qualifiedName.prepend(scName+"::"); } 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); //} #if 0 if (!root->doc.isEmpty() || !root->brief.isEmpty() || (root->bodyLine!=-1 && Config_getBool("SOURCE_BROWSER")) ) // block contains something that ends up in the docs { if (!root->doc.isEmpty() && !cd->documentation().isEmpty()) { warn( root->fileName,root->startLine, "Warning: class %s already has a detailed description found in file %s at line %d. " "Skipping the one found here.", fullName.data(),cd->docFile().data(),cd->docLine() ); } else if (!root->doc.isEmpty()) #endif { cd->setDocumentation(root->doc,root->docFile,root->docLine); } #if 0 if (!root->brief.isEmpty() && !cd->briefDescription().isEmpty()) { warn( root->fileName,root->startLine, "Warning: class %s already has a brief description found in file %s at line %d\n" " skipping the one found here.", fullName.data(),cd->briefFile().data(),cd->briefLine() ); } else if (!root->brief.isEmpty()) #endif { 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 0 } #endif 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 cd=%p\n", fullName.data(),root->section,root->tArgLists ? (int)root->tArgLists->count() : -1,cd); cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition cd->setBriefDescription(root->brief,root->briefFile,root->briefLine); cd->setIsObjectiveC(root->objc); 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() : ""); 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() ); } } } } //---------------------------------------------------------------------- // 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 %d\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():""); 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(const 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():""); // 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 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(), // root->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,".","::"); //MemberDef *mtd=0; //usingCd = getResolvedClass(nd,fd,name,&mtd); 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( "",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 0 if (mtd) // add the typedef to the correct scope { if (nd) { //printf("Inside namespace %s\n",nd->name().data()); nd->addUsingDeclaration(mtd); } else if (fd) { //printf("Inside file %s\n",nd->name().data()); fd->addUsingDeclaration(mtd); } } else #endif 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",nd->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 templAl = md->templateArguments(); LockingPtr 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->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 the function is inside a namespace NamespaceDef *nd = 0; QCString nscope; if (!scope.isEmpty()) { nscope=removeAnonymousScopes(scope); if (!nscope.isEmpty()) { nd = getResolvedNamespace(nscope); } } 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 annonymous 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()) //{ // nd = getResolvedNamespace(nscope); //} 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():""); // 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 *pLength=0) { 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; return i; } else { 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); // 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 annonymous scope we insert the member // into a non-annonymous 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() || 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()))!=-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()); if (!rname.isEmpty()) { ClassDef *cd=0; // check if this function's parent is a class QCString scope=rootNav->parent()->name(); //stripAnonymousNamespaceScope(root->parent->name); 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=memIndexte; } } 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 mdAl = md->argumentList(); if ( matchArguments2(md->getOuterScope(),mfd,mdAl.pointer(), rnd ? rnd : Doxygen::globalScope,rfd,root->argList, FALSE) ) { 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->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 (!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()); 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 mmdAl = mmd->argumentList(); LockingPtr 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) { 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 mdefAl = mdef->argumentList(); LockingPtr 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 mdefAl = mdef->argumentList(); LockingPtr mdecAl = mdec->argumentList(); if ( matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdefAl.pointer(), mdec->getOuterScope(),mdec->getFileDef(),mdecAl.pointer(), TRUE ) ) /* match found */ { LockingPtr defDict = mdef->getReferencesMembers(); LockingPtr 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 mdAl = md->argumentList(); LockingPtr 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 * that inherits from B will have T and S in the dictionary. */ static QDict *getTemplateArgumentsInName(ArgumentList *templateArguments,const QCString &name) { QDict *templateNames = new QDict(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); } //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n", // name.data(), // context ? context->name().data() : "", // cd ? cd->name().data() : "", // result ? result->name().data() : "", // Doxygen::classSDict.find(name) // ); return result; } enum FindBaseClassRelation_Mode { TemplateInstances, DocumentedOnly, Undocumented }; static bool findClassRelation( EntryNav *rootNav, Definition *context, ClassDef *cd, BaseInfo *bi, QDict *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 *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():""); 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():"",masterCd?masterCd->name().data():""); 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 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 *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 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 *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 qdi(*templateNames); // int *tempArgIndex; // for (;(tempArgIndex=qdi.current());++qdi) // { // printf("(%s->%d) ",qdi.currentKey().data(),*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->setIsObjectiveC(root->objc); 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 = 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 * 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': if (!insideString && !insideChar) { if (e') 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 *templateNames, FindBaseClassRelation_Mode mode, bool isArtificial ) { //printf("findClassRelation(class=%s base=%s templateNames=", // cd->name().data(),bi->name.data()); //if (templateNames) //{ // QDictIterator 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; if (biName.left(2)=="::") // explicit global scope { biName=biName.right(biName.length()-2); explicitGlobalScope=TRUE; } //printf("biName=`%s'\n",biName.data()); 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 ? 0 : cd, 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():"", // baseClassName.data(), // baseClass?baseClass->name().data():"", // 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=baseClassName.mid(i,e-i); baseClassName=baseClassName.left(i)+baseClassName.right(baseClassName.length()-e); baseClass=getResolvedClass(cd, 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(cd, 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 (!found) { baseClass=findClassWithinClassContext(context,cd,baseClassName); //printf("findClassWithinClassContext(%s,%s)=%p\n", // cd->name().data(),baseClassName.data(),baseClass); found = baseClass!=0 && baseClass!=cd; } bool isATemplateArgument = templateNames!=0 && templateNames->find(biName)!=0; // make templSpec canonical 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) { //printf(" => findTemplateInstanceRelation\n"); findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,isArtificial); } else if (mode==DocumentedOnly || mode==Undocumented) { //printf(" => insert base class\n"); QCString usedName; if (baseClassTypeDef) { 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)) { 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 edi(classEntries); EntryNav *rootNav; for (;(rootNav=edi.current());++edi) { ClassDef *cd; // strip any annonymous 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 edi(classEntries); EntryNav *rootNav; for (;(rootNav=edi.current());++edi) { ClassDef *cd; // strip any annonymous 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 edi(classEntries); EntryNav *rootNav; for (;(rootNav=edi.current());++edi) { ClassDef *cd; rootNav->loadEntry(g_storage); Entry *root = rootNav->entry(); // strip any annonymous 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 edi(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 annonymous scopes first QDict *templInstances = 0; if (cd && (templInstances=cd->getTemplateInstances())) { Debug::print(Debug::Classes,0," Template class %s : \n",cd->name().data()); QDictIterator 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 *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 *baseClassNames = tcd->getTemplateBaseClassNames(); QDict *templateNames = getTemplateArgumentsInName(tl,bi->name); // for each template name that we inherit from we need to // substitute the formal with the actual arguments QDict *actualTemplateNames = new QDict(17); actualTemplateNames->setAutoDelete(TRUE); QDictIterator 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->name(); if (pd->getGroupDef()) { name = pd->getGroupDef()->getOutputFileBase(); } { LockingPtr< QList > xrefItems = pd->xrefListItems(); addRefItem(xrefItems.pointer(), theTranslator->trPage(TRUE,TRUE), name,pd->title()); } } 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 > xrefItems = dd->xrefListItems(); addRefItem(xrefItems.pointer(), theTranslator->trDir(TRUE,TRUE), name,dd->displayName()); } } //---------------------------------------------------------------------- // 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 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+="

"; doc+=root->doc; } md->setDocumentation(doc,root->docFile,root->docLine); md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); md->setDocsForDefinition(!root->proto); } else { //printf("Adding docs md->docs=`%s' root->docs=`%s'!\n", // md->documentation().data(),root->doc.data()); // documentation outside a compound overrides the documentation inside it #if 0 if ( /* !md->isStatic() && !root->stat && do not replace doc of a static */ ( md->documentation().isEmpty() || /* no docs yet */ (rootNav->parent()->name().isEmpty() && /* or overwrite prototype docs */ !root->proto && md->isPrototype() /* with member definition docs */ ) ) && !root->doc.isEmpty() ) #endif { //printf("overwrite!\n"); md->setDocumentation(root->doc,root->docFile,root->docLine); md->setDocsForDefinition(!root->proto); } //printf("Adding brief md->brief=`%s' root->brief=`%s'!\n", // md->briefDescription().data(),root->brief.data()); // brief descriptions inside a compound override the documentation // outside it #if 0 if ( /* !md->isStatic() && !root->stat && do not replace doc of static */ ( md->briefDescription().isEmpty() || /* no docs yet */ !rootNav->parent()->name().isEmpty() /* member of a class */ ) && !root->brief.isEmpty() ) #endif { //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) // || (md->isVariable() && !root->explicitExternal) ) { //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() : ""); FileDef *fd=rootNav->fileDef(); //printf("File %s\n",fd ? fd->name().data() : ""); NamespaceSDict *nl = fd ? fd->getUsedNamespaces() : 0; //SDict *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 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); //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); warn(root->fileName,root->startLine, "Warning: no matching file member found for \n%s",fullFuncDecl.data()); if (mn->count()>0) { warn_cont("Possible candidates:\n"); for (mni.toFirst();(md=mni.current());++mni) { warn_cont(" %s\n",md->declaration()); } } } } else // got docs for an undefined member! { if (root->type!="friend class" && root->type!="friend struct" && root->type!="friend union") { warn(root->fileName,root->startLine, "Warning: documented function `%s' was not declared or defined.",decl ); } } return TRUE; } static bool isSpecialization( const QList &srcTempArgLists, const QList &dstTempArgLists ) { QListIterator srclali(srcTempArgLists); QListIterator 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 &srcTempArgLists, const QList &dstTempArgLists, ArgumentList *funcTempArgList, // can be used to match template specializations const QCString &src ) { QCString dst; QRegExp re(idMask); //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 srclali(srcTempArgLists); QListIterator 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 (name==tsa->name) { if (tda) { name=tda->name; // substitute found=TRUE; } else if (fa) { name=fa->type; found=TRUE; } } if (tda) ++tdali; else if (fa) fa=funcTempArgList->next(); } } dst+=name; p=i+l; } dst+=src.right(src.length()-p); return dst; } static void substituteTemplatesInArgList( const QList &srcTempArgLists, const QList &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 isObjC=%d\n", root,funcDecl.data(),root->relates.data(),overloaded,isFunc,root->mGrpId, root->tArgLists,root->tArgLists ? root->tArgLists->count() : 0, root->spec,root->objc ); 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->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); //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() : "", 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); 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():"",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 declTemplArgs; cd->getTemplateParameterLists(declTemplArgs); LockingPtr templAl = md->templateArguments(); if (templAl!=0) { declTemplArgs.append(templAl.pointer()); } // get the template parameter lists found at the member definition QList *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 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; if (mn->count()>0) { //printf("Assume template class\n"); for (mni.toFirst();(md=mni.current());++mni) { ClassDef *cd=md->getClassDef(); //printf("cd->name()==%s className=%s\n",cd->name().data(),className.data()); if (cd!=0 && rightScopeMatch(cd->name(),className)) { LockingPtr templAl = md->templateArguments(); if (root->tArgLists && templAl!=0 && root->tArgLists->getLast()->count()<=templAl->count()) { addMethodToClass(rootNav,cd,md->name(),isFriend); return; } candidates++; } } } warn(root->fileName,root->startLine, "Warning: no %smatching class member found for", noMatchCount>1 ? "uniquely " : "" ); if (root->tArgLists) { QListIterator alli(*root->tArgLists); ArgumentList *al; for (;(al=alli.current());++alli) { warn_cont(" template %s\n",tempArgListToString(al).data()); } } QCString fullFuncDecl=funcDecl.copy(); if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE); warn_cont(" %s\n",fullFuncDecl.data()); if (candidates>0) { warn_cont("Possible candidates:\n"); for (mni.toFirst();(md=mni.current());++mni) { ClassDef *cd=md->getClassDef(); if (cd!=0 && rightScopeMatch(cd->name(),className)) { LockingPtr templAl = md->templateArguments(); if (templAl!=0) { warn_cont(" template %s\n",tempArgListToString(templAl.pointer()).data()); } warn_cont(" "); if (md->typeString()) { warn_cont("%s ",md->typeString()); } QCString qScope = cd->qualifiedNameWithTemplateParameters(); if (!qScope.isEmpty()) warn_cont("%s::%s",qScope.data(),md->name().data()); if (md->argsString()) warn_cont("%s",md->argsString()); if (noMatchCount>1) warn_cont(" at line %d of file %s",md->getDefLine(),md->getDefFileName().data()); warn_cont("\n"); } } } } } else if (cd) // member specialization { MemberDef::MemberType mtype=MemberDef::Function; ArgumentList *tArgList = new ArgumentList; // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists); MemberDef *md=new MemberDef( root->fileName,root->startLine, funcType,funcName,funcArgs,exceptions, 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+="

"; 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 rmdAl = rmd->argumentList(); newMember=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 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,&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 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); 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 ) ) { // For C# enum value are only inside the enum scope, so we // must create them here e->loadEntry(g_storage); MemberDef *fmd = addVariableToFile(e,MemberDef::EnumValue, md->getOuterScope() ? md->getOuterScope()->name() : QCString(), e->name(),FALSE,0); 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); 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 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(" Base argList=`%s'\n Super argList=`%s'\n", // argListToString(bmd->argumentList()).data(), // argListToString(md->argumentList()).data() // ); LockingPtr bmdAl = bmd->argumentList(); LockingPtr mdAl = md->argumentList(); 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)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 *templInstances = cd->getTemplateInstances(); if (templInstances) { QDictIterator 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(*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(*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\n",md->name().data()); 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); } } } } //---------------------------------------------------------------------------- // 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() ) { // 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(*outputList); cd->writeMemberList(*outputList); } // even for undocumented classes, the inner classes can be documented. cd->writeDocumentationForInnerClasses(*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(*outputList); //if (Config_getBool("ALPHABETICAL_INDEX")) //{ msg("Generating alphabetical compound index...\n"); writeAlphabeticalIndex(*outputList); //} msg("Generating hierarchical class index...\n"); writeHierarchicalIndex(*outputList); msg("Generating member index...\n"); writeClassMemberIndex(*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 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 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()); } } } //---------------------------------------------------------------------------- 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("",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) { #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->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,"\\","/"); if (normalizedName.at(normalizedName.length()-1)!='/') { normalizedName+='/'; } DirDef *dir,*matchingDir=0; SDict::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",root->name.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(); addRefItem(root->sli,"page", usingTreeIndex()?"main":"index", title ); 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=usingTreeIndex()?"main":"index"; Doxygen::mainPage = new PageDef(root->fileName,root->startLine, indexName, root->brief+root->doc,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 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 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():"", // 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 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(*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->args); pd->setFileName(convertNameToFile(pd->name()+"-example")); 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()?"":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() { 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(*outputList,n,n,pd->name()); startTitle(*outputList,n); outputList->docify(pd->name()); endTitle(*outputList,n,0); 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() ); endFile(*outputList); } 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(*outputList); } } } //---------------------------------------------------------------------------- //static void generatePackageDocs() //{ // writePackageIndex(*outputList); // // if (Doxygen::packageDict.count()>0) // { // PackageSDict::Iterator pdi(Doxygen::packageDict); // PackageDef *pd; // for (pdi.toFirst();(pd=pdi.current());++pdi) // { // pd->writeDocumentation(*outputList); // } // } //} //---------------------------------------------------------------------------- // generate module pages static void generateNamespaceDocs() { writeNamespaceIndex(*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(*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(*outputList); cd->writeMemberList(*outputList); } cd->writeDocumentationForInnerClasses(*outputList); } } } #if defined(_WIN32) static QCString fixSlashes(QCString &s) { QCString result; uint i; for (i=0;iError: $DOXYSEARCH not found. Check cgi script!\"" << 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 " << endl; // t << "#include " << endl; // t << "#include " << 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(\"

Error: failed to execute %s

\\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()); // } // //outputList->generateExternalIndex(); // outputList->pushGeneratorState(); // outputList->disableAllBut(OutputGenerator::Html); // startFile(*outputList,"header"+Doxygen::htmlFileExtension,0,"Search Engine",TRUE); // outputList->endPlainFile(); // outputList->startPlainFile("footer"+Doxygen::htmlFileExtension); // endFile(*outputList,TRUE); // 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) { QTextStream t(&f); t.setEncoding(QTextStream::UnicodeUTF8); 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()); } //---------------------------------------------------------------------------- // returns TRUE if the name of the file represented by `fi' matches // one of the file patterns in the `patList' list. static bool patternMatch(QFileInfo *fi,QStrList *patList) { bool found=FALSE; if (patList) { QCString pattern=patList->first(); while (!pattern.isEmpty() && !found) { int i=pattern.find('='); if (i!=-1) pattern=pattern.left(i); // strip of the extension specific filter name #if defined(_WIN32) // windows QRegExp re(pattern,FALSE,TRUE); // case insensitive match #else // unix QRegExp re(pattern,TRUE,TRUE); // case sensitive match #endif found = found || re.match(fi->fileName())!=-1 || re.match(fi->filePath())!=-1 || re.match(fi->absFilePath())!=-1; //printf("Matching `%s' against pattern `%s' found=%d\n", // fi->fileName().data(),pattern.data(),found); pattern=patList->next(); } } return found; } static int transcodeCharacterBuffer(BufStr &srcBuf,int size, const char *inputEncoding,const char *outputEncoding) { if (inputEncoding==0 || outputEncoding==0) return size; if (qstricmp(inputEncoding,outputEncoding)==0) return size; void *cd = portable_iconv_open(outputEncoding,inputEncoding); if (cd==(void *)(-1)) { err("Error: unsupported character conversion: '%s'->'%s': %s\n" "Check the INPUT_ENCODING setting in the config file!\n", inputEncoding,outputEncoding,strerror(errno)); exit(1); } int tmpBufSize=size*4+1; BufStr tmpBuf(tmpBufSize); size_t iLeft=size; size_t oLeft=tmpBufSize; const char *srcPtr = srcBuf.data(); char *dstPtr = tmpBuf.data(); uint newSize=0; if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft)) { newSize = tmpBufSize-oLeft; srcBuf.shrink(newSize); strncpy(srcBuf.data(),tmpBuf.data(),newSize); //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data()); } else { err("Error: failed to translate characters from %s to %s: check INPUT_ENCODING\n", inputEncoding,outputEncoding); exit(1); } portable_iconv_close(cd); return newSize; } //---------------------------------------------------------------------------- // reads a file into an array and filters out any 0x00 and 0x06 bytes, // because these are special for the parser. void copyAndFilterFile(const char *fileName,BufStr &dest) { // try to open file int size=0; //uint oldPos = dest.curPos(); //printf(".......oldPos=%d\n",oldPos); QFileInfo fi(fileName); if (!fi.exists()) return; QCString filterName = getFileFilter(fileName); if (filterName.isEmpty()) { QFile f(fileName); if (!f.open(IO_ReadOnly)) { err("Error: could not open file %s\n",fileName); return; } size=fi.size(); // read the file dest.skip(size); if (f.readBlock(dest.data()/*+oldPos*/,size)!=size) { err("Error while reading file %s\n",fileName); return; } } else { QCString cmd=filterName+" \""+fileName+"\""; Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data()); FILE *f=portable_popen(cmd,"r"); if (!f) { err("Error: could not execute filter %s\n",filterName.data()); return; } const int bufSize=1024; char buf[bufSize]; int numRead; while ((numRead=fread(buf,1,bufSize,f))>0) { //printf(">>>>>>>>Reading %d bytes\n",numRead); dest.addArray(buf,numRead),size+=numRead; } portable_pclose(f); } // filter unwanted bytes from the resulting data uchar conv[256]; int i; for (i=0;i<256;i++) conv[i]=i; conv[0x06]=0x20; // replace the offending characters with spaces conv[0x00]=0x20; // remove any special markers from the input uchar *p=(uchar *)dest.data()/*+oldPos*/; for (i=0;igetParser(extension); QFileInfo fi(fileName); BufStr preBuf(fi.size()+4096); //BufStr *bufPtr = &preBuf; if (Config_getBool("ENABLE_PREPROCESSING") && parser->needsPreprocessing(extension)) { msg("Preprocessing %s...\n",s->data()); preprocessFile(fileName,preBuf); } else { msg("Reading %s...\n",s->data()); copyAndFilterFile(fileName,preBuf); } preBuf.addChar('\n'); /* to prevent problems under Windows ? */ // do character transcoding if needed. transcodeCharacterBuffer(preBuf,preBuf.curPos(), Config_getString("INPUT_ENCODING"),"UTF-8"); 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=inputFiles.next(); } } //---------------------------------------------------------------------------- // 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 *killDict ) { QDir dir((const char *)fi->absFilePath()); 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(); 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 *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 adi(Doxygen::aliasDict); QCString *s; for (adi.toFirst();(s=adi.current());++adi) { *s = expandAlias(adi.currentKey(),*s); } } //---------------------------------------------------------------------------- static void escapeAliases() { QDictIterator 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(QTextStream &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)) { QTextStream t(&f); QDictIterator 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)) { QTextStream 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-2008\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 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+1setAutoDelete(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); // register any additional parsers here... initDefaultExtensionMapping(); initClassMemberIndices(); initNamespaceMemberIndices(); initFileMemberIndices(); } void cleanUpDoxygen() { delete Doxygen::inputNameDict; delete Doxygen::includeNameDict; delete Doxygen::exampleNameDict; delete Doxygen::imageNameDict; delete Doxygen::dotFileNameDict; delete Doxygen::mainPage; delete Doxygen::pageSDict; delete Doxygen::exampleSDict; delete Doxygen::globalScope; delete Doxygen::xrefLists; delete Doxygen::parserManager; cleanUpPreprocessor(); delete theTranslator; delete outputList; Mappers::freeMappers(); codeFreeScanner(); if (Doxygen::symbolMap) { // iterate through Doxygen::symbolMap and delete all // DefinitionList objects, since they have no owner QDictIterator 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) { 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+4parse(argv[optind+4])) { err("Error opening or reading configuration file %s!\n",argv[optind+4]); cleanUpDoxygen(); exit(1); } Config::instance()->substituteEnvironmentVars(); Config::instance()->convertStrToVal(); 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); } 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+3parse(argv[optind+3])) { err("Error opening or reading configuration file %s!\n",argv[optind+3]); exit(1); } Config::instance()->substituteEnvironmentVars(); Config::instance()->convertStrToVal(); Config::instance()->check(); } else // use default config { Config::instance()->init(); } if (optind+2>=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::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) { checkConfiguration(); generateConfigFile(configName,shortList); if (g_dumpConfigAsXML) { dumpConfigAsXML(); exit(0); } 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()); } void checkConfiguration() { Config::instance()->substituteEnvironmentVars(); Config::instance()->convertStrToVal(); Config::instance()->check(); initWarningFormat(); 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"); } #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 parseInput() { atexit(exitDoxygen); /************************************************************************** * 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(); } /************************************************************************** * 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 **************************************************************************/ Doxygen::symbolMap = new QDict(1000); 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); } 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("",1,""); 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::sectionDict.setAutoDelete(TRUE); Doxygen::memGrpInfoDict.setAutoDelete(TRUE); Doxygen::tagDestinationDict.setAutoDelete(TRUE); Doxygen::lookupCache.setAutoDelete(TRUE); Doxygen::dirRelations.setAutoDelete(TRUE); excludeNameDict.setAutoDelete(TRUE); /************************************************************************** * Initialize some global constants **************************************************************************/ int &tabSize = Config_getInt("TAB_SIZE"); spaces.resize(tabSize+1); int sp;for (sp=0;sp *killDict = new QDict(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; // 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(); // 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"); 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("Searching for included using directives...\n"); findIncludedUsingDirectives(); 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(); // calling buildClassList may result in cached relations that // become invalid after resolveClassNestingRelation(), 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("Searching for members imported via using declarations...\n"); findUsingDeclImports(rootNav); findUsingDeclarations(rootNav); 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 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(); //if (Config_getBool("OPTIMIZE_OUTPUT_VHDL")) //{ // VhdlDocGen::computeVhdlComponentRelations(classEntries,g_storage); //} //else //{ computeClassRelations(); //} 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 todo/test/bug list items...\n"); addListReferences(); 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::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(); outputList = new OutputList(TRUE); if (Config_getBool("GENERATE_HTML")) { outputList->add(new HtmlGenerator); HtmlGenerator::init(); if (Config_getBool("GENERATE_HTMLHELP")) Doxygen::indexList.addIndex(new HtmlHelp); if (Config_getBool("GENERATE_QHP")) Doxygen::indexList.addIndex(new Qhp); #if 0 if (Config_getBool("GENERATE_INDEXLOG")) Doxygen::indexList.addIndex(new IndexLog); #endif if (usingTreeIndex()) Doxygen::indexList.addIndex(new FTVHelp); if (Config_getBool("GENERATE_DOCSET")) Doxygen::indexList.addIndex(new DocSets); Doxygen::indexList.initialize(); Doxygen::indexList.addImageFile("tab_r.gif"); Doxygen::indexList.addImageFile("tab_l.gif"); Doxygen::indexList.addImageFile("tab_b.gif"); Doxygen::indexList.addStyleSheetFile("tabs.css"); Doxygen::indexList.addImageFile("doxygen.png"); if (Config_getBool("HTML_DYNAMIC_SECTIONS")) HtmlGenerator::generateSectionImages(); copyStyleSheet(); } if (Config_getBool("GENERATE_LATEX")) { outputList->add(new LatexGenerator); LatexGenerator::init(); } if (Config_getBool("GENERATE_MAN")) { outputList->add(new ManGenerator); ManGenerator::init(); } if (Config_getBool("GENERATE_RTF")) { 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.setEncoding(QTextStream::UnicodeUTF8); Doxygen::tagFile << "" << endl; Doxygen::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")); outputList->writeStyleInfo(0); // write first part outputList->disableAllBut(OutputGenerator::Latex); outputList->parseText(genString); outputList->writeStyleInfo(1); // write second part //parseText(*outputList,theTranslator->trWrittenBy()); outputList->writeStyleInfo(2); // write third part outputList->parseText(genString); outputList->writeStyleInfo(3); // write fourth part //parseText(*outputList,theTranslator->trWrittenBy()); outputList->writeStyleInfo(4); // write last part outputList->enableAll(); //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(*outputList); msg("Generating page index...\n"); writePageIndex(*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(*outputList); msg("Generating class documentation...\n"); generateClassDocs(); if (Config_getBool("HAVE_DOT") && Config_getBool("GRAPHICAL_HIERARCHY")) { msg("Generating graphical class hierarchy...\n"); writeGraphicalClassHierarchy(*outputList); } msg("Generating namespace index...\n"); generateNamespaceDocs(); msg("Generating namespace member index...\n"); writeNamespaceMemberIndex(*outputList); if (Config_getBool("GENERATE_LEGEND")) { msg("Generating graph info page...\n"); writeGraphInfo(*outputList); } if (Config_getBool("SHOW_DIRECTORIES")) { msg("Generating directory documentation...\n"); generateDirDocs(*outputList); } msg("Generating file index...\n"); writeFileIndex(*outputList); if (Config_getBool("SHOW_DIRECTORIES")) { msg("Generating directory index...\n"); writeDirIndex(*outputList); } msg("Generating example index...\n"); writeExampleIndex(*outputList); msg("Generating file member index...\n"); writeFileMemberIndex(*outputList); //writeDirDependencyGraph(Config_getString("HTML_OUTPUT")); 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 (Doxygen::formulaList.count()>0 && Config_getBool("GENERATE_HTML")) { 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(); //} Doxygen::indexList.finalize(); if (!Config_getString("GENERATE_TAGFILE").isEmpty()) { Doxygen::tagFile << "" << endl; delete tag; } if (Config_getBool("GENERATE_HTML") && Config_getBool("DOT_CLEANUP")) removeDoxFont(Config_getString("HTML_OUTPUT")); if (Config_getBool("GENERATE_RTF") && Config_getBool("DOT_CLEANUP")) removeDoxFont(Config_getString("RTF_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") && 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")); if (portable_system(Config_getString("HHC_LOCATION"), "index.hhp", FALSE)) { err("Error: failed to run html help compiler on index.hhp\n"); } QDir::setCurrent(oldDir); } #if 0 if ( Config_getBool("GENERATE_HTMLHELP") && !Config_getString("DOXYGEN2QTHELP_LOC").isEmpty() && !Config_getString("QTHELP_CONFIG").isEmpty()) { msg("Running doxygen2qthelp...\n"); const QCString qtHelpFile = Config_getString("QTHELP_FILE"); const QCString args = QCString().sprintf("--config=%s index.hhp%s%s", Config_getString("QTHELP_CONFIG").data(), (qtHelpFile.isEmpty() ? "" : " "), (qtHelpFile.isEmpty() ? "" : qtHelpFile.data())); const QString oldDir = QDir::currentDirPath(); QDir::setCurrent(Config_getString("HTML_OUTPUT")); if (portable_system(Config_getString("DOXYGEN2QTHELP_LOC"), args.data(), FALSE)) { err("Error: failed to run doxygen2qthelp on index.hhp\n"); } QDir::setCurrent(oldDir); } #endif 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")); if (portable_system(Config_getString("QHG_LOCATION"), args.data(), FALSE)) { err("Error: failed to run qhelpgenerator on index.qhp\n"); } QDir::setCurrent(oldDir); } if (Config_getBool("SEARCHENGINE")) { msg("Generating search index\n"); HtmlGenerator::writeSearchPage(); Doxygen::searchIndex->write(Config_getString("HTML_OUTPUT")+"/search.idx"); } 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, Doxygen::sysElapsedTime ); } /************************************************************************** * 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; }