nginx 配置文件解析函数------------ngx_conf_read_token

1. 首先在函数ngx_conf_parse 根据文件名申请buff


2.根据文件大小读取文件内容

    

3. 逐个读取字符

      


解析文件中的字符步骤:
一)last_space 为1的情况,表示刚开始解析或者是前面已经解析到一个关键字(一般以空格,分号, 左大括号等为边界).
1. pos指针一个字符一个字符往右移动 ch = *pos++
2. 遇到空格,制表符(\t),回车,换行,直接跳过
3.更新局部变量start为pos指针-1
4.遇到分号,左大括号和右大括号直接return返回
5.遇到# 设置comment标记,continue
6.遇到\,’,”,设置对应标记,并将last_space 设置为0
7.其它情况设置last_space 为0


二)Last_sapce  不为0,解析关键字过程中
1. 遇到${,continue
2.遇到\, $等设置标记位,continue
3.遇到第二个双引号或者单引号,清空引号标记位,设置找到关键字标记found=1,并且设置后面跟着的字符必须有空格Need_space=1
4.遇到空格,\t,回车,换行,{,分号,设置找到关键字标记found=1,并设置last_space为1,重新开始寻找新的关键字.
5.找到关键字found=1,从存放关键字的数组cf->args找到一个空位置,将关键字存放到该字符串数组的空位中。如果是因为第4步骤的分号或者{设置的,这return OK或BLOKC_START。更新found为0



static ngx_int_t
ngx_conf_read_token(ngx_conf_t *cf)
{
    u_char      *start, ch, *src, *dst;
    off_t        file_size;
    size_t       len;
    ssize_t      n, size;
    ngx_uint_t   found, need_space, last_space, sharp_comment, variable;
    ngx_uint_t   quoted, s_quoted, d_quoted, start_line;
    ngx_str_t   *word;
    ngx_buf_t   *b;

    found = 0;
    need_space = 0;
    last_space = 1;
    sharp_comment = 0;
    variable = 0;
    quoted = 0;
    s_quoted = 0;
    d_quoted = 0;

    cf->args->nelts = 0;/*设置保存参数的数组个数为0,即清空保存配置的数组*/
    /*获取配置文件的buffer*/
    b = cf->conf_file->buffer;
    /*上一次读取结束的文件buf位置,第一次读取时为文件的起始位置*/ 
    start = b->pos;
    /*上一次读取的结束行数*/
    start_line = cf->conf_file->line;
    /*配置文件大小(字节)*/
    file_size = ngx_file_size(&cf->conf_file->file.info);
    ngx_conf_log_error(NGX_LOG_DEBUG,cf,0,"file_size=%lu,start_line=%uz",file_size,start_line);
    for ( ;; ) {
        /*第一次,b->pos = b->last,后续b->last指向读取buffer的结尾,
         * 从第2次循环开始,如果为真,表示前一次读取的buffer已经全部解析完毕,需要进入判断一下是否文件已经读完,或者没有读完的话,继续读取
         * 第2次以后if为真的情况是,文件的大小大于buffer的长度(4096),此时需要多次读取*/
        if (b->pos >= b->last) {
            /*如果偏移量大于文件大小,说明文件已读取完毕,返回NGX_CONF_FILE_DONE*/
            if (cf->conf_file->file.offset >= file_size) {

                if (cf->args->nelts > 0 || !last_space) {

                    if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
                        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                           "unexpected end of parameter, "
                                           "expecting \";\"");
                        return NGX_ERROR;
                    }

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                  "unexpected end of file, "
                                  "expecting \";\" or \"}\"");
                    return NGX_ERROR;
                }
                ngx_conf_log_error(NGX_LOG_DEBUG,cf,0,"start =%d,pos=%d",start,b->pos);
                return NGX_CONF_FILE_DONE;
            }
            /*这个两个值在重新执行for循环的情况下应该相等,因为前面刚刚把b->pos赋值给了start*/
            len = b->pos - start; /*len表示还有多少已扫描但是没有被解析*/
            /*如果这个长度超过了最大文件buffer,NGX_CONF_BUFFER(4096),出错返回*/
            if (len == NGX_CONF_BUFFER) {
                cf->conf_file->line = start_line;

                if (d_quoted) {
                    ch = '"';

                } else if (s_quoted) {
                    ch = '\'';

                } else {
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                       "too long parameter \"%*s...\" started",
                                       10, start);
                    return NGX_ERROR;
                }

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "too long parameter, probably "
                                   "missing terminating \"%c\" character", ch);
                return NGX_ERROR;
            }
            /**/
            if (len) {
                ngx_memmove(b->start, start, len);
            }

            size = (ssize_t) (file_size - cf->conf_file->file.offset);
            /*即文件大小大于分配的buffer长度,size取为buffer长度*/
            if (size > b->end - (b->start + len)) {
                size = b->end - (b->start + len);
            }
            /*读取size大小的文件数据到buf中,起始地址为b->start + len,修改文件偏移量*/
            n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
                              cf->conf_file->file.offset);

            if (n == NGX_ERROR) {
                return NGX_ERROR;
            }

            if (n != size) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   ngx_read_file_n " returned "
                                   "only %z bytes instead of %z",
                                   n, size);
                return NGX_ERROR;
            }
            /*修改当前buf的位置b->pos*/
            b->pos = b->start + len;
            /*指向数据的结尾*/
            b->last = b->pos + n;
            start = b->start;
        }
        /*一个字节一个字节地循环处理文件buff内容*/
        ch = *b->pos++;
        /*如果是换行符*/
        if (ch == LF) {
            cf->conf_file->line++;//处理的行数+1

            if (sharp_comment) {//修改注释行标记位
                sharp_comment = 0;
            }
        }
        /*如果前面是#开头的注释,直接跳过#后面的所有字符,直到上面的遇到换行符后,
         * 将该注释标记去除,表示注释结束,新的行开始0*/
        if (sharp_comment) {
            continue;
        }
        /*前面有反斜杠转义字符\,说明这个字符是一个转义字符,忽略\后面的字符*/
        if (quoted) {
            quoted = 0;
            continue;
        }
        /*need_space默认为0,单引号或者双引号结束后,需要有以下分隔符:
         * 空格,制表符,回车,换行,分号,左大括号,右小括号
         * 遇到其它都是错误的,返回error*/
        if (need_space) {
            /*如果遇到空格,制表符,回车,换行,则将need_space清空,last_space置1*/
            if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
                last_space = 1;
                need_space = 0;
                continue;
            }
            /*如果遇到分号;则该行读取结束*/
            if (ch == ';') {
                return NGX_OK;
            }
            /*遇到'{'表示新的block的开始*/
            if (ch == '{') {
                return NGX_CONF_BLOCK_START;
            }
            
            if (ch == ')') {
                last_space = 1;
                need_space = 0;

            } else {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "unexpected \"%c\"", ch);
                 return NGX_ERROR;
            }
        }
        /*last_space初始化为1,last_space表示上一个字符为字符串分隔符,需要重新计算start,以表示新的token的起始地址*/
        if (last_space) {
            /*遇到空格,制表符,回车,换行等字符,则无需处理*/
            if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
                continue;
            }

            start = b->pos - 1;/*更新start值,作为新的token(关键字)的起始地址*/
            start_line = cf->conf_file->line;

            switch (ch) {
            /*遇到;或者{,直接返回*/
            case ';':
            case '{':
                if (cf->args->nelts == 0) {
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                       "unexpected \"%c\"", ch);
                    return NGX_ERROR;
                }

                if (ch == '{') {
                    return NGX_CONF_BLOCK_START;
                }

                return NGX_OK;
             
            case '}': //遇到}也返回
                if (cf->args->nelts != 0) {
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                       "unexpected \"}\"");
                    return NGX_ERROR;
                }

                return NGX_CONF_BLOCK_DONE;

            case '#':/*遇到#,设置sharp_comment标记*/
                sharp_comment = 1;
                continue;

            case '\\':/*遇到\,设置quoted=1,last_space=0*/
                quoted = 1;
                last_space = 0;
                continue;

            case '"'://遇到第一个“,设置d_quoted为1
                start++;
                d_quoted = 1;
                last_space = 0;
                continue;

            case '\''://遇到第一个单引号',设置s_quoted为1
                start++;
                s_quoted = 1;
                last_space = 0;
                continue;

            default:
                last_space = 0;
            }

        } else {
            if (ch == '{' && variable) {
                continue;
            }

            variable = 0;

            if (ch == '\\') {
                quoted = 1;
                continue;
            }
            /*遇到$符,设置variable为1*/
            if (ch == '$') {
                variable = 1;
                continue;
            }

            if (d_quoted) {
                if (ch == '"') {//遇到第二个双引号”,设置d_quoted=0,need_space=1,found=1
                    d_quoted = 0;
                    need_space = 1;
                    found = 1;
                }

            } else if (s_quoted) {//遇到第二个单引号’,设置s_quoted=0,need_space=1,found=1
                if (ch == '\'') {
                    s_quoted = 0;
                    need_space = 1;
                    found = 1;
                }

            } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF
                       || ch == ';' || ch == '{')//遇到空格,制表符,回车,换行符,分号,{,设置last_space=1,found=1
            {
                last_space = 1;
                found = 1;
            }
            //正常情况下,遇到空格或分号结束时,设置了last_space=1,found=1
            if (found) {
                /*args是初始化为10个ngx_str_t大小的动态数组,在ngx_init_cycle中初始化
                 * 存放配置文件中的一行中的关键字,如:
                 * worker_processes  1;
                 * args的第一个数组元素就是 worker_process
                 * 第二个数组元素就是1*/
                word = ngx_array_push(cf->args);//找到数组可用的位置
                if (word == NULL) {
                    return NGX_ERROR;
                }
                /*文件数据的起始位置为start,当前的位置为b->pos*/
                word->data = ngx_pnalloc(cf->pool, b->pos - start + 1);
                if (word->data == NULL) {
                    return NGX_ERROR;
                }
                /*将start开始的数据拷贝到word->data中,并记录拷贝的大小len*/
                for (dst = word->data, src = start, len = 0;
                     src < b->pos - 1;
                     len++)
                {
                    if (*src == '\\') {
                        switch (src[1]) {
                        case '"':
                        case '\'':
                        case '\\':
                            src++;
                            break;

                        case 't':
                            *dst++ = '\t';
                            src += 2;
                            continue;

                        case 'r':
                            *dst++ = '\r';
                            src += 2;
                            continue;

                        case 'n':
                            *dst++ = '\n';
                            src += 2;
                            continue;
                        }

                    }
                    *dst++ = *src++;
                }
                *dst = '\0';
                word->len = len;
                /*正常情况下,遇到分号,返回一行*/
                if (ch == ';') {
                    return NGX_OK;
                }
                /*如果是一个block,如event,则返回NGX_CONF_BLOCK_START*/
                if (ch == '{') {
                    return NGX_CONF_BLOCK_START;
                }
                /*等于其它的符号,如空格,则继续循环*/
                found = 0;
            }
        }
    }
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值