readfile.c from the previous page read every chunk and discarded it.
tci_getline cannot do that — it must return one line at a time and
remember exactly where it left off for the next call.
The problem
After the first call to tci_getline returns line 1, the second call
must continue reading from where the first one stopped. There are two
possible starting points:
- The buffer still contains bytes after the
'\n'— the beginning of line 2 was already read but not returned. - The buffer is empty — the next
read()starts a new chunk.
In both cases, the function needs to know the state left by the previous
call. Local variables are gone after return. Global variables work but
are visible to every translation unit — a translation unit is a single
.c source file after the preprocessor has run.
Any .c file in the project that includes libtci.h could read or
overwrite a global variable in tci_getline.c. That is not a
theoretical concern; it is exactly the kind of bug that appears when a
project grows past a single file.
Static local variables
static in C has two roles. In
c02/02
you used it on file-scope functions to control linkage — a static
function is invisible outside its translation unit. For local
variables it controls storage duration instead: a static local
persists between calls, scoped entirely to the enclosing function.
Before using it in tci_getline, see it in isolation:
#include <stdio.h>
int call_count(void)
{
static int count = 0; /* initialised once, persists forever */
count++;
return (count);
}
int main(void)
{
printf("%d\n", call_count()); /* 1 */
printf("%d\n", call_count()); /* 2 */
printf("%d\n", call_count()); /* 3 */
return (0);
}count is initialised to 0 the first time the function is entered.
Every subsequent call finds the value left by the previous one. This is
the same mechanism tci_getline will use to preserve the leftover bytes.
The leftover variable
Declare a static pointer in tci_getline.c:
static char *leftover;leftover is NULL before the first call — static pointers are
zero-initialised. After each call that returns a line, leftover holds
everything that was read but not yet returned: the bytes after the
'\n', if any, or the beginning of a partial line.
The lifecycle looks like this:
Memory ownership
leftover points to heap-allocated memory. Two rules apply:
-
When replacing it:
strjoinallocates a new string containing the oldleftoverplus the new chunk. Free the old pointer before assigning the new one — or save it first, assign the new value, then free.tmp = leftover; leftover = strjoin(leftover, buf); free(tmp); -
At EOF: either return
leftoverdirectly (the caller inherits the allocation and frees it when done), or return NULL after freeing. Setleftover = NULLin both cases so the next call starts clean.
The next page assembles these pieces into the complete tci_getline
function.