thecodingidiot.com

The ReaderPersistent State

Persistent State

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:

  1. The buffer still contains bytes after the '\n' — the beginning of line 2 was already read but not returned.
  2. 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:

  1. When replacing it: strjoin allocates a new string containing the old leftover plus 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);
  2. At EOF: either return leftover directly (the caller inherits the allocation and frees it when done), or return NULL after freeing. Set leftover = NULL in both cases so the next call starts clean.

The next page assembles these pieces into the complete tci_getline function.