I did a project for college where I had to implement a function that:
Repeated calls (e.g., using a loop) to
get_next_line()
should let you read the text file pointed to by the file descriptor, one line at a time. Returns the line that was read. If there is nothing else to read or if an error occurred, it returnsNULL
. The returned line includes the terminating\n
character, except if the end of file was reached and does not end with a\n
character.
However now that I am doing another project I had to implement this function to read a file and noticed that it crashes my program with SIGABRT whenever finish reading a file close it and then run it on the same file descriptor again.
(run get_next_line()
several times until end of file; close file; open it again on same fd; run get_next_line()
).
However for the life of me I can't figure out what is causing this. I suspect some weird behaviour of read()
but I am not sure.
My code:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFFER_SIZE 42
#define NUM_OF_FD 255
size_t ft_strlen(const char *s)
{
size_t i;
if (!s)
return (0);
i = 0;
while (s[i])
{
i ;
}
return (i);
}
char *ft_strchr(const char *s, int c)
{
char chr;
chr = (char)c;
if(!s)
return (NULL);
while (*s && *s != chr)
s ;
if (*s == chr)
return ((char *)s);
else
return (NULL);
}
char *strldup(char *s1, int start, int end)
{
char *ptr;
int i;
i = 0;
ptr = malloc(end - start 1);
if (!ptr)
return (NULL);
while (start < end)
ptr[i ] = s1[start ];
ptr[i] = 0;
return (ptr);
}
/*
Allocates (with malloc) and returns a new string,
result of the concatenation of s1 and s2
S1 is freed
*/
char *ft_strjoinfree(char *s1, char *s2)
{
int i;
char *str;
char *hld;
size_t size;
if (!s1 && !s2)
return (NULL);
i = 0;
size = (ft_strlen(s1) ft_strlen(s2) 1);
str = malloc(sizeof(char) * size);
if (!str)
return (NULL);
hld = s1;
if (s1)
while (*s1)
str[i ] = *s1 ;
if (s2)
while (*s2)
str[i ] = *s2 ;
str[i] = '\0';
if (hld)
free(hld);
return (str);
}
char *returner(char **backup, int fd)
{
int index;
char *line;
char *temp;
index = ft_strchr(backup[fd], '\n') - backup[fd] 1;
line = strldup(backup[fd], 0, index);
temp = strldup(backup[fd], index, ft_strlen(backup[fd]));
free(backup[fd]);
backup[fd] = temp;
return (line);
}
char *cycle(char **backup, int fd, char *buf)
{
int size;
size = read(fd, buf, BUFFER_SIZE);
buf[size] = '\0';
while (size > 0)
{
backup[fd] = ft_strjoinfree(backup[fd], buf);
if (ft_strchr(backup[fd], '\n') != NULL)
return (returner(backup, fd));
size = read(fd, buf, BUFFER_SIZE);
buf[size] = '\0';
}
return (NULL);
}
char *get_next_line(int fd)
{
char buf[BUFFER_SIZE 1];
static char *backup[NUM_OF_FD];
char *line;
if (fd < 0 || fd > NUM_OF_FD)
return (NULL);
line = NULL;
if (ft_strchr(backup[fd], '\n') == NULL)
{
line = cycle(backup, fd, buf);
if (line != NULL)
return (line);
}
else
return (returner(backup, fd));
if (backup[fd] != NULL && *backup[fd] != '\0')
{
line = backup[fd];
backup[fd] = NULL;
return (line);
}
free(line);
free(backup[fd]);
return (NULL);
}
int main(int argc, char **argv)
{
int fd;
char *line;
//open file
if (argc != 2)
exit(1);
fd = open(argv[1], O_RDONLY);
if (fd < 0)
exit(2);
//check file and get sizes
line = get_next_line(fd);
while (line != NULL)
{
printf("%s", line);
free(line);
line = NULL;
line = get_next_line(fd);
}
close(fd);
fd = open(argv[1], O_RDONLY);
if (fd < 0)
exit(2);
//Generates SIGABRT
line = get_next_line(fd);
while (line != NULL)
{
printf("%s", line);
free(line);
line = NULL;
line = get_next_line(fd);
}
close(fd);
}
File:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 10 10 0 0 10 10 0 0 0 10 10 10 10 10 0 0 0
0 0 10 10 0 0 10 10 0 0 0 0 0 0 0 10 10 0 0
0 0 10 10 0 0 10 10 0 0 0 0 0 0 0 10 10 0 0
0 0 10 10 10 10 10 10 0 0 0 0 10 10 10 10 0 0 0
0 0 0 10 10 10 10 10 0 0 0 10 10 0 0 0 0 0 0
0 0 0 0 0 0 10 10 0 0 0 10 10 0 0 0 0 0 0
0 0 0 0 0 0 10 10 0 0 0 10 10 10 10 10 10 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
I get SIGABRT
I know that the SIGABRT
is coming from the free()
in ft_strjoinfree
but for that to happen read must return a value but read nothing.
Note that the code is formatted that way as it is a college requirement no more than 25 lines per function.
CodePudding user response:
char *get_next_line(int fd)
{
char buf[BUFFER_SIZE 1];
static char *backup[NUM_OF_FD];
char *line;
//...
free(line);
free(backup[fd]);
return (NULL);
}
When you have reached end-of-file the first time through, you do free(backup[fd])
. Now backup[fd]
contains a (non-null) pointer to unusable memory. When you reopen the file, it is likely that the same fd is used, and so you continue to use the same backup[fd]
pointer, accessing freed memory and causing the crash.
It looks like your code is set up to handle a NULL pointer in backup[fd]
and allocate memory accordingly, since that's what will happen on the first call. So I think that after freeing backup[fd]
, you should set it to NULL
; then the next time through, you will really be starting fresh, and your code will know that fresh memory needs to be allocated.