/****************************************************************************** * * * * Copyright (C) 1997-2004 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. * */ %{ /* * includes */ #include <stdio.h> #include <assert.h> #include <ctype.h> #include "qtbc.h" #include <qstrlist.h> #include <qfileinfo.h> #include <qfile.h> #include <qdict.h> #include <qtextstream.h> #include <qdir.h> #include "version.h" //#include "suffixtree.h" //#include "searchindex.h" #include "logos.h" static QCString convertToXML(const char *s) { QCString result; if (s==0) return result; const char *p=s; char c; while ((c=*p++)) { switch (c) { case '<': result+="<"; break; case '>': result+=">"; break; case '&': result+="&"; break; case '\'': result+="'"; break; case '"': result+="""; break; default: result+=c; break; } } return result; } struct MemberDef { QCString name; QCString anchor; QCString args; }; struct ClassDef { QCString name; QStrList bases; QCString fileName; bool isFile; QList<MemberDef> memberList; }; QList<ClassDef> classList; QDict<ClassDef> classDict(1009); QList<ClassDef> fileList; QDict<ClassDef> fileDict(1009); static bool genTag; static bool genIndex; static QStrList bases; static QCString inputString; static int inputPosition; static QCString yyFileName; static int yyLineNr; static QCString classFile; static QCString memberRef; static QCString memberName; static QCString memberArgs; static QCString className; static QCString docBaseLink; static QCString docAnchor; static QCString docRefName; static bool nameBug; //static SearchIndex searchIndex; #define YY_NEVER_INTERACTIVE 1 /* ----------------------------------------------------------------- */ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); static int yyread(char *buf,int max_size) { int c=0; while( c < max_size && inputString[inputPosition] ) { *buf = inputString[inputPosition++] ; c++; buf++; } return c; } static void addClass(const char *clName) { if (classDict[clName]==0) { ClassDef *cd=new ClassDef; cd->name=clName; cd->fileName=yyFileName; cd->isFile=FALSE; classList.append(cd); classDict.insert(clName,cd); } } static void addFile(const char *fName) { if (classDict[fName]==0) { ClassDef *fd=new ClassDef; fd->name=fName; fd->fileName=yyFileName; fd->isFile=TRUE; classList.append(fd); classDict.insert(fName,fd); } } static void addBases(const char *clName) { ClassDef *cd=0; if (clName && (cd=classDict[clName])) cd->bases=bases; } static void addMember(const char *memName,const char *memRef,const char *memArgs) { ClassDef *cd=classList.last(); MemberDef *md; md=new MemberDef; md->name=memName; md->anchor=memRef; md->args=memArgs; cd->memberList.append(md); } static void addReference() { //printf("addReference() key: %s ref:%s\n", // docRefName.data(),(docBaseLink+"#"+docAnchor).data()); //if (genIndex && !docRefName.isEmpty() && !docBaseLink.isEmpty()) //{ // if (docAnchor.isEmpty()) // searchIndex.addReference(docRefName,docBaseLink); // else // searchIndex.addReference(docRefName,docBaseLink+"#"+docAnchor); // searchIndex.addWord(docRefName,docRefName,TRUE); //} } QCString unhtmlify(const char *str) { QCString result; const char *p=str; char c; while ((c=*p)!='\0') { if (c!='&') { result+=c; p++; } else { if (strncmp(p,"&",5)==0) { result+='&'; p+=5; } else if (strncmp(p,"<",4)==0) { result+='<'; p+=4; } else if (strncmp(p,">",4)==0) { result+='>'; p+=4; } else /* should not happen */ { result+='&'; p++; } } } return result; } %} %x Start %x SearchClassFile %x ReadClassFile %x CheckClassName %x ReadClassName %x SearchMemberRef %x ReadMemberRef %x SearchMemberName %x ReadMemberName %x ReadOperator %x SearchBaseClasses %x ReadBaseClass %x SearchRefName %x ReadRefName %x SearchArgs %x ReadArgs %x SearchWords %x SkipHTMLTag %x CheckConstructor %x SkipPreformated %% <Start>^"<li>" { BEGIN( SearchClassFile ); } <Start>^"<td"[^\n]*"<h1 align=center>" | // Qt-3.x.x+ <Start>^"<h1 align=center>" { // Qt variant BEGIN( ReadClassName ); } <Start>^("<hr>")?"<h1>" { // Doxygen variant BEGIN( ReadClassName ); } <Start>^"Inherits " { //printf("Inherits found\n"); BEGIN( SearchBaseClasses ); } <Start>^"<h3 class=\"fn\">"/[a-z_A-Z0-9] { // needed due to inconsistency in the Qt docs BEGIN( CheckConstructor ); } <Start>"<pre>" { BEGIN( SkipPreformated ); } <Start>"<a name=\"" { BEGIN( SearchWords ); } <Start>"<" { BEGIN( SkipHTMLTag ); } <Start>"&"[a-zA-Z]+";" <Start,SkipPreformated>[a-z_A-Z][a-z_A-Z0-9]* { //printf("tag: %s#%s ref: %s word: `%s'\n", // docBaseLink.data(),docAnchor.data(), // docRefName.data(),yytext); //if (genIndex && !docRefName.isEmpty() && yyleng>2) // searchIndex.addWord(docRefName, // yytext,FALSE // ); } <SkipPreformated>"</pre>" { BEGIN( Start ); } <SkipPreformated>[^\<\n]+ <CheckConstructor>[a-z_A-Z0-9~:]+ { QCString s=yytext; if (s.find("::")!=-1) { docRefName=yytext; addReference(); nameBug=TRUE; } else { nameBug=FALSE; } BEGIN( Start ); } <SearchWords>[a-z_A-Z0-9]+ { docAnchor = yytext; if (docAnchor=="details" || docAnchor=="_details") { docRefName=className.copy(); addReference(); BEGIN( Start ); } else { BEGIN( SearchRefName ); } } <SearchRefName>"\" doxytag=\"" { BEGIN( ReadRefName ); } <SearchRefName>"\"></a><a" { // HACK: avoid finding links in code fragments BEGIN( Start ); } <SearchRefName>"\"></a>" { // HACK: deal with Qt code if (nameBug) BEGIN( Start ); else BEGIN( ReadRefName ); } <ReadRefName>[a-z_A-Z0-9:\.\+\-]*"operator"[ \t]*("new"|"delete"|("&"("&"|"=")*)|(">"(">"|"=")*)|("<"("<"|"=")*)|("->"[*]*)|[+\-*%/|~!=,\^]|[+\-*%/\^!|~=\[(][=|+\-\])]) { // hmm, looks impressive :-) docRefName=unhtmlify(yytext); addReference(); BEGIN( Start ); } <ReadRefName>[a-z_A-Z0-9~:\.\+\-]+ { //printf("ReadRef=%s\n",yytext); docRefName=yytext; addReference(); BEGIN( Start ); } <SearchBaseClasses>"<a "[a-z_A-Z0-9 .:\=\"\-\+\/\@]+">" { //printf("Search %s\n",yytext); BEGIN( ReadBaseClass ); } <SearchBaseClasses>\n { addBases(className); BEGIN( Start ); } <ReadBaseClass>[a-z_A-Z0-9]+ { bases.append(yytext); BEGIN( SearchBaseClasses ); } <SearchClassFile>"<a class=\"el\" href=\"" { BEGIN( ReadClassFile ); } <SearchClassFile>"<a href=\"" { BEGIN( ReadClassFile ); } <ReadClassName>[a-z_A-Z0-9:\.\-\+]+ { className=yytext; BEGIN( CheckClassName); } <CheckClassName>"Class Reference" { //printf("className=%s\n",className.data()); addClass(className); BEGIN( Start ); } <CheckClassName>"File Reference" { //printf("className=%s\n",className.data()); addFile(className); BEGIN( Start ); } <CheckClassName>[a-z_A-Z0-9]+ { // not a class file className.resize(0); BEGIN( Start ); } <ReadClassFile>[a-z_A-Z0-9.\-\+]+ { classFile=yytext; BEGIN( SearchMemberRef ); } <SearchMemberRef,ReadClassFile>"#" { if (YY_START==ReadClassFile) { classFile=yyFileName; } BEGIN( ReadMemberRef ); } <ReadMemberRef>[a-z_A-Z0-9]+ { memberRef=yytext; BEGIN( SearchMemberName ); } <SearchMemberName>"<strong>"|"<b>" { // <strong> is for qt-1.44, <b> is for qt-2.00 BEGIN( ReadMemberName ); } <SearchMemberName>[a-z_A-Z~] { unput(*yytext); BEGIN( ReadMemberName ); } <ReadMemberName>"operator" { memberName="operator"; BEGIN( ReadOperator ); } <ReadOperator>[+\-*/%\^&|~!=()\[\]] { memberName+=*yytext; } <ReadOperator>"<" { memberName+="<"; } <ReadOperator>">" { memberName+=">"; } <ReadOperator>"new" { memberName+=" new"; } <ReadOperator>"delete" { memberName+=" delete"; } <ReadOperator>"<" { BEGIN( SearchArgs ); } <ReadMemberName>[a-z_A-Z0-9]+ { memberName=yytext; BEGIN( SearchArgs ); } <SearchArgs>"</a>" { //printf("SearchArg className=%s memberName=%s\n",className.data(),memberName.data()); if (!className.isEmpty() && !memberName.isEmpty()) BEGIN( ReadArgs ); else BEGIN( Start ); } <ReadArgs>"&" { memberArgs+="&"; } <ReadArgs>"<" { memberArgs+="<"; } <ReadArgs>">" { memberArgs+=">"; } <ReadArgs>" " { memberArgs+=" "; } /* <ReadArgs>[{}] { // handle enums memberArgs.resize(0); addMember(memberName,memberRef,memberArgs); if (*yytext=='}') BEGIN( Start ); else BEGIN( SearchClassFile ); } */ <ReadArgs>"<"|"\n" { //printf("adding member %s\n",memberName.data()); memberArgs=memberArgs.stripWhiteSpace(); //if (newClass) //{ // newClass=FALSE; // addClass(className); //} addMember(memberName,memberRef,memberArgs); memberArgs.resize(0); if (*yytext=='<') BEGIN( SkipHTMLTag); else BEGIN( Start ); } <ReadArgs>. { memberArgs+=(*yytext)&0x7f; } <SkipHTMLTag>">" { BEGIN( Start ); } <SkipHTMLTag>[a-zA-Z]+ <*>. <*>\n { yyLineNr++; if (YY_START!=SkipHTMLTag) BEGIN( Start ); } %% /*@ ---------------------------------------------------------------------------- */ void parse(QCString &s) { bases.clear(); nameBug = FALSE; //newClass = TRUE; inputString = s; inputPosition = 0; yyLineNr = 0; tagYYrestart( tagYYin ); BEGIN( Start ); tagYYlex(); //printf("Number of lines scanned: %d\n",yyLineNr); } void parseFile(QFileInfo &fi) { fprintf(stderr,"Parsing file %s...\n",fi.fileName().data()); QFile f; f.setName(fi.absFilePath()); if (f.open(IO_ReadOnly)) { yyFileName = fi.fileName(); className.resize(0); memberName.resize(0); //printf("Parsing file %s...\n",fi.fileName().data()); QCString input(fi.size()+1); docBaseLink=fi.fileName(); docRefName=fi.fileName().copy(); //searchIndex.addReference(docRefName,docBaseLink); //searchIndex.addWord(docRefName,docRefName,TRUE); f.readBlock(input.data(),fi.size()); input.at(fi.size())='\0'; parse(input); } else { fprintf(stderr,"Warning: Cannot open file %s\n",fi.fileName().data()); } } void parseFileOrDir(const char *fileName) { QFileInfo fi(fileName); if (fi.exists()) { if (fi.isFile()) { parseFile(fi); } else if (fi.isDir()) { QDir dir(fileName); dir.setFilter( QDir::Files ); dir.setNameFilter( "*.html" ); const QFileInfoList *list = dir.entryInfoList(); QFileInfoListIterator it( *list ); QFileInfo *cfi; for ( it.toFirst() ; (cfi=it.current()) ; ++it) { if (cfi->isFile()) { parseFile(*cfi); } } } } else { fprintf(stderr,"Warning: File %s does not exist\n",fileName); } } void usage(const char *name) { fprintf(stderr,"Doxytag version %s\nCopyright Dimitri van Heesch 1997-2004\n\n", versionString); fprintf(stderr," Generates a tag file and/or a search index for a set of HTML files\n\n"); fprintf(stderr,"Usage: %s [-t tag_file] [ html_file [html_file...] ]\n",name); fprintf(stderr,"Options:\n"); fprintf(stderr," -t <tag_file> Generate tag file <tag_file>.\n"); fprintf(stderr,"If no HTML files are given all files in the current dir that\n" "have a .html extension are parsed.\n\n"); exit(1); } const char *getArg(int argc,char **argv,int &optind,const char c) { char *s=0; if (strlen(&argv[optind][2])>0) s=&argv[optind][2]; else if (optind+1<argc) s=argv[++optind]; else { fprintf(stderr,"option -%c requires an argument\n",c); exit(1); } return s; } int main(int argc,char **argv) { QCString tagName; QCString indexName; int optind=1; const char *arg; while (optind<argc && argv[optind][0]=='-') { switch(argv[optind][1]) { case 't': arg=getArg(argc,argv,optind,'t'); tagName=arg; break; case 's': arg=getArg(argc,argv,optind,'s'); indexName=arg; break; case 'h': case '?': usage(argv[0]); break; default: fprintf(stderr,"Unknown option -%c\n",argv[optind][1]); usage(argv[0]); } optind++; } genTag = !tagName.isEmpty(); genIndex = !indexName.isEmpty(); if (!genTag && !genIndex) { fprintf(stderr,"Nothing to do !\n\n"); usage(argv[0]); } int i; if (optind>=argc) { parseFileOrDir("."); } else { for (i=optind;i<argc;i++) { parseFileOrDir(argv[i]); } } if (genIndex) { fprintf(stderr,"Error: doxytag cannot be used to generate a search index anymore.\n" "This functionality has been integrated into doxygen.\n"); // printf("Writing search index\n"); // if (!searchIndex.saveIndex(indexName)) // { // fprintf(stderr,"Error: Could not write search index\n"); // } // QFileInfo fi(indexName); // if (fi.exists()) // { // QCString dir=convertToQCString(fi.dir().absPath()); // fi.setFile(dir+"/search.png"); // if (!fi.exists()) writeSearchButton(dir); // fi.setFile(dir+"/doxygen.png"); // if (!fi.exists()) writeLogo(dir); // fi.setFile(dir+"/search.cgi"); // if (!fi.exists()) // { // QFile f; // f.setName(dir+"/search.cgi"); // if (f.open(IO_WriteOnly)) // { // QTextStream t(&f); // t << "#!/bin/sh" << endl // << "DOXYSEARCH=" << endl // << "DOXYPATH=" << endl // << "if [ -f $DOXYSEARCH ]" << endl // << "then" << endl // << " $DOXYSEARCH $DOXYPATH" << endl // << "else" << endl // << " echo \"Content-Type: text/html\"" << endl // << " echo \"\"" << endl // << " echo \"<H1>Error: $DOXYSEARCH not found. Check cgi script!\"" << endl // << "fi" << endl; // f.close(); // } // else // { // fprintf(stderr,"Error: could not open file %s for writing\n",(dir+"/search.cgi").data()); // } // } // } } if (genTag) { QFile f; f.setName(tagName); if (f.open(IO_WriteOnly)) { QTextStream t(&f); t << "<tagfile>" << endl; ClassDef *cd=classList.first(); while (cd) { t << " <compound kind=\""; if (cd->isFile) t << "file"; else t << "class"; t << "\">" << endl; t << " <name>" << convertToXML(cd->name) << "</name>" << endl; char *base=cd->bases.first(); while (base) { t << " <base>" << convertToXML(base) << "</base>" << endl; base=cd->bases.next(); } t << " <filename>" << convertToXML(cd->fileName) << "</filename>" << endl; MemberDef *md=cd->memberList.first(); while (md) { t << " <member kind=\"function\">" << endl; t << " <name>" << convertToXML(md->name) << "</name>" << endl; t << " <anchor>" << convertToXML(md->anchor) << "</anchor>" << endl; t << " <arglist>" << convertToXML(md->args) << "</arglist>" << endl; t << " </member>" << endl; md=cd->memberList.next(); } t << " </compound>" << endl; cd=classList.next(); } t << "</tagfile>" << endl; } else { fprintf(stderr,"Error: Could not write tag file %s\n",tagName.data()); } } return 0; } extern "C" { int tagYYwrap() { return 1 ; } };