Home > Blockchain >  Read() is returning without reading
Read() is returning without reading

Time:06-20

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 returns NULL. 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.

  • Related