libgit2/src/fileops.c
Ramsay Jones fc3c3a2083 Avoid using a gcc compiler language extension
In particular, using pointer arithmetic on void pointers,
despite being quite useful, is not legal in standard C.
Avoiding non-standard C constructs will help in porting
the library to other compilers/platforms.

Signed-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
2009-03-17 18:58:30 -07:00

268 lines
4.5 KiB
C

#include "common.h"
#include "fileops.h"
#include <sys/mman.h>
int gitfo_open(const char *path, int flags)
{
int fd = open(path, flags);
return fd >= 0 ? fd : git_os_error();
}
int gitfo_creat(const char *path, int mode)
{
int fd = creat(path, mode);
return fd >= 0 ? fd : git_os_error();
}
int gitfo_read(git_file fd, void *buf, size_t cnt)
{
char *b = buf;
while (cnt) {
ssize_t r = read(fd, b, cnt);
if (r < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return git_os_error();
}
if (!r) {
errno = EPIPE;
return git_os_error();
}
cnt -= r;
b += r;
}
return GIT_SUCCESS;
}
int gitfo_write(git_file fd, void *buf, size_t cnt)
{
char *b = buf;
while (cnt) {
ssize_t r = write(fd, b, cnt);
if (r < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return git_os_error();
}
if (!r) {
errno = EPIPE;
return git_os_error();
}
cnt -= r;
b += r;
}
return GIT_SUCCESS;
}
int gitfo_exists(const char *path)
{
struct stat sb;
return stat(path, &sb);
}
off_t gitfo_size(git_file fd)
{
struct stat sb;
if (fstat(fd, &sb))
return git_os_error();
return sb.st_size;
}
int gitfo_read_file(gitfo_buf *obj, const char *path)
{
git_file fd;
off_t len;
unsigned char *buff;
assert(obj && path && *path);
if ((fd = gitfo_open(path, O_RDONLY)) < 0)
return GIT_ERROR;
if (((len = gitfo_size(fd)) < 0)
|| ((buff = git__malloc(len + 1)) == NULL)) {
gitfo_close(fd);
return GIT_ERROR;
}
if (gitfo_read(fd, buff, len) < 0) {
gitfo_close(fd);
free(buff);
return GIT_ERROR;
}
buff[len] = '\0';
gitfo_close(fd);
obj->data = buff;
obj->len = len;
return GIT_SUCCESS;
}
void gitfo_free_buf(gitfo_buf *obj)
{
assert(obj);
free(obj->data);
obj->data = NULL;
}
int gitfo_map_ro(gitfo_map *out, git_file fd, off_t begin, size_t len)
{
out->data = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, begin);
if (out->data == (void*)-1)
return git_os_error();
out->len = len;
return GIT_SUCCESS;
}
void gitfo_free_map(gitfo_map *out)
{
munmap(out->data, out->len);
}
/* cached diskio */
struct gitfo_cache {
git_file fd;
unsigned int cache_size, pos;
unsigned char *cache;
};
gitfo_cache *gitfo_enable_caching(git_file fd, size_t cache_size)
{
gitfo_cache *ioc;
ioc = git__malloc(sizeof(*ioc));
if (!ioc)
return NULL;
ioc->pos = 0;
ioc->cache_size = cache_size;
ioc->cache = git__malloc(cache_size);
if (!ioc->cache) {
free(ioc);
return NULL;
}
return ioc;
}
GIT_INLINE(void) gitfo_add_to_cache(gitfo_cache *ioc, void *buf, size_t len)
{
memcpy(ioc->cache + ioc->pos, buf, len);
ioc->pos += len;
}
int gitfo_flush_cached(gitfo_cache *ioc)
{
int result = GIT_SUCCESS;
if (ioc->pos) {
result = gitfo_write(ioc->fd, ioc->cache, ioc->pos);
ioc->pos = 0;
}
return result;
}
int gitfo_write_cached(gitfo_cache *ioc, void *buff, size_t len)
{
unsigned char *buf = buff;
for (;;) {
size_t space_left = ioc->cache_size - ioc->pos;
/* cache if it's small */
if (space_left > len) {
gitfo_add_to_cache(ioc, buf, len);
return GIT_SUCCESS;
}
/* flush the cache if it doesn't fit */
if (ioc->pos) {
int rc;
gitfo_add_to_cache(ioc, buf, space_left);
rc = gitfo_flush_cached(ioc);
if (rc < 0)
return rc;
len -= space_left;
buf += space_left;
}
/* write too-large chunks immediately */
if (len > ioc->cache_size)
return gitfo_write(ioc->fd, buf, len);
}
return GIT_SUCCESS;
}
int gitfo_close_cached(gitfo_cache *ioc)
{
git_file fd;
if (gitfo_flush_cached(ioc) < 0)
return GIT_ERROR;
fd = ioc->fd;
free(ioc->cache);
free(ioc);
return gitfo_close(fd);
}
int gitfo_dirent(
char *path,
size_t path_sz,
int (*fn)(void *, char *),
void *arg)
{
size_t wd_len = strlen(path);
DIR *dir;
struct dirent *de;
if (!wd_len || path_sz < wd_len + 2)
return GIT_ERROR;
while (path[wd_len - 1] == '/')
wd_len--;
path[wd_len++] = '/';
path[wd_len] = '\0';
dir = opendir(path);
if (!dir)
return git_os_error();
while ((de = readdir(dir))) {
size_t de_len;
int result;
/* always skip '.' and '..' */
if (de->d_name[0] == '.') {
if (de->d_name[1] == '\0')
continue;
if (de->d_name[1] == '.' && de->d_name[2] == '\0')
continue;
}
de_len = strlen(de->d_name);
if (path_sz < wd_len + de_len + 1) {
closedir(dir);
return GIT_ERROR;
}
strcpy(path + wd_len, de->d_name);
result = fn(arg, path);
if (result < 0) {
closedir(dir);
return result;
}
if (result > 0) {
closedir(dir);
return result;
}
}
closedir(dir);
return GIT_SUCCESS;
}