This project was developed as part of the 42 School curriculum by hrasamoe.
get_next_line is a project from the 42 Common Core.
Its objective is to implement a function that reads from a file (or any input stream via a file descriptor) line by line, without knowing the line length in advance and without using the standard getline() function.
The function must:
- Return a complete line including the
\ncharacter (except for the last line if the file does not end with\n). - Work correctly with any
BUFFER_SIZEvalue (1, 42, 9999, 1000000, etc.). - Return
NULLcleanly on end-of-file (EOF) or error. - Avoid memory leaks.
- Handle multiple file descriptors independently.
This project strengthens understanding of:
- File I/O (
open,read,close) - Static variables
- Buffer management
- Dynamic memory allocation
- Edge case handling
git clone <repo_url> get_next_line
cd get_next_line#include "get_next_line.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void test_file(const char *filename, int fd_to_use)
{
int fd;
char *line;
int line_nb;
printf("\n=== Test file: %s ===\n", filename);
if (fd_to_use >= 0)
fd = fd_to_use;
else
{
fd = open(filename, O_RDONLY);
if (fd < 0)
{
perror("open");
return;
}
}
line_nb = 0;
while (1)
{
line = get_next_line(fd);
if (!line)
break;
line_nb++;
printf("Line %3d |%s| (len: %zu)\n", line_nb, line, ft_strlen(line));
free(line);
}
printf("→ End after %d lines\n", line_nb);
if (fd_to_use < 0)
close(fd);
}
int main(void)
{
printf("BUFFER_SIZE = %d\n\n", BUFFER_SIZE);
test_file("files/test.txt", -1);
printf("\n=== Test STDIN (Ctrl+D to finish) ===\n");
test_file("STDIN", 0);
return 0;
}# Default BUFFER_SIZE
cc -Wall -Wextra -Werror get_next_line.c get_next_line_utils.c -o gnl_test
# Custom BUFFER_SIZE
cc -Wall -Wextra -Werror -D BUFFER_SIZE=1 get_next_line.c get_next_line_utils.c -o gnl_test
cc -Wall -Wextra -Werror -D BUFFER_SIZE=42 get_next_line.c get_next_line_utils.c -o gnl_test
cc -Wall -Wextra -Werror -D BUFFER_SIZE=9999 get_next_line.c get_next_line_utils.c -o gnl_test./gnl_test- Gemini
Used to:
- Better understand how
BUFFER_SIZEimpacts reading behavior - Clarify temporary memory storage strategy
- Improve newline (
\n) and EOF handling - Structure and refine the README documentation
read() does not read a line. It reads a fixed number of bytes.
Problems:
- A line may be larger than
BUFFER_SIZE. - A line may be split across multiple
read()calls. read()does not preserve state between calls.- Multiple file descriptors may be used simultaneously.
To solve this, the algorithm:
- Accumulates partial data.
- Stores unused data between function calls.
- Returns exactly one complete line per call.
static char *remain[FD_MAX];Each file descriptor has its own persistent buffer.
Purpose:
- Prevent interference between multiple FDs.
- Preserve leftover data for the next call.
Function: read_to_static()
Steps:
- Allocate a temporary buffer of size
BUFFER_SIZE. - Call
read(fd, buffer, BUFFER_SIZE). - Append the buffer to
remain[fd]. - Repeat until:
- A
\nis found, or read()returns 0 (EOF).
- A
Core loop:
while (!ft_strchr(*remain, '\n') && bytes_read > 0)Purpose:
- Stop reading once a complete line exists.
- Minimize unnecessary system calls.
Function: extract_line()
Steps:
- Scan
remainuntil\nor\0. - Copy that portion using
ft_substr. - Return the newly allocated string.
Result:
- The returned line is independent.
- The caller is responsible for freeing it.
Function: update_remain()
Example:
Before extraction:
"Hello\nWorld\n"
After extracting "Hello\n":
Remaining buffer:
"World\n"
If nothing remains:
- Free the memory.
- Set pointer to
NULL.
Purpose:
- Avoid re-reading the same data.
- Maintain correct state across calls.
Each call to get_next_line(fd):
- Validates the file descriptor.
- Reads and accumulates data until a newline is found.
- Extracts one line.
- Updates the remaining buffer.
- Returns the line.
This implementation ensures:
- Correct line boundaries.
- No data loss.
- Support for multiple file descriptors.
- Safe and controlled memory management.