ふつうのLinuxプログラミングをやってみる その５

有名な本らしいので買ってみました
<a target="_blank" href="https://www.amazon.co.jp/gp/product/4797328355/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4797328355&linkCode=as2&tag=lespacetranqu-22&linkId=b690c3257ff896f2239f1107e5bf95a4">ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道</a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=lespacetranqu-22&l=am2&o=9&a=4797328355" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<a target="_blank" href="https://www.amazon.co.jp/gp/product/B075ST51Y5/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=B075ST51Y5&linkCode=as2&tag=lespacetranqu-22&linkId=aa0915aa60afe5a385a365f9059a2877">ふつうのLinuxプログラミング 第2版　Linuxの仕組みから学べるgccプログラミングの王道</a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=lespacetranqu-22&l=am2&o=9&a=B075ST51Y5" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />


> ふつうのLinuxプログラミングをやってみる その１
https://qiita.com/uturned0/items/b9ae846f2aff5865c074
ふつうのLinuxプログラミングをやってみる その2
https://qiita.com/uturned0/items/56beac990cdd6f1059ed
ふつうのLinuxプログラミングをやってみる その3
https://qiita.com/uturned0/items/675092da8aa89c4b1ff0
その４
https://qiita.com/uturned0/items/8f5765cfc0f0be8a1981
その５
https://qiita.com/uturned0/items/ab97deb489c994a836da
その６
https://qiita.com/uturned0/items/b7df48e87ae9170f3698
その７
https://qiita.com/uturned0/items/263151cf7c83bff7fac1
その７−３
https://qiita.com/uturned0/items/3fbf3ed6bb2a47325f59

---

# chapter 5

file descripter はただのint = streamの番号,IDのようなもの

どのprocessも３つのストリームがあって、file descripterが既知のものに鳴っている

fd 0 = standard input = マクロ名 STDIN_FILENO = path: /dev/stdin
fd 1 = standard output = マクロ名 STDOUT_FILENO = path: /dev/stdout
fd 2 = standard errour output = マクロ名 STDERR_FILENO = path: /dev/stderr


標準入力の標準はデフォルトという意味

```ruby
$ cat
aaa
aaa
```

cat コマンドは引数なしだとstdinをstdout煮出すだけの動きをする。ストリームをin-outつないでるだけ。


```ruby
$ cat < .bashrc
# .bashrc
# Source global definitions
...
```

ファイルを与えた時、catは実はファイルをreadするということをしていない！！　シェルが勝手にファイルの中身をstdinとしてcatに渡しているだけ。 **catは入力がファイルだということを知らない** そうだったんだ！！！！！すげぇ！！

STDERRがあるのは、pipeしてると次のprocessのstdinに入って、気付かないから。stderrは特別に、その外に表示することになってる。アタマイイ。stdoutは機械向け、stderrは人間向け。なるほど。





## read(2)

`$ man 2 read` すると

> 名前
>        read - ファイルディスクリプタから読み込む
> 
> 書式
> 
>        #include <unistd.h>
> 
>        ssize_t read(int fd, void *buf, size_t count);    <---プロトタイプ宣言


ssize_t は符号付き整数型、size_tは符号なし整数型、つまり int/longなんだけど、kenelが違うとint型の仕様が違ったりするので、その際を隠蔽してる。必ずssize_tを使う。

### C 言語のプロトタイプとは

先に名前、返り値だけ指定しといて、後で中身を書くやり方のやーつ

```c
/* プロトタイプ宣言 */
void hello(void);
 
int main(void){
  ...長い処理
}
 
/* hello関数をあとで宣言 */
void hello(void){
  printf("Hello, World!\n");
}
```
> https://webkaru.net/clang/function-declaration-prototypes/

人間が読める文字を格納するchar配列の最後は `'\0` にするのが慣習。printfがそう。しかしread()は違う。
read()→printf()するとヤバい。

## この本のストリームとは

