grepを読む

2-6.C言語

prtext

printf埋め込み前のgrep.c(3.1)を使ってprtextを読んでいきます。
変数や関数の名前の意味を推測していきます。

1292 /* Output the lines between BEG and LIM.  Deal with context.  */
1293 static void
1294 prtext (char *beg, char *lim)

引数のbeg、limとは何でしょう。
prtextを使用しているソースを確認してみましょう。

1409           char *prbeg = out_invert ? p : b;
1410           char *prend = out_invert ? b : endp;
1411           prtext (prbeg, prend);

使用されている変数名はprbegとprendです。
begin~endってことですね。
つまりbegはbeginの略です。

となると、limは、limitやlimitterといったところでしょう。

続いてこちら。

1296   static bool used;     /* Avoid printing SEP_STR_GROUP before any output.

usedとは実に簡単な名前ですが、何がusedなんでしょう。この関数(prtext)がusedなのか。コメントでは、出力の前のSEP_STR_GROUPの表示を中止するとあります。
では、SEP_STR_GROUPとは何なのか。

56行目に定義されています。

  54 enum { SEP_CHAR_SELECTED = ':' };
  55 enum { SEP_CHAR_REJECTED = '-' };
  56 static char const SEP_STR_GROUP[] = "--";

セパレータとして文字列を定義しているんですね。

続いてこの行。

1297   char eol = eolbyte;

eolなので、End of Lineですね。eolbyteということは行末の1Byteの文字でしょうか。
grep.c内を、eolbyteで検索してみます。

takk@deb9:~/tmp/grep-3.1/src$ grep -n 'eolbyte\>.*=' grep.c
2429:  eolbyte = '\n';
2676:        eolbyte = '\0';
takk@deb9:~/tmp/grep-3.1/src$

行末の1Byte、改行またはNULL文字ってことですね。

prepending

1299   if (!out_quiet && pending > 0)
1300     prpending (beg);

out_quietは、outは出力、quietは静かな、なので、「静かな出力」。。。ん~。色を付けないってことでしょうか。

pendingは、日本語でも使いますね。保留って意味でしょう。

prependingは、prependの名詞ですね。prependは先頭に追加するという意味です。

各変数定義の説明を読んでみます。

1027 static intmax_t pending;        /* Pending lines of output.
1028                                    Always kept 0 if out_quiet is true.  */

Pending linesとあるので、pendingは、出力保留行の行数のようですね。
次行きます。

