Files
libucore/src/read_file.c
T
2014-09-12 01:11:40 +02:00

96 lines
2.0 KiB
C

#include <stdlib.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "ucore/read_file.h"
#define CHUNK_SZ 1024
char *
uc_read_fd(int fd, size_t *length, size_t max)
{
char *s = NULL;
char *p;
struct stat statb;
size_t alloc_sz;
*length = 0;
if (fstat(fd, &statb) == -1) {
goto out_close;
}
if ((statb.st_mode & S_IFMT) == S_IFREG) {
if (statb.st_size > (off_t)max) {
errno = EMSGSIZE;
goto out_close;
}
if (statb.st_size > SSIZE_MAX) {
//otherwise we cannot detect the return value of read()
alloc_sz = SSIZE_MAX;
} else {
alloc_sz = statb.st_size;
}
} else {
alloc_sz = CHUNK_SZ;
}
s = malloc(alloc_sz + 1); //+1 for nul terminator
if (s == NULL) {
goto out_close;
}
p = s;
for (;;) {
ssize_t rc = read(fd, p, alloc_sz - (p - s));
if (rc == 0) {
*p = 0; // nul terminate. all malloc calls adds room for this byte
goto out_close;
} else if (rc == -1) {
goto out_err_free;
} else if (*length + rc > max) {
errno = EMSGSIZE;
goto out_err_free;
}
if (p + rc == s + alloc_sz) { //reached end of allocated memory, grow it.
char *tmp = realloc(s, alloc_sz + CHUNK_SZ + 1);
if (tmp == NULL) {
goto out_err_free;
}
s = tmp;
p = s + alloc_sz;
alloc_sz += CHUNK_SZ;
} else {
p += rc;
}
*length += rc;
}
out_err_free:
free(s);
s = NULL;
out_close:
close(fd);
return s;
}
char *
uc_read_file(const char *f, size_t *length, size_t max)
{
int fd;
if ((fd = open(f, O_RDONLY)) == -1) {
*length = 0;
return NULL;
}
return uc_read_fd(fd, length, max);
}