Provide a structure that knows how to produce the next line depending on its contents. For example (untested):
typedef struct {
FILE *fp;
const char *str;
} Source;
Source *source_new_from_file(const char *filename)
{
Source *ret = malloc(sizeof(Source));
if (!ret)
return NULL;
ret->fp = fopen(filename, "rb");
if (!ret->fp)
return NULL;
ret->str = NULL;
return ret;
}
Source *source_new_from_str(const char *str)
{
Source *ret = malloc(sizeof(Source));
if (!ret)
return NULL;
ret->fp = NULL;
ret->str = str;
return ret;
}
bool source_read_line(Source *s, char *dest, size_t destsize)
{
if (s->fp)
return fgets(dest, destsize, s->fp) != NULL;
const char *newline = strchr(s->str, '\n');
if (!newline)
// handle trailing line without newline, like fgets does
newline = s->str + strlen(s->str);
if (newline == s->str)
return false; // no more data
size_t linelen = newline - s->str;
if (linelen > destsize - 1)
linelen = destsize - 1;
memcpy(dest, s->str, linelen);
dest[linelen + 1] = '\0';
s->str = newline;
return true;
}
// ...
void* producer(void* param)
{
Source *source = param;
char line[256];
while (source_read_line(source, line, sizeof line)) {
processLine(line);
}
return NULL;
}
With this code you can pass any kind of source to the producer. Even if a new kind of source appears (say, the resultset of a database query, or an XML document where "lines" are encoded as XML elements), you will be able to implement the new functionality only by extending the source_read_line, and without changing any of the code in producer.
parampoint to a structure or union, in which you explicitly tell it what to use.mmap(), you simply pass achar *for both eventualities. Even the code is the same.void *. Also, memory mapping is unportable in C and can fail if the file is not a regular file (e.g. if it's a pipe).