cmemory-leaksinfinite-loopgetlinefile-descriptor

Problem extracting a line from a buffer readed from a file descriptor


Well, i am trying to create a program that reads from a file descriptor piece by piece (defined as BUFFER_SIZE), until it finds a '\n' or the EOF. The problem its i am getting stuck in a infinite loop, which i try to address the cause without success, (you can notice the printf's all around the code). note: for this assignment i am only allowed to use a limited number of standard functions so to use realloc i have to implement my own, same with strlen and others. (assume that they are correct). ALLOWED FUNCTIONS: WRITE, READ, MALLOC AND FREE. There is the code:

void    *ft_realloc(void *ptr, size_t oldsize, size_t size)
{
void    *new;
size_t  minsize;

if (size == 0)
{
    free(ptr);
    return (NULL);
}
if (ptr == NULL)
    return (malloc(size));
new = malloc(size);
if (!new)
   return (NULL);
minsize = size;
if (oldsize < size)
   minsize = oldsize;
memcpy(new, ptr, minsize);
while (size - oldsize != 0)
   ((char *)new)[oldsize++] = '\0';
free(ptr);
return (new);
}

int find_newline_eof(char *buffer)
{
int i;

i = 0;
if (!buffer)
    return (-1);
while (buffer[i])
{
    if (buffer[i] == '\n')
        return (i);
    i++;
}
return (-1);
}

char    *extract_line(char **buffer, int newline_pos)
{
char    *line;
int     remaining_len;
//in terms of params, to newline be -1, means that we reach EOF 
 without '\n'
if (!*buffer)
    return (NULL);
if (newline_pos == -1)
    newline_pos = ft_strlen(*buffer);
line = malloc(newline_pos + 1);
if (!line)
    return (NULL);
memcpy(line, *buffer, newline_pos);
line[newline_pos] = '\0';
if ((*buffer)[newline_pos] == '\n')
    newline_pos++;
printf("reached EXTRACT_LINE, line: (%s)\n", line);
return (NULL); // The function reaches HERE, the error is beyond.
remaining_len = ft_strlen(*buffer + newline_pos);
// memmove safer against overlapping than memcpy
memmove(*buffer, *buffer + newline_pos, remaining_len + 1);
*buffer = ft_realloc(*buffer, ft_strlen(*buffer), remaining_len + 1);
if (!*buffer)
{
    free(line);
    return (NULL);
}
return (line);
}

int read_newpiece(int fd, char **buffer)
{
int         bytes_readed;
char        current_buffer[BUFFER_SIZE + 1];
char        *new_buffer;

bytes_readed = read(fd, current_buffer, BUFFER_SIZE);
if (bytes_readed <=0) //SAFE CHECKS
    return (bytes_readed);
current_buffer[bytes_readed] = '\0';
//printf("entered here!\n");
//return (bytes_readed);
if (!*buffer)
{
    *buffer = malloc(1);
    if (!*buffer)
        return (-1);
    (*buffer)[0] = '\0'; // Initialize as an empty string
}
new_buffer = ft_realloc(*buffer, ft_strlen(*buffer), 
ft_strlen(*buffer) + bytes_readed + 1);
if (!new_buffer)
    return (-1);
*buffer = new_buffer;
strncat(*buffer, current_buffer, bytes_readed);
return (bytes_readed);
}

char    *get_next_line(int fd)
{
static char *buffer = NULL;
char        *line; //returned value
int         newline_index; //position where we find the EOF or '\n';

if (fd < 0 || BUFFER_SIZE <= 0)
    return (NULL);
if (!buffer)
{
    buffer = malloc(1);
    if (!buffer)
        return (NULL);
    buffer[0] = '\0';
}
newline_index = -1; //-1 (NOT FOUND)
while (newline_index == -1 && read_newpiece(fd, &buffer) > 0)
{
    printf("buffer after read: /%s/\n", buffer);
    newline_index = find_newline_eof(buffer);
    printf("newline index: %d\n", newline_index);
    //return (NULL);
}
if (newline_index == -1 && !buffer)
   return (NULL);
line = extract_line(&buffer, newline_index);
printf("extracted line: ///%s///\n", line);
return (NULL);
}

int main(int ac, char **av)
{
int     fd;
char    *line;

if (ac < 2)
    return (write(1, "Usage: ./a.out <filename>\n", 27));
fd = open(av[1], O_RDONLY);
if (fd <= -1) //SAFE CHECKS
    return (write(1, "error opening file\n", 20));

while ((line = get_next_line(fd)) != NULL)
{
    printf("%s\n", line);
    free (line);
}
close (fd);
return (0);
}

the realloc take three params, the original buffer, the length of this buffer and the new desired size. It copies the entire buffer to a new one if the size > length, and copies (size) characters if the size if size < length. I appreciate any suggestion on any aspect of the code, but mainly to resolve the problem

i try to identify the issue step by step with aid of printf to check the value in some contexts


Solution

  • get_next_line() returns on "" on every call so the infinite loop is in main():

        while ((line = get_next_line(fd)) != NULL) {
            printf("%s\n", line);
            free (line);
        }
    

    This is because read_newpiece() never assigns anything to *buffer with the unconditional return bytes_readed in the middle of the function.

    Here is a minimal implementation where OP can replace and/or reimplement restricted functions. It's possible to read a block at time but ideally caller would ideally retain the lookahead via argument (possible struct) instead of a static variable. Don't forget to handle short reads (read was able to read less than a block). Block size should probably be an argument. These all complicate the implementation.

    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    
    // If you use a macro instead of a function then you can
    // retain the regular realloc() interface.
    void realloc_or_die(void **ptr, size_t n) {
        void *tmp = realloc(*ptr, n);
        if(!tmp) {
            perror("realloc failed");
            free(ptr);
            exit(1);
        }
        *ptr = tmp;
    }
    
    // Return number of bytes read not counting '\0'
    size_t get_next_line(char **line, int fd) {
        size_t i = 0;
        for(;;) {
            realloc_or_die((void **) line, i+1);
            if(!read(fd, *line+i, 1)) {
                break;
            }
            i++;
            if((*line)[i] == '\n')
                break;
        }
        realloc_or_die((void **) line, i+1);
        (*line)[i] = '\0';
        return i;
    }
    
    int main(int ac, char **av) {
        if (ac < 2) {
            printf("Usage: ./a.out <filename>\n");
            return EXIT_FAILURE;
        }
        int fd = open(av[1], O_RDONLY);
        if (fd < 0) {
            printf("error opening file\n");
            return EXIT_FAILURE;
        }
        char *line = NULL;
        for(;;) {
            if(!get_next_line(&line, fd))
                break;
            printf("%s", line);
        }
        free (line);
        close (fd);
        printf("\n");
    }
    

    and example runs:

    $ ./a.out <(printf 'hello\nworld\n')
    hello
    world
    
    $ ./a.out <(printf 'hello')
    hello
    $