A C function that reads one line at a time from any file descriptor. Built without the standard library, using only a static variable to maintain state between calls.
get_next_line implements a line-by-line reader for file descriptors in C. Each successive call returns the next line from the given fd, including the terminating \n character (except at end of file). It works on regular files, standard input, or any valid file descriptor.
The function is designed to be minimal and efficient: it reads only as much data as necessary on each call, never loading the entire file into memory.
- No standard library — only
read,malloc, andfreeare used - No global variables — state is maintained through a single
staticvariable - No
lseek()— fully sequential, forward-only reading - Configurable buffer size — set at compile time via
-D BUFFER_SIZE=n, works correctly with any value from1to10000000+ - Bonus: multiple fd support — simultaneous reading from several file descriptors without losing state on any of them
char *get_next_line(int fd);Returns the next line read from fd, or NULL if there is nothing left to read or an error occurred.
# With a custom buffer size
cc -Wall -Wextra -Werror -D BUFFER_SIZE=42 get_next_line.c get_next_line_utils.c
# With default buffer size (defined in the header)
cc -Wall -Wextra -Werror get_next_line.c get_next_line_utils.c
# Bonus — multiple fd support
cc -Wall -Wextra -Werror -D BUFFER_SIZE=42 get_next_line_bonus.c get_next_line_utils_bonus.c#include "get_next_line.h"
#include <fcntl.h>
#include <stdio.h>
int main(void)
{
int fd;
char *line;
fd = open("file.txt", O_RDONLY);
while ((line = get_next_line(fd)) != NULL)
{
printf("%s", line);
free(line);
}
close(fd);
return 0;
}get_next_line/
├── get_next_line.h # Header — prototype + BUFFER_SIZE fallback
├── get_next_line.c # Core function
├── get_next_line_utils.c # Helper functions (strjoin, strchr, strlen...)
├── get_next_line_bonus.h # Bonus header
├── get_next_line_bonus.c # Bonus — multiple fd support
├── get_next_line_utils_bonus.c # Bonus helpers
└── README.md
The implementation relies on a static buffer (leftover) to persist unprocessed data between calls. Here is how a single call unfolds:
-
Check the leftover : if a
\nis already present in the data saved from the previous call, extract and return the line immediately without callingread()again. -
Read in chunks :
read()fills a temporary buffer ofBUFFER_SIZEbytes. Each chunk is appended to the leftover via a customstrjoin. This continues until a\nis found or EOF is reached. -
Extract the line : once a newline is found (or EOF), the portion up to and including
\nis returned as the line. The remainder is saved back into the static variable for the next call. -
Cleanup on EOF : when
read()returns 0, any remaining content in the leftover is returned as the final line (without\n), then the static is freed and set to NULL.
This approach avoids reading the whole file upfront and works correctly regardless of BUFFER_SIZE, even with pathological values like 1 or 10000000.
Bonus — multiple file descriptors: instead of a single char * static, the bonus uses a char * array indexed by fd (static char *leftover[OPEN_MAX]). Each fd gets its own independent leftover buffer, allowing interleaved reads across multiple descriptors without interference.
| Rule | Description |
|---|---|
make / make all |
Compile mandatory part |
make bonus |
Compile bonus (multiple fd support) |
make clean |
Remove object files |
make fclean |
Remove object files and binary |
make re |
Full rebuild |
This project was created as part of the 42 curriculum.