The idea for this came from another thread in the C++ forum that wanted to duplicate the _getdelim() function that is supported by GNU compilers g++. I made one major change in that this version does not allocate new memory every time it is called, which is grossly inefficient. Instead, this function only allocates new memory if the pointer to pointer passed to _getdelim() is NULL. If not NULL then it assumes the calling program has allocated the memory. The function might expand the size of the line buffer if the number of characters read exceeds the length of the line buffer.

I also removed the goto statements that are in the original GNU function and the ancient K&R style function parameter declarations.

This code snippet uses only standard C functions -- nothing compiler-specific, so it most likely will compile with any 32-bit or 64-bit compiler.

Edited 6 Years Ago by Ancient Dragon: n/a

Comments
Nice!
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#pragma warning(disable: 4996)

/* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR
  (and null-terminate it). *LINEPTR is a pointer returned from malloc (or
   NULL), pointing to *N characters of space.  It is realloc'd as
   necessary.  Returns the number of characters read (not including the
   null terminator), or -1 on error or EOF.  */
static __inline void __set_errno(unsigned int no)
{
    errno = no;
}

size_t __getdelim (char** lineptr, size_t* n, int terminator,FILE* stream)
{
    const size_t BLOCKSIZE = 255;
    int c;
    size_t len = 0;
    size_t linesize = 0;
    int newalloc = 0;
    if( lineptr == NULL || stream == NULL || n == NULL)
    {
        _set_errno(EINVAL);
        return -1;
    }
    linesize = BLOCKSIZE;
    if( *lineptr == NULL)
    {
        // allocate new memory if required
        *lineptr = malloc(BLOCKSIZE);
        *n = BLOCKSIZE;
        newalloc = 1;
    }
    else
        linesize = *n;
    while( (c = fgetc(stream)) != EOF && c != terminator)
    {
        // make sure we have enough room for the new character
        // If not, then streatch it out a bit.
        if( (len+1) == linesize)
        {
            linesize += BLOCKSIZE;
            *n = linesize;
            *lineptr = realloc(*lineptr, linesize);
        }
        (*lineptr)[len++] = c;
    }
    if( len == 0 && c != terminator) // check for blank lines
    {
        _set_errno(EINVAL);
        if( newalloc )
        {
            free(*lineptr);
            *lineptr = NULL;
        }
        else
            (*lineptr)[0] = 0; // truncate the string
        return -1;
    }
    (*lineptr)[len] = 0; // null-terminate the string
       
    return len;
}

int main()
{
    unsigned int n = 255;
    char* lineptr = malloc(n);
    FILE* fp = fopen("test3.c", "r");
    if( fp != NULL)
    {
        while(__getdelim(&lineptr, &n, '\n', fp) != -1 )
        {
            printf("%s\n", lineptr);
        }
        free(lineptr);
        lineptr = NULL;
        fclose(fp);
    }

}

>>There are no checks against failed memory allocations.
Because its not really necessary on today's computers with lots and lots of memory, unless of course memory has been corrupt. I haven't seen malloc() return NULL since the days of 16-bit MS-DOS or Win95. If the computer doesn't have enough memory for malloc() then there are many many more problems to worry about.

>>I might suggest removing the leading underscores -- that's the implementor's namespace.

Agree -- its also a common way to indicate that a function is part of a library (*.lib or *.a)

Edited 6 Years Ago by Ancient Dragon: n/a

The article starter has earned a lot of community kudos, and such articles offer a bounty for quality replies.