Subject: Slightly modifying fgetln(3) semantics
To: None <tech-userlevel@NetBSD.ORG>
From: Luke Mewburn <lukem@NetBSD.ORG>
List: tech-userlevel
Date: 09/18/1997 22:21:59
Greetings.

fgetln(3) reads a line from stdin (upto newline or EOF), and returns a
pointer to this string, along with its length.  The string is not NUL
terminated.

The most common use of fgetln(3) is something like:

	FILE *fp;
	char *lp;
	int len;

	while ((lp = fgetln(fp, &len)) != NULL) {
                lp[--len] = '\0';	/* statement 'X' */
		/* use lp */
	}

Many programs in the tree do effectively this; a NUL terminated
string is more easily manipulated by the various stdio functions.
However, in the case where the end of file isn't preceeded with a
newline, statement 'X' will incorrectly erase the last character
of the last line.

Some programs attempt to work around this by checking if lp[len-1] == '\n'
and take a variety of action, including: a) copying lp to another buffer
and NUL terminating it, or b) throwing the line away.

I believe that a better approach is to modify fgetln(3) so that the
string *is* NUL terminated, even if the length field isn't modified to
take this into account. Examination of the source code for fgetln(3)
[ src/lib/libc/stdio/fgetln.c ] reveals that there are a few lines
wrapped with
	#ifdef notyet
		/* ... */
	#endif
that appear to do exactly this.

I'm not sure why Chris Torek (the author of our stdio, who I've CC-ed
in this email) didn't include this code; IMHO it would make using the
strings returned from fgetln(3) just that bit easier, even in the case
where the line doesn't have the trailing \n that can be conveniently
replaced with a NUL.

Are they any comments or objections to enabling this functionality?

Luke.

PS: there's about 7 programs (from a list I made a couple of months
ago) that incorrectly assume each line at lp[len-1]  == '\n'...

--
Luke Mewburn <lukem@netbsd.org>
Developer, NetBSD Foundation.