file descripterで表現され、read() write() を呼べるもののこと

fileをopenすると read/writeできるものが作られる。そこにはストリームがある。
pipe/network socketもストリームの仲間。



## open(2) 


```
$ man 2 open
名前
       open, creat - ファイルやデバイスのオープン、作成を行う

書式
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

       int open(const char *pathname, int flags);
       int open(const char *pathname, int flags, mode_t mode);

       int creat(const char *pathname, mode_t mode);
```

openはpathにつながるストリームを作成し、そのストリームを指す **file descripterを返す** ほう。socketみたいなものを開いて、そのIDを返すのか。なるほど

2nd argのflagは O_RDONLY, O_WRONLY, O_RDWR 文字通りｒ・ｗ・ｒｗ

書き込む場合(O_WRONLY or O_RDWR) は追加のflagを一緒に2nd argに入れられるぽい

O_CREAT if not exist, create a file 
O_EXCL if exist return error
O_TRUNC if exist make the file empty 
O_APPEND 追記していく


# close()

ファイルを閉じる＝ストリームを破棄する＝close()
kernelがprocess終了時に全streamは破棄してくれる。プロセスが使える同時ストリーム数は制限がある。
close()したほうがよさげ。


# cat コマンドを作る

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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)
{
	int fd;
	unsigned char buf[BUFFER_SIZE];
	int n;

	fd = open(path, O_RDONLY);
	if (fd < 0) die(path);
	for (;;) {
		n = read(fd, buf, sizeof buf);
		if (n < 0) die(path);
		if (n == 0) break;
		if (write(STDOUT_FILENO, buf, n) < 0) die (path);
	}
	if (close(fd) < 0) die(path);
}

static void
die(const char *s)
{
	perror(s);
	exit(1);
}
```

```ruby
コンパイル
$ gcc -Wall -O2 cat.c

実行
$ ./a.out
./a.out: file name not given

ファイル渡す
$ ./a.out /etc/passwd
##
# User Database
#
...

複数ファイル
$ ./a.out /etc/hosts /etc/resolv.conf
##
# Host Database
#
...
# macOS Notice
#
# This file is not consulted for DNS hostname resolution, address
...

エラー
$ ./a.out notexist
notexist: No such file or directory
```


おお〜〜〜動いた〜〜〜〜！！！


`argc` 引数の数。

`argv[0]` programの名前

`For (;;)` 泣いても喚いても繰り返せ

`n==0` ファイルが終わったら。

```ruby
$ man 2 read
RETURN VALUES
     If successful, the number of bytes actually read is returned.  Upon reading end-of-file, zero is returned.
```

ということなので、read()で0が来たらファイルの終わり。その続きに

```ruby
 Otherwise, a -1 is returned and
     the global variable errno is set to indicate the error.
```

errorなら -1 がreturnとある. do_catの中のエラー処理はそんな漢字。


`read(fd, buf, sizeof buf)` の意味

**fd** = open()したfileのstream. stream IDぽいintが入ってる。open()がIDを作ってreturnしてくれる

**buf** = fdから入ってきたデータ(byte列）を貯めるバッファ。

**sizeof buf** = buf変数の長さ。最大でここまでreadする（bufに貯める)オプション
defineしてるBUFFER_SIZEにしても動くけど、bufとBUFFER_SIZEの関連がこの行だけではわからないので、sizeof buf のほうが良いコード。

**n** = read()が読み込んだサイズ。途中でファイル終わることもあるので。これをwrite()にわたす


##### bufは初期化されず、常に上書きされている
bufを初期化しないのに、ループでread()から詰め込んでイケてるのはなぜだろう。もしかしてbufは常に上書きされてて、でもwrite()するときに必要な n byteしか出力しないからバレてないんじゃないだろうか。

試した。

こういうファイルを作る

```ruby
$ cat test.txt
abc
d
```


catコマンドのbuffer sizeを変更

```
#define BUFFER_SIZE 4  <--- abc[改行]の4バイトがバッファになるように
```

write()を1byte `(n+1)`多く出すようにする

```
		if (write(STDOUT_FILENO, buf, n+1) < 0) die (path);
