Its nice to have a standards based platform and in that vein C certainly has progressed a long way. But you still get the odd system that isn’t either set correctly up by default with my favorite extensions; namely all things GNU! Yes I admit to liking non-standard well coded extensions, in fact who would work without them these days except under a very specific set of requirements or academic interest. Things just would not get done so quickly without them.
So I got a Mac. Yep its sooo cool that when my 5 year old daughter walked in the room and saw it for the first time she exclaimed ‘Daddy! That’s sooo cool!’ Kind of an ego boost for someone who gave up on cool and cool things 20 years ago. However cool comes at a price, its got great development tools but I want cross platform development to run on my Linux boxen too. Long story short, it doesn’t have the standard GNU extensions so I had two choices, fart around with the environment or rewrite one or two functions myself.
So here’s the goods. Now because I don’t want people to just copy and paste my material without thinking about it (I don’t mind the copying I just like people to put in some effort and think) I’ve copied an earlier version of the functions which works for some situations but not for others. Its close tot he real deal but there are about 5 significant issues which would make it dangerous to use this code without doing something about them.
Here it is, have fun, have a play! (Note: the whole thing is under GPL 3, comments from GNU)
/*
ssize_t getdelim (char **lineptr, size_t *n, int delimiter, FILE *stream)
This function is like getline except that the character which tells it to stop
reading is not necessarily newline. The argument delimiter specifies the
delimiter character; getdelim keeps reading until it sees that character (or end
of file).
The text is stored in lineptr, including the delimiter character and a
terminating null. Like getline, getdelim makes lineptr bigger if it isn't big
enough.
getline is in fact implemented in terms of getdelim
*/
ssize_t getdelim( char **lineptr, size_t *n, int delimiter, FILE *stream )
{
/* setup the environment */
int c = getc( stream );
long i = 0;
char *a = NULL;
/* count the characters needed for the buffer */
while ( c != delimiter && c != EOF ) {
c = getc( stream );
i++;
}
/* test for and arrange the buffer size */
if ( i == 0 )
return -1;
if ( fseek( stream, -i, SEEK_CUR ) )
return -2;
if ( *n < i + 1 ) {
a = ( char * )realloc( *lineptr, i + 1 );
if ( a == NULL )
return -3;
*lineptr = a;
*n = i;
}
/* read the data into the buffer */
if ( fread( *lineptr, sizeof( char ), i, stream ) != i )
return -4;
( *lineptr )[i] = 0;
/* return the number of chars read */
return i;
}
/*
ssize_t getline (char **lineptr, size_t *n, FILE *stream)
This function reads an entire line from stream, storing the text (including the
newline and a terminating null character) in a buffer and storing the buffer
address in *lineptr.
Before calling getline, you should place in *lineptr the address of a buffer *n
bytes long, allocated with malloc. If this buffer is long enough to hold the
line, getline stores the line in this buffer. Otherwise, getline makes the
buffer bigger using realloc, storing the new buffer address back in *lineptr and
the increased size back in *n. See Unconstrained Allocation.
If you set *lineptr to a null pointer, and *n to zero, before the call, then
getline allocates the initial buffer for you by calling malloc.
In either case, when getline returns, *lineptr is a char * which points to the
text of the line.
When getline is successful, it returns the number of characters read (including
the newline, but not including the terminating null). This value enables you to
distinguish null characters that are part of the line from the null character
inserted as a terminator.
This function is a GNU extension, but it is the recommended way to read lines
from a stream. The alternative standard functions are unreliable.
If an error occurs or end of file is reached without any bytes read, getline
returns -1.
*/
ssize_t getline (char **lineptr, size_t *n, FILE *stream)
{
return getdelim ( lineptr, n, '\n', stream );
}