Skip to content

Repository files navigation

📖 get_next_line

C 42 Project Memory and File Descriptors

About

get_next_line is a low-level C function that reads a file descriptor one line at a time.

Each call returns the next available line, including the terminating newline character when one is present. The function preserves unread data between calls by using static storage.

This project was completed as part of the 42 / 1337 Common Core.

Function Prototype

char	*get_next_line(int fd);

How It Works

The function repeatedly reads data from the supplied file descriptor until:

  • a newline character is found;
  • the end of the file is reached;
  • or a read or allocation error occurs.

Unread data is preserved inside a static variable so that the following call can continue from the correct position.

File descriptor
      │
      ▼
 read() into buffer
      │
      ▼
 append buffer to stash
      │
      ├── newline found ──► extract and return one line
      │
      └── no newline ─────► continue reading
                              │
                              ▼
                       preserve remainder

Return Value

The function returns:

  • the next line read from the file descriptor;
  • the final line even when it does not end with \n;
  • NULL when no more data is available;
  • NULL when an error occurs.

The caller is responsible for freeing every returned line.

Static Storage

A static variable is used to retain unread data between function calls.

For example, when the file contains:

Hello, 42!
This is get_next_line.

The first read may retrieve more than one line. The function returns:

Hello, 42!\n

and preserves:

This is get_next_line.

for the following call.

Mandatory Part

The mandatory implementation supports reading from one file descriptor at a time.

Typical files:

get_next_line.c
get_next_line_utils.c
get_next_line.h

Bonus Part

The bonus implementation supports multiple file descriptors simultaneously.

Each descriptor keeps its own independent stash, allowing calls such as:

line1 = get_next_line(fd1);
line2 = get_next_line(fd2);
line3 = get_next_line(fd1);

Typical bonus files:

get_next_line_bonus.c
get_next_line_utils_bonus.c
get_next_line_bonus.h

BUFFER_SIZE

The amount of data read during each call to read() is controlled by the BUFFER_SIZE macro.

Example:

cc -Wall -Wextra -Werror -D BUFFER_SIZE=42 \
    main.c get_next_line.c get_next_line_utils.c

The implementation should work with different values, including:

BUFFER_SIZE=1
BUFFER_SIZE=42
BUFFER_SIZE=1024
BUFFER_SIZE=9999

A small buffer causes more calls to read(), while a larger buffer may retrieve multiple lines at once.

Usage

Example program:

#include "get_next_line.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int	main(void)
{
	int		fd;
	char	*line;

	fd = open("example.txt", O_RDONLY);
	if (fd < 0)
		return (1);
	line = get_next_line(fd);
	while (line)
	{
		printf("%s", line);
		free(line);
		line = get_next_line(fd);
	}
	close(fd);
	return (0);
}

Compile it with:

cc -Wall -Wextra -Werror \
    -D BUFFER_SIZE=42 \
    main.c \
    get_next_line.c \
    get_next_line_utils.c \
    -o gnl

Run:

./gnl

Important Edge Cases

The implementation should correctly handle:

  • empty files;
  • files containing only one character;
  • files containing only newline characters;
  • lines longer than BUFFER_SIZE;
  • files without a final newline;
  • very small buffer sizes;
  • very large buffer sizes;
  • invalid file descriptors;
  • closed file descriptors;
  • read errors;
  • repeated calls after reaching the end of the file;
  • multiple file descriptors in the bonus version.

Memory Management

All dynamically allocated memory must be released correctly.

The implementation must avoid:

  • memory leaks;
  • double frees;
  • invalid reads;
  • invalid writes;
  • losing the saved remainder;
  • returning pointers to temporary buffers.

A returned line remains owned by the caller:

line = get_next_line(fd);
if (line)
{
	printf("%s", line);
	free(line);
}

Testing

A basic test can read and display an entire file:

#include "get_next_line.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int	main(int argc, char **argv)
{
	int		fd;
	char	*line;

	if (argc != 2)
		return (1);
	fd = open(argv[1], O_RDONLY);
	if (fd < 0)
		return (1);
	while ((line = get_next_line(fd)) != NULL)
	{
		printf("%s", line);
		free(line);
	}
	close(fd);
	return (0);
}

Useful tools include:

valgrind --leak-check=full ./gnl example.txt

Recommended tests:

empty.txt
one_character.txt
one_line.txt
multiple_lines.txt
long_line.txt
no_final_newline.txt
only_newlines.txt

Implementation Overview

The implementation can be divided into three main stages:

1. Read and accumulate

Read chunks into a temporary buffer and append them to the saved stash until a newline or EOF is found.

2. Extract the line

Create a new string containing the first complete line from the stash.

3. Preserve the remainder

Remove the returned line from the stash and keep the remaining characters for the next call.

Learning Outcomes

This project provided practical experience with:

  • file descriptors;
  • the read() system call;
  • static variables;
  • dynamic memory allocation;
  • string manipulation;
  • persistent state between function calls;
  • memory ownership;
  • defensive error handling;
  • reading multiple file descriptors;
  • testing with different compilation-time configurations.

Author

Walid Krati


This repository contains my implementation of the 42 get_next_line project. It is intended as a portfolio and educational reference. Students are encouraged to understand and implement the project independently.

About

Efficient line-by-line file reader in C using static buffers and dynamic memory management, developed for the 42 get_next_line project.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages