【C言語】ファイルディスクリプタからファイルの内容を読む

# :muscle:注意

これはある課題の１つなので、課題の題意の意図に沿って実装します

# :muscle:概要１

どういったことをするかをまず書く

```
１）ファイルディスクリプタを受け取る
２）そのファイルからBUFFER_SIZE分読み込む
３）改行までをリターンする
```

ただし、ここでは以下の関数のみが使える

```
read, malloc, free
```

コンパイルオプションは以下

```shell
$ gcc -Wall -Wextra -Werror -D BUFFER_SIZE="任意の値" get_next_line.c get_next_line_utils.c
```

つまり、こんな感じの`main.c`を作ってテストファイルの内容を出力できればＯＫってこと

```main.c
#include <stdio.h>
#include <fcntl.h>
#include "get_next_line.h"

int	main(void)
{
	int		fd;
	char	*line;

	line = "";
	fd = open("test.txt", O_RDONLY);
	while (line)
	{
		line = get_next_line(fd);
		printf("> %s", line);
		free(line);
	}
	return (0);
}
```

# :muscle:基本的な関数

`get_next_line_utils.c`に基本的な関数を書いていく
`stdlib.h`に入っているような関数は自作です
内容については触れません

```get_next_line_utils.c
#include "get_next_line.h"

size_t	ft_strlen(const char *s)
{
	size_t	return_len;

	return_len = 0;
	while (s[return_len] != '\0')
		return_len ++;
	return (return_len);
}

char	*ft_strchr(char *s, int c)
{
	int	i;

	i = 0;
	if (!s)
		return (0);
	if (c == '\0')
		return ((char *)&s[ft_strlen(s)]);
	while (s[i] != '\0')
	{
		if (s[i] == (char) c)
			return ((char *)&s[i]);
		i++;
	}
	return (0);
}

char	*ft_strjoin(char *s1, char *s2)
{
	size_t	i;
	size_t	c;
	char	*rtn;

	if (!s1)
	{
		s1 = (char *)malloc(1 * sizeof(char));
		s1[0] = '\0';
	}
	if (!s1 || !s2)
		return (NULL);
	rtn = malloc((ft_strlen(s1) + ft_strlen(s2) + 1) * sizeof(char));
	if (rtn == NULL)
		return (NULL);
	i = -1;
	c = 0;
	if (s1)
		while (s1[++i] != '\0')
			rtn[i] = s1[i];
	while (s2[c] != '\0')
		rtn[i++] = s2[c++];
	rtn[ft_strlen(s1) + ft_strlen(s2)] = '\0';
	free(s1);
	return (rtn);
}
```

# :muscle:心臓部分の実装

こんな感じで形を作ります。
何回も呼び出されるので`static char`で保存する部分を定義します

```get_next_line.c
/*
lineを定義（CHAR）
saveを定義（STATIC　CHAR）

もし、fdが０より小さいもしくはBUFFER_SIZEが０以下の場合
	0を返す
saveに読み込んだファイルの内容を格納
もし、saveがなかった場合
	NULLを返す
lineに改行が来るまでを入れる
saveに改行後を入れる
lineをリターンする
*/

#include "get_next_line.h"

char	*get_next_line(int fd)
{
	char		*line;
	static char	*save;

	if (fd < 0 || BUFFER_SIZE <= 0)
		return (0);
	save = ft_read(fd, save);
	if (!save)
		return (NULL);
	line = ft_get_line(save);
	save = ft_save(save);
	return (line);
}
```

サブ関数を作っていきます。
まず、saveに'\n'が来るまでファイルを読み込み、代入する関数を作ります

```get_next_line.c
/*
tmpを定義（CHAR）
read関数の返り値を格納するread_rtnを定義（INT）

tmpの領域をBUFFER_SIZE+1分、charのサイズで確保する
もし、tmpがNULLの場合
	NULLを返す（mallocが失敗したときのため）
read_rtn=1で初期化
saveに'\n'が入っていない、かつread_rtnが0でない場合
	BUFFER_SIZE分、ファイルを読み、tmpに格納する
	もし、read_rtnが-1の場合（readに失敗した場合）
		tmpをフリーする
		NULLを返す
	tmpをNULL止めする
	saveにtmpをくっつけて再代入
tmpをフリーする
saveを返す
*/

char	*ft_read(int fd, char *save)
{
	char	*tmp;
	int		read_rtn;

	tmp = malloc((BUFFER_SIZE + 1) * sizeof(char));
	if (!tmp)
		return (NULL);
	read_rtn = 1;
	while (!ft_strchr(save, '\n') && read_rtn != 0)
	{
		read_rtn = read(fd, tmp, BUFFER_SIZE);
		if (read_rtn == -1)
		{
			free(tmp);
			return (NULL);
		}
		tmp[read_rtn] = '\0';
		save = ft_strjoin(save, tmp);
	}
	free(tmp);
	return (save);
}
```

