/* 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 <stdlib.h>	  /* 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;
	}
}


syntax highlighted by Code2HTML, v. 0.9.1