nginx的配置文件解析:ngx_conf_read_token函數

該函數獲取配置文件nginx.conf中的配置行或者配置塊起始處的token.將這些token放在cf->args動態數組中, 並返回這些token所在配置文件中的位置. 
例如一個配置行的話. 就返回NGX_OK. 配置行以分號結尾. 如果是一個配置塊的起始處, 即以'{'結尾, 則返回NGX_CONF_BLOCK_START.
對於token的定義爲: 連續非空白字符. 單雙引號包圍的字符. 單雙引號內允許轉義序列如\t, \r, \n.
'#'之後爲註釋. 其後行內的所有字符全部忽略. 

理解該函數的要點只需理解幾個標識的具體含義即可.
found: 爲1表明發現一個token. 然後將該token壓入cf->args數組中.
need_space: token被單雙引號包圍時, 最後一個分號後面必須是空白字符、分號、坐花括號或者右括號(左括號匹配在代碼中沒看見?). 否則出錯.
last_space: 爲1表明前面掃描的字符均爲空白字符. 即還未發現token起始位置. 爲0表明已找到token的起始位置.
sharp_comment: 掃描到'#'字符後置1, 其後所有字符都會忽略直至一行結束.
quoted: 轉義序列, '\'之後的字符被跳過.然後quoted置0.
s_quoted, d_quoted: token以單或雙引號起始, 必須以單或雙引號結束.

例如:
worker_processes  1;
解析後cf->args數組裏有兩個ngx_str_t結構. 兩個字符串分別是"worker_processes"和"1", 返回值爲NGX_OK。
event {
     worker_connections     1024;
}
解析時cf_args數組裏有一個ngx_str_t結構.字符串爲"event". 返回值爲NGX_CONF_BLOCK_START.

