summaryrefslogtreecommitdiff
path: root/src/libc/stdio
diff options
context:
space:
mode:
Diffstat (limited to 'src/libc/stdio')
-rw-r--r--src/libc/stdio/file.c359
-rw-r--r--src/libc/stdio/file.h14
-rw-r--r--src/libc/stdio/misc.c52
3 files changed, 425 insertions, 0 deletions
diff --git a/src/libc/stdio/file.c b/src/libc/stdio/file.c
new file mode 100644
index 0000000..efaf013
--- /dev/null
+++ b/src/libc/stdio/file.c
@@ -0,0 +1,359 @@
+#include "file.h"
+#include <bits/panic.h>
+#include <camellia.h>
+#include <camellia/syscalls.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+static FILE _stdin_null = { .fd = STDIN_FILENO };
+static FILE _stdout_null = { .fd = STDOUT_FILENO };
+static FILE _stderr_null = { .fd = STDERR_FILENO };
+FILE *const stdin = &_stdin_null;
+FILE *const stdout = &_stdout_null;
+FILE *const stderr = &_stderr_null;
+
+
+FILE *fopen(const char *path, const char *mode) {
+ FILE *f;
+ hid_t h;
+ int flags = 0;
+ if (!path) {
+ errno = 1;
+ return NULL;
+ } else if (path[0] == '!') {
+ /* special handling for "!files" */
+ path++;
+ if (!strcmp(path, "stdin")) return file_clone(stdin, mode);
+ if (!strcmp(path, "stdout")) return file_clone(stdout, mode);
+ if (!strcmp(path, "stderr")) return file_clone(stderr, mode);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (strchr(mode, 'e')) {
+ /* camellia extension: open as executable */
+ flags |= OPEN_EXEC;
+ } else if (strchr(mode, 'r')) {
+ flags |= OPEN_READ;
+ if (strchr(mode, '+'))
+ flags |= OPEN_WRITE;
+ } else {
+ flags |= OPEN_WRITE | OPEN_CREATE;
+ }
+
+ h = camellia_open(path, flags);
+ if (h < 0) return NULL;
+
+ if (mode[0] == 'w')
+ _sys_write(h, NULL, 0, 0, WRITE_TRUNCATE);
+
+ f = fdopen(h, mode);
+ if (!f) close(h);
+ setvbuf(f, NULL, _IOFBF, 0);
+ 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 (_sys_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 = calloc(1, sizeof *f);
+ if (f) {
+ f->fd = fd;
+ f->pos = mode[0] == 'a' ? -1 : 0;
+ }
+ return f;
+}
+
+FILE *file_clone(const FILE *f, const char *mode) {
+ hid_t h = _sys_dup(f->fd, -1, 0);
+ FILE *f2;
+ if (h < 0) return NULL;
+
+ f2 = fdopen(h, mode);
+ if (!f2) {
+ close(h);
+ return NULL;
+ }
+ f2->pos = f->pos;
+ f2->eof = f->eof;
+ f2->fd = h;
+ return f2;
+}
+
+// TODO popen / pclose
+FILE *popen(const char *cmd, const char *mode) {
+ (void)cmd; (void)mode;
+ errno = ENOSYS;
+ return NULL;
+}
+
+int pclose(FILE *f) {
+ (void)f;
+ errno = ENOSYS;
+ return -1;
+}
+
+// TODO tmpfile()
+FILE *tmpfile(void) {
+ errno = ENOSYS;
+ return NULL;
+}
+
+
+int fextflags(FILE *f, int extflags) {
+ int old = f->extflags;
+ f->extflags = extflags;
+ return old;
+}
+
+int setvbuf(FILE *restrict f, char *restrict buf, int type, size_t size) {
+ if (type == _IONBF) {
+ free(f->readbuf);
+ f->readbuf = NULL;
+ return 0;
+ } else if (type == _IOFBF && buf == NULL) {
+ (void) size;
+ f->rblen = 0;
+ f->rbcap = BUFSIZ;
+ f->readbuf = malloc(f->rbcap);
+ return f->readbuf ? 0 : -1;
+ } else {
+ return errno = ENOSYS, -1;
+ }
+}
+
+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 = 0;
+ if (f->readbuf) {
+ if (0 == f->rblen && total - pos < (f->rbcap >> 1)) {
+ res = _sys_read(f->fd, f->readbuf, f->rbcap, f->pos);
+ if (res < 0) {
+ f->error = true;
+ errno = -res;
+ break;
+ } else if (res == 0) {
+ f->eof = true;
+ break;
+ } else {
+ f->rblen = res;
+ }
+ }
+ if (0 < f->rblen) {
+ res = MIN(total - pos, f->rblen);
+ memcpy(buf + pos, f->readbuf, res);
+ f->rblen -= res;
+ memmove(f->readbuf, f->readbuf + res, f->rblen);
+ }
+ }
+ if (res == 0) {
+ /* no cache hit */
+ res = _sys_read(f->fd, buf + pos, total - pos, f->pos);
+ if (res < 0) {
+ f->error = true;
+ errno = -res;
+ break;
+ } else if (res == 0) {
+ f->eof = true;
+ break;
+ }
+ }
+ pos += res;
+ fadvance(res, f);
+ if (f->extflags & FEXT_NOFILL) break;
+ }
+ return pos == total ? nitems : (pos/size);
+}
+
+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 = _sys_write(f->fd, buf + pos, total - pos, f->pos, 0);
+ 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 fputs(const char *s, FILE *f) {
+ return fprintf(f, "%s\n", s);
+}
+
+char *fgets(char *buf, int size, FILE *f) {
+ int pos, c;
+ for (pos = 0; pos < size-1; ) {
+ c = fgetc(f);
+ if (c == EOF) break;
+ buf[pos++] = c;
+ if (c == '\n') break;
+ }
+ if (pos == 0 || f->error) {
+ return NULL;
+ } else {
+ buf[pos] = '\0';
+ return buf;
+ }
+}
+
+int fgetc(FILE *f) {
+ char c;
+ size_t ret = fread(&c, 1, 1, f);
+ return ret ? c : EOF;
+}
+int getc(FILE *f) { return fgetc(f); }
+
+int fputc(int c, FILE *f) {
+ return fwrite(&c, 1, 1, f) ? c : EOF;
+}
+int putc(int c, FILE *f) { return fputc(c, f); }
+
+// TODO ungetc
+int ungetc(int c, FILE *f) {
+ (void)c; (void)f;
+ __libc_panic("unimplemented");
+}
+
+int fseek(FILE *f, long offset, int whence) {
+ return fseeko(f, offset, whence);
+}
+
+int fseeko(FILE *f, off_t offset, int whence) {
+ if (fflush(f))
+ return -1;
+
+ long base;
+ switch (whence) {
+ case SEEK_SET:
+ base = 0;
+ break;
+ case SEEK_CUR:
+ base = f->pos;
+ // TODO untested
+ if (f->readbuf) {
+ base -= f->rblen;
+ }
+ break;
+ case SEEK_END:
+ base = _sys_getsize(f->fd);
+ if (base < 0)
+ base = -1;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ f->rblen = 0;
+
+ if (base >= 0 && base + offset < 0) {
+ /* underflow */
+ errno = EINVAL;
+ return -1;
+ } else if (base < 0 && base + offset >= 0) {
+ /* overflow - went from a negative offset (relative to EOF)
+ * to a positive offset (from start of file).
+ * can only happen when getsize() is unsupported */
+ errno = EINVAL;
+ return -1;
+ }
+ f->pos = base + offset;
+ f->eof = false;
+ return 0;
+}
+
+long ftell(FILE *f) {
+ return ftello(f);
+}
+
+off_t ftello(FILE *f) {
+ return f->pos;
+}
+
+int fclose(FILE *f) {
+ fflush(f);
+ if (f->fd > 0) close(f->fd);
+ free(f->readbuf);
+ if (f != &_stdin_null && f != &_stdout_null && f != &_stderr_null)
+ free(f);
+ return 0;
+}
+
+int fflush(FILE *f) {
+ (void)f;
+ return 0;
+}
+
+int feof(FILE *f) {
+ return f->eof;
+}
+
+int ferror(FILE *f) {
+ return f->error;
+}
+
+void clearerr(FILE *f) {
+ f->error = false;
+ f->eof = false;
+}
diff --git a/src/libc/stdio/file.h b/src/libc/stdio/file.h
new file mode 100644
index 0000000..3bd64a1
--- /dev/null
+++ b/src/libc/stdio/file.h
@@ -0,0 +1,14 @@
+#pragma once
+#include <stdbool.h>
+#include <stdio.h>
+
+struct _LIBC_FILE {
+ int fd;
+ long pos;
+ bool eof;
+ bool error;
+ int extflags;
+
+ char *readbuf;
+ size_t rblen, rbcap;
+};
diff --git a/src/libc/stdio/misc.c b/src/libc/stdio/misc.c
new file mode 100644
index 0000000..45144f3
--- /dev/null
+++ b/src/libc/stdio/misc.c
@@ -0,0 +1,52 @@
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+void perror(const char *s) {
+ if (s) fprintf(stderr, "%s: ", s);
+ fprintf(stderr, "%s\n", strerror(errno));
+}
+
+int puts(const char *s) {
+ return printf("%s\n", s);
+}
+
+int getchar(void) {
+ return fgetc(stdin);
+}
+
+int putchar(int c) {
+ return fputc(c, stdout);
+}
+
+off_t lseek(int fd, off_t off, int whence) {
+ (void)fd; (void)off; (void)whence;
+ errno = ENOSYS;
+ return -1;
+}
+
+int remove(const char *path) {
+ return unlink(path);
+}
+
+// TODO! VFSOP_MOVE
+int rename(const char *old, const char *new) {
+ (void)old; (void)new;
+ errno = ENOSYS;
+ return -1;
+}
+
+// TODO tmpnam
+char *tmpnam(char *s) {
+ static char buf[L_tmpnam];
+ if (!s) s = buf;
+ strcpy(s, "/tmp/tmpnam");
+ return s;
+}
+
+// TODO sscanf
+int sscanf(const char *restrict s, const char *restrict format, ...) {
+ (void)s; (void)format;
+ return 0;
+}