秋风技术 发表于 2021-10-19 21:55:50

Mono源码学习-怎样解析config文件

起因

为什么要选择解析config文件,主要是mono源码比较巨大且复杂.先从一个模块开始读源码.再者就是对用c语言如何解析xml文件,还是布满好奇的.可能是因为一直用c#的原因,因为.Net Framework 提供xpath/xml linq等方式解析,利用方便,也从来没有想过了解其中的原理.利用的时间也是针对业务的.以是这里带着这份好奇,开始了.

本文中涉及到config文件,其实默认存放在ect/mono目录下的.其实说mono解析config文件.不如说用eglib来解析简单类型xml文件.
函数调用流程

//--driver.c        mono_main                  //mono主函数//---mono-config.c                mono_config_parse                        mono_config_parse_file                                mono_config_parse_file_with_context                                        g_file_get_contents                                 //获取config文件内容和文件大小                                        mono_config_parse_xml_with_context   //真正解析xml                                                mono_config_init                                                                                                                                         //创建一个config_handlers哈希表                                                g_markup_parse_context_new                                                g_markup_parse_context_parse                                        //按字符解析xml                                                g_markup_parse_context_end_parse                                                g_markup_parse_context_free             //解析xml结束先从mono_main函数开始,源码函数行数较多,只保留跟解析config相关代码.

