cTags源碼分析(1) - 概要

main.c開始。git

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。以後根據不一樣系統的狀況,從新定義了argcargv數組

第一個函數setCurrentDirectory()定義在routines.c中,根據系統不一樣,定義了CurrentDirectory一個全局變量(使用getcwd()),並在最後增長了OUTPUT_PATH_SEPARATOR函數

第二個函數setExecutableName (*argv++)定義了ExecutableProgram(主文件路徑)和ExecutableName(主文件名)兩個全局變量。ui

第三個函數checkRegex()regex庫的內部函數,檢查regex可否正常工做.this

以後,args = cArgNewFromArgv (argv)將默認的選項格式加以處理轉換到args中.指針

previewFirstOption (args)處理-v和沒有選項的狀況(直接退出)。code

testEtagsInvocation ()檢測是否有-e選項,如有,則初始化etags內存

initializeParsing()是重要的一步,它檢查每個內建的語言,它們的名稱是否合法,它們的regex是否存在,若是是,則將它們插入LanguageTable[]數組中。這個數組的成員類型是parserDefinition*,而parserDefinition的定義以下:ci

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表等)。get

接下來的一大步是initOptions(),設置默認選項,創建默認語言映射,自動添加.git等控制文件到--exclude選項中。

readOptionConfiguration ()讀取目錄下的.ctags配置文件與環境變量。

parseOptions (args)把以前簡單分拆的選項細分紅longOptionsshortOptions,並設置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
}

簡而言之,就是如下幾步:

  1. openTagFile (resize)打開Tag文件;

  2. 根據選項不一樣,分別調用createTagsForArgs (args)createTagsFromListFile (Option.fileList)createTagsFromFileInput (stdin, TRUE)recurseIntoDirectory ("."),而它們都調用了createTagsForEntry(filename),通過檢查後,最終調用了parse.c中的parseFile(filename);

  3. closeTagFile (resize)關閉Tag文件。

最終,經過以下幾步,free掉前幾步申請的動態內存,防止內存泄漏:

    cArgDelete (args);  //釋放args[]
    freeKeywordTable ();  //釋放KeywordTable[]
    freeRoutineResources ();  //釋放CurrentDirectory
    freeSourceFileResources ();  //釋放File(一個用於讀取文件的變量)
    freeTagFileResources ();  //釋放TagFile變量
    freeOptionResources ();  //釋放Options變量
    freeParserResources ();  //釋放LanguageTable[]
    freeRegexResources ();  //釋放Regex庫內存
相關文章
相關標籤/搜索