From 642b5fb0007b64c77d186fcb018d571152ee1d47 Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Mon, 14 Aug 2023 18:51:07 +0200
Subject: reorganization: first steps

---
 src/libc/stdio/file.c | 359 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/libc/stdio/file.h |  14 ++
 src/libc/stdio/misc.c |  52 ++++++++
 3 files changed, 425 insertions(+)
 create mode 100644 src/libc/stdio/file.c
 create mode 100644 src/libc/stdio/file.h
 create mode 100644 src/libc/stdio/misc.c

(limited to 'src/libc/stdio')

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;
+}
-- 
cgit v1.2.3