int mono_main (int argc, char* argv[]){        //只保留,解析config文件相关变量        //...        char *config_file = NULL;        //....        for (i = 1; i < argc; ++i)         {                if (strcmp(argv, "--config") == 0) { //在运行时,获取--config对应的                        if (i + 1 >= argc) {                                fprintf(stderr, "error: --config requires a filename argument\n");                                return 1;                        }                        config_file = argv[++i];       //假如不指定--config选项,config_file为NULL                }        }        //...        mono_set_rootdir ();                              //设置etc和lib目录        //....        /* Parse gac loading options before loading assemblies. */        if (mono_compile_aot || action == DO_EXEC || action == DO_DEBUGGER) {                mono_config_parse (config_file);   //调用,由于没有指定--config选项,这里为NULL        }        //...        //...}mono_config_parse函数:
//解析config文件void mono_config_parse (const char *filename) {        const char *home;        char *mono_cfg;#ifndef TARGET_WIN32        char *user_cfg;#endif        if (filename) {                                                 //由于filename为NULL,不为真                mono_config_parse_file (filename);                return;        }        //获取环境变量MONO_CONFIG        char *env_home = g_getenv ("MONO_CONFIG");                       if (env_home) {                mono_config_parse_file (env_home);                return;        }        //在mono_main函数,mono_set_rootdir已经设置过mono的etc和lib目录        //mono_get_config_dir函数主要是获取mono_cfg_dir全局变量的值,mono_cfg_dir存放的是etc目录路径        //拼接具体config所在的路径        mono_cfg = g_build_filename (mono_get_config_dir (), "mono", "config", NULL);        mono_config_parse_file (mono_cfg);                        //解析config文件        g_free (mono_cfg);#if !defined(TARGET_WIN32)        home = g_get_home_dir ();        user_cfg = g_strconcat (home, G_DIR_SEPARATOR_S, ".mono/config", NULL);        mono_config_parse_file (user_cfg);        g_free (user_cfg);#endif}先不上代码,先看一下图

https://p6.toutiaoimg.com/large/pgc-image/e33e00f487364005b064dba302732d24mono加载config文件到内存,然后创建parse context,开始按字符解析xml
mono_config_parse_file源码

static void mono_config_parse_file (const char *filename){        ParseState state = {NULL};                                              //初始化ParseState        state.user_data = (gpointer) filename;                        //user_data存放config所在路径        mono_config_parse_file_with_context (&state, filename); //读取config文件到内存上,并开始按字符解析}mono_config_parse_file_with_context源码

/* If assembly is NULL, parse in the global context */static int mono_config_parse_file_with_context (ParseState *state, const char *filename){        gchar *text;        gsize len;        gint offset;        mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_CONFIG,                        "Config attempting to parse: &#39;%s&#39;.", filename);        //在函数内容对text进行分配内存空间,获取config文件大小和内容        if (!g_file_get_contents (filename, &text, &len, NULL))                 return 0;        offset = 0;        if (len > 3 && text == &#39;\xef&#39; && text == (gchar) &#39;\xbb&#39; && text == &#39;\xbf&#39;)                offset = 3; /* Skip UTF-8 BOM */        if (state->user_data == NULL)                                //在一次判读user_data是否为空,为空,就保存filename的地址                state->user_data = (gpointer) filename;   //***重点***        mono_config_parse_xml_with_context (state, text + offset, len - offset);        g_free (text);                                                //开释text指向的内存空间                return 1;}g_file_get_contents代码较多,我简单用readfile函数进行替代,更容易明白

#define _CRT_SECURE_NO_WARNINGS#include #include #include /* * filename要读取文件的名称 * content   char类型二级指针,存放要读取文件的内容,返回 * len         int类型指针,存放文件的长度,返回 */int readfile(char* filename, char** content, int* len){        assert(filename != NULL);        FILE* pfile = fopen(filename, "r");        assert(pfile != NULL);                fseek(pfile, 0, SEEK_END);                //1. 将文件指针移动到文件尾部        int total = ftell(pfile);                        //2. 获取文件的大小        fseek(pfile, 0, SEEK_SET);               //3. 在将文件指针移动到文件头部,方便将文件读取到字符串                char* str = calloc(1, total + 1);    //4. 根据文件大小进行分配空间        int result = fread(str, total + 1, sizeof(char), pfile);//5. 读取文件内容到str中        fclose(pfile);        str = &#39;\0&#39;;        *content = str;        *len = total;        return 0;}int main(int argc, char* argv[]){                char* filename = "config";                                char* content;                                                //存放读取文件的内容        int len;                                                           //存放文件的长度        readfile(filename, &content, &len);        printf("%s\n", content);        printf("--------------------------\n");        printf("%d\n", len);        return 0;}其实g_file_get_contents函数代码不是很多,只有50行左右,只是展示不是很方便,更多的留给g_markup_parse_context_parse函数.
g_markup_parse_context_parse函数

因为这个一看结构较多,还有回调函数也很多.简单用图说一下
https://p9.toutiaoimg.com/large/pgc-image/a3a7eb08b8a449c8b1326f4a52306025mono解析xml,转换器
gbooleang_markup_parse_context_parse (GMarkupParseContext *context,                              const gchar *text, gssize text_len,                              GError **gerror){        const char *p,*end;        end = text + text_len;   //根据文件大小,得出文件末端的位置,方便下边循环有停止条件//逐个字符遍历,进行提取        for (p = text; p < end; p++){                char c = *p;                switch (context->state){                case START:                              if (c == &#39; &#39; || c == &#39;\t&#39; || c == &#39;\f&#39; || c == &#39;\n&#39; || (c & 0x80))                                continue;                        if (c == &#39;state = SKIP_XML_DECLARATION;                                        p++;                                } else                                        context->state = START_ELEMENT;                                                                                                continue;                        }                        set_error ("%s", "Expected < to start the document");                        goto fail;                case SKIP_XML_DECLARATION:                case START_ELEMENT: {                                                                                                                                const char *element_start = p, *element_end;                        char *ename = NULL;                        int full_stop = 0, l;                        gchar **names = NULL, **values = NULL;                        for (; p < end && my_isspace (*p); p++)                                ;                        if (p == end){                                set_error ("%s", "Unfinished element");                                goto fail;                        }                        if (*p == &#39;!&#39; && (p+2 < end) && (p == &#39;-&#39;) && (p == &#39;-&#39;)){                                context->state = COMMENT;                                p += 2;                                break;                        }                                                if (!my_isnamestartchar (*p)){                                set_error ("%s", "Expected an element name");                                goto fail;                        }                                                for (++p; p < end && my_isnamechar (*p); p++)                                ;                        if (p == end){                                set_error ("%s", "Expected an element");                                goto fail;                        }                        element_end = p;                                                for (; p < end && my_isspace (*p); p++)                                ;                        if (p == end){                                set_error ("%s", "Unfinished element");                                goto fail;                        }                        p = parse_attributes (p, end, &names, &values, gerror, &full_stop, context->state);                        if (p == end){                                if (names != NULL) {                                        g_strfreev (names);                                        g_strfreev (values);                                }                                /* Only set the error if parse_attributes did not */                                if (gerror != NULL && *gerror == NULL)                                        set_error ("%s", "Unfinished sequence");                                goto fail;                        }                        l = (int)(element_end - element_start);                        ename = g_malloc (l + 1);                        if (ename == NULL)                                goto fail;                        strncpy (ename, element_start, l);                        ename = 0;                        if (context->state == START_ELEMENT)                                if (context->parser.start_element != NULL)                                        context->parser.start_element (context, ename,                                                                     (const gchar **) names,                                                                     (const gchar **) values,                                                                     context->user_data, gerror);                        if (names != NULL){                                g_strfreev (names);                                g_strfreev (values);                        }                        if (gerror != NULL && *gerror != NULL){                                g_free (ename);                                goto fail;                        }                                                if (full_stop){                                if (context->parser.end_element != NULL &&context->state == START_ELEMENT){                                        context->parser.end_element (context, ename, context->user_data, gerror);                                        if (gerror != NULL && *gerror != NULL){                                                g_free (ename);                                                goto fail;                                        }                                }                                g_free (ename);                        } else {                                context->level = g_slist_prepend (context->level, ename);                        }                                                context->state = TEXT;                        break;                } /* case START_ELEMENT */                case TEXT: {                        if (c == &#39;state = FLUSH_TEXT;                                break;                        }                        if (context->parser.text != NULL){                                if (context->text == NULL)                                        context->text = g_string_new ("");                                g_string_append_c (context->text, c);                        }                        break;                }                case COMMENT:                        if (*p != &#39;-&#39;)                                break;                        if (p+2 < end && (p == &#39;-&#39;) && (p == &#39;>&#39;)){                                context->state = TEXT;                                p += 2;                                break;                        }                        break;                                        case FLUSH_TEXT:                        if (context->parser.text != NULL && context->text != NULL){                                context->parser.text (context, context->text->str, context->text->len,                                                      context->user_data, gerror);                                if (gerror != NULL && *gerror != NULL)                                        goto fail;                        }                                                if (c == &#39;/&#39;)                                context->state = CLOSING_ELEMENT;                        else {                                p--;                                context->state = START_ELEMENT;                        }                        break;                case CLOSING_ELEMENT: {                        GSList *current = context->level;                        char *text;                        if (context->level == NULL){                                set_error ("%s", "Too many closing tags, not enough open tags");                                goto fail;                        }                                                text = current->data;                        if (context->parser.end_element != NULL){                                context->parser.end_element (context, text, context->user_data, gerror);                                if (gerror != NULL && *gerror != NULL){                                        g_free (text);                                        goto fail;                                }                        }                        g_free (text);                        while (p < end && *p != &#39;>&#39;)                                p++;                                                context->level = context->level->next;                        g_slist_free_1 (current);                        context->state = TEXT;                        break;                } /* case CLOSING_ELEMENT */                                        } /* switch */        }        return TRUE; fail:        if (context->parser.error && gerror != NULL && *gerror)                context->parser.error (context, *gerror, context->user_data);                destroy_parse_state (context);        return FALSE;}本文大体理了一下大体思路,涉及到结构和函数指针,以及多级指针,是没有讲的,准备单独来讲.
页: [1]
查看完整版本: Mono源码学习-怎样解析config文件