/* vi: set tabstop=4 shiftwidth=4 */ /* * $Id: fgetline.c,v 2.2 2005/08/07 17:20:16 schweikh Exp $ */ /*@ignore@*/ #ifndef _POSIX_SOURCE # define _POSIX_SOURCE 1 #endif /*@end@*/ /*@+charintliteral@*/ #include /* malloc */ #include "error.h" #include "fgetline.h" /* * fgetline: reads a stream up to the next eol character and * returns the pointer to that line. The eol char is replaced * by a \0. Returns NULL on EOF or error. To distinguish * between EOF and error use feof(fp) which is nonzero * on EOF. Memory is allocated using malloc(). The caller * is responsible for freeing. */ /* * M_START is the initial size of the allocated memory. It must be * even and is doubled every time the line fills up the memory. */ #define M_START 16 #if M_START % 2 == 1 #error M_START must be even #endif char * fgetline (FILE *fp, int eol) { char *mem, *write_to; /* mem is const, write_to changes */ int input; size_t mem_size = M_START; input = fgetc (fp); if (input == EOF) { return NULL; /* feof(fp) now is nonzero */ } check (ungetc (input, fp) != 0); /* okay, there is at least one character to read */ mem = NULL; while (mem = xrealloc (mem, mem_size), 1) { /* * On the first iteration, make write_to = mem, otherwise * let write_to point to the newly allocated memory. */ write_to = (mem_size == M_START ? mem : mem + mem_size/2); /*@i@*/ while (write_to != mem + mem_size) { /* while we have space to write */ input = fgetc (fp); if (input == eol || input == EOF) { *write_to = '\0'; return mem; } *write_to++ = (char) input; } /* * We have written mem+mem_size-write_to chars into mem[], it's full. * But there might be more to read or an EOF coming. Even if EOF is * next, we have to realloc to have space for the terminating \0. */ mem_size *= 2; } }