saveの中から改行が来るまでを取り出して、返す関数を作ります

```get_next_line.c
/*
汎用カウンターiを定義（INT）
'\n'の前までを保存する*rtnを定義（CHAR）

i=0で初期化
もし、saveが空な場合
	NULLを返す
save[i]に内容がある間、かつsave[i]が'\n'でない場合
	iに１ずつ足していく（'\n'を含まずに文字数をカウントしている）
マロックでrtnの領域を確保する（'\n'と'\0'の場所のため+2している）
もし、rtnがない場合
	NULLを返す（mallocが失敗したときのため）
i=0で初期化
save[i]に内容がある間、かつsave[i]が'\n'でない場合
	rtn[i]にsave[i]を代入する
	iに１を足す
もし、save[i]に'\n'が来ていた場合
	rtn[i]に'\n'を代入する
rtn[i]をNULL止めする
rtnを返す
*/

char	*ft_get_line(char *save)
{
	int		i;
	char	*rtn;

	i = 0;
	if (!save[i])
		return (NULL);
	while (save[i] && save[i] != '\n')
		i++;
	rtn = (char *)malloc(sizeof(char) * (i + 2));
	if (!rtn)
		return (NULL);
	i = 0;
	while (save[i] && save[i] != '\n')
	{
		rtn[i] = save[i];
		i++;
	}
	if (save[i] == '\n')
	{
		rtn[i] = '\n';
		i++;
	}
	rtn[i] = '\0';
	return (rtn);
}
```

saveの中から改行の後を取り出して、返す関数を作ります

```get_next_line.c
/*
汎用カウンターiを定義（INT）
汎用カウンターcを定義（INT）
'\n'の後ろを保存する*sを定義（CHAR）

i=0で初期化
save[i]があり、かつsave[i]が'\n'でない場合、
	iに１を足し続ける
もし、save[i]がない場合、（'\n'の後ろに何もない場合）
	saveをフリーする
	NULLを返す
sを「saveの長さ - '\n'までの長さ + 1」で確保する（'\n'の後ろとNULL分確保）
もし、sがNULLの場合
	NULLを返す（mallocが失敗したときのため）
iに１を足す
c = 0で初期化
save[i]がNULL出ない場合
	s[c++] = save[i]で'\n'以降を代入し続ける
sをNULL止めする
saveをフリーする
sを返す
*/

char	*ft_save(char *save)
{
	int		i;
	int		c;
	char	*s;

	i = 0;
	while (save[i] && save[i] != '\n')
		i++;
	if (!save[i])
	{
		free(save);
		return (NULL);
	}
	s = (char *)malloc((ft_strlen(save) - i + 1) * sizeof(char));
	if (!s)
		return (NULL);
	i++;
	c = 0;
	while (save[i])
		s[c++] = save[i++];
	s[c] = '\0';
	free(save);
	return (s);
}
```

# :muscle:検証

```shell
$ cat test.txt -e
hello world$
hello$
$
 $
  $
   $
   $
   $
    $
     $
      $
      $
       a$
       g$
       a$
       gr@[rgeapage,pg@agre$
       ae$
       gpamtamt$
       $
       $
       $
       $
       $
aopt,,,,,gbg$
$ clear && gcc -Wall -Wextra -Werror -D BUFFER_SIZE=42 get_next_line.c get_next_line_utils.c main.c -g && ./a.out | cat -e
> hello world$
> hello$
> $
>  $
>   $
>    $
>    $
>    $
>     $
>      $
>       $
>       $
>        a$
>        g$
>        a$
>        gr@[rgeapage,pg@agre$
>        ae$
> (null)$ 
```

でけた！
詳しい説明とかできてないけど、もしかしたら書き加えるかも

# :muscle:参考サイト

https://zenn.dev/tkoyasak/articles/69ac9d00cf2018

http://www9.plala.or.jp/sgwr-t/lib/calloc.html

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/read.2.html

https://qiita.com/toshihirock/items/78286fccf07dbe6df38f