本章で作ったcatコマンドを改造して、コマンドライン引数でファイル名が渡されなかったら標準入力を読むようにしなさい。サンプルコードのcat3.cを参照してください。サンプルコードのwc-l-syscall.cを参照してください。タブ文字('\t')を「\t」という2文字、改行を「'$'+改行」の2文字として置き換えながら出力するcatコマンドを書きなさい。サンプルコードのcat-escape.cを参照してください。ファイル末尾に'\n'がない場合にも対応しましょう。サンプルコードのwc-l-stdio.cを参照してください。fread()とfwrite()を使ってcatコマンドを書きなさい。サンプルコードのcat5.cを参照してください。第6章の練習問題で作った、'\t'や'\n'を可視化する機能を、catコマンドのオプションとして使えるようにしなさい。サンプルコードのcat4.cを参照してください。ファイルの最後の数行を出力するtailコマンドの実装を考えなさい。出力する行数は固定で構いません。(少し難しい)サンプルコードのtail.cを参照してください。buffer)というデータ構造を使っています。末尾まで到達したら最初に戻ってデータを上書きするところが異なります。tailコマンドの場合、ファイルを読み込みながら常にそのときの末尾10行だけがあれば十分なわけです。言い換えると、11行目を読んだら1行目は不要になるはずです。そこで11行目は1行目を書いたところに上書きしてしまえばいいだろうという発想が出てきます。リングバッファはまさにそのような処理をするためのデータ構造です。本物のgrepコマンドにある-iオプションと-vオプションを、本章で作ったgrepコマンドに追加しなさい。サンプルコードのgrep2.cを参照してください。正規表現に適合した行ではなく、適合した部分文字列を出力するコマンドsliceを書きなさい。regexec()の第3引数と第4引数を使う必要があるのでマニュアルで調べましょう。サンプルコードのslice.cを参照してください。コマンドライン引数で指定されたディレクトリ以下を再帰的にトラバースして、見つかったファイルのパスをすべて表示するプログラムを書きなさい。シンボリックリンクをたどってはいけません。サンプルコードのtraverse.cを参照してください。ファイルをopen()して、close()する前にそのファイルをrename()すると何が起きるでしょうか。unlink()はどうか、別のファイルをrename()するとどうなるか、実験して調べなさい。open元のファイルをrename()したりunlink()したりしても、すでに接続されているストリームを使うとファイルの読み書きを継続できます。これはUNIXのファイルシステムの大きな特徴です。サンプルコードのmkpath.cを参照してください。エラーになったら親ディレクトリを作りに行くという戦術を採用しています。mkdirしてみて、成功したらそれで終わります。この繰り返しでディレクトリを再帰的に作成します。この戦術のよいところは、同じ戦術のmkpathプログラムがいくつ同時に動いていても正常に動作するところです。他の方法としてはstat(2)でディレクトリが存在するかチェックしてからmkdir(2)で作りに行く方法も考えられます。mkdir(2)がエラーになる可能性があります。mkdir(2)する戦術だと、そのような微妙なタイミングによるエラーは起きません。標準入力の末尾数行だけを出力するtailコマンドを書きなさい。表示する行数はコマンドラインオプションで受け取れるようにしてください。サンプルコードのtail2.cを参照してください。動的にメモリを確保する必要があるため、mallocを使うところだけが違います。fork()したらプロセスが使うメモリは倍になるのでしょうか。forkするだけではメモリ使用量はほとんど増えません。Linuxでは、forkした直後の2つのプロセスはほとんどの論理アドレスについて元の物理メモリを共有するからです。どちらかのプロセスでメモリへの変更を行うと、そのとき始めて新しい物理メモリが割り当てられます。Writeと言います。fork()とexec()を使ってプログラムを起動する、簡単なシェルを書きなさい。サンプルコードのsh1.cを参照してください。問題2で作ったシェルにパイプとリダイレクトを実装しなさい。(難しい)サンプルコードのsh2.cを参照してください。SIGINTシグナルを受けたらメッセージを出力して終了するプログラムを書きなさい。シグナルを待つにはpause()というAPIが使えます。サンプルコードのtrap.cを参照してください。pause(2)でシグナルをじっと待ちます。難しいところはないですね。シェルのpwdコマンドを、シェルから独立したプログラムとして書くのは適切でしょうか。調べて、その理由を答えなさい。子プロセスであればシェルのカレントディレクトリを正しく取得できるからです。シェルのcdコマンドを、シェルから独立したプログラムとして書くのは適切でしょうか。調べて、その理由を答えなさい。シェルのカレントディレクトリはシェルプロセスの属性だからです。自分以外のプロセスの属性を変更することは、基本的にはできません。シェルプロセスのカレントディレクトリは変更できないわけです。第10章で作成したlsコマンドを改造して、ファイルのオーナーのユーザー名(IDではない)と最終更新時刻を表示するようにしなさい。サンプルコードのls2.cを参照してください。ユーザーIDはgetpwuid(3)で名前に、時刻はctime(3)で文字列に変換します。そこは難しくないと思いますが、パスのバッファを確保するのが地味に面倒ですね......。telnetコマンドの使い方を調べて、daytimeサーバに接続してみなさい。CentOSではyumコマンドでtelnetパッケージをインストールする必要があります。daytime」で接続します。すると次のように現在時刻が表示され、自動的に接続が切れるはずです。echoプロトコルは、こちらからソケットに書き込んだ内容をそのまま返してくるテスト用プロトコルです。echoプロトコルのクライアントを書きなさい。組み込まれているので、同じ方法で起動できます。サンプルコードのechoclient.cを参照してください。内容はほとんどdaytimeクライアントと同じです。ハマりがちな点は、fdopenするときにモードを書き込み可能(w+)にするのを忘れる、くらいでしょう。