aboutsummaryrefslogtreecommitdiff
path: root/src/posix/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/posix/file.c')
-rw-r--r--src/posix/file.c405
1 files changed, 405 insertions, 0 deletions
diff --git a/src/posix/file.c b/src/posix/file.c
new file mode 100644
index 0000000..314235c
--- /dev/null
+++ b/src/posix/file.c
@@ -0,0 +1,405 @@
+#include <bh/io.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+typedef struct bh_file_s
+{
+ char *path;
+ int mode;
+ int flags;
+ int handle;
+} bh_file_t;
+
+
+static int file_info(bh_file_t *file,
+ size_t *size,
+ const char **ident);
+
+
+static int file_init(bh_file_t *file,
+ const char *path);
+
+
+static int file_destroy(bh_file_t *file);
+
+
+static int file_open(bh_file_t *file,
+ int *mode);
+
+
+static int file_close(bh_file_t *file);
+
+
+static int file_read(bh_file_t *file,
+ char *data,
+ size_t *size);
+
+
+static int file_write(bh_file_t *file,
+ const char *data,
+ size_t *size);
+
+
+static int file_peek(bh_file_t *file,
+ char *data,
+ size_t *size);
+
+
+static int file_flush(bh_file_t *file);
+
+
+static int file_seek(bh_file_t *file,
+ int64_t *pos,
+ int *dir);
+
+
+static int file_tell(bh_file_t *file,
+ int64_t *pos);
+
+
+static int file_size(bh_file_t *file,
+ int64_t *size);
+
+
+static int file_flags(bh_file_t *file);
+
+
+static int file_clear(bh_file_t *file);
+
+
+static int file_info(bh_file_t *file,
+ size_t *size,
+ const char **name)
+{
+ static const char classname[] = BH_FILE_CLASSNAME;
+
+ if (size)
+ *size = sizeof(*file);
+ if (name)
+ *name = classname;
+ return BH_OK;
+}
+
+
+static int file_init(bh_file_t *file,
+ const char *path)
+{
+ /* Check if path is valid */
+ if (!path)
+ return BH_ERROR;
+
+ /* Duplicate path string and initialize the file struct */
+ file->path = strdup(path);
+ file->mode = 0;
+ file->handle = -1;
+ file->flags = 0;
+
+ return BH_OK;
+}
+
+
+static int file_destroy(bh_file_t *file)
+{
+ /* Close the file handle on destruction */
+ if (file->handle != -1)
+ file_close(file);
+
+ /* Free path string */
+ free(file->path);
+
+ return BH_OK;
+}
+
+
+static int file_openflags(int mode)
+{
+ int flags = 0;
+
+ /* Determine read/write flags */
+ if ((mode & BH_IO_READWRITE) == BH_IO_READWRITE)
+ flags |= O_RDWR;
+ else if (mode & BH_IO_WRITE)
+ flags |= O_WRONLY;
+ else if (mode & BH_IO_READ)
+ flags |= O_RDONLY;
+ else
+ return -1;
+
+ /* Check if existing file should be opened */
+ if (!(mode & BH_IO_EXIST))
+ {
+ flags |= O_CREAT;
+
+ /* Check if file should be created */
+ if (mode & BH_IO_CREATE)
+ flags |= O_EXCL;
+ }
+
+ /* Check if file should be opened in append mode */
+ if (mode & BH_IO_APPEND)
+ flags |= O_APPEND;
+
+ /* Check if file should be truncated */
+ if (mode & BH_IO_TRUNCATE)
+ flags |= O_TRUNC;
+
+ return flags;
+}
+
+
+static int file_open(bh_file_t *file,
+ int *mode)
+{
+ static const mode_t open_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ int flags;
+
+ /* If file is already opened - report error */
+ if (file->handle != -1)
+ return BH_ERROR;
+
+ /* Determine file open flags */
+ flags = file_openflags(*mode);
+ if (flags == -1)
+ return BH_ERROR;
+
+ /* Open the file */
+ file->handle = open(file->path, flags, open_mode);
+ if (file->handle == -1)
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+
+static int file_close(bh_file_t *file)
+{
+ /* If file is closed - report error */
+ if (file->handle == -1)
+ return BH_ERROR;
+
+ /* Close and reset the file handle */
+ close(file->handle);
+ file->handle = -1;
+
+ return BH_OK;
+}
+
+
+static int file_read(bh_file_t *file,
+ char *data,
+ size_t *size)
+{
+ ssize_t readed;
+
+ /* Check if file is open */
+ if (file->handle == -1)
+ goto error;
+
+ /* Read data from the file */
+ readed = read(file->handle, data, *size);
+ if (readed < 0)
+ goto error;
+
+ /* Check for EOF condition */
+ if (readed > 0)
+ file->flags &= ~BH_IO_FLAG_EOF;
+ else
+ file->flags |= BH_IO_FLAG_EOF;
+
+ *size = readed;
+ return BH_OK;
+
+error:
+ file->flags |= BH_IO_FLAG_ERROR;
+ return BH_ERROR;
+}
+
+
+static int file_write(bh_file_t *file,
+ const char *data,
+ size_t *size)
+{
+ ssize_t written;
+
+ /* Check if file is open */
+ if (file->handle == -1)
+ goto error;
+
+ /* Write data to the file */
+ written = write(file->handle, data, *size);
+ if (written < 0)
+ goto error;
+
+ /* Check for EOF condition */
+ if (!written)
+ file->flags |= BH_IO_FLAG_EOF;
+ else
+ file->flags &= ~BH_IO_FLAG_EOF;
+
+ *size = written;
+ return BH_OK;
+
+error:
+ file->flags |= BH_IO_FLAG_ERROR;
+ return BH_ERROR;
+}
+
+
+static int file_peek(bh_file_t *file,
+ char *data,
+ size_t *size)
+{
+ int64_t position;
+ int direction;
+
+ /* Check if file is open */
+ if (file->handle == -1)
+ goto error;
+
+ /* Read data from the file */
+ if (file_read(file, data, size))
+ goto error;
+
+ /* Backtrack by the read amount */
+ position = -((int64_t)*size);
+ direction = BH_IO_SEEK_CUR;
+ if (file_seek(file, &position, &direction))
+ goto error;
+
+ return BH_OK;
+
+error:
+ file->flags |= BH_IO_FLAG_ERROR;
+ return BH_ERROR;
+}
+
+
+static int file_flush(bh_file_t *file)
+{
+ /* Check if file is open */
+ if (file->handle == -1)
+ {
+ file->flags |= BH_IO_FLAG_ERROR;
+ return BH_ERROR;
+ }
+
+ /* Flush the buffers */
+ fsync(file->handle);
+ return BH_OK;
+}
+
+
+static int file_seek(bh_file_t *file,
+ int64_t *pos,
+ int *dir)
+{
+ /* Check if file is open */
+ if (file->handle == -1)
+ goto error;
+
+ /* Seek to the specified position */
+ if (lseek(file->handle, *pos, *dir) == -1)
+ goto error;
+
+ return BH_OK;
+
+error:
+ file->flags |= BH_IO_FLAG_ERROR;
+ return BH_ERROR;
+}
+
+
+static int file_tell(bh_file_t *file,
+ int64_t *pos)
+{
+ /* Check if file is open */
+ if (file->handle == -1)
+ goto error;
+
+ /* Get current offset in the file */
+ if ((*pos = lseek(file->handle, 0, SEEK_CUR)) == -1)
+ goto error;
+
+ return BH_OK;
+
+error:
+ file->flags |= BH_IO_FLAG_ERROR;
+ return BH_ERROR;
+}
+
+
+static int file_size(bh_file_t *file,
+ int64_t *size)
+{
+ struct stat sb;
+
+ /* Check if file is open */
+ if (file->handle == -1)
+ goto error;
+
+ /* Get file size from the OS */
+ if (fstat(file->handle, &sb))
+ goto error;
+
+ *size = sb.st_size;
+ return BH_OK;
+
+error:
+ file->flags |= BH_IO_FLAG_ERROR;
+ return BH_ERROR;
+}
+
+
+static int file_flags(bh_file_t *file)
+{
+ /* If file handle is valid - append IO_OPEN flag */
+ if (file->handle != -1)
+ return file->flags | BH_IO_FLAG_OPEN;
+
+ return file->flags;
+}
+
+
+static int file_clear(bh_file_t *file)
+{
+ /* Clear IO_ERROR flag */
+ file->flags &= ~BH_IO_FLAG_ERROR;
+ return BH_OK;
+}
+
+
+static int file_proc(bh_file_t *file,
+ int type,
+ void *arg1,
+ void *arg2)
+{
+ switch (type)
+ {
+ case BH_IO_INFO_CB: return file_info(file, (size_t *)arg1, (const char **)arg2);
+ case BH_IO_INIT_CB: return file_init(file, (const char *)arg1);
+ case BH_IO_DESTROY_CB: return file_destroy(file);
+ case BH_IO_OPEN_CB: return file_open(file, (int *)arg1);
+ case BH_IO_CLOSE_CB: return file_close(file);
+ case BH_IO_READ_CB: return file_read(file, (char *)arg1, (size_t *)arg2);
+ case BH_IO_WRITE_CB: return file_write(file, (const char *)arg1, (size_t *)arg2);
+ case BH_IO_PEEK_CB: return file_peek(file, (char *)arg1, (size_t *)arg2);
+ case BH_IO_FLUSH_CB: return file_flush(file);
+ case BH_IO_SEEK_CB: return file_seek(file, (int64_t *)arg1, (int *)arg2);
+ case BH_IO_TELL_CB: return file_tell(file, (int64_t *)arg1);
+ case BH_IO_SIZE_CB: return file_size(file, (int64_t *)arg1);
+ case BH_IO_FLAGS_CB: return file_flags(file);
+ case BH_IO_CLEAR_CB: return file_clear(file);
+ default: return BH_NOIMPL;
+ }
+}
+
+
+bh_io_t *bh_file_new(const char *path)
+{
+ return bh_io_new((bh_io_func_t)file_proc, (void *)path);
+}