The tester exercises several inputs that expose weaknesses in a naive implementation. This page works through each one.
BUFFER_SIZE = 1
The tester recompiles with -D BUFFER_SIZE=1 and runs the same suite.
Every read() call returns exactly one byte. The loop runs once per
character.
The algorithm does not change — strjoin accumulates one-character
chunks until a '\n' is found. The only question is whether the
implementation actually compiles and works correctly when the buffer
holds a single byte.
Check tci_getline.c for any assumption that BUFFER_SIZE > 1. The
array declaration char buf[BUFFER_SIZE + 1] is correct — at
BUFFER_SIZE=1 it allocates buf[2], which holds one byte plus the
null terminator added on line buf[bytes] = '\0'.
No trailing newline at EOF
A file that ends without '\n':
first line
second line
third line without newlineAfter reading "third line without newline" into leftover, read()
returns 0. flush_leftover checks whether leftover is non-empty —
it is — and returns the string without a '\n' at the end. The caller
receives the last line without modification.
This is correct behaviour: the line exists, it just lacks a terminating newline. The caller cannot distinguish "line with newline stripped" from "line that never had one" — nor should it need to.
Empty file
open succeeds. The first read() returns 0 immediately. leftover
is the empty string "" (set by tci_strdup("") at the start of the
loop). flush_leftover tests !**leftover — true for an empty string —
frees it, sets leftover = NULL, and returns NULL.
Return value: NULL. Correct — no lines in an empty file.
Invalid file descriptor
read(-1, buf, BUFFER_SIZE) returns −1. flush_leftover is called
with leftover holding the empty string. The same path as an empty
file: return NULL.
Memory leaks
Run valgrind after reading a file to completion:
gcc -Wall -Wextra -g -std=c99 -D BUFFER_SIZE=32 -o gltest gl_test.c -L. -ltci -I.
valgrind --leak-check=full ./gltest test.txtWhere gl_test.c reads every line until NULL:
#include "libtci.h"
#include <fcntl.h> /* open, O_RDONLY */
#include <stdio.h> /* printf */
#include <stdlib.h> /* free */
int main(void)
{
int fd;
char *line;
fd = open("test.txt", O_RDONLY);
while ((line = tci_getline(fd)) != NULL) {
printf("%s", line);
free(line); /* caller owns every string tci_getline returns */
}
close(fd);
return (0);
}valgrind must report zero bytes lost. The two memory paths to verify:
- After
strjoin, the oldleftoveris freed viatmp. No leak from accumulation. - In
flush_leftover, whenleftoveris empty, it is freed before returning NULL. When non-empty, it is returned — the caller'sfree(line)handles it.
Run the full suite
make re
bash test.shAll tci_getline tests — including BUFFER_SIZE=1, no trailing newline,
and empty file — should now pass. The next page adds support for reading
multiple file descriptors simultaneously.