从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库内存