```

実行したら、通り過ぎたはずの `c` が出てきた！

```c
$ ./a.out test
abc
d
c$
```

1行目で入ったbufは きっと `abc[改行]`
2行目は `d[改行]` がread()されたはずで、でもbufには前のループのが残ってるのできっと `d[改行]c[改行]` になったんじゃないだろうか。n+1しか出してないので `d[改行]c` だけ出た。

```
		if (write(STDOUT_FILENO, buf, n+2) < 0) die (path);
```

↑ n+2 にしたら、予想通りcの後ろの改行もでました

```
$ ./a.out test
abc
d
c
$
```




## perror()

よくわからんかった。errnoに合わせたエラーメッセージを標準出力に返す。stderrじゃないのね。
errnoがどこから来るのかよくわからん。die()すると、例えば read()のエラー `EFAULT` が謎の通路を通ってperror()に届くんだろうか。perror()はprogram名(argv[0]) と errnoから作ったエラーメッセージを出す。
perror(path) といって渡したpathは何も表示されない。謎すぎる。


### lseek()

fdのcursor(file offset)を動かせる。試しにwrite()するたびにlseek +1 してみた。

```c
		if (write(STDOUT_FILENO, buf, n) < 0) die (path);
		lseek(fd, +1, SEEK_CUR);            <------------------
```

各行の先頭文字を消すつもりだったけど、ずれがずれを生むのでずれずれになった。なるほど。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/ee211c46-047d-6cb3-0f11-4b15524eb3a5.png)





## いくらでも引数をたしてもいいという `...`

```ruby
$ man ioctl
NAME
     ioctl -- control device

SYNOPSIS
     #include <sys/ioctl.h>

     int
     ioctl(int fildes, unsigned long request, ...);
```

第3引数の`...` は可変長引数で、好きな数だけ好きな型の引数を渡していい、という意味



### 練習問題 5.8
catが引数無しで来たらstdinを引くようにする

引数なしエラーのとこで do_catに `"/dev/stdin"` を渡すだけでよい。


```c
main(int argc, char *arvg[])
{
	int i;
	if (argc < 2) {
		do_cat("/dev/stdin");
	}
```

結果

```
$ gcc -Wall -O2 cat.c && ./a.out test
abc
def
xyz
012

$ gcc -Wall -O2 cat.c && ./a.out
a
a
b
b
c
c
^C
```


最初シングルquoteで書いたらwarningが出て、double quotesにしたら直った。なんとなく最近読んだ <a target="_blank" href="https://www.amazon.co.jp/gp/product/4798047767/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4798047767&linkCode=as2&tag=lespacetranqu-22&linkId=8c7293db081310ffa9061bc13130592a">これならわかる! C言語入門講座 第2版</a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=lespacetranqu-22&l=am2&o=9&a=4798047767" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> が役に立った。何がどう違うのか忘れたけど、signel/doulbe quoteはすっごい違うものという記憶は残ってた。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">char i = &#39;a&#39;<br>char i[] = &quot;abc&quot;<br>こんな謎のルールを学んだ。single double quote大事なのね<br>あと今更ながらポインタを使う意味がわかった、遠くの変数書き換えるためなのね</p>&mdash; くじ@uturned (@uturned0) <a href="https://twitter.com/uturned0/status/1314937895233490944?ref_src=twsrc%5Etfw">October 10, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## byte単位で読むread()の問題
きれいにいけば日本語も表示できるが、ちょっとでもずれると文字化けする。
文字単位で拾えればこうはならない。それは、次のchapterで。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/5819b035-74a2-d3f6-440d-2ae37f957050.png)





ふう。やっとchapter 5 終わった。もう1時。おやすみなさい。本格的になってきた！