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
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
$