open, read, write, close

UNIXのAPIの最も基本的な要素であるopen, read, write, closeを駆使して「cat」コマンドの原型を作成。


/* mycat.c */
/* concatenation */
#include<unistd.h> /* read, write close */
#include<sys/types.h> /* open */
#include<sys/stat.h> /* open */
#include<fcntl.h> /* open */
#include<stdio.h> /* fprint, perror */
#include<stdlib.h> /* exit */

/*ファイルを開いて内部情報を読み込んで、書き出して、最後は閉じる関数のプロトタイプ宣言*/
static void do_cat(const char *path);
/*エラー処理用の関数のプロトタイプ宣言*/
static void die(const char *s);


int main(int argc, char *argv[])
{
int i;

/*引数が一つも無いときは、エラー処理を行う*/
if(argc < 2){
fprintf(stderr, "%s: file name not given\n", argv[0]);
exit(1);
}

for(i=1; i<argc; i++){
do_cat(argv[i]);
}
/*正常終了*/
exit(0);
}



#define BUFFER_SIZE 2048



static void do_cat(const char *path)
{
/*file descriptorを宣言*/
int fd;

unsigned char buf[BUFFER_SIZE];
int n;

/* openは成功すればストリームを作るとともにfile descriptorを返す。*/
/* openは失敗すれば-1を返す*/
/* Read Onlyモードで開く*/
fd = open(path, O_RDONLY);
/*開けなかった時はプログラムを終了*/
if(fd < 0) {die(path);}

for(;;){
/*fd番目のストリームからバイト列を読み込む*/
/*openによる読み込みが問題無く終了したときは0を、エラーが生じたときは-1を返す*/
n = read(fd, buf, sizeof(buf) );
if(n<0){ die(path);}
if(n==0){ break;}
/* writeによりbufsizeバイト分をbufからファイルディスクリプタ番(今回は標準出力*/
/*、すなわちSTDOUT_FILENO番)のストリームに書き込む*/
/* 書き込んだときは書いたバイト数を返す*/
if(write(STDOUT_FILENO, buf, n) < 0){ die(path);}
}
if(close(fd) < 0){ die(path); }
}



/*エラー処理の関数dieの中身*/
static void die(const char *s)
{
/*システムコールが失敗した時はグローバル変数errnoに-1がセットされる*/
/*perrorはこれを読み込んでエラーメッセージを出力する*/
perror(s);
exit(1);
}

/* ここまで */



#コンパイルして実行してみます。
$ gcc -o mycat mycat.c

#テストファイルを二つ作ります。
$ echo  "I love you" > test1
$ echo  "She loves you" > test2

#実行


$ ./mycat test1
I love you



$ ./mycat test2
She loves you


$ ./mycat test1 test2
I love you
She loves you



$ ./mycat  test*
I love you
She loves you

#引数を与えない時のエラー出力のテスト
$ ./mycat
./mycat: file name not given

#存在しないファイル名を与える
$ ./mycat TEST
TEST: No such file or directory