R
rihad
Hi, here's an implementation of file slurping in C, like @lines = <FILE> in perl
or $lines = file("/path/to/file") in php. There's also a main() testing it out.
All suggestions are very welcome! Thank you.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TEST_MAIN
#define CHUNK 4096
#define SLURP_TEXT 1
#define SLURP_BINARY 2
/* returns NULL terminated array of strings from text file, and array's size */
char **fslurp(const char *fname, int *);
/* copies file to memory, returns size in memory and pointer to beginning */
unsigned char *fslurpb(const char *fname, size_t *size);
/* frees whatever fslurp() returned */
void ffree(char **list);
/* frees whatever fslurpb() returned */
void ffreeb(unsigned char *mem);
static void *slurp(const char *, int, size_t *);
char **fslurp(const char *fname, int *numread)
{
char **retval = 0;
char *mem = 0, *p;
size_t offt = 0;
size_t num_lines = 0, softlim = 1;
mem = slurp(fname, SLURP_TEXT, &offt);
/* got the file, now set up the pointers */
for (p = mem; p < mem + offt; ) {
char *q;
if (num_lines + 1 == softlim) {
char **tmp = realloc(retval, (softlim += 5) * sizeof *retval);
if (!tmp)
goto nomem;
retval = tmp;
}
retval[num_lines++] = p;
if (!(q = memchr(p, '\n', mem + offt - p - 1)))
q = mem + offt;
*q = 0;
p = q + 1;
}
retval[num_lines] = 0; /* we got room for an extra 0 */
goto exit;
nomem:
free(retval);
free(mem);
retval = 0;
exit:
if (numread)
*numread = num_lines;
return retval;
}
unsigned char *fslurpb(const char *fname, size_t *size)
{
return slurp(fname, SLURP_BINARY, size);
}
void ffree(char **list)
{
if (list) {
free(list[0]);
free(list);
}
}
void ffreeb(unsigned char *mem)
{
free(mem);
}
static void *slurp(const char *fname, int mode, size_t *numread)
{
unsigned char *mem = 0;
size_t offt = 0, size = 0;
const char *realmode;
FILE *fp;
switch (mode) {
case SLURP_TEXT:
realmode = "r";
break;
case SLURP_BINARY:
default:
realmode = "rb";
break;
}
if ((fp = fopen(fname, realmode)) == 0)
goto exit;
/* slurp the file in */
for (; {
void *p;
size_t read;
if (!(p = realloc(mem, size += CHUNK)))
goto nomem;
mem = p;
read = fread(mem + offt, 1, CHUNK, fp);
offt += read;
if (read < CHUNK)
goto exit;
}
nomem:
free(mem);
mem = 0;
exit:
if (fp)
fclose(fp);
*numread = offt;
return mem;
}
#ifdef TEST_MAIN
/* really lame */
static int is_unix(void)
{
int retval = 0;
FILE *fp = fopen("/bin/sh", "r");
if (fp) {
retval = 1;
fclose(fp);
} else {
retval = (getenv("LOGNAME") != 0);
}
return retval;
}
static void list_dump(char **list)
{
if (!list) {
fputs("(null)", stderr);
} else {
int n;
for (n = 1 ; *list; list++, n++) {
fprintf(stderr, "%3d) %s\n", n, *list);
}
}
}
int main(int argc, char **argv)
{
/* test text slurp */
{
char **lines = fslurp(is_unix() ? "/etc/services" : "c:/boot.ini", 0);
list_dump(lines);
ffree(lines);
}
/* test binary slurp */
{
const char *fname = argv[0];
size_t num;
void *mem = fslurpb(fname, &num);
if (!mem) {
char buf[FILENAME_MAX + 32];
sprintf(buf, "%s could not be read", fname);
perror(buf);
} else {
fprintf(stderr, "Read %s into memory, size: %lu\n", fname,
(unsigned long) num);
ffreeb(mem);
}
}
getchar();
return 0;
}
#endif /* TEST_MAIN */
or $lines = file("/path/to/file") in php. There's also a main() testing it out.
All suggestions are very welcome! Thank you.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TEST_MAIN
#define CHUNK 4096
#define SLURP_TEXT 1
#define SLURP_BINARY 2
/* returns NULL terminated array of strings from text file, and array's size */
char **fslurp(const char *fname, int *);
/* copies file to memory, returns size in memory and pointer to beginning */
unsigned char *fslurpb(const char *fname, size_t *size);
/* frees whatever fslurp() returned */
void ffree(char **list);
/* frees whatever fslurpb() returned */
void ffreeb(unsigned char *mem);
static void *slurp(const char *, int, size_t *);
char **fslurp(const char *fname, int *numread)
{
char **retval = 0;
char *mem = 0, *p;
size_t offt = 0;
size_t num_lines = 0, softlim = 1;
mem = slurp(fname, SLURP_TEXT, &offt);
/* got the file, now set up the pointers */
for (p = mem; p < mem + offt; ) {
char *q;
if (num_lines + 1 == softlim) {
char **tmp = realloc(retval, (softlim += 5) * sizeof *retval);
if (!tmp)
goto nomem;
retval = tmp;
}
retval[num_lines++] = p;
if (!(q = memchr(p, '\n', mem + offt - p - 1)))
q = mem + offt;
*q = 0;
p = q + 1;
}
retval[num_lines] = 0; /* we got room for an extra 0 */
goto exit;
nomem:
free(retval);
free(mem);
retval = 0;
exit:
if (numread)
*numread = num_lines;
return retval;
}
unsigned char *fslurpb(const char *fname, size_t *size)
{
return slurp(fname, SLURP_BINARY, size);
}
void ffree(char **list)
{
if (list) {
free(list[0]);
free(list);
}
}
void ffreeb(unsigned char *mem)
{
free(mem);
}
static void *slurp(const char *fname, int mode, size_t *numread)
{
unsigned char *mem = 0;
size_t offt = 0, size = 0;
const char *realmode;
FILE *fp;
switch (mode) {
case SLURP_TEXT:
realmode = "r";
break;
case SLURP_BINARY:
default:
realmode = "rb";
break;
}
if ((fp = fopen(fname, realmode)) == 0)
goto exit;
/* slurp the file in */
for (; {
void *p;
size_t read;
if (!(p = realloc(mem, size += CHUNK)))
goto nomem;
mem = p;
read = fread(mem + offt, 1, CHUNK, fp);
offt += read;
if (read < CHUNK)
goto exit;
}
nomem:
free(mem);
mem = 0;
exit:
if (fp)
fclose(fp);
*numread = offt;
return mem;
}
#ifdef TEST_MAIN
/* really lame */
static int is_unix(void)
{
int retval = 0;
FILE *fp = fopen("/bin/sh", "r");
if (fp) {
retval = 1;
fclose(fp);
} else {
retval = (getenv("LOGNAME") != 0);
}
return retval;
}
static void list_dump(char **list)
{
if (!list) {
fputs("(null)", stderr);
} else {
int n;
for (n = 1 ; *list; list++, n++) {
fprintf(stderr, "%3d) %s\n", n, *list);
}
}
}
int main(int argc, char **argv)
{
/* test text slurp */
{
char **lines = fslurp(is_unix() ? "/etc/services" : "c:/boot.ini", 0);
list_dump(lines);
ffree(lines);
}
/* test binary slurp */
{
const char *fname = argv[0];
size_t num;
void *mem = fslurpb(fname, &num);
if (!mem) {
char buf[FILENAME_MAX + 32];
sprintf(buf, "%s could not be read", fname);
perror(buf);
} else {
fprintf(stderr, "Read %s into memory, size: %lu\n", fname,
(unsigned long) num);
ffreeb(mem);
}
}
getchar();
return 0;
}
#endif /* TEST_MAIN */