从main.c
开始。
extern int main (int __unused__ argc, char **argv) { cookedArgs *args; #ifdef VMS extern int getredirection (int *ac, char ***av); /* do wildcard expansion and I/O redirection */ getredirection (&argc, &argv); #endif #ifdef AMIGA /* This program doesn't work when started from the Workbench */ if (argc == 0) exit (1); #endif #ifdef __EMX__ _wildcard (&argc, &argv); /* expand wildcards in argument list */ #endif #if defined (macintosh) && BUILD_MPW_TOOL == 0 argc = ccommand (&argv); #endif setCurrentDirectory (); setExecutableName (*argv++); checkRegex (); args = cArgNewFromArgv (argv); previewFirstOption (args); testEtagsInvocation (); initializeParsing (); initOptions (); readOptionConfiguration (); verbose ("Reading initial options from command line "); parseOptions (args); checkOptions (); makeTags (args); /* Clean up. */ cArgDelete (args); freeKeywordTable (); freeRoutineResources (); freeSourceFileResources (); freeTagFileResources (); freeOptionResources (); freeParserResources (); freeRegexResources (); exit (0); return 0; }
一开始cTags维护了一个自己的cookedArgs
类型的参数列表,args
。之后根据不同系统的情况,重新定义了argc
与argv
。
第一个函数setCurrentDirectory()
定义在routines.c
中,根据系统不同,定义了CurrentDirectory
一个全局变量(使用getcwd()
),并在最后增加了OUTPUT_PATH_SEPARATOR
。
第二个函数setExecutableName (*argv++)
定义了ExecutableProgram
(主文件路径)和ExecutableName
(主文件名)两个全局变量。
第三个函数checkRegex()
是regex
库的内部函数,检查regex
能否正常工作.
之后,args = cArgNewFromArgv (argv)
将默认的选项格式加以处理转换到args
中.
previewFirstOption (args)
处理-v
和没有选项的情况(直接退出)。
testEtagsInvocation ()
检测是否有-e
选项,若有,则初始化etags
。
initializeParsing()
是重要的一步,它检查每一个内建的语言,它们的名称是否合法,它们的regex
是否存在,如果是,则将它们插入LanguageTable[]
数组中。这个数组的成员类型是parserDefinition*
,而parserDefinition
的定义如下:
typedef struct { /* defined by parser */ char* name; /* name of language */ kindOption* kinds; /* tag kinds handled by parser */ unsigned int kindCount; /* size of `kinds' list */ const char *const *extensions; /* list of default extensions */ const char *const *patterns; /* list of default file name patterns */ parserInitialize initialize; /* initialization routine, if needed (初始化的函数指针) */ simpleParser parser; /* simple parser (common case) */ rescanParser parser2; /* rescanning parser (unusual case) */ boolean regex; /* is this a regex parser? */ /* used internally */ unsigned int id; /* id assigned to language */ boolean enabled; /* currently enabled? */ stringList* currentPatterns; /* current list of file name patterns */ stringList* currentExtensions; /* current list of extensions */ } parserDefinition;
之后initializeParsing
调用initializeParsers ()
,即循环调用每个LanguageTable[]
中语言的LanguageTable[i]->initialize()
,进行语言初始化工作(建立hash表等)。
接下来的一大步是initOptions()
,设置默认选项,建立默认语言映射,自动添加.git
等控制文件到--exclude
选项中。
readOptionConfiguration ()
读取目录下的.ctags
配置文件与环境变量。
parseOptions (args)
把之前简单分拆的选项细分成longOptions
和shortOptions
,并设置Options
结构的相应位。
checkOptions()
是对选项的静态检查。
makeTags(args)
,是最重要的部分,代码如下:
static void makeTags (cookedArgs *args) { clock_t timeStamps [3]; boolean resize = FALSE; boolean files = (boolean)(! cArgOff (args) || Option.fileList != NULL || Option.filter); if (! files) { if (filesRequired ()) error (FATAL, "No files specified. Try "%s --help".", getExecutableName ()); else if (! Option.recurse && ! etagsInclude ()) return; } #define timeStamp(n) timeStamps[(n)]=(Option.printTotals ? clock():(clock_t)0) if (! Option.filter) openTagFile (); timeStamp (0); if (! cArgOff (args)) { verbose ("Reading command line arguments "); resize = createTagsForArgs (args); } if (Option.fileList != NULL) { verbose ("Reading list file "); resize = (boolean) (createTagsFromListFile (Option.fileList) || resize); } if (Option.filter) { verbose ("Reading filter input "); resize = (boolean) (createTagsFromFileInput (stdin, TRUE) || resize); } if (! files && Option.recurse) resize = recurseIntoDirectory ("."); timeStamp (1); if (! Option.filter) closeTagFile (resize); timeStamp (2); if (Option.printTotals) printTotals (timeStamps); #undef timeStamp }
简而言之,就是以下几步:
openTagFile (resize)
打开Tag文件;
根据选项不同,分别调用createTagsForArgs (args)
、createTagsFromListFile (Option.fileList)
、createTagsFromFileInput (stdin, TRUE)
、recurseIntoDirectory (".")
,而它们都调用了createTagsForEntry(filename)
,经过检查后,最终调用了parse.c
中的parseFile(filename)
;
closeTagFile (resize)
关闭Tag文件。
最终,通过如下几步,free
掉前几步申请的动态内存,防止内存泄漏:
cArgDelete (args); //释放args[] freeKeywordTable (); //释放KeywordTable[] freeRoutineResources (); //释放CurrentDirectory freeSourceFileResources (); //释放File(一个用于读取文件的变量) freeTagFileResources (); //释放TagFile变量 freeOptionResources (); //释放Options变量 freeParserResources (); //释放LanguageTable[] freeRegexResources (); //释放Regex库内存