HiveBrain v1.2.0
Get Started
← Back to all entries
patterncMinor

NULL-eating / elimination version of fgets() in C

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
versionnullfgetseliminationeating

Problem

I've come up with the following routine to read data from a file stream, but I don't want any NULLs in the stream to terminate the string early. This function will read in a "line", and allow it to be processed with normal C string functions. Does anyone see any inherent problems or potential bugs in this routine?

char *my_fgets(char *Buf, int iMaxCount, FILE *fp)
{
    // Handle basic failures.
    if(!Buf) return NULL;
    if(iMaxCount  Buf) && *(p-1) != 0x0a && *p != NULL) *(--p) = 0x00;

    // Free our work buffer, and return the new 'clean' data.
    free(lbuf);
    return Buf;
}


It might of importance to note that this code is called by a Kernel driver, and MUST be fast. Speed is totally of the essence here, which is why this is all in one function, and uses calls like fread() over fgets() to get a chunk of data in one shot. That might not matter in the final analysis, but I thought I would mention it.

Solution

There's absolutely no need for an intermediate (dynamic) buffer. Everything you do can be done directly in the client buffer (Buf).

I don't see a reason for fread-ing data. fgets works as good, and spares you from calling ftell and fseek.

The way you remove null chars is suboptimal. I'd keep count of null chars encountered, and shift non-null chars by that count, in a single loop. In any case, I'd factor removal code into a separate function (No Raw Loops rule strikes again).

Looking for a newline is better be done with a standard memchr().

That said, the main code condenses to

char * fgets_and_remove_nulls(char * buf, size_t count, FILE * fp)
{
    memset(buf, 0, size);
    if(fgets(buf, size, fp) == NULL) return NULL;
    char * end = memchr(buf, '\n', size);
    if (end == NULL) end = buf + size;
    remove_null_chars(buf, end);
    return buf;
}

Code Snippets

char * fgets_and_remove_nulls(char * buf, size_t count, FILE * fp)
{
    memset(buf, 0, size);
    if(fgets(buf, size, fp) == NULL) return NULL;
    char * end = memchr(buf, '\n', size);
    if (end == NULL) end = buf + size;
    remove_null_chars(buf, end);
    return buf;
}

Context

StackExchange Code Review Q#61392, answer score: 3

Revisions (0)

No revisions yet.