I am working on a function that must read one line at a time from a file, give the file descriptor of said file as input.
Here is my get_next_line function, and helper functions on top of it
# include <fcntl.h>
# include <stdlib.h>
# include <unistd.h>
# include <stdio.h>
char *addline(int fd, char *line)
{
int i;
char *tmp;
int bytes;
i = 0;
bytes = 1;
if (read(fd, 0, 0) < 0 )
return (NULL);
while (line[i] != '\n')
{
tmp = (char *) malloc((BUFFER_SIZE + 1) * sizeof(char));
if (!tmp)
return (NULL);
bytes = read(fd, tmp, BUFFER_SIZE);
if (bytes == -1)
return (free(tmp), free(line), NULL);
tmp[bytes] = '\0';
line = ft_strjoin(line, tmp);
if (bytes == 0)
return (line);
i = 0;
while (line[i] && line[i] != '\n')
i++;
}
return (line);
}
char *ft_nextline(char *str, int i)
{
int j;
char *line;
j = i;
line = (char *) malloc((i + 1) * sizeof(char));
if (!line)
return (NULL);
line[i] = '\0';
while (i--)
line[i] = str[i];
i = 0;
if (str[j] == '\0')
{
str[i] = '\0';
return (line);
}
while (str[j])
{
str[i] = str[j];
i++;
j++;
}
str[i] = '\0';
return (line);
}
char *get_next_line(int fd)
{
static char *line;
int i;
if (fd < 0 || fd > 1023 || BUFFER_SIZE <= 0)
return (NULL);
if (!line)
line = ft_strdup("");
line = addline(fd, line);
i = 0;
if (!line)
return (free(line), NULL);
while (line[i] && line[i] != '\n')
i++;
if (line[i] == '\n')
return (ft_nextline(line, ++i));
else if (line[i] == '\0' && i > 0)
return (ft_nextline(line, i));
return (free(line), NULL);
}
I will basically read into the file BUFFER_SIZE elements ( integer defined in the header file, it will have different values like 1, 42, 10000 or 9999), join this read string with a line string and check for \n. If found it shall return this result, from which I will extract the line, then memmove the useful bytes (everything after \n) to the bytes I've extracted, null terminating the result.
This process, with enough frees in different places (especially in my strjoin function) will allow me to print a line, remember what i read from the next and continue this process until EOF.
Below my two utils functions, string duplicate and string join :
char *ft_strdup(char *s)
{
char *duplicate;
size_t i;
size_t j;
i = 0;
j = 0;
while (s[i])
i++;
duplicate = (char *) malloc((i + 1) * sizeof(char));
if (!duplicate)
return (NULL);
duplicate[i] = '\0';
while (s[j])
{
duplicate[j] = s[j];
j++;
}
return (duplicate);
}
char *ft_strjoin(char *s1, char *s2)
{
int i;
int j;
char *s3;
i = 0;
j = 0;
while (s1[i])
i++;
while (s2[j])
j++;
if (i == 0 && j == 0)
return (free(s1), free(s2), NULL);
else if (i == 0 && j != 0)
return (free(s1), s2);
else if (j == 0 && i != 0)
return (free(s2), s1);
s3 = (char *) malloc((i + j + 1) * sizeof(char));
if (!s3)
return (NULL);
s3[i + j] = '\0';
while (j--)
s3[i + j] = s2[j];
while (i--)
s3[i] = s1[i];
return (free(s1), free(s2), s3);
}
I do not pass a check of a testor program called francinette
The test consists of a "NULL check" that doesn't pass. I understand that it's an edge case where my function should return NULL, but segfaults instead.
The input is empty.txt, which is empty.
How can my function return NULL on my own tests and not NULL on the tests of the francinette? The function is Get_Next_Line.
Below is the output of francinette that causes my problem.
BUFFER_SIZE: 1
Invalid fd : 1.OK 2.OK 3.OK 4_LEAKS.OK 5_NULL_CHECK.OK
empty.txt : 1.OK 2.OK 3_LEAKS.OK 4.KO Segfault
Segmentation fault (core dumped)
make: \[Makefile:23: mandatory\] Error 139 (ignored)
BUFFER_SIZE: 10
Invalid fd : 1.OK 2.OK 3.OK 4_LEAKS.OK 5_NULL_CHECK.OK
empty.txt : 1.OK 2.OK 3_LEAKS.OK 4.KO Segfault
Segmentation fault (core dumped)
make: \[Makefile:24: mandatory\] Error 139 (ignored)
BUFFER_SIZE: 1000000
Invalid fd : 1.OK 2.OK 3.OK 4_LEAKS.OK 5_NULL_CHECK.OK
empty.txt : 1.OK 2.OK 3_LEAKS.OK 4.KO Segfault
Segmentation fault (core dumped)
make: \[Makefile:25: mandatory\] Error 139 (ignored)
What is the solution?
get_next_line()to be one that the caller has responsibility to free (so it frees it), which could cause a problem on subsequent calls toget_nextLine()because it invalidates that function'sstaticcopy of a pointer to the same space. Even if that turns out not to be the issue, though,staticlocal variables are tricky and usually best avoided. Especially modifiable ones and those that get exposed outside the function to which they belong.duplicate = (char *) malloc((i + 1) * sizeof(char));are not needed induplicate = (char *) malloc((i + 1) * sizeof(char));.. Considerduplicate = malloc(i + 1);unless you like WET.get_next_line()does not know whether it has been freed, and assumes that it has not been. If its assumption is wrong then that leaves it dereferencing an invalid pointer. If it is the caller's responsibility to free the returned pointer, then the corresponding responsibility ofget_next_line()is to allocate fresh space on every call.#include <stdlib.h>in the head of the compilation unit) because you are smart enough and you know what you are doing. The compiler will silently interpret (well not silently, you will get just a warning if you are using C98, for example) and let you on your own.