#define _FILE_OFFSET_BITS 64 #include "../CgeFile.h" #include #include #include #include #include #include #include static const int64_t off_t_max = (sizeof(off_t) == 8) ? INT64_MAX : (sizeof(off_t) == 4) ? INT32_MAX : 0; static const int64_t off_t_min = (sizeof(off_t) == 8) ? INT64_MIN : (sizeof(off_t) == 4) ? INT32_MIN : 0; struct CgeFile { int flags; int handle; int lastError; }; static int errnoToFileCode(int err) { switch (err) { case 0: return CGE_FILE_EOK; case ENOMEM: return CGE_FILE_EOOM; case ENOSPC: return CGE_FILE_ENOSPC; case EINVAL: return CGE_FILE_EINVAL; case ENOENT: return CGE_FILE_ENOENT; case EACCES: case EPERM: return CGE_FILE_EACCES; case EROFS: return CGE_FILE_EROFS; case EEXIST: return CGE_FILE_EEXIST; case EISDIR: return CGE_FILE_EISDIR; case EMFILE: case ENFILE: return CGE_FILE_EMFILE; case ENAMETOOLONG: return CGE_FILE_ENAMETOOLONG; default: return CGE_FILE_EUNKNOWN; } } static int fileOpenFlags(int mode) { int flags = 0; if ((mode & CGE_FILE_READWRITE) == CGE_FILE_READWRITE) flags |= O_RDWR; else if (mode & CGE_FILE_WRITE) flags |= O_WRONLY; else if (mode & CGE_FILE_READ) flags |= O_RDONLY; else return -1; if (!(mode & CGE_FILE_EXIST)) { flags |= O_CREAT; if (mode & CGE_FILE_CREATE) flags |= O_EXCL; } if (mode & CGE_FILE_APPEND) flags |= O_APPEND; if (mode & CGE_FILE_TRUNCATE) flags |= O_TRUNC; return flags; } static int fileInit(CgeFile *file, const char *path, int mode) { static const mode_t openMode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); int flags; if (!path) return CGE_FILE_EINVAL; flags = fileOpenFlags(mode); if (flags == -1) return CGE_FILE_EINVAL; file->flags = 0; file->handle = open(path, flags, openMode); if (file->handle == -1) return errnoToFileCode(errno); return CGE_FILE_EOK; } CgeFile *CgeFileNew(const char *path, int mode, int *result) { CgeFile *file; int code = 0; if ((file = malloc(sizeof(*file)))) { if ((code = fileInit(file, path, mode))) { free(file); file = NULL; } } if (result) *result = code; return file; } void CgeFileFree(CgeFile *file) { close(file->handle); free(file); } int CgeFileRead(CgeFile *file, void *buffer, size_t size, size_t *actual) { ssize_t readed; if (file->flags & CGE_FILE_ERROR) return 0; readed = read(file->handle, buffer, size); if (readed < 0) goto error; if (readed > 0) file->flags &= ~CGE_FILE_EOF; else file->flags |= CGE_FILE_EOF; if (actual) *actual = readed; return 1; error: file->flags |= CGE_FILE_ERROR; file->lastError = errnoToFileCode(errno); return 0; } int CgeFileWrite(CgeFile *file, const void *buffer, size_t size, size_t *actual) { ssize_t written; if (file->flags & CGE_FILE_ERROR) return 0; written = write(file->handle, buffer, size); if (written < 0) goto error; if (actual) *actual = written; return 1; error: file->flags |= CGE_FILE_ERROR; file->lastError = errnoToFileCode(errno); return 0; } int CgeFileTell(CgeFile *file, int64_t *offset) { if (file->flags & CGE_FILE_ERROR) return 0; if ((*offset = lseek(file->handle, 0, SEEK_CUR)) == -1) goto error; return 1; error: file->flags |= CGE_FILE_ERROR; file->lastError = errnoToFileCode(errno); return 0; } int CgeFileSeek(CgeFile *file, int64_t offset, int whence) { if (file->flags & CGE_FILE_ERROR) return 0; if (offset < off_t_min || offset > off_t_max) { file->flags |= CGE_FILE_ERROR; file->lastError = CGE_FILE_EINVAL; return 0; } if (lseek(file->handle, offset, whence) == -1) goto error; return 1; error: file->flags |= CGE_FILE_ERROR; file->lastError = errnoToFileCode(errno); return 0; } int CgeFileFlush(CgeFile *file) { if (fsync(file->handle)) goto error; return 1; error: file->flags |= CGE_FILE_ERROR; file->lastError = errnoToFileCode(errno); return 0; } int CgeFileSize(CgeFile *file, int64_t *size) { struct stat sb; if (fstat(file->handle, &sb)) goto error; *size = sb.st_size; return 1; error: file->flags |= CGE_FILE_ERROR; file->lastError = errnoToFileCode(errno); return 0; } int CgeFileFlags(CgeFile *file, int *flags) { *flags = file->flags; return 1; } int CgeFileClear(CgeFile *file) { file->flags &= ~CGE_FILE_ERROR; return 1; } int CgeFileErrorCode(CgeFile *file) { return file->lastError; }