1301
1302   char *p = beg;
1303
1304   if (!out_quiet)
1305     {
1306       /* Deal with leading context.  */
1307       char const *bp = lastout ? lastout : bufbeg;
1308       intmax_t i;
1309       for (i = 0; i < out_before; ++i)
1310         if (p > bp)
1311           do
1312             --p;
1313           while (p[-1] != eol);
1314

コメントのDeal withは~対応する、で、contextは文脈、
先頭の文脈に対応する、でしょうか。

lastoutの定義を読んでみます。

1023 static char *lastout;           /* Pointer after last character output;
1024                                    NULL if no character has been output
1025                                    or if it's conceptually before bufbeg. *

lastoutはそのままっぽいです。最後に出力した文字の後のポインタですね。

1309行目でout_beforeという変数も使われているので、定義箇所を見てみます。beforeもあればafterもあるようです。

1010 static intmax_t out_before;     /* Lines of leading context. */
1011 static intmax_t out_after;      /* Lines of trailing context. */

こうして読んでいくと、意外にグローバル変数が多く使われていますね。

で、いろんなポインタやら行数やらを使って何をしているかというと、
この3行で、begポインタをEOLの位置まで戻しているだけのようです。

1311           do
1312             --p;
1313           while (p[-1] != eol);

すっきりした書き方の参考になります。

前の2行を合わせてみると、一切{}を使っていないのが、美しい。

1309       for (i = 0; i < out_before; ++i)
1310         if (p > bp)
1311           do
1312             --p;
1313           while (p[-1] != eol);

まあ、私の好みってだけかもしれません。

group_separator

1315       /* Print the group separator unless the output is adjacent to
1316          the previous output in the file.  */
1317       if ((0 <= out_before || 0 <= out_after) && used
1318           && p != lastout && group_separator)
1319         {
1320           pr_sgr_start_if (sep_color);
1321           fputs_errno (group_separator);
1322           pr_sgr_end_if (sep_color);
1323           putchar_errno ('\n');
1324         }

1317,1318行目のif文から読み取ることが難しいので、コメント文が頼りです。
出力が前の出力と隣接しない限りはグループセパレータをPrintする、とありますね。

グループセパレータとはgroup_separatorで、このように定義されています。

 182 /* The group separator used when context is requested. */
 183 static const char *group_separator = SEP_STR_GROUP;

SEP_STR_GROUPは前にも見ましたが、このような定義でしたね。

  54 enum { SEP_CHAR_SELECTED = ':' };
  55 enum { SEP_CHAR_REJECTED = '-' };
  56 static char const SEP_STR_GROUP[] = "--";

grepの結果を仕切る文字列で、これ(緑色)のことです。

グループセパレータを引数にしている、この関数は何をしているのでしょう。

1321           fputs_errno (group_separator);

errnoのfputs?
よくわかりません。関数定義を見てみます。

 382 static void
 383 fputs_errno (char const *s)
 384 {
 385   if (fputs (s, stdout) < 0)
 386     stdout_errno = errno;
 387 }

ううっ、これはっ。引数をfputsした後、エラーが発生した場合に、errnoを記憶する処理のようですが、ん~。関数名が分かりにくいっ。
なるべくなら関数の内部を読まずに、ソースが読めることが理想なので、fputs_holding_errnoとかだったら中身見なかったかもしれません。まあholding errnoが正しい英語か分かりませんが。
まあそもそも関数化が必要だったのだろうかと思います。使用箇所は多くないので。

takk@deb9:~/tmp/grep-3.1/src$ grep fputs_errno grep.c
fputs_errno (char const *s)
  fputs_errno (input_filename ());
          fputs_errno (group_separator);
takk@deb9:~/tmp/grep-3.1/src$

out_invert

1335   intmax_t n;
1336   if (out_invert)
1337     {
1338       /* One or more lines are output.  */
1339       for (n = 0; p < lim && n < outleft; n++)
1340         {
1341           char *nl = memchr (p, eol, lim - p);
1342           nl++;
1343           if (!out_quiet)
1344             prline (p, nl, SEP_CHAR_SELECTED);
1345           p = nl;
1346         }
1347     }
1348   else

invertとついているので反転ですね。しかし、out_invert、出力を反転するとは、どういうことなのでしょう。

定義を見てみます。

1006 static bool out_invert;         /* Print nonmatching stuff. */

分かりました。マッチしていない文字列を出力するフラグですね。

さらに、オプション処理しているcase文を読めば理解できます。

2658       case 'v':
2659         out_invert = true;
2660         break;

-vオプションを使った時のフラグですね。

out_quiet

out_invertがfalseの場合の処理からです。

1348   else
1349     {
1350       /* Just one line is output.  */
1351       if (!out_quiet)
1352         prline (beg, lim, SEP_CHAR_SELECTED);
1353       n = 1;
1354       p = lim;
1355     }
1356

out_quietもおそらくオプションのフラグでしょう。定義を見てみます。

1005 static bool out_quiet;          /* Suppress all normal output. */

mainで、以下のように処理しているので、他のフラグと関係しているフラグのように見受けられます。

2824   out_quiet = count_matches | done_on_match;

count_matchesとdone_on_matchはそれぞれ以下の定義です。

1012 static bool count_matches;      /* Count matching lines.  */

1029 static bool done_on_match;      /* Stop scanning file on first match.  */

これらのフラグもやはり、他のフラグに依存しています。

2819   if ((exit_on_match | dev_null_output) || list_files != LISTFILES_NONE)
2820     {
2821       count_matches = false;
2822       done_on_match = true;
2823     }

依存しているフラグの元をたどると、
例えば、exit_on_matchは、mainで、qオプションのフラグとして設定していますので、ほとんどのフラグはオプションから設定されると考えて良いかと思います。

2641       case 'q':
2642         exit_on_match = true;
2643         exit_failure = 0;
2644         break;

dev_null_outputの場合もmainで設定しているようですが、少し複雑なことをしています。

2798   bool possibly_tty = false;
2799   struct stat tmp_stat;
2800   if (! exit_on_match && fstat (STDOUT_FILENO, &tmp_stat) == 0)
2801     {
2802       if (S_ISREG (tmp_stat.st_mode))
2803         out_stat = tmp_stat;
2804       else if (S_ISCHR (tmp_stat.st_mode))
2805         {
2806           struct stat null_stat;
2807           if (stat ("/dev/null", &null_stat) == 0
2808               && SAME_INODE (tmp_stat, null_stat))
2809             dev_null_output = true;
2810           else
2811             possibly_tty = true;
2812         }
2813     }

なんとなくですが、grep実行時に、出力をどこへ渡すのか判断していて、フラグ設定しているように思えます。
プログラム中のフラグは、オプションによるものと、出力先によるものがあるようです。

caseからオプションを読む

オプションとフラグを関係を読むのが面倒なので、grepを使って整理します。
オプションはcase文でフラグ設定されるので、case文を検索。

takk@deb9:~/tmp/grep-3.1/src$ grep -n case grep.c
460:  {"ignore-case", no_argument, NULL, 'i'},
490:bool match_icase;
758:    case LONGINT_OK:
759:    case LONGINT_OVERFLOW:
1616:    case FTS_D:
1625:    case FTS_DC:
1631:    case FTS_DNR:
1632:    case FTS_ERR:
1633:    case FTS_NS:
1637:    case FTS_DEFAULT:
1638:    case FTS_NSOK:
1661:    case FTS_F:
1662:    case FTS_SLNONE:
1665:    case FTS_SL:
1666:    case FTS_W:
1929:  -i, --ignore-case         ignore case distinctions\n\
2026:   Exit in case of conflicts or if M is not available.  */
2240:   case folded counterparts and toupper translates none of its bytes.  */
2243:fgrep_icase_charlen (char const *pat, size_t patlen, mbstate_t *mbs)
2251:      if (MB_LEN_MAX < wn || case_folded_counterparts (wc, folded))
2265:   single-byte characters or characters not subject to case folding,
2269:fgrep_icase_available (char const *pat, size_t patlen)
2275:      int n = fgrep_icase_charlen (pat + i, patlen - i, &mbs);
2301:        case (size_t) -2:
2308:        case (size_t) -1:
2312:        case 1:
2315:            case '$': case '*': case '.': case '[': case '\\': case '^':
2349:        case '$': case '*': case '.': case '[': case '^':
2352:        case '(': case '+': case '?': case '{': case '|':
2357:        case '\\':
2361:              case '\n':
2362:              case 'B': case 'S': case 'W': case'\'': case '<':
2363:              case 'b': case 's': case 'w': case '`': case '>':
2364:              case '1': case '2': case '3': case '4':
2365:              case '5': case '6': case '7': case '8': case '9':
2368:              case '(': case '+': case '?': case '{': case '|':
2381:        if (match_icase)
2383:            int ni = fgrep_icase_charlen (q, len, &mb_state);
2465:      case 'A':
2469:      case 'B':
2473:      case 'C':
2479:      case 'D':

省略

2732:      case EXCLUDE_DIRECTORY_OPTION:
2743:      case GROUP_SEPARATOR_OPTION:
2747:      case LINE_BUFFERED_OPTION:
2751:      case LABEL_OPTION:
2755:      case 0:
2867:             || (match_icase && !fgrep_icase_available (keys, keycc)))))
takk@deb9:~/tmp/grep-3.1/src$

多すぎですね。
‘.’のパターンで絞ります。

takk@deb9:~/tmp/grep-3.1/src$ grep -n "case.*'.'" grep.c
460:  {"ignore-case", no_argument, NULL, 'i'},
2315:            case '$': case '*': case '.': case '[': case '\\': case '^':
2349:        case '$': case '*': case '.': case '[': case '^':
2352:        case '(': case '+': case '?': case '{': case '|':
2362:              case 'B': case 'S': case 'W': case'\'': case '<':
2363:              case 'b': case 's': case 'w': case '`': case '>':
2364:              case '1': case '2': case '3': case '4':
2365:              case '5': case '6': case '7': case '8': case '9':
2368:              case '(': case '+': case '?': case '{': case '|':
2465:      case 'A':
2469:      case 'B':
2473:      case 'C':
2479:      case 'D':
2488:      case 'E':
2492:      case 'F':
2496:      case 'P':
2500:      case 'G':
2504:      case 'X': /* undocumented on purpose */
2508:      case 'H':
2513:      case 'I':
2517:      case 'T':
2521:      case 'U':
2526:      case 'u':
2531:      case 'V':

省略

2646:      case 'R':
2649:      case 'r':
2654:      case 's':
2658:      case 'v':
2662:      case 'w':
2667:      case 'x':
2671:      case 'Z':
2675:      case 'z':
takk@deb9:~/tmp/grep-3.1/src$

さらにcaseが複数出現する行は除外します。

takk@deb9:~/tmp/grep-3.1/src$ grep "case.*'.'" grep.c | grep -v 'case.*case'
  {"ignore-case", no_argument, NULL, 'i'},
      case 'A':
      case 'B':
      case 'C':
      case 'D':
      case 'E':
      case 'F':
      case 'P':
      case 'G':
      case 'X': /* undocumented on purpose */
      case 'H':
      case 'I':
      case 'T':
      case 'U':
      case 'u':
      case 'V':
      case 'a':
      case 'b':
      case 'c':
      case 'd':
      case 'e':
      case 'f':
      case 'h':
      case 'i':
      case 'y':                 /* For old-timers . . . */
      case 'L':
      case 'l':
      case 'm':
      case 'n':
      case 'o':
      case 'q':
      case 'R':
      case 'r':
      case 's':
      case 'v':
      case 'w':
      case 'x':
      case 'Z':
      case 'z':
takk@deb9:~/tmp/grep-3.1/src$

これらのcase文の下の行のフラグを抽出すればよいですね。

caseからオプションを読む その2

takk@deb9:~/tmp/grep-3.1/src$ grep "case.*'.'" grep.c | grep -v 'case.*case'
  {"ignore-case", no_argument, NULL, 'i'},
      case 'A':
      case 'B':
      case 'C':
      case 'D':
      case 'E':
      case 'F':
      case 'P':
      case 'G':
      case 'X': /* undocumented on purpose */
      case 'H':
      case 'I':
      case 'T':
      case 'U':
      case 'u':
      case 'V':
      case 'a':
      case 'b':
      case 'c':
      case 'd':
      case 'e':
      case 'f':
      case 'h':
      case 'i':
      case 'y':                 /* For old-timers . . . */
      case 'L':
      case 'l':
      case 'm':
      case 'n':
      case 'o':
      case 'q':
      case 'R':
      case 'r':
      case 's':
      case 'v':
      case 'w':
      case 'x':
      case 'Z':
      case 'z':
takk@deb9:~/tmp/grep-3.1/src$

ここから行番号を取得して、ファイルに落とします。

takk@deb9:~/tmp/grep-3.1/src$ grep -n "case.*'.'" grep.c | grep -v 'case.*case' | cut -d: -f1 > filter
takk@deb9:~/tmp/grep-3.1/src$

このような行番号の一覧ができました。

takk@deb9:~/tmp/grep-3.1/src$ head filter
460
2465
2469
2473
2479
2488
2492
2496
2500
2504
takk@deb9:~/tmp/grep-3.1/src$

正規表現で先頭で検索できるように修正。

takk@deb9:~/tmp/grep-3.1/src$ sed 's/^/\^/' -i filter
takk@deb9:~/tmp/grep-3.1/src$

このファイルをgrepに適用します。

takk@deb9:~/tmp/grep-3.1/src$ head filter
^460
^2465
^2469
^2473
^2479
^2488
^2492
^2496
^2500
^2504
takk@deb9:~/tmp/grep-3.1/src$

nlでgrep.cの行頭に行番号を付与してから、 grepでさきほど作成したfilterで抽出します。

takk@deb9:~/tmp/grep-3.1/src$ nl -ba -nln grep.c | grep -A1 -f filter
460       {"ignore-case", no_argument, NULL, 'i'},
461       {"initial-tab", no_argument, NULL, 'T'},
--
2465          case 'A':
2466            context_length_arg (optarg, &out_after);
--
2469          case 'B':
2470            context_length_arg (optarg, &out_before);
--
2473          case 'C':
2474            /* Set output match context, but let any explicit leading or
--
2479          case 'D':
2480            if (STREQ (optarg, "read"))
--
2488          case 'E':
2489            matcher = setmatcher ("egrep", matcher);
--
2492          case 'F':
2493            matcher = setmatcher ("fgrep", matcher);
--
2496          case 'P':
2497            matcher = setmatcher ("perl", matcher);
--
2500          case 'G':
2501            matcher = setmatcher ("grep", matcher);
--
2504          case 'X': /* undocumented on purpose */
2505            matcher = setmatcher (optarg, matcher);
--
2508          case 'H':
2509            with_filenames = true;
--
2513          case 'I':
2514            binary_files = WITHOUT_MATCH_BINARY_FILES;
--
2517          case 'T':
2518            align_tabs = true;
--
2521          case 'U':
2522            if (O_BINARY)
--
2526          case 'u':
2527            /* Obsolete option; it has no effect.  FIXME: Diagnose use of
--
2531          case 'V':
2532            show_version = true;
--
2535          case 'a':
2536            binary_files = TEXT_BINARY_FILES;
--
2539          case 'b':
2540            out_byte = true;
--
2543          case 'c':
2544            count_matches = true;
--
2547          case 'd':
2548            directories = XARGMATCH ("--directories", optarg,
--
2554          case 'e':
2555            cc = strlen (optarg);
--
2568          case 'f':
2569            if (STREQ (optarg, "-"))
--
2601          case 'h':
2602            with_filenames = false;
--
2606          case 'i':
2607          case 'y':                 /* For old-timers . . . */
2608            match_icase = true;
--
2611          case 'L':
2612            /* Like -l, except list files that don't contain matches.
--
2617          case 'l':
2618            list_files = LISTFILES_MATCHING;
--
2621          case 'm':
2622            switch (xstrtoimax (optarg, 0, 10, &max_count, ""))
--
2633          case 'n':
2634            out_line = true;
--
2637          case 'o':
2638            only_matching = true;
--
2641          case 'q':
2642            exit_on_match = true;
--
2646          case 'R':
2647            fts_options = basic_fts_options | FTS_LOGICAL;
--
2649          case 'r':
2650            directories = RECURSE_DIRECTORIES;
--
2654          case 's':
2655            suppress_errors = true;
--
2658          case 'v':
2659            out_invert = true;
--
2662          case 'w':
2663            wordinit ();
--
2667          case 'x':
2668            match_lines = true;
--
2671          case 'Z':
2672            filename_mask = 0;
--
2675          case 'z':
2676            eolbyte = '\0';
takk@deb9:~/tmp/grep-3.1/src$

460,461行目はcase文ではなかったので、どこかで条件を間違えたのでしょう。

caseからオプションを読む その3

実はもっと簡単なgrep指定がありました。こちらを使います。

takk@deb9:~/tmp/grep-3.1/src$ grep -A1 "^......case.*'.'" grep.c
      case 'A':
        context_length_arg (optarg, &out_after);
--
      case 'B':
        context_length_arg (optarg, &out_before);
--

省略

--
      case 'Z':
        filename_mask = 0;
--
      case 'z':
        eolbyte = '\0';
takk@deb9:~/tmp/grep-3.1/src$

並び替えると見やすくなります。

takk@deb9:~/tmp/grep-3.1/src$ grep -A1 "^......case.*'.'" grep.c | tr -d '\n' | perl -pe 's/\bcase/\ncase/g'

case 'A':        context_length_arg (optarg, &out_after);--
case 'B':        context_length_arg (optarg, &out_before);--
case 'C':        /* Set output match context, but let any explicit leading or-- 
case 'D':        if (STREQ (optarg, "read"))--
case 'E':        matcher = setmatcher ("egrep", matcher);--
case 'F':        matcher = setmatcher ("fgrep", matcher);--
case 'P':        matcher = setmatcher ("perl", matcher);--
case 'G':        matcher = setmatcher ("grep", matcher);--
case 'X': /* undocumented on purpose */        matcher = setmatcher (optarg, matcher);--
case 'H':        with_filenames = true;--
case 'I':        binary_files = WITHOUT_MATCH_BINARY_FILES;--
case 'T':        align_tabs = true;--
case 'U':        if (O_BINARY)--
case 'u':        /* Obsolete option; it has no effect.  FIXME: Diagnose use of--
case 'V':        show_version = true;--
case 'a':        binary_files = TEXT_BINARY_FILES;--
case 'b':        out_byte = true;--
case 'c':        count_matches = true;--
case 'd':        directories = XARGMATCH ("--directories", optarg,--
case 'e':        cc = strlen (optarg);--
case 'f':        if (STREQ (optarg, "-"))--
case 'h':        with_filenames = false;--
case 'i':
case 'y':                       /* For old-timers . . . */        match_icase = true;--
case 'L':        /* Like -l, except list files that don't contain matches.--    
case 'l':        list_files = LISTFILES_MATCHING;--
case 'm':        switch (xstrtoimax (optarg, 0, 10, &max_count, ""))--
case 'n':        out_line = true;--
case 'o':        only_matching = true;--
case 'q':        exit_on_match = true;--
case 'R':        fts_options = basic_fts_options | FTS_LOGICAL;--
case 'r':        directories = RECURSE_DIRECTORIES;--
case 's':        suppress_errors = true;--
case 'v':        out_invert = true;--
case 'w':        wordinit ();--
case 'x':        match_lines = true;--
case 'Z':        filename_mask = 0;--
case 'z':        eolbyte = '\0';takk@deb9:~/tmp/grep-3.1/src$

各オプションとフラグの関係がすぐに分かるようになりました。
一つピックアップして、manと見比べます。

takk@deb9:~/tmp/grep-3.1/src$ !! | shuf | head -1
grep -A1 "^......case.*'.'" grep.c | tr -d '\n' | perl -pe 's/case/\ncase/g' | shuf | head -1
case 'c':        count_matches = true;--
takk@deb9:~/tmp/grep-3.1/src$

man grepの -cの説明はこのようになっています。

       -c, --count
              通常の出力はせず、各入力ファイルについてマッチした行数を表示しま
              す。 -v, --invert-match オプション (上記参照) と共に指定した場合
              は、  マッチしなかった行数を表示します。  (-c オプションは POSIX
              で指定されています)

-cオプションのフラグは、count_matchesです。それはそれで分かりやすいのですが、ロングオプションの–countの名前を合わせても良かったのでは? と思ってしまいました。

pretext その2

オプションと各フラグの関係がおおむね分かったので、続きを読んでいきます。
prtextの最後の部分です。

1357   after_last_match = bufoffset - (buflim - p);
1358   pending = out_quiet ? 0 : MAX (0, out_after);
1359   used = true;
1360   outleft -= n;
1361 }

after_last_matchの定義を見てみます。

 807 static off_t after_last_match;  /* Pointer after last matching line that
 808                                    would have been output if we were
 809                                    outputting characters. */

時々みかけるoff_t、どんな型なのか気になります。
/usr/includeの中を探してみます。

takk@deb9:/usr/include$ grep -r 'define.*\<off_t\>' *
numpy/npy_common.h:    #define npy_off_t off_t
zconf.h:#      define z_off_t off_t
takk@deb9:/usr/include$ 

見つかりません。
define定義ではなく、typdefですね。

takk@deb9:/usr/include$ grep -r 'typedef.*\<off_t\>' *
fcntl.h:typedef __off_t off_t;
fcntl.h:typedef __off64_t off_t;
mysql/my_global.h:typedef off_t os_off_t;
stdio.h:typedef __off_t off_t;
stdio.h:typedef __off64_t off_t;
unistd.h:typedef __off_t off_t;
unistd.h:typedef __off64_t off_t;
x86_64-linux-gnu/sys/mman.h:typedef __off_t off_t;
x86_64-linux-gnu/sys/mman.h:typedef __off64_t off_t;
x86_64-linux-gnu/sys/types.h:typedef __off_t off_t;
x86_64-linux-gnu/sys/types.h:typedef __off64_t off_t;
x86_64-linux-gnu/sys/stat.h:typedef __off_t off_t;
x86_64-linux-gnu/sys/stat.h:typedef __off64_t off_t;
takk@deb9:/usr/include$

ありました、が、さらに__off_tまたは__off64_tの定義を見つけねばなりません。

off_t

off_tの検索結果をもう一度みてみます。

takk@deb9:/usr/include$ grep -r 'typedef.*\<off_t\>' *
fcntl.h:typedef __off_t off_t;
fcntl.h:typedef __off64_t off_t;
mysql/my_global.h:typedef off_t os_off_t;
stdio.h:typedef __off_t off_t;
stdio.h:typedef __off64_t off_t;
unistd.h:typedef __off_t off_t;
unistd.h:typedef __off64_t off_t;
x86_64-linux-gnu/sys/mman.h:typedef __off_t off_t;
x86_64-linux-gnu/sys/mman.h:typedef __off64_t off_t;
x86_64-linux-gnu/sys/types.h:typedef __off_t off_t;
x86_64-linux-gnu/sys/types.h:typedef __off64_t off_t;
x86_64-linux-gnu/sys/stat.h:typedef __off_t off_t;
x86_64-linux-gnu/sys/stat.h:typedef __off64_t off_t;
takk@deb9:/usr/include$

似たような定義をいろんな所でしてるんですね。

off_tを探すのは骨が折れる気がしますので、GNUのサイトでも見てみます。

File Position Primitive (The GNU C Library)
File Position Primitive (The GNU C Library)
13.3 Setting the File Position of a Descriptor

の章に、

Function: off_t lseek (int filedes, off_t offset, int whence)

を見つけることができますが、この関数に使われている引数の型や、戻り値の型について詳細の説明があります。

例えば、filedesに指定する値に関しては、

SEEK_SET

    Specifies that offset is a count of characters from the beginning of the file.
    SEEK_CUR

    Specifies that offset is a count of characters from the current file position. This count may be positive or negative.
SEEK_END

    Specifies that offset is a count of characters from the end of the file. ..

~省略~

まあ、ここらへんはman lseekにもありますね。

で、off_tの説明を見てみると、

Data Type: off_t

    This is a signed integer type used to represent file sizes. In the GNU C Library, this type is no narrower than int.

    If the source is compiled with _FILE_OFFSET_BITS == 64 this type is transparently replaced by off64_t. 

Data Type: off64_t

    This type is used similar to off_t. The difference is that even on 32 bit machines, where the off_t type would have 32 bits, off64_t has 64 bits and so is able to address files up to 2^63 bytes in length.

    When compiling with _FILE_OFFSET_BITS == 64 this type is available under the name off_t. 

ただの整数型でした。
できれば、Webを探さずに、ソースからぱっと見つけたいですね。

main関数

mainを読んでいきます。
mainなので、switch caseのかさばるオプション処理がほとんどだと思いますが。まあ、最初から見ていきましょう。

2413 int
2414 main (int argc, char **argv)
2415 {
2416   char *keys = NULL;
2417   size_t keycc = 0, oldcc, keyalloc = 0;

まずはkeys、keycc、oldcc、keyallocというローカル変数の登場。
ソースを読む時に楽しいことの一つが、変数の名前から処理を想像するってのがあります。
で、これらの変数名から、想像できるかというと・・・ちょっと敷居が高かったです。

さらに他のローカル変数も見てみましょう。

2418   int matcher = -1;
2419   bool with_filenames = false;
2420   size_t cc;
2421   int opt, prepended;
2422   int prev_optind, last_recursive;
2423   int fread_errno;
2424   intmax_t default_context;
2425   FILE *fp;

matcher。-1が初期値で代入されてるってことは、0以上の数が有効な数値に違いないです。マッチした数でしょうか。マッチ数を英語的にmatcherというかは謎です。

with_filenamesは、たぶんそのままですね。検索結果にファイル名を含めるかのフラグでしょう。
ccは、まったく分かりません。
optは、オプション関連かと思います。intなのでoptの数とかでしょうか。
FILE *fpは、grepをする対象のファイルをオープンするためのファイルポインタですね。
まあ、こんな風に、変数名で、いろいろ想像します。大半は間違っていますが。

さらに読み進めます。

2426   exit_failure = EXIT_TROUBLE;
2427   initialize_main (&argc, &argv);
2428
2429   eolbyte = '\n';
2430   filename_mask = ~0;
2431
2432   max_count = INTMAX_MAX;
2433

グローバル変数の初期化ですね。exit_failureは、grepのプログラムが終了した時のエラーコードでしょう。

initialize_main。main処理の初期化ってよく分かりません。この処理が何をやっているか覚えたらステップアップできそうです。

eolbyte。当然End Of Lineの略ですね。改行コードで初期化されていますが、EOLのキャラクタが改行以外にも、変化するのでグローバル変数なんでしょう。

filename_mask。 0を反転。ん? ~って反転でしたっけ? 忘れたなら、プログラミングして即確認。

takk@deb9:~/tmp$ cat t.c
#include <stdio.h>
int main()
{
        printf("~0 = %x\n",~0);
}
takk@deb9:~/tmp$
takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
~0 = ffffffff
takk@deb9:~/tmp$

~0は、bit反転で、合ってました。

filename_maskなのに、オール1のbitって、どんな意味なんでしょう。

setlocale

2434   /* The value -1 means to use DEFAULT_CONTEXT. */
2435   out_after = out_before = -1;
2436   /* Default before/after context: changed by -C/-NUM options */
2437   default_context = -1;
2438   /* Changed by -o option */
2439   only_matching = false;

ようやくコメントが現れました。しかし、なくても意味のわかるコメントかもしれません。
デフォルト値としてよく-1を使ってますからね。

続きます。

2441   /* Internationalization. */
2442 #if defined HAVE_SETLOCALE
2443   setlocale (LC_ALL, "");
2444 #endif

おきまりのLOCALEの設定っぽいです。

man 3 setlocaleを読んでみます。

SETLOCALE(3)               Linux Programmer's Manual              SETLOCALE(3)

名前
       setlocale - 現在のロケール (locale) を設定する

書式
       #include <locale.h>

       char *setlocale(int category, const char *locale);

説明
       setlocale()   関数はプログラムのカレントロケールを設定したり 問い合わせ
       たりするのに用いられる。

       locale が NULL でなければ、プログラムのカレントロケールは引き数に従って
       変更される。 引き数 category はプログラムのカレントロケールのどの部分を
       変更するかを決める。

続きます。

2445 #if defined ENABLE_NLS
2446   bindtextdomain (PACKAGE, LOCALEDIR);
2447   textdomain (PACKAGE);
2448 #endif

また知らない関数が出てきました。

BINDTEXTDOMAIN(3)          Library Functions Manual          BINDTEXTDOMAIN(3)

NAME
       bindtextdomain - set directory containing message catalogs

SYNOPSIS
       #include <libintl.h>

       char * bindtextdomain (const char * domainname, const char * dirname);

DESCRIPTION
       The  bindtextdomain  function  sets the base directory of the hierarchy
       containing message catalogs for a given message domain.

今回初めて知りましたが、何をやる関数かは分かりませんが文章を読む限り、きっと便利な関数に違いありません。

コメント

タイトルとURLをコピーしました