#contents
*はじめに [#d1b9bdd7]
[[青木峰郎さんは「lsのソースを読んでないのはプログラマとしてかなりまずいのではないでしょうか」と語られたそうです>http://www.gihyo.co.jp/magazine/wdpress/orangenews/archives/10-lsFreeBSD.html]]。というわけ、だけでもないのですがlsを読みます。
ls -l
とした場合に何が行われるかを眺めましょう。
なお今回対象としたバージョンはcoreutils 5.97に含まれているlsです。
*処理の流れ [#g1a19e27]
ではmain関数を眺めてみます。ざっと眺めると以下のブロックに分けることができそうです。
+表示内容の設定
+ファイル情報の表示
それぞれ見ていくことにしましょう。
*表示内容の設定 [#w39d7b78]
**decode_switches() [#dabcf01d]
まずdecode_switch関数を呼んでデフォルト値の設定やオプションの解析を行っています。デフォルト値はいろいろありますがとりあえず以下ぐらいが注目対象でしょう。
:time_type|time_mtime
:sort_type|sort_name
:numeric_ids|false
:indicator_style|none
:dereference|DEREF_UNDEFINED
:immediate_dirs|false
:ignore_mode|IGNORE_DEFAULT
次に環境変数とかから値を設定しています。へ〜って思ったのは以下。仮想コンソールの幅って以下のように取得するらしいです。[[参考>http://www.linux.or.jp/JM/html/LDP_man-pages/man4/tty_ioctl.4.html]]
#ifdef TIOCGWINSZ
{
struct winsize ws;
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1
&& 0 < ws.ws_col && ws.ws_col == (size_t) ws.ws_col)
line_length = ws.ws_col;
}
#endif
って、-lだとこの値使ってませんね。
次にオプションを解析しています。-lしか付けてないので変化があるのは以下のみ。
:format|long_format
最後にformatがlong_formatなのでlong_time_formatの設定をしています。time_style_optionは設定しておらず環境変数もなしとすると"posix-long-iso"になります。lib/hard-locale.cに書かれているhard_locale関数および実験によるとロケールが設定されているとhard_locale関数はtrueを返すようなので下に進んでlong_time_formatがlong-isoに設定されるようです。ん〜、デフォルトでlocale使われてた気がするけど別のlsだったかな?
**残りの設定 [#fd35179e]
main関数に戻るとまだ設定されていない値が設定されています。
:dereference|DEREF_NEVER
:format_needs_stat|true
:format_needs_type|false
*ファイル情報の表示 [#eb43ae04]
表示内容の設定が終わったので次にファイル情報の表示をしてるっぽいところを眺めます。表示するファイルを何も指定しない(カレントディレクトリが表示対象)でimmediate_dirsがfalseなのでqueue_directory関数が呼ばれています。ってqueue_directory関数はpending_dirsに表示対象をつないでるだけですね。
今までの流れからfile_indexは0です。pending_dirsはさっきqueue_directory関数を呼んでNULLでなくなっているのでそちらを見ましょう。-Rとか付けてないので素直にprint_dir関数へ。
**print_dir() [#e66434f7]
再帰の部分は無視します。普通にopendir関数とかを呼んでディレクトリエントリを取得し、以下のことをしています。
+ファイル情報の取得
+ファイル情報のソート
+ファイル情報の表示
***gobble_file() [#e51e44c1]
ではファイル情報の取得です。え〜っと、いきなり超絶なifがありますが、format_needs_statがtrueなのでtrueですね。
dereferenceはDEREF_NEVERなのでlstatでファイル情報を拾っています。
次のACL情報を拾っているのは、有効なのかどうかわかりませんが無視しときます。
次にシンボリックリンクの処理を行っています。リンク先を設定した後の処理は、indicator_styleがnoneなので何も行われないはずですね。それにしても変数名も定数名も同じスタイルなのでどっちが変数なのかわからなくなります。不等号比較しているところは左側が定数名かな。
次、ファイルタイプの設定を行っています。
その後、各フィールドの長さを拾って表示する際の最大長を設定しています。ファイルサイズは-hオプションとかが指定されていると若干複雑になるようですが、指定してないので無視します。
***sort_files() [#ubdf4660]
ファイル情報が取得できたのでsort_typeに応じて普通にソートしてるだけ、かと思いきやいきなりsetjmp関数呼んでいます。どうやら初めは現在のロケールでソートしてみて、現在のロケールでソートできない名前があったら(longjmpされてsetjmp関数しているところに戻る)文字列としてソートするということをしているようです。
***print_current_files() [#g2aa3c1d]
各々のファイルについて、formatはlong_formatなのでprint_long_format関数へ。
まずアクセス権を文字列化しています。mode_string関数はlib/filemode.cに書かれています。
次、ファイル日時情報を拾っています。get_stat_*time関数はlib/stat-time.hです。プラットフォームによってstat構造体の中身が違うようなのでいろいろやっていますが深く気にする必要はないでしょう。
表示する情報をバッファに突っ込んで・・・、所有者とかを表示する場合は直接fputsしてますね。長さがどれぐらいになるかわからないからってことですかね。getuser関数とかはlib/idcache.cにあります。ファイル名通り単純にpasswdから情報拾ってるのではなくキャッシュしているようです。
日付の表示は6ヶ月以上前かどうかでスタイルを変えています。今回はlong-isoなので6ヶ月以上前でも六ヶ月以内でも同じ表示になります。
その後、印字可能かどうかを考慮した上でファイル名とシンボリックリンクならリンク先を表示しています。
*おわりに [#q4a391fd]
以上、ls -lの挙動を見てきました。今回は感想ではなく青木さんが「lsぐらい読んどかないとまずいんじゃないでしょうか」と語られた理由を考えてみます。
-引数によっていろいろ挙動が変わる
-変数はどこでどう設定されるのかをちゃんと把握しておく必要がある
-UNIX系OSのAPIがいろいろ使われている
ということを読んでいて感じた→ということを知っておいた方がいいということではないかなと思います。それではみなさんもよいコードリーディングを。