summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/true/true.c3
-rw-r--r--src/libc/_start2.c2
-rw-r--r--src/libc/include/camellia/intr.h4
-rw-r--r--src/libc/include/unistd.h8
-rw-r--r--src/libc/intr.c13
-rw-r--r--src/libc/stdio/fprintf.c (renamed from src/libc/printf.c)40
-rw-r--r--src/libc/stdio/sprintf.c28
-rw-r--r--src/libc/stdlib/progname.c28
-rw-r--r--src/libc/stdlib/stdlib.c (renamed from src/libc/stdlib.c)24
-rw-r--r--src/libc/string/basic.c105
-rw-r--r--src/libc/string/string.c108
-rw-r--r--src/libc/unistd.c315
-rw-r--r--src/libc/unistd/cwd.c95
-rw-r--r--src/libc/unistd/simple.c128
-rw-r--r--src/libc/unistd/unistd.c85
15 files changed, 503 insertions, 483 deletions
diff --git a/src/cmd/true/true.c b/src/cmd/true/true.c
new file mode 100644
index 0000000..061ed7e
--- /dev/null
+++ b/src/cmd/true/true.c
@@ -0,0 +1,3 @@
+int main(void) {
+ return 0;
+}
diff --git a/src/libc/_start2.c b/src/libc/_start2.c
index b4eb76a..dbc91d7 100644
--- a/src/libc/_start2.c
+++ b/src/libc/_start2.c
@@ -36,7 +36,7 @@ _Noreturn void _start2(struct execdata *ed) {
_sys_intr_set(intr_trampoline);
intr_set(intr_default);
- __setinitialcwd(ed->cwd);
+ __initialcwd = ed->cwd;
exit(main(ed->argc, ed->argv, ed->envp));
}
diff --git a/src/libc/include/camellia/intr.h b/src/libc/include/camellia/intr.h
new file mode 100644
index 0000000..b9390fd
--- /dev/null
+++ b/src/libc/include/camellia/intr.h
@@ -0,0 +1,4 @@
+#pragma once
+
+void intr_set(void (*fn)(void));
+void intr_default(void);
diff --git a/src/libc/include/unistd.h b/src/libc/include/unistd.h
index 750d6e2..2158511 100644
--- a/src/libc/include/unistd.h
+++ b/src/libc/include/unistd.h
@@ -1,5 +1,6 @@
#pragma once
#include <camellia/types.h> // TODO only needed because of hid_t
+#include <camellia/intr.h> // TODO only included for backwards compat
#include <sys/types.h>
#include <getopt.h>
@@ -64,8 +65,5 @@ unsigned int sleep(unsigned int seconds);
* @return 0 on failure, length of the path otherwise */
size_t absolutepath(char *out, const char *in, size_t size);
-// TODO put in an internal libc header
-void __setinitialcwd(const char *c);
-
-void intr_set(void (*fn)(void));
-void intr_default(void);
+/* used internally */
+extern const char *__initialcwd;
diff --git a/src/libc/intr.c b/src/libc/intr.c
new file mode 100644
index 0000000..cdefc2f
--- /dev/null
+++ b/src/libc/intr.c
@@ -0,0 +1,13 @@
+#include <camellia/intr.h>
+#include <stdlib.h>
+
+static void intr_null(void) { }
+
+extern void (*volatile _intr)(void);
+void intr_set(void (*fn)(void)) {
+ _intr = fn ? fn : intr_null;
+}
+
+void intr_default(void) {
+ exit(-1);
+}
diff --git a/src/libc/printf.c b/src/libc/stdio/fprintf.c
index a760240..cd2851c 100644
--- a/src/libc/printf.c
+++ b/src/libc/stdio/fprintf.c
@@ -1,27 +1,10 @@
-#include <camellia/syscalls.h>
#include <shared/printf.h>
#include <stdio.h>
-#include <string.h>
-
static void backend_file(void *arg, const char *buf, size_t len) {
fwrite(buf, 1, len, arg);
}
-int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) {
- return __printf_internal(fmt, ap, backend_file, f);
-}
-
-
-int printf(const char *restrict fmt, ...) {
- int ret;
- va_list argp;
- va_start(argp, fmt);
- ret = vprintf(fmt, argp);
- va_end(argp);
- return ret;
-}
-
int fprintf(FILE *restrict f, const char *restrict fmt, ...) {
int ret;
va_list argp;
@@ -31,11 +14,15 @@ int fprintf(FILE *restrict f, const char *restrict fmt, ...) {
return ret;
}
-int sprintf(char *restrict s, const char *restrict fmt, ...) {
+int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) {
+ return __printf_internal(fmt, ap, backend_file, f);
+}
+
+int printf(const char *restrict fmt, ...) {
int ret;
va_list argp;
va_start(argp, fmt);
- ret = vsnprintf(s, ~0, fmt, argp);
+ ret = vprintf(fmt, argp);
va_end(argp);
return ret;
}
@@ -43,18 +30,3 @@ int sprintf(char *restrict s, const char *restrict fmt, ...) {
int vprintf(const char *restrict fmt, va_list ap) {
return vfprintf(stdout, fmt, ap);
}
-
-int vsprintf(char *restrict s, const char *restrict fmt, va_list ap) {
- return vsnprintf(s, ~0, fmt, ap);
-}
-
-int _klogf(const char *fmt, ...) {
- char buf[256];
- int ret;
- va_list argp;
- va_start(argp, fmt);
- ret = vsnprintf(buf, sizeof buf, fmt, argp);
- va_end(argp);
- _sys_debug_klog(buf, ret);
- return ret;
-}
diff --git a/src/libc/stdio/sprintf.c b/src/libc/stdio/sprintf.c
new file mode 100644
index 0000000..0bfbb17
--- /dev/null
+++ b/src/libc/stdio/sprintf.c
@@ -0,0 +1,28 @@
+#include <camellia/syscalls.h>
+#include <shared/mem.h>
+#include <shared/printf.h>
+#include <stdio.h>
+
+int sprintf(char *restrict s, const char *restrict fmt, ...) {
+ int ret;
+ va_list argp;
+ va_start(argp, fmt);
+ ret = vsnprintf(s, ~0, fmt, argp);
+ va_end(argp);
+ return ret;
+}
+
+int vsprintf(char *restrict s, const char *restrict fmt, va_list ap) {
+ return vsnprintf(s, ~0, fmt, ap);
+}
+
+int _klogf(const char *fmt, ...) {
+ char buf[256];
+ int ret;
+ va_list argp;
+ va_start(argp, fmt);
+ ret = vsnprintf(buf, sizeof buf, fmt, argp);
+ va_end(argp);
+ _sys_debug_klog(buf, ret);
+ return ret;
+}
diff --git a/src/libc/stdlib/progname.c b/src/libc/stdlib/progname.c
new file mode 100644
index 0000000..4cc0fb2
--- /dev/null
+++ b/src/libc/stdlib/progname.c
@@ -0,0 +1,28 @@
+#include <_proc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char *progname;
+const char *getprogname(void) {
+ return progname;
+}
+void setprogname(const char *pg) {
+ progname = pg;
+ setproctitle(NULL);
+}
+
+void setproctitle(const char *fmt, ...) {
+ // TODO bounds checking
+ if (!fmt) {
+ strcpy(_psdata_loc->desc, progname);
+ return;
+ }
+ sprintf(_psdata_loc->desc, "%s: ", progname);
+
+ va_list argp;
+ va_start(argp, fmt);
+ vsnprintf(_psdata_loc->desc + strlen(_psdata_loc->desc), 128, fmt, argp);
+ va_end(argp);
+}
+
diff --git a/src/libc/stdlib.c b/src/libc/stdlib/stdlib.c
index 16c8f70..1dee760 100644
--- a/src/libc/stdlib.c
+++ b/src/libc/stdlib/stdlib.c
@@ -1,4 +1,3 @@
-#include <_proc.h>
#include <bits/panic.h>
#include <camellia.h>
#include <camellia/syscalls.h>
@@ -10,29 +9,6 @@ _Noreturn void abort(void) {
_sys_exit(1);
}
-static const char *progname;
-const char *getprogname(void) {
- return progname;
-}
-void setprogname(const char *pg) {
- progname = pg;
- setproctitle(NULL);
-}
-
-void setproctitle(const char *fmt, ...) {
- // TODO bounds checking
- if (!fmt) {
- strcpy(_psdata_loc->desc, progname);
- return;
- }
- sprintf(_psdata_loc->desc, "%s: ", progname);
-
- va_list argp;
- va_start(argp, fmt);
- vsnprintf(_psdata_loc->desc + strlen(_psdata_loc->desc), 128, fmt, argp);
- va_end(argp);
-}
-
char *mktemp(char *tmpl) {
// TODO mktemp mkstemp
return tmpl;
diff --git a/src/libc/string/basic.c b/src/libc/string/basic.c
new file mode 100644
index 0000000..902779b
--- /dev/null
+++ b/src/libc/string/basic.c
@@ -0,0 +1,105 @@
+/** String functions that don't depend on any external functions. */
+
+#include <string.h>
+
+char *strchr(const char *s, int c) {
+ for (; *s || c == 0; s++) {
+ if (*s == c) return (char *)s;
+ }
+ return NULL;
+}
+
+char *strrchr(const char *s, int c) {
+ for (int i = strlen(s) + 1; i >= 0; i--) {
+ if (s[i] == c) return (char *)s + i;
+ }
+ return NULL;
+}
+
+size_t strspn(const char *s, const char *accept) {
+ size_t l = 0;
+ for (; s[l] && strchr(accept, s[l]); l++);
+ return l;
+}
+
+size_t strcspn(const char *s, const char *reject) {
+ size_t l = 0;
+ for (; s[l] && !strchr(reject, s[l]); l++);
+ return l;
+}
+
+char *strpbrk(const char *s1, const char *s2) {
+ for (; *s1; s1++) {
+ if (strchr(s2, *s1)) return (char*)s1;
+ }
+ return NULL;
+}
+
+char *strtok(char *restrict s, const char *restrict sep) {
+ static char *state;
+ return strtok_r(s, sep, &state);
+}
+
+char *strtok_r(char *restrict s, const char *restrict sep, char **restrict state) {
+ char *end;
+ if (!s) s = *state;
+ s += strspn(s, sep); /* beginning of token */
+ if (!*s) return NULL;
+
+ end = s + strcspn(s, sep);
+ if (*end) {
+ *end = '\0';
+ *state = end + 1;
+ } else {
+ *state = end;
+ }
+ return s;
+}
+
+int strncmp(const char *s1, const char *s2, size_t n) {
+ for (size_t i = 0; i < n; i++) {
+ if (s1[i] < s2[i]) return -1;
+ if (s1[i] > s2[i]) return 1;
+ }
+ return 0;
+}
+
+int strcoll(const char *s1, const char *s2) {
+ return strcmp(s1, s2);
+}
+
+char *strstr(const char *s1, const char *s2) {
+ size_t l1 = strlen(s1), l2 = strlen(s2);
+ for (; l2 <= l1; s1++, l1--) {
+ if (memcmp(s1, s2, l2) == 0) return (char*)s1;
+ }
+ return NULL;
+}
+
+char *strcat(char *restrict dst, const char *restrict src) {
+ return strcpy(dst + strlen(dst), src);
+}
+
+char *strcpy(char *restrict s1, const char *restrict s2) {
+ char *ret = s1;
+ while (*s2) *s1++ = *s2++;
+ *s1 = *s2;
+ return ret;
+}
+
+char *strncpy(char *restrict dst, const char *restrict src, size_t n) {
+ for (size_t i = 0; i < n; i++) {
+ dst[i] = src[i];
+ if (dst[i] == '\0') return dst + i; // TODO fill with null bytes
+ }
+ return dst;
+}
+
+size_t strnlen(const char *s, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ if (!s[i]) {
+ return i;
+ }
+ }
+ return len;
+}
diff --git a/src/libc/string/string.c b/src/libc/string/string.c
index 748b0b5..e93b0a6 100644
--- a/src/libc/string/string.c
+++ b/src/libc/string/string.c
@@ -1,3 +1,5 @@
+/** String functions that don't fit into any of the other .c files */
+
#include <bits/panic.h>
#include <ctype.h>
#include <errno.h>
@@ -5,107 +7,14 @@
#include <string.h>
#include <strings.h>
-char *strchr(const char *s, int c) {
- for (; *s || c == 0; s++) {
- if (*s == c) return (char *)s;
- }
- return NULL;
-}
-
-char *strrchr(const char *s, int c) {
- for (int i = strlen(s) + 1; i >= 0; i--) {
- if (s[i] == c) return (char *)s + i;
- }
- return NULL;
-}
-
-size_t strspn(const char *s, const char *accept) {
- size_t l = 0;
- for (; s[l] && strchr(accept, s[l]); l++);
- return l;
-}
-
-size_t strcspn(const char *s, const char *reject) {
- size_t l = 0;
- for (; s[l] && !strchr(reject, s[l]); l++);
- return l;
-}
-
-char *strpbrk(const char *s1, const char *s2) {
- for (; *s1; s1++) {
- if (strchr(s2, *s1)) return (char*)s1;
- }
- return NULL;
-}
-
-char *strtok(char *restrict s, const char *restrict sep) {
- static char *state;
- return strtok_r(s, sep, &state);
-}
-
-char *strtok_r(char *restrict s, const char *restrict sep, char **restrict state) {
- char *end;
- if (!s) s = *state;
- s += strspn(s, sep); /* beginning of token */
- if (!*s) return NULL;
-
- end = s + strcspn(s, sep);
- if (*end) {
- *end = '\0';
- *state = end + 1;
- } else {
- *state = end;
- }
- return s;
-}
-
-int strncmp(const char *s1, const char *s2, size_t n) {
- for (size_t i = 0; i < n; i++) {
- if (s1[i] < s2[i]) return -1;
- if (s1[i] > s2[i]) return 1;
- }
- return 0;
-}
-
-int strcoll(const char *s1, const char *s2) {
- return strcmp(s1, s2);
-}
-
-// TODO implement strstr using Boyer-Moore
-char *strstr(const char *s1, const char *s2) {
- size_t l1 = strlen(s1), l2 = strlen(s2);
- for (; l2 <= l1; s1++, l1--) {
- if (memcmp(s1, s2, l2) == 0) return (char*)s1;
- }
- return NULL;
-}
-
-char *strcat(char *restrict dst, const char *restrict src) {
- return strcpy(dst + strlen(dst), src);
-}
-
-char *strcpy(char *restrict s1, const char *restrict s2) {
- char *ret = s1;
- while (*s2) *s1++ = *s2++;
- *s1 = *s2;
- return ret;
-}
-
-char *strncpy(char *restrict dst, const char *restrict src, size_t n) {
- for (size_t i = 0; i < n; i++) {
- dst[i] = src[i];
- if (dst[i] == '\0') return dst + i; // TODO fill with null bytes
- }
- return dst;
-}
-
char *strncat(char *restrict dst, const char *restrict src, size_t n) {
(void)dst; (void)src; (void)n;
__libc_panic("unimplemented");
}
char *stpncpy(char *restrict dst, const char *restrict src, size_t n) {
- return stpncpy(dst, src, n) + n;
+ (void)dst; (void)src; (void)n;
+ __libc_panic("unimplemented");
}
char *strdup(const char *s) {
@@ -115,15 +24,6 @@ char *strdup(const char *s) {
return buf;
}
-size_t strnlen(const char *s, size_t len) {
- for (size_t i = 0; i < len; i++) {
- if (!s[i]) {
- return i;
- }
- }
- return len;
-}
-
char *strsignal(int sig) {
static char buf[32];
snprintf(buf, sizeof(buf), "signal %d", sig);
diff --git a/src/libc/unistd.c b/src/libc/unistd.c
deleted file mode 100644
index e87cdb9..0000000
--- a/src/libc/unistd.c
+++ /dev/null
@@ -1,315 +0,0 @@
-#include <bits/panic.h>
-#include <camellia.h>
-#include <camellia/path.h>
-#include <camellia/syscalls.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <elfload.h>
-
-int errno = 0;
-static char *_environ[] = {NULL};
-char **environ = _environ;
-
-int fork(void) {
- return _sys_fork(0, NULL);
-}
-
-pid_t vfork(void) {
- // TODO vfork is implemented improperly and will break stuff
- return _sys_fork(0, NULL);
-}
-
-int close(hid_t h) {
- return _sys_close(h);
-}
-
-_Noreturn void exit(int c) {
- _sys_exit(c);
-}
-_Noreturn void _exit(int c) { exit(c); };
-
-ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize) {
- (void)path; (void)buf; (void)bufsize;
- errno = ENOSYS;
- return -1;
-}
-
-int link(const char *path1, const char *path2) {
- (void)path1; (void)path2;
- errno = ENOSYS;
- return -1;
-}
-
-int unlink(const char *path) {
- hid_t h = camellia_open(path, OPEN_WRITE);
- if (h < 0) return errno = -h, -1;
- long ret = _sys_remove(h);
- if (ret < 0) return errno = -ret, -1;
- return 0;
-}
-
-int rmdir(const char *path) {
- (void)path;
- __libc_panic("unimplemented");
-}
-
-int symlink(const char *path1, const char *path2) {
- (void)path1; (void)path2;
- errno = ENOSYS;
- return -1;
-}
-
-// TODO isatty
-int isatty(int fd) {
- return fd <= 2 ? 1 : 0;
-}
-
-
-int execv(const char *path, char *const argv[]) {
- return execve(path, argv, NULL);
-}
-
-int execvp(const char *path, char *const argv[]) {
- // TODO execvp
- return execve(path, argv, NULL);
-}
-
-int execvpe(const char *path, char *const argv[], char *const envp[]) {
- if (path[0] != '/') {
- char *exp = malloc(strlen(path) + 6);
- int ret;
- strcpy(exp, "/bin/");
- strcat(exp, path);
- ret = execve(exp, argv, envp);
- free(exp);
- return ret;
- }
- return execve(path, argv, envp);
-}
-
-int execve(const char *path, char *const argv[], char *const envp[]) {
- FILE *file = fopen(path, "e");
- char hdr[4] = {0};
- if (!file)
- return -1;
-
- fread(hdr, 1, 4, file);
- fseek(file, 0, SEEK_SET);
-
- if (!memcmp("\x7f""ELF", hdr, 4)) {
- elf_execf(file, (void*)argv, (void*)envp);
- fclose(file);
- } else if (!memcmp("#!", hdr, 2)) {
- char buf[256];
- fseek(file, 2, SEEK_SET);
- if (fgets(buf, sizeof buf, file)) {
- const char *argv [] = {buf, path, NULL};
- char *endl = strchr(buf, '\n');
- if (endl) *endl = '\0';
- execve(buf, (void*)argv, envp);
- }
- }
-
- errno = EINVAL;
- return -1;
-}
-
-
-static const char *__initialcwd;
-static char *cwd = NULL, *cwd2 = NULL;
-static size_t cwdcapacity = 0;
-
-static const char *getrealcwd(void) {
- /* __initialcwd can't just be initialized with "/" because ld has seemingly
- * started to revolt against humanity and not process half the relocations
- * it sees. */
- if (cwd) return cwd;
- if (__initialcwd) return __initialcwd;
- return "/";
-}
-
-int chdir(const char *path) {
- hid_t h;
- char *tmp;
- size_t len = absolutepath(NULL, path, 0) + 1; /* +1 for the trailing slash */
- if (cwdcapacity < len) {
- cwdcapacity = len;
- if (cwd) {
- cwd = realloc(cwd, len);
- cwd2 = realloc(cwd2, len);
- } else {
- size_t initlen = strlen(__initialcwd) + 1;
- if (len < initlen)
- len = initlen;
- cwd = malloc(initlen);
- cwd2 = malloc(initlen);
- memcpy(cwd, __initialcwd, initlen);
- }
- }
- absolutepath(cwd2, path, cwdcapacity);
- len = strlen(cwd2);
- if (cwd2[len - 1] != '/') {
- cwd2[len] = '/';
- cwd2[len + 1] = '\0';
- }
-
- /* check if exists */
- h = camellia_open(cwd2, OPEN_READ);
- if (h < 0) return errno = ENOENT, -1;
- close(h);
-
- tmp = cwd;
- cwd = cwd2;
- cwd2 = tmp;
- return 0;
-}
-
-char *getcwd(char *buf, size_t capacity) {
- const char *realcwd = getrealcwd();
- size_t len = strlen(realcwd) + 1;
- if (capacity < len) {
- errno = capacity == 0 ? EINVAL : ERANGE;
- return NULL;
- }
- memcpy(buf, realcwd, len);
- return buf;
-}
-
-uid_t getuid(void) { return 0; }
-uid_t geteuid(void) { return 0; }
-gid_t getgid(void) { return 0; }
-gid_t getegid(void) { return 0; }
-
-int access(const char *path, int mode) {
- // TODO impl access()
- (void)path; (void)mode;
- return 0;
-}
-
-int chown(const char *path, uid_t owner, gid_t group) {
- (void)path; (void)owner; (void)group;
- errno = ENOSYS;
- return -1;
-}
-
-int setpgid(pid_t pid, pid_t pgid) {
- (void)pid; (void)pgid;
- return errno = ENOSYS, -1;
-}
-
-pid_t tcgetpgrp(int fd) {
- (void)fd;
- return errno = ENOSYS, -1;
-}
-
-int tcsetpgrp(int fd, pid_t pgrp) {
- (void)fd; (void)pgrp;
- return errno = ENOSYS, -1;
-}
-
-pid_t getpgrp(void) {
- __libc_panic("unimplemented");
-}
-
-pid_t getpid(void) {
- return _sys_getpid();
-}
-
-pid_t getppid(void) {
- return _sys_getppid();
-}
-
-int getgroups(int size, gid_t list[]) {
- (void)size; (void)list;
- __libc_panic("unimplemented");
-}
-
-ssize_t read(int fd, void *buf, size_t count) {
- // TODO real file descriptor emulation - store offsets
- return _sys_read(fd, buf, count, -1);
-}
-
-ssize_t write(int fd, const void *buf, size_t count) {
- // TODO real file descriptor emulation - store offsets
- return _sys_write(fd, buf, count, -1, 0);
-}
-
-int pipe(int pipefd[2]) {
- // TODO pipe buffering
- int ret = _sys_pipe(pipefd, 0);
- if (ret < 0) {
- errno = -ret;
- return -1;
- }
- return 0;
-}
-
-int dup(int oldfd) {
- (void)oldfd;
- __libc_panic("unimplemented");
-}
-
-int dup2(int oldfd, int newfd) {
- int ret = _sys_dup(oldfd, newfd, 0);
- if (ret < 0) {
- errno = -ret;
- ret = -1;
- }
- return ret;
-}
-
-unsigned int sleep(unsigned int seconds) {
- _sys_sleep(seconds * 1000);
- return 0;
-}
-
-size_t absolutepath(char *out, const char *in, size_t size) {
- const char *realcwd = getrealcwd();
- size_t len, pos = 0;
- if (!in) return strlen(realcwd) + 1;
-
- if (!(in[0] == '/')) {
- len = strlen(realcwd);
- if (pos + len <= size && out != realcwd)
- memcpy(out + pos, realcwd, len);
- pos += len;
-
- if (realcwd[len - 1] != '/') {
- if (pos + 1 <= size) out[pos] = '/';
- pos++;
- }
- }
-
- len = strlen(in);
- if (pos + len <= size)
- memcpy(out + pos, in, len);
- pos += len;
-
- if (pos <= size) {
- pos = path_simplify(out, out, pos);
- if (pos == 0) return 0;
- }
-
- if (pos + 1 <= size) out[pos] = '\0';
- pos++;
-
- return pos;
-}
-
-void __setinitialcwd(const char *s) {
- __initialcwd = s;
-}
-
-static void intr_null(void) { }
-
-extern void (*volatile _intr)(void);
-void intr_set(void (*fn)(void)) {
- _intr = fn ? fn : intr_null;
-}
-
-void intr_default(void) {
- exit(-1);
-}
diff --git a/src/libc/unistd/cwd.c b/src/libc/unistd/cwd.c
new file mode 100644
index 0000000..7689d56
--- /dev/null
+++ b/src/libc/unistd/cwd.c
@@ -0,0 +1,95 @@
+#include <camellia.h>
+#include <camellia/path.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static char *cwd = NULL, *cwd2 = NULL;
+static size_t cwdcapacity = 0;
+
+static const char *getrealcwd(void) {
+ if (cwd) return cwd;
+ if (__initialcwd) return __initialcwd;
+ return "/";
+}
+
+int chdir(const char *path) {
+ hid_t h;
+ char *tmp;
+ size_t len = absolutepath(NULL, path, 0) + 1; /* +1 for the trailing slash */
+ if (cwdcapacity < len) {
+ cwdcapacity = len;
+ if (cwd) {
+ cwd = realloc(cwd, len);
+ cwd2 = realloc(cwd2, len);
+ } else {
+ size_t initlen = strlen(__initialcwd) + 1;
+ if (len < initlen)
+ len = initlen;
+ cwd = malloc(initlen);
+ cwd2 = malloc(initlen);
+ memcpy(cwd, __initialcwd, initlen);
+ }
+ }
+ absolutepath(cwd2, path, cwdcapacity);
+ len = strlen(cwd2);
+ if (cwd2[len - 1] != '/') {
+ cwd2[len] = '/';
+ cwd2[len + 1] = '\0';
+ }
+
+ /* check if exists */
+ h = camellia_open(cwd2, OPEN_READ);
+ if (h < 0) return errno = ENOENT, -1;
+ close(h);
+
+ tmp = cwd;
+ cwd = cwd2;
+ cwd2 = tmp;
+ return 0;
+}
+
+char *getcwd(char *buf, size_t capacity) {
+ const char *realcwd = getrealcwd();
+ size_t len = strlen(realcwd) + 1;
+ if (capacity < len) {
+ errno = capacity == 0 ? EINVAL : ERANGE;
+ return NULL;
+ }
+ memcpy(buf, realcwd, len);
+ return buf;
+}
+
+size_t absolutepath(char *out, const char *in, size_t size) {
+ const char *realcwd = getrealcwd();
+ size_t len, pos = 0;
+ if (!in) return strlen(realcwd) + 1;
+
+ if (!(in[0] == '/')) {
+ len = strlen(realcwd);
+ if (pos + len <= size && out != realcwd)
+ memcpy(out + pos, realcwd, len);
+ pos += len;
+
+ if (realcwd[len - 1] != '/') {
+ if (pos + 1 <= size) out[pos] = '/';
+ pos++;
+ }
+ }
+
+ len = strlen(in);
+ if (pos + len <= size)
+ memcpy(out + pos, in, len);
+ pos += len;
+
+ if (pos <= size) {
+ pos = path_simplify(out, out, pos);
+ if (pos == 0) return 0;
+ }
+
+ if (pos + 1 <= size) out[pos] = '\0';
+ pos++;
+
+ return pos;
+}
diff --git a/src/libc/unistd/simple.c b/src/libc/unistd/simple.c
new file mode 100644
index 0000000..911c974
--- /dev/null
+++ b/src/libc/unistd/simple.c
@@ -0,0 +1,128 @@
+/** Functions from unistd.h that are either just a syscall or a return. */
+
+#include <camellia/syscalls.h>
+#include <errno.h>
+#include <unistd.h>
+
+int errno = 0;
+static char *_environ[] = {NULL};
+char **environ = _environ;
+const char *__initialcwd = NULL;
+
+int fork(void) {
+ return _sys_fork(0, NULL);
+}
+
+pid_t vfork(void) {
+ // TODO vfork is implemented improperly and will break stuff
+ return _sys_fork(0, NULL);
+}
+
+int close(hid_t h) {
+ return _sys_close(h);
+}
+
+_Noreturn void exit(int c) {
+ _sys_exit(c);
+}
+_Noreturn void _exit(int c) {
+ exit(c);
+};
+
+ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize) {
+ (void)path; (void)buf; (void)bufsize;
+ errno = ENOSYS;
+ return -1;
+}
+
+int link(const char *path1, const char *path2) {
+ (void)path1; (void)path2;
+ errno = ENOSYS;
+ return -1;
+}
+
+int symlink(const char *path1, const char *path2) {
+ (void)path1; (void)path2;
+ errno = ENOSYS;
+ return -1;
+}
+
+// TODO isatty
+int isatty(int fd) {
+ return fd <= 2 ? 1 : 0;
+}
+
+uid_t getuid(void) { return 0; }
+uid_t geteuid(void) { return 0; }
+gid_t getgid(void) { return 0; }
+gid_t getegid(void) { return 0; }
+
+int access(const char *path, int mode) {
+ // TODO impl access()
+ (void)path; (void)mode;
+ return 0;
+}
+
+int chown(const char *path, uid_t owner, gid_t group) {
+ (void)path; (void)owner; (void)group;
+ errno = ENOSYS;
+ return -1;
+}
+
+int setpgid(pid_t pid, pid_t pgid) {
+ (void)pid; (void)pgid;
+ return errno = ENOSYS, -1;
+}
+
+pid_t tcgetpgrp(int fd) {
+ (void)fd;
+ return errno = ENOSYS, -1;
+}
+
+int tcsetpgrp(int fd, pid_t pgrp) {
+ (void)fd; (void)pgrp;
+ return errno = ENOSYS, -1;
+}
+
+pid_t getpid(void) {
+ return _sys_getpid();
+}
+
+pid_t getppid(void) {
+ return _sys_getppid();
+}
+
+ssize_t read(int fd, void *buf, size_t count) {
+ // TODO real file descriptor emulation - store offsets
+ // TODO errno
+ return _sys_read(fd, buf, count, -1);
+}
+
+ssize_t write(int fd, const void *buf, size_t count) {
+ // TODO real file descriptor emulation - store offsets
+ return _sys_write(fd, buf, count, -1, 0);
+}
+
+int pipe(int pipefd[2]) {
+ // TODO pipe buffering
+ int ret = _sys_pipe(pipefd, 0);
+ if (ret < 0) {
+ errno = -ret;
+ return -1;
+ }
+ return 0;
+}
+
+int dup2(int oldfd, int newfd) {
+ int ret = _sys_dup(oldfd, newfd, 0);
+ if (ret < 0) {
+ errno = -ret;
+ ret = -1;
+ }
+ return ret;
+}
+
+unsigned int sleep(unsigned int seconds) {
+ _sys_sleep(seconds * 1000);
+ return 0;
+}
diff --git a/src/libc/unistd/unistd.c b/src/libc/unistd/unistd.c
new file mode 100644
index 0000000..102e72a
--- /dev/null
+++ b/src/libc/unistd/unistd.c
@@ -0,0 +1,85 @@
+#include <bits/panic.h>
+#include <camellia.h>
+#include <camellia/syscalls.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <elfload.h>
+
+int unlink(const char *path) {
+ hid_t h = camellia_open(path, OPEN_WRITE);
+ if (h < 0) return errno = -h, -1;
+ long ret = _sys_remove(h);
+ if (ret < 0) return errno = -ret, -1;
+ return 0;
+}
+
+int rmdir(const char *path) {
+ (void)path;
+ __libc_panic("unimplemented");
+}
+
+int execv(const char *path, char *const argv[]) {
+ return execve(path, argv, NULL);
+}
+
+int execvp(const char *path, char *const argv[]) {
+ // TODO execvp
+ return execve(path, argv, NULL);
+}
+
+int execvpe(const char *path, char *const argv[], char *const envp[]) {
+ if (path[0] != '/') {
+ char *exp = malloc(strlen(path) + 6);
+ int ret;
+ strcpy(exp, "/bin/");
+ strcat(exp, path);
+ ret = execve(exp, argv, envp);
+ free(exp);
+ return ret;
+ }
+ return execve(path, argv, envp);
+}
+
+int execve(const char *path, char *const argv[], char *const envp[]) {
+ FILE *file = fopen(path, "e");
+ char hdr[4] = {0};
+ if (!file)
+ return -1;
+
+ fread(hdr, 1, 4, file);
+ fseek(file, 0, SEEK_SET);
+
+ if (!memcmp("\x7f""ELF", hdr, 4)) {
+ elf_execf(file, (void*)argv, (void*)envp);
+ fclose(file);
+ } else if (!memcmp("#!", hdr, 2)) {
+ char buf[256];
+ fseek(file, 2, SEEK_SET);
+ if (fgets(buf, sizeof buf, file)) {
+ const char *argv [] = {buf, path, NULL};
+ char *endl = strchr(buf, '\n');
+ if (endl) *endl = '\0';
+ execve(buf, (void*)argv, envp);
+ }
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+pid_t getpgrp(void) {
+ __libc_panic("unimplemented");
+}
+
+int getgroups(int size, gid_t list[]) {
+ (void)size; (void)list;
+ __libc_panic("unimplemented");
+}
+
+int dup(int oldfd) {
+ (void)oldfd;
+ __libc_panic("unimplemented");
+}