點擊(此處)摺疊或打開

  1. static ngx_int_t
  2. ngx_conf_read_token(ngx_conf_t *cf)
  3. {
  4.     u_char *start, ch, *src, *dst;
  5.     off_t file_size;
  6.     size_t len;
  7.     ssize_t n, size;
  8.     ngx_uint_t found, need_space, last_space, sharp_comment, variable;
  9.     ngx_uint_t quoted, s_quoted, d_quoted, start_line;
  10.     ngx_str_t *word;
  11.     ngx_buf_t *b;

  12.     found = 0;
  13.     need_space = 0;
  14.     last_space = 1;
  15.     sharp_comment = 0;
  16.     variable = 0;
  17.     quoted = 0;
  18.     s_quoted = 0;
  19.     d_quoted = 0;

  20.     cf->args->nelts = 0;
  21.     b = cf->conf_file->buffer;
  22.     start = b->pos;
  23.     start_line = cf->conf_file->line;

  24.     file_size = ngx_file_size(&cf->conf_file->file.info);

  25.     for ( ;; ) {
  26. //已處理完緩衝區中的字符.
  27.         if (b->pos >= b->last) {

  28.             if (cf->conf_file->file.offset >= file_size) {
  29. //文件已處理完. 但是已解析出的token數大於0或者已發現一個token的
  30. //起始位置. 配置行不完整. 配置語法出錯。
  31.                 if (cf->args->nelts > 0 || !last_space) {

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

  38.                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  39.                                   "unexpected end of file, "
  40.                                   "expecting \";\" or \"}\"");
  41.                     return NGX_ERROR;
  42.                 }
  43. //處理完配置文件.
  44.                 return NGX_CONF_FILE_DONE;
  45.             }
  46. //未處理的緩衝區字符長度.
  47.             len = b->pos - start;
  48. //若長度等於緩衝區長度. 則配置出錯.
  49. //token長度不能大於NGX_CONF_BUFFER.
  50.             if (len == NGX_CONF_BUFFER) {
  51.                 cf->conf_file->line = start_line;

  52.                 if (d_quoted) {
  53.                     ch = '"';

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

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

  62.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  63.                                    "too long parameter, probably "
  64.                                    "missing terminating \"%c\" character", ch);
  65.                 return NGX_ERROR;
  66.             }
  67. //將未處理完的字符拷貝到緩衝區開始出. 這些字符是一個token的一部分
  68.             if (len) {
  69.                 ngx_memmove(b->start, start, len);
  70.             }
  71. // 配置文件未處理的長度.
  72.             size = (ssize_t) (file_size - cf->conf_file->file.offset);
  73. //若超過可用緩衝區長度則一次讀取可用緩衝區長度的字符.
  74.             if (size > b->end - (b->start + len)) {
  75.                 size = b->end - (b->start + len);
  76.             }
  77. //從上次讀取結束的地方開始讀取size個字符繼續處理.
  78.             n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
  79.                               cf->conf_file->file.offset);

  80.             if (n == NGX_ERROR) {
  81.                 return NGX_ERROR;
  82.             }

  83.             if (n != size) {
  84.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  85.                                    ngx_read_file_n " returned "
  86.                                    "only %z bytes instead of %z",
  87.                                    n, size);
  88.                 return NGX_ERROR;
  89.             }
  90. //[b->pos, b->last) 之間是需要處理的字符.
  91.             b->pos = b->start + len;
  92.             b->last = b->pos + n;
  93.             start = b->start;
  94.         }

  95.         ch = *b->pos++;
  96. //當前字符是換行符. 行計數器增1, 若之前掃描到該行首是註釋符, 則註釋標識清
  97. //0。
  98.         if (ch == LF) {
  99.             cf->conf_file->line++;

  100.             if (sharp_comment) {
  101.                 sharp_comment = 0;
  102.             }
  103.         }
  104. //當前行是註釋行, 吃掉該行後續所有字符.
  105.         if (sharp_comment) {
  106.             continue;
  107.         }
  108. //跳過\後面的字符. 即轉義序列.
  109.         if (quoted) {
  110.             quoted = 0;
  111.             continue;
  112.         }
  113. //當token被單或雙引號包圍是need_space會被置1.
  114.         if (need_space) {

  115.             if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
  116.                 last_space = 1;
  117.                 need_space = 0;
  118.                 continue;
  119.             }


  120. //配置行以分號結尾.
  121.             if (ch == ';') {
  122.                 return NGX_OK;
  123.             }
  124. //配置塊以{開始
  125.             if (ch == '{') {
  126.                 return NGX_CONF_BLOCK_START;
  127.             }

  128.             if (ch == ')') {
  129.                 last_space = 1;
  130.                 need_space = 0;

  131.             } else {
  132.                  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  133.                                     "unexpected \"%c\"", ch);
  134.                  return NGX_ERROR;
  135.             }
  136.         }
  137. //last_space爲1表明未發現token起始位置, 吃掉所有空白字符.
  138.         if (last_space) {
  139.             if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
  140.                 continue;
  141.             }
  142. //非空白字符是一個token的起始位置.
  143.             start = b->pos - 1;
  144.             start_line = cf->conf_file->line;

  145.             switch (ch) {

  146.             case ';':
  147.             case '{':
  148. //在分號和左括號之前必定會出現至少一個token. 否則是配置語法錯誤
  149.                 if (cf->args->nelts == 0) {
  150.                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  151.                                        "unexpected \"%c\"", ch);
  152.                     return NGX_ERROR;
  153.                 }
  154. //配置塊起始位置.
  155.                 if (ch == '{') {
  156.                     return NGX_CONF_BLOCK_START;
  157.                 }
  158. //配置行結束.
  159.                 return NGX_OK;
  160. //配置塊結束標識右括號獨佔一行.
  161.             case '}':
  162.                 if (cf->args->nelts != 0) {
  163.                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  164.                                        "unexpected \"}\"");
  165.                     return NGX_ERROR;
  166.                 }

  167.                 return NGX_CONF_BLOCK_DONE;
  168. //註釋符.
  169.             case '#':
  170.                 sharp_comment = 1;
  171.                 continue;
  172. //轉義序列.其後面的一個字符將被跳過.
  173.             case '\\':
  174.                 quoted = 1;
  175.                 last_space = 0;
  176.                 continue;
  177. //引號開頭的token.
  178.             case '"':
  179.                 start++;
  180.                 d_quoted = 1;
  181.                 last_space = 0;
  182.                 continue;

  183.             case '\'':
  184.                 start++;
  185.                 s_quoted = 1;
  186.                 last_space = 0;
  187.                 continue;

  188.             default:
  189.                 last_space = 0;
  190.             }
  191. //到這裏開始處理token字符.
  192.         } else {
  193.             if (ch == '{' && variable) {
  194.                 continue;
  195.             }

  196.             variable = 0;
  197. //轉義序列.其後面的一個字符將被跳過
  198.             if (ch == '\\') {
  199.                 quoted = 1;
  200.                 continue;
  201.             }
  202. //??????這裏是什麼???
  203.             if (ch == '$') {
  204.                 variable = 1;
  205.                 continue;
  206.             }
  207. //單雙引號包圍的字符是一個token.
  208.             if (d_quoted) {
  209.                 if (ch == '"') {
  210.                     d_quoted = 0;
  211.                     need_space = 1;
  212.                     found = 1;
  213.                 }

  214.             } else if (s_quoted) {
  215.                 if (ch == '\'') {
  216.                     s_quoted = 0;
  217.                     need_space = 1;
  218.                     found = 1;
  219.                 }
  220. //字符是空格、製表符、換行符、分號或是左花括號表示一個token的結束.
  221. //也即發現一個token
  222.             } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF
  223.                        || ch == ';' || ch == '{')
  224.             {
  225.                 last_space = 1;
  226.                 found = 1;
  227.             }

  228.             if (found) {
  229. //將該token放入cf->args動態數組中.
  230.                 word = ngx_array_push(cf->args);
  231.                 if (word == NULL) {
  232.                     return NGX_ERROR;
  233.                 }

  234.                 word->data = ngx_pnalloc(cf->pool, b->pos - start + 1);
  235.                 if (word->data == NULL) {
  236.                     return NGX_ERROR;
  237.                 }
  238. //將token中的轉義序列轉義後複製給word->data, 其他的按原樣複製.
  239.                 for (dst = word->data, src = start, len = 0;
  240.                      src < b->pos - 1;
  241.                      len++)
  242.                 {
  243.                     if (*src == '\\') {
  244.                         switch (src[1]) {
  245.                         case '"':
  246.                         case '\'':
  247.                         case '\\':
  248.                             src++;
  249.                             break;

  250.                         case 't':
  251.                             *dst++ = '\t';
  252.                             src += 2;
  253.                             continue;

  254.                         case 'r':
  255.                             *dst++ = '\r';
  256.                             src += 2;
  257.                             continue;

  258.                         case 'n':
  259.                             *dst++ = '\n';
  260.                             src += 2;
  261.                             continue;
  262.                         }

  263.                     }
  264.                     *dst++ = *src++;
  265.                 }
  266.                 *dst = '\0';
  267.                 word->len = len;
  268. //若token以分號結束則處理完一個配置行.
  269.                 if (ch == ';') {
  270.                     return NGX_OK;
  271.                 }
  272. //若token以左花括號結束則發現配置塊起始位置.
  273.                 if (ch == '{') {
  274.                     return NGX_CONF_BLOCK_START;
  275.                 }
  276. //發現token標識清0
  277.                 found = 0;
  278.             }
  279.         }
  280.     }
  281. }
發佈了50 篇原創文章 · 獲贊 48 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章