1 /******************************************************************************
2  *
3  * Copyright (C) 2009-2015 by Joenio Costa.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15 
16 /** @file
17  *  @brief Code parse based on doxyapp by Dimitri van Heesch
18  *
19  */
20 
21 #include <stdlib.h>
22 #if !defined(_WIN32) || defined(__CYGWIN__)
23   #include <unistd.h>
24 #else
25   #include <windows.h>
26 #endif
27 #include "version.h"
28 #include "doxygen.h"
29 #include "outputgen.h"
30 #include "parserintf.h"
31 #include "classlist.h"
32 #include "config.h"
33 #include "filedef.h"
34 #include "util.h"
35 #include "filename.h"
36 #include "arguments.h"
37 #include "memberlist.h"
38 #include "types.h"
39 #include <string>
40 #include <cstdlib>
41 #include <sstream>
42 #include <map>
43 #include "qcstring.h"
44 #include "namespacedef.h"
45 #include "portable.h"
46 #include "dir.h"
47 
48 class Doxyparse : public CodeOutputInterface
49 {
50   public:
Doxyparse(const FileDef * fd)51     Doxyparse(const FileDef *fd) : m_fd(fd) {}
~Doxyparse()52    ~Doxyparse() {}
53 
54     // these are just null functions, they can be used to produce a syntax highlighted
55     // and cross-linked version of the source code, but who needs that anyway ;-)
codify(const QCString &)56     void codify(const QCString &) override {}
writeCodeLink(CodeSymbolType,const QCString &,const QCString &,const QCString &,const QCString &,const QCString &)57     void writeCodeLink(CodeSymbolType,const QCString &,const QCString &,const QCString &,const QCString &,const QCString &)  override {}
startCodeLine(bool)58     void startCodeLine(bool) override {}
endCodeLine()59     void endCodeLine() override {}
writeCodeAnchor(const QCString &)60     void writeCodeAnchor(const QCString &) override {}
startFontClass(const QCString &)61     void startFontClass(const QCString &) override {}
endFontClass()62     void endFontClass() override {}
writeLineNumber(const QCString &,const QCString &,const QCString &,int,bool)63     void writeLineNumber(const QCString &,const QCString &,const QCString &,int,bool) override {}
writeTooltip(const QCString &,const DocLinkInfo &,const QCString &,const QCString &,const SourceLinkInfo &,const SourceLinkInfo &)64     virtual void writeTooltip(const QCString &,const DocLinkInfo &,
65                               const QCString &,const QCString &,const SourceLinkInfo &,
66                               const SourceLinkInfo &) override {}
setCurrentDoc(const Definition *,const QCString &,bool)67     void setCurrentDoc(const Definition *,const QCString &,bool) override {}
addWord(const QCString &,bool)68     void addWord(const QCString &,bool) override {}
startCodeFragment(const QCString &)69     void startCodeFragment(const QCString &) override {}
endCodeFragment(const QCString &)70     void endCodeFragment(const QCString &) override {}
71 
linkableSymbol(int l,const char * sym,Definition * symDef,Definition * context)72     void linkableSymbol(int l, const char *sym, Definition *symDef, Definition *context)
73     {
74       if (!symDef) {
75         // in this case we have a local or external symbol
76 
77         // TODO record use of external symbols
78         // TODO must have a way to differentiate external symbols from local variables
79       }
80     }
81 
82   private:
83     const FileDef *m_fd;
84 };
85 
86 static bool is_c_code = true;
87 
findXRefSymbols(FileDef * fd)88 static void findXRefSymbols(FileDef *fd)
89 {
90   // get the interface to a parser that matches the file extension
91   auto intf=Doxygen::parserManager->getCodeParser(fd->getDefFileExtension());
92 
93   // get the programming language from the file name
94   SrcLangExt lang = getLanguageFromFileName(fd->name());
95 
96   // reset the parsers state
97   intf->resetCodeParserState();
98 
99   // create a new backend object
100   Doxyparse *parse = new Doxyparse(fd);
101 
102   // parse the source code
103   intf->parseCode(*parse, 0, fileToString(fd->absFilePath()), lang, FALSE, 0, fd);
104 
105   // dismiss the object.
106   delete parse;
107 }
108 
ignoreStaticExternalCall(const MemberDef * context,const MemberDef * md)109 static bool ignoreStaticExternalCall(const MemberDef *context, const MemberDef *md) {
110   if (md->isStatic()) {
111     if(md->getFileDef() && context->getFileDef()) {
112       if(md->getFileDef()->getOutputFileBase() == context->getFileDef()->getOutputFileBase())
113         // TODO ignore prefix of file
114         return false;
115       else
116         return true;
117     }
118     else {
119       return false;
120     }
121   }
122   else {
123     return false;
124   }
125 }
126 
startYamlDocument()127 static void startYamlDocument() {
128   printf("---\n");
129 }
printFile(std::string file)130 static void printFile(std::string file) {
131   printf("%s:\n", file.c_str());
132 }
printModule(std::string module)133 static void printModule(std::string module) {
134   printf("  \"%s\":\n", unescapeCharsInString(module.c_str()).data());
135 }
printClassInformation(std::string information)136 static void printClassInformation(std::string information) {
137   printf("    information: %s\n", information.c_str());
138 }
printInherits()139 static void printInherits() {
140   printf("    inherits:\n");
141 }
printInheritance(std::string base_class)142 static void printInheritance(std::string base_class) {
143   printf("      - \"%s\"\n", base_class.c_str());
144 }
printDefines()145 static void printDefines() {
146   printf("    defines:\n");
147 }
printDefinition(std::string type,std::string signature,int line)148 static void printDefinition(std::string type, std::string signature, int line) {
149   printf("      - \"%s\":\n", signature.substr(0, 1022).c_str());
150   printf("          type: %s\n", type.c_str());
151   printf("          line: %d\n", line);
152 }
printProtection(std::string protection)153 static void printProtection(std::string protection) {
154   printf("          protection: %s\n", protection.c_str());
155 }
printPrototypeYes()156 static void printPrototypeYes() {
157   printf("          prototype: yes\n");
158 }
printNumberOfLines(int lines)159 static void printNumberOfLines(int lines) {
160   printf("          lines_of_code: %d\n", lines);
161 }
printNumberOfArguments(size_t arguments)162 static void printNumberOfArguments(size_t arguments) {
163   printf("          parameters: %zu\n", arguments);
164 }
printUses()165 static void printUses() {
166   printf("          uses:\n");
167 }
printReferenceTo(std::string type,std::string signature,std::string defined_in)168 static void printReferenceTo(std::string type, std::string signature, std::string defined_in) {
169   printf("            - \"%s\":\n", signature.substr(0, 1022).c_str());
170   printf("                type: %s\n", type.c_str());
171   printf("                defined_in: \"%s\"\n", unescapeCharsInString(defined_in.c_str()).data());
172 }
printNumberOfConditionalPaths(const MemberDef * md)173 static void printNumberOfConditionalPaths(const MemberDef* md) {
174   printf("          conditional_paths: %d\n", md->numberOfFlowKeyWords());
175 }
176 
isPartOfCStruct(const MemberDef * md)177 static int isPartOfCStruct(const MemberDef * md) {
178   return is_c_code && md->getClassDef() != NULL;
179 }
180 
sanitizeString(std::string data)181 std::string sanitizeString(std::string data) {
182   QCString new_data = QCString(data.c_str());
183   new_data = substitute(new_data,"\"", "");
184   new_data = substitute(new_data,"\'", ""); // https://github.com/analizo/analizo/issues/138
185   return !new_data.isEmpty() ? new_data.data() : "";
186 }
187 
argumentData(const Argument & argument)188 std::string argumentData(const Argument &argument) {
189   std::string data = "";
190   if (argument.type.size() > 1)
191     data = sanitizeString(argument.type.data());
192   else if (!argument.name.isEmpty())
193     data = sanitizeString(argument.name.data());
194   return data;
195 }
196 
functionSignature(const MemberDef * md)197 std::string functionSignature(const MemberDef* md) {
198   std::string signature = sanitizeString(md->name().data());
199   if(md->isFunction()){
200     const ArgumentList &argList = md->argumentList();
201     signature += "(";
202     auto it = argList.begin();
203     if(it!=argList.end()) {
204       signature += argumentData(*it);
205       for(++it; it!=argList.end(); ++it) {
206         signature += std::string(",") + argumentData(*it);
207       }
208     }
209     signature += ")";
210   }
211   return signature;
212 }
213 
referenceTo(const MemberDef * md)214 static void referenceTo(const MemberDef* md) {
215   std::string type = md->memberTypeName().data();
216   std::string defined_in = "";
217   std::string signature = "";
218   if (isPartOfCStruct(md)) {
219     signature = md->getClassDef()->name().data() + std::string("::") + functionSignature(md);
220     defined_in = md->getClassDef()->getFileDef()->getOutputFileBase().data();
221   }
222   else {
223     signature = functionSignature(md);
224     if (md->getClassDef()) {
225       defined_in = md->getClassDef()->name().data();
226     }
227     else if (md->getFileDef()) {
228       defined_in = md->getFileDef()->getOutputFileBase().data();
229     }
230     else if (md->getNamespaceDef()) {
231       defined_in = md->getNamespaceDef()->name().data();
232     }
233   }
234   printReferenceTo(type, signature, defined_in);
235 }
236 
protectionInformation(Protection protection)237 void protectionInformation(Protection protection) {
238   if (protection == Public) {
239     printProtection("public");
240   }
241   else if (protection == Protected) {
242     printProtection("protected");
243   }
244   else if (protection == Private) {
245     printProtection("private");
246   }
247   else if (protection == Package) {
248     printProtection("package");
249   }
250 }
251 
cModule(const ClassDef * cd)252 void cModule(const ClassDef* cd) {
253   const MemberList* ml = cd->getMemberList(MemberListType_variableMembers);
254   if (ml) {
255     const FileDef *fd = cd->getFileDef();
256     const MemberList *fd_ml = fd->getMemberList(MemberListType_allMembersList);
257     if (!fd_ml || fd_ml->size() == 0) {
258       printModule(fd->getOutputFileBase().data());
259       printDefines();
260     }
261     for (const auto &md : *ml) {
262       printDefinition("variable", cd->name().data() + std::string("::") + md->name().data(), md->getDefLine());
263       protectionInformation(md->protection());
264     }
265   }
266 }
267 
checkOverrideArg(const ArgumentList & argList,const MemberDef * md)268 static bool checkOverrideArg(const ArgumentList &argList, const MemberDef *md) {
269   if(!md->isFunction() || argList.empty()){
270     return false;
271   }
272 
273   for (const Argument &argument : argList) {
274     if(md->name() == argument.name) {
275       return true;
276     }
277   }
278 
279   return false;
280 }
281 
functionInformation(const MemberDef * md)282 void functionInformation(const MemberDef* md) {
283   std::string temp = "";
284   int size = md->getEndBodyLine() - md->getStartBodyLine() + 1;
285   printNumberOfLines(size);
286   const ArgumentList &argList = md->argumentList();
287   if (!argList.empty())
288   {
289     temp = argumentData(argList.front());
290 // TODO: This is a workaround; better not include "void" in argList, in the first place.
291     if (temp!="void")
292     {
293       printNumberOfArguments(argList.size());
294     }
295   }
296 
297   printNumberOfConditionalPaths(md);
298   auto refList = md->getReferencesMembers();
299   if (!refList.empty()) {
300     printUses();
301     for (const auto &rmd : refList) {
302       if (rmd->definitionType() == Definition::TypeMember && !ignoreStaticExternalCall(md, rmd) && !checkOverrideArg(argList, rmd)) {
303         referenceTo(rmd);
304       }
305     }
306   }
307 }
308 
prototypeInformation(const MemberDef * md)309 void prototypeInformation(const MemberDef* md) {
310   printPrototypeYes();
311   const ArgumentList &argList = md->argumentList();
312   printNumberOfArguments(argList.size());
313 }
314 
lookupSymbol(const Definition * d)315 static void lookupSymbol(const Definition *d) {
316   if (d->definitionType() == Definition::TypeMember) {
317     const MemberDef *md = dynamic_cast<const MemberDef*>(d);
318     std::string type = md->memberTypeName().data();
319     std::string signature = functionSignature(md);
320     printDefinition(type, signature, md->getDefLine());
321     protectionInformation(md->protection());
322     if (md->isFunction() && md->isPrototype()) {
323       prototypeInformation(md);
324     }
325     else if (md->isFunction()) {
326       functionInformation(md);
327     }
328   }
329 }
330 
listMembers(const MemberList * ml)331 void listMembers(const MemberList *ml) {
332   if (ml) {
333     for (const auto &md : *ml) {
334       lookupSymbol((Definition*) md);
335     }
336   }
337 }
338 
listAllMembers(const ClassDef * cd)339 void listAllMembers(const ClassDef* cd) {
340   // methods
341   listMembers(cd->getMemberList(MemberListType_functionMembers));
342   // constructors
343   listMembers(cd->getMemberList(MemberListType_constructors));
344   // attributes
345   listMembers(cd->getMemberList(MemberListType_variableMembers));
346 }
347 
classInformation(const ClassDef * cd)348 static void classInformation(const ClassDef* cd) {
349   if (is_c_code) {
350     cModule(cd);
351   } else {
352     printModule(cd->name().data());
353     if (!cd->baseClasses().empty()) {
354       printInherits();
355       for (const auto &bcd : cd->baseClasses()) {
356         printInheritance(sanitizeString(bcd.classDef->name().data()));
357       }
358     }
359     if(cd->isAbstract()) {
360       printClassInformation("abstract class");
361     }
362     printDefines();
363     listAllMembers(cd);
364   }
365 }
366 
checkLanguage(std::string & filename,std::string extension)367 static bool checkLanguage(std::string& filename, std::string extension) {
368   if (filename.find(extension, filename.size() - extension.size()) != std::string::npos) {
369     return true;
370   } else {
371     return false;
372   }
373 }
374 
375 /* Detects the programming language of the project. Actually, we only care
376  * about whether it is a C project or not. */
detectProgrammingLanguage(FileNameLinkedMap & fnli)377 static void detectProgrammingLanguage(FileNameLinkedMap &fnli) {
378   for (const auto &fn : fnli) {
379     std::string filename = fn->fileName().str();
380     if (
381         checkLanguage(filename, ".cc") ||
382         checkLanguage(filename, ".cxx") ||
383         checkLanguage(filename, ".cpp") ||
384         checkLanguage(filename, ".java") ||
385         checkLanguage(filename, ".py") ||
386         checkLanguage(filename, ".pyw") ||
387         checkLanguage(filename, ".cs")
388        ) {
389       is_c_code = false;
390     }
391   }
392 }
393 
listSymbols()394 static void listSymbols() {
395   detectProgrammingLanguage(*Doxygen::inputNameLinkedMap);
396 
397   // iterate over the input files
398   for (const auto &fn : *Doxygen::inputNameLinkedMap) {
399     for (const auto &fd : *fn) {
400       printFile(fd->absFilePath().data());
401       MemberList *ml = fd->getMemberList(MemberListType_allMembersList);
402       if (ml && ml->size() > 0) {
403         printModule(fd->getOutputFileBase().data());
404         printDefines();
405         listMembers(ml);
406       }
407 
408       ClassDefSet visitedClasses;
409       for (const auto &cd : fd->getClasses()) {
410         if (visitedClasses.find(cd)==visitedClasses.end()) {
411           classInformation(cd);
412           visitedClasses.insert(cd);
413         }
414       }
415     }
416   }
417   // TODO print external symbols referenced
418 }
419 
main(int argc,char ** argv)420 int main(int argc,char **argv) {
421   int locArgc = argc;
422 
423   if (locArgc == 2)
424   {
425     if (!strcmp(argv[1],"--help"))
426     {
427       printf("Usage: %s [source_file | source_dir]\n",argv[0]);
428       exit(0);
429     }
430     else if (!strcmp(argv[1],"--version"))
431     {
432       printf("%s version: %s\n",argv[0],getFullVersion());
433       exit(0);
434     }
435   }
436 
437   if (locArgc!=2)
438   {
439     printf("Usage: %s [source_file | source_dir]\n",argv[0]);
440     exit(1);
441   }
442 
443   // initialize data structures
444   initDoxygen();
445 
446   // check and finalize the configuration
447   checkConfiguration();
448   adjustConfiguration();
449 
450   // setup the non-default configuration options
451 
452   // we need a place to put intermediate files
453   std::ostringstream tmpdir;
454   unsigned int pid = Portable::pid();
455   if (!Portable::getenv("TMP").isEmpty())
456     tmpdir << Portable::getenv("TMP") << "/doxyparse-" << pid;
457   else if (!Portable::getenv("TEMP").isEmpty())
458     tmpdir << Portable::getenv("TEMP") << "/doxyparse-" << pid;
459   else
460     tmpdir << "doxyparse-" << pid;
461 
462   Config_updateString(OUTPUT_DIRECTORY,tmpdir.str().c_str());
463   // enable HTML (fake) output to omit warning about missing output format
464   Config_updateBool(GENERATE_HTML,TRUE);
465   // disable latex output
466   Config_updateBool(GENERATE_LATEX,FALSE);
467   // be quiet
468   Config_updateBool(QUIET,TRUE);
469   // turn off warnings
470   Config_updateBool(WARNINGS,FALSE);
471   Config_updateBool(WARN_IF_UNDOCUMENTED,FALSE);
472   Config_updateBool(WARN_IF_DOC_ERROR,FALSE);
473   // Extract as much as possible
474   Config_updateBool(EXTRACT_ALL,TRUE);
475   Config_updateBool(EXTRACT_STATIC,TRUE);
476   Config_updateBool(EXTRACT_PRIVATE,TRUE);
477   Config_updateBool(EXTRACT_LOCAL_METHODS,TRUE);
478   Config_updateBool(EXTRACT_PACKAGE,TRUE);
479   // Extract source browse information, needed
480   // to make doxygen gather the cross reference info
481   Config_updateBool(SOURCE_BROWSER,TRUE);
482   // find functions call between modules
483   Config_updateBool(CALL_GRAPH,TRUE);
484   // loop recursive over input files
485   Config_updateBool(RECURSIVE,TRUE);
486   // add file extensions
487   Config_updateList(FILE_PATTERNS, { "*.cc", "*.cxx", "*.cpp", "*.java",
488                                      "*.py", "*.pyw", "*.cs", "*.c", "*.h", "*.hh", "*.hpp"});
489   // set the input
490   StringVector inputList;
491   for (int i = 1; i < argc; i++) {
492     if (strcmp(argv[i], "-") == 0) {
493       char filename[1024];
494       while (1) {
495         scanf("%s[^\n]", filename);
496         if (feof(stdin)) {
497           break;
498         }
499         inputList.push_back(filename);
500       }
501     } else {
502       inputList.push_back(argv[i]);
503     }
504   }
505   Config_updateList(INPUT,inputList);
506   if (inputList.empty()) {
507     exit(0);
508   }
509 
510   // parse the files
511   parseInput();
512 
513   // iterate over the input files
514   for (const auto &fn : *Doxygen::inputNameLinkedMap) {
515     for (const auto &fd : *fn) {
516       // get the references (linked and unlinked) found in this file
517       findXRefSymbols(fd.get());
518     }
519   }
520 
521   Dir thisDir;
522   // remove temporary files
523   if (!Doxygen::filterDBFileName.isEmpty()) thisDir.remove(Doxygen::filterDBFileName.str());
524 
525   // clean up after us
526   thisDir.rmdir(Config_getString(OUTPUT_DIRECTORY).str());
527 
528   startYamlDocument();
529   listSymbols();
530 
531   std::string cleanup_command = "rm -rf ";
532   cleanup_command += tmpdir.str();
533   system(cleanup_command.c_str());
534 
535   exit(0);
536 }
537