summaryrefslogtreecommitdiff
path: root/src/user/lib/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/user/lib/file.c')
-rw-r--r--src/user/lib/file.c159
1 files changed, 159 insertions, 0 deletions
diff --git a/src/user/lib/file.c b/src/user/lib/file.c
new file mode 100644
index 0000000..44fa194
--- /dev/null
+++ b/src/user/lib/file.c
@@ -0,0 +1,159 @@
+#include "file.h"
+#include <camellia/syscalls.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static FILE _stdin_null = { .fd = 0 };
+static FILE _stdout_null = { .fd = 1 };
+FILE *const stdin = &_stdin_null, *const stdout = &_stdout_null;
+
+
+FILE *fopen(const char *path, const char *mode) {
+ FILE *f;
+ handle_t h;
+ int flags = 0;
+ if (mode[0] == 'w' || mode[0] == 'a')
+ flags |= OPEN_CREATE;
+ // TODO truncate on w
+
+ h = _syscall_open(path, strlen(path), flags);
+ if (h < 0) {
+ errno = -h;
+ return NULL;
+ }
+ f = fdopen(h, mode);
+ if (!f) close(h);
+ return f;
+}
+
+ FILE *freopen(const char *path, const char *mode, FILE *f) {
+ /* partially based on the musl implementation of freopen */
+ FILE *f2;
+ if (!path) goto fail;
+ f2 = fopen(path, mode);
+ if (!f2) goto fail;
+
+ if (f->fd == f2->fd) {
+ f2->fd = -1;
+ } else {
+ if (_syscall_dup(f2->fd, f->fd, 0) < 0) goto fail2;
+ }
+ f->pos = f2->pos;
+ f->eof = f2->eof;
+ fclose(f2);
+ return f;
+
+fail2:
+ fclose(f2);
+fail:
+ fclose(f);
+ return NULL;
+}
+
+FILE *fdopen(int fd, const char *mode) {
+ FILE *f;
+ f = malloc(sizeof *f);
+ if (!f) return NULL;
+ f->pos = mode[0] == 'a' ? -1 : 0;
+ f->eof = false;
+ f->fd = fd;
+ f->error = false;
+ return f;
+}
+
+FILE *file_clone(const FILE *f) {
+ handle_t h = _syscall_dup(f->fd, -1, 0);
+ FILE *f2;
+ if (h < 0) return NULL;
+
+ f2 = fdopen(h, "r+");
+ if (!f2) {
+ close(h);
+ return NULL;
+ }
+ f2->pos = f->pos;
+ f2->eof = f->eof;
+ f2->fd = h;
+ return f2;
+}
+
+static void fadvance(long amt, FILE *f) {
+ bool pos_neg = f->pos < 0;
+ f->pos += amt;
+ if (pos_neg && f->pos >= 0)
+ f->pos = -1;
+}
+
+size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict f) {
+ size_t total = size*nitems, pos = 0;
+ unsigned char *buf = ptr;
+
+ if (f->fd < 0) {
+ errno = EBADF;
+ return 0;
+ }
+ if (size == 0)
+ return 0;
+
+ while (pos < total) {
+ long res = _syscall_read(f->fd, buf + pos, total - pos, f->pos);
+ if (res < 0) {
+ f->error = true;
+ errno = -res;
+ return pos/size;
+ } else if (res == 0) {
+ f->eof = true;
+ return pos/size;
+ } else {
+ pos += res;
+ fadvance(res, f);
+ }
+ }
+ return nitems;
+}
+
+size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict f) {
+ size_t total = size*nitems, pos = 0;
+ const unsigned char *buf = ptr;
+
+ if (f->fd < 0) {
+ errno = EBADF;
+ return 0;
+ }
+ if (size == 0)
+ return 0;
+
+ while (pos < total) {
+ long res = _syscall_write(f->fd, buf + pos, total - pos, f->pos);
+ if (res < 0) {
+ f->error = true;
+ errno = -res;
+ return pos/size;
+ } else if (res == 0) {
+ f->eof = true;
+ return pos/size;
+ } else {
+ pos += res;
+ fadvance(res, f);
+ }
+ }
+ return nitems;
+}
+
+int fclose(FILE *f) {
+ if (f->fd > 0) close(f->fd);
+ if (f != &_stdin_null && f != &_stdout_null)
+ free(f);
+ return 0;
+}
+
+int feof(FILE *f) {
+ return f->eof;
+}
+
+int ferror(FILE *f) {
+ return f->error;
+}