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 < 1) return NULL;    if(!fp) return NULL;    // Create a buffer on the heap for our data to be read in    char *lbuf = (char*)malloc(iMaxCount);    char *p = lbuf;    // Zero out the buffer, no stray chars please.    memset(Buf, 0x00, iMaxCount);    // Where are we at in the file?    int b = ftell(fp);    // Read in the buffer, up to iMaxCount-1.  Leave one char for a final NULL.    size_t read = fread((void*)&lbuf[0], sizeof(char), iMaxCount-1, fp);    lbuf[iMaxCount-1] = NULL;    // Failed.    if(!read) {        free(lbuf);        return NULL;    }    // Walk the buffer, looking for a newline char.  Once we find it, we know we are at the end of the line,     // but we don't want to read past the number of bytes we originally read, in case we did not hit a newline.    size_t left = read;    while((left) && *(p++) != '\n') left--;    // Copy the data we read to the return Buffer, up to the newline char.    memcpy(Buf, lbuf, p - lbuf);    // Set the file pointer to the newline char + 1    fseek(fp, b+(p - lbuf), SEEK_SET);    // Setup how many bytes we have left in the buffer to look at.    left = (p - lbuf);    p = Buf;        while(left)    {        // Walk the buffer, looking for NULL chars.        if(*(p++) == NULL) {            char *p1;            // We hit a NULL, so shift the buffer left, eat the NULL, and continue our search.            for(p1 = p-1;p1 < (Buf + (iMaxCount-1));p1++) p1[0] = p1[1];            // We ate one char, so we need to step back one byte, and continue our search.  This handles a case where two NULLs may be back to back.            p--;        }        left--;    }     // If our last char is not a NULL, we need to set it to NULL to properly terminate the string.    if((p > 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.
NULL-eating / elimination version of fgets() in C
algorithm;c;io;null
In this answer I'm aggregating and elaborating on the answers already given, and then I provide a heavily-commented alternate version of your function.NULL vs. '\0'You shouldn't be using NULL the way that you are. It is true that NULL is almost always defined as#define NULL 0and your code above will work. However, the purpose of NULL in C code is to represent an invalid pointer, and so you should only use it in that case. The ASCII byte 0x00 is sometimes referred to as NUL, but this is only coincidentally similar to NULL in C.The correct notation for a 0x00 byte in C (especially in the context of C-strings) is '\0'. Just like NULL, this is a shorthand for 0, but it conveys your intention much better (and saves you from running into compilation errors on the odd system where NULL is (void *)0).On Rewriting Standard FunctionsIn your code, you provided (effectively) your own implementations of the standard functions fgets() and memchr(). You should always use the standard functions, unless you are absolutely sure that your version will perform better. Standard library routines have almost always been optimized like crazy, and have probably considered/discovered edge-cases that you might not. They've also been heavily tested in production.Cases where you might legitimately reimplement standard routines includeTheir solution is too general, and your specific use-case allows for optimizations that the general case doesn't.They don't exist, you're in the kernel (which it sounds like you might be?), or some other scenario.You have constraints that the standard implementations don't conform to.An Alternate ImplementationSo here's an implementation which uses all of the above advice and provides a better algorithm for stripping the '\0's from your buffer.#include <stdio.h>  // fgets()#include <string.h> // memchr()// my_fgets() behaves exactly like the standard fgets(), except that it removes// any '\0' bytes from the data it reads, and then adds a single '\0' byte to the// end.char *my_fgets(char *buf, size_t iMaxCount, FILE *fp) {  // Delegate the actual reading to fgets(). The people who implemented it are  // almost definitely doing it in the fastest way possible.  if (fgets(buf, iMaxCount - 1, fp) == NULL) {    return NULL;  }  // Use the standard memchr() function to find the first occurrance of a byte   // in a buffer. The standard function is probably optimized like crazy.  char *end = memchr(buf, '\n', iMaxCount - 1);   // We want end to point just past the end of the data read by fgets(), so that  // we can use it as an upper bound in the for-loop below.  if (end == NULL) {    // If fgets() didn't find a newline, then end is NULL, and it read in a    // whole (iMaxCount - 1) bytes of data. So end needs to point to the very    // last byte of buf.    end = buf + (iMaxCount - 1);   } else {    // Otherwise we found a '\n', that was the last byte read by fgets(), and    // memchr() already found it for us, and returned a pointer to it which is    // currently stored in end. We just have to advance end by one (so that    // we're pointing just past the data we read).    end++;  }  // In the following loop, i_ptr is used to read each of the input bytes, and  // o_ptr is used to write out only the ones which are not '\0'.  char *i_ptr, *o_ptr;  // Note that we increment i_ptr every time, but we only increment o_ptr after  // we find a valid non-'\0' character.  for (i_ptr = o_ptr = buf; i_ptr < end; i_ptr++) {    if (*i_ptr != '\0') {      // *i_ptr isn't a '\0', so we keep it in the array      *o_ptr++ = *i_ptr;    }  }  // We've advanced o_ptr past all of the non-'\0' elements of the buffer, so  // now it points exactly past the end, right where we want our '\0' byte.  *o_ptr = '\0';  // fgets() is specified as returning the input buffer, so we do that too.  return buf;}