C言語入門(27) 実際にプログラムを読んで理解を深めよう ls編(1)

Posted: 2013年02月14日

前回でひと通りC言語について書いてきたと思っています。
書き残しに気がついたら随時、書いていきます・・・

これからは、C言語で記述されたプログラムを見て解説していきます。
初回は、lsコマンドの実装を見ていきます。
lsコマンドとは、Windowsではdirに近いコマンドで
フォルダのファイル一覧を表示するコマンドです。
lsのソースを読むことを推奨または、読んでいないことは恥ずべきこととも言われています。

最新のlsコマンドのソースは
http://www.freebsd.org/cgi/cvsweb.cgi/src/bin/ls/
です。

今回解説するソース(2013/02/08現在最新)は、
/wp-content/uploads/2013/09/ls.zip
です。

初回という事でエントリーポイントから始めます。
ls,c main関数です。

プログラムプログラム:

    /* Terminal defaults to -Cq, non-terminal defaults to -1. */
    if (isatty(STDOUT_FILENO)) {
        termwidth = 80;
        if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
            termwidth = atoi(p);
        else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 &&
            win.ws_col > 0)
            termwidth = win.ws_col;
        f_nonprint = 1;
    } else {
        f_singlecol = 1;
        /* retrieve environment variable, in case of explicit -C */
        p = getenv("COLUMNS");
        if (p)
            termwidth = atoi(p);
    }

解説解説:

isattyで端末(コマンドプロンプト)実行かどうかを判断します。
端末ではない場合は、パイプ(コマンドの連続実行)echo | uname

端末の場合出力幅を取得します。
取得には、環境変数COLUMNSの値かioctlでウィンドウサイズを取得します
パイプの場合は、端末に出力するわけではないため環境変数の値を用います。

プログラムプログラム:

    while ((ch = getopt(argc, argv,
        "1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,")) != -1) {
        switch (ch) {
        /*
         * The -1, -C, -x and -l options all override each other so
         * shell aliasing works right.
         */
        case '1':
            f_singlecol = 1;
            f_longform = 0;
            f_stream = 0;
            break;
        // 省略
        default:
        case '?':
            usage();
        }
    }

解説解説:

getopt関数で引数の文字列から指定した文字を抜き出し処理を行なっています。
取得した文字分ループを行い、指定された引数に対応するフラグを立てます。
対象外、?が指定された場合は、使用方法を表示する関数usageを呼び出します。
後日usageについても解説していきますがusageで処理が終了する処理が記述されています。

ちなみにlsの引数は下記の入力が許されています。

ls [-ABCFGHILPRSTUWZabcdfghiklmnopqrstuwxy1,] [-D format]

各引数の詳細はlsで検索すれば出てくると思います。

プログラムプログラム:

    /* Enabling of colours is conditional on the environment. */
    if (getenv("CLICOLOR") &&
        (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE")))
#ifdef COLORLS
        if (tgetent(termcapbuf, getenv("TERM")) == 1) {
            ansi_fgcol = tgetstr("AF", &bp);
            ansi_bgcol = tgetstr("AB", &bp);
            attrs_off = tgetstr("me", &bp);
            enter_bold = tgetstr("md", &bp);

            /* To switch colours off use 'op' if
             * available, otherwise use 'oc', or
             * don't do colours at all. */
            ansi_coloff = tgetstr("op", &bp);
            if (!ansi_coloff)
                ansi_coloff = tgetstr("oc", &bp);
            if (ansi_fgcol && ansi_bgcol && ansi_coloff)
                f_color = 1;
        }
#else
        warnx("color support not compiled in");
#endif /*COLORLS*/

#ifdef COLORLS
    if (f_color) {
        /*
         * We can't put tabs and color sequences together:
         * column number will be incremented incorrectly
         * for "stty oxtabs" mode.
         */
        f_notabs = 1;
        (void)signal(SIGINT, colorquit);
        (void)signal(SIGQUIT, colorquit);
        parsecolors(getenv("LSCOLORS"));
    }
#endif

解説解説:

COLORLS定義されているときに色設定を行います。
端末以外は、色設定する必要がないため条件で除外しています。
tgetentで環境変数TERMの内容を設定し
tgetstrで対象の項目(TERMに設定されている色設定)を取得します。

色設定が行われていない場合は、
signal(SIGINT…で
キーボードの入力を求めます。
signal(SIGQUIT…で
キーボードの入力を中断します。
環境変数LSCOLORSに設定されている色設定を読み込みグローバル変数に設定しています。
oarsecolorsはprint.cに定義されている関数のため後日解説していきます。

プログラムプログラム:

    /*
     * If not -F, -P, -d or -l options, follow any symbolic links listed on
     * the command line.
     */
    if (!f_nofollow && !f_longform && !f_listdir && (!f_type || f_slash))
        fts_options |= FTS_COMFOLLOW;

    /*
     * If -W, show whiteout entries
     */
#ifdef FTS_WHITEOUT
    if (f_whiteout)
        fts_options |= FTS_WHITEOUT;
#endif

    /* If -i, -l or -s, figure out block size. */
    if (f_inode || f_longform || f_size) {
        if (f_kblocks)
            blocksize = 2;
        else {
            (void)getbsize(&notused, &blocksize);
            blocksize /= 512;
        }
    }
    // 省略

解説解説:

上の処理で設定してきたフラグをビット演算し一つの変数にまとめています。

プログラムプログラム:

    if (argc)
        traverse(argc, argv, fts_options);
    else
        traverse(1, dotav, fts_options);
    exit(rval);

解説解説:

引数が存在すればtraverseに設定した引数とフラグを
引数が存在しなければ引数がないこととフラグを渡しています。

traverse関数でディレクトリ内を走査するようなので
メインの処理は次回です。

今回は、引き数値のチェックがメインでしたが
コマンドラインプログラムの場合は、引数で駆動することが多いので参考になることは多いと思います。–

カテゴリー: プログラム, 入門 | タグ: , , | コメント無